You can use more than one useEffect().
For example, if my variable is data1
, I can use all of this in my component:
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"), [] );
Since the cleanup is not dependent on the username
, you could put the cleanup in a separate useEffect
that is given an empty array as second argument.
Example
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>
To add to the accepted answer, I had a similar issue and solved it using a similar approach with the contrived example below. In this case I needed to log some parameters on componentWillUnmount
and as described in the original question I didn't want it to log every time the params changed.
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
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>
)
}
In the code above, the fetchLegos function returns a promise. We can “cancel” the promise by having a conditional in the scope of useEffect, preventing the app from setting state after the component has unmounted.
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With