Hooks в Fresh

В Fresh, фреймворке для создания современных веб-приложений на JavaScript и TypeScript с использованием Deno, концепция Hooks заимствована из подхода React, но адаптирована под серверный и клиентский рендеринг без необходимости виртуального DOM. Hooks позволяют управлять состоянием, жизненным циклом компонентов и побочными эффектами, сохраняя чистоту функций и предсказуемость поведения компонентов.


useState

useState — базовый Hook для управления локальным состоянием компонента.

import { useState } from "https://deno.land/x/fresh/hooks.ts";

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

  return (
    <button onCl ick={() => setCount(count + 1)}>
      Счётчик: {count}
    </button>
  );
}

Ключевые моменты:

  • Инициализация состояния: Передается начальное значение. Оно может быть примитивом, объектом или массивом.
  • Функция обновления: setCount заменяет текущее значение новым. В отличие от классового состояния в React, обновление полностью заменяет значение.
  • Перерисовка компонента: Любое изменение состояния вызывает повторный рендер соответствующего компонента на клиенте.

useEffect

useEffect управляет побочными эффектами, например, сетевыми запросами или подписками. В Fresh Hook работает аналогично React, но учитывает серверный рендеринг.

import { useEffect } from "https://deno.land/x/fresh/hooks.ts";

function DataFetcher() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch("/api/data")
      .then(res => res.json())
      .then(setData);
  }, []);
  
  return <div>{data ? JSON.stringify(data) : "Загрузка..."}</div>;
}

Особенности:

  • Массив зависимостей: Определяет, когда эффект выполняется. Пустой массив [] — один раз после монтирования.
  • Очистка эффектов: Возвращаемая функция используется для отмены подписок или таймеров.
useEffect(() => {
  const interval = setInterval(() => console.log("Тик"), 1000);
  return () => clearInterval(interval);
}, []);

useMemo и useCallback

useMemo и useCallback оптимизируют производительность, предотвращая лишние вычисления или пересоздание функций при каждом рендере.

import { useMemo, useCallback } from "https://deno.land/x/fresh/hooks.ts";

function ExpensiveComponent({ items }) {
  const total = useMemo(() => {
    return items.reduce((sum, item) => sum + item.value, 0);
  }, [items]);

  const handleClick = useCallback(() => {
    console.log("Нажато");
  }, []);

  return <button onCl ick={handleClick}>Сумма: {total}</button>;
}

Ключевые моменты:

  • useMemo возвращает мемоизированное значение.
  • useCallback возвращает мемоизированную функцию.
  • Оба Hook используют массив зависимостей, чтобы отслеживать изменения и решать, нужно ли пересчитывать значение или функцию.

useRef

useRef позволяет сохранять изменяемые объекты между рендерами без вызова повторного рендера.

import { useRef } from "https://deno.land/x/fresh/hooks.ts";

function FocusInput() {
  const inputRef = useRef(null);

  const focus = () => inputRef.current?.focus();

  return (
    <>
      <input ref={inputRef} />
      <button onCl ick={focus}>Фокус</button>
    </>
  );
}

Применение:

  • Хранение DOM-узлов для прямого доступа.
  • Хранение состояния, которое не влияет на рендер.
  • Поддержка таймеров или идентификаторов без обновления компонента.

Особенности Hook-системы Fresh

  • Серверный рендеринг: Hooks учитывают различие между серверным и клиентским рендерингом. Эффекты, требующие браузерного окружения, выполняются только на клиенте.
  • Изоляция компонентов: Состояние каждого компонента полностью независимое, что предотвращает конфликты между инстанциями.
  • Типизация в TypeScript: Использование Hooks с TypeScript позволяет строго проверять типы состояния и функций обновления.
const [count, setCount] = useState<number>(0);

Комбинирование Hooks

Hooks можно комбинировать для реализации сложной логики:

function Timer() {
  const [seconds, setSeconds] = useState(0);
  const intervalRef = useRef<number | null>(null);

  useEffect(() => {
    intervalRef.current = setInterval(() => setSeconds(s => s + 1), 1000);
    return () => intervalRef.current && clearInterval(intervalRef.current);
  }, []);

  const reset = useCallback(() => setSeconds(0), []);

  return (
    <div>
      Время: {seconds} секунд
      <button onCl ick={reset}>Сброс</button>
    </div>
  );
}

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


Практические рекомендации

  • Использовать useState для локального состояния компонента, а глобальное состояние хранить через контексты или внешние хранилища.
  • Эффекты с useEffect разделять по задачам, чтобы не создавать громоздкие функции.
  • Применять useMemo и useCallback только там, где есть реальная нагрузка, чтобы избежать избыточного кода.
  • useRef использовать для управления DOM и таймерами, которые не должны вызывать ререндер.

Hooks в Fresh формируют единый, предсказуемый и эффективный способ управления состоянием и поведением компонентов, позволяя создавать масштабируемые и отзывчивые веб-приложения.