简体   繁体   English

在 React 中从 Axios 获取数据的问题

[英]Problem fetching data from Axios in React

I'm creating a search that will print out results from the following API: https://jsonplaceholder.typicode.com/users .我正在创建一个将从以下 API 打印结果的搜索: https : //jsonplaceholder.typicode.com/users

At this stage I just want the data to print out as search results.在这个阶段,我只想将数据作为搜索结果打印出来。 Currently, the "Failed to fetch results. Please check network" error message displays after any search.目前,任何搜索后都会显示“无法获取结果。请检查网络”错误消息。

Here's my search component:这是我的搜索组件:

import React from "react";
import "../styles.css";
import axios from "axios";

class Search extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      query: "",
      results: {},
      loading: false,
      message: ""
    };

    this.cancel = "";
  }

  fetchSearchResults = (updatedPageNo = "", query) => {
    const pageNumber = updatedPageNo ? `&page=${updatedPageNo}` : "";
    // By default the limit of results is 20
    const searchUrl = `https://jsonplaceholder.typicode.com/users${query}${pageNumber}`;
    if (this.cancel) {
      // Cancel the previous request before making a new request
      this.cancel.cancel();
    }
    // Create a new CancelToken
    this.cancel = axios.CancelToken.source();

    axios
      .get(searchUrl, {
        cancelToken: this.cancel.token
      })
      .then(res => {
        const resultNotFoundMsg = !res.data.length
          ? "There are no more search results. Please try a new search."
          : "";
        this.setState({
          results: res.data,
          message: resultNotFoundMsg,
          loading: false
        });
      })
      .catch(error => {
        if (axios.isCancel(error) || error) {
          this.setState({
            loading: false,
            message: "Failed to fetch results.Please check network"
          });
        }
      });
  };

  handleOnInputChange = event => {
    const query = event.target.value;
    if (!query) {
      this.setState({ query, results: {}, message: "" });
    } else {
      this.setState({ query, loading: true, message: "" }, () => {
        this.fetchSearchResults(1, query);
      });
    }
  };

  renderSearchResults = () => {
    const { results } = this.state;
    if (Object.keys(results).length && results.length) {
      return (
        <ul>
          {results.map(result => (
            <li>{result.name}</li>
          ))}
        </ul>
      );
    }
  };

  render() {
    const { query, message } = this.state;

    return (
      <div className="container">
        {/*Heading*/}
        <h2 className="heading">Live Search: React Application</h2>
        {/*Search Input*/}
        <label className="search-label" htmlFor="search-input">
          <input
            type="text"
            value={query}
            id="search-input"
            placeholder="Search..."
            onChange={this.handleOnInputChange}
          />
          <i className="fa fa-search search-icon" />
        </label>

        {/* Error Message*/}
        {message && <p className="message">{message}</p>}

        {/*Result*/}
        {this.renderSearchResults()}
      </div>
    );
  }
}

export default Search;

You need to set this.cancel to undefined when you initialise it, and after a search.您需要在初始化时和搜索后将this.cancel设置为undefined Something like this:像这样的东西:

class Search extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      query: "",
      results: {},
      loading: false,
      message: ""
    };

    this.cancel = undefined;
  }

  fetchSearchResults = (updatedPageNo = "", query) => {
    const pageNumber = updatedPageNo ? `&page=${updatedPageNo}` : "";
    // By default the limit of results is 20
    const searchUrl = `https://jsonplaceholder.typicode.com/users${query}${pageNumber}`;
    if (this.cancel) {
      // Cancel the previous request before making a new request
      this.cancel.cancel();
    }
    // Create a new CancelToken
    this.cancel = axios.CancelToken.source();

    axios
      .get(searchUrl, {
        cancelToken: this.cancel.token
      })
      .then(res => {
        const resultNotFoundMsg = !res.data.length
          ? "There are no more search results. Please try a new search."
          : "";
        this.setState({
          results: res.data,
          message: resultNotFoundMsg,
          loading: false
        });
         this.cancel = undefined;
      })
      .catch(error => {
        if (axios.isCancel(error) || error) {
          this.setState({
            loading: false,
            message: "Failed to fetch results.Please check network"
          });
        }
        this.cancel = undefined;
      });
  };

But it's better to do it in one place:但最好在一个地方进行:

class Search extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      query: "",
      results: {},
      loading: false,
      message: ""
    };

    this.cancel = undefined;
  }

  fetchSearchResults = (updatedPageNo = "", query) => {
    const pageNumber = updatedPageNo ? `&page=${updatedPageNo}` : "";
    // By default the limit of results is 20
    const searchUrl = `https://jsonplaceholder.typicode.com/users${query}${pageNumber}`;
    if (this.cancel) {
      // Cancel the previous request before making a new request
      this.cancel.cancel();
    }
    // Create a new CancelToken
    this.cancel = axios.CancelToken.source();

    axios
      .get(searchUrl, {
        cancelToken: this.cancel.token
      })
      .then(res => {
        const resultNotFoundMsg = !res.data.length
          ? "There are no more search results. Please try a new search."
          : "";
        this.setState({
          results: res.data,
          message: resultNotFoundMsg,
          loading: false
        });
      })
      .catch(error => {
        if (axios.isCancel(error) || error) {
          this.setState({
            loading: false,
            message: "Failed to fetch results.Please check network"
          });
        }
      })
     .finally(() => {this.cancel = undefined})
  };

If your environment supports Promise.finally .如果您的环境支持Promise.finally

The reason why the code fails is in the target searchUrl .代码失败的原因在于目标searchUrl

A quick look and I can see that the searchUrl that is formed when the user types "Tim" is:快速浏览一下,我可以看到用户键入“Tim”时形成的searchUrl是:

https://jsonplaceholder.typicode.com/userstim&page=1

If you look at the HTTP request there's an 404 error:如果您查看 HTTP 请求,则会出现 404 错误:

GET https://jsonplaceholder.typicode.com/userstim&page=1
[HTTP/2 404 Not Found 18ms]

So, have in mind that you should always look into the original error message, you can of course present a different message to the end user, but this would have been helpful to you:因此,请记住,您应该始终查看原始错误消息,您当然可以向最终用户显示不同的消息,但这会对您有所帮助:

  .catch(error => {
    console.log("error: ", error.message);
    if (axios.isCancel(error) || error) {
      this.setState({
        loading: false,
        message: "Failed to fetch results.Please check network"
      });
    }
  });

So, the reason why this is not working is the searchUrl pointing to an unexisting endpoint location.因此,这不起作用的原因是searchUrl指向不存在的端点位置。 You can simply remove the query and see it in action!您可以简单地删除query并查看它的运行情况!

const searchUrl = `https://jsonplaceholder.typicode.com/users`;

在此处输入图片说明

So, fix the searchUrl and check the API documentation to understand what to do to filter by username.因此,修复searchUrl并检查 API 文档以了解如何按用户名进行过滤。

It's out of the scope for the question but you can filter the data after the request to /users if a way to fetch by name doesn't exist...这超出了问题的范围,但如果不存在按名称获取的方法,您可以在向/users发出请求后过滤数据...

data.filter(item => item.username === query)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM