If you have ever used React, you should be familiar with power of React lifecycles. The upcoming React hooks are about to change the way we handle lifecycles.
React hooks are a unification of existing features including state and lifecycles. In this post I am going to show you how to convert lifecycle class methods into React hooks to shine some light on React hooks.
For each of the three most common lifecycle methods (componentDidMount, componentDidUpdate, componentWillUnmount), I will demonstrate a class style implementation and a hooks style counterpart followed by an explanation.
componentDidMount
class Example extends React.Component {
componentDidMount() {
console.log('I am mounted!');
}
render() {
return null;
}
}
function Example() {
useEffect(() => console.log('mounted'), []);
return null;
}
useEffect is a React hook where you can apply side effects, for example, getting data from server.
The first argument is a callback that will be fired after browser layout and paint. Therefore it does not block the painting process of the browser.
The second argument is an array of values (usually props).
- If any of the value in the array changes, the callback will be fired after every render.
- When it's not present, the callback will always be fired after every render.
- When it's an empty list, the callback will only be fired once, similar to componentDidMount.
componentDidUpdate
componentDidMount() {
console.log('mounted or updated');
}
componentDidUpdate() {
console.log('mounted or updated');
}
useEffect(() => console.log('mounted or updated'));
There is not a straight forward implementation in hooks to replace componentDidUpdate. The useEffect
function can be used to trigger callbacks after every render of the component including after component mounts and component updates.
However this is not a big problem since most of the time we place similar functions in componentDidMount and componentDidUpdate.
Mimicing only componentDidUpdate can be a discussion of another post.
componentWillUnmount
componentWillUnmount() {
console.log('will unmount');
}
useEffect(() => {
return () => {
console.log('will unmount');
}
}, []);
When you return a function in the callback passed to useEffect
, the returned function will be called before the component is removed from the UI.
As we discussed previously, we need to pass an empty list as the second argument for useEffect
so that the callback will only be called once. This apply to the returned function too.
Normally we do cleanups in the componentWillUnmount. Let's say you want to create an event listener on componentDidMount and clean it up on componentDidUnmount. With hooks the code will be combined into one callback function.
We can create multiple hooks for different side effects and reuse them. Grouping correlated code together and making state management reusable is one of the main purpose of React hooks.
useEffect vs useLayoutEffect
Now we can convert componentDidMount, componentDidUpdate, and componentWillUnmount into React hooks, great!
Not so fast, there is a catch: the effects are not exactly the same between the two styles.
Unlike componentDidMount and componentDidUpdate, the function passed to useEffect fires after layout and paint, during a deferred event.
Normally this is not a problem. But if you want to manipulate DOM in the effect, and want to make sure it happens before browser paint, you need to use useLayoutEffect. The syntax is the same as useEffect
.
Summary
To sum it up, we can use useEffect
hook to replace lifecycle methods, but they are not exactly the same. Try to think in hooks when you use them!
References
- https://reactjs.org/docs/hooks-faq.html#from-classes-to-hooks
- https://reactjs.org/docs/hooks-reference.html#useeffect
If you enjoy reading this, here is my blog https://trentyang.com/replace-react-lifecycles-with-hooks/
Top comments (15)
If you use
useEffect
with empty deps array, ESLint would throw an error, that you have to add all dependencies, or omit deps array at all. So, it's not a replacement for componentdidmount lifecycle. What do you use yourself, when you need to do something, only when your component is mounted?I created an account just to like your comment. I am stuck because my
useEffect
hook relies on local state. Since I need to add local state in the dependency array for the function to work, I cannot use it as a stand-in for componentDidUnmount.Where is the
onUnmount
hook?Take a look at this thread once: github.com/facebook/react/issues/1... Might be helpful
That's problem of ESlint. You can easily change the rule by the way.
Of course you can, but it's not recommended.
Thank you for the article. Although I really like Hooks but the life cycle methods in Classes, properly explains what their use is for. Such as componentDidMount. With hooks by using above technique, it doesn't explain much since there are just small differences between them.
I just created an account to thank you! Nice sheatcheet to have! :)
So how exactly can you perform an action once the component is mounted in the functional component if it needs a reference to dependencies. Say I have to reference a dispatch method
useEffect(() => {
dispatch({
type: "COMPONENT_INITIALIZED",
payload: { },
});
}, [dispatch]);
I get COMPONENT_INITIALIZED on every render because dispatch is different.
"Mimicing only componentDidUpdate can be a discussion of another post." - I would love to read this post :)
Great post !
Have one question tho - what is the goto approach when working with canvases ? (useEffect or useLayoutEffect to get the id e.g
reactjs.org/docs/hooks-faq.html#ca...
This was a great resource. Thanks for writing!
Thank you!! <-- *I just created my account just for that!
reactjs.org/docs/hooks-faq.html#ho...