对 useEffect 挂钩内的多个 setState() 调用做出批量更新反应

问题描述 投票:0回答:3

根据 Dan Abramov 在 SO 上的回答,我发现了以下内容:

React 是否保持状态更新的顺序?

目前(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"/>

javascript reactjs react-hooks setstate use-effect
3个回答
11
投票
好问题。以下是完成@FranklinOcean 答案的附加信息。

2021 年更新:React 18 即将发生的变化

请参阅 React 18 中有关此主题的 Dan Abramov 更新,该更新添加了自动批处理:

https://github.com/reactwg/react-18/discussions/21

截至 2021 年,当前版本的 React 的答案,即 17.0.2 及以下版本。

基于

以下代码和框


批量setStuff

调用:

    同步内联组件功能块(我认为这相当于让一个效果在其他效果之前运行并且没有依赖性)
  • useEffect
     同步块中
  • 在合成事件处理程序中同步(由react管理,例如
  • onClick={handlerFunction}

非批量调用每次都会触发重新渲染:

    任何异步代码(上述任何用例中的 Promise/异步函数)
  • 非合成事件(在反应库外部管理的事件)
  • 包括 XHR 或其他网络回调

我将尝试使用未来版本的 React 重新运行沙箱,看看效果如何!


4
投票
如果状态更新直接发生,React 将批量更新。

批量:

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> ); };
    

0
投票
只是要加入一个对我有用的“黑客”

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
,然后在 
每个 菜肴更新时触发它..

© www.soinside.com 2019 - 2024. All rights reserved.