jaki jest właściwy sposób wywołania API w reakcji js?

145

Niedawno przeniosłem się z Angular do ReactJs. Używam jQuery do wywołań API. Mam API, które zwraca losową listę użytkowników, która ma zostać wydrukowana na liście.

Nie wiem, jak pisać wywołania interfejsu API. Jaka jest najlepsza praktyka w tym zakresie?

Próbowałem wykonać następujące czynności, ale nie otrzymuję żadnych wyników. Jestem otwarty na wdrażanie alternatywnych bibliotek API w razie potrzeby.

Poniżej mój kod:

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>
    )
  }
}
Raj Rj
źródło
2
Zależę od używanej biblioteki zarządzania stanem. Jeśli nie używasz żadnego, możesz przenieść wywołania API do oddzielnego pliku i wywołać funkcje API w swojej sytuacji w componentDidMountwywołaniu zwrotnym.
nawet
Możesz użyć fetch()zamiast jQuery, jeśli używasz jQuery tylko do wykonywania żądań Ajax.
Fred
1
Dlaczego warto korzystać z Jquery? Jquery to ogromna biblioteka i jest niepotrzebna
Robin
Dodam tylko, że obecnie useEffectprawdopodobnie jest to miejsce, w którym można teraz umieszczać wywołania interfejsu API. Zobacz btholt.github.io/complete-intro-to-react-v5/effects
shw

Odpowiedzi:

104

W takim przypadku możesz wykonać wywołanie Ajax wewnątrz componentDidMount, a następnie zaktualizować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>
    );
  }
}
Oleksandr T.
źródło
2
Udało się, dzięki… Czy mógłbyś mi zasugerować „która z nich jest najlepszą biblioteką do lepszego zarządzania państwem”
Raj Rj
3
@Raj Rj w tych dniach myślę, że to Redux
Oleksandr T.
8
Redux jest obecnie bardziej popularny, jego styl wywodzi się z programowania funkcjonalnego. Jeśli pochodzisz ze stylu OOP, Mobx ( mobxjs.github.io/mobx ) to doskonała biblioteka do zarządzania stanem, pozwala skupić się na pisaniu kodu biznesowego i ostatecznie redukuje kod standardowy
Nhan Tran
27

Możesz sprawdzić architekturę Flux . Polecam również zapoznać się z implementacją React-Redux . Umieść wywołania interfejsu API w swoich działaniach. Jest znacznie czystszy niż włożenie wszystkiego do komponentu.

Akcje to rodzaj metod pomocniczych, które można wywołać, aby zmienić stan aplikacji lub wykonać wywołania interfejsu API.

Jei Trooper
źródło
Troper Dziękuję. Czy mam więc przechowywać wywołania związane z interfejsem API w osobnych plikach? Jak mam je nazwać w mojej „klasie komponentów”? jaką strukturę folderów należy przestrzegać? jaka jest najlepsza praktyka? PS: Jestem nowy, jeśli chodzi o zadawanie podstawowych pytań.
Raj Rj
W implementacji Redux metody działania są wstrzykiwane do komponentów. Te metody staną się teraz właściwościami twojego komponentu, który możesz wywołać. Możesz zapoznać się z zestawem startowym React-Redux dla konstrukcji.
Jei Trooper
13

Użyj fetchmetody wewnątrz, componentDidMountaby zaktualizować stan:

componentDidMount(){
  fetch('https://randomuser.me/api/')
      .then(({ results }) => this.setState({ person: results }));
}
miłość do kodowania
źródło
11

Ta dyskusja trwała już jakiś czas, a odpowiedź @Alexander T. była dobrym przewodnikiem do naśladowania dla nowszych React, takich jak ja. I podzielę się kilkoma dodatkowymi wiedzą na temat wielokrotnego wywoływania tego samego API w celu odświeżenia komponentu. Myślę, że jest to prawdopodobnie powszechny problem, z którym nowicjusz może napotkać na początku.

componentWillReceiveProps(nextProps)z oficjalnej dokumentacji :

Jeśli chcesz zaktualizować stan w odpowiedzi na zmiany właściwości (na przykład, aby go zresetować), możesz porównać this.props i nextProps i wykonać przejścia stanu za pomocą this.setState () w tej metodzie.

Możemy wywnioskować, że tutaj jest miejsce, w którym obsługujemy właściwości z komponentu nadrzędnego, mamy wywołania API i stan aktualizacji.

Bazuj na przykładzie @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 (...)
  }
}

Aktualizacja

componentWillReceiveProps() zostanie wycofany.

Oto tylko niektóre metody (wszystkie w Doc ) w cyklu życia, które moim zdaniem byłyby związane z wdrażaniem API w ogólnym przypadku: wprowadź opis obrazu tutaj

