当在一个异步操作之后需要setState多次时,render函数会执行多次。
import React, {useState} from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [num, setNum] = useState(1);
const [str, setStr] = useState('a');
const clickHandler = () => {
setTimeout(() => { // 模拟异步
setNum(2); // 这里触发的console.log('render')是没必要的
setStr('c'); // 我们仅仅需要这里触发console.log('render')
}, 1000);
}
console.log('render'); // 初始化时log一次,clickHandler触发后log两次。
return (
<div className="App">
<h1 onClick={clickHandler}>
<span>{num}</span>
<span>{str}</span>
</h1>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<App />,
rootElement
);
很显然我们仅仅需要第二次的render足以。
优化:
import React, {useState} from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [num, setNum] = useState(1);
const [str, setStr] = useState('a');
const clickHandler = () => {
setTimeout(() => { // 模拟异步
ReactDOM.unstable_batchedUpdates(() => { // 仅仅加了unstable_batchedUpdates
// 这里的两个setState会合并执行一次。
setNum(2);
setStr('c');
});
}, 1000);
}
console.log('render'); // 初始化时log一次,clickHandler触发后log一次。
return (
<div className="App">
<h1 onClick={clickHandler}>
<span>{num}</span>
<span>{str}</span>
</h1>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<App />,
rootElement
);
注意事项:
setTimeout(() => {
let tmp = 1;
ReactDOM.unstable_batchedUpdates(() => { //unstable_batchedUpdates 是一个同步操作
setNum(2);
setStr('c');
tmp = 3;
});
console.log(tmp) // 3
}, 1000);
const clickHandler = () => {
// setTimeout(() => { // 如果setState之前没有异步操作,则不需要unstable_batchedUpdates
// ReactDOM.unstable_batchedUpdates(() => {
// 这里的两个setState也会合并执行一次,react内部会自动处理。
// 但如果有异步,react无法侦测异步。所以需要自己加unstable_batchedUpdates。
setNum(2);
setStr('c');
// });
// }, 1000);
}
如果有用react-redux可以用batch代替unstable_batchedUpdates。batch底层还是调用的unstable_batchedUpdates。
import { batch } from 'react-redux';
- 此文的方法对redux的dispatch同样生效。dispatch底层其实也是用setState触发组建更新。