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

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

Во-первых, важно понимать, что этот хук довольно новый, и он только в экспериментальной версии React. Итак, вам нужно убедиться, что вы используете экспериментальную версию React и React DOM, чтобы начать пробовать и использовать этот хук.

  "dependencies": {
    "react": "experimental",
    "react-dom": "experimental"
  },

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

App.tsx

import { useState } from "react"
import { Data } from "./Data"
const URLS = {
  USERS:
    "https://dummyjson.com/users?limit=3&select=id,firstName,lastName,gender,age",
  POSTS: "https://dummyjson.com/posts?limit=3&select=id,title,body",
  COMMENTS: "https://dummyjson.com/comments?limit=3",
}
function App() {
  const [url, setUrl] = useState(URLS.USERS)
  const handleUrlChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setUrl(event.target.value)
  }
  return (
    <div className="App">
      <div className="radios">
        <label>
          <input
            type="radio"
            value={URLS.USERS}
            checked={url === URLS.USERS}
            onChange={handleUrlChange}
          />
          Users
        </label>
        <label>
          <input
            type="radio"
            value={URLS.POSTS}
            checked={url === URLS.POSTS}
            onChange={handleUrlChange}
          />
          Posts
        </label>
        <label>
          <input
            type="radio"
            value={URLS.COMMENTS}
            checked={url === URLS.COMMENTS}
            onChange={handleUrlChange}
          />
          Comments
        </label>
      </div>
      <Data url={url} />
    </div>
  )
}

export default App

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

Data.tsx

import { useEffect, useState } from "react"
interface IProps {
  url: string
}
export const Data: React.FC<IProps> = ({ url }) => {
  const [isLoading, setIsLoading] = useState(true)
  const [isError, setIsError] = useState(false)
  const [data, setData] = useState()

  useEffect(() => {
    setIsError(false)
    setIsLoading(true)
    setData(undefined)

    fetch(url)
      .then((res) => res.json())
      .then(setData)
      .catch(() => setIsError(true))
      .finally(() => setIsLoading(false))
  }, [url])

  return isLoading ? (
    <h1>Loading....</h1>
  ) : isError ? (
    <h1>Error</h1>
  ) : (
    <pre>{JSON.stringify(data, null, 2)}</pre>
  )
}

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

Когда запрос на выборку возвращает обещание, у нас могут быть либо наши данные, либо ошибка. Мы устанавливаем наше состояние на загрузку, ошибку и данные, затем внутри нашей функции fetch() мы обязательно получаем данные и устанавливаем ошибку, если мы их получаем; если все работает нормально, мы меняем состояние загрузки с false на true, чтобы в случае ошибки отображался значок загрузки.

Это немного громоздко, и работа с чем-либо асинхронным, таким как промисы и React, всегда была серьезной проблемой, но хук use решает все эти проблемы за нас. Мы можем избавиться от всего нашего useState, нашего useEffect и остального нашего кода, поэтому все, что мы делаем, — это простой запрос на выборку.

Data.tsx (после изменения)

import { use } from "react"
interface IProps {
  url: string
}
export const Data: React.FC<IProps> = ({ url }) => {
  const data = use(fetch(url).then((res) => res.json()))

  return <pre>{JSON.stringify(data, null, 2)}</pre>
}

Чтобы использовать хук use, мы передаем ему обещание. Это обещание выборки возвращает либо данные JSON, либо ошибку, которую мы установили в переменную с именем Data. То, как работает этот хук, очень похоже на то, как если бы вы сделали что-то вроде await this fetch, где он даст вам данные или выдаст ошибку, которую вы должны поймать где-то еще.

App.tsx (после изменения)

import { Suspense, useState } from "react"
import { Data } from "./Data"
import ErrorBoundry from "./ErrorBoundary"
const URLS = {
  USERS:
    "https://dummyjson.com/users?limit=3&select=id,firstName,lastName,gender,age",
  POSTS: "https://dummyjson.com/posts?limit=3&select=id,title,body",
  COMMENTS: "https://dummyjson.com/comments?limit=3",
}
function App() {
  const [url, setUrl] = useState(URLS.USERS)
  const handleUrlChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setUrl(event.target.value)
  }
  return (
    <div className="App">
      <div className="radios">
        <label>
          <input
            type="radio"
            value={URLS.USERS}
            checked={url === URLS.USERS}
            onChange={handleUrlChange}
          />
          Users
        </label>
        <label>
          <input
            type="radio"
            value={URLS.POSTS}
            checked={url === URLS.POSTS}
            onChange={handleUrlChange}
          />
          Posts
        </label>
        <label>
          <input
            type="radio"
            value={URLS.COMMENTS}
            checked={url === URLS.COMMENTS}
            onChange={handleUrlChange}
          />
          Comments
        </label>
      </div>
      <ErrorBoundry fallback={<div>{"Error..."}</div>}>
        <Suspense fallback={<div>{"Loading..."}</div>}>
          <Data url={url} />
        </Suspense>
      </ErrorBoundry>
    </div>
  )
}

export default App

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

Чтобы поймать ошибки в нашем коде, нам нужно использовать то, что называется границей ошибки. Это особенно часто встречается при работе с саспенсом.

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

Далее мы будем использовать реквизит под названием «откат» для отображения сообщения об ошибке в случае возникновения ошибки. Если ошибки нет, то мы просто визуализируем все, что находится внутри границы ошибки. Этот подход работает очень похоже на то, как мы справляемся с неизвестностью.

ErrorBoundry.tsx

import React from "react"
class ErrorBoundry extends React.Component {
  state = { hasError: false, error: null }

  static getDeliveredStateFromError(error: any) {
    return {
      hasError: true,
      error,
    }
  }
  render() {
    if (this.state.hasError) {
      return this.props.fallback
    }
    return this.props.children
  }
}

export default ErrorBoundry

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

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