React Async для декларативного извлечения данных

Благодаря конструкции

async-await
мы получаем возможность перейти императивному синхронному коду для извлечения данных. React — это библиотека, которая упрощает декларативное создание пользовательских интерфейсов. Вот почему одновременно может применяться императивный код (для извлечения данных), так и декларативный (для создания пользовательского интерфейса).

React-async предоставляет декларативный API для вызова REST API с использованием одного компонента React. Он выполняет обработку ошибок, разрешение promise и повторный вызов promise. А также отлично справляется с локальным асинхронным состоянием.

Содержание

Декларативное извлечение данных с помощью React-Async

Декларативное извлечение данных — это подход, используемый при асинхронных вызовах API. Это противоположность императивного подхода, при реализации которого необходимо детализировать этапы.

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

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

Продемонстрируем это, выполнив вызов к конечной точке, чтобы получить список цен на криптовалюту.

import React, { Component } from 'react';
import axios from 'axios';

class App extends Component {
  state = {
    data: [],
    error: '',
  };

  componentDidMount() {     
    axios
      .get('https://api.coinmarketcap.com/v1/ticker/?limit=1')
      .then(res => this.setState({ data: res.data }))
      .catch(error => this.setState({ error }));
  }

  render () {
    return (
      <div className="App">
        <ul>
          {this.state.data.map(el => (
            <li>
              {el.name}: {el.price_usd}
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

export default App;

Примечание: установите axios, используя команду ‘npm install axios`

Посмотреть демо

Здесь мы выполняем вызов API в функции componentDidMount, чтобы он начал работать сразу после загрузки компонента. Для работы с данными нужно выполнить следующие этапы:

  • Выполнить запрос к API.
  • Получить ответ
  • Извлечь данные из ответа
  • Сохранить данные локально;

При возникновении ошибки в процессе извлечения данных:

  • Перехватить ошибку
  • Сохранить данные локально.

Но если код написан декларативно, то его можно переписать так, чтобы стал менее объемным. Для этого используем React-Async.

Сначала нужно установить пакет, введя в терминале команду

npm install react-async
. Затем создать компонент:

import React, { Component } from 'react';
import Async from 'react-async';

const loadJson = () =>
  fetch("https://api.coinmarketcap.com/v1/ticker/?limit=1")
    .then(res => (res.ok ? res : Promise.reject(res)))
    .then(res => res.json())

const App = () => (
  <Async promiseFn={loadJson}>
    {({ data, error, isLoading }) => {
      if (isLoading) return "Loading..."
      if (error) return ``Something went wrong: ${error.message}``

      if (data)
        return (
          <div>
             {data.map(el => (
              <li>
                {el.name}: {el.price_usd}
              </li>
             ))}
          </div>
        )

      return null
    }}
  </Async>
)

export default App;

Но так как мы используем CodeSandbox, то добавим React-Async из меню зависимостей.

Посмотреть демо

Мы переписали компонент, используя вместо классов хуки. Сначала мы создаем функцию 

loadJson
для обработки данных. Затем внутри компонента App используем компонент Async, доступный через библиотеку React-Async.

Когда promise будет разрешен, мы получим свойства для работы с другими сценариями.

  • С помощью функции isLoading можно вывести для пользователей сообщение, пока данные еще не загружены.
  • errorстановится доступен в случае ошибки во время извлечения.
  • data— это фактические данные, возвращаемые после завершения извлечения.

Теперь нам больше не нужно использовать классы или методы жизненного цикла для загрузки данных. И при этом мы не указывали React-Async, как обрабатывать данные или как обновлять состояние.

React-Async управляет состоянием загрузки через резервный isLoading. Он обрабатывается до тех пор, пока

data
не  будет готов к визуализации. То есть когда зависимый асинхронный вызов разрешит и вернет данные.

Вспомогательные компоненты

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

const App = () => (
  <Async promiseFn={loadJson}>
    <Async.Loading>Loading...</Async.Loading>

    <Async.Resolved>
      {data => (
        <div>           
          {data.map(el => (
            <li>
              {el.name}: {el.price_usd}
            </li>
          ))}
        </div>
      )}
    </Async.Resolved>

    <Async.Rejected>
      {error => `Something went wrong: ${error.message}`}
    </Async.Rejected>
  </Async>
)

Посмотреть демо

В приведенном выше примере, чтобы упростить код и сделать его более удобным для чтения, мы использовали функции

**Async.Loading**,
 
**Async.Resolved**
и 
**Async.Rejected**.

Вспомогательные компоненты, предоставляемые React-Async, могут принимать в качестве дочерних элементов элемент React или функцию. Когда вы предоставите функцию, то получите свойство рендера, которое можно использовать в своем компоненте.

Дополнительные вспомогательные функции

Создадим небольшое приложение для профиля пользователя, которое использует еще несколько вспомогательных функций. Обновите свой компонент следующим образом:

import React, { Component } from 'react';
import Async from 'react-async';

const loadUser = ({ userId }) =>
  fetch('`https://reqres.in/api/users/${userId}'`)
    .then(res => (res.ok ? res : Promise.reject(res)))
    .then(res => res.json())

const UserPlaceholder = () => (
  <div>
    <div>User Details Loading</div>
  </div>
)

const UserDetails = ({ data }) => (
  <div className="details">
    <img className="avatar" src={data.data.avatar} alt="" />
    <div>
      {data.data.first_name} {data.data.last_name}
    </div>
  </div>
)

const App = () => (
    <Async promiseFn={loadUser} userId={1}>
      <Async.Pending>
        <UserPlaceholder />
      </Async.Pending>
      <Async.Fulfilled>{data => <UserDetails data={data} />}</Async.Fulfilled>
      <Async.Rejected>{error => <p>{error.message}</p>}</Async.Rejected>
    </Async>
)
export default App;

Посмотреть демо

Рассмотрим функции, которые мы объявили:

  • loadUser — функция для обработки выборки данных. Она принимает свойство (userId) и запрашивает API на основе идентификатора.
  • userPlaceholder — это запасной компонент, который будет отображаться, когда promise еще не разрешен.
  • userDetails — этот компонент обрабатывает отображение пользовательских данных. Он принимает данные через свойство и обрабатывается только после разрешения promise .

Что дальше?

В этом руководстве мы рассмотрели, как использовать библиотеку React-Async, чтобы упростить извлечение данных. А также вспомогательные функции, которые она предлагает. React-Async — это мощный инструмент. Чтобы узнать о нем больше, ознакомьтесь с официальной документацией.

Данная публикация представляет собой перевод статьи «React Async for Declarative Data Fetching» , подготовленной дружной командой проекта Интернет-технологии.ру

Меню