Reputation: 752
I am trying to understand how is exactly react useEffect cleanup used and for what purpose. Could you please help me fix my code by adding the cleanup
as far as I am able to understand it's used in case of API calls in order to close a subscription. Still unable to clarify why it used and how exactly.
useEffect(() => {
async function onLoad() {
try {
const examSessions = await loadSessions(id);
setSessions(examSessions);
console.log(`examSession`,examSessions)
} catch (e) {
// onError(e);
console.log(e)
}
setIsLoading(false);
}
onLoad()
}, [id])
function loadSessions(id) {
const result = API.get("vce", `/session/${id}`)
return result
}
Upvotes: 0
Views: 594
Reputation: 984
You don't need to use cleanup hook here. You are not subscribed to anything. When you subscribed in a useEffect to something and don't unsubscribe on a component destruction, this subscription will continue listening to remote source, leaching device memory and user data.
To test it, you can just attach say a click listener to a button, mount/unmount a component (without page refresh) and see in chrome dev tools how the list of subscriptions/listeners grows.
Edit: Try this
useEffect(() => {
let unmounted = false;
async function onLoad() {
try {
const examSessions = await loadSessions(id);
setSessions(examSessions);
console.log(`examSession`,examSessions)
} catch (e) {
// onError(e);
console.log(e)
}
if(!unmounted)setIsLoading(false);
}
onLoad()
return ()=> unmounted = true;
}, [id])
Upvotes: 0
Reputation: 26920
Without the cleanup, your effects will continue doing whatever they were doing forever even though the component has become unmounted.
This is important for complex components that might be responsible for setting up network things like Web Socket connections or requesting data using fetch
. If you didn't cancel or close those, those resources would still be open and possibly sending signals.
Consider the following example where we can toggle a Foo
component that ticks up. With the cleanup commented out, we still see "Tick" even though it has unmounted because we don't take the time to clean up the interval.
In fact, if you re-toggle the Foo
back on, you'll start to see even more "Tick"s in console because there are now 2+ intervals ticking about. Imagine if that was a Web Socket and we were scheduling a new heartbeat tick every time we remounted; the server would be very angry at our apps heart palpitations.
const {
useState,
useEffect,
createElement
} = React;
const {
render
} = ReactDOM;
const Foo = () => {
const [t, setT] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
console.log("Tick");
setT(currT => currT + 1);
}, 1000);
// Without the following, you will see "Tick" forever because it isn't cleaned up
//return () => clearInterval(interval);
}, []);
return createElement("div", {}, t);
};
const App = () => {
const [should, setShould] = useState(true);
return createElement("div", null,
createElement("button", {
onClick: () => setShould(currShould => !currShould)
}, "toggle"),
should ? createElement(Foo) : null
);
};
render(
createElement(App),
document.getElementById('app')
);
<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="app"></div>
Compare that to the following example which does have the cleanup which will stop showing "Tick" whenever you toggle the Foo
component.
const {
useState,
useEffect,
createElement
} = React;
const {
render
} = ReactDOM;
const Foo = () => {
const [t, setT] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
console.log("Tick");
setT(currT => currT + 1);
}, 1000);
// Without the following, you will see "Tick" forever because it isn't cleaned up
return () => clearInterval(interval);
}, []);
return createElement("div", {}, t);
};
const App = () => {
const [should, setShould] = useState(true);
return createElement("div", null,
createElement("button", {
onClick: () => setShould(currShould => !currShould)
}, "toggle"),
should ? createElement(Foo) : null
);
};
render(
createElement(App),
document.getElementById('app')
);
<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="app"></div>
Upvotes: 1