Photo by Lautaro Andreani on Unsplash
How to use React useEffect to post data and refresh without infinite callback
Table of contents
Introduction
This tutorial will walk you through updating your page DOM after an API call, using react hooks.
In this example, we want to show new items added to the list when the user clicks on the add Post
button. After adding a new post to the list, we want to trigger the fetch API to update the list the user can see. The default behavior of React useEffect()
can lead to an infinite loop, this can lead to performance issues.
Requirements
This tutorial requires a basic understanding of the following concepts
- React
- React Hooks (
useEffect(), useState()
) Axios
API module (fetching and posting data) - Please install through npmcreate-react-app
The problem
Create a component called Posts
with the code below.
const Posts = () => {
const [posts, setPosts] = useState([])
const baseUrl = 'https://jsonplaceholder.typicode.com/posts/'
const getPosts = async () => {
try {
const userPosts = await axios.get(baseUrl)
setPosts(userPosts.data)
} catch (error) {
console.error(error.message)
}
}
useEffect(() => {
getPosts()
}, [])
/* Hook to add post name through API */
const [newPost, setNewPost] = useState({ postName: '' })
const handleChange = (event) => {
setNewPost({ ...newPost, [event.target.name]: event.target.value })
}
const handleSubmit = (e) => {
e.preventDefault()
const url = baseUrl + 'add/'
axios.post(url, {
name: newPost.postName,
user: 1
})
.then(response => {
console.log(response.data)
})
.catch(error => {
console.log(error.response.data)
})
e.target.reset()
}
return (
<div>
<form onSubmit={handleSubmit}>
<input name="postName" onChange={handleChange} required />
<button type="submit">Add Post</button>
</form>
{posts.map(post=>(
<li key={post.id}>{post.title}</li>
))}
</div>
)
}
In the code above, we created a functional component called Posts
that fetches and displays data from an API using axios
. This component also has a form to add a new post name.
💡 The problem with this code is that when a new post name is added by
axios.post
to the database, the user cannot see it immediately. Unless they refresh the page.
By adding the dependency posts
to useEffect()
. When a new post name is added, the list will automatically update for the user to see the new post name.
useEffect(() => {
getPosts()
}, [posts]) #<----new
The problem with this solution is that if you include a simple console.log('I was hit')
in the getPost()
function, you will see that the useEffect()
runs continuously.
Because adding posts
as a dependency leads to useEffect()
being called continuously because of the way object reference works in JavaScript. See react useEffect infinite loop.
The Solution
The ideal solution is to use a dependency that is more predictable, like a simple count. This will be updated only after a successful axios
POST
request; when a post has been added successfully.
Let us update our code.
const [postLength, setPostLength] = useState(0) #<----new
useEffect(() => {
getPosts()
}, [postLength]) #<----new
And add setPostLength(postLength + 1)
to the successful callback of our API call.
const handleSubmit = (e) => {
e.preventDefault()
const url = baseUrl + 'add/'
axios.post(url, {
name: newPost.postName,
user: 1
})
.then(response => {
console.log(response.data)
setPostLength(postLength + 1) #<----new
})
.catch(error => {
console.log(error.response.data)
})
e.target.reset()
}
What we have done here, is to add a new state object called postLength
, and use it as the dependency for useEffect()
.
Now the useEffect()
will be listening for changes to postLength
. And its value changes, only when will getPosts()
is succesful.
The API GET request for all posts will only be called when a new post is added. We ensured that by adding the setPostLength(postLength + 1)
to the successful callback of our API POST request.
With this solution, we can update our list after every addition to the list. While avoiding the infinite loop of react useEffect()
when using dependencies.
This solution can also be extended to other operations like editing or deleting any item in the list. All you have to do is change the state of your dependency after a successful API call.