When you try to execute an async activity inside the React useEffect hook, you might have seen the following warnings:
Effect callbacks are synchronous to prevent race conditions. Put the async function inside:
useEffect function must return a cleanup function or nothing
Consider the following code:
import { useEffect, useState } from "react"
function App() {
const [posts, setPosts] = useState([])
useEffect(async () => {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/posts`)
const data = await response.json()
setPosts(data)
} catch (e) {
console.error(e)
}
}, [])
return (
<div className="App">
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
)
}
export default App
Here, we are passing async function to the useEffect hook. As you may be aware, async functions return a Promise. However, useEffect expects the function to either return nothing or a clean up function. Hence reacts throws this warning.
There are 2 ways to fix this.
Moving async call to another function
We can define another function inside the useEffect and call it inside the useEffect as shown below:
import { useEffect, useState } from "react"
function App() {
const [posts, setPosts] = useState([])
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts`
)
const data = await response.json()
setPosts(data)
} catch (e) {
console.error(e)
}
}
fetchData()
}, [])
return (
<div className="App">
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
)
}
export default App
Now the function passed to useEffect returns nothing, thus by fulfilling the condition.
Also, you could prevent the race condition by cancelling previous requests.
Using .then() chaining
Instead of using async await syntax, we can use .then()
to resolve the promise:
import { useEffect, useState } from "react"
function App() {
const [posts, setPosts] = useState([])
useEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/posts`)
.then(response => response.json())
.then(data => {
setPosts(data)
})
.catch(e => {
console.log(e)
})
}, [])
return (
<div className="App">
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
)
}
export default App
Now if you run the code, you should not be seeing the warning anymore.
Top comments (0)