仅对组件进行影响()清理?

让我解释一下这段代码的结果,因为它很容易提出我的问题。

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()。(仅在卸载组件时记录“清理”)

271235 次浏览

由于清理不依赖于 username,因此可以将清理放在一个单独的 useEffect中,该 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>

您可以使用多个 useEffect ()。

例如,如果我的变量是 data1,我可以在我的组件中使用所有这些:

useEffect( () => console.log("mount"), [] );
useEffect( () => console.log("data1 update"), [ data1 ] );
useEffect( () => console.log("any update") );
useEffect( () => () => console.log("data1 update or unmount"), [ data1 ] );
useEffect( () => () => console.log("unmount"), [] );
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 函数返回一个承诺。我们可以“取消”这个承诺,方法是在 useEffect 的范围内设置一个条件,防止应用程序在卸载组件后设置状态。

警告: 无法对已卸载的组件执行 React 状态更新。 这是不可操作的,但它表明应用程序中存在内存泄漏。 若要修复,请取消 useEffect 清理函数中的所有订阅和异步任务。

useEffect 在它自己的作用域内被隔离,并相应地被呈现。 图片来源: < a href = “ https://reactjs.org/docs/hooks-custom.html”rel = “ nofollow norefrer”> https://reactjs.org/docs/hooks-custom.html

enter image description here

而不是创建太多复杂的函数和方法我所做的就是创建一个事件监听器然后自动完成挂载和卸载而不用担心手动完成。这里有一个例子。

//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)}

为了补充已被接受的答案,我有一个类似的问题,并用类似的方法解决了它与人为的例子如下。在这种情况下,我需要在 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

使用自定义 js 事件,您可以模拟卸载一个组件 WillUnmount,即使它有依赖项。

问题:

    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])

警告: 使用效果必须是有序的,使用效果没有依赖关系之前必须写入,这是为了避免事件被调用后,其删除。

下面是我的解决方案,概括为一个自定义钩子:

import React, { useEffect, useRef } from 'react';


const useUnmountEffect = (effect, dependencies) => {
if (typeof effect !== 'function') {
console.error('Effect must be a function');
}


const componentWillUnmount = useRef(false)


useEffect(() => () => {
componentWillUnmount.current = true
}, []);


useEffect(() => () => {
if (componentWillUnmount.current) {
effect?.();
}
}, dependencies);
}


export default useUnmountEffect;

那么:

function useOnUnmount(callback: () => void) {
const onUnmount = useRef<(() => void) | null>(null);
onUnmount.current = callback;


useEffect(() => {
return () => onUnmount.current?.();
}, []);
}


useOnUnmount(() => {
console.log("unmount", props);
});

你可以这样写:

  useEffect(() => {
return () => {};
}, []);