Sve*_*ies 4 javascript reactjs react-hooks use-effect
假设我有一个父组件,它呈现一个子组件,而子组件所做的就是保持某种状态,并在其自身状态发生变化时触发 onChange 处理程序。请参阅下面的代码或 CodeSandbox。
这段代码进入无限循环,我想摆脱这种行为。
可能的解决方案,但我不喜欢的解决方案:
1:我可以将所有状态放在父组件中,并用父组件控制子组件,但这并不是我真正想要的。在现实生活中,我的子组件不仅仅负责一个简单的计数器,而且我想从父组件中轻松使用它。子组件内部有复杂的行为,我想向父组件传达一些简单的更改。或者这在 React 中是绝对不能做的吗?(保持子级状态并触发父级状态更新的更改)我想说这不一定吗?
2:我还可以从handleInternalChange 处理程序中触发onChange 处理程序。而且,也不是我想要的。在现实生活中,我的子组件将从组件本身的几个不同位置进行更新,因此状态更改是观察和触发 onChange 父组件的最优雅的方式。
3:我可以省略 useEffect 依赖项数组中的 onChange 依赖项。不推荐这样做,React社区参考了这个解释。我理解,只是我觉得这种情况是例外?** 另外,我使用 CRA,它带有开箱即用的很棒的 linter 等,如果我从依赖项中删除 onChange 处理程序,linter 会抱怨它,而且我不喜欢开始制定自己的班轮规则。对于像我这样的简单用例,社区设置的 linter 应该可以正常工作。
我认为会发生什么 我认为会发生的是,父级被更新,然后重新渲染整个事物,并且不知何故, onChange 处理程序也被“更改”。据我所说,该函数实际上并没有改变,但 React 认为(或知道)它改变了,因此它在子组件中再次触发 useEffect 调用,然后无限循环诞生。
但是,就我而言, onChange 函数没有改变。那么为什么会触发 useEffect 调用呢?我怎样才能防止这种情况发生?
import React, { useState, useEffect } from "react";
const Comp = ({ onChange }) => {
const [internalState, setInternalState] = useState(0);
const handleChange = () => {
setInternalState(internalState + 1);
};
useEffect(() => {
const result = internalState.toString();
onChange(result);
}, [internalState, onChange]);
return (
<div onClick={handleChange}>
CLICK ME
<div>{`INTERNAL NUM: ${internalState}`}</div>
</div>
);
};
export default function App() {
const [state, setState] = useState("");
const handleChange = () => setState(state + 1);
return (
<div className="App">
<h3>{`STATE: ${state}`}</h3>
<Comp onChange={handleChange} />
</div>
);
}
Run Code Online (Sandbox Code Playgroud)
** 经过一段时间后,在其他情况下,onChange 属性当然可以更改,只需为该属性分配不同的函数即可。所以这个规则对我来说是非常清楚的。只是(如上一段所述)为什么在这种情况下它的行为会发生变化?因为我的功能根本没有改变。
您应该使用 useCallback 挂钩包装 handleChange 方法,以便它被创建一次。
const handleChange = useCallback(() => setState(state + 1),[]);
Run Code Online (Sandbox Code Playgroud)
发生无限循环是因为您onChange在组件中添加了方法作为 useEffect 的依赖项<Comp />。
useEffect 接受一系列依赖项,并在依赖项之一发生更改时运行效果。
由于您已添加 onchange 处理程序作为依赖项,因此每次父组件重新渲染时,handleChange都会创建一个新的方法实例,该实例不等于以前的 handlechange 方法。
组件渲染流程将是这样的:
<App />组件创建handleChange方法并将其传递给<Comp /><Comp />将在初始渲染后被调用,并从那里<App />调用组件的 handleChange 方法。<App />并重新渲染它。重新渲染 handleChange 的新实例时,会创建并将 onChange 属性传递给<Comp />组件。为了防止这种情况,handleChange方法应该用useCallback包装。传递给 useCallback 钩子的回调函数将被记忆,因此当子组件比较新旧 prop 时,它们保持相等。
{} === {} // false
[] === [] // false
(() => {}) == (() => {}) //false
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2940 次 |
| 最近记录: |