Предположения:

Знакомство с React 16+ и хуками React

Знание асинхронного программирования в JavaScript и синтаксисе ES6+

Какая лучшая библиотека для получения, кэширования, синхронизации и обновления состояния сервера в приложении React? И почему это React Query? :)

При работе с состоянием клиента библиотека React является отличным инструментом для создания сложных пользовательских интерфейсов. Начиная с версии 16.8, React Hooks позволяет нам использовать больше функций React даже без написания класса.

Однако работа с состоянием сервера отличается. Вот почему:

  • Он сохраняется удаленно
  • Для получения данных требуются асинхронные API.
  • Ранее полученные данные могут быть «устаревшими», вам необходимо знать, когда это происходит, и обновлять их как можно быстрее.

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

  • Дедупликация нескольких запросов одних и тех же данных в один запрос
  • Реализовать кэширование и другие оптимизации производительности

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

И это мотивация React Query! У него есть настройки по умолчанию, которые дают отличные результаты без настройки. React Query позволяет нам хранить код извлечения данных в компонентах, которым нужны данные, но за кулисами он управляет кешем данных, передавая уже извлеченные данные компонентам, когда они их запрашивают.

Давайте теперь разберем пример с печально известным Pokemon API с помощью React Query. (Sисходные файлы в CodeSandbox).

Наши цели:

1. Получите данные для 12 pokemons, отправив запрос GET на https://pokeapi.co.api/v2/pokemon?limit=12.

Response:
  {
    ...
    "results": [
      {
       "name": "bulbasaur",
       "url": "https://pokeapi.co/api/v2/pokemon/1/"
      },
      {
       "name": "ivysaur",
       "url": "https://pokeapi.co/api/v2/pokemon/2/"
      },
      ...
    ]
  }

2. Получить данные для каждого pokemon, отправив запрос GET к его свойству url из предыдущего ответа.

3. Представьте name, image и type всех покемонов на карточке, используя React и немного CSS.

Для начала напишем две функции, которые позволят нам отправлять асинхронные HTTP-запросы на эти конечные точки. Также мы будем использовать Axios, просто из предпочтения. Fetch API одинаково прекрасно работает и используется в официальных примерах. Каждая функция имеет описательное имя, как показано ниже:

const fetchPokemons = async () => {
  const { data } = await axios.get("https://pokeapi.co.api/v2/pokemon?limit=12");
  const pokemons = await Promise.all(data.results.map(getPokemonData));
  return pokemons;
};

const getPokemonData = async ({ url }) => {
  const {
    data: { id, name, sprites, types }
  } = await axios.get(url);

  return {
    id,
    name,
    img: sprites.other["official-artwork"]?.front_default,
    pokemonType: types[0].type.name
  };
};

Объяснение: мы используем деструктуризацию объекта, чтобы получить вложенные данные из ответа JSON. Сопоставление через results в fetchPokemons возвращает список промисов, а метод Promise.all принимает этот список и обрабатывает его параллельно.

Затем мы завернем наше дерево компонентов в QueryClientProvider. React Query, который будет использовать это, чтобы сделать объект QueryClient доступным, чтобы мы могли вызывать хуки из библиотеки в компонентах-потомках. Мы делаем это в App.js вот так:

import { QueryClient, QueryClientProvider } from "react-query";
import Pokemons from "./components/Pokemons";

const queryClient = new QueryClient();

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
       <Pokemons />
    </QueryClientProvider>
  );
}

Хук useQuery в React Query похож на то, как мы реализуем пользовательские хуки React. Разница в том, что мы передаем useQuery в key и асинхронную функцию для получения объекта со свойствами data,status, и error.

const { data, status, error } = useQuery(key, () => asyncFunction(url));

keyиспользуется для идентификации данных в кэше; он может сразу же вернуть данные, соответствующие существующим ключам, а затем в фоновом режиме получить последние данные с сервера. key может быть string, array или сериализованным object. В Pokemons.js мы используем строку pokemons вот так:

import { useQuery } from "react-query";
import fetchPokemons from "../api";

export default function Pokemons() {
  const {
    data: pokemons,
    isLoading,
    error
  } = useQuery("pokemons", () => fetchPokemons());
  
  if (error) return <p>{error.message}</p>;
  if (isLoading) return <p>Loading....</p>;

  return (
    <div>
       {pokemons.map(({ id, img, name, pokemonType }) => (
         <div key={id}>
          <img src={img} alt={name} />
          <div>
            <h2>{name}</h2>
            <span>{pokemonType}</span>
          </div>
         </div>
       ))}
    </div>
  );
}

В компоненте мы получили isLoading — состояние, которое говорит нам, что запрос не имеет данных и в настоящее время извлекается. Подробнее о результатах запроса и состояниях в документации.

Ага! Это все, что нам нужно сделать. React Query делает кеш доступным, если мы хотим напрямую получить доступ к извлеченным данным или каким-либо образом манипулировать ими. В другом месте другой компонент может использовать связанный ключ запроса и метод getQueryData для доступа к уже извлеченным данным. Хук useQuery также принимает объект config в качестве третьего аргумента, что позволяет нам управлять всеми видами функций, связанных с запросами, такими как истечение срока действия кэша, политики повторных попыток при возникновении ошибок выборки, функции обратного вызова, необходимость работы с границами ошибки или заданными исходными данными. С другой стороны, если мы также хотим обновить состояние на сервере, React Query предоставляет для этой цели хук useMutation.

Вот скриншот нашего примера проекта:

Просмотреть исходные файлы в CodeSandbox

React Query помогает синхронизировать пользовательский интерфейс приложения React с состоянием, хранящимся на сервере. Посетите документацию React Query, чтобы найти примеры и ссылки на дополнительные учебные ресурсы об этой мощной библиотеке.