Diversity w polskim IT
Harsh Makadia
Harsh MakadiaSenior Software Developer @ ZURU Tech India Pvt Ltd

Jak korzystać z React Hooks

Zobacz, jak konwertować komponent klasowy na komponent funkcyjny za pomocą hooków. Sprawdź, jak działa useState i useEffect.
20.09.20194 min
Jak korzystać z React Hooks

Nowa aktualizacja biblioteki React pojawiła się stosunkowo niedawno, więc możliwe, że jeszcze nie używałeś nowych funkcji w swojej. Dokładnie 6 lutego 2019 opublikowano React Hooks, czyli React wersję 16.8.0.

Ten artykuł pokaże, jak zacząć korzystać z React Hooks, zostaną tu wyjaśnione podstawowe metody, które w pełni pozwolą na wykorzystanie nowych funkcji.

Zacznijmy od krótkiego wyjaśnienia czym są Hooki.

Hooki to funkcje, które pozwalają “doczepić się” do funkcji stanu i cyklu życia w funkcyjnch komponentach. Hooki nie działają w klasach, toteż możesz używać Reacta bez klas.


useState

useState jest hookiem, wywołujemy go wewnątrz komponentu funkcyjnego, gdy chcemy dodać stan lokalny. React zachowa ten stan podczas ponownego renderowania.

useState zwraca dwie rzeczy: aktualną wartość stanu oraz funkcję, która pozwala zaktualizować komponent. Wywołanie funkcji będzie działać podobnie do this.setState, który zaktualizuje wartość stanu, z tym wyjątkiem, że nie scali nowego i starego stanu ze sobą.


useEffect

useEffect pozwala wykonać „efekty uboczne” w komponentach funkcyjnych. 

useEffect pełni podobną funkcję jak metody cyklu życia (ang. Lifecycle) w komponentach klasowych componentDidMount, componentDidUpdate i componentWillUnMount.

Masz także możliwość zdecydować, kiedy nastąpi kolejne renderowanie. Prześledź poniższy kod, gdzie przekazaliśmy tablicę za pomocą useEffect.

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes​

Powiedzmy, że wartość licznika to 60 i jeśli komponent ponownie zostanie wyrenderowany z niezmienioną wartością licznika (np. 60), React porówna poprzednią wartość renderowania i zdecyduje czy wywołać efekt, czy nie. Dzięki temu zwiększona zostaje wydajność i unikamy niepotrzebnych wywołań.

Jeżeli w tablicy jest wiele elementów, React uruchomi efekt ponownie, nawet jeśli tylko jeden z nich się zmieni.


Konwertowanie komponentu klasowego na komponent funkcyjny za pomocą hooków

Zobaczmy na przykładzie, w jaki sposób można uzyskać zachowanie komponentu klasowego w komponencie funkcyjnym używając hooków.

Przykład: Załóżmy, że musisz wielokrotnie wykonać zapytanie API, pobierać dane i wypełniać komponent, a kliknięcie przycisku wczytaj pobierze jeszcze większą ilość danych z serwera.

Przed Reactem 16.8.0 (Hooks) nie było to możliwe przy użyciu komponentów funkcyjnych. Metody cyklu życia są niedostępne w komponencie funkcyjnym i nie było możliwości zarządzania stanem wewnątrz komponentu funkcyjnego.

Do wywołań API używamy interfejsów API z Githuba:
https://developer.github.com/v3/search/#search-commits

Tak właśnie wygląda typowy Reactowy kod dla zwykłego komponentu klasowego, jak i  komponentu funkcyjnego używającego hooków.

Za każdym razem, gdy wywołujemy API potrzebujemy kilku wartości stanu:

  • Danych, które mają być renderowane
  • Liczba stron
  • Stan ładowania (wyświetlanie ekranu/komponentu ładowania, póki nie otrzymamy danych z serwera)
  • Stan błędu (wyświetlanie komunikatu o błędzie, gdy takowy wystąpi podczas pobierania danych)

