Let me explain the result of this code for asking my issue easily.
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>
);
};
When the ForExample component
mounts, 'effect' will be logged. This is related to the componentDidMount()
.
And whenever I change name input, both 'effect' and 'cleaned up' will be logged. Vice versa, no message will be logged whenever I change username input since I added [username]
to the second parameter of useEffect()
. This is related to the componentDidUpdate()
Lastly, when the ForExample component
unmounts, 'cleaned up' will be logged. This is related to the componentWillUnmount()
.
We all know that.
To sum, 'cleaned up' is invoked whenever the component is being re-rendered(includes unmount)
If I want to make this component to log 'cleaned up' for only the moment when it is unmount, I just have to change the second parameter of useEffect()
to []
.
But If I change [username]
to []
, ForExample component
no longer implements the componentDidUpdate()
for name input.
What I want to do is that, to make the component supports both componentDidUpdate()
only for name input and componentWillUnmount()
. (logging 'cleaned up' for only the moment when the component is being unmounted)
This question is related to
reactjs
react-hooks
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;_x000D_
_x000D_
const ForExample = () => {_x000D_
const [name, setName] = useState("");_x000D_
const [username, setUsername] = useState("");_x000D_
_x000D_
useEffect(_x000D_
() => {_x000D_
console.log("effect");_x000D_
},_x000D_
[username]_x000D_
);_x000D_
_x000D_
useEffect(() => {_x000D_
return () => {_x000D_
console.log("cleaned up");_x000D_
};_x000D_
}, []);_x000D_
_x000D_
const handleName = e => {_x000D_
const { value } = e.target;_x000D_
_x000D_
setName(value);_x000D_
};_x000D_
_x000D_
const handleUsername = e => {_x000D_
const { value } = e.target;_x000D_
_x000D_
setUsername(value);_x000D_
};_x000D_
_x000D_
return (_x000D_
<div>_x000D_
<div>_x000D_
<input value={name} onChange={handleName} />_x000D_
<input value={username} onChange={handleUsername} />_x000D_
</div>_x000D_
<div>_x000D_
<div>_x000D_
<span>{name}</span>_x000D_
</div>_x000D_
<div>_x000D_
<span>{username}</span>_x000D_
</div>_x000D_
</div>_x000D_
</div>_x000D_
);_x000D_
};_x000D_
_x000D_
function App() {_x000D_
const [shouldRender, setShouldRender] = useState(true);_x000D_
_x000D_
useEffect(() => {_x000D_
setTimeout(() => {_x000D_
setShouldRender(false);_x000D_
}, 5000);_x000D_
}, []);_x000D_
_x000D_
return shouldRender ? <ForExample /> : null;_x000D_
}_x000D_
_x000D_
ReactDOM.render(<App />, document.getElementById("root"));
_x000D_
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>_x000D_
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>_x000D_
_x000D_
<div id="root"></div>
_x000D_
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
useEffect are isolated within its own scope and gets rendered accordingly. Image from https://reactjs.org/docs/hooks-custom.html
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("will update data1"), [ data1 ] );
useEffect( () => console.log("will update any") );
useEffect( () => () => console.log("will update data1 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>
)
}
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.
instead of creating too many complicated functions and methods what I do is I create an event listener and automatically have mount and unmount done for me without having to worry about doing it manually. Here is an example.
//componentDidMount
useEffect( () => {
window.addEventListener("load", pageLoad);
//component will unmount
return () => {
window.removeEventListener("load", pageLoad);
}
});
now that this part is done I just run anything I want from the pageLoad function like this.
const pageLoad = () =>{
console.log(I was mounted and unmounted automatically :D)}
Source: Stackoverflow.com