javascript - 为什么每个状态钩子(Hook)更新都会导致异步代码中的单独重新渲染?

标签 javascript reactjs react-hooks

有两个useState组件中使用的钩子(Hook)。如果我们允许事件循环在修改状态之前“重排”,每个钩子(Hook)都会导致单独的重新渲染。

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

export default function App() {
  const [foo, setFoo] = useState("initial_foo");
  const [bar, setBar] = useState("initial_bar");

  const onAction = useCallback(async () => {
      await sleep(0); // this is the culprit
      setFoo("updated_foo");
      setBar("updated_bar");
  }, []);

  console.log(`rendering foo:${foo} bar:${bar}`);

  return (
    <>
      <button onClick={onAction}>run test</button>
      <div>{"foo: " + foo}</div>
      <div>{"bar: " + bar}</div>
    </>
  );
}
带有 await sleep(0) 的控制台输出:
rendering foo:initial_foo bar:initial_bar
rendering foo:updated_foo bar:initial_bar
rendering foo:updated_foo bar:updated_bar
如果我们删除 await sleep(0); ,一切正常:setFoosetBar导致一次重新渲染。
没有 await sleep(0) 的控制台输出:
rendering foo:initial_foo bar:initial_bar
rendering foo:updated_foo bar:updated_bar
The demo (在演示控制台中的输出是重复的,因为它开启了 React 严格模式)
  • 为什么会这样?
  • 它是错误还是功能?
  • 我们如何防止这种中间重新渲染?

  • 更新:
    问题已在 React project repo 中创建

    最佳答案

    在 promise 中(以及与此相关的计时器)setState是同步的。因此,您有两个重新渲染(首先是 setFoo,然后是 setBar),而不是组合(批处理)一个。
    这是一个例子:
    如果你点击 Promise 按钮​​中的 setState,你会得到 rerender在每个状态之后记录,但是如果您在 eventHandler 中单击 setState,您将首先获得两个 setState 日志,然后只有一个重新渲染。这种行为应该在 React 17 中改变,其中状态更新只会是异步的。

    function App() {
      const [foo, setFoo] = React.useState(); 
      const [bar, setBar] = React.useState();
      function handlerPromise() {
        Promise.resolve().then(() => {
          console.log("setFoo");
          setFoo({});
          console.log("setBar");
          setBar({});
        }) 
      }
          function handler() {
          console.log("setFoo");
          setFoo({});
          console.log("setBar");
          setBar({});
    
      }
      console.log("rerender");
      return (
      <div>
      <button onClick={handlerPromise}>setState in Promise</button>
      <button onClick={handler}>setState in an eventHandler</button>
      </div>
      )
    }
    
    
    
    ReactDOM.render(<App/>, document.getElementById("root"))
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.development.js"></script>
    
    <div id="root"></div>

    关于javascript - 为什么每个状态钩子(Hook)更新都会导致异步代码中的单独重新渲染?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63678705/

    相关文章:

    javascript - 如何使用 Array.prototype.filter 方法在 Express 应用程序中模拟 lodash .find 方法

    javascript - 使用 Google Apps 脚本 replaceText() 不适用于问号

    javascript - 返回 NaN 的数字?

    javascript - 为什么for .. in 得到了所有的属性,而for .. of 却没有得到所有的值?

    css - 如何使用 :global css classes - css modules

    javascript - 如何在写操作中使用firebase数据库推送方法生成的 key ?

    javascript - React jQuery 无法将元素识别为 Bootstrap 模式

    javascript - React Hooks 状态更改未传入正确的 props

    javascript - 无法使用react useState更新状态

    reactjs - 在 ReactJS 中使用复杂对象的 useState 无法按预期工作