【问题标题】:what is right way to do API call in react js?在 react js 中进行 API 调用的正确方法是什么?
【发布时间】:2016-12-09 02:25:11
【问题描述】:

我最近从 Angular 转到了 ReactJs。我正在使用 jQuery 进行 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>
    )
  }
}

【问题讨论】:

  • 我取决于你使用的是什么状态管理库。如果您没有使用任何,您可以将您的 api 调用移动到单独的文件,并在 componentDidMount 回调中调用您的情况下的 api 函数。
  • 如果你只用jQuery做Ajax请求,可以用fetch()代替jQuery。
  • 为什么要使用 Jquery? jquery 是一个庞大的库,没有必要
  • 只是在这里添加,目前useEffect 可能是现在放置 api 调用的地方。见btholt.github.io/complete-intro-to-react-v5/effects

标签: javascript jquery reactjs


【解决方案1】:

这种情况下,你可以在componentDidMount里面做ajax调用,然后更新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>
    );
  }
}

【讨论】:

  • 它成功了,谢谢.. 你能建议我“哪个是更好的状态管理的最佳库”
  • @Raj Rj 这些天我认为是 Redux
  • Redux 在当今比较流行,它的风格来自函数式编程。如果你来自 OOP 风格,Mobx (mobxjs.github.io/mobx) 是一个优秀的状态管理库,它可以让你专注于编写业务代码并最终减少你的样板代码
【解决方案2】:

我想让你看看 redux http://redux.js.org/index.html

他们有非常明确的方式来处理异步调用,即 API 调用,而不是使用 jQuery 进行 API 调用,我想推荐使用 fetchrequest npm软件包,现代浏览器目前支持 fetch,但服务器端也可以使用 shim。

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

