In this example, I have this react class:
class MyDiv extends React.component
constructor(){
this.state={sampleState:'hello world'}
}
render(){
return <div>{this.state.sampleState}
}
}
The question is if I can add React hooks to this. I understand that React-Hooks is alternative to React Class style. But if I wish to slowly migrate into React hooks, can I add useful hooks into Classes?
High order components are how we have been doing this type of thing until hooks came along. You can write a simple high order component wrapper for your hook.
function withMyHook(Component) {
return function WrappedComponent(props) {
const myHookValue = useMyHook();
return <Component {...props} myHookValue={myHookValue} />;
}
}
While this isn't truly using a hook directly from a class component, this will at least allow you to use the logic of your hook from a class component, without refactoring.
class MyComponent extends React.Component {
render(){
const myHookValue = this.props.myHookValue;
return <div>{myHookValue}</div>;
}
}
export default withMyHook(MyComponent);
Class components don't support hooks -
According to the Hooks-FAQ:
You canβt use Hooks inside of a class component, but you can definitely mix classes and function components with Hooks in a single tree. Whether a component is a class or a function that uses Hooks is an implementation detail of that component. In the longer term, we expect Hooks to be the primary way people write React components.
As other answers already explain, hooks API was designed to provide function components with functionality that currently is available only in class components. Hooks aren't supposed to used in class components.
Class components can be written to make easier a migration to function components.
With a single state:
class MyDiv extends Component {
state = {sampleState: 'hello world'};
render(){
const { state } = this;
const setState = state => this.setState(state);
return <div onClick={() => setState({sampleState: 1})}>{state.sampleState}</div>;
}
}
is converted to
const MyDiv = () => {
const [state, setState] = useState({sampleState: 'hello world'});
return <div onClick={() => setState({sampleState: 1})}>{state.sampleState}</div>;
}
Notice that useState state setter doesn't merge state properties automatically, this should be covered with setState(prevState => ({ ...prevState, foo: 1 }));
With multiple states:
class MyDiv extends Component {
state = {sampleState: 'hello world'};
render(){
const { sampleState } = this.state;
const setSampleState = sampleState => this.setState({ sampleState });
return <div onClick={() => setSampleState(1)}>{sampleState}</div>;
}
}
is converted to
const MyDiv = () => {
const [sampleState, setSampleState] = useState('hello world');
return <div onClick={() => setSampleState(1)}>{sampleState}</div>;
}
Complementing Joel Cox's good answer
Render Props also enable the usage of Hooks inside class components, if more flexibility is needed:
class MyDiv extends React.Component {
render() {
return (
<HookWrapper
// pass state/props from inside of MyDiv to Hook
someProp={42}
// process Hook return value
render={hookValue => <div>Hello World! {hookValue}</div>}
/>
);
}
}
function HookWrapper({ someProp, render }) {
const hookValue = useCustomHook(someProp);
return render(hookValue);
}
For side effect Hooks without return value:
function HookWrapper({ someProp }) {
useCustomHook(someProp);
return null;
}
// ... usage
<HookWrapper someProp={42} />
Source: React Training
you can achieve this by generic High order components
HOC
import React from 'react';
const withHook = (Component, useHook, hookName = 'hookvalue') => {
return function WrappedComponent(props) {
const hookValue = useHook();
return <Component {...props} {...{[hookName]: hookValue}} />;
};
};
export default withHook;
Usage
class MyComponent extends React.Component {
render(){
const myUseHookValue = this.props.myUseHookValue;
return <div>{myUseHookValue}</div>;
}
}
export default withHook(MyComponent, useHook, 'myUseHookValue');
Hooks are not meant to be used for classes but rather functions. If you wish to use hooks, you can start by writing new code as functional components with hooks
According to React FAQs
You canβt use Hooks inside of a class component, but you can
definitely mix classes and function components with Hooks in a single
tree. Whether a component is a class or a function that uses Hooks is
an implementation detail of that component. In the longer term, we
expect Hooks to be the primary way people write React components.
const MyDiv = () => {
const [sampleState, setState] = useState('hello world');
render(){
return <div>{sampleState}</div>
}
}
You can use the react-universal-hooks library. It lets you use the "useXXX" functions within the render function of class-components.
It's worked great for me so far. The only issue is that since it doesn't use the official hooks, the values don't show react-devtools.
To get around this, I created an equivalent by wrapping the hooks, and having them store their data (using object-mutation to prevent re-renders) on component.state.hookValues. (you can access the component by auto-wrapping the component render functions, to run set currentCompBeingRendered = this)
For more info on this issue (and details on the workaround), see here: https://github.com/salvoravida/react-universal-hooks/issues/7
Stateful components or containers or class-based components ever support the functions of React Hooks, so we don't need to React Hooks in Stateful components just in stateless components.
Some additional informations
What are React Hooks?
So what are hooks? Well hooks are a new way or offer us a new way of writing our components.
Thus far, of course we have functional and class-based components, right? Functional components receive props and you return some JSX code that should be rendered to the screen.
They are great for presentation, so for rendering the UI part, not so much about the business logic and they are typically focused on one or a few purposes per component.
Class-based components on the other hand also will receive props but they also have this internal state. Therefore class-based components are the components which actually hold the majority of our business logic, so with business logic, I mean things like we make an HTTP request and we need to handle the response and to change the internal state of the app or maybe even without HTTP. A user fills out the form and we want to show this somewhere on the screen, we need state for this, we need class-based components for this and therefore we also typically use class based components to orchestrate our other components and pass our state down as props to functional components for example.
Now one problem we have with this separation, with all the benefits it adds but one problem we have is that converting from one component form to the other is annoying. It's not really difficult but it is annoying.
If you ever found yourself in a situation where you needed to convert a functional component into a class-based one, it's a lot of typing and a lot of typing of always the same things, so it's annoying.
A bigger problem in quotation marks is that lifecycle hooks can be hard to use right.
Obviously, it's not hard to add componentDidMount and execute some code in there but knowing which lifecycle hook to use, when and how to use it correctly, that can be challenging especially in more complex applications and anyways, wouldn't it be nice if we had one way of creating components and that super component could then handle both state and side effects like HTTP requests and also render the user interface?
Well, this is exactly what hooks are all about. Hooks give us a new way of creating functional components and that is important.
React Hooks let you use react features and lifecycle without writing a class.
It's like the equivalent version of the class component with much smaller and readable form factor. You should migrate to React hooks because it's fun to write it.
But you can't write react hooks inside a class component, as it's introduced for functional component.
This can be easily converted to :
class MyDiv extends React.component
constructor(){
this.state={sampleState:'hello world'}
}
render(){
return <div>{this.state.sampleState}
}
}
const MyDiv = () => {
const [sampleState, setSampleState] = useState('hello world');
return <div>{sampleState}</div>
}
It won't be possible with your existing class components. You'll have to convert your class component into a functional component and then do something on the lines of -
function MyDiv() {
const [sampleState, setSampleState] = useState('hello world');
return (
<div>{sampleState}</div>
)
}
For me React.createRef() was helpful.
ex.:
constructor(props) {
super(props);
this.myRef = React.createRef();
}
...
<FunctionComponent ref={this.myRef} />
Origin post here.
I've made a library for this. React Hookable Component.
Usage is very simple. Replace extends Component or extends PureComponent with extends HookableComponent or extends HookablePureComponent. You can then use hooks in the render() method.
import { HookableComponent } from 'react-hookable-component';
// ππππππππ
class ComponentThatUsesHook extends HookableComponent<Props, State> {
render() {
// ππππππ
const value = useSomeHook();
return <span>The value is {value}</span>;
}
}
if you didn't need to change your class component then create another functional component and do hook stuff and import it to class component
Doesn't work anymore in modern React Versions. Took me forever, but finally resulted going back to go ol' callbacks. Only thing that worked for me, all other's threw the know React Hook Call (outside functional component) error.
Non-React or React Context:
class WhateverClass {
private xyzHook: (XyzHookContextI) | undefined
public setHookAccessor (xyzHook: XyzHookContextI): void {
this.xyzHook = xyzHook
}
executeHook (): void {
const hookResult = this.xyzHook?.specificHookFunction()
...
}
}
export const Whatever = new WhateverClass() // singleton
Your hook (or your wrapper for an external Hook)
export interface XyzHookContextI {
specificHookFunction: () => Promise<string>
}
const XyzHookContext = createContext<XyzHookContextI>(undefined as any)
export function useXyzHook (): XyzHookContextI {
return useContext(XyzHookContextI)
}
export function XyzHook (props: PropsWithChildren<{}>): JSX.Element | null {
async function specificHookFunction (): Promise<void> {
...
}
const context: XyzHookContextI = {
specificHookFunction
}
// and here comes the magic in wiring that hook up with the non function component context via callback
Whatever.setHookAccessor(context)
return (
< XyzHookContext.Provider value={context}>
{props.children}
</XyzHookContext.Provider>
)
}
Voila, now you can use ANY react code (via hook) from any other context (class components, vanilla-js, β¦)!
(β¦hope I didn't make to many name change mistakes :P)
Yes, but not directly.
Try react-iifc, more details in its readme.
https://github.com/EnixCoda/react-iifc
Try with-component-hooks:
https://github.com/bplok20010/with-component-hooks
import withComponentHooks from 'with-component-hooks';
class MyComponent extends React.Component {
render(){
const props = this.props;
const [counter, set] = React.useState(0);
//TODO...
}
}
export default withComponentHooks(MyComponent)
2.Try react-iifcοΌ https://github.com/EnixCoda/react-iifc
I've read in a few places that functional components are more lightweight than class components, and from here it sounds like Facebook intends for functional + hooks to be the preferred paradigm for new components.
The problem I see is that with functional components things like class methods get redefined on every render, instead of just once. Is this not an issue, or do the other advantages of functional components simply outweigh this?
Example for clarity:
Class Component
class SignInForm extends React.Component {
...
// Only gets defined when the component is created with `React.createElement`
submit = () => {
// send POST request to get an auth token, etc.
}
render() {
<form>
...
<button onClick={this.submit}>Sign In</button>
</form>
}
}
Functional Component
function SignInForm (props) {
...
// Gets defined on every render, since this essentially *is* the `render` function
const submit = () => {
// send POST request to get an auth token, etc.
}
return (
<form>
...
<button onClick={submit}>Sign In</button>
</form>
);
}
Slowest part in web apps is the rendering. So creating a new function on every render is not a big deal. Also for web apps, build size matter and for functional components, minification works better than as it works for class-based components.
With the release of new features like memo, useMemo, useCallback etc. we can get the same performance as we get with class-based components.
React community is moving towards functional components and hooks which is an indication that functional components are not bad.
I'm trying to use the new Context API in my app and it looks like every time I update the context, it re-renders any component connected to it regardless. I have a sandbox demo setup to see code and working issue. When you type in the input - the buttons context is rendered and vice-versa. My original thinking was that if you type in the input, only the input context would be printed out.
DEMO
Is this how it works or am I missing something?
Thanks,
Spencer
The way I avoid re-rendering with react context API:
First I write my component as pure functional component:
const MyComponent = React.memo(({
somePropFromContext,
otherPropFromContext,
someRegularPropNotFromContext
}) => {
... // regular component logic
return(
... // regular component return
)
});
Then I write a function to select props from context:
function select(){
const { someSelector, otherSelector } = useContext(MyContext);
return {
somePropFromContext: someSelector,
otherPropFromContext: otherSelector,
}
}
I have my connect HOC wrote:
function connect(WrappedComponent, select){
return function(props){
const selectors = select();
return <WrappedComponent {...selectors} {...props}/>
}
}
All together
import connect from 'path/to/connect'
const MyComponent ... //previous code
function select() ... //previous code
export default connect(MyComponent, select)
Usage
<MyComponent someRegularPropNotFromContext={something} />
Demo
Demo on codesandbox
Conclusion
MyComponent will re-render only if the specifics props from context updates with a new value, if the value is the same, it will not re-render. Also it avoid re-rendering on any other value from context that is not used inside MyComponent. The code inside select will execute every time the context updates, but as it does nothing, its ok, since no re-rendering of MyComponent is wasted.
That is the expected behaviour. Components as consumers re-renders when their provider data updates. Further more, shouldComponentUpdate hooks do not work on Consumers.
Quoting React's content API:
All Consumers that are descendants of a Provider will re-render whenever the Providerβs value prop changes. The propagation from Provider to its descendant Consumers is not subject to the shouldComponentUpdate method, so the Consumer is updated even when an ancestor component bails out of the update.
For more info check here
After switching from Redux to MobX for React I'm starting to extremely like MobX. It's pretty awesome.
MobX has a certain behavior where it won't update component if the provided store observable is not used in render. I think generally that's a pretty great behavior that makes components render only when something actually changed.
But... I do encountered couple of cases where I do not want or need to use MobX observable inside render method and in such cases my this.props.store with MobX store won't get updated.
And in such cases, as a workaround, I just reference the observable in the render method, but I don't think that's a very clean approach, and I'm wondering if there's a cleaner way to do that?
This component code should explain even more what I'm exactly asking about.
It's a component that changes body overflow style based on a MobX observable that I have in my store.
/*------------------------------------*\
Imports
\*------------------------------------*/
import React from 'react';
import connectStore from 'script/connect-store';
import addClass from 'dom-helpers/class/addClass';
import removeClass from 'dom-helpers/class/removeClass';
/*------------------------------------*\
Component
\*------------------------------------*/
class Component extends React.Component {
constructor(props) {
super(props);
this.constructor.displayName = 'BodyClassSync';
}
componentDidMount() {
this.checkAndUpdateMuiClass();
}
componentDidUpdate() {
this.checkAndUpdateMuiClass();
}
checkAndUpdateMuiClass() {
// This place is the only place I need latest MobX store...
if (this.props.store.muiOverlay) {
addClass(window.document.body, 'mod-mui-overlay');
}
else {
removeClass(window.document.body, 'mod-mui-overlay');
}
}
render() {
// This is my workaround so the componentDidUpdate will actually fire
// (with latest and updated store)
// when the 'muiOverlay' observable changes in the MobX store
// Is there any cleaner/better way to do this?
this.props.store.muiOverlay;
// This component doesn't and shouldn't render anything
return null;
}
}
/*------------------------------------*\
Export
\*------------------------------------*/
const ComponentWithStore = connectStore(Component);
export default ComponentWithStore;
(Note: I don't use #decorators, I connect store using ES5 syntax in the connectStore function. And it would be awesome if solution to this would be also in ES5.)
You could use an autorun in componentDidMount and dispose the listener in componentWillUnmount so that you don't have to reference it in the render method.
class Component extends React.Component {
constructor(props) {
super(props);
this.constructor.displayName = 'BodyClassSync';
}
componentDidMount() {
this.dispose = autorun(() => {
const { muiOverlay } = this.props.store;
if (muiOverlay) {
addClass(window.document.body, 'mod-mui-overlay');
} else {
removeClass(window.document.body, 'mod-mui-overlay');
}
});
}
componentWillUnmount() {
this.dispose();
}
render() {
return null;
}
}
Since you are not really doing anything in this component, you could put this autorun directly in your store as well.
Not sure how I missed it, but thanks to #Tholle I've reread the "Reacting to observables" section in the MobX docs and found out the reaction helper was the most suitable solution for me.
While, the reaction was the most fitting for the exact problem I had. The autorun and when helpers are pretty similar in the function and I probably could use them for my use case too.
https://mobx.js.org/refguide/autorun.html
https://mobx.js.org/refguide/when.html
https://mobx.js.org/refguide/reaction.html
This is the code example that shows how to use reaction for anyone that just wants quick copy+paste:
componentDidMount(){
this._notificationsReactionDispose = mobx.reaction(
() => this.props.store.notifications,
(notifications, reaction) => {
... the code that runs when "notifications" in my store change
}
);
}
componentWillUnmount() {
this._notificationsReactionDispose();
}
Using React and Redux, imagine you have a component method that sends a request to an external API.
import React, { Component } from 'react';
import { connect } from 'react-redux';
class MyComp extends Component {
boolUpdate (val) {
fetch('http://myapi.com/bool', { val });
}
shouldComponentUpdate (nextProps) {
return false;
}
render () {
return <h1>Hello</h1>;
}
}
const mapStateToProps = ({ bool }) => ({ bool });
export default connect(mapStateToProps)(MyComp);
Now let's say that you want to invoke boolUpdate() each time the bool prop changes, but this should not trigger a component update because nothing in the render of the component is affected.
What's the best way to do this in React?
Until recently people used to do something like:
componentWillReceiveProps (nextProps) {
if (nextProps.bool !== this.props.bool) this.boolUpdate(nextProps.bool);
}
But as of React v16.3 componentWillReceiveProps() has been deprecated. In this example we can't use componentDidUpdate() either, because shouldComponentUpdate() prevents that from happening. And getDerivedStateFromProps() is a static method, so it doesn't have access to the instance methods.
So, the only option we're left with seems to be using shouldComponentUpdate() itself. Something along the lines of:
shouldComponentUpdate (nextProps) {
if (nextProps.bool !== this.props.bool) this.boolUpdate(nextProps.bool);
return false;
}
This looks rather "hacky" to me though, and not what shouldComponentUpdate() was designed for.
Does anybody have a better pattern to suggest?
Is there a preferred way to listen to specific prop changes and trigger component methods?
Thanks!
If you want to run some code (e.g. data fetching) when props change, do it in componentDidUpdate.
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
this.fetchData();
}
}
In your example, this won't work because shouldComponentUpdate returns false. I'd argue this is not a very common case because typically you still want to re-render if props change.
For example, if the user ID changes, you might want to show a loading indicator while the data for the new user is loading. So avoiding a re-render is not very useful in this case.
However, if you're absolutely sure you both need to prevent a re-render and need to perform a side effect like fetching data on props change, you can split your component in two. The outer component would do the data fetching in componentDidUpdate, and return <InnerComponent {...this.props} />. The inner component would have a shouldComponentUpdate implementation that prevents re-rendering further. Again, I wouldn't expect this to be a common scenario, but you can do this.
Based on the React docs and this discussion on github, The place to fetch new data based on props change is actually componentDidUpdate.
The rendering was actually splitted to two phases, the Render Phase which is pure and creates no side effects and the Commit Phase which can run side effects, work with the DOM and schedule updates.
You can see that explained well in Dan Abramov's diagram:
Dan also mentioned:
People used to mix these two different things in
componentWillReceiveProps, which is why we have to split it into a
pure method (getDerivedStateFromProps) and an existing impure one
where itβs okay to do side effects (componentDidUpdate).
And for the solution itself, Im attaching the example from the docs:
Fetching external data when props change
Here is an example of a component that fetches external data based on props values:
Before:
componentDidMount() {
this._loadAsyncData(this.props.id);
}
componentWillReceiveProps(nextProps) {
if (nextProps.id !== this.props.id) {
this.setState({externalData: null});
this._loadAsyncData(nextProps.id);
}
}
After:
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.id !== prevState.prevId) {
return {
externalData: null,
prevId: nextProps.id,
};
}
return null;
}
componentDidMount() {
this._loadAsyncData(this.props.id);
}
componentDidUpdate(prevProps, prevState) {
if (this.state.externalData === null) {
this._loadAsyncData(this.props.id);
}
}