在React js中进行API调用的正确方法是什么?


137

我最近从Angular转到了ReactJs。我正在使用jQuery进行API调用。我有一个API,该API返回要打印在列表中的随机用户列表。

我不确定如何编写我的API调用。最佳做法是什么?

我尝试了以下操作,但未得到任何输出。如果需要,我愿意实现替代API库。

下面是我的代码:

import React from 'react';

export default class UserList extends React.Component {    
  constructor(props) {
    super(props);
    this.state = {
      person: []
    };
  }

  UserList(){
    return $.getJSON('https://randomuser.me/api/')
    .then(function(data) {
      return data.results;
    });
  }

  render() {
    this.UserList().then(function(res){
      this.state = {person: res};
    });
    return (
      <div id="layout-content" className="layout-content-wrapper">
        <div className="panel-list">
          {this.state.person.map((item, i) =>{
            return(
              <h1>{item.name.first}</h1>
              <span>{item.cell}, {item.email}</span>
            )
          })}
        <div>
      </div>
    )
  }
}

2
我取决于您使用的状态管理库。如果您不使用任何API,则可以将api调用移至单独的文件,并根据情况在componentDidMount回调中调用api函数。
1ven

您可以使用fetch(),而不是jQuery的,如果你只使用jQuery做Ajax请求。
弗雷德

为什么要使用Jquery?jQuery是一个庞大的库,它是不必要的
Robin Robin

仅在此处添加当前useEffect可能是现在放置api调用的地方。见btholt.github.io/complete-intro-to-react-v5/effects
shw

Answers:


98

在这种情况下,您可以在中进行ajax调用componentDidMount,然后进行更新state

export default class UserList extends React.Component {
  constructor(props) {
    super(props);

    this.state = {person: []};
  }

  componentDidMount() {
    this.UserList();
  }

  UserList() {
    $.getJSON('https://randomuser.me/api/')
      .then(({ results }) => this.setState({ person: results }));
  }

  render() {
    const persons = this.state.person.map((item, i) => (
      <div>
        <h1>{ item.name.first }</h1>
        <span>{ item.cell }, { item.email }</span>
      </div>
    ));

    return (
      <div id="layout-content" className="layout-content-wrapper">
        <div className="panel-list">{ persons }</div>
      </div>
    );
  }
}

2
它奏效了,谢谢。您能建议我“哪一个是更好的州管理的最佳图书馆”
Raj Rj

3
这几天@Raj Rj我想是Redux
Alexander T.

8
如今,Redux越来越流行,其风格来自功能编程。如果您来自OOP风格,Mobx(mobxjs.github.io/mobx)是一个出色的状态管理库,它使您可以专注于编写业务代码并最终减少样板代码
Nhan Tran

25

您可能想查看Flux体系结构。我也建议您检查React-Redux的实现。将您的api调用放入您的操作中。这比将它们全部放入组件中要干净得多。

操作是一种帮助程序方法,您可以调用这些方法来更改应用程序状态或进行api调用。


Troper谢谢你。那么,我应该将与API相关的调用保存在单独的文件中吗?在我的“组件类”中如何称呼它们?我应该遵循哪种文件夹结构?最佳做法是什么?PS-我是新来的反应者,所以请问这个基本问题。
Raj Rj

在redux实施中,将操作方法​​注入组件。这些方法现在将成为您可以调用的组件的支持。您可以检查出该结构的react-redux-starter-kit
Jei Trooper

12

使用fetch内部方法componentDidMount更新状态:

componentDidMount(){
  fetch('https://randomuser.me/api/')
      .then(({ results }) => this.setState({ person: results }));
}

11

讨论已经有一段时间了,@ Alexander T.的回答为像我这样的更新的React提供了很好的指导。我将分享一些有关多次调用同一API来刷新组件的其他专门知识,我认为这可能是新手一开始可能会遇到的常见问题。

componentWillReceiveProps(nextProps),来自官方文档

如果您需要更新状态以响应道具更改(例如,将其重置),则可以比较this.props和nextProps并在此方法中使用this.setState()执行状态转换。

我们可以得出结论,这里是我们处理来自父组件的道具,进行API调用和更新状态的地方。

基于@Alexander T.的示例:

