Axios is a powerful HTTP client that allows to easily implement Ajax requests in a JavaScript app. We’ve covered the basics of using Axios with React here, so you can read that first if Axios or Axios + React is all new to you.
In this tutorial we’ll be building a live search feature inside a React app with the help of Axios. Our app will allow us to do a simple movie search using the API from themoviedb.org.
This tutorial is divided into 3 section:
This tutorial assumes that you have some experience using React, so we’ll skip the initializing step to save our valuable time. You can use your any favorite boilerplate, and in this tutorial we’ll simply use Create React App to initialize the app.
Once the app is initialized, let’s add axios
to it:
$ yarn add axios or npm i axios
Next, copy the code below to your App
component:
import React, { Component } from 'react';
import axios from 'axios';
import Movies from './Movies';
class App extends Component {
state = {
movies: null,
loading: false,
value: ''
};
search = async val => {
this.setState({ loading: true });
const res = await axios(
`https://api.themoviedb.org/3/search/movie?query=${val}&api_key=dbc0a6d62448554c27b6167ef7dabb1b`
);
const movies = await res.data.results;
this.setState({ movies, loading: false });
};
onChangeHandler = async e => {
this.search(e.target.value);
this.setState({ value: e.target.value });
};
get renderMovies() {
let movies = <h1>There's no movies</h1>;
if (this.state.movies) {
movies = <Movies list={this.state.movies} />;
}
return movies;
}
render() {
return (
<div>
<input
value={this.state.value}
onChange={e => this.onChangeHandler(e)}
placeholder="Type something to search"
/>
{this.renderMovies}
</div>
);
}
}
export default App;
Note: Movies
is just presentational/dumb component and simply renders the data we give it. It does not touch our data.
So, we have a controlled input
element that calls onChangeHandler
method when we type something in. onChangeHandler
changes the value property in the state
and calls the search
method, giving it the input’s value as an argument.
Take the following piece of code from above:
search = async val => {
this.setState({ loading: true });
const res = await axios(
`https://api.themoviedb.org/3/search/movie?query=${val}&api_key=dbc0a6d62448554c27b6167ef7dabb1b`
);
const movies = await res.data.results;
this.setState({ movies, loading: false });
};
In the search
method we are making a GET
request to our API to get the movies we want. Once we get the results we update the component’s state
via setState
. And when we change the state via setState
, the component re-renders with the changed state.
Simple as that!
You may notice that we send requests every time when we update the input. This can lead to an overload of requests, especially when we receive large responses.
To see this problem in action, open the network tab in your browser’s DevTools. Clear the network tab. Type some movie’s name in the input.
As you see we’re downloading all the data every time a keystroke happens. To solve this issue let’s create a utils.js
file in the src
directory:
$ cd src
$ touch utils.js
Copy the following code into utils.js
:
import axios from 'axios';
const makeRequestCreator = () => {
let token;
return (query) => {
// Check if we made a request
if(token){
// Cancel the previous request before making a new request
token.cancel()
}
// Create a new CancelToken
token = axios.CancelToken.source()
try{
const res = await axios(query, {cancelToken: cancel.token})
const result = data.data
return result;
} catch(error) {
if(axios.isCancel(error)) {
// Handle if request was cancelled
console.log('Request canceled', error.message);
} else {
// Handle usual errors
console.log('Something went wrong: ', error.message)
}
}
}
}
export const search = makeRequestCreator()
Change the App
component as well to make use of our new utility function:
// ...
import { search } from './utils'
class App extends Component {
// ...
search = async val => {
this.setState({ loading: true });
// const res = await axios(
const res = await search(
`https://api.themoviedb.org/3/search/movie?query=${val}&api_key=dbc0a6d62448554c27b6167ef7dabb1b`
);
const movies = res;
this.setState({ movies, loading: false });
};
// ...
Axios
has so called cancel tokens that allow us to cancel requests.
In makeRequestCreator
we create a variable called token
. Then with a request, if the token
variable exists we call its cancel
method to cancel the previous request. Then we assign token
a new CancelToken
. After that we make a request with the given query and return the result.
If something goes wrong we catch the errors in the catch
block and we can check and handle whether a request was cancelled or not.
Let’s see what happens in the network tab now:
As you see we downloaded only one response. Now our users pay for only what they use.
If we type the same text in the input multiple times, again, we make a new request each time.
Let’s fix this. We will change our utility function in utils.js
a little bit:
const resources = {};
const makeRequestCreator = () => {
let cancel;
return async query => {
if (cancel) {
// Cancel the previous request before making a new request
cancel.cancel();
}
// Create a new CancelToken
cancel = axios.CancelToken.source();
try {
if (resources[query]) {
// Return result if it exists
return resources[query];
}
const res = await axios(query, { cancelToken: cancel.token });
const result = res.data.results;
// Store response
resources[query] = result;
return result;
} catch (error) {
if (axios.isCancel(error)) {
// Handle if request was cancelled
console.log('Request canceled', error.message);
} else {
// Handle usual errors
console.log('Something went wrong: ', error.message);
}
}
};
};
export const search = makeRequestCreator()
Here we created a resources
constant which keeps our downloaded responses. And when we are doing a new request we first check if our resources
object has a result for this query. If it does, we just return that result. If it doesn’t have a suitable result we make a new request and store the result in resources
. Easy enough!
Let’s summarize everything in a few words. Every time when we type something in the input
:
If you’re interested, you can find a Redux version of this app in this repo
Congrats 🎉🎉🎉! We’ve built a live search feature which doesn’t download unnecessary responses as well as caches responses. I hope you’ve learned a thing or two about how to build an efficient live search feature in React with the help of Axios.
Now, our users spend as little traffic data as possible. If you enjoyed this tutorial, please share it out! 😄
You can find final result and source code here for this post in this CodeSandbox.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!