it says that : "Each time that our component renders, our effect is called, adding another event listener. With just a few clicks and re-renders, we have attached a lot of event listeners to the DOM! We need to clean up after ourselves!".
so for every click the count goes from 1 to 3 to 7 instead of incrementing by 1. I don't understand how does so many event listeners got attached and how is this effecting the total count?
import React, { useState, useEffect } from 'react';
export default function Counter() {
const [clickCount, setClickCount] = useState(0);
const increment = () => {
setClickCount(prev => prev 1);
};
useEffect(() => {
document.addEventListener('mousedown', increment);
})
return (
<h1>Document Clicks: {clickCount}</h1>
);
}
CodePudding user response:
You're adding a listener to the document itself, so you need to remove it when component is unmounted.
And you need to add an empty dependency array to not add a new listener on each rerender
And you either need to wrap increment
in a useCallback
or use a different increment function in the useEffect
:
import React, { useState, useEffect } from 'react';
export default function Counter() {
const [clickCount, setClickCount] = useState(0);
const increment = useCallback(() => {
setClickCount(prev => prev 1);
}, [setClickCount]);
useEffect(() => {
document.addEventListener('mousedown', increment);
return () => document.removeEventListener('mousedown', increment);
}, [])
return (
<h1>Document Clicks: {clickCount}</h1>
);
}
CodePudding user response:
Without using an array of dependencies, every change will trigger your useEffect
to run again.
So every re-render, every changing state will cause you to create another eventListener
.
Let's talk about variant useEffect
:
useEffect(() => {
// invoking on every re-render
})
useEffect(() => {
// invoking on component did mount (once)
}, [])
useEffect(() => {
// invoking on component did mount and with every change on counter
}, [counter])
Also, you need to remove your eventLinstener
on the component unmount:
useEffect(() => {
document.addEventListener('mousedown', increment);
return () => document.removeEventListener('mousedown', increment);
}, [])
Optional
As mentioned with Samathingamajig in the answers, you can use an optimized way to define your increment
function with useCallback
.
With the current implementation, your increment
method will be re-defined every time (with every change and re-render) but using useCallback
it will be redefined only when the useCallback
's array of dependencies got changes.
import React, {useEffect, useCallback} from 'react';
// rest of the codes ...
const increment = useCallback(() => {
setClickCount(prevState => prevState 1);
}, [setClickCount]);