Kolejny przykład asynchronicznego useEffect z internetu, omawiamy go sobie, kontynuacja lekcji poprzedniej. Do dzieła.

Najpierw użycie tego hooka:

export default function App() {
  const [query, setQuery] = useState("");
  const [selectedId, setSelectedId] = useState(null);
  const { movies, isLoading, error } = useMovies(query);

Czyli widzimy, że:

  • query to stan RFC App
  • to query jest przekazywane do useMovies, jest jego zależnością
  • selectedId nas nie interesuje, jest potrzebne w App do czego innego
  • Hook zwraca movies, isLoading oraz error

Ok, jak on wygląda:

import { useState, useEffect } from "react";

const KEY = "f84fc31d";

Na początku importy, oraz stała key, powyżej RFC lub funkcji hook w tym wypadku, zatem stała, współdzielona wewnątrz pliku, jako const o typie prostym niezmienna.

Teraz sam hook:

export function useMovies(query) {
  const [movies, setMovies] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState("");

Już widać, że wszystko, co zwracamy to są obiekty state. Po prostu zarządzanie tymi stanami wywaliliśmy do reużywalnego custom hooka.

Dalej mamy useEffect:

export function useMovies(query) {
  const [movies, setMovies] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState("");

  useEffect(
    function () {
      

      const controller = new AbortController();

      async function fetchMovies() {
        //(...)
      }

      fetchMovies();

      return function () {
        controller.abort();
      };
    },
    [query]
  );

  return { movies, isLoading, error };
}

AbortController powyżej poziomu definicji async funkcji fetchmovies, jej definicja, wywołanie, cleanup używający aborta, query w talicy zależności.

Teraz zobaczmy definicję tej funkcji:

 async function fetchMovies() {
        try {
          setIsLoading(true);
          setError("");

          const res = await fetch(
            `http://www.omdbapi.com/?apikey=${KEY}&s=${query}`,
            { signal: controller.signal }
          );

          if (!res.ok)
            throw new Error("Something went wrong with fetching movies");

          const data = await res.json();
          if (data.Response === "False") throw new Error("Movie not found");

          setMovies(data.Search);
          setError("");
        } catch (err) {
          if (err.name !== "AbortError") {
            console.log(err.message);
            setError(err.message);
          }
        } finally {
          setIsLoading(false);
        }
      }

Nic trudnego. Mamy też swego rodzaju optymalizację przed wywołaniem fetchMovies, poprzednio ją przeoczyłem, trochę przypadkiem, ale też bez sensu było przytłaczać tym kodem.

Oto optymalizacja:

 if (query.length < 3) {
        setMovies([]);
        setError("");
        return;
      }

      fetchMovies();

      return function () {
        controller.abort();
      };
    },
    [query]
  );

  return { movies, isLoading, error };
}

Czyli nie wysyłamy requestów przesadnie dużo. Ok, to by było na tyle, temat będziemy kontynuować.