Apologies. React noob here.
Got an issue where my code has stuck on a never ending loop. I’ve stripped the code down only to the part I believe that’s causing the problem.
function Packages(){ const [packages, setPackages] = useState([]); useEffect(() => { if(!packages){ getPackages(); } }); const getPackages = async () => { const resp1 = await instance.get('https://jsonplaceholder.typicode.com/todos/1'); setPackages(resp1); }; }
I’ve used the useEffect
method to match the componentDidMount. Is that correct?
The moment I comment “setPackages(resp1);” the never ending loop stops. I probably am using the useEffect and useState hooks wrong.
Appreciate any guidance on this.
Advertisement
Answer
If you don’t pass a second argument to the useEffect
hook, it will execute every time your component re-renders.
If you want to fetch the data only once after the component has mounted, then pass an empty dependency array to the useEffect
hook.
useEffect(() => { if(!packages){ getPackages(); } }, []);
Doing this will fix the infinite execution of useEffect
hook BUT now React will throw a warning about missing dependencies of useEffect
hook and that is because you will need to add the function getPackages
in the dependency array of the useEffect
hook BUT this will also lead to infinite execution of the useEffect
hook.
You have two options to fix this problem:
Get rid of
getPackages
function and move its code inside theuseEffect
hookuseEffect(() => { if(!packages){ (async () => { const resp1 = await instance.get(...); setPackages(resp1); })(); } }, []);
Use the
useCallback
hook to memoize thegetPackages
function so that it can be safely added in the dependency array of theuseEffect
hook.useEffect(() => { if(!packages){ getPackages(); } }, [getPackages]); const getPackages = useCallback(async () => { const resp1 = await instance.get(...); setPackages(resp1); }, []);
Also you don’t need the folowing check inside the useEffect
hook:
if(!packages) { ... }
This check is not only unnecessary but will also lead to problems:
Using
packages
inside theuseEffect
will lead to a warning about the missing dependencies of theuseEffect
hook. This can be fixed by addingpackages
in the dependency array of theuseEffect
hookIf you add
packages
in the dependency array of theuseEffect
hook, it can lead to your original problem, i.e. infinite execution of theuseEffect
hook.useEffect
hook shouldn’t update the state that is specified in its dependency array.As the initial value of
packages
is an array,if (!packages)
condition will never be true because an array is a truthy value and inverting it will always evaluate to false.You probably meant to write
if (!packages.length)
but as mentioned before, this check is unnecessary.
Here’s how I would rewrite your component:
function Packages() { const [packages, setPackages] = useState([]); useEffect(() => { instance .get('https://jsonplaceholder.typicode.com/todos/1') .then((data) => setPackages(data)) .catch(error => { /* handle error */ }; }, []); }
For further reading on useEffect
hook, read: Using the Effect Hook