在 react js 中进行 API 调用的正确方法是啥?

Posted

技术标签:

【中文标题】在 react js 中进行 API 调用的正确方法是啥?【英文标题】: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 【参考方案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 />
                  <div>
                      <div>user.name.firstuser.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

进一步阅读:

Using the Effect Hook 在官方文档中。 How to fetch data with React Hooks? by Robin Wieruch

【讨论】:

以上是关于在 react js 中进行 API 调用的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Vue/React:调用函数传递数据的正确方法?

在 React 中进行 API 调用

在 REACT 中进行 API 调用时动态设置目标

无法调用 React JS webpack-dev-server api

尝试使用 axios 在 React/Phoenix 应用程序中进行 API 调用(使用早午餐)

在 MERN 应用程序(React 客户端)中外包 API 调用的正确方法?