Creating a Live Search Feature in React Using Axios
source link: https://www.tuicool.com/articles/hit/nIFnMna
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
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 withReact 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:
- Part 1: How to make live search work in React with Axios
- Part 2:Preventing unnecessary requests
- Part 3:Caching HTTP requests and resonses
:crocodile: Alligator.io recommends ⤵
Fullstack Advanced React & GraphQL by Wes BosInitializing the App
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 useCreate 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.
Input Component
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.
Search
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!
Preventing Unnecessary Requests
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.results; this.setState({ movies, loading: false }); }; ...
What’s happening there now?
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 .
Caching HTTP Requests and Responses
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); } } }; };
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
:
- We cancel the previous request, if any.
- If we already have a previous result for what we typed we just return it without making a new request.
- If we don’t have that result we make a new one and store it.
If you’re interested, you can find a Redux version of this app in this repo
Conclusion
Congrats :tada::tada::tada:! 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! :smile:
You can find final result and source code here for this post in this CodeSandbox .
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK