在事件处理程序中进行 setState 调用后,React 何时重新渲染

IT技术 javascript reactjs react-hooks setstate
2021-04-30 10:45:29

来自 React 文档:

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已完成运行?我的意思是立即,比如立即同步?

从我构建的这个片段来看,这似乎是真的。React 将完成同步应用更新(重新渲染)handleClick如果我的假设有误,请纠正我。

见下面的片段:

  • 尽可能快地点击 3 次
  • handleClick将调用 asetState并将记录当前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个回答

一旦handleClick完成,是否保证立即重新渲染handleClick已完成运行?我的意思是立即,比如立即同步

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

在 Twitter 上从 Dan Abramov 那里了解到这一点(他使用旧的术语来描述事件)。


¹ 编辑:事实上,在他的回答中,约瑟夫 D. 指出丹阿布拉莫夫的评论说,目前是这样,但也说“这是实现细节,可能会在未来版本中改变”

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

是的onClick

正如丹·阿布拉莫夫所指出的

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