как правильно выполнить вызов API в React JS?

138

Недавно я перешел с 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>
    )
  }
}
Радж Рдж
источник
2
Я зависит от того, какую библиотеку управления состоянием вы используете. Если вы их не используете, вы можете переместить свои вызовы api в отдельный файл и вызвать функции api в своей ситуации в componentDidMountобратном вызове .
11
Вы можете использовать fetch()jQuery вместо jQuery, если вы используете jQuery только для выполнения запросов Ajax.
Фред
Зачем использовать JQuery? Jquery - огромная библиотека, и в ней нет необходимости
Робин
Просто добавлю здесь, что в настоящее время useEffect, вероятно, является местом для размещения вызовов API. См btholt.github.io/complete-intro-to-react-v5/effects
SHW

Ответы:

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
Это сработало, спасибо .. Не могли бы вы посоветовать мне, «какая из них лучшая библиотека для лучшего управления государством»
Радж Ридж
3
@Raj Rj в наши дни, я думаю, это Redux
Александр Т.
8
Redux более популярен в наши дни, его стиль основан на функциональном программировании. Если вы пришли из стиля ООП, Mobx ( mobxjs.github.io/mobx ) - отличная библиотека управления состоянием, она позволяет вам сосредоточиться на написании бизнес-кода и, в конечном итоге, сокращает ваш шаблонный код
Нхан Тран
25

Вы можете ознакомиться с архитектурой Flux . Я также рекомендую ознакомиться с реализацией React-Redux . Поместите свои вызовы api в свои действия. Это намного чище, чем помещать все это в компонент.

Действия - это своего рода вспомогательные методы, которые вы можете вызывать для изменения состояния приложения или выполнения вызовов API.

Jei Trooper
источник
Тропер Спасибо. Итак, следует ли мне хранить вызовы, связанные с API, в отдельных файлах? И как мне назвать их в моем «классе компонентов»? какой структуре папок мне следует придерживаться? каковы лучшие практики? PS - Я новичок в реакции, поэтому задаю эти основные вопросы.
Raj Rj
В реализации redux методы действий внедряются в компоненты. Эти методы теперь станут опорами вашего компонента, который вы можете вызывать. Вы можете проверить стартовый комплект react-redux для структуры.
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() будет устаревшим.

Вот только некоторые методы (все они в Документе ) в жизненном цикле, которые, я думаю, будут связаны с развертыванием API в общем случае: введите описание изображения здесь

Обращаясь к диаграмме выше:

  • Развернуть API в componentDidMount()

    Правильный сценарий для вызова API здесь заключается в том, что содержимое (из ответа API) этого компонента будет статическим, componentDidMount()срабатывать только один раз, пока компонент монтируется, даже новые реквизиты передаются из родительского компонента или требуют действий re-rendering.
    Компонент проверяет разницу для повторного рендеринга, но не для повторного монтирования .
    Цитата из документа :

Если вам нужно загрузить данные с удаленной конечной точки, это хорошее место для создания экземпляра сетевого запроса.


  • Развернуть API в static getDerivedStateFromProps(nextProps, prevState)

Следует заметить , что существует два вида компонента обновления , setState() в текущем компоненте бы не привести этот метод для запуска, но повторный рендеринг или новые подпорки из родительского компонента делают. Мы смогли выяснить, что этот метод также срабатывает при установке.

Это подходящее место для развертывания API, если мы хотим использовать текущий компонент как шаблон, а новые параметры для API - это реквизиты, поступающие из родительского компонента .
Мы получаем другой ответ от API и возвращаем здесь новый, stateчтобы изменить содержимое этого компонента.

Например: у
нас есть раскрывающийся список для разных автомобилей в родительском компоненте, этот компонент должен отображать сведения о выбранном.


  • Развернуть API в componentDidUpdate(prevProps, prevState)

В отличие от static getDerivedStateFromProps()этого метод вызывается сразу после каждого рендеринга, кроме первоначального. У нас может быть вызов API и разница в рендеринге в одном компоненте.

Расширьте предыдущий пример:
компонент для отображения деталей автомобиля может содержать список серий этого автомобиля, если мы хотим проверить серийный автомобиль 2013 года, мы можем щелкнуть или выбрать или ... элемент списка, чтобы первым setState()отразить это поведение (например, выделение элемента списка) в этом компоненте, и далее componentDidUpdate()мы отправляем наш запрос с новыми параметрами (состоянием). После получения ответа мы setState()снова за рендеринг различного содержания деталей автомобиля. Чтобы следующее не componentDidUpdate()привело к возникновению цикла бесконечности, нам нужно сравнить состояние, используя prevStateв начале этого метода, чтобы решить, отправляем ли мы API и отображаем ли новый контент.

