useEffect 中是否可以有条件地进行不同的清理?
Can different cleanups be done conditionally in useEffect?
所以,我有一个 useEffect
这样的:
useEffect(()=>{
if(foo) {
// do something
return () => { // cleanup function }
}
}, [foo])
此处,即使执行了 if
块,也不会调用 cleanup
函数。但是如果我修改效果为:
useEffect(()=>{
if(foo) {
// do something
}
return () => { // cleanup function }
}, [foo])
有效。
那么,是否仅当 return
是 useEffect
的最后一条语句时才进行清理,还是我遗漏了什么?
当您 return 将回调中的函数传递给 useEffect
时,returned 函数将在组件从 UI 中删除之前被调用。
通常我们会在 componentWillUnmount
中对基于 class 的组件进行清理。假设您想在 componentDidMount
上创建一个事件侦听器并在 componentDidUnmount
上清理它。就像 class 基于钩子的代码将合并到一个回调函数中。
对于清理定义这样的例子
useEffect(()=>{
let interval = setInterval(()=>{
// do something
}),1000}
return () => { clearInterval(interval) }
}, [foo])
为了更好地理解检查官方 Effects with Cleanup
每次调用 useEffect
更新程序函数时都会重新创建清理函数。当依赖关系发生变化时(如果没有依赖关系,则在每次渲染时)或卸载组件之前,将调用当前清理函数。调用cleanup之后,updater会被调用,可以returned一个新的cleanup函数。
您可以 return 一个不同的函数,或者根本 none,无论何时调用更新程序函数。
比如多次点击Inc按钮,可以在控制台看到清理功能只对偶数counter
存在,因为它是return有条件地编辑。
const { useState, useEffect } = React;
const Demo = () => {
const [counter, setCount] = useState(0);
useEffect(() => {
console.log(`Effect ${counter}`);
if(counter % 2 === 0) {
return () => console.log(`Cleanup ${counter}`);
}
}, [counter]);
return (
<div>
<p>Counter: {counter}</p>
<button onClick={() => setCount(counter + 1)}>Inc</button>
</div>
);
};
ReactDOM.render(
<Demo />,
root
);
.as-console-wrapper { top: 0; left: 50% !important; max-height: unset !important; }
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
有时,将清除函数嵌套在条件中可能会造成混淆。另一种选择是始终 return 清理函数,并将逻辑放入其中:
useEffect(()=>{
if(foo) {
// do something
}
return () => {
if(foo) {
// cleanup something
}
}
}, [foo])
你可以在这个例子中看到,结果是一样的:
const { useState, useEffect } = React;
const Demo = () => {
const [counter, setCount] = useState(0);
useEffect(() => {
console.log(`Effect ${counter}`);
return () => {
if(counter % 2 === 0) {
console.log(`Cleanup ${counter}`);
}
};
}, [counter]);
return (
<div>
<p>Counter: {counter}</p>
<button onClick={() => setCount(counter + 1)}>Inc</button>
</div>
);
};
ReactDOM.render(
<Demo />,
root
);
.as-console-wrapper { top: 0; left: 50% !important; max-height: unset !important; }
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
所以,我有一个 useEffect
这样的:
useEffect(()=>{
if(foo) {
// do something
return () => { // cleanup function }
}
}, [foo])
此处,即使执行了 if
块,也不会调用 cleanup
函数。但是如果我修改效果为:
useEffect(()=>{
if(foo) {
// do something
}
return () => { // cleanup function }
}, [foo])
有效。
那么,是否仅当 return
是 useEffect
的最后一条语句时才进行清理,还是我遗漏了什么?
当您 return 将回调中的函数传递给 useEffect
时,returned 函数将在组件从 UI 中删除之前被调用。
通常我们会在 componentWillUnmount
中对基于 class 的组件进行清理。假设您想在 componentDidMount
上创建一个事件侦听器并在 componentDidUnmount
上清理它。就像 class 基于钩子的代码将合并到一个回调函数中。
对于清理定义这样的例子
useEffect(()=>{
let interval = setInterval(()=>{
// do something
}),1000}
return () => { clearInterval(interval) }
}, [foo])
为了更好地理解检查官方 Effects with Cleanup
每次调用 useEffect
更新程序函数时都会重新创建清理函数。当依赖关系发生变化时(如果没有依赖关系,则在每次渲染时)或卸载组件之前,将调用当前清理函数。调用cleanup之后,updater会被调用,可以returned一个新的cleanup函数。
您可以 return 一个不同的函数,或者根本 none,无论何时调用更新程序函数。
比如多次点击Inc按钮,可以在控制台看到清理功能只对偶数counter
存在,因为它是return有条件地编辑。
const { useState, useEffect } = React;
const Demo = () => {
const [counter, setCount] = useState(0);
useEffect(() => {
console.log(`Effect ${counter}`);
if(counter % 2 === 0) {
return () => console.log(`Cleanup ${counter}`);
}
}, [counter]);
return (
<div>
<p>Counter: {counter}</p>
<button onClick={() => setCount(counter + 1)}>Inc</button>
</div>
);
};
ReactDOM.render(
<Demo />,
root
);
.as-console-wrapper { top: 0; left: 50% !important; max-height: unset !important; }
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
有时,将清除函数嵌套在条件中可能会造成混淆。另一种选择是始终 return 清理函数,并将逻辑放入其中:
useEffect(()=>{
if(foo) {
// do something
}
return () => {
if(foo) {
// cleanup something
}
}
}, [foo])
你可以在这个例子中看到,结果是一样的:
const { useState, useEffect } = React;
const Demo = () => {
const [counter, setCount] = useState(0);
useEffect(() => {
console.log(`Effect ${counter}`);
return () => {
if(counter % 2 === 0) {
console.log(`Cleanup ${counter}`);
}
};
}, [counter]);
return (
<div>
<p>Counter: {counter}</p>
<button onClick={() => setCount(counter + 1)}>Inc</button>
</div>
);
};
ReactDOM.render(
<Demo />,
root
);
.as-console-wrapper { top: 0; left: 50% !important; max-height: unset !important; }
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>