簡體   English   中英

在 React JS 中調用 API 的正確方法是什么?

[英]what is right way to do API call in react js?

我最近從 Angular 轉到 ReactJs。我正在使用 jQuery 撥打 API 電話。 我有一個 API,它返回一個隨機用戶列表,該列表將打印在列表中。

我不確定如何編寫我的 API 電話。 這方面的最佳做法是什么?

我嘗試了以下但我沒有得到任何 output。如有必要,我願意實施替代 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>
    )
  }
}

在這種情況下,您可以在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>
    );
  }
}

您可能想查看Flux Architecture 我還建議查看React-Redux 實現 將您的 api 調用放在您的操作中。 它比將所有內容都放在組件中要干凈得多。

Actions 是一種輔助方法,您可以調用它們來更改應用程序狀態或執行 api 調用。

componentDidMount使用fetch方法更新狀態:

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

這個討論已經有一段時間了,@Alexander T. 的回答為像我這樣的 React 新手提供了一個很好的指導。 我將分享一些關於多次調用相同 API 以刷新組件的額外知識,我認為這可能是初學者的常見問題。

componentWillReceiveProps(nextProps) ,來自官方文檔

如果您需要更新狀態以響應 prop 更改(例如,重置它),您可以比較 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 相關: 在此處輸入圖片說明

參考上圖:

  • componentDidMount()部署 API

    此處調用 API 的正確場景是該組件的內容(來自 API 的響應)將是靜態的, componentDidMount()僅在組件掛載時觸發一次,即使是從父組件傳遞的新道具或有動作引導re-rendering
    該組件會檢查差異以重新渲染但不重新安裝
    引用自doc

如果您需要從遠程端點加載數據,這是實例化網絡請求的好地方。


  • static getDerivedStateFromProps(nextProps, prevState)部署 API

我們應該注意到有兩種組件更新當前組件中的setState()不會觸發此方法,但重新渲染或來自父組件的新道具 我們可以發現這個方法在掛載時也會觸發。

如果我們想使用當前組件作為模板,這是部署 API 的合適位置,並且進行 API 調用的新參數是來自父組件的 props
我們從 API 收到不同的響應並在此處返回一個新state以更改此組件的內容。

例如:
我們在父組件中有一個不同汽車的下拉列表,這個組件需要顯示所選汽車的詳細信息。


  • componentDidUpdate(prevProps, prevState)部署 API

static getDerivedStateFromProps() ,此方法在除初始渲染之外的每次渲染后立即調用。 我們可以在一個組件中調用 API 並呈現差異。

擴展前面的例子:
顯示 Car 詳細信息的組件可能包含該車的系列列表,如果我們想查看 2013 年生產的一個,我們可以點擊或選擇或...列表項引導第一個setState()來反映此行為(例如突出顯示該組件中的列表項),在接下來的componentDidUpdate()我們發送帶有新參數(狀態)的請求。 得到響應后,我們再次setState()用於渲染 Car 詳細信息的不同內容。 為了防止后面的componentDidUpdate()導致無限循環,我們需要利用這個方法開頭的prevState來比較狀態,決定是否發送API並渲染新的內容。

這個方法真的可以像static getDerivedStateFromProps()和 props 一樣使用,但需要通過使用prevProps來處理props的變化。 並且我們需要配合componentDidMount()來處理初始的 API 調用。

引用自doc

...這也是一個做網絡請求的好地方,只要你比較當前的 props 和之前的 props ...

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

他們有很好定義的方式來處理異步調用,即 API 調用,而不是使用 jQuery 進行 API 調用,我想推薦使用fetchrequest npm 包,現代瀏覽器目前支持fetch ,但也可以使用墊片服務器端。

還有另一個驚人的包superagent ,它在發出 API 請求時有很多選擇,並且非常易於使用。

Render 函數應該是純的,這意味着它只使用 state 和 props 來渲染,永遠不要嘗試修改渲染中的狀態,這通常會導致丑陋的錯誤並顯着降低性能。 如果您將 React 應用程序中的數據獲取和渲染關注點分開,這也是一個好點。 我建議你閱讀這篇文章,它很好地解釋了這個想法。 https://medium.com/@learnreact/container-components-c0e67432e005#.sfydn87nm

React v16文檔中的這一部分將回答您的問題,請繼續閱讀有關 componentDidMount() 的內容:

組件DidMount()

componentDidMount() 在組件安裝后立即調用。 需要 DOM 節點的初始化應該在這里進行。 如果您需要從遠程端點加載數據,這是實例化網絡請求的好地方。 此方法是設置任何訂閱的好地方。 如果這樣做,請不要忘記在 componentWillUnmount() 中取消訂閱。

如您所見, componentDidMount被認為是執行api 調用的最佳位置和循環,也可以訪問節點,這意味着此時可以安全地進行調用、更新視圖或文檔准備就緒時可以執行的任何操作,如果您是使用 jQuery,它應該以某種方式提醒您 document.ready() 函數,您可以在其中確保一切准備就緒,可以在代碼中執行任何操作...

1) 您可以使用 F etch API從端點獲取數據:

為用戶獲取所有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,您可以省去將 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 />}

您還可以使用函數組件中的鈎子獲取數據

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

一種干凈的方法是使用try/catch 函數componentDidMount內部進行異步 API 調用。

當我們調用 API 時,我們會收到響應。 然后我們對其應用 JSON 方法,將響應轉換為 JavaScript 對象。 然后我們從那個響應對象中只取他名為“results”(data.results)的子對象。

一開始我們將 state 中的“userList”定義為一個空數組。 一旦我們調用 API 並從該 API 接收數據,我們就會使用setState 方法將“結果”分配給 userList。

在渲染函數中,我們告訴 userList 將來自狀態。 由於 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>
      )
}}

外部 API 調用的最佳位置和實踐是 React Lifecycle 方法componentDidMount() ,在 API 調用執行后,您應該更新本地狀態以觸發新的render()方法調用,然后更新的本地狀態的變化將應用於組件視圖。

作為 React 中初始外部數據源調用的另一個選項,指向類的constructor()方法。 構造函數是在組件對象實例初始化時執行的第一個方法。 您可以在高階組件的文檔示例中看到這種方法。

方法componentWillMount()UNSAFE_componentWillMount()不應用於外部 API 調用,因為它們將被棄用。 在這里你可以看到常見的原因,為什么這個方法會被棄用。

無論如何,您絕不能使用 render() 方法或直接從 render() 調用的方法作為外部 API 調用的點。 如果您這樣做,您的應用程序將被阻止

作為對 Oleksandr T. 出色回答的補充/更新:

  • 如果您使用類組件,后端調用應該發生在componentDidMount
  • 如果你用鈎子代替,你應該使用效果鈎子

例如:

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

進一步閱讀:

您必須嘗試 API 調用的“axios”庫。 而不是直接使用 jQuery。

謝謝。

將 axios 用於支持取消、攔截器等的 api 請求會很棒。與 axios 一起,我使用 react-redux 進行狀態管理,使用 redux-saga/redux-thunk 處理副作用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM