I was reading the React Hook documentation on feature updates and looked at the following quote:
The " " and "-" buttons use functional form because of the update Value is based on previous value
But I don't see what is the purpose of requiring feature updates, and what's the difference between them and directly using the old state to calculate the new state.
Why does React useState Hook's updater function require a functional update form? Are there any examples where we can clearly see the difference (and therefore using direct update would cause an error)?
For example, if I change this example from the 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> </> ); }
Update directlycount
:
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 don't see any difference in behavior and can't imagine a situation where the count won't be updated (or isn't up to date). Because every time the count changes, a new closure of onClick
is called, capturing the latest count
.
P粉8181258052023-10-23 10:02:44
I recently stumbled upon this need. For example, let's say you have a component that fills an array with a certain number of elements and is able to append to that array based on some user action (like in my case I load a feed 10 items at a time because the user keeps Scroll down the screen. The code looks a little 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 dependencies list in the useEffect hook because you're calling setFeedItems in it, so you'll be stuck in a loop.
Feature updates to the rescue:
useEffect(() => { if (data) { setFeedItems(prevItems => [...prevItems, ...data.items]); } }, [data]); // <--- all good now
P粉4574458582023-10-23 09:44:55
State updates in React are asynchronous. Therefore, when you next update, there may be old values in count
. For example, compare the results of these two code examples:
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> </> ); }