【译】深入了解React 18的自动批处理

选中文字可对指定文章内容进行评论啦,绿色背景文字可以点击查看评论额。

 

React在事件处理或者内置的hook中,使用批处理合并多个状态的更新。避免了每次的状态更新都要重新渲染组件,从而提高应用的性能。

在React 17以及之前的版本,仅支持浏览器事件的批更新。React 18更新带来增强版的批处理,称为自动批处理Automatic Batching)。它对所有状态的更新,不管来源哪里,都可以使用批处理。

React 17的批处理

如上面提到的,React 17仅支持浏览器event和hook的批处理。因为状态更新只能在异步操作中,因此 原生事件处理不能做批处理。

为了更好地理解这一点,让我们考虑以下在 React 组件中进行状态更新的示例:

import { useState } from "react";
import "./App.css";const App = () => {  const [additionCount, setAdditionCount] = useState(0);
  const [subtractionCount, setSubtractionCount] = useState(0);
  
  console.log("Component Rendering");
  
  const handleOnClick = () => {
    setAdditionCount(additionCount + 1);
    setSubtractionCount(subtractionCount - 1);
  };
    
  return (
     <div>
         <button style = {{ width: "50%", height: "30%" }}
            onClick = {()=>{
               handleOnClick();
             }}
         >
            Click Me!
         </button><div>
            Add Count: {additionCount}
         </div>
         <div>
            Substraction Count: {substractionCount}
         </div></div>
  );
};
export default App;

在上面的示例中,当用户单击按钮时,将调用 handleOnClick() 函数。 它将在每次单击时执行两种状态更新。

如果观察浏览器控制台,将看到两次状态更新只打印一次“Component Rendering”消息。

现在你看到 React 批处理了两个状态的更新,且只重新渲染一次组件。

但是,如果我们在与浏览器无关的上下文中执行状态更新呢?

例如,考虑一个异步加载数据的 fetch() 调用:

// event handler
const handleOnClickAsync = () => {
fetch(“https://jsonplaceholder.typicode.com/todos/1").then(() => {
setAdditionCount(additionCount + 1);
setSubstractionCount(substractionCount — 1);
});
};

// html
<button style={{ width: “50%”, height: “30%” }} 
onClick ={() => {
handleOnClickAsync();
}}
>
Click Me Async Call!
</button>

上面示例是在 fetch() 函数的回调中执行状态更新。 如果执行示例后观察浏览器控制台,将看到 2 条消息。 这表明每次状态更新都会发生两次单独的重新渲染。

这有什么缺点?

对于小型应用,重新渲染过程可能不会产生重大影响。 但是,随着 Web 应用的增长,嵌套组件的数量将会增加。 因此,如果父组件执行非批量状态更新,则每次状态更新都会重新渲染整个组件树。

此行为可能会导致与应用程序速度相关的严重性能问题。 这就是 React 引入自动批处理的原因。

使用自动批处理

React v18 确保默认情况下从任何位置调用的状态更新都会被批处理。 这将批量更新状态,包括native事件处理程序、异步操作、timeout和interval。

让我们考虑一个简单的 React 应用程序来了解自动批处理的工作原理。

首先,让我们使用最新的 React 18 测试版(npm install react@beta react-dom@beta)创建一个 React 项目。

之后,必须更新 index.js 文件以使用 createRoot() API 来启用所有 React 18 功能以使用自动批处理。

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";const container = document.getElementById("root");
// Create a root.
const root = ReactDOM.createRoot(container);
// Render the top component to the root.
root.render(<App />);
reportWebVitals();

现在,让我们用 3 个事件侦听器更新 App.js 文件,其中一个事件侦听器包含使用以下方法调用的状态更新:

  • 事件处理程序(Event Handlers)
  • 异步操作
  • 超时(Timeout)
import logo from "./logo.svg";
import "./App.css";
import { useState } from "react";const App = () => {const [count, setCount] = useState(0);
const [clicked, setClicked] = useState(false);console.log("React 18 Application Re-Rendering");// click event
const handleClick = () => {
  // 1 Re-Render
  setClicked(!clicked);setCount(count + 1); 
};
  
// async operation
const handleAsyncClick = () => {      
  fetch("https://jsonplaceholder.typicode.com/todos/1").then(() => {
    // trigger 1 re-render due to React 18 Improved Batching
    setClicked(!clicked);
    setCount(count + 1);
  });
};
  
// timeout/interval
const handleTimeOutClick = () => {
  setTimeout(() => {
     // trigger 1 re-render due to React 18 Improved Batching
     setClicked(!clicked);
    setCount(count + 1); 
   });
 };
    
 return (
   <div className="App">
     <header className="App-header">
       <div> Count: {count} </div>
       <div> Clicked: {clicked} </div>
       <button onClick={handleClick}> Event Handler </button>
       <button onClick={handleAsyncClick}> Async Handler </button>
       <button onClick={handleTimeOutClick}> Timeout Handler </button>
     </header>
   </div>
 );
};
    
export default App;

当三个按钮都被单击时,浏览器控制台中会打印三个日志,尽管每个事件处理程序中都会发生两个状态更新。

上面的示例显示了一个最佳的重新渲染过程,其中每个事件仅会一次重新渲染,因为 React 批处理所有状态更新,而不管调用位置在哪。