Odnosząc się do powyższego schematu:

  • Wdróż interfejs API w componentDidMount()

    Właściwy scenariusz wywołania API jest taki, że zawartość (z odpowiedzi API) tego komponentu będzie statyczna, componentDidMount()uruchamiana tylko raz podczas montowania komponentu, nawet nowe właściwości są przekazywane z komponentu nadrzędnego lub mają działania do wykonania re-rendering.
    Komponent sprawdza różnicę, aby ponownie renderować, ale nie montować ponownie .
    Cytat z doc :

Jeśli chcesz załadować dane ze zdalnego punktu końcowego, jest to dobre miejsce na utworzenie wystąpienia żądania sieciowego.


  • Wdróż interfejs API w static getDerivedStateFromProps(nextProps, prevState)

Powinniśmy zauważyć, że istnieją dwa rodzaje aktualizacji komponentów , setState() w obecnym komponencie nie spowodowałoby to wyzwolenia tej metody, ale ponowne renderowanie lub nowe właściwości z komponentu nadrzędnego tak. Mogliśmy się dowiedzieć, że ta metoda również strzela podczas montażu.

Jest to właściwe miejsce na wdrożenie API, jeśli chcemy użyć obecnego komponentu jako szablonu, a nowe parametry API to właściwości pochodzące z komponentu nadrzędnego .
Otrzymujemy inną odpowiedź z API i zwracamy statetutaj nową , aby zmienić zawartość tego komponentu.

Na przykład:
Mamy listę rozwijaną dla różnych samochodów w komponencie nadrzędnym, ten komponent musi wyświetlać szczegóły wybranego.


  • Wdróż interfejs API w componentDidUpdate(prevProps, prevState)

W odróżnieniu od static getDerivedStateFromProps()tej metody jest wywoływana natychmiast po każdym renderowaniu, z wyjątkiem renderowania początkowego. Moglibyśmy wywołać API i renderować różnicę w jednym komponencie.

Rozszerz poprzedni przykład:
Komponent pokazujący szczegóły samochodu może zawierać listę serii tego samochodu, jeśli chcemy sprawdzić wersję produkcyjną z 2013 roku, możemy kliknąć lub wybrać lub ... element listy, który będzie prowadził jako pierwszy, setState()aby to odzwierciedlić zachowanie (takie jak podświetlanie pozycji listy) w tym komponencie, aw dalszej części componentDidUpdate()wysyłamy nasze żądanie z nowymi parametrami (stan). Po otrzymaniu odpowiedzi setState()ponownie wykonujemy renderowanie innej zawartości szczegółów samochodu. Aby następujące elementy nie componentDidUpdate()powodowały pętli nieskończoności, musimy porównać stan, używając prevStatena początku tej metody, aby zdecydować, czy wyślemy API i wyrenderujemy nową zawartość.

Ta metoda naprawdę mogłaby być użyta tak jak w static getDerivedStateFromProps()przypadku rekwizytów, ale trzeba obsługiwać zmiany propspoprzez wykorzystanie prevProps. Musimy współpracować, componentDidMount()aby obsłużyć początkowe wywołanie API.

Cytat z doc :

... Jest to również dobre miejsce do wykonywania żądań sieciowych, o ile porównujesz obecne rekwizyty z poprzednimi ...

Carr
źródło
10

Chciałbym, abyś rzucił okiem na redux http://redux.js.org/index.html

Mają bardzo dobrze zdefiniowany sposób obsługi wywołań asynchronicznych, czyli wywołań API, i zamiast używać jQuery do wywołań API, polecam używanie pakietów fetch lub request npm, pobieranie jest obecnie obsługiwane przez nowoczesne przeglądarki, ale dostępna jest również podkładka dla po stronie serwera.

Jest też inny niesamowity superagent pakietu , który ma wiele opcji podczas wysyłania żądań API i jest bardzo łatwy w użyciu.

Devarsh Shah
źródło
3

Funkcja renderowania powinna być czysta, oznacza to, że do renderowania używa tylko stanu i właściwości, nigdy nie próbuj modyfikować stanu w renderowaniu, zwykle powoduje to brzydkie błędy i znacznie zmniejsza wydajność. Jest to również dobry punkt, jeśli oddzielisz pobieranie danych i problemy z renderowaniem w aplikacji React. Polecam przeczytanie tego artykułu, który bardzo dobrze wyjaśnia ten pomysł. https://medium.com/@learnreact/container-components-c0e67432e005#.sfydn87nm

Nhan Tran
źródło
3

Ta część dokumentacji React v16 odpowie na twoje pytanie, przeczytaj o componentDidMount ():

componentDidMount ()

componentDidMount () jest wywoływana natychmiast po podłączeniu składnika. Inicjalizacja wymagająca węzłów DOM powinna znaleźć się tutaj. Jeśli chcesz załadować dane ze zdalnego punktu końcowego, jest to dobre miejsce na utworzenie wystąpienia żądania sieciowego. Ta metoda jest dobrym miejscem do konfigurowania jakichkolwiek subskrypcji. Jeśli to zrobisz, nie zapomnij zrezygnować z subskrypcji w componentWillUnmount ().

