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

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


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

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

Пример простого пользовательского хука для счётчика:

import { useState } from 'preact/hooks';

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

    const increment = () => setCount(prev => prev + 1);
    const decrement = () => setCount(prev => prev - 1);
    const reset = () => setCount(initialValue);

    return { count, increment, decrement, reset };
}

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

function CounterComponent() {
    const { count, increment, decrement, reset } = useCounter(5);

    return (
        <div>
            <p>Счёт: {count}</p>
            <button onCl ick={increment}>+</button>
            <button onCl ick={decrement}>-</button>
            <button onCl ick={reset}>Сброс</button>
        </div>
    );
}

Комбинирование хуков

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

Пример пользовательского хука для работы с API:

import { useState, useEffect } from 'preact/hooks';

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 => {
                if (!response.ok) throw new Error('Ошибка сети');
                return response.json();
            })
            .then(json => setData(json))
            .catch(err => setError(err))
            .finally(() => setLoading(false));
    }, [url]);

    return { data, loading, error };
}

Использование этого хука в компоненте выглядит лаконично:

function UsersList() {
    const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/users');

    if (loading) return <p>Загрузка...</p>;
    if (error) return <p>Ошибка: {error.message}</p>;

    return (
        <ul>
            {data.map(user => <li key={user.id}>{user.name}</li>)}
        </ul>
    );
}

Пользовательские хуки с мемоизацией и колбэками

Для оптимизации производительности часто используют useMemo и useCallback внутри пользовательских хуков. Это позволяет избегать лишних пересчётов значений и пересоздания функций при ререндере.

Пример хука с мемоизацией:

import { useState, useMemo } from 'preact/hooks';

function useFilteredList(items, filter) {
    const [search, setSearch] = useState('');

    const filteredItems = useMemo(() => {
        return items.filter(item => item.toLowerCase().includes(search.toLowerCase()));
    }, [items, search, filter]);

    return { filteredItems, search, setSearch };
}

Организация и переиспользование логики

Создание пользовательских хуков повышает читаемость и структуру проекта. Основные рекомендации при разработке хуков:

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

Примеры расширенных хуков

  1. Хук для работы с локальным хранилищем:
import { useState, useEffect } from 'preact/hooks';

function useLocalStorage(key, initialValue) {
    const [value, setValue] = useState(() => {
        const stored = localStorage.getItem(key);
        return stored ? JSON.parse(stored) : initialValue;
    });

    useEffect(() => {
        localStorage.setItem(key, JSON.stringify(value));
    }, [key, value]);

    return [value, setValue];
}
  1. Хук для отслеживания размеров окна:
import { useState, useEffect } from 'preact/hooks';

function useWindowSize() {
    const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });

    useEffect(() => {
        const handleResize = () => setSize({ width: window.innerWidth, height: window.innerHeight });
        window.addEventListener('resize', handleResize);
        return () => window.removeEventListener('resize', handleResize);
    }, []);

    return size;
}

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