Что такое hooks в Bliss

В библиотеке Bliss.js hooks представляют собой механизм для управления состоянием, жизненным циклом и поведением компонентов в реактивном стиле. Они позволяют отделить логику работы с DOM и состоянием от структуры разметки, обеспечивая чистый и удобный код. Hooks в Bliss.js можно рассматривать как функции, которые подключаются к компонентам и реагируют на изменения данных, событий или жизненного цикла.

useState: локальное состояние компонента

useState — базовый hook для управления локальным состоянием. Он позволяет хранить значение и обновлять его, вызывая повторный рендер компонента при изменении.

Пример использования:

import { useState } from 'blissjs';

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

    return `
        <div>
            <p>Счетчик: ${count}</p>
            <button oncl ick="${() => setCount(count + 1)}">Увеличить</button>
        </div>
    `;
}

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

  • useState(initialValue) возвращает массив из текущего значения и функции для его обновления.
  • Любое изменение состояния через setCount автоматически обновляет компонент.
  • Значение состояния можно использовать напрямую в шаблоне или логике компонента.

useEffect: побочные эффекты и жизненный цикл

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

Пример:

import { useState, useEffect } from 'blissjs';

function Timer() {
    const [seconds, setSeconds] = useState(0);

    useEffect(() => {
        const interval = setInterval(() => setSeconds(s => s + 1), 1000);
        return () => clearInterval(interval);
    }, []);
    
    return `<p>Прошло секунд: ${seconds}</p>`;
}

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

  • Первый аргумент useEffect — функция, выполняющая побочный эффект.
  • Возвращаемая функция используется для очистки ресурсов при размонтировании компонента.
  • Второй аргумент — массив зависимостей. Если он пустой, эффект выполняется один раз после монтирования.

useRef: сохранение ссылок на DOM и значения между рендерами

useRef позволяет хранить ссылки на элементы DOM или любые данные, которые не должны вызывать повторный рендер при изменении.

import { useRef, useEffect } from 'blissjs';

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

    useEffect(() => {
        inputRef.current.focus();
    }, []);

    return `<input type="text" ref="${inputRef}"/>`;
}

Характерные свойства:

  • useRef(initialValue) возвращает объект с полем current.
  • Изменение current не вызывает обновления компонента.
  • Используется для прямого взаимодействия с DOM или сохранения любых промежуточных данных.

useMemo и useCallback: оптимизация производительности

Для компонентов с тяжелой логикой используются useMemo и useCallback.

useMemo кэширует результат функции:

import { useMemo, useState } from 'blissjs';

function Fibonacci({ n }) {
    const fib = useMemo(() => {
        function calcFib(x) {
            if (x <= 1) return x;
            return calcFib(x - 1) + calcFib(x - 2);
        }
        return calcFib(n);
    }, [n]);

    return `<p>Фибоначчи(${n}) = ${fib}</p>`;
}

useCallback кэширует функцию, предотвращая её пересоздание при каждом рендере:

import { useState, useCallback } from 'blissjs';

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

    const increment = useCallback(() => setCount(c => c + 1), []);

    return `<button oncl ick="${increment}">Нажато ${count} раз</button>`;
}

Ключевые принципы:

  • useMemo(fn, deps) выполняет функцию fn только при изменении зависимостей deps.
  • useCallback(fn, deps) возвращает мемоизированную функцию, обновляющуюся только при изменении deps.

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

Bliss.js позволяет создавать собственные hooks для повторного использования логики.

Пример:

import { useState, useEffect } from 'blissjs';

function useWindowWidth() {
    const [width, setWidth] = useState(window.innerWidth);

    useEffect(() => {
        const handleResize = () => setWidth(window.innerWidth);
        window.addEventListener('resize', handleResize);
        return () => window.removeEventListener('resize', handleResize);
    }, []);

    return width;
}

function WidthDisplay() {
    const width = useWindowWidth();
    return `<p>Ширина окна: ${width}px</p>`;
}

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

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

Правила использования hooks

  1. Вызывать только на верхнем уровне компонента. Hooks нельзя вызывать внутри условных операторов, циклов или вложенных функций. Это сохраняет предсказуемость порядка вызова.

  2. Использовать только внутри компонентов или других hooks. Нельзя вызывать вне функций компонентов или пользовательских hooks.

  3. Чётко определять зависимости для эффектов и мемоизации. Неправильный список зависимостей может привести к багам и лишним рендерам.

  4. Разделять логику состояния и представления. Hooks позволяют изолировать функциональность, что улучшает читаемость и тестируемость кода.

Взаимодействие hooks с DOM

Bliss.js обеспечивает реактивное обновление DOM при изменении состояния. Основной принцип:

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

Совмещение hooks

Hooks можно комбинировать, создавая сложные компоненты:

import { useState, useEffect, useRef } from 'blissjs';

function ChatBox() {
    const [messages, setMessages] = useState([]);
    const inputRef = useRef(null);

    useEffect(() => {
        const timer = setInterval(() => {
            setMessages(prev => [...prev, `Сообщение ${prev.length + 1}`]);
        }, 3000);
        return () => clearInterval(timer);
    }, []);

    const sendMessage = () => {
        setMessages(prev => [...prev, inputRef.current.value]);
        inputRef.current.value = '';
    };

    return `
        <div>
            <ul>${messages.map(m => `<li>${m}</li>`).join('')}</ul>
            <input ref="${inputRef}"/>
            <button oncl ick="${sendMessage}">Отправить</button>
        </div>
    `;
}

Комбинирование позволяет:

  • Разделять логику состояний, эффектов и ссылок на DOM.
  • Сохранять чистоту компонентов при высокой интерактивности.
  • Повторно использовать пользовательские hooks для разных частей приложения.

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