799

With React 16.8.6 (it was good on previous version 16.8.3), I get this error when I attempt to prevent an infinite loop on a fetch request:

./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

I've been unable to find a solution that stops the infinite loop. I want to stay away from using useReducer(). I did find this discussion [ESLint] Feedback for 'exhaustive-deps' lint rule #14920 where a possible solution is You can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing. I'm not confident in what I'm doing, so I haven't tried implementing it just yet.

I have this current setup, React hook useEffect runs continuously forever/infinite loop and the only comment is about useCallback() which I'm not familiar with.

How I'm currently using useEffect() (which I only want to run once in the beginning similar to componentDidMount()):

useEffect(() => {
    fetchBusinesses();
  }, []);
const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
Yilmaz
  • 35,338
  • 10
  • 157
  • 202
russ
  • 8,023
  • 3
  • 8
  • 8
  • 22
    Since this question gets a lot of traffic, here is a link to [Dan Abramov's blog](https://overreacted.io/a-complete-guide-to-useeffect/) where he explains useEffect and it's dependencies in detail. – chetan Feb 28 '21 at 12:05
  • 6
    And a feature request so React improve useEffect API to avoid this issue, clearly separating effect triggers from effect dependencies: https://github.com/facebook/react/issues/22132 Since Next.js enabled linting as a default, this warning must appear million times a day everywhere in the world, this has to stop somehow. – Eric Burel Nov 24 '21 at 12:59
  • 8
    Agreed, this is totally unclear from the official documentation. A library like React shouldn't need forums and blog posts to get it working. – Kokodoko Dec 08 '21 at 16:50

23 Answers23

705

If you aren't using fetchBusinesses method anywhere apart from the effect, you could simply move it into the effect and avoid the warning

useEffect(() => {
    const fetchBusinesses = () => {
       return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
  fetchBusinesses();
}, []);

If however you are using fetchBusinesses outside of the effect, you must note two things

  1. Is there any issue with you not passing fetchBusinesses as a method when it's used during mount with its enclosing closure?
  2. Does your method depend on some variables which it receives from its enclosing closure? This is not the case for you.
  3. On every render, fetchBusinesses will be re-created and hence passing it to useEffect will cause issues. So first you must memoize fetchBusinesses if you were to pass it to the dependency array.

To sum it up I would say that if you are using fetchBusinesses outside of useEffect you can disable the rule using // eslint-disable-next-line react-hooks/exhaustive-deps otherwise you can move the method inside of useEffect

To disable the rule you would write it like

useEffect(() => {
   // other code
   ...
 
   // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) 
Rany Albeg Wein
  • 3,304
  • 3
  • 16
  • 26
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • 70
    I used the solution you outlined nicely. Another solution that I used else where because of a different setup was`useCallback()`. So for example: ```const fetchBusinesses= useCallback(() => { ... }, [...])``` and the `useEffect()` would look like this: ```useEffect(() => { fetchBusinesses(); }, [fetchBusinesses]);``` – russ Apr 25 '19 at 22:39
  • 6
    @russ, you are correct, you would need to memoize fetchBusiness using useCallback if you are to pass it to dependency array – Shubham Khatri Apr 26 '19 at 05:41
  • It would be nice if you showed where to put the eslint-disable statement. I thought it would be above useEffect – user210757 Jun 18 '19 at 21:30
  • 49
    using `// eslint-disable-next-line react-hooks/exhaustive-deps` to explain to the linter that your code is correct is like a hack. I expect they will find another solution to make the linter smarter enough to detect when an argument is not mandatory – Olivier Boissé Oct 27 '19 at 11:59
  • How about with async function? – Tapas Adhikary Nov 20 '19 at 05:08
  • @TapasAdhikary, an async function can also be called from within the useEffect – Shubham Khatri Nov 20 '19 at 05:10
  • Thanks @ShubhamKhatri, I get that. My Question was if we want to use the function definition itself inside useEffect() and my function is an ansync function which was outside of useEffect(), how to get that inside? The aync function got the await keyword which can not be placed as is , I believe. – Tapas Adhikary Nov 20 '19 at 05:18
  • 3
    @TapasAdhikary, yes you can have a async function in useEffect, you just need to write it differently. Please check https://stackoverflow.com/questions/53332321/react-hook-warnings-for-async-function-in-useeffect-useeffect-function-must-ret/53332372#53332372 – Shubham Khatri Nov 20 '19 at 05:19
  • "If there any issue if you do not pass fetchBusinesses as method and that it being used during mount with its enclosing closure will have not any issues. "-> This sentence is unclear. – Giorgi Moniava Dec 23 '19 at 10:07
  • I can forsee lots of code in many projects everywhere writing that disable line. I feel this is a bug react devs needs to address. – Fiddle Freak Aug 19 '20 at 13:52
  • But this doesn't work when we use async and await inside the useEffect function. So I tried putting it outside. But it's still not working! – Akhila Oct 11 '20 at 20:55
  • I think that the accepted answer should be the one by 'r g' below. It is a bad practice IMO to comment out lint errors when there's another viable solution. + you need to trust the next developer that edits this code to firstly remove the comment. – Erez Cohen Nov 02 '20 at 10:09
  • I hate this, having to move the function inside of the hook. There must be a better way. – Robert Moskal Nov 16 '20 at 15:17
  • 21
    the linter is still dumb today, and if you want componentDidMount-like behaviour while using outside variables (needing some but not all of them to trigger a rerender if they change), you get that warning no matter what you do.... at least I wasn't able to find a solution online – rayaqin Apr 09 '21 at 14:01
  • 1
    @rayaqin Do can disable the linter for that line, if you are absolutely sure what you are doing is right – Shubham Khatri Apr 09 '21 at 16:03
  • that wont make the browser warning go away, just the warning in the IDE – rayaqin Apr 11 '21 at 08:03
  • Hi guys, I've submitted a feature request for an additional "triggers" parameter in `useEffect`, to differentiate deps that must triggers a rerun of the effect, and deps that simply triggers an update of the effect callback but not running it. I'd like to have some feedback on it: https://github.com/facebook/react/issues/22132 (related code sandbox: https://codesandbox.io/s/fancy-sea-zj5e4). – Eric Burel Aug 19 '21 at 08:42
  • 2
    And people wonder why us "lazy" devs tend to ignore warnings lol – CatDadCode Oct 01 '21 at 16:22
  • The manufactorers of linters should correct this or am I wrong? Feels like a bug, people start to change 'onMount' to 'onUpdated' because of a misleading (wrong) warning? – faebster Mar 14 '23 at 23:52
410

There are very good options for state management librariess if you are creating a new app or have enough flexibility. Check out Recoil.

Just for completeness:

1. (Stopped working) Use function as useEffect callback

useEffect(fetchBusinesses, [])

2. Declare function inside useEffect()

useEffect(() => {
  function fetchBusinesses() {
    ...
  }
  fetchBusinesses()
}, [])

3. Memoize with useCallback()

In this case, if you have dependencies in your function, you will have to include them in the useCallback dependencies array and this will trigger the useEffect again if the function's params change. Besides, it is a lot of boilerplate... So just pass the function directly to useEffect as in 1. useEffect(fetchBusinesses, []).

const fetchBusinesses = useCallback(() => {
  ...
}, [])
useEffect(() => {
  fetchBusinesses()
}, [fetchBusinesses])

4. Function's default argument

As suggested by Behnam Azimi

It's not best practice, but it could be useful in some cases.

useEffect((fetchBusinesses = fetchBusinesses) => {
   fetchBusinesses();
}, []);

5. Create a custom hook

Create a custom hook and call it when you need to run function only once. It may be cleaner. You can also return a callback to reset re-run the "initialization" when need.

// customHooks.js
const useInit = (callback, ...args) => {
  const [mounted, setMounted] = useState(false)
  
  const resetInit = () => setMounted(false)

  useEffect(() => {
     if(!mounted) {
        setMounted(true);
        callback(...args);
     }
  },[mounted, callback]);

  return [resetInit]
}

// Component.js
return ({ fetchBusiness, arg1, arg2, requiresRefetch }) => {
  const [resetInit] = useInit(fetchBusiness, arg1, arg2)

  useEffect(() => {
    resetInit()
  }, [requiresRefetch, resetInit]);

6. Disable eslint's warning

Disabling warnings should be your last resort, but when you do, better do it inline and explicitly, because future developers may be confused or create unexpected bugs without knowing linting is off

useEffect(() => {
  fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps
AncientSwordRage
  • 7,086
  • 19
  • 90
  • 173
jpenna
  • 8,426
  • 5
  • 28
  • 36
  • 4
    Is disbaling the eslint's warning okay todo? – Rohan Shenoy Sep 02 '20 at 13:48
  • 33
    I prefer disabling warnings as last resort, because future developers may be confused or create unexpected bugs without knowing linting is off – jpenna Sep 02 '20 at 18:12
  • In latest react. If I declare a state and the component has useEffect then the warning is show. So in this case solution 1,2 and 3 are not applicable since I want it to run only once. What should I do? – Rohan Shenoy Sep 03 '20 at 04:32
  • @RohanShenoy If I understand correctly, you want to use state inside `useEffect`, that's a completely different situation. In this case you can only disable the warning, because now you have a second external dependency – jpenna Sep 04 '20 at 15:36
  • 3
    `useEffect(fetchBusinesses, [])` will throw _"TypeError: An effect function must not return anything besides a function, which is used for clean-up. "_ since `fetchBusinesses` returns a promise. – Emile Bergeron Dec 16 '20 at 19:49
  • @EmileBergeron That's true with the implementation of the OP! They could just remove the `return` from `fetchBusinesses` though, since it isn't required. – jpenna Dec 17 '20 at 01:44
  • @EmileBergeron useEffect shouldn't be passed an async function, the way to do that is: ```useEffect( () => { asyncFunc() } ,[])``` – Shani Kehati Feb 11 '21 at 10:49
  • 1
    `// eslint-disable-line react-hooks/exhaustive-deps` works like a charm. – Lane Feb 20 '21 at 09:19
  • 2
    The first suggestion does not eliminate the warning surprisingly – ericjam Mar 31 '21 at 14:42
  • This doesn't work ANYMORE!!!! – Alex Sep 19 '21 at 20:35
  • Is there any differenc if the function is async? This is what I do, is this corrent? const loadAgents = useCallback(async ()=> { try { ... const response = await axios.post(...); setAgentsData(response.data); } catch (e) { ... } finally { ... } }, []); useEffect(() => { if (...) loadAgents(); }, [uiState.selectedSwitch, loadAgents]); – Daniel Tkach Oct 08 '21 at 17:29
  • Note, I had to put `// eslint-disable-next-line react-hooks/exhaustive-deps` and not `// eslint-disable-line react-hooks/exhaustive-deps` – Lewy Blue Dec 25 '21 at 23:58
  • 1
    @LewyBlue that's because you added the comment *above* the dependencies line – jpenna Dec 27 '21 at 12:46
  • 2. Declare function inside useEffect() would not work if the function is called from outside the useEffect. –  Aug 24 '22 at 13:40
  • Furthermore, solution 4 can lead to ''fetchBusinesses' was used before it was defined' –  Aug 24 '22 at 13:42
  • this is the best answer to ANY question, ever. – vikingsteve Apr 07 '23 at 23:14
175
./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

It's not a JavaScript/React error, but an ESLint (eslint-plugin-react-hooks) warning.

It's telling you that the hook depends on function fetchBusinesses, so you should pass it as a dependency.

useEffect(() => {
  fetchBusinesses();
}, [fetchBusinesses]);

It could result in invoking the function on every render if the function is declared in a component like:

const Component = () => {
  /*...*/

  // New function declaration every render
  const fetchBusinesses = () => {
    fetch('/api/businesses/')
      .then(...)
  }

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

because every time the function is redeclared with a new reference.

The correct way of doing this stuff is:

const Component = () => {
  /*...*/

  // Keep the function reference
  const fetchBusinesses = useCallback(() => {
    fetch('/api/businesses/')
      .then(...)
  }, [/* Additional dependencies */])

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

Or just define the function in useEffect.

More: [ESLint] Feedback for 'exhaustive-deps' lint rule #14920

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
r g
  • 3,586
  • 1
  • 11
  • 27
  • 2
    the solution is fine and if on the function you modify another state you have to add the dependecies to avoid another unexpected behavior – cesarlarsson Jan 09 '20 at 19:12
  • 3
    I would agree this is a solution to get rid of the linter warning. But I cannot understand WHY the linter is throwing a warning. Because all you're doing anyways is curing the symptoms of an non-existent disease. The dependency array is used by react to determine when to execute the function passed to useEffect, so passing in fetchBusinesses in this case shouldn't be necessary imo. Please do correct me if I'm wrong. – Marcus Ekström Apr 29 '22 at 10:59
  • Agreed - the linter warnings are amazingly useful for `useCallback` and `useMemo` but it's an invalid assumption to assume that business logic of when you want to run a `useEffect` is equivalent to when each of the dependencies update. For example there are many reasons you might only want to run an effect on component mount and `useEffect(() => { ... }, [])` is the perfect tool to achieve that. React/es-lint are making something which doesn't need to be hard, hard. – Tim Trewartha Jan 04 '23 at 13:54
31

These warnings are very helpful for finding components that do not update consistently: Is it safe to omit functions from the list of dependencies?.

However, if you want to remove the warnings throughout your project, you can add this to your ESLint configuration:

  {
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/exhaustive-deps": 0
    }
  }
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jordan Daniels
  • 4,896
  • 1
  • 19
  • 29
  • Works great. Thank you for sharing this solution so I don't have to add comments in my `useEffect`. – Pegues Mar 10 '23 at 14:11
25

The solution is also given by React. They advice you use useCallback which will return a memoize version of your function:

The 'fetchBusinesses' function makes the dependencies of the useEffect Hook (at line NN) change on every render. To fix this, wrap the 'fetchBusinesses' definition into its own useCallback() Hook react-hooks/exhaustive-deps

useCallback is simple to use as it has the same signature as useEffect. The difference is that useCallback returns a function. It would look like this:

 const fetchBusinesses = useCallback( () => {
        return fetch("theURL", {method: "GET"}
    )
    .then(() => { /* Some stuff */ })
    .catch(() => { /* Some error handling */ })
  }, [/* deps */])
  // We have a first effect that uses fetchBusinesses
  useEffect(() => {
    // Do things and then fetchBusinesses
    fetchBusinesses();
  }, [fetchBusinesses]);
   // We can have many effects that use fetchBusinesses
  useEffect(() => {
    // Do other things and then fetchBusinesses
    fetchBusinesses();
  }, [fetchBusinesses]);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Stephane L
  • 2,879
  • 1
  • 34
  • 44
  • 1
    In my case this `useCallBack` hook solved my problem. To see in detail visit [documentation](https://reactjs.org/docs/hooks-reference.html#usecallback) – khan Sep 08 '21 at 18:36
15
const [mount, setMount] = useState(false)
const fetchBusinesses = () => {
   // Function definition
}
useEffect(() => {
   if(!mount) {
      setMount(true);
      fetchBusinesses();
   }
},[fetchBusinesses, mount]);

This is solution is pretty simple and you don't need to override ESLint warnings. Just maintain a flag to check whether the component is mounted or not.

Yasin
  • 1,150
  • 5
  • 19
  • 39
12

Just disable ESLint for the next line;

useEffect(() => {
   fetchBusinesses();
// eslint-disable-next-line
}, []);

In this way, you are using it just like a component did mount (called once).

updated

or

const fetchBusinesses = useCallback(() => {
 // Your logic in here
 }, [someDeps])

useEffect(() => {
   fetchBusinesses();
// No need to skip the ESLint warning
}, [fetchBusinesses]);

fetchBusinesses will be called every time someDeps changes.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user3550446
  • 405
  • 3
  • 10
  • instead of disabling, just doing this: `[fetchBusinesses]` will automatically remove the warning and that solved the issue for me. – rotimi-best Jun 03 '19 at 07:33
  • 13
    @RotimiBest - doing this causes an infinite re-render as described in the question – user210757 Jun 18 '19 at 21:43
  • I actually did it this way in one of my projects a while ago and it didn't produce an infinite loop. I will check again though. – rotimi-best Jun 19 '19 at 10:35
  • 2
    @user210757 Wait but why will it cause an infinite loop, its not like you are setting the state after fetching the data from the server. If you were updating the state the just write an if condition before calling the function in `useEffect` that checks if the state is empty. – rotimi-best Jan 25 '20 at 05:51
  • @rotimi-best been ahwile since I commented but I would say the function is re-created every time thus never the same so will always re-render, unless you move into the useEffect body or useCallback – user210757 Jan 26 '20 at 18:34
  • see this useful discussion: https://github.com/facebook/create-react-app/issues/6880 – Joe Jan 29 '20 at 12:42
  • What do you mean by *"just like a component did mount"* (seems incomprehensible)? Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/56254883/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Jul 15 '21 at 09:16
9

This article is a good primer on fetching data with hooks: https://www.robinwieruch.de/react-hooks-fetch-data/

Essentially, include the fetch function definition inside useEffect:

useEffect(() => {
  const fetchBusinesses = () => {
    return fetch("theUrl"...
      // ...your fetch implementation
    );
  }

  fetchBusinesses();
}, []);
helloitsjoe
  • 6,264
  • 3
  • 19
  • 32
7

You try this way:

const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"})
        .then(res => normalizeResponseErrors(res))
        .then(res => {
            return res.json();
        })
        .then(rcvdBusinesses => {
            // Some stuff
        })
        .catch(err => {
            // Some error handling
        });
  };

and

useEffect(() => {
    fetchBusinesses();
});

It works for you.

But my suggestion is try this way and it also works for you. It's better than the previous way. I use it this way:

useEffect(() => {
    const fetchBusinesses = () => {
        return fetch("theURL", {method: "GET"})
            .then(res => normalizeResponseErrors(res))
            .then(res => {
                return res.json();
            })
            .then(rcvdBusinesses => {
                // Some stuff
            })
            .catch(err => {
                // Some error handling
            });
    };

    fetchBusinesses();
}, []);

If you get data on the base of a specific id, then add in callback useEffect [id]. Then it cannot show you the warning React Hook useEffect has a missing dependency: 'any thing'. Either include it or remove the dependency array

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kashif
  • 1,364
  • 17
  • 21
6

Actually the warnings are very useful when you develop with hooks. But in some cases, it can needle you. Especially when you do not need to listen for dependencies change.

If you don't want to put fetchBusinesses inside the hook's dependencies, you can simply pass it as an argument to the hook's callback and set the main fetchBusinesses as the default value for it like this:

useEffect((fetchBusinesses = fetchBusinesses) => {
   fetchBusinesses();
}, []);

It's not best practice, but it could be useful in some cases.

Also, as Shubham wrote, you can add the below code to tell ESLint to ignore the checking for your hook.

// eslint-disable-next-line react-hooks/exhaustive-deps
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Behnam Azimi
  • 2,260
  • 3
  • 34
  • 52
4

Well, if you want to look into this differently, you just need to know what options React has that are non exhaustive-deps. One of the reason you should not use a closure function inside the effect is on every render, it will be recreated/destroyed again.

So there are multiple React methods in hooks that are considered stable and non-exhausted where you do not have to apply to the useEffect dependencies, and in turn will not break the rules engagement of react-hooks/exhaustive-deps. For example, the second return variable of useReducer or useState which is a function.

const [,dispatch] = useReducer(reducer, {});

useEffect(() => {
    dispatch(); // Non-exhausted - ESLint won't nag about this
}, []);

So in turn, you can have all your external dependencies coexist with your current dependencies within your reducer function.

const [,dispatch] = useReducer((current, update) => {
    const { foobar } = update;
    // Logic

    return { ...current, ...update };
}), {});

const [foobar, setFoobar] = useState(false);

useEffect(() => {
    dispatch({ foobar }); // non-exhausted `dispatch` function
}, [foobar]);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
syarul
  • 2,161
  • 2
  • 20
  • 22
3

In my case, it had this warning with my local variable organization, and when I put organization in the dependency array, useEffect would fetch infinitely. Therefore if you have some problems like mine, use useEffect with the dependency array and split:

Because if you have multiple API calls that modify state, it invokes useEffect multiple times.

From:

  const { organization } = useSelector(withOrganization)
  const dispatch = useDispatch()

  useEffect(() => {
    dispatch(getOrganization({}))
    dispatch(getSettings({}))
    dispatch(getMembers({}))
  }, [dispatch, organization])

To:

  const { organization } = useSelector(withOrganization)
  const dispatch = useDispatch()

  useEffect(() => {
    dispatch(getOrganization({}))
    dispatch(getSettings({}))
  }, [dispatch, organization])

  useEffect(() => {
    dispatch(getMembers({}))
  }, [dispatch])
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jasurbek Nabijonov
  • 1,607
  • 3
  • 24
  • 37
3

This is not an answer specific to the question use case but more general case, and cover the case when useEffect or extract and import is not warking. The useRef senario:

Sometimes the scenario is that useEffect should have the empty array and you still want to use inside the useEffect parts of the state but still you don't want inject them as dependencies, also you might tried the useCallback and now react is complains about the dependencies of the useCallback and you stuck. In this case in some cases you can use useRef. for example:

const locationRef = useRef(location);
useEffect(()=>{
const qs = locationRef.current.search
...
},[])

You should be careful when using this technique and be aware of that useRef is not activate a render process.

Eran Or
  • 1,252
  • 15
  • 22
2

You can remove the second argument type array [], but the fetchBusinesses() will also be called on every update. You can add an IF statement into the fetchBusinesses() implementation if you like.

React.useEffect(() => {
  fetchBusinesses();
});

The other one is to implement the fetchBusinesses() function outside your component. Just don't forget to pass any dependency arguments to your fetchBusinesses(dependency) call, if any.

function fetchBusinesses (fetch) {
  return fetch("theURL", { method: "GET" })
    .then(res => normalizeResponseErrors(res))
    .then(res => res.json())
    .then(rcvdBusinesses => {
      // some stuff
    })
    .catch(err => {
      // some error handling
    });
}

function YourComponent (props) {
  const { fetch } = props;

  React.useEffect(() => {
    fetchBusinesses(fetch);
  }, [fetch]);

  // ...
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
2

If you want to disable this useless message, just add this at the begining of your file.

/* eslint-disable react-hooks/exhaustive-deps */

This will disable the warning for the entire file.

Ikenna Emman
  • 153
  • 1
  • 8
1

This warning happens, if variables that you are using inside useEffect are defined inside the component or passed to the component as a prop. Since you defined fetchBusinesses() inside same component, and eslint follows that rule, you have to pass it to the dependency array. But in your case, just passing [] would also work

In this case it will work, but what if fetchBusinesses was using setState inside the function and calling it would rererender the component, therefore your fetchBusinesses would change so useEffect would run which would create an infinite loop. So just blindly following the eslint might cause extra bugs.

Solution for your use case to make eslint happy, using useCallback with an [].

const memoizedFetchBusinesses=useCallback(
        fetchBusinesses,
        [] // unlike useEffect you always have to pass a dependency array
       )

When your component first renders, a function referred as fetchBusinessess is created in the memory and memoizedFetchBusinesses variable is created which also references same function in the memory.

after the first rerender, a function referred as fetchBusinessess will be created again but this time in a different memory location Since we have [ ] in useCallback, memoizedFetchBusinesses will give you the original fetchBusinesses function in the same memory. useCallback here will you the same reference of the function which was created in the first rendering of your component.

useEffect(()=>{
    memoizedFetchBusinesses();
},[memoizedFetchBusinesses]) 

instead you could define function like this

const fetchBusinesses = useCallback(() => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  },[]);

then in useEffect

useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);
Yilmaz
  • 35,338
  • 10
  • 157
  • 202
  • 2
    you still get the warning even if you import the function from your store. Because your functions will be passed to the props object through mapDispatchToProps call or by using the connect tag's second argument. `connect(mapStateToProps, {fetchBusinesses})(Component)` – osmancakirio Aug 24 '21 at 07:59
  • @osmancakirio Did you find a resolution to the warning in this case? I have the same problem... – ndtreviv Oct 28 '21 at 14:12
  • 1
    @ndtreviv I refactored the components to use redux-hooks now instead of connect tag. then I give the dispatch function to the dependency array. It is also recommended by the redux devs since as they say it is safe to do this because the reference to the dispatch function almost never changes. – osmancakirio Nov 03 '21 at 16:19
1

You can get rid of this Es-lint warning by passing a reference to it:

Example mentioned below, however you can watch the solution on this link: https://www.youtube.com/watch?v=r4A46oBIwZk&t=8s

Warning: Line 13:8: React Hook React.useEffect has missing dependencies: 'history' and 'currentUser?.role'. Either include them or remove the dependency array react-hooks/exhaustive-deps

React.useEffect(() => {
    if (currentUser?.role !== "Student") {
        return history.push("/")
    }
}, [])

Resolution: Step 1: Move business logic it to separate const.

Now the warning is: React Hook React.useEffect has a missing dependency: 'roleChecking'.

const roleChecking = () =>{
   if (currentUser?.role !== "Student") {
        return history.push("/")
    }
}

React.useEffect(() => {
    roleChecking()
}, [])

Last step is to create a reference to the function:

  const roleRef = React.useRef();

  const roleChecking = () => {
    if (currentUser?.role !== "Student") {
      return history.push("/");
    }
  };
  roleRef.current = roleChecking;

  React.useEffect(() => {
   return roleRef.current();
  }, [currentUser?.role]);
1

It seems the fetchBusinesses function declared in the component. It means in every render it declares new function which triggers the hook.

There are 2 approaches to fix the issue.

  1. Move the fetchBusinesses function declaration out of component.

  2. Wrap the fetchBusinesses function with useCallback hook.

First option is preferable.

Narek Ghazaryan
  • 2,758
  • 1
  • 7
  • 11
1

Search for the keywords to learn more about each warning. To ignore, add // eslint-disable-next-line to the line before.

For Example: The function used in useEffect is causing warning

useEffect(() => {
  handleConnectWallet();
}, []);

To ignore the warning we simply add "// eslint-disable-next-line" before the warning line i.e.

useEffect(() => {
  handleConnectWallet();
  // eslint-disable-next-line
}, []);
Aun Shahbaz Awan
  • 559
  • 7
  • 10
0

Just pass the function as the argument in the array of useEffect...

useEffect(() => {
   functionName()
}, [functionName])
Manish
  • 471
  • 1
  • 10
  • 22
0

using UseEffect fetchBusinesses calling function declare in useEffect() by declaring a const variable after that calling the name of the function,

useEffect(()=>{
const fetchBusinesses=()=>{
   console.log(useeffect fetchbussinesses functions)
}
 fetchBusinesses();
},[declare the variable used in useeffect hooks ])
Vignesh
  • 174
  • 1
  • 5
0

To disable this warning in your project

add this "react-hooks/exhaustive-deps": "off" to .eslintrc.js file

Raad Altaie
  • 1,025
  • 1
  • 15
  • 28
-1

You are making use of useEffect and when you do that, very frequently you want to make use of some variables that are used as props or state inside your component.

There is a rule built into eslint that wants you to reference any different prop or piece of state inside of the useEffect dependency array. That's the array that controls when useEffect gets executed. That rule wants to see it listed inside that array which decides when to re-run the useEffect function.

So you would need to add in [fetchBusinesses] and the warning should be gone.

Now, why does that rule wants us to put that in there?

There are some scenarios where making use of useEffect and not properly listing out all the pieces of state and props inside the array can lead to weird and hard to debug problems.

So this rule is to help avoid those hard to understand problems that can come up with useEffect.

Now, arbitrarily adding to that array can lead to bugs as well. So either way you are running into bugs you have to solve. According to your comments that seemed to solve it for you, but I would have wanted to investigate further to see if you by any chance got a second GET request in your Network tab in Chrome after adding in the fetchBusinesses function to your useEffect array.

halfer
  • 19,824
  • 17
  • 99
  • 186
Daniel
  • 14,004
  • 16
  • 96
  • 156