Параметризация хуков

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


Основы хуков с параметрами

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

import { useState, useEffect } from 'haunted';

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

  function increment() {
    setCount(c => c + step);
  }

  return [count, increment];
}

Здесь useCounter принимает два параметра: initialValue и step. Такой подход позволяет использовать один и тот же хук с разными начальными значениями и шагом изменения:

const [counter1, increment1] = useCounter(0, 1);
const [counter2, increment2] = useCounter(10, 5);

Параметры и реактивность

Важно понимать, что в Haunted аргументы хуков не являются реактивными сами по себе. Если значение аргумента изменится после первого вызова хука, его внутреннее состояние не обновится автоматически. Для управления изменяемыми параметрами используется комбинация useState и useEffect:

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

  useEffect(() => {
    setCount(initialValue);
  }, [initialValue]);

  function increment() {
    setCount(c => c + 1);
  }

  return [count, increment];
}

В этом примере счётчик будет подстраиваться под новые значения параметра initialValue после его изменения.


Параметризация побочных эффектов

useEffect в Haunted может принимать зависимости, аналогично React, что позволяет параметризовать побочные эффекты. Это полезно для вызова API или подписок с разными конфигурациями:

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

  useEffect(() => {
    if (!url) return;
    setLoading(true);
    fetch(url)
      .then(res => res.json())
      .then(json => setData(json))
      .finally(() => setLoading(false));
  }, [url]);

  return { data, loading };
}

Параметр url позволяет динамически менять источник данных. Каждый новый URL триггерит повторное выполнение эффекта.


Создание настраиваемых хуков с замыканиями

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

function useTimer(interval = 1000) {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setSeconds(s => s + 1);
    }, interval);

    return () => clearInterval(id);
  }, [interval]);

  return seconds;
}

Параметр interval задаёт частоту обновления таймера. Замыкание обеспечивает доступ к текущему значению интервала без нарушения реактивности.


Параметризация хуков с объектами и функциями

Хуки можно параметризовать не только примитивными типами, но и объектами, функциями, массивами. Важно учитывать референсное сравнение для зависимостей useEffect:

function useFilteredList(items, filterFn) {
  const [filtered, setFiltered] = useState(() => items.filter(filterFn));

  useEffect(() => {
    setFiltered(items.filter(filterFn));
  }, [items, filterFn]);

  return filtered;
}

Если filterFn создаётся заново при каждом рендере, эффект будет выполняться чаще, чем нужно. Решением является мемоизация функций через useCallback или внешнее определение функции.


Практика: комбинирование параметров

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

function usePaginatedFetch(url, pageSize = 10) {
  const [page, setPage] = useState(1);
  const { data, loading } = useFetch(`${url}?_limit=${pageSize}&_page=${page}`);

  function nextPage() {
    setPage(p => p + 1);
  }

  function prevPage() {
    setPage(p => Math.max(1, p - 1));
  }

  return { data, loading, page, nextPage, prevPage };
}

Здесь pageSize параметризует как сам хук usePaginatedFetch, так и вложенный useFetch, обеспечивая гибкость и переиспользуемость.


Рекомендации по параметризации

  • Использовать по умолчанию значения параметров, чтобы хук был максимально универсальным.
  • Реактивные значения параметров обрабатывать через useEffect или useState, если их изменения должны отражаться на состоянии.
  • Мемоизировать функции и объекты для зависимостей эффектов, чтобы избегать лишних пересчётов.
  • Для сложных хуков создавать композицию простых параметризованных хуков, а не один массивный хук.

Параметризация хуков в Haunted превращает их в мощный инструмент для создания настраиваемой, реактивной и модульной логики веб-компонентов, позволяя строить гибкие и легко сопровождаемые приложения.