according to the doc
If you’re migrating code from a class component, note
useLayoutEffectfires in the same phase ascomponentDidMountandcomponentDidUpdate. However, we recommend starting withuseEffectfirst and only tryinguseLayoutEffectif that causes a problem.
So if you want your side effect to run in a class component with the same behaviour you gotta use componentDidMount and componentDidUpdate like you thought. The difference between useEffect and useLayoutEffect is that useEffect only runs after the DOM has been updated (the effect will run after the render is committed to the screen). useLayoutEffect will trigger the effect right after the DOM mutations are computed. Therefore, updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.
Here there's a good explanation about useEffect and useLayoutEffect. But thinking on class components, it's equivalent to componentDidMount and componentDidUpdate because it is the commit phase. That's the phase where changes to DOM are allowed to happen as well as side effects and scheduled updates. Both componentDidMount and componentDidUpdate have the synchronous behaviour just like useLayoutEffect. useEffect is the usual recommended option because it won't block the browser rendering which is better for performance in most cases, being an optimized hook version of componentDidMount and componentDidUpdate.
React Hooks and Component Lifecycle Equivalent
How to use componentWillMount() in React Hooks?
Why useEffect hook runs before the DOM is loaded?
react useEffect trigger although dependency value remain the same?
The legacy documentation has the following note about the useLayoutEffect hook:
If you’re migrating code from a class component, note useLayoutEffect fires in the same phase as componentDidMount and componentDidUpdate. However, we recommend starting with useEffect first and only trying useLayoutEffect if that causes a problem.
And it makes me confused. Both componentDidMount and componentDidUpdate run after render method in a class component. This makes me think that code inside both componentDid... lifecycle methods will run after the UI is already rendered on a screen.
And useLayoutEffect has this in the description:
Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint
Which means "before UI s rendered on a screen", doesn't it? Then how is this possible?
useLayoutEffect fires in the same phase as componentDidMount and componentDidUpdate
Or did I get something wrong?
componentDidMount
Pass an empty array as the second argument to useEffect() to run only the callback on mount only.
function ComponentDidMount() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
console.log('componentDidMount');
}, []);
return (
<div>
<p>componentDidMount: {count} times</p>
<button
onClick={() => {
setCount(count + 1);
}}
>
Click Me
</button>
</div>
);
}
ReactDOM.render(
<div>
<ComponentDidMount />
</div>,
document.querySelector("#app")
);
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="app"></div>
componentDidUpdate
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render. useEffect runs on every render including the first. So if you want to have a strict equivalent as componentDidUpdate, you have to use useRef to determine if the component has been mounted once. If you want to be even stricter, use useLayoutEffect(), but it fires synchronously. In most cases, useEffect() should be sufficient.
This answer is inspired by Tholle, all credit goes to him.
function ComponentDidUpdate() {
const [count, setCount] = React.useState(0);
const isFirstUpdate = React.useRef(true);
React.useEffect(() => {
if (isFirstUpdate.current) {
isFirstUpdate.current = false;
return;
}
console.log('componentDidUpdate');
});
return (
<div>
<p>componentDidUpdate: {count} times</p>
<button
onClick={() => {
setCount(count + 1);
}}
>
Click Me
</button>
</div>
);
}
ReactDOM.render(
<ComponentDidUpdate />,
document.getElementById("app")
);
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="app"></div>
componentWillUnmount
Return a callback in useEffect's callback argument and it will be called before unmounting.
function ComponentWillUnmount() {
function ComponentWillUnmountInner(props) {
React.useEffect(() => {
return () => {
console.log('componentWillUnmount');
};
}, []);
return (
<div>
<p>componentWillUnmount</p>
</div>
);
}
const [count, setCount] = React.useState(0);
return (
<div>
{count % 2 === 0 ? (
<ComponentWillUnmountInner count={count} />
) : (
<p>No component</p>
)}
<button
onClick={() => {
setCount(count + 1);
}}
>
Click Me
</button>
</div>
);
}
ReactDOM.render(
<div>
<ComponentWillUnmount />
</div>,
document.querySelector("#app")
);
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="app"></div>
From React docs:
If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.
By that saying they mean:
componentDidMount is sort of useEffect(callback, [])
componentDidUpdate is sort of useEffect(callback, [dep1, dep2, ...]) - the array of deps tell React: "if one of the deps is change, run the callback after rendering".
componentDidMount + componentDidUpdate is sort of useEffect(callback)
componentWillUnmount is sort of the returned function from the callback:
useEffect(() => {
/* some code */
return () => {
/* some code to run when rerender or unmount */
}
)
With the help of Dan Abramov's phrasing from his blog, and some additions of my own:
While you can use those hooks, it’s not an exact equivalent. Unlike componentDidMount and componentDidUpdate, it will capture props and state. So even inside the callbacks, you’ll see the props and state of the specific render (which means in componentDidMount the initial props and state). If you want to see “latest” something, you can write it to a ref. But there’s usually a simpler way to structure the code so that you don’t have to.
The returned function which supposes to be alternative to componentWillUnmount also is not an exact equivalent, since the function will run every time the component will re-render and when the component will unmount.
Keep in mind that the mental model for effects is different from component lifecycles, and trying to find their exact equivalents may confuse you more than help. To get productive, you need to “think in effects”, and their mental model is closer to implementing synchronization than to responding to lifecycle events.
Example from Dan's blog:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
setTimeout(() => {
console.log(`You clicked ${count} times`);
}, 3000);
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}

