I have a function that is being used in my useEffect hook and I had to add in the dependency array. I also have some other dependencies that are being passed as props from an external component. Because of that dependency function, I had to wrap my function in a useCallback for it to avoid to change on every render. But I'm confused why the error is still popping out? And also how should I get accross it ?
import _ from "lodash";
import React, { useCallback, useEffect, useState } from "react";
function App({uniqueCol,data,handleSelect, unselectAll = false}) {
const [selected, setSelected] = useState(new Set(""));
const [currentRows, setCurrentRows] = useState(data);
//here it is fine when I add currentRows and uniqueCol as dependencies
const changeSelect = useCallback(
(id: string, status: boolean) => {
const cr = currentRows.map((row) => {
if (uniqueCol) {
// @ts-ignore
if (row[uniqueCol] == id) row.selected = status;
}
return row;
});
setCurrentRows(cr);
},
[currentRows, uniqueCol]
);
useEffect(() => {
selected.forEach((sel) => changeSelect(sel, true));
setCurrentRows(data);
}, [changeSelect, data, selected]);
/*here it causes errors when I add changeSelect, currentRows,
handleSelect as dependencies but works fine when I remove them*/
const unSelectAll = useCallback(() => {
setSelected(new Set(""));
_.map(currentRows, "id").forEach((val) => {
changeSelect(val, false);
});
if (handleSelect) handleSelect([]);
}, [changeSelect, currentRows, handleSelect]);
//another useEffect that is using the function unSelectAll as a dependency
useEffect(() => {
unSelectAll();
}, [unSelectAll, unselectAll]);
return <h1>Anything here</h1>;
}
The error I'm getting is:
react-dom.development.js:67 Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
The code that you have posted doesn't include "the full story", but I feel it is reasonable to assume the following, because you said "I had to wrap my function in a useCallback for it to avoid to change on every render",
but your usage of useCallback() has practically no effect here:
changeSelect will change on every render, because:
changeSelect has currentRows in the dependencies, andchangeSelect calls setCurrentRows()unSelectAll will change on every render, because:
unSelectAll has changeSelect in the dependencies, andchangeSelect changes on every renderSo a simplified version to illustrate the "recursive" dependencies would be:
function App(){
const [currentRows, setCurrentRows] = useState(data);
const changeSelect = useCallback(
() => { setCurrentRows([]); },
[ currentRows, uniqueCol ]
);
const unSelectAll = useCallback(
() => { changeSelect(); },
[ changeSelect, currentRows ]
);
return <>...</>;
}
This is only a guess, but probably some other code - probably the useEffect which you mentioned, but haven't posted the code - is using unSelectAll
as a dependency, and changes maybe uniqueCol, so that
App re-renders if uniqueCol changeschangeSelect changes on every renderunSelectAll changes because changeSelect changesuniqueCol changes because unSelectAll changes (probably ? in some other code ?)At least probably this is the root cause, and the actual recursive chain (the infinite loop) is closed somewhere else, for this or a similar reason.