【讨论】:

    【解决方案3】:

    您可能想查看Flux Architecture。我还建议查看React-Redux Implementation。将您的 api 调用放入您的操作中。比全部放在组件里要干净得多。

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

    【讨论】:

    • 特罗珀 谢谢你。那么,我应该将与 API 相关的调用保存在单独的文件中吗?以及如何在我的“组件类”中调用它们?我应该遵循什么文件夹结构?最佳做法是什么? P.S.-我是新手,所以问这个基本问题。
    • 在redux实现中,action方法被注入到组件中。这些方法现在将成为您可以调用的组件的道具。您可以查看react-redux-starter-kit 的结构。
    【解决方案4】:

    render 函数应该是纯的,也就是说它只使用 state 和 props 来渲染,从不尝试修改 render 中的 state,这通常会导致丑陋的 bug 并显着降低性能。如果您在 React 应用程序中分离数据获取和渲染关注点,这也是一个好点。我建议你阅读这篇文章,它很好地解释了这个想法。 https://medium.com/@learnreact/container-components-c0e67432e005#.sfydn87nm

    【讨论】:

      【解决方案5】:

      这个讨论已经有一段时间了,@Alexander T. 的回答为像我这样的新 React 提供了一个很好的指导。并且我将分享一些关于多次调用同一个 API 以刷新组件的额外知识,我认为这可能是初学者的常见问题。

      componentWillReceiveProps(nextProps),来自official documentation

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

      我们可以得出结论,这里是我们处理来自父组件的 props、进行 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() 仅在组件挂载时触发一次,即使是从父组件传递的新道具或有行动带领re-rendering
        该组件会检查差异以重新渲染,但不检查重新安装
        引用doc:

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


      • 将 API 部署到 static getDerivedStateFromProps(nextProps, prevState)

      我们应该注意到有两种组件更新setState()在当前组件中不会触发这个方法,而是重新父组件的渲染或新道具会。 我们可以发现这个方法在挂载时也会触发。

      这是部署 API 的合适位置如果我们想使用当前组件作为模板,并且进行 API 调用的新参数是来自父组件的 props
      我们收到来自 API 的不同响应,并在此处返回一个新的state 以更改此组件的内容。

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


      • 将 API 部署到 componentDidUpdate(prevProps, prevState)

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

      扩展前面的例子:
      显示汽车详细信息的组件可能包含该汽车的系列列表,如果我们要查看 2013 年生产的系列,我们可以单击或选择或...列表项引导第一个 setState() 以反映此行为(例如在此组件中突出显示列表项),并在下面的componentDidUpdate() 中发送带有新参数(状态)的请求。得到响应后,我们再次setState() 用于渲染汽车细节的不同内容。为了防止后面的componentDidUpdate()引起无限循环,我们需要在这个方法的开头使用prevState比较状态,来决定是否发送API并渲染新的内容。

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

      引用doc:

      ...这也是一个做网络请求的好地方,只要你 比较当前的道具和以前的道具...

      【讨论】:

        【解决方案6】:

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

        componentDidMount()

        componentDidMount() 在组件被调用后立即调用 安装。需要 DOM 节点的初始化应该放在这里。如果你 需要从远程端点加载数据,这是一个很好的地方 实例化网络请求。这种方法是设置的好地方 任何订阅。如果您这样做,请不要忘记取消订阅 组件WillUnmount()。

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

        【讨论】:

          【解决方案7】:

          使用componentDidMount 中的fetch 方法更新状态:

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

          【讨论】:

            【解决方案8】:

            1) 您可以使用 Fetch API 从 Endd Points 获取数据:

            为用户获取所有Github repose 的示例

              /* 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 可以省去传递结果的中间步骤 对 .json() 方法的 http 请求。 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 />}
            

            【讨论】:

            • 我使用 axios 工作正常
            【解决方案9】:

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

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

            一开始我们将 state 中的“userList”定义为一个空数组。一旦我们进行 API 调用并从该 API 接收数据,我们就会使用 setState 方法 将“结果”分配给 userList。

            在渲染函数中,我们告诉 userList 将来自 state。由于 userList 是一个对象数组,我们通过它进行映射,以显示每个对象“用户”的图片、姓名和电话号码。为了检索这些信息,我们使用点表示法(例如 user.phone)。

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

            用户列表.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>
                  )
            }}
            

            【讨论】:

              【解决方案10】:

              外部 API 调用的最佳位置和实践是 React 生命周期方法 componentDidMount(),在执行 API 调用后,您应该更新本地状态以触发 new render( ) 方法调用,那么更新后的本地状态的变化将应用到组件视图上。

              作为 React 中 initial 外部数据源调用的其他选项,指向类的 constructor() 方法。构造函数是组件对象实例初始化时执行的第一个方法。您可以在 Higher-Order Components 的文档示例中看到这种方法。

              方法 componentWillMount()UNSAFE_componentWillMount() 不应用于外部 API 调用,因为它们打算被弃用。 Here 你可以看到常见的原因,为什么这个方法会被弃用。

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

              【讨论】:

                【解决方案11】:

                你也可以在你的函数组件中fetch data with hooks

                完整的 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"))
                

                【讨论】:

                  【解决方案12】:

                  对于支持取消、拦截器等的 api 请求使用 axios 会很棒。与 axios 一起,我使用 react-redux 进行状态管理,使用 redux-saga/redux-thunk 进行副作用。

                  【讨论】:

                  • 虽然这不是错误的,因为使用 axios 和 redux 是获取数据和管理状态的有效方法,但它并不能真正回答问题,它更接近于评论。
                  【解决方案13】:

                  作为对 Oleksandr T. 出色答案的补充/更新:

                  • 如果您使用类组件,后端调用应该发生在componentDidMount
                  • 如果您改用 hooks,则应使用 effect hook

                  例如:

                  import React, { useState, useEffect } from 'react';
                     
                  useEffect(() => {
                     fetchDataFromBackend();
                  }, []);
                  
                  // define fetchDataFromBackend() as usual, using Fetch API or similar;
                  // the result will typically be stored as component state
                  

                  进一步阅读:

                  【讨论】:

                    猜你喜欢
                    • 2019-12-11
                    • 2018-07-08
                    • 2019-12-13
                    • 2021-08-22
                    • 1970-01-01
                    • 2014-03-30
                    • 2017-06-05
                    • 2018-12-20
                    相关资源
                    最近更新 更多