Home > Software design >  useEffect() without a cleanup function
useEffect() without a cleanup function

Time:10-31

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]);
  • Related