我一直在学习React,我读到从useEffect
返回的函数是用来做清理的,React在组件卸载时执行清理。
因此,我对其进行了一些试验,但在下面的示例中发现,每次组件重新呈现时都会调用该函数,而不是仅在组件从DOM卸载时调用,即每次组件重新呈现时调用console.log("unmount");
。
为什么会这样?
function Something({ setShow }) {
const [array, setArray] = useState([]);
const myRef = useRef(null);
useEffect(() => {
const id = setInterval(() => {
setArray(array.concat("hello"));
}, 3000);
myRef.current = id;
return () => {
console.log("unmount");
clearInterval(myRef.current);
};
}, [array]);
const unmount = () => {
setShow(false);
};
return (
<div>
{array.map((item, index) => {
return (
<p key={index}>
{Array(index + 1)
.fill(item)
.join("")}
</p>
);
})}
<button onClick={() => unmount()}>close</button>
</div>
);
}
function App() {
const [show, setShow] = useState(true);
return show ? <Something setShow={setShow} /> : null;
}
6条答案
按热度按时间bsxbgnwa1#
React在卸载组件时执行清理。
我不知道你从哪里读到的,但这句话是不正确的。当钩子的依赖关系改变,效果钩子需要用新的值再次运行时,React会执行清理。这种行为是故意为了保持视图对变化数据的React。离开官方的例子,让我们假设一个应用从朋友的个人资料订阅状态更新。作为一个伟大的朋友,你是,你决定与他们解除好友关系并与其他人建立好友关系。现在,应用需要取消订阅以前好友的状态更新,并听取新好友的更新。这是很自然的,而且很容易通过
useEffect
的工作方式实现。通过在依赖列表中包含朋友id,我们可以指示钩子只需要在朋友id改变时运行。
在你的例子中,你已经在依赖列表中指定了
array
,并且你在一个设定的时间间隔内改变数组,每次你改变数组,钩子都会重新运行。只需从依赖项列表中删除数组并使用回调版本的
setState
钩子就可以实现正确的功能。回调版本总是在状态的前一版本上操作,因此不需要在每次数组更改时刷新钩子。一些额外的反馈是直接在
clearInterval
中使用id,因为在创建cleanup函数时,该值会被关闭(捕获),没有必要将其保存到ref中。lh80um4z2#
React文档中有一个解释部分。
简而言之,原因是这样的设计可以防止陈旧数据和更新错误。
React中的
useEffect
钩子设计用于处理初始渲染和任何后续渲染(here's more about it)。效果是通过其依赖项控制的,而不是通过使用它们的组件的生命周期控制的。
任何时候效果的依赖关系改变,
useEffect
将清除以前的效果并运行新的效果。这样的设计更具可预测性-each render has its own independent (pure) behavioral effect。这确保了UI总是显示正确的数据(因为React的心智模型中的UI是特定渲染状态的屏幕截图)。
我们控制效果的方法是通过它们的依赖性。
为了防止清理在每个渲染上运行,我们只需要不改变效果的依赖关系。
具体来说,在您的示例中,由于
array
正在更改(即Object.is(oldArray, newArray) === false
),因此正在进行清理您将使用以下行引起此更改:
qlckcl4x3#
正如其他人所说,useEffect依赖于useEffect第二个参数中指定的"array"的变化,因此通过将其设置为空数组,这将有助于在组件挂载时触发useEffect一次。
此处的技巧是更改Array的先前状态。
见下文:
我派生了您的CodeSandbox用于演示:https://codesandbox.io/s/heuristic-maxwell-gcuf7?file=/src/index.js
o75abkj44#
查看代码,我可以猜到这是因为第二个参数
[array]
。您正在更新它,所以它将调用一个重新呈现。尝试设置一个空数组。每次状态更新都将调用重新渲染和卸载,并且该数组正在更改。
fykwrbwg5#
这似乎是意料之中的。根据这里的文档,
useEffect
在第一次渲染,每次更新和卸载后被调用。https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
头端
如果您熟悉React类生命周期方法,您可以将useEffect Hook看作componentDidMount、componentDidUpdate和componentWillUnmount之前的组合。
e4yzc0pl6#
这是一个显示渲染和效果顺序的Jest测试。
正如您可以从expect中看到的,一旦依赖项
foo
由于状态更新而更改,它将触发一个NEW渲染,然后是第一个渲染的cleanup函数。