React函数式组件中实现「批量更新」的两种方式
首先看下下面的这段代码,在获取到数据后,分别更新了两个 State ,线上你是不是也是这样写的?
function NormalComponent() {
const [list, setList] = useState(null)
const [info, setInfo] = useState(null)
useEffect(() => {
;(async () => {
const data = await getApiData()
setList(data.list)
setInfo(data.info)
}, [])
return <div>非批量更新组件时 Render 次数:{renderOnce("normal")}</div>
如果你也是这样写的,那么,这个组件会在 setList(data.list) 后触发组件的 Render 过程,然后在 setInfo( http:// data.info ) 后再次触发 Render 过程,造成性能损失。那么应该怎么写才能实现批量更新呢?下面介绍两种方式:
1、将多个 State 合并为单个 State。例如使用 如下代码 替代 list 和 info 两个 State。
const [data, setData] = useState({ list: null, info: null })
2、使用 React 官方提供的 unstable_batchedUpdates 方法,将多次 setState 封装到 unstable_batchedUpdates 回调中,修改后代码如下。
function BatchedComponent() {
const [list, setList] = useState(null)
const [info, setInfo] = useState(null)
useEffect(() => {
;(async () => {
const data = await getData()
unstable_batchedUpdates(() => {
setList(data.list)
setInfo(data.info)
}, [])
return <div>批量更新组件时 Render 次数:{renderOnce("batched")}</div>
为啥回这样呢?
主要因为批量更新 setState 时,多次执行 setState 只会触发一次 Render 过程。相反在立即更新 setState 时,每次 setState 都会触发一次 Render 过程,就存在性能影响。
附验证demo:
import { useEffect, useState } from "react";
import { unstable_batchedUpdates } from "react-dom";
const renderCntMap = {};
function renderOnce(name) {
return (renderCntMap[name] = (renderCntMap[name] || 0) + 1);
async function getData() {
return {
list: [1, 2, 3, 4],
info: { name: "MoonBall" }
function NormalComponent() {
const [list, setList] = useState(null);
const [info, setInfo] = useState(null);
useEffect(() => {
(async () => {
const data = await getData();
setList(data.list);
setInfo(data.info);
})();
}, []);
return <div>非批量更新组件时 Render 次数:{renderOnce("normal")}</div>;
function BatchedComponent() {
const [list, setList] = useState(null);
const [info, setInfo] = useState(null);
useEffect(() => {
(async () => {
const data = await getData();
unstable_batchedUpdates(() => {
setList(data.list);
setInfo(data.info);
})();
}, []);
return <div>批量更新组件时 Render 次数:{renderOnce("batched")}</div>;
export default function App() {
return (
<div className="App">
<h1>批量更新,验证 Render 次数</h1>