In general, infinite loops are considered bad practices. But in some edge cases, you don't have any choice other than an infinite loop. It is good to know the infinite loop patterns of React.
When the infinite loop is running without any stop, eventually browser will kill the tab that your code is running. So don't use Infinite Loop
without any breaking point.
useEffect
The useEffect hook allows us to perform side effects in a component. When hooks were introduced the react 16, the useEffect hook
gained more traction than any other hooks. Because it provides combined functionalities of componentDidMount
, componentDidUpdate
and componentWillUnmount
life cycle methods.
useEffect hook triggers the callback function, only if the dependencies were changed. And it uses shallow
comparison to compare the values of the hook.
You can consider the useEffect as a power stone, it is a most powerful stone and if you haven't handled it properly the stone would destroy you.
Without dependency
useEffect without dependencies generally consider a bad practice, so always try to avoid it.
Consider the following code, it will call the API forever.
What's happening?
If the useEffect is triggering the callback only when the dependencies are changes, why we ended with an infinite loop here.
You need to take into account important another mantra of React which is “When state or Props is changing, the component will re-render”.
In this code, we set the state value using setData
on-network call success, it will trigger the component re-render. Since the useEffect doesn't have value to compare, so it will call the callback.
And Again Fetch will trigger setData
and setData
will trigger, the component re-render and so on.
How to fix this issue?
We need to specify the dependencies as the empty array.
According to official docs, it is not safe to omit the dependencies
Function as dependency
useEffect uses shallow object comparison to determine, whether the data was changed or not. Due to weird JavaScript conditional systems 😈.
Let's consider the following code:
The function getData
is passed as dependencies.
When you run this code, it will throw Maximum update depth exceeded
which means the code having an infinite loop.
What's happening?
Since useEffect uses the shallow comparison for the compare the values. The shallow comparison for the function will always give false.
How to fix it?
To fix this issue, we need to use another infinity stone called useCallback
.
useCallback
return a memoized version of callback, which only change when the dependencies change.
Array as dependency
As you might know, the shallow comparison for two is always false, so passing dependencies as an array will also lead to Infinite Loop
.
Let's consider the following code:
Here, the array dep
passed as dependencies for the useEffect.
When you run this code, the browser console will throw this error.
What's happening?
The shallow comparison of two arrays is always false, so useEffect
will always trigger the callback.
How to fix it?
Since the useCallback
return is a function, we can't use that.
So, guess what?
We need to use another hook called useRef
.
useRef
returns a mutable object with .current
having the initial value.
Objects as dependency
You might guess the shallow comparison of two objects is always false, so useEffect
will trigger the callback always.
Let us consider this code:
data
is passed as the dependencies for the useEffect
.
When you run this code, your browser console will be thrown an infinity loop error.
What's happening here?
The shallow comparison of objects would be false always, so it will trigger the useEffect's callback.
How to fix it?
If we memoize the dependencies, we break the infinity loop. So how to do that?
Yes, we are going to use another infinity stone called useMemo
.
useMemo
will only recompute the memoized value when the dependencies have changed.
Wrong dependency
Wrong dependencies are nothing to do with React
not even javascript
. When the wrong dependencies are used, we must take the blame.
Let us consider the code:
I hope there is no need to explain this issue pattern and its fixes. If you want the explanation and fixes, please let me know in the comment.
Note: There is plenty of ways to avoid infinite loop inside a React component, I mentioned only a few ways.
Always use eslint-plugin-react-hooks or create-react-app, it will help you to find those issues before those get into the production server.
A company lost 72k in a week due to an infinite loop.
So always give special care to our power stone (useEffect
).
https://www.nerdyviews.com/2019/06/infinity-gauntlet-keychain.html
Further reading: Memoization Demystified in 7 Minutes.