根据 Dan Abramov 在 SO 上的回答,我发现了以下内容:
目前(React 16 及更早版本),默认情况下仅对 React 事件处理程序内部的更新进行批处理。 有一个不稳定的 API,可以在极少数情况下在您需要时强制在事件处理程序外部进行批处理。
他还在这个 Github 问题中提到:
https://github.com/facebook/react/issues/10231#issuecomment-316644950
在当前版本中,如果您位于 React 事件处理程序中,它们将被一起批处理。React 会批处理 React 事件处理程序期间完成的所有 setState,并在退出其自己的浏览器事件处理程序之前应用它们。
但事实是,这个片段似乎证明,
setState
内部的多个useEffect()
调用的更新是批量的。
问题
React 是否也总是对setState()
内的多个
useEffect
调用进行批量更新?还有什么地方可以做到这一点?
注意:根据他的回答,在下一个主要版本(可能是 v17)中,React 将默认在所有地方进行批处理。
SNIPPET: 在 useEffect()
内通过多个
setState()
调用进行批量更新
function App() {
console.log('Rendering app...');
const [myState,setMyState] = React.useState(0);
const [booleanState, setBooleanState] = React.useState(false);
console.log('myState: ' + myState);
console.log('booleanState: ' + booleanState);
React.useEffect(()=>{
console.log('Inside useEffect...');
setMyState(1);
setMyState((prevState) => prevState +1);
setMyState(3);
setMyState(4);
setMyState(5);
setBooleanState(true);
},[]);
return(
<div>App - Check out my console!</div>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
2021 年更新:React 18 即将发生的变化
请参阅 React 18 中有关此主题的 Dan Abramov 更新,该更新添加了自动批处理:https://github.com/reactwg/react-18/discussions/21
截至 2021 年,当前版本的 React 的答案,即 17.0.2 及以下版本。
基于以下代码和框:
批量setStuff
调用:
useEffect
同步块中
onClick={handlerFunction}
)
非批量调用每次都会触发重新渲染:
批量:
export default () => {
const [a, setA] = React.useState(0);
const [b, setB] = React.useState(0);
useEffect(() => {
setA(1); setB(1);
},[]);
return (
<div>
<p>A: {a}</p>
<p>B: {b}</p>
</div> );
};
单击此按钮只会重新渲染组件一次。
非批量:
export default () => {
const [a, setA] = React.useState(0);
const [b, setB] = React.useState(0);
useEffect(() => {
setTimeout(() => { setA(1); setB(1); }, 1000); }
, []);
return (
<div>
<p>A: {a}</p>
<p>B: {b}</p>
</div> );
};
const [dishes, dishesSet] = React.useState(state)
const [isConnectedToDB, isConnectedToDBSet] = React.useState(false)
React.useEffect(() => {
const databaseDishesRef = firebase.database().ref(process.env.FIREBASE_DISHES_REF)
databaseDishesRef.on('value', (snapshot) => {
const value = snapshot.val()
// isConnectedToDBSet(true) // this was not working
dishesSet(() => {
isConnectedToDBSet(true) // had to move it in here to batch the state updates
return Object.entries(value).map(([, dish]) => dish)
})
})
return () => {
databaseDishesRef.off('value')
}
}, [])
另一种选择是为 React.useEffect
编写一个
isConnectedToDB
,然后在每个 菜肴更新时触发它..