我正在阅读有关功能更新的 React Hook 文档并查看以下引用:
“+”和“-”按钮使用函数形式,因为更新 值基于之前的值
但我看不出需要功能更新的目的是什么,以及它们与直接使用旧状态计算新状态之间有什么区别。
为什么 React useState Hook 的更新器功能需要功能更新形式? 有哪些示例可以让我们清楚地看到差异(因此使用直接更新会导致错误)?
例如,如果我从文档中更改此示例
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> </> ); }
直接更新count
:
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> </> ); }
我看不出行为有任何差异,也无法想象计数不会更新(或者不是最新的)的情况。因为每当计数发生变化时,都会调用 onClick
的新闭包,捕获最新的 count
。
P粉8181258052023-10-23 10:02:44
我最近偶然发现了这个需求。例如,假设您有一个组件,它用一定数量的元素填充一个数组,并且能够根据某些用户操作附加到该数组(就像在我的例子中,我一次加载一个提要 10 个项目,因为用户不断向下滚动屏幕。代码看起来有点像这样:
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> ...
显然,您不能只将 feedItems 添加到 useEffect 挂钩中的依赖项列表中,因为您要在其中调用 setFeedItems,因此您会陷入循环。
功能更新来拯救:
useEffect(() => { if (data) { setFeedItems(prevItems => [...prevItems, ...data.items]); } }, [data]); // <--- all good now
P粉4574458582023-10-23 09:44:55
React 中状态更新是异步的。因此,当您下次更新时,count
中可能会有旧值。例如,比较这两个代码示例的结果:
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> </> ); }
和
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> </> ); }