I have been trying to understand when is unsubscribe(the callback in useEffect) gets called exactly.
This is the codepen link : https://codepen.io/deen_john/pen/eYmNdMy Code :
const { useState, useEffect } = React ;
function App() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
console.log("use effect called ");
return () => {
console.log("unsubscribe ")
}
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Problem : In my example, the callback function in useEffect hook (i.e unsubscribe) , gets called every time i click on button (i.e every time i update the button state). But, as per the React documentation, callback in useEffect works like componentWillUnmount lifecycle , so in my example it should have been called only if the App component is unmounted. I am just updating the button state here,not unmounting App component on every click.
The useEffect(callback, [prop, state]) invokes the callback after the changes are being committed to DOM and if and only if any value in the dependencies array [prop, state] has changed.
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. The instruction is pretty clear and straightforward, "cancel all subscriptions and asynchronous tasks in a useEffect cleanup function".
The useEffect Hook is built in a way that we can return a function inside it and this return function is where the cleanup happens. The cleanup function prevents memory leaks and removes some unnecessary and unwanted behaviors.
When you call useEffect , you're telling React to run your “effect” function after flushing changes to the DOM. Effects are declared inside the component so they have access to its props and state. By default, React runs the effects after every render — including the first render.
The unsubscribe event of useEffect is called everytime before the next call to useEffect as well as when the component unmounts
Since in your case, you haven't passed any dependency array to useEffect, your useEffect will run on each render of the component and hence the unsubscribe will run on each re-render before the next useEffect is triggered
A better way to write the above code would be to pass count in the dependency array to useEffect
const { useState, useEffect } = React;
function App() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
console.log("use effect called ");
return () => {
console.log("unsubscribe ")
}
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
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