React — это библиотека пользовательского интерфейса JavaScript с открытым исходным кодом, разработанная Facebook, она завоевала большую популярность в сообществе разработчиков интерфейса.

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

В версии 16.8 в React были добавлены хуки. Хуки позволяют функциональным компонентам иметь доступ к состояниям некоторых функций React (таким как состояния, методы жизненного цикла). Благодаря хукам отпала необходимость в компонентах класса.

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

Примечание. Я просто дам небольшое введение об этих хуках. Использование этих хуков — отдельная тема.

  • useState: Хук React useState позволяет нам генерировать и отслеживать состояния в функциональных компонентах.
  • useEffect: Хук React useEffect позволяет нам выполнять побочные эффекты в функциональных компонентах.
  • useRef: Хук React useRef позволяет нам сохранять значения между рендерами. Мы можем использовать useRef для доступа к элементам DOM.

React предлагает нам больше хуков ( useCallback, useMemo, useContext, useReducer ...)

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

Пользовательский хук useFetch

Представьте, что нам нужен вызов GET fetch для нашего компонента. Лучшее место, где мы делаем этот вызов, — перед рендерингом компонента, поэтому мы должны использовать хук useEffect для этой операции. Простой код для работы с выборкой должен выглядеть следующим образом:

import React, { useState, useEffect } from 'react';
import useFetch from '../hooks'

const initialState = {
    data: null,
    error: null,
    loading: null,
}

const ExampleComponent = () =>  {
  
  const [info, setInfo] = useState({...initialState})

  const fetchData = async (url) => {
        try {
            const result = await fetch(url);
            const data = await res.json();
            setInfo(prev => ({...prev, data, loading: false}))
        } catch (error) {
            setInfo(prev => ({...prev, error, loading: false}))
        }
    }

    useEffect(() => {
        setInfo(prev => ({...prev, loading: true}))
        fetchData(targetURL)
    },[])

  return (
    <>
      {error && 'Error!'}
      {loading && 'Loading...'}
      {data.entries.map(el => (
        <div key={el.API}>{el.description}</div>
      )}
    </>
  )
}

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

Основная цель этого хука — вернуть нам три разных значения: данные, ошибку и состояние загрузки соответственно.

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

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

Чтобы определить новый пользовательский хук, нам нужны встроенные хуки, которые нам обслуживает React.

Мы сохраняем параметры нашего состояния с помощью хука useState и делаем вызов выборки с помощью хука useEffect.

Решение должно выглядеть так:

import React, { useEffect, useState } from 'react';

const initialState = {
    data: null,
    error: null,
    loading: null,
}

const useFetch = (targetURL) => {
    const [info, setInfo] = useState({...initialState})

    const fetchData = async (url) => {
        try {
            const result = await fetch(url);
            const data = await res.json();
            setInfo(prev => ({...prev, data, loading: false}))
        } catch (error) {
            setInfo(prev => ({...prev, error, loading: false}))
        }
    }

    useEffect(() => {
        setInfo(prev => ({...prev, loading: true}))
        fetchData("https://api.publicapis.org/entries")
    },[targetURL])
    
    return (
        <>
            {error && 'Error!'}
	    {loading && 'Loading...'}
	    {data.entries.map(el => (
	        <div key={el.API}>{el.description}</div>
	    )}
	</>
    )
}

export default useFetch;

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

const {data, error, loading} = useFetch("exampleURL")

Будет лучше хранить возвращаемые значения в самом состоянии. Полностью покрытый пример использования:

import React, { useState } from 'react';
import useFetch from '../hooks'

const ExampleComponent = () =>  {
  
  const [info, setInfo] = useState(useFetch("https://api.publicapis.org/entries"))
  return (
    <>
      {error && 'Error!'}
      {loading && 'Loading...'}
      {data.entries.map(el => (
        <div key={el.API}>{el.description}</div>
      )}
    </>
  )
}

В результате нам удалось создать полностью настраиваемый хук для выполнения всех запросов GET, которые могут возникнуть в будущем. Этот хук открыт для разработки, например, в него можно добавить флаг для операций POST. Основная цель этого поста — дать представление о том, как это работает.