在React中进行批处理
React 18即将推出Automatic Batching ,以减少渲染,同时还有其他新的功能,如SSR对Suspense的支持。对并发功能的支持将有助于改善用户体验。在以前的版本中,React也有批处理功能。但是,随着自动批处理的引入,重新渲染的行为将是统一的。
在React中,如果状态或道具的值发生变化,组件会重新渲染。在类组件中,状态更新可以使用setState 。在功能组件中,状态值可以通过useState 返回的函数进行更新。
React在更新组件状态时执行Batching,以提高性能。Batching意味着将多个状态更新分组为一个重新渲染。让我们看看在v18 之前,Batching是如何工作的,以及在v18 中带来了哪些变化。
React 18之前的批处理
React默认只在事件处理程序中执行批量更新。
因此,setState只在事件处理程序中是异步的。但是,在异步函数中是同步的,比如Promises、setTimeout
class App extends React.Component {
constructor() {
super();
this.state = {
name: 'setState',
};
this.handleClickSync = this.handleClickSync.bind(this);
this.handleClickAsync = this.handleClickAsync.bind(this);
}
handleClickSync() {
Promise.resolve().then(() => {
this.setState({ name: 'sync' });
console.log('state', this.state.name); // sync
});
}
handleClickAsync() {
this.setState({ name: 'Async' });
console.log('state', this.state.name); // sync (value of previous state)
// after re-render state value will be `Async`
}
render() {
return (
<div>
<h1 onClick={this.handleClickSync}>Sync setState</h1>
<h1 onClick={this.handleClickAsync}>Async setState</h1>
</div>
);
}
}
如果n 状态更新出现在异步函数中,React会重新渲染组件n ,每次渲染更新一个状态:
const App = () => {
const [counter1, setCounter1] = useState(0);
const [counter2, setCounter2] = useState(0);
const handleClickWithBatching = () => {
setCounter1((count) => count + 1);
setCounter2((count) => count + 1);
};
const handleClickWithoutBatching = () => {
Promise.resolve().then(() => {
setCounter1((count) => count + 1);
setCounter2((count) => count + 1);
});
};
console.log('counters', counter1, counter2);
/*
On click of Single re-render
conuters: 1 1
On click of Multiple re-render
conuters: 2 1
counters: 2 2
*/
return (
<div className="App">
<h2 onClick={handleClickWithBatching}>Single Re-render</h2>
<h2 onClick={handleClickWithoutBatching}>Multiple Re-render</h2>
</div>
);
};
然而,强制批处理可以在不稳定的API的帮助下实现ReactDOM.unstable_batchedUpdates
import { unstable_batchedUpdates } from 'react-dom';
const handleClickWithoutBatching = () => {
Promise.resolve().then(() => {
unstable_batchedUpdates(() => {
setCounter1((count) => count + 1);
setCounter2((count) => count + 1);
});
});
};
/*
On click of Single re-render
conuters: 1 1
On click of Multiple re-render
counters: 2 2
*/
Note: The API is unstable in the sense that React might remove this API once uniformity is brought in the functionality of Batching
React 18中的批处理
React 18在createRoot API的帮助下执行自动批处理:
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
setState是异步的,即使是在异步fucntions里面。
批处理将在整个组件中进行,无论其来源如何:
const handleClick = () => {
setCounter1((count) => count + 1);
setCounter2((count) => count + 1);
};
const handleClick = () => {
Promise.resolve().then(() => {
setCounter1((count) => count + 1);
setCounter2((count) => count + 1);
});
};
//In both cases, component will be re-rendered only once
人们可以选择不使用Batching。ReactDOM.flushSync
import { flushSync } from 'react-dom';
const handleClick = () => {
flushSync(() => {
setCounter1((count) => count + 1);
});
flushSync(() => {
setCounter2((count) => count + 1);
});
};
ReactDOM.unstable_batchedUpdates API在React 18中仍然存在,但它可能会在未来的主要版本中被删除。
学习愉快