Teh*_*ila 13 javascript infinite-loop reactjs react-hooks
假设我有这个简单的虚拟组件:
const Component = () => {
const [state, setState] = useState(1);
setState(1);
return <div>Component</div>
}
Run Code Online (Sandbox Code Playgroud)
在此代码中,我直接在组件主体中将状态更新为与之前相同的值。但是,即使值保持不变,这也会导致过多的重新渲染。
据我所知,在 中React.useState,如果状态值更新为与之前相同的值 - React 不会重新渲染组件。那么为什么会发生在这里呢?
但是useEffect,如果我尝试在组件主体中而不是直接在组件主体中执行类似的操作:
const Component = () => {
const [state, setState] = useState(1);
useEffect(() => {
setState(1);
}, [state])
return <div>Component</div>
}
Run Code Online (Sandbox Code Playgroud)
这不会导致任何无限循环,并且完全符合如果状态保持不变,React 不会重新渲染组件的规则。
所以我的问题是:为什么当我直接在组件主体中执行此操作时会导致无限循环,而在组件主体中却useEffect不会?
有人对此有一些“幕后”解释吗?
Dre*_*ese 10
第一个示例是无意的副作用,将无条件触发重新渲染,而第二个示例是有意的副作用,并允许 React 组件生命周期按预期运行。
我认为当 React 调用组件的渲染方法来计算下一个渲染周期的差异时,您正在将组件生命周期的“渲染阶段”与我们在React时的“提交阶段”期间通常所说的“渲染周期”混为一谈。已更新 DOM。
参见组件生命周期图:
请注意,在 React 函数组件中,整个函数体都是“render”方法,函数的返回值是我们想要刷新或提交到 DOM 的值。现在我们都应该知道,React 组件的“render”方法被认为是一个没有副作用的纯函数。换句话说,渲染的结果是状态和道具的纯函数。
在第一个示例中,排队状态更新是在正常组件生命周期(即 mount、update、unmount )之外调用的无意副作用。
const Component = () => {
const [state, setState] = useState(1);
setState(1); // <-- unintentional side-effect
return <div>Component</div>;
};
Run Code Online (Sandbox Code Playgroud)
它在“渲染阶段”触发重新渲染。React 组件永远没有机会完成渲染周期,因此没有什么可以“比较”或摆脱的,因此渲染循环发生。
另一个例子是排队状态更新是故意的副作用。该钩子在下一个 UI 更改刷新或提交到 DOM 后useEffect的渲染周期结束时运行。
const Component = () => {
const [state, setState] = useState(1);
useEffect(() => {
setState(1); // <-- intentional side-effect
}, [state]);
return <div>Component</div>;
}
Run Code Online (Sandbox Code Playgroud)
钩子大致useEffect是相当于类组件的、和生命周期方法的函数组件。无论依赖关系如何,在组件安装时都保证至少运行一次。该效果将运行一次并排队状态更新。React 将“看到”排队的值与当前状态值相同,并且不会触发重新渲染。componentDidMountcomponentDidUpdatecomponentWillUnmount
这里的要点是不要在React 组件中编写无意和意外的副作用,因为这会导致和/或导致错误代码。