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

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

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

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

import { useState, useEffect } from 'react';

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

    useEffect(() => {
        setLoading(true);
        fetch(url)
            .then(response => response.json())
            .then(json => setData(json))
            .catch(err => setError(err))
            .finally(() => setLoading(false));
    }, [url]);

    return { data, loading, error };
}

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

Принципы построения пользовательских хуков

  1. Инкапсуляция состояния и побочных эффектов Хук должен скрывать внутреннюю реализацию состояния и побочных эффектов. Компонент получает только нужные значения и функции управления.

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

const { data, loading, error } = useFetch('https://api.example.com/items');
  1. Повторное использование логики Любая логика, которая повторяется в нескольких компонентах, может быть вынесена в хук. Это улучшает читаемость и поддержку кода.

  2. Следование правилам хуков Пользовательские хуки обязаны вызывать другие хуки только на верхнем уровне функции. Любое условное использование нарушает работу внутреннего механизма React и вызывает ошибки.

Примеры сложных пользовательских хуков

Хук для формы с валидацией

import { useState } from 'react';

function useForm(initialValues, validate) {
    const [values, setValues] = useState(initialValues);
    const [errors, setErrors] = useState({});

    const handleChange = (event) => {
        const { name, value } = event.target;
        setValues(prev => ({ ...prev, [name]: value }));
    };

    const handleSubmit = (callback) => (event) => {
        event.preventDefault();
        const validationErrors = validate(values);
        setErrors(validationErrors);
        if (Object.keys(validationErrors).length === 0) {
            callback();
        }
    };

    return { values, errors, handleChange, handleSubmit };
}

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

Хук для локального хранения состояния

import { useState, useEffect } from 'react';

function useLocalStorage(key, initialValue) {
    const [storedValue, setStoredValue] = useState(() => {
        try {
            const item = window.localStorage.getItem(key);
            return item ? JSON.parse(item) : initialValue;
        } catch (error) {
            console.error(error);
            return initialValue;
        }
    });

    const setValue = (value) => {
        try {
            const valueToStore = value instanceof Function ? value(storedValue) : value;
            setStoredValue(valueToStore);
            window.localStorage.setItem(key, JSON.stringify(valueToStore));
        } catch (error) {
            console.error(error);
        }
    };

    return [storedValue, setValue];
}

useLocalStorage синхронизирует состояние компонента с localStorage, что удобно для сохранения настроек или данных между сессиями.

Комбинирование пользовательских хуков

Пользовательские хуки можно комбинировать для создания сложных функциональных блоков:

function useAuthenticatedFetch(url, token) {
    const { data, loading, error } = useFetch(url);
    const [authError, setAuthError] = useState(null);

    useEffect(() => {
        if (error && error.status === 401) {
            setAuthError('Необходима аутентификация');
        }
    }, [error]);

    return { data, loading, error: authError || error };
}

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

Лучшие практики

  • Использовать префикс use в названии каждого хука.
  • Возвращать только необходимые данные и методы, скрывая внутренние детали.
  • Минимизировать количество побочных эффектов в хуках.
  • Документировать хуки и их параметры для удобства повторного использования.
  • Проверять зависимости useEffect, чтобы избежать бесконечных циклов рендеринга.

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