How to use componentWillMount() in React Hooks?
In the official docs of React it mentions -
If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.
My question is - how can we use the componentWillMount()
lifecyle method in a hook?
Solution 1:
You cannot use any of the existing lifecycle methods (componentDidMount
, componentDidUpdate
, componentWillUnmount
etc.) in a hook. They can only be used in class components. And with Hooks you can only use in functional components. The line below comes from the React doc:
If you’re familiar with React class lifecycle methods, you can think of
useEffect
Hook ascomponentDidMount
,componentDidUpdate
, andcomponentWillUnmount
combined.
suggest is, you can mimic these lifecycle method from class component in a functional components.
Code inside componentDidMount
run once when the component is mounted. useEffect
hook equivalent for this behaviour is
useEffect(() => {
// Your code here
}, []);
Notice the second parameter here (empty array). This will run only once.
Without the second parameter the useEffect
hook will be called on every render of the component which can be dangerous.
useEffect(() => {
// Your code here
});
componentWillUnmount
is use for cleanup (like removing event listeners, cancel the timer etc). Say you are adding a event listener in componentDidMount
and removing it in componentWillUnmount
as below.
componentDidMount() {
window.addEventListener('mousemove', () => {})
}
componentWillUnmount() {
window.removeEventListener('mousemove', () => {})
}
Hook equivalent of above code will be as follows
useEffect(() => {
window.addEventListener('mousemove', () => {});
// returned function will be called on component unmount
return () => {
window.removeEventListener('mousemove', () => {})
}
}, [])
Solution 2:
useComponentWillMount hook
const useComponentWillMount = (cb) => {
const willMount = useRef(true)
if (willMount.current) cb()
willMount.current = false
}
This hook could be a saver when there is an issue of sequence (such as running before another script). If that isn't the case, use useComnponentDidMount which is more aligned with React hooks paradigm.
useComponentDidMount hook
const useComponentDidMount = cb => useEffect(cb, []);
If you know your effect should only run once at the beginning use this solution. It will run only once after component has mounted.
useEffect paradigm
Class components have lifecycle methods which are defined as points in the timeline of the component. Hooks don't follow this paradigm. Instead effects should be structured by their content.
function Post({postID}){
const [post, setPost] = useState({})
useEffect(()=>{
fetchPosts(postID).then(
(postObject) => setPost(postObject)
)
}, [postID])
...
}
In the example above the effect deals with fetching the content of a post. Instead of a certain point in time it has a value it is dependent on - postID
. Every time postID
gets a new value (including initialization) it will rerun.
Component Will Mount discussion
In class components componentWillMount is considered legacy (source 1, source2). It's legacy since it might run more than once, and there is an alternative - using the constructor. Those considerations aren't relevant for a functional component.
Solution 3:
According to reactjs.org, componentWillMount will not be supported in the future. https://reactjs.org/docs/react-component.html#unsafe_componentwillmount
There is no need to use componentWillMount.
If you want to do something before the component mounted, just do it in the constructor().
If you want to do network requests, do not do it in componentWillMount. It is because doing this will lead to unexpected bugs.
Network requests can be done in componentDidMount.
Hope it helps.
updated on 08/03/2019
The reason why you ask for componentWillMount is probably because you want to initialize the state before renders.
Just do it in useState.
const helloWorld=()=>{
const [value,setValue]=useState(0) //initialize your state here
return <p>{value}</p>
}
export default helloWorld;
or maybe You want to run a function in componentWillMount, for example, if your original code looks like this:
componentWillMount(){
console.log('componentWillMount')
}
with hook, all you need to do is to remove the lifecycle method:
const hookComponent=()=>{
console.log('componentWillMount')
return <p>you have transfered componeWillMount from class component into hook </p>
}
I just want to add something to the first answer about useEffect.
useEffect(()=>{})
useEffect runs on every render, it is a combination of componentDidUpdate, componentDidMount and ComponentWillUnmount.
useEffect(()=>{},[])
If we add an empty array in useEffect it runs just when the component mounted. It is because useEffect will compare the array you passed to it. So it does not have to be an empty array.It can be array that is not changing. For example, it can be [1,2,3] or ['1,2']. useEffect still only runs when component mounted.
It depends on you whether you want it to run just once or runs after every render. It is not dangerous if you forgot to add an array as long as you know what you are doing.
I created a sample for hook. Please check it out.
https://codesandbox.io/s/kw6xj153wr
updated on 21/08/2019
It has been a while since I wrote the above answer. There is something that I think you need to pay attention to. When you use
useEffect(()=>{},[])
When react compares the values you passed to the array [], it uses Object.is()
to compare.
If you pass an object to it, such as
useEffect(()=>{},[{name:'Tom'}])
This is exactly the same as:
useEffect(()=>{})
It will re-render every time because when Object.is()
compares an object, it compares its reference, not the value itself. It is the same as why {}==={} returns false because their references are different.
If you still want to compare the object itself not the reference, you can do something like this:
useEffect(()=>{},[JSON.stringify({name:'Tom'})])
Update on 7/09/2021:
A few updates about the dependency:
Generally speaking, if you use a function or an object as a dependency, it will always re-render. But react already provides you with the solution: useCallback and useMemo
useCallback is able to memorize a function. useMemo is able to memorize an object.
See this article:
https://javascript.plainenglish.io/5-useeffect-infinite-loop-patterns-2dc9d45a253f