在事件处理程序内部进行 setState 调用后,React 何时重新呈现
When does React re-render after a setState call made inside an event handler
来自 React 文档:
https://reactjs.org/docs/state-and-lifecycle.html
State Updates May Be Asynchronous
React may batch multiple setState() calls into a single update for performance.
这完全有道理。如果你有类似下面的函数,在每次 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
将调用 setState
并将记录当前的 props.counter
App
上有一个昂贵的循环,所以重新渲染需要很长时间
- 您点击的速度将比 React 重新渲染整个应用程序的速度快得多
- 但是你会看到
props.counter
每次都不一样,没有任何重复,即使你点击了很多次真的很快
- 这意味着一旦你的第二次点击被处理(这将需要一段时间,因为昂贵的循环),React 已经重新渲染了整个东西并且
handleClick
函数已经用来自 counter
更新状态的 props.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"/>
Once handleClick
has completed, is a re-render guaranteed to happen immediatelly handleClick
has done running? I mean literally immediately, like synchronously immediately
对于某些事件,包括 click
,React 保证在另一个事件发生之前重新渲染组件。 (mousemove
等其他事件并非如此。)您可以找到哪些事件是哪些 here。当前的术语似乎是 DiscreteEvent
s 是存在此保证的那些,而 UserBlockingEvent
s 是没有保证的那些。在当前的实现中,这很可能意味着它是在事件处理结束时同步完成的¹,但我认为保证没有那么具体。
我是从 Dan Abramov on Twitter 那里学来的(他对事件使用了较旧的术语)。
¹ 编辑:事实上,在 Joseph D. points to a comment by Dan Abramov 中说现在是,但也说 "This is implementation detail and may change in future versions".
is it guaranteed that a re-render will occur immediately (synchronously) after that completion of the handler?
是 onClick
。
React batches all setStates done during a React event handler, and applies them just before exiting its own browser event handler.
来自 React 文档:
https://reactjs.org/docs/state-and-lifecycle.html
State Updates May Be Asynchronous
React may batch multiple setState() calls into a single update for performance.
这完全有道理。如果你有类似下面的函数,在每次 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
将调用setState
并将记录当前的props.counter
App
上有一个昂贵的循环,所以重新渲染需要很长时间- 您点击的速度将比 React 重新渲染整个应用程序的速度快得多
- 但是你会看到
props.counter
每次都不一样,没有任何重复,即使你点击了很多次真的很快 - 这意味着一旦你的第二次点击被处理(这将需要一段时间,因为昂贵的循环),React 已经重新渲染了整个东西并且
handleClick
函数已经用来自counter
更新状态的props.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"/>
Once
handleClick
has completed, is a re-render guaranteed to happen immediatellyhandleClick
has done running? I mean literally immediately, like synchronously immediately
对于某些事件,包括 click
,React 保证在另一个事件发生之前重新渲染组件。 (mousemove
等其他事件并非如此。)您可以找到哪些事件是哪些 here。当前的术语似乎是 DiscreteEvent
s 是存在此保证的那些,而 UserBlockingEvent
s 是没有保证的那些。在当前的实现中,这很可能意味着它是在事件处理结束时同步完成的¹,但我认为保证没有那么具体。
我是从 Dan Abramov on Twitter 那里学来的(他对事件使用了较旧的术语)。
¹ 编辑:事实上,在
is it guaranteed that a re-render will occur immediately (synchronously) after that completion of the handler?
是 onClick
。
React batches all setStates done during a React event handler, and applies them just before exiting its own browser event handler.