Last updated: Apr 7, 2024
Reading timeยท3 min
To update state when props change in React:
useEffect
hook.useEffect
is rerun.import {useEffect, useState} from 'react'; function Child({parentCount}) { const [childCount, setChildCount] = useState(0); useEffect(() => { setChildCount(parentCount * 2); console.log('useEffect logic ran'); }, [parentCount]); // ๐๏ธ Add props as dependencies return ( <div> <button>Child count {childCount}</button> </div> ); } export default function Parent() { const [parentCount, setParentCount] = useState(0); return ( <div> <button onClick={() => setParentCount(current => current + 1)} > Parent count: {parentCount} </button> <hr /> <Child parentCount={parentCount} /> </div> ); }
We used the useEffect hook to update the state of a component when its props change.
useEffect(() => { setChildCount(parentCount * 2); console.log('useEffect logic ran'); }, [parentCount]); // ๐๏ธ Add props as dependencies
The logic in the useEffect
hook is rerun every time one of its dependencies
changes.
Every time the parentCount
prop changes, the useEffect
hook is rerun and we
use the setChildCount
function to update the state.
useEffect
hook.Note that the function we passed to the useEffect
hook is also invoked on the
initial render.
useEffect
on the initial renderIf you don't want to run the logic in your useEffect
hook on the initial
render, but only when the specific prop changes, use a ref
to return early on
the initial render.
import {useEffect, useRef, useState} from 'react'; function Child({parentCount}) { const [childCount, setChildCount] = useState(0); const isFirstRender = useRef(true); useEffect(() => { if (isFirstRender.current) { isFirstRender.current = false; return; // ๐๏ธ Return early if first render } setChildCount(parentCount * 2); console.log('useEffect logic ran'); }, [parentCount]); return ( <div> <button>Child count {childCount}</button> </div> ); } export default function Parent() { const [parentCount, setParentCount] = useState(0); return ( <div> <button onClick={() => setParentCount(current => current + 1)}> Parent count: {parentCount} </button> <hr /> <Child parentCount={parentCount} /> </div> ); }
We used a ref to exit early when the
useEffect
hook is run on mount.
Use this approach if you want to listen for props changes but need to skip the first render.
I've also written a detailed guide on how to call a child function from a parent component.
It should be noted that if you update the value of a prop and the prop is present in the hook's dependencies array, you would cause an infinite re-render loop.
Here is an example that demonstrates this issue.
import {useEffect, useState} from 'react'; function Child({parentCount, setParentCount}) { useEffect(() => { // ๐๏ธ This causes an infinite loop setParentCount(current => current + 1); console.log('useEffect logic ran'); }, [parentCount, setParentCount]); // ๐๏ธ parentCount is a dependency return ( <div> <button>Parent count {parentCount}</button> </div> ); } export default function Parent() { const [parentCount, setParentCount] = useState(0); return ( <div> <Child setParentCount={setParentCount} parentCount={parentCount} /> </div> ); }
The issue is that we added the parentCount
prop to the hook's dependencies
array but we are also updating its value in the hook.
Every time useEffect
is run, the value of parentCount
is changed, which
re-runs the hook again because parentCount
is in its dependencies array.
I've also written an article on how to update a component's state on click.