DEV Community

Muhammad Yusuf
Muhammad Yusuf

Posted on

Create react form without making multiple requests

If you're reading this, maybe you've been in a situation where you're making a react form where you hit an API when you type in the search box, but the downside is when you type 'batman', the axios made 6 different requests which is 'b', 'ba', 'bat', 'batm', 'batma', and the actual query we're looking for, 'batman'. I hope this article works for you. Let's get started!

Assuming you've initialized the react app by using:

npx create-react-app .
Enter fullscreen mode Exit fullscreen mode

and installed axios by using:

npm i axios
Enter fullscreen mode Exit fullscreen mode

Let's start with initial code in app.js, we'll make a book search feature:

import './App.css';

function App() {
  return (
    <div className="App">
        <input type="text" />
        <div>Result</div>
      <div>Loading...</div>
      <div>Error</div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Inside the default App div, we put a text input and three different divs, each for result, loading, and error.

Now we create our states by importing useState and useEffect:

import { useState, useEffect } from 'react'
Enter fullscreen mode Exit fullscreen mode

and define our book states at the beginning of the App function (before return):

const [query, setQuery] = useState('')
const [loading, setLoading] = useState(true)
const [error, setError] = useState(true)
const [books, setBooks] = useState([])
Enter fullscreen mode Exit fullscreen mode

Then we'll create the axios request in the useEffect hook:

useEffect(() => {
  setLoading(true)
  axios({
    method: 'get',
    url: 'http://openlibrary.org/search.json',
    params: { q: query }
  })
    .then(response => {
        let data = response.data.docs.map(book => book.title)
      console.log({ query, data })
        setBooks(data)
        setLoading(false)
    })
    .catch(err => {
        setError(err)
        setLoading(false)
    })
}, [query])
Enter fullscreen mode Exit fullscreen mode

We set the loading value with true to indicate that the axios is sending a request to the url, and set it to false after we get either result. And we set the books value with only their title if it succeeded, and set the error if it failed.

Then we 'rework' the three divs like this:

{
  books.map((book, index) => {
    return <div key={book}>{ book }</div>
  })
}
{
  loading &&
  <div>Loading..</div>
}
{
  error &&
  <div>Error</div>
}
Enter fullscreen mode Exit fullscreen mode

And after you run it by using:

npm run start
Enter fullscreen mode Exit fullscreen mode

and type 'batman', it should look like this:
Chrome console showing 6 requests

Notice the queries were finished in an order we don't want, so the most recent query we'll get is the query 'b' instead of 'batman'. So now we'll make it so the axios only take the last change made in the input, which is 'batman'.

axios.CancelToken

Actually axios has an option called cancelToken that can be used to cancel the request. How do we do that? By putting it to the option when we make a request:

useEffect(() => {
  let cancel;
  console.log('axios called')
  axios({
    method: 'get',
    url: 'http://openlibrary.org/search.json',
    params: { q: query },
    cancelToken: new axios.CancelToken(c => cancel = c)
  })
    .then(...)
    .catch(...)
  return () => cancel()
})
Enter fullscreen mode Exit fullscreen mode

The cancel variable will then hold the cancel function from axios.CancelToken, and will be called in useEffect callback return so axios will be cancelled once another useEffect is triggered.

Now we can test it out by running:

npm run start
Enter fullscreen mode Exit fullscreen mode

And type 'batman' in our search box, we'll only get one request
Chrome console showing one request

Notice that axios was called 6 times, but only 'batman' request was sent, because the 'b', 'ba', 'bat', 'batm', 'batma' requests are cancelled.

Lastly, we'll get an error for every request cancellation, but because that is intended we can filter the error out by adding conditional in our catch block:

.catch(err => {
  if (axios.isCancel(err)) return
  ...
})
Enter fullscreen mode Exit fullscreen mode

source:
Web Dev Simplified
https://www.youtube.com/watch?v=NZKUirTtxcg

Axios Documentation
https://github.com/axios/axios

Top comments (0)