在事件处理程序内进行setState调用后,React何时重新呈现

时间:2019-09-27 07:46:20

标签: javascript reactjs react-hooks setstate

从React DOCS:

https://reactjs.org/docs/state-and-lifecycle.html

  

状态更新可能是异步的

     

React可以将多个setState()调用批处理为一个更新,以提高性能。

这很有道理。如果您具有类似下面的功能,则在每次setState调用中重新渲染都是非常低效的

const [state1,setState1] = useState(false);
const [state2,setState2] = useState(false);
const [state3,setState3] = useState(false);

function handleClick() {
  setState1(true);
  setState2(true);
  setState3(true);
}

因此,在上述情况下,我期望React将所有3个setState调用分批处理成一个单独的重新渲染。确实做到了!

但是我想知道的是:

handleClick完成后,是否可以立即 handleClick完成重渲染?我的意思是立即字面意思,像立即同步一样?

从我构建的此代码段来看,这似乎是对的。 handleClick完成后,React将同步应用更新(重新渲染)。如果我猜错了,请纠正我。

请参见下面的代码段

  • 最快点击3次
  • handleClick将调用setState并记录当前的props.counter
  • App上有一个昂贵的循环,因此重新渲染将花费很长时间
  • 您将能够以比React能够重新渲染整个应用程序的速度更快的速度点击
  • 但是您会发现props.counter每次都是不同的,即使您快速地多次单击,也没有任何重复。
  • 这意味着处理完第二次点击(由于昂贵的循环会花一些时间)后,React已经重新渲染了整个内容,并且handleClick函数已经用新值重新创建了来自props.counter更新状态的counter
  • 尝试快速单击5次,您会发现行为相同。

问题

在事件处理函数内进行setState调用后,一旦该处理函数运行完毕,是否可以保证在处理函数完成后立即(同步)进行重新渲染?

function App() {
  console.log("App rendering...");
  const [counter, setCounter] = React.useState(0);

  // AN EXPENSIVE LOOP TO SLOW DOWN THE RENDER OF 'App'
  
  for (let i = 0; i < 100000; ) {
    for (let j = 0; j < 10000; j++) {}
    i = i + 1;
  }

  return <Child counter={counter} setCounter={setCounter} />;
}

function Child(props) {
  console.log("Child rendering...");
  
  // THIS FUNCTION WILL CALL 'setState'
  // AND WILL ALSO LOG THE CURRENT 'props.counter'
  
  function handleClick() {
    props.setCounter(prevState => prevState + 1);
    console.log(props.counter);
  }

  return (
    <React.Fragment>
      <div>Counter: {props.counter}</div>
      <button onClick={handleClick}>Click</button>
    </React.Fragment>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<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"/>

2 个答案:

答案 0 :(得分:5)

  

handleClick完成后,是否保证立即handleClick完成重新渲染?我的意思是立即立即,就像立即同步

对于某些事件,包括click,React保证在发生另一个事件之前将重新渲染组件。 (对于其他事件,例如mousemove则不是这样。)您可以找到哪些事件是here。当前的术语似乎是DiscreteEvent是此保证存在的术语,而UserBlockingEvent是其不能保证的术语。在当前的实现中,很可能意味着它是在事件处理结束时同步完成的,¹但我认为保证并不那么具体。

我从Dan Abramov on Twitter(他使用较旧的术语来表示事件)中学到了这一点。


¹编辑:实际上,在his answer中,Joseph D.指向a comment by Dan Abramov表示,目前,这还表示”“这是实现细节,将来可能会改变版本”

答案 1 :(得分:3)

  

是否保证在处理程序完成后立即(同步)进行重新渲染?

onClick是。

As pointed by Dan Abramov

  

React批处理在React事件处理程序中完成的所有setState,并在退出自己的浏览器事件处理程序之前应用它们