0

I am using a useEffect hook and am getting a compiler error saying React Hook useEffect has missing dependencies. Either include them or remove the dependency array react-hooks/exhaustive-deps

It says this for both functions. I was wondering how the useCallBack hook would be used in this instance. I haven't had much luck finding anything for it. Any help to get rid of these compiler warnings would be appreiated!

PetullaPapi
  • 41
  • 1
  • 5

2 Answers2

1

I asume all the code you posted is in your React component. Not sure how you work with the returned values from the methods. It's not clear from the example code.

First about the second error after you have added array of dependecies - both functions are redeclared on each render. Thus React thinks they changed and will rerun the hook. You have to wrap both credits and gpa methods in useCallback to avoid this with data as a dependency.

Personally I would refactor both methods to pure functions. Meaning they will take data as an argument of the function instead of taking it from upper scope. Then if you wrap both of them in useCallback they will never change since there will be no dependecy. Then you would add both methods to useEffect hook dependency together with data and both functions will be calculated once the data change. The result will be same as if oyu would just wrap the methods in useCallback. But I think with pure functions, the code is much more easy to reason about. You have two methods which expects some argument and once the data change you run those methods to recalculate some changes.

This is how I would refactro the code:

const credits = useCallback((courses) => {
  let credits = 0
  courses.forEach((c) => (
    credits += parseInt(c.courseCredits)
  ))
  if(isNaN(credits)) credits = 0
  return credits
}, []);   // <- you have no dependencies, so it will be always same function for entire component lifecycle
  
  const gpa = useCallback((courses) => {
    let honorPoints = 0
    let credits = 0
    let cumulativeGpa = 0
    courses.forEach(c => {
      credits += parseInt(c.courseCredits)
      honorPoints += parseFloat(getGradeNumber(c.numericGrade) * c.courseCredits)
      cumulativeGpa = honorPoints / credits
    })
    if(isNaN(cumulativeGpa)) cumulativeGpa = 0
    return cumulativeGpa.toFixed(2)
  }, []);   // <- you have no dependencies, so it will be always same function for entire component lifecycle

  useEffect(() => {
    credits(data)
    gpa(data)
  }, [credits, gpa, data]); // <- credits and gpa will never change so the useEffect will run only when data change
Dharman
  • 30,962
  • 25
  • 85
  • 135
  • +1. With these being pure functions, you don't even need to wrap these in `useCallback` or have the pure functions as dependencies for `useEffect`. But this would be the way to do it if there's more to this than what's posted in the question. – Jacob Nov 03 '20 at 21:27
  • Yeah, if they are pure functions without any dependency, it's probably best to declare them outside of component (then you can re-use them in other components as well). Then they will always be the same and are not needed to be listed in hook's dependecies. – Tomáš Pustelník Nov 04 '20 at 07:39
0

@Tomáš Pustelník is right, but i have another solution for you. You can disable eslint for example:

  useEffect(() => {
    credits()
    gpa()
    // eslint-disable-line react-hooks/exhaustive-deps
  }, [data]);
Hossein Mohammadi
  • 1,388
  • 11
  • 16