Widoczne na powyższym obrazku komponent klasowy i komponent funkcyjny działa tak samo ładując commity z GitHuba. Ten prosty przykład pomoże Ci zrozumieć, jak w łatwy sposób zacząć korzystać z hooków we własnej aplikacji. Dzięki hookom możesz pisać czytelny i czysty kod.

Fragment kodu
- kod z komponentem klasowym wywołującym API

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class GithubCommit extends React.Component {
  constructor() {
    super();
    this.state = {
      commitHistory: [],
      page: 1,
      isLoading: true
    };
    this.loadMoreCommit = this.loadMoreCommit.bind(this);
  }

  componentDidMount() {
    this.loadCommitHistory();
  }

  loadMoreCommit() {
    const { page } = this.state;
    this.setState(
      {
        page: page + 1
      },
      () => this.loadCommitHistory()
    );
  }

  loadCommitHistory() {
    const { page } = this.state;
    // Fetching data from FaceBook Jest Repo
    fetch(
      `https://api.github.com/search/commits?q=repo:facebook/react+css&page=${page}`,
      {
        method: "GET",
        headers: new Headers({
          Accept: "application/vnd.github.cloak-preview"
        })
      }
    )
      .then(res => res.json())
      .then(response =>
        this.setState({ commitHistory: response.items, isLoading: false })
      )
      .catch(error => console.log(error));
  }

  render() {
    const { commitHistory, isLoading } = this.state;
    return (
      <div>
        <h1> API calls with React Class Component </h1>
        {isLoading && <p>Wait I'm Loading comments for you</p>}

        {commitHistory.length !== 0 && (
          <button onClick={() => this.loadMoreCommit()}>
            Load More Commits
          </button>
        )}

        {commitHistory.map((c, index) => (
          <div key={index}>
            {c.commit && (
              <>
                <div>
                  <h2 style={{ textDecoration: "Underline" }}>
                    {c.commit.committer.name}
                  </h2>
                  <p>{c.commit.message}</p>
                </div>
                <hr />
              </>
            )}
          </div>
        ))}
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<GithubCommit />, rootElement);

- kod z hookami wywołujący API

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function GithubCommit() {
  const [page, setPage] = useState(1);
  const [commitHistory, setCommitHistory] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  const loadMoreCommit = () => {
    setPage(page + 1);
  };

  useEffect(() => {
    fetch(
      `https://api.github.com/search/commits?q=repo:facebook/react+css&page=${page}`,
      {
        method: "GET",
        headers: new Headers({
          Accept: "application/vnd.github.cloak-preview"
        })
      }
    )
      .then(res => res.json())
      .then(response => {
        setCommitHistory(response.items);
        setIsLoading(false);
      })
      .catch(error => console.log(error));
  }, [page]);

  return (
    <div>
      <h1> API calls with React Hooks </h1>
      {isLoading && <p>Wait I'm Loading comments for you</p>}

      {commitHistory.length !== 0 && (
        <button onClick={loadMoreCommit}>Load More Commits</button>
      )}

      {commitHistory.map((c, index) => (
        <div key={index}>
          {c.commit && (
            <>
              <div>
                <h2 style={{ textDecoration: "Underline" }}>
                  {c.commit.committer.name}
                </h2>
                <p>{c.commit.message}</p>
              </div>
              <hr />
            </>
          )}
        </div>
      ))}
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<GithubCommit />, rootElement);

Zasady, o których należy pamiętać pracując z React Hooks:

  1. Nie konwertuj starego kodu, używającego komponentów klasowych na hooki. Zaleca się używać hooków w nowych implementacjach.
  2. useStatei useEffect to dwie, nowe koncepcje, które musisz znać by opanować hooki.
  3. Hooki są wywoływane tylko na najwyższym poziomie. Nie wywołuj hooków wewnątrz pętli, warunków i zagnieżdżonych funkcji.
  4. Hooki wywołuj tylko z komponentów funkcyjnych React. Nie wywołuj hooków ze zwykłych funkcji JavaScript.

W ten sposób hooki mogą być przydatne przy wywoływaniu API. Już nie musimy konwertować komponentów funkcyjnych na komponenty klasowe tylko dlatego, że w komponencie funkcyjnym nie możemy zarządzać stanem.

<p>Loading...</p>