让我解释一下此代码的结果,以方便地询问我的问题。
const ForExample = () => {
const [name, setName] = useState('');
const [username, setUsername] = useState('');
useEffect(() => {
console.log('effect');
console.log({
name,
username
});
return () => {
console.log('cleaned up');
console.log({
name,
username
});
};
}, [username]);
const handleName = e => {
const { value } = e.target;
setName(value);
};
const handleUsername = e => {
const { value } = e.target;
setUsername(value);
};
return (
<div>
<div>
<input value={name} onChange={handleName} />
<input value={username} onChange={handleUsername} />
</div>
<div>
<div>
<span>{name}</span>
</div>
<div>
<span>{username}</span>
</div>
</div>
</div>
);
};
安装ForExample component
时,将记录“效果”。这与componentDidMount()
有关。
每当我更改名称输入时,都会记录“效果”和“清理”。反之亦然,因为我在[username]
的第二个参数中添加了useEffect()
,所以每次更改用户名输入都不会记录任何消息。这与componentDidUpdate()
最后,当卸载ForExample component
时,将记录“清理”。这与componentWillUnmount()
有关。
我们都知道。
总而言之,只要重新渲染组件(包括卸载),就会调用“清理”
如果要使该组件仅在卸载时记录“清理”,则只需将useEffect()
的第二个参数更改为[]
。
但是,如果我将[username]
更改为[]
,ForExample component
将不再实现componentDidUpdate()
的名称输入。
我要做的是,使组件仅支持componentDidUpdate()
仅用于名称输入和componentWillUnmount()
。 (仅在卸载组件时记录“清理”)
答案 0 :(得分:4)
您可以使用多个useEffect
例如,如果我的变量是data1,我可以在组件中使用所有这些
useEffect( () => console.log("mount"), [] );
useEffect( () => console.log("will update data1"), [ data1 ] );
useEffect( () => console.log("will update any") );
useEffect( () => () => console.log("will update data1 or unmount"), [ data1 ] );
useEffect( () => () => console.log("unmount"), [] );
答案 1 :(得分:4)
要添加到已接受的答案中,我遇到了类似的问题,并使用类似的方法通过下面的人为示例解决了该问题。在这种情况下,我需要在 componentWillUnmount
上记录一些参数,并且如原始问题中所述,我不希望每次更改参数时都记录。
const componentWillUnmount = useRef(false)
// This is componentWillUnmount
useEffect(() => {
return () => {
componentWillUnmount.current = true
}
}, [])
useEffect(() => {
return () => {
// This line only evaluates to true after the componentWillUnmount happens
if (componentWillUnmount.current) {
console.log(params)
}
}
}, [params]) // This dependency guarantees that when the componentWillUnmount fires it will log the latest params
答案 2 :(得分:3)
由于清理操作不依赖于username
,因此您可以将清理操作放在单独的useEffect
中,该清理操作将空数组作为第二个参数。
示例
const { useState, useEffect } = React;
const ForExample = () => {
const [name, setName] = useState("");
const [username, setUsername] = useState("");
useEffect(
() => {
console.log("effect");
},
[username]
);
useEffect(() => {
return () => {
console.log("cleaned up");
};
}, []);
const handleName = e => {
const { value } = e.target;
setName(value);
};
const handleUsername = e => {
const { value } = e.target;
setUsername(value);
};
return (
<div>
<div>
<input value={name} onChange={handleName} />
<input value={username} onChange={handleUsername} />
</div>
<div>
<div>
<span>{name}</span>
</div>
<div>
<span>{username}</span>
</div>
</div>
</div>
);
};
function App() {
const [shouldRender, setShouldRender] = useState(true);
useEffect(() => {
setTimeout(() => {
setShouldRender(false);
}, 5000);
}, []);
return shouldRender ? <ForExample /> : null;
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
答案 3 :(得分:2)
function LegoComponent() {
const [lego, setLegos] = React.useState([])
React.useEffect(() => {
let isSubscribed = true
fetchLegos().then( legos=> {
if (isSubscribed) {
setLegos(legos)
}
})
return () => isSubscribed = false
}, []);
return (
<ul>
{legos.map(lego=> <li>{lego}</li>)}
</ul>
)
}
在上面的代码中,fetchLegos函数返回一个promise。我们可以通过在useEffect范围内设置条件来“取消”承诺,从而防止应用程序在组件卸载后设置状态。
警告:无法在已卸载的组件上执行React状态更新。 这是空操作,但它表明应用程序中发生内存泄漏。 要修复,请取消使用useEffect清理功能中的所有订阅和异步任务。
答案 4 :(得分:1)
我没有创建太多复杂的函数和方法,而是创建了一个事件侦听器,并自动为我完成了挂载和卸载,而不必担心手动进行。这是一个例子。
//componentDidMount
useEffect( () => {
window.addEventListener("load", pageLoad);
//component will unmount
return () => {
window.removeEventListener("load", pageLoad);
}
});
这部分已经完成,我只需要像这样从pageLoad函数运行任何我想要的东西。
const pageLoad = () =>{
console.log(I was mounted and unmounted automatically :D)}
答案 5 :(得分:0)
useEffect在其自己的范围内被隔离,并相应地呈现。 图片来自https://reactjs.org/docs/hooks-custom.html
答案 6 :(得分:0)
使用自定义 js 事件,您可以模拟卸载 componentWillUnmount,即使有依赖项。
问题:
useEffect(() => {
//Dependent Code
return () => {
// Desired to perform action on unmount only 'componentWillUnmount'
// But it does not
if(somethingChanged){
// Perform an Action only if something changed
}
}
},[somethingChanged]);
解决方案:
// Rewrite this code to arrange emulate this behaviour
// Decoupling using events
useEffect( () => {
return () => {
// Executed only when component unmounts,
let e = new Event("componentUnmount");
document.dispatchEvent(e);
}
}, []);
useEffect( () => {
function doOnUnmount(){
if(somethingChanged){
// Perform an Action only if something changed
}
}
document.addEventListener("componentUnmount",doOnUnmount);
return () => {
// This is done whenever value of somethingChanged changes
document.removeEventListener("componentUnmount",doOnUnmount);
}
}, [somethingChanged])
注意事项: useEffects 必须是有序的,没有依赖的 useEffect 之前必须写,这是为了避免事件被删除后调用。