Рубрики
Без рубрики

Загрузка и отображение данных с крючками

В этой серии вместо использования библиотеки государства-менеджмента или предлагает одноразмерное приспособление – все это решение … помечено с помощью React, Teamscript, JavaScript.

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

  • В этой первой статье мы опишем, как мы загружаем и отображаем данные с крючками.
  • Во второй статье мы узнаем, как менять удаленные данные с крючками.
  • В третьей статье мы увидим, как делиться данными между компонентами с контекстом React, не используя глобал, синглтонов или прибегающих к библиотекам государственных управлений, таких как MOBX или Redux.
  • В четвертой статье мы увидим, как делиться данными между компонентами, использующими SWR , что, вероятно, что мы должны были сделать с самого начала.

Последний код можно найти в этом Github repo. . Это типограф, но аннотации типа минимальны. Также обратите внимание, что это не код производства. Для того, чтобы сосредоточиться на государственном управлении, многие другие аспекты не были рассмотрены (например, Инверсия зависимости , тестирование или оптимизация).

Загрузка данных с крючками

Допустим, у нас есть API отдыха со списком Commodore 64 Игры. Я имею в виду, почему бы не?

Требование: Мы хотим загрузить список и отображать игры.

1. Базовая выборка

Вот как мы извлекаем наш список игр с сервера:

const getGames = () => {
  return fetch('http://localhost:3001/games/').then(response => response.json());
};

Мы можем использовать это в приложении React. Наша первая итерация выглядит так:

App.tsx (визуализируется index.tsx) ( см. репо )

import React from 'react';

const getGames = () => {
  return fetch('http://localhost:3001/games/').then(response => response.json());
};

export const App = () => {
  const [games, setGames] = React.useState([]);

  React.useEffect(() => {
    getGames().then(games => setGames(games));
  }, []);

  return 
{JSON.stringify(games, null, 2)}
; };

На первом визуализации наших Приложение Компонент, Игры Массив будет пустым. Затем, когда обещание вернулось GetGames решает, Игры Массив содержит все наши игры, и они будут отображаться очень базовой образом.

2. Пользовательский реактивный крюк

Мы можем легко извлечь это на пользовательский реактивный крюк в отдельном файле.

usegames.ts ( Смотреть репо )

import React from 'react';

const getGames = () => {
  return fetch('http://localhost:3001/games/').then(response => response.json());
};

export const useGames = () => {
  const [games, setGames] = React.useState([]);

  React.useEffect(() => {
    getGames().then(games => setGames(games));
  }, []);

  return games;
};

App.tsx ( Смотреть репо )

import React from 'react';
import { useGames } from './useGames';

export const App = () => {
  const games = useGames();
  return 
{JSON.stringify(games, null, 2)}
; };

3. Обработка ошибок и в ожидании состояния

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

Мы можем исправить это. Для этого есть библиотеки, самое популярное существо React-Async ; Но я еще не хочу добавлять зависимости. Посмотрим, какой минимальный код необходим для обработки ошибок и в ожидании состояний.

seasyncfunction

Мы пишем пользовательский крючок, который принимает ASYNC-функцию (которая возвращает обещание) и значение по умолчанию.

Этот крюк возвращает кортеж с 3 элементами: [Значение, Ошибка, Испыды] Отказ Он называет Async Function один раз *, и обновляет значение, когда он разрешается, если не будет ошибка, конечно.

function useAsyncFunction(asyncFunction: () => Promise, defaultValue: T) {
  const [state, setState] = React.useState({
    value: defaultValue,
    error: null,
    isPending: true
  });

  React.useEffect(() => {
    asyncFunction()
      .then(value => setState({ value, error: null, isPending: false }))
      .catch(error => setState({ ...state, error: error.toString(), isPending: false }));
  }, [asyncFunction]); // *

  const { value, error, isPending } = state;
  return [value, error, isPending];
}

* Useffect внутри нашего seasyncfunction Позвонит ASYNC функцию один раз, а затем каждый раз asyncfunction изменения. Для получения более подробной информации: Использование головного крюка , Использование эффекта крюка , Крючки API Ссылка Отказ

Теперь в usegames.ts мы можем просто использовать этот новый пользовательский крючок, передавая GetGames Функция и начальное значение пустого массива в качестве аргументов.

...
export const useGames = () => {
  const games = useAsyncFunction(getGames, []); // 🤔 new array on every render?
  return games;
};

Есть небольшая проблема, хотя. Мы передаем новый пустой массив каждый раз Usegames называется, что каждый раз наш Приложение Компонент оказывает. Это приводит к восстановлению наших данных на каждой рендере, но каждая извлекающаяся приводит к новым визуализации, поэтому он приводит к бесконечному циклам.

Мы можем избежать этого, сохраняя исходное значение в постоянной за крюк:

...
const emptyList = [];

export const useGames = () => {
  const [games] = useAsyncFunction(getGames, emptyList);
  return games;
};

