In my previous blog post, I discussed useMemo. I received valuable insights in the comments, pointing out the importance of caution when combining useMemo with useEffect since it could lead to an infinite loop. So, in today's post, I aim to delve deeper into this topic, focusing specifically on when this issue may arise.
useEffect(setup, dependencies?)
The setup
is a function and runs every time some values in the dependencies
array are changed.
const cachedValue = useMemo(calculateValue, dependencies)
The calculateValue
is a function and runs every time some values in the dependencies
array are changed, and then the new value is cached.
Let's take a look at when an infinite loop occurs.
Here's an example:
import { useEffect, useMemo, useState } from "react"
export const InfiniteCountUp = () => {
const [count, setCount] = useState(0)
const cachedMemo = useMemo(() => count * 2, [count])
useEffect(() => {
console.log("Triggered")
}, [cachedMemo])
return (
<div>
<p>{cachedMemo}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
)
}
Every time the Increment button is clicked, the count
is updated, which triggers the recalculation of the cachedMemo
value due to the dependency on count
in the useMemo dependency array. Subsequently, the useEffect is triggered as it depends on the updated cachedMemo
value.
This sequence of events could potentially lead to an infinite loop.
Another example is when fetching data.
import { useEffect, useMemo, useState } from "react"
export const InfiniteDataFetching = ({ postId }) => {
const [post, setPost] = useState("")
const cachedMemo = useMemo(() => {
const { postId } = post
return postId
}, [post])
useEffect(() => {
const fetchData = async () => {
try {
const res = await fetch(`https://example.blog/${postId}`)
const data = await res.json()
setPost(data)
} catch (error) {
console.error(error)
}
}
fetchData()
}, [postId, cachedMemo])
return <div>{post && <p>{post.article}</p>}</div>
}
In this example, each time the postId
changes, new data is fetched, setting the new post. This setup creates a potential infinite loop: changes in postId
trigger useEffect, and the memoized cachedMemo
value is recalculated during each render, potentially leading to repeated calls of the effect.
To avoid an infinite loop, it would be better to carefully consider the overall flow. As the code gets larger, it's easy to fix a smaller part without seeing the whole picture. So, taking a step back and understanding the interactions between different parts of the code can help us avoid an infinite loop and decide which dependencies truly need to be included.
Top comments (0)