javascript - 涉及先前状态的计算何时需要功能更新?

标签 javascript reactjs react-hooks

根据documentation对于 useState React Hook:

If the new state is computed using the previous state, you can pass a function to setState. The function will receive the previous value, and return an updated value.

如此给定

const [count, setCount] = useState(initialCount);

你可以写

setCount(prevCount => prevCount + 1);

我理解将更新函数形式与 setState 一起使用的原因,因为可以批处理多个调用。然而

During subsequent re-renders, the first value returned by useState will always be the most recent state after applying updates.

所以我不清楚为什么上面的例子不能写成

setCount(count + 1);

(这是它在 Using the State Hook 中的显示方式)。

是否存在必须通过 useState Hook 使用功能更新才能获得正确结果的情况?

(编辑:可能与 https://github.com/facebook/react/issues/14259 有关)

最佳答案

仍然需要功能更新语法的主要场景是在异步代码中。想象一下,在 useEffect 中,您执行某种 API 调用,当它完成时,您更新一些状态,这些状态也可以通过其他方式更改。 useEffect 将在效果开始时关闭状态值,这意味着到 API 调用完成时,状态可能已过时。

下面的示例通过单击按钮触发两个在不同时间完成的不同异步进程来模拟此场景。一键立即更新计数;一个按钮在不同时间触发两个异步增量,而不使用功能更新语法(朴素按钮);最后一个按钮使用功能更新语法(Robust button)在不同时间触发两个异步增量。

您可以在 CodeSandbox 中使用它来查看效果。

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

function App() {
  const [count, setCount] = useState(1);
  const [triggerAsyncIndex, setTriggerAsyncIndex] = useState(1);
  const [triggerRobustAsyncIndex, setTriggerRobustAsyncIndex] = useState(1);
  useEffect(
    () => {
      if (triggerAsyncIndex > 1) {
        setTimeout(() => setCount(count + 1), 500);
      }
    },
    [triggerAsyncIndex]
  );
  useEffect(
    () => {
      if (triggerAsyncIndex > 1) {
        setTimeout(() => setCount(count + 1), 1000);
      }
    },
    [triggerAsyncIndex]
  );
  useEffect(
    () => {
      if (triggerRobustAsyncIndex > 1) {
        setTimeout(() => setCount(prev => prev + 1), 500);
      }
    },
    [triggerRobustAsyncIndex]
  );
  useEffect(
    () => {
      if (triggerRobustAsyncIndex > 1) {
        setTimeout(() => setCount(prev => prev + 1), 1000);
      }
    },
    [triggerRobustAsyncIndex]
  );
  return (
    <div className="App">
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      <br />
      <button onClick={() => setTriggerAsyncIndex(triggerAsyncIndex + 1)}>
        Increment Count Twice Async Naive
      </button>
      <br />
      <button
        onClick={() => setTriggerRobustAsyncIndex(triggerRobustAsyncIndex + 1)}
      >
        Increment Count Twice Async Robust
      </button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Edit Async state updates

另一种可能需要进行功能更新的情况是,如果多个效果正在更新状态(即使是同步的)。一旦一个效果更新了状态,另一个效果就会查看过时的状态。与异步场景相比,这种场景在我看来不太可能(并且在大多数情况下似乎是一个糟糕的设计选择)。

关于javascript - 涉及先前状态的计算何时需要功能更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54936481/

相关文章:

javascript - 如何从 Angular ui bootstrap 模式传递表单数据以查看

html - 在 react 中单击输入时显示叠加层

javascript - 在 Flatlist 项目中 react Native ref。仅返回最后一件商品

reactjs - React useEffect 钩子(Hook)是否保证按顺序执行?

javascript - React-Router-dom 和 useHistory。将新 URL 推送到历史记录并更改 URL 但页面没有更改

reactjs - React Hooks - 修改后的状态不会立即反射(reflect)出来

javascript - 在 ExpressJS 中导入模块时出错

javascript - 日志 = 新日志 ("debug");

reactjs - 如何在Dockerfile中使用CMD以分离模式在Docker中运行 `httpd`?

javascript - 使用 jQuery 将操作绑定(bind)到 iframe 内的元素