If we use the class implementation:
componentDidUpdate() {
setTimeout(() => {
console.log(`You clicked ${this.state.count} times`);
}, 3000);
}

this.state.count always points at the latest count rather than the one belonging to a particular render.
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
useEffectHook ascomponentDidMount,componentDidUpdate, andcomponentWillUnmountcombined.
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.
const mouseMoveHandler = () => {}
componentDidMount() {
window.addEventListener('mousemove', mouseMoveHandler)
}
componentWillUnmount() {
window.removeEventListener('mousemove', mouseMoveHandler)
}
Hook equivalent of above code will be as follows:
useEffect(() => {
const mouseMoveHandler = () => {}
window.addEventListener('mousemove', mouseMoveHandler);
// returned function will be called on component unmount
return () => {
window.removeEventListener('mousemove', mouseMoveHandler)
}
}, [])
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.
I also faced with this problem and my solution is to create internal state that will be set when the DOM is re-rendered, see the following code to understand:
const [rerender, setRerender] = useState(); // or any state
const [afterRender, setAfterRender] = useState();// internal state
// (1)
useEffect(() => {
if (!afterRender) return;
// here DOM is loaded and you can query DOM elements
// then reset
setAfterRender(false);
}, [afterRender]);
useEffect(() => {
setAfterRender(true); // (1) will be called after DOM rendered
}, [rerender]); // or don't set any if you want to listen to all re-render events
return {
setRerender, // expose function trigger re-render data
}
Updated in 2021 (React version 17.0.2)
useEffect will run after DOM loaded and UI paint (just useLayoutEffect run before UI paint).
It run in order:
- DOM manipulation.
- useLayoutEffect
- UI paint
- useEffect
It works on both componentDidMount and componentDidUpdate lifecycle.
(1)first useEffect call is when component mount.
useEffect(() => {
console.log('log')
}, [test])
(2)if you pass second Array dependencies argument to useEffect and that argument wasn't empty, any time one of that dependency changes useEffect also recalled.
useEffect(() => {
console.log('log')
}, [test])
(3) if array of dependencies was empty, useEffect only one time called(after component mount for first time).
useEffect(() => {
console.log('log')
}, [])
(4)if useEffect implementing without second argument any time component state update useEffect also called.
useEffect(() => {
console.log('log')
})
for your situation you can check if test is 0 cancel execute useEffect body code like this:
function App() {
const [test, setTest ] = useState(0)
useEffect(() => {
if(test < 1) return;
console.log('log')
}, [test])
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
From https: Using the Effect Hook
What does useEffect do? By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we’ll refer to it as our “effect”), and call it later after performing the DOM updates.
Your component rendered, useEffect triggered, and your log ran.
It sounds like you might be confusing useEffect (which only takes a single argument, a callback) with other hooks that take multiple arguments.
---- EDIT (Comments-Related) ----
useEffect is not a direct replacement for class-based componentDidMount and componentDidUpdate methods, but because of the timing of when it runs it can often be used to replace those methods. See for further info. You may also want to read about the related useLayoutEffect hook.
If your intent is to hook up some logic "on load", such as an event handler, you don't need a hook to do that. Instead you just need a React event handler attribute, eg.
<option onChange={changeHandler} />
(See for further info.)