'Infinity loops with useEffect and useCallback - Reactjs
somehow I got an infinity loop, the weird situation that I did solve it but I got a warning and I wish to fix the warning.
this is code that work:
import { ArrowDropDown, ArrowRight } from "@material-ui/icons";
import React, { useState, useEffect, useCallback } from "react";
import "./tree.css";
const Tree = ({ explorer }) => {
const [expand, setExpand] = useState(true);
const [arrow, setArrow] = useState(false);
const stateHandler = () => {
setExpand(!expand);
setArrow(!arrow);
};
useEffect(() => {
//this function will display only the first Tree as init the page.
stateHandler();
}, []);
return (
<div>
<div className="treeInfo">
{arrow ? (
<ArrowDropDown className="treeIcon" />
) : (
<ArrowRight className="treeIcon" />
)}
<span className="treeTitle" onClick={stateHandler}>
{explorer.name}
</span>
</div>
<div
style={{
display: expand ? "block" : "none",
paddingLeft: 20,
cursor: "pointer",
}}
>
{explorer.items.map((explore) => {
return <Tree key={explore.id} explorer={explore} />;
})}
{/* {explorer.items.map((explore) => (
<Tree explorer={explore} />
))} */}
</div>
</div>
);
};
export default Tree;
and this is the warning:
src\components\Tree\Tree.js
Line 17:6: React Hook useEffect has a missing dependency: 'stateHandler'. Either include it or remove the dependency array react-hooks/exhaustive-deps
Search for the keywords to learn more about each warning.
To ignore, add // eslint-disable-next-line to the line before.
WARNING in src\components\Tree\Tree.js
Line 17:6: React Hook useEffect has a missing dependency: 'stateHandler'. Either include it or remove the dependency array react-hooks/exhaustive-deps
webpack compiled with 1 warning
**
- The result : (it's good its what I want to display a tree view of files)
**
well as I can read I got this warning because useEffect got not dependencies, when I add the stateHandler dependencies I got an infinity loop, so I add a callback function but it still doesn't solve the infinity loop. this is the code with the useCallback (its the same code, just with useCallback and a bit of configure of the useEffect):
const initTree = useCallback(() => {
setExpand(!expand);
setArrow(!arrow);
}, [setExpand, setArrow, arrow, expand]);
useEffect(() => {
//this function will display only the first Tree as init the page.
initTree();
}, [initTree]);
Solution 1:[1]
You need to change this:
const initTree = useCallback(() => {
setExpand(!expand);
setArrow(!arrow);
}, [setExpand, setArrow, arrow, expand]);
to this:
const initTree = useCallback(() => {
setExpand(e => !e);
setArrow(a => !a);
}, []);
Otherwise what happens is this:
- The component renders,
initTreevariable is initialized with a function, theEffectis run andsetExpandandsetArroware called. - That triggers a new render
- The
useCallbackhook checks if the deps ofinitTreehave changed and yes,arrowandexpandhave changed indeed, henceinitTreevariable is updated with a new function - The effect checks if
initTreehas changed from the previous render, and yes, it has changed, hence the effect executes again callinginitTreeagain. - There you are stuck in an infinite render loop.
Eslint shouldn't complain of missing deps if you don't put the setState in deps, since they do not change during renders, unless you are passing them through props.
Solution 2:[2]
If you are ok with having another state, you can simply set something like a loading state and make the useEffect depend on that
State would look like this:
const [expand, setExpand] = useState(true);
const [arrow, setArrow] = useState(false);
const [loading, setLoading] = useState(true);
And then based on this, useEffect would change as follows:
useEffect(() => {
//this function will display only the first Tree as init the page.
stateHandler();
setLoading(false);
}, [loading]);
Once loading is set to false, it's value is not going to change and the useEffect will not be triggered indefinitely
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Aneesh |