Небольшой текстура

Вы можете пропустить этот раздел, если вы используете простой JavaScript.

Если вы используете строгий приведенный текст, вышеуказанный код не будет работать из-за опции компилятора «NoMplicitalaNy». Это потому, что Const Delpylist = []; неявно массив любой Отказ

Мы можем аннотировать это, как COND DEPTYLIST: любой [] = []; и двигаться дальше. Но мы используем Teadercript по причине. Что явный любой может (и должен) быть более конкретным.

Каковы элементы этого списка? Игры! Это список игр.

const emptyList: Game[] = [];

Конечно, теперь мы должен Определить Игра тип. Но не отчаивайся! У нас есть наш ответ JSON с сервера, когда каждый объект игры выглядит так:

{
  "id": 5,
  "title": "Kung-Fu Master",
  "year": 1984,
  "genre": "beat'em up",
  "url": "https://en.wikipedia.org/wiki/Kung-Fu_Master_(video_game)",
  "status": "in-progress",
  "img": "http://localhost:3001/img/kung-fu-master.gif"
}

Мы можем использовать Transform.tools Чтобы преобразовать это в интерфейс TearmScript (или введите).

type Game = {
  id: number;
  title: string;
  year: number;
  genre: string;
  url: string;
  status: 'not-started' | 'in-progress' | 'finished';
  img: string;
};
Еще кое-что:

Мы сказали seasyncfunction Вернул кортеж, но вывод TypectScript (@ 3.6.2) не понимает этого. Он выводит тип возврата как Массив <(Boolean | игра [] | NULL)> Отказ Мы можем явно аннотировать возвратный тип функции, чтобы быть [T, строка | null, boolean] где T это (универсальный) тип стоимость , (Строка | NULL) это тип Ошибка и логический это Испынды Отказ

export function useAsyncFunction(
  asyncFunction: () => Promise,
  defaultValue: T
): [T, string | null, boolean] {
  ...
}

Теперь, когда мы используем функцию, TypeScript предлагает правильные типы.

const [games] = useAsyncFunction(getGames, emptyList); // games is of type Game[]

Конец Skyscript Interlude.

Составление наших пользовательских крючков

seasyncfunction.ts Теперь выглядит так: ( Смотреть репо )

import React from 'react';

export function useAsyncFunction(
  asyncFunction: () => Promise,
  defaultValue: T
): [T, string | null, boolean] {
  const [state, setState] = React.useState({
    value: defaultValue,
    error: null,
    isPending: true
  });

  React.useEffect(() => {
    asyncFunction()
      .then(value => setState({ value, error: null, isPending: false }))
      .catch(error =>
        setState({ value: defaultValue, error: error.toString(), isPending: false })
      );
  }, [asyncFunction, defaultValue]);

  const { value, error, isPending } = state;
  return [value, error, isPending];
}

И мы используем его в нашем Usegames крюк:

usegames.ts ( Смотреть репо )

import { useAsyncFunction } from './useAsyncFunction';

const getGames = (): Promise => {
  return fetch('http://localhost:3001/games/').then(response => response.json());
};

type Game = {
  id: number;
  title: string;
  year: number;
  genre: string;
  url: string;
  status: 'not-started' | 'in-progress' | 'finished';
  img: string;
};

const emptyList: Game[] = [];

export const useGames = () => {
  const [games] = useAsyncFunction(getGames, emptyList);
  return games;
};

Изменение UI для отображения ошибок и ожидающих состояний

Здорово! Но мы все еще не обращаемся к ошибкам и ожидающим состояниях. Нам нужно изменить наши Приложение компонент:

import React from 'react';
import { useGames } from './useGames';

export const App = () => {
  const { games, error, isPending } = useGames();

  return (
    <>
      {error && 
ERROR! {error}...
} {isPending &&
LOADING...
}
{JSON.stringify(games, null, 2)}
); };

И наше Usegames Крючок должен вернуть объект с тремя клавишами: Игры , Ошибка , Испынды Отказ

export const useGames = () => {
  const [games, error, isPending] = useAsyncFunction(getGames, emptyList);
  return { games, error, isPending };
};

Мы также улучшаем наши GetGames Функция для обработки кодов состояния HTTP отличается от 200 в качестве ошибок:

const getGames = (): Promise => {
  return fetch('http://localhost:3001/games/').then(response => {
    if (response.status !== 200) {
      throw new Error(`${response.status} ${response.statusText}`);
    }
    return response.json();
  });
};

Наш код пока выглядит так: ( См. Репо ).

Заключение

Мы видели, как загружать данные из API отдыха с помощью реактивных крюков.

В Следующая статья Посмотрим, как изменить удаленные данные, используя HTTP Патч Запрос и как обновить наши данные клиента, когда запрос успешен.

Ресурсы

Дальнейшее чтение:

Оригинал: “https://dev.to/juliang/loading-and-displaying-data-with-hooks-jlj”