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