如你所见,React 18 处理组件重新渲染的方式是一种改进。 React 17 有一个空白,非浏览器事件没有被批处理。 React 18 填补了这一空白,并通过减少不必要的重新渲染来提高应用程序性能。

如何停止自动批处理?

自动批处理确实是一个了不起的功能。 但是,在某些情况下,我们需要防止这种情况发生。 为此,React 在 react-dom 中提供了一个名为 flushSync() 的方法,允许我们为特定状态更新触发重新渲染。

flushSync何工作?

要使用flushSync,只需使用以下语法从 react-dom 导入它:

import { flushSync } from 'react-dom';

然后,在事件处理中调用该方法,并将状态更新放在 flushSync() 内。

const handleClick = () => {
flushSync(() => {
setClicked(!clicked);
// react will create a re-render here
});

setCount(count + 1);
// react will create a re-render here
};

当事件被调用时,React 将在 flushSync() 处更新 DOM 并在 setCount(count + 1) 处再次更新 DOM,从而避免批处理。

原文:https://blog.bitsrc.io/automatic-batching-in-react-18-what-you-should-know-d50141dc096e

版权声明:著作权归作者所有。

相关推荐

Python校验处理用户输入的方法

Python处理用户输入需要考虑几个问题:用户输入的数据是否合法用户输入非法数据是否会抛出异常用户输入非法数据后,给出友好提示并等待继续输入在Python里比较简单的做法是使用一直循环,如果用户输入错误数据,执行continue继续等待用户输入,如果用户输入正确数据,执行break跳出循环。用户输入可能会抛出异常这里需要使用try-catch来捕获用户输入非法数据导致的异常。python 

MyBatis执行MySql批量插入数据

MySQL批量插入数据语法为:insert into my_table(field1, field2, field3) values ("f1_vaule1","f2_vaule1","f3_vaule1"), ("f1_vaule2","f2_vaule2","f3_vaule2"), ("f1_vaule3","f2_vaule3","f3_vaule3

[译]了解AttnGAN:文字到图像转换器

文字到图像的转换一直令我着迷,最近的AttnGAN论文引起了我的注意。 在这篇文章中,我会试着提供一个直观的说明,希望让你好奇,进一步挖掘:-)。在我们开始实际模型之前,有一些先决条件:1、注意力为了避免重蹈覆辙(并推广我自己的课程),请看我以前的帖子,我在这里提供了一个关于“深度学习中的注意力”的小介绍。2、生成式对抗网络简单地说,一个GAN是两个网络的组合:一个生成器(从噪声中产生有

[译]了解AttnGAN:文字到图像转换器

文字到图像的转换一直令我着迷,最近的AttnGAN论文引起了我的注意。 在这篇文章中,我会试着提供一个直观的说明,希望让你好奇,进一步挖掘:-)。在我们开始实际模型之前,有一些先决条件:1、注意力为了避免重蹈覆辙(并推广我自己的课程),请看我以前的帖子,我在这里提供了一个关于“深度学习中的注意力”的小介绍。2、生成式对抗网络简单地说,一个GAN是两个网络的组合:一个生成器(从噪声中产生有

React Apollo入门

入门Apollo Client最简单的方法是使用Apollo Boost,它会给客户端配置推荐的设置。使用Apollo Boost创建app的内容包括缓存,本地状态管理以及错误处理。安装首先安装以下的软件包:npm install apollo-boost react-apollo graphql-tag graphq

理解React Native的Props和State

Props和State是React Native分别用来控制组件中两种不同类型的数据。Propsprops是Properties的简写,称为属性。props的特点是在组件内它是不可变,或者说不能被修改。在React数据流是单向的,从父组件到子组件的方向传递。props就是组件从父组件接收的数据,在组件内部是不能对它修改。props不可变的特性是有助于我们写可复用的组件。我们只需要对一个组

Android Studio 3配置自动导入包Auto Import

在Windows Android Studio提示导入的快捷键:Ctrl+Alt +O。配置自动导入Auto ImportFile -> Settings -> Editor -> General -> Auto Import在右侧窗口Java配置下:把Insert imports on paste的值改为All勾选Add unambigious imports on th

Spring Security自定义验证失败处理器AuthenticationFailureHandler

Spring Security的AuthenticationManager用来处理验证的请求,处理的结果分两种:验证成功:结果由AuthenticationSuccessHandler处理验证失败:结果由交给AuthenticationFailureHandler处理。在Spring Security内置了几种验证失败处理器:DelegatingAuthenticationFailureHandl

windows批处理操作bat命令

Windows环境下运行bat指令-Windows下 初始化MySQL SQL 文件@echo off start cmd /k "echo initmysql && title init-mysql && cd/d D:softmysql-5.7.26-winx64bin && mysql -h localhost -uroot -

JPA实体状态深入理解

我们在学习JPA实体状态的时候,常常会问,JPA的实体有多少状态呢?相信这个问题不难回答:瞬时态(transient)托管态(persistent)游离态(detached)移除态(removed)注意:这里最后一个移除态,有的时候也叫删除态(deleted),至于它和移除态有啥区别,暂时没有想到,如果您对此有更加深刻的理解,请留言回复。为什么会有这四种状态呢?啥,这个也有为啥,网上不是都这么说的