在useEffect挂钩中对多个setState()调用进行批处理更新

时间:2019-07-04 09:39:39

标签: javascript reactjs react-hooks setstate use-effect

关于丹·阿布拉莫夫(Dan Abramov)在此处的回答,我发现了以下内容:

Does React keep the order for state updates?

  

当前(第16和更早的版本),默认情况下仅批处理React事件处理程序中的更新。有一个不稳定的API可以在极少数情况下在需要时强制在事件处理程序之外进行批处理。< / p>

他还在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"/>

3 个答案:

答案 0 :(得分:6)

好问题。这是完成@FranklinOcean答案的其他信息。

当前版本的React答案,即16.8.3。

基于on the following codesandbox


批量setStuff通话:

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

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

  • 任何异步代码(上述任何一种情况下的Promise / async函数)
  • 非综合事件(事件由外部库响应管理)
  • 其中包括XHR或其他网络回调

我将尝试使用React的未来版本重新运行沙箱,以了解其运行情况!

答案 1 :(得分:1)

如果状态更新直接发生,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> ); 
}; 

答案 2 :(得分:0)

只是想加入一个对我有用的“黑客”

@app.post("video/{video_id}")
async def do_something():
    # do something
    save_to_db(video_id)
    

另一种方法是编写一个“Reac.useEffect 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') } }, []) isConnectedToDB”,然后在每次菜品更新时触发它..