API хуков React стал эволюционным скачком в том, как мы обрабатываем состояние и побочные эффекты в наших компонентах React. Хуки позволяют использовать состояние и другие функции React без написания класса. Среди всего разнообразия хуков выделяется одна особенность — возможность создавать свои собственные хуки. Это позволяет вам абстрагировать и повторно использовать логику состояния в компонентах, делая ваш код СУХИМ (не повторяйте себя), более легким для чтения и более удобным для сопровождения. В этом посте мы углубимся в мир кастомных хуков.

Разрушение пользовательских хуков

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

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

Основные хуки: строительные блоки пользовательских хуков

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

использование состояния

useState — это хук, который мы используем для добавления состояния нашим функциональным компонентам. Вот простой пример использования:

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

использованиеЭффект

useEffect — это хук, который позволяет нам выполнять побочные эффекты в наших функциональных компонентах. Среди прочего, побочными эффектами могут быть выборка данных, подписки или ручное изменение DOM. Вот простой пример useEffect:

import { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

В этом примере хук useEffect обновляет заголовок документа после того, как React обновляет DOM.

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

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

import { useState, useEffect } from 'react';

function useWindowSize() {
  // We initialize the state with undefined to distinguish between the
  // JavaScript environment and when we've measured the window size.
  const [windowSize, setWindowSize] = useState({
    width: undefined,
    height: undefined,
  });

  useEffect(() => {
    // We define a function to call when the window is resized.
    function handleResize() {
      // We set the window width and height into the state.
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    }
    
    // We add an event listener to the window for the resize event.
    window.addEventListener("resize", handleResize);
    
    // We call the handleResize function to set the initial size.
    handleResize();
    
    // Cleanup after effect
    // We remove the event listener when the component is unmounted.
    return () => window.removeEventListener("resize", handleResize);
  }, []); // The empty array ensures that this effect runs only on component mount

  return windowSize;
}

В этом фрагменте кода мы создаем хук useWindowSize, который использует хуки useState и useEffect для отслеживания текущего размера окна.

Использование нашего пользовательского хука

Теперь, когда мы создали наш пользовательский хук, давайте посмотрим, как мы можем использовать его в компоненте:

import React from 'react';
import useWindowSize from './useWindowSize';

function ShowWindowDimensions() {
  const size = useWindowSize();

  return (
    <div>
      {size.width}px / {size.height}px
    </div>
  );
}

export default ShowWindowDimensions;

Здесь мы используем наш хук useWindowSize в компоненте ShowWindowDimensions. Этот компонент теперь будет отображать текущую ширину и высоту окна и будет перерисовываться при каждом изменении размера окна.

Шаг вперед: пример более сложного пользовательского хука

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

import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchData() {
      setLoading(true);
      try {
        const response = await fetch(url);
        const data = await response.json();
        setData(data);
        setError(null);
      } catch (error) {
        setError(error);
      } finally {
        setLoading(false);
      }
    }

    fetchData();
  }, [url]); // dependency array ensures the effect runs when url changes

  return { data, loading, error };
}

В хуке useFetch мы используем useState для управления данными, загрузкой и состояниями ошибок. Хук useEffect отвечает за получение данных с предоставленного URL-адреса.

Использование пользовательского хука выборки данных

Чтобы использовать наш хук useFetch, мы создаем компонент, который извлекает и отображает список сообщений из JSONPlaceholder:

import React from 'react';
import useFetch from './useFetch';

function Posts() {
  const { data: posts, loading, error } = useFetch("https://jsonplaceholder.typicode.com/posts");

  if (loading) {
    return <div>Loading...</div>;
  }

  if (error) {
    return <div>Error: {error.message}</div>;
  }

  return (
    <div>
      <h1>Posts</h1>
      {posts.map((post) => (
        <div key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.body}</p>
        </div>
      ))}
    </div>
  );
}

export default Posts;

Здесь компонент Posts извлекает и отображает список сообщений. Состояние компонента (данные, загрузка и ошибка) полностью управляется хуком useFetch.

Заключение: сила нестандартных хуков

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

По словам инженера-программиста Роберта К. Мартина, «Чистый код не создается с соблюдением набора правил. Вы не станете мастером программного обеспечения, изучив список эвристик. Профессионализм и мастерство исходят из ценностей, которые определяют дисциплину». Используя настраиваемые хуки для абстрагирования и повторного использования логики в компонентах, мы можем создавать более профессиональный, удобный в сопровождении и чистый код.

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

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

Если вам понравилась статья и вы хотите выразить свою поддержку, сделайте следующее:

👏 Аплодируйте истории (50 аплодисментов), чтобы эта статья попала в топ

👉Подпишитесь на меня в Среднем

Посмотрите больше контента в моем профиле Medium