我对 React 何时决定重新渲染感到困惑。
function App() {
const [position, setPosition] = useState({x: 0, y: 0});
console.log(position);
const handleMove = (e) => {
const newPos = {x: e.clientX, y: e.clientY};
setPosition(newPos);
position.x = e.clientX;
position.y = e.clientY;
setPosition(position);
};
return (
<div onPointerMove={e => {
handleMove(e);
}}>
<h1>{position.x}</h1>
<h1>{position.y}</h1>
</div>
);
}
上面的代码来 self 的 React 组件函数。
在此代码中,您可以看到我正在改变用作状态的对象。虽然这是 React 禁止的方法,但我还是出于实验目的编写了这段代码。
对原始对象进行变异,然后调用setState函数后,不会发生重新渲染。为了强制重新渲染,我创建并分配了 newPos,然后重新分配了原始位置对象。
记录后,我注意到我的组件函数被不断调用,并且位置的 x 和 y 值不断更新。我还确认在返回的 JSX 对象中,position.x 和position.y 值正在发生变化。
但是,实际网页并未反射(reflect)这些更改。可能是什么原因? JSX返回后,比较现有DOM和新创建的DOM的过程中,重新渲染是否被拒绝?但 JSX 中的值(value)观已经明显改变。
有谁能给出明确的答案吗?
最佳答案
我认为理解这一点的最好方法是将其视为两个单独的行为,单独来看,每个行为都有意义:
- 状态正在更改(通过
setPosition(newPos)
),因此 React 安排一个新的调用来渲染组件。该新调用将在 batching 之后看到最新状态。 ,包括position
对象的当前内容。- 这看起来很合法,对吧?状态确实发生了变化,那么为什么 React 不应该重新调用组件函数呢?
- 当然,如果它足够聪明,可以在这种情况下跳过渲染组件(因为当它调用它时,状态已经变回原来的状态),那么可能会更好,但是什么也没有渲染错误。
- 但是状态改变最终并没有真正发生(因为
setPosition(position)
撤销了setPosition(newPos)
- 显然 React 从未检查过,更不用说存储了position
的内容,所以它只知道它是同一个对象); React 会检测到这一点,并跳过渲染后步骤:它不会递归遍历新结果和旧结果来查看更改的内容,它不会重新渲染任何属性已更改的子组件,正如您所见,它不更新 DOM。- 这看起来也合法,对吧?状态最终是相同的,那么为什么 React 应该重做所有这些工作呢?
- 当然,如果 React 没有这种优化,并且即使结果没有状态更改它也执行了所有渲染后步骤,那也可以OK ;但是,很高兴它有这种优化。
总的来说,我同意这些行为看起来很奇怪——如果组件函数最终没有实际使用结果,为什么还要费力地重新调用组件函数呢? ——但是,当然,这不是计算机“思考”的方式。渲染后步骤有一个“哦,等等,状态又变回来了!”优化,但他们不负责告诉渲染步骤如何完成其工作。
请注意,渲染后步骤通常比渲染本身要昂贵得多,因此即使在当前形式下,优化也是有用的;但如果 React 的 future 版本包含您显然期望的优化(在这种情况下完全跳过渲染函数),我一点也不感到惊讶。
顺便说一句,这一点:
<div onPointerMove={e => {
handleMove(e);
}}>
在每个渲染上指定一个新的/不同的pointerMove
处理程序。在您不仅仅是进行试验的真实组件中,您可能想要使用 the useCallback
hook以避免导致子组件不必要的重新渲染和(更糟糕的是)不必要的 DOM 更新。
关于javascript - 在React中,到底什么时候决定重新渲染?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77653432/