【问题标题】:Are setState updates in async event handlers batched?异步事件处理程序中的 setState 更新是批处理的吗?
【发布时间】:2020-09-26 16:36:14
【问题描述】:

根据我在 react 版本 16(当前)及以下版本中的理解,setState 调用是批处理的,如果它们是在组件生命周期事件或事件处理程序中进行的。否则,为了批量调用,可以使用 ReactDOM.unstable_batchedUpdates 中的一个选项。

如果事件处理程序是 async 函数,浏览器将触发事件处理程序但随后将返回一个承诺,因此实际的事件处理程序 Promise 回调将不会运行,直到选择下一个微任务在事件循环中。换句话说,setState 更新实际上不会发生在即时事件处理程序中。

这是否意味着如果我们希望在事件处理程序中批处理 setState 更新,我们需要选择 ReactDOM.unstable_batchedUpdates

【问题讨论】:

    标签: javascript reactjs async-await dom-events setstate


    【解决方案1】:

    经过研究,我相信答案是async 事件处理程序的initial 部分(最终转换为在后台返回的Promise 的执行程序函数)将批量处理setState 更新,但在任何await 调用之后都不会更新。

    这是因为async函数体中的所有内容之前第一个await被翻译为执行器函数,该函数在事件的浏览器事件处理程序中执行,但之后的所有内容都结束了作为初始执行器函数的链式Promise 回调,这些链式回调在微任务队列上执行。

    这都是因为async () => {} 被翻译成return new Promise().then() 之类的东西,其中每个都是在await 语句之后为代码创建的回调。

    const onClick = async e => {
      // 1 and 2 will be batched
      setState(1)
      setState(2)
    
      await apiCall()
    
      // 3 and 4 will not be batched
      setState(3)
      setState(4)
    }
    

    【讨论】:

      【解决方案2】:

      下面的调用将由 React 批处理,并导致单次重新渲染。

      const onClick = (e) => {
          setHeader('Some Header');
          setTitle('Some Tooltip');
      };
      

      如果没有 ReactDOM.unstable_batchedUpdates,React 会进行 2 次 sync 调用来重新渲染组件。现在它将使用此 API 进行一次重新渲染。

      const onClick = (e) => {
          axios.get('someurl').then(response => {
              ReactDOM.unstable_batchedUpdates(() => {
                  setHeader('Some Header');
                  setTitle('Some Tooltip');
              });
          });
      };
      

      补充说明:

      1. 我们在项目中使用过它一次,它可以无缝运行。但我个人更喜欢将状态作为对象并立即更新事物,而不是这种方法。但我知道这并不总是可行的。
      2. Here 我们可以看到它处于实验模式,所以不确定你是否应该在生产中使用它。

      更新

      根据 OP 评论,下面是 JavaScript 中异步等待的版本。

      const onClick = async e => {
      
        setState(1);
        setState(2);
        await apiCall();
      
        ReactDOM.unstable_batchedUpdates(() => {
            setState(3);
            setState(4);
        });
      }
      

      上面的代码会触发 2 次重新渲染。一次更新 1/2,另一次更新 3/4。

      【讨论】:

      • 这不涉及async 事件处理程序部分。
      • @AdamThompson 对于在事件处理程序返回(异步)之后执行的 setter,答案是否定的,除非您将其包装在 ReactDOM.unstable_batchedUpdates 中。
      • API 的事件处理程序是 async。您也可以解决直接承诺。我认为这应该掩盖你的要求。如果我在这里误解了任何内容,请提供一点澄清。
      • 查看我发布的答案,答案是 async 事件处理程序中第一个 await 之前的任何内容都应该被批处理 - 如果我遗漏任何内容,请纠正我。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-06
      • 1970-01-01
      • 2017-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多