Этот метод действительно можно использовать так же, как static getDerivedStateFromProps() с props, но необходимо обрабатывать изменения props, используя prevProps. И нам нужно сотрудничать с componentDidMount()для обработки первоначального вызова API.

Цитата из документа :

... Это также хорошее место для выполнения сетевых запросов, если вы сравниваете текущие реквизиты с предыдущими ...

Карр
источник
10

Я бы хотел, чтобы вы взглянули на redux http://redux.js.org/index.html

У них очень хорошо определенный способ обработки асинхронных вызовов, то есть вызовов API, и вместо использования jQuery для вызовов API я хотел бы порекомендовать использовать пакеты fetch или request npm, fetch в настоящее время поддерживается современными браузерами, но также доступна прокладка для на стороне сервера.

Есть еще один замечательный пакет суперагент , который имеет множество опций при выполнении запроса API и очень прост в использовании.

Деварш Шах
источник
3

Функция рендеринга должна быть чистой, это означает, что она использует только состояние и реквизиты для рендеринга, никогда не пытайтесь изменить состояние при рендеринге, это обычно вызывает уродливые ошибки и значительно снижает производительность. Это также хороший момент, если вы разделяете задачи извлечения данных и рендеринга в своем приложении React. Я рекомендую вам прочитать эту статью, которая очень хорошо объясняет эту идею. https://medium.com/@learnreact/container-components-c0e67432e005#.sfydn87nm

Нхан Тран
источник
3

Эта часть документации React v16 ответит на ваш вопрос, прочтите о componentDidMount ():

componentDidMount ()

componentDidMount () вызывается сразу после монтирования компонента. Здесь должна пройти инициализация, требующая узлов DOM. Если вам нужно загрузить данные с удаленной конечной точки, это хорошее место для создания экземпляра сетевого запроса. Этот метод - хорошее место для настройки любых подписок. Если вы это сделаете, не забудьте отказаться от подписки в componentWillUnmount ().

Как видите, componentDidMount считается лучшим местом и циклом для выполнения вызова api , а также доступа к узлу, что означает, что к этому времени можно безопасно выполнять вызов, обновлять представление или что-то еще, что вы могли бы сделать, когда документ готов, если вы используя jQuery, он должен как-то напоминать вам функцию document.ready (), где вы можете убедиться, что все готово для того, что вы хотите сделать в своем коде ...

Алиреза
источник
3

1) Вы можете использовать F etch API для получения данных из конечных точек:

Пример получения всего 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"))
iamnotsam
источник
1

Лучшим местом и практикой для внешних вызовов API является метод React Lifecycle componentDidMount () , где после выполнения вызова API вы должны обновить локальное состояние для запуска нового вызова метода render () , тогда изменения в обновленном локальном состоянии будут применяться к виду компонентов.

В качестве другого варианта для первоначального вызова внешнего источника данных в React указывается метод constructor () класса. Конструктор - это первый метод, выполняемый при инициализации экземпляра объекта компонента. Вы могли увидеть этот подход в примерах документации для компонентов более порядка. .

Метод componentWillMount () и UNSAFE_componentWillMount () не должен использоваться для внешних вызовов API, поскольку они предназначены для устареют. Вот вы можете увидеть общие причины, по которым этот метод будет устаревшим.

В любом случае вы никогда не должны использовать метод render () или метод, вызываемый напрямую из render () в качестве точки для внешнего вызова API. Если вы это сделаете, ваше приложение будет заблокировано .

zdrsoft
источник
0

Чистый способ - сделать асинхронный вызов API внутри componentDidMount с функцией try / catch .

Когда мы вызываем API, мы получаем ответ. Затем мы применяем к нему метод JSON, чтобы преобразовать ответ в объект JavaScript. Затем мы берем из этого объекта ответа только его дочерний объект с именем «результаты» (data.results).

Вначале мы определили "userList" в состоянии как пустой массив. Как только мы вызываем API и получаем данные от этого API, мы присваиваем «результаты» userList с помощью метода setState. .

Внутри функции рендеринга мы сообщаем, что список пользователей будет поступать из состояния. Поскольку 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 я использую response-redux для управления состоянием и redux-saga / redux-thunk для побочных эффектов.

Шиванг Гупта
источник
Хотя это не неверно, поскольку использование axios и redux является допустимым способом получения данных и управления состоянием, на самом деле это не отвечает на вопрос и ближе к комментарию.
Эмиль Бержерон