export default class UserList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {person: []};
  }

  componentDidMount() {
   //For our first load. 
   this.UserList(this.props.group); //maybe something like "groupOne"
  }

  componentWillReceiveProps(nextProps) {
    // Assuming parameter comes from url.
    // let group = window.location.toString().split("/")[*indexParameterLocated*];
    // this.UserList(group);

    // Assuming parameter comes from props that from parent component.
    let group = nextProps.group; // Maybe something like "groupTwo" 
    this.UserList(group);
  }

  UserList(group) {
    $.getJSON('https://randomuser.me/api/' + group)
      .then(({ results }) => this.setState({ person: results }));
  }

  render() {
    return (...)
  }
}

更新资料

componentWillReceiveProps() 将不推荐使用。

在我的生命周期中,这里仅是一些方法(所有方法都在Doc中),我认为通常情况下与部署API有关: 在此处输入图片说明

通过参考上图:

  • 在中部署API componentDidMount()

    在这里进行API调用的合适方案是,该组件的内容(来自API的响应)将是静态的,componentDidMount()在组件安装时仅触发一次,甚至新的props从父组件传递或具有Lead动作re-rendering
    组件会检查差异以重新渲染,但不会重新安装
    doc引用:

如果需要从远程端点加载数据,这是实例化网络请求的好地方。


  • 在中部署API static getDerivedStateFromProps(nextProps, prevState)

我们应该注意到,有两种组件更新setState() 在当前组件不会导致这种方法来触发,但重新渲染或新道具父组件做。我们发现该方法在安装时也会触发。

如果我们想使用当前组件(例如模板),并且这是API的新参数是来自父组件的props,那么这是部署API的合适位置。
我们收到来自API的不同响应,并state在此处返回新值以更改此组件的内容。

例如:
父组件中有一个用于不同汽车的下拉列表,该组件需要显示所选汽车的详细信息。


  • 在中部署API componentDidUpdate(prevProps, prevState)

不同于static getDerivedStateFromProps(),此方法在每个渲染(初始渲染除外)之后立即调用。我们可以调用API并在一个组件中呈现差异。

扩展前面的示例:
用于显示汽车详细信息的组件可能包含该汽车系列的列表,如果我们要查看2013年生产的汽车,我们可以单击或选择或...该列表项将率先setState()反映出来。行为(例如,突出显示列表项)在此组件中的行为,然后在下文中,componentDidUpdate()我们发送带有新参数(状态)的请求。得到响应后,我们setState()再次用于渲染Car详细信息的不同内容。为防止以下情况componentDidUpdate()导致无限循环,我们需要通过prevState在此方法开始时利用来确定我们是否发送API并呈现新内容来比较状态。

该方法确实可以像static getDerivedStateFromProps()道具一样使用,但是需要props通过使用来处理的更改prevProps。并且我们需要配合componentDidMount()以处理初始API调用。

doc引用:

...这也是进行网络请求的好地方,只要您将当前的道具与以前的道具进行比较...


10

我希望您看看redux http://redux.js.org/index.html

它们具有处理异步调用(即API调用)的非常明确的方式,并且我建议使用fetch请求 npm包,而不是使用jQuery进行API调用,现代浏览器目前支持fetch,但也可以使用shim服务器端。

还有另一个令人惊奇的软件包superagent,它在发出API请求时有很多选择,而且非常易于使用。



3

React v16文档中的这一部分将回答您的问题,请继续阅读有关componentDidMount()的内容:

componentDidMount()

挂载组件后立即调用componentDidMount()。需要DOM节点的初始化应该在这里进行。如果需要从远程端点加载数据,这是实例化网络请求的好地方。此方法是设置任何订阅的好地方。如果这样做,请不要忘记取消订阅componentWillUnmount()。

如您所见,componentDidMount被认为是进行api调用,访问节点的最佳位置和周期,这意味着此时可以安全地进行调用,更新视图或在文档准备就绪时执行任何操作(如果可以)使用jQuery,它应该以某种方式提醒您document.ready()函数,在这里您可以确保一切准备就绪,可以在代码中进行任何操作...


3

1)您可以使用F etch API从End Point获取数据:

Github为用户获取所有安息的示例

  /* Fetch GitHub Repos */
  fetchData = () => {

       //show progress bar
      this.setState({ isLoading: true });

      //fetch repos
      fetch(`https://api.github.com/users/hiteshsahu/repos`)
      .then(response => response.json())
      .then(data => {
        if (Array.isArray(data)) {
          console.log(JSON.stringify(data));
          this.setState({ repos: data ,
                         isLoading: false});
        } else {
          this.setState({ repos: [],
                          isLoading: false  
                        });
        }
      });
  };

2)其他替代品是Axios

