使用Axios在React中创建实时搜索功能

来自菜鸟教程
跳转至:导航、​搜索

Axios 是一个强大的 HTTP 客户端,允许在 JavaScript 应用程序中轻松实现 Ajax 请求。 我们在这里 介绍了将 Axios 与 React 一起使用的基础知识,因此如果 Axios 或 Axios + React 对您来说是全新的,您可以先阅读它。

在本教程中,我们将借助 Axios 在 React 应用程序中构建实时搜索功能。 我们的应用程序将允许我们使用来自 themoviedb.org 的 API 进行简单的电影搜索。

本教程分为 3 个部分:

初始化应用程序

本教程假设您有一些使用 React 的经验,因此我们将跳过初始化步骤以节省我们宝贵的时间。 您可以使用您喜欢的任何样板,在本教程中,我们将简单地使用 Create React App 来初始化应用程序。

初始化应用程序后,让我们添加 axios 到它:

$ yarn add axios or npm i axios

接下来,将以下代码复制到您的 App 组件中:

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;

注意:Movies 只是展示/哑组件,只是渲染我们给它的数据。 它不会触及我们的数据。


输入组件

因此,我们有一个受控的 input 元素,当我们输入内容时它会调用 onChangeHandler 方法。 onChangeHandler 更改 state 中的 value 属性并调用 search 方法,将输入值作为参数提供给它。

搜索

从上面获取以下代码:

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 });
};

search 方法中,我们向 API 发出 GET 请求以获取我们想要的电影。 一旦我们得到结果,我们通过 setState 更新组件的 state。 当我们通过 setState 更改状态时,组件会重新渲染更改后的状态。

就那么简单!

防止不必要的请求

您可能会注意到 每次更新输入 时都会发送请求。 这可能导致请求过载,尤其是当我们收到大量响应时。

要查看此问题的实际效果,请在浏览器的 DevTools 中打开网络选项卡。 清除网络选项卡。 在输入中键入一些电影的名称。

如您所见,每次击键时我们都会下载所有数据。 为了解决这个问题,让我们在 src 目录中创建一个 utils.js 文件:

$ cd src
$ touch utils.js

将以下代码复制到 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()

更改 App 组件以使用我们的新实用函数:

// ...
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 有所谓的 取消令牌 允许我们取消请求。

makeRequestCreator 中,我们创建了一个名为 token 的变量。 然后通过请求,如果 token 变量存在,我们调用它的 cancel 方法来取消之前的请求。 然后我们为 token 分配一个新的 CancelToken。 之后,我们使用给定的查询发出请求并返回结果。

如果出现问题,我们会在 catch 块中捕获错误,我们可以检查并处理请求是否被取消。

现在让我们看看网络选项卡中发生了什么:

如您所见,我们只下载了一个回复。 现在我们的用户只为他们使用的东西付费

缓存 HTTP 请求和响应

如果我们在输入中多次输入相同的文本,那么我们每次都会发出一个新的请求。

让我们解决这个问题。 我们将稍微改变 utils.js 中的效用函数:

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()

在这里,我们创建了一个 resources 常量来保存我们下载的响应。 当我们做一个新的请求时,我们首先检查我们的 resources 对象是否有这个查询的结果。 如果是这样,我们只返回该结果。 如果没有合适的结果,我们会发出一个新请求并将结果存储在 resources 中。 够简单!

让我们用几句话来总结一下。 每次我们在 input 中键入内容时:

  • 如果有的话,我们会取消之前的请求。
  • 如果我们已经有我们键入的内容的先前结果,我们只需返回它而不发出新请求。
  • 如果我们没有那个结果,我们会制作一个新的并存储它。

如果你有兴趣,你可以在 this repo 中找到这个应用程序的 Redux 版本

结论

恭喜🎉🎉🎉! 我们构建了一个实时搜索功能,它不会下载不必要的响应以及缓存响应。 我希望你已经了解了如何在 Axios 的帮助下在 React 中构建 高效 实时搜索功能。

现在,我们的用户花费尽可能少的流量数据。 如果您喜欢本教程,请分享出去! 😄

您可以在 this CodeSandbox 中找到这篇文章的最终结果和源代码。