I'm reading React Hook documentation about functional updates and see this quote:
The ”+” and ”-” buttons use the functional form, because the updated value is based on the previous value
But I can't see for what purposes functional updates are required and what's the difference between them and directly using old state in computing new state.
Why functional update form is needed at all for updater functions of React useState Hook? What are examples where we can clearly see a difference (so using direct update will lead to bugs)?
For example, if I change this example from documentation
function Counter({initialCount}) { const [count, setCount] = useState(initialCount); return ( <> Count: {count} <button onClick={() => setCount(initialCount)}>Reset</button> <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button> </> ); }
to updating count
directly:
function Counter({initialCount}) { const [count, setCount] = useState(initialCount); return ( <> Count: {count} <button onClick={() => setCount(initialCount)}>Reset</button> <button onClick={() => setCount(count + 1)}>+</button> <button onClick={() => setCount(count - 1)}>-</button> </> ); }
I can't see any difference in behaviour and can't imagine case when count will not be updated (or will not be the most recent). Because whenever count is changing, new closure for onClick
will be called, capturing the most recent count
.
The React useState Hook allows us to track state in a function component. State generally refers to data or properties that need to be tracking in an application.
As mentioned in the beginning, it can accept a function instead of a value. This function will be called by React and the current version of the state will be passed as an argument. This way is called a “functional update.”
useState is a Hook (function) that allows you to have state variables in functional components. You pass the initial state to this function and it returns a variable with the current state value (not necessarily the initial state) and another function to update this value.
The answer: They're just queues setState , and React. useState create queues for React core to update the state object of a React component. So the process to update React state is asynchronous for performance reasons. That's why changes don't feel immediate.
State update is asynchronous in React. So it is possible that there would be old value in count
when you're updating it next time. Compare, for example, result of these two code samples:
function Counter({initialCount}) { const [count, setCount] = useState(initialCount); return ( <> Count: {count} <button onClick={() => setCount(initialCount)}>Reset</button> <button onClick={() => { setCount(prevCount => prevCount + 1); setCount(prevCount => prevCount + 1)} }>+</button> </> ); }
and
function Counter({initialCount}) { const [count, setCount] = useState(initialCount); return ( <> Count: {count} <button onClick={() => setCount(initialCount)}>Reset</button> <button onClick={() => { setCount(count + 1); setCount(count + 1)} }>+</button> </> ); }
I stumbled into a need for this recently. For example let's say you have a component that fills up an array with some amount of elements and is able to append to that array depending on some user action (like in my case, I was loading a feed 10 items at a time as the user kept scrolling down the screen. the code looked kind of like this:
function Stream() { const [feedItems, setFeedItems] = useState([]); const { fetching, error, data, run } = useQuery(SOME_QUERY, vars); useEffect(() => { if (data) { setFeedItems([...feedItems, ...data.items]); } }, [data]); // <---- this breaks the rules of hooks, missing feedItems ... <button onClick={()=>run()}>get more</button> ...
Obviously, you can't just add feedItems to the dependency list in the useEffect hook because you're invoking setFeedItems in it, so you'd get in a loop.
functional update to the rescue:
useEffect(() => { if (data) { setFeedItems(prevItems => [...prevItems, ...data.items]); } }, [data]); // <--- all good now
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With