使用axios,您可以省去将http请求的结果传递给.json()方法的中间步骤。Axios只是返回您期望的数据对象。

  import axios from "axios";

 /* Fetch GitHub Repos */
  fetchDataWithAxios = () => {

     //show progress bar
      this.setState({ isLoading: true });

      // fetch repos with axios
      axios
          .get(`https://api.github.com/users/hiteshsahu/repos`)
          .then(result => {
            console.log(result);
            this.setState({
              repos: result.data,
              isLoading: false
            });
          })
          .catch(error =>
            this.setState({
              error,
              isLoading: false
            })
          );
}

现在,您可以选择使用以下任何一种策略来获取数据 componentDidMount

class App extends React.Component {
  state = {
    repos: [],
   isLoading: false
  };

  componentDidMount() {
    this.fetchData ();
  }

同时,您可以在加载数据时显示进度条

   {this.state.isLoading && <LinearProgress />}

2

您还可以使用挂钩获取数据在函数组件中

api调用的完整示例:https : //codesandbox.io/s/jvvkoo8pq3

第二个示例:https : //jsfiddle.net/bradcypert/jhrt40yv/6/

const Repos = ({user}) => {
  const [repos, setRepos] = React.useState([]);

  React.useEffect(() => {
    const fetchData = async () => {
        const response = await axios.get(`https://api.github.com/users/${user}/repos`);
        setRepos(response.data);
    }

    fetchData();
  }, []);

  return (
  <div>
    {repos.map(repo =>
      <div key={repo.id}>{repo.name}</div>
    )}
  </div>
  );
}

ReactDOM.render(<Repos user="bradcypert" />, document.querySelector("#app"))

1

作为最佳的外部API调用地方和实践,是React Lifecycle方法componentDidMount(),在执行API调用后,您应该更新本地状态以触发新的render()方法调用,然后更新后的本地状态中的更改将应用于组件视图。

作为React中初始外部数据源调用的另一种选择,指向该类的Constructor()方法。构造函数是在初始化组件对象实例时执行的第一个方法。您可以在高阶组件的文档示例中看到此方法。

不应将方法componentWillMount()UNSAFE_componentWillMount()用于外部API调用,因为它们已被弃用。在这里您可以看到常见原因,为什么不赞成使用此方法。

无论如何,您绝不能使用render()方法或直接从render()调用的方法作为外部API调用的点。如果这样做,您的应用程序将被阻止


0

一种干净的方法是使用try / catch函数componentDidMount内部进行异步API调用。

调用API时,我们会收到响应。然后,我们对其应用JSON方法,以将响应转换为JavaScript对象。然后,我们从该响应对象中仅获取其子对象“ results”(data.results)。

首先,我们将状态为“ userList”的数组定义为一个空数组。进行API调用并从该API接收数据后,我们立即使用setState方法将“结果”分配给userList

在render函数内部,我们告诉userList将来自状态。由于userList是对象数组,我们通过它映射,以显示每个对象“用户”的图片,名称和电话号码。要检索此信息,我们使用点符号(例如user.phone)。

注意:根据您的API,您的响应可能看起来有所不同。Console.log整个“响应”,以查看您需要从中获取哪些变量,然后在setState中分配它们。

UserList.js

import React, { Component } from "react";

export default class UserList extends Component {
   state = {
      userList: [], // list is empty in the beginning
      error: false
   };

   componentDidMount() {
       this.getUserList(); // function call
   }

   getUserList = async () => {
       try { //try to get data
           const response = await fetch("https://randomuser.me/api/");
           if (response.ok) { // ckeck if status code is 200
               const data = await response.json();
               this.setState({ userList: data.results});
           } else { this.setState({ error: true }) }
       } catch (e) { //code will jump here if there is a network problem
   this.setState({ error: true });
  }
};

  render() {
  const { userList, error } = this.state
      return (
          <div>
            {userList.length > 0 && userList.map(user => (
              <div key={user}>
                  <img src={user.picture.medium} alt="user"/>
                  <div>
                      <div>{user.name.first}{user.name.last}</div>
                      <div>{user.phone}</div>
                      <div>{user.email}</div>
                  </div>
              </div>
            ))}
            {error && <div>Sorry, can not display the data</div>}
          </div>
      )
}}

0

最好将axios用于支持取消,拦截器等的api请求。与axios一起,我将react-redux用于状态管理,并将redux-saga / redux-thunk用于副作用。


尽管这是不正确的,但是使用axios和redux是获取数据和管理状态的有效方法,但它并不能真正回答问题,而且更接近注释。
Emile Bergeron
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.