Jak widzisz, componentDidMount jest uważane za najlepsze miejsce i cykl do wykonania wywołania interfejsu API , a także uzyskania dostępu do węzła, co oznacza, że ​​w tym czasie można bezpiecznie wykonać wywołanie, zaktualizować widok lub cokolwiek zrobić, gdy dokument jest gotowy, jeśli jesteś używając jQuery, powinno jakoś przypomnieć Ci funkcję document.ready (), w której możesz upewnić się, że wszystko jest gotowe na wszystko, co chcesz zrobić w swoim kodzie ...

Alireza
źródło
3

1) Możesz użyć F etch API do pobierania danych z Endd Points:

Przykład pobierania całego Githubodpoczynku dla użytkownika

  /* 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) Inną alternatywą jest Axios

Używając axios możesz wyciąć środkowy krok przekazywania wyników żądania http do metody .json (). Axios po prostu zwraca obiekt danych, którego można się spodziewać.

  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
            })
          );
}

Teraz możesz wybrać pobieranie danych przy użyciu dowolnej z tych strategii w componentDidMount

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

  componentDidMount() {
    this.fetchData ();
  }

W międzyczasie możesz wyświetlić pasek postępu podczas ładowania danych

   {this.state.isLoading && <LinearProgress />}
Hitesh Sahu
źródło
2

Możesz także pobierać dane z zaczepami w komponentach funkcji

pełny przykład z wywołaniem api: https://codesandbox.io/s/jvvkoo8pq3

drugi przykład: 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
źródło
1

Najlepszym miejscem i praktyką dla zewnętrznych wywołań API jest metoda React Lifecycle componentDidMount () , gdzie po wykonaniu wywołania API należy zaktualizować stan lokalny do wyzwolenia nowego wywołania metody render () , wtedy zmiany w zaktualizowanym stanie lokalnym będą zastosować w widoku komponentu.

Jako inną opcję dla początkowego wywołania zewnętrznego źródła danych w Reakcie wskazano konstruktor () metoda klasy. Konstruktor jest pierwszą metodą wykonywaną podczas inicjowania instancji obiektu składnika. Takie podejście można zobaczyć w przykładach dokumentacji dla komponentów wyższego rzędu .

Metody componentWillMount () i UNSAFE_componentWillMount () nie powinny być używane do zewnętrznych wywołań API, ponieważ mają być przestarzałe.Tutaj możesz zobaczyć typowe powody, dla których ta metoda zostanie wycofana.

W każdym razie nigdy nie wolno używać metody render () ani metody wywoływanej bezpośrednio z metody render () jako punktu wywołania zewnętrznego interfejsu API. Jeśli to zrobisz, Twoja aplikacja zostanie zablokowana .

zdrsoft
źródło
0

Czystym sposobem jest wykonanie asynchronicznego wywołania API wewnątrz componentDidMount za pomocą funkcji try / catch .

Kiedy wywołaliśmy API, otrzymujemy odpowiedź. Następnie stosujemy na nim metodę JSON, aby przekonwertować odpowiedź na obiekt JavaScript. Następnie pobieramy z tego obiektu odpowiedzi tylko jego obiekt potomny o nazwie „results” (data.results).

Na początku zdefiniowaliśmy „userList” w stanie jako pustą tablicę. Jak tylko wykonamy wywołanie API i otrzymamy dane z tego API, przypisujemy "wyniki" do userList przy użyciu metody setState .

Wewnątrz funkcji renderującej mówimy, że userList będzie pochodzić ze stanu. Ponieważ userList jest tablicą obiektów, które mapujemy za jej pomocą, wyświetlamy obraz, nazwę i numer telefonu każdego obiektu „user”. Aby uzyskać te informacje, używamy notacji kropkowej (np. Użytkownik.telefon).

UWAGA : w zależności od interfejsu API Twoja odpowiedź może wyglądać inaczej. Console.log całą „odpowiedź”, aby zobaczyć, jakich zmiennych z niej potrzebujesz, a następnie przypisz je w 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>
      )
}}
Tania Shulga
źródło
0

Byłoby wspaniale użyć axios dla żądania api, które obsługuje anulowanie, przechwytywacze itp. Wraz z axios, używam re-redux do zarządzania stanem i redux-saga / redux-thunk do efektów ubocznych.

Shivang Gupta
źródło
Chociaż nie jest to niepoprawne, ponieważ użycie axios i redux jest prawidłowym sposobem pobierania danych i zarządzania stanem, tak naprawdę nie odpowiada na pytanie i jest bliżej komentarza.
Emile Bergeron