Updating state when props change in React

avatar

Borislav Hadzhiev

Mon May 02 20222 min read

Updating state when props change in React #

To update state when props change in React:

  1. Pass the props as dependencies to the useEffect hook.
  2. Every time the props change, the logic in useEffect is reran.
App.js
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> ); }

update state on props change

We used the useEffect hook to update the state of a component when its props change.

App.js
useEffect(() => { setChildCount(parentCount * 2); console.log('useEffect logic ran'); }, [parentCount]); // 👈️ add props as dependencies

The logic in the useEffect hook is reran every time one of its dependencies changes.

Every time the parentCount prop changes, the useEffect hook is reran and we use the setChildCount function to update the state.

Add all of the props you want to track to the dependencies array of your useEffect hook.

Note that the function we passed to useEffect hook is also invoked on the initial render.

If 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.

App.js
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 ran on mount.

Use this approach if you want to listen for props changes but need to skip the first render.

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.

App.js
import {useEffect, useState} from 'react'; function Child({parentCount, setParentCount}) { useEffect(() => { // 👇️ this causes 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 ran, the value of parentCount is changed, which re-runs the hook again because parentCount is in its dependencies array.

Use the search field on my Home Page to filter through my more than 1,000 articles.