首页  >  问答  >  正文

为什么需要使用功能更新形式的 React useState?

我正在阅读有关功能更新的 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粉155551728P粉155551728334 天前642

全部回复(2)我来回复

  • P粉818125805

    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

    回复
    0
  • P粉457445858

    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>
        </>
      );
    }

    回复
    0
  • 取消回复