Хуки и функциональные паттерны

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

useState — базовый хук для управления состоянием компонента. Его синтаксис:

import { useState } from 'inferno-hooks';

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

    return (
        <button onCl ick={() => setCount(count + 1)}>
            {count}
        </button>
    );
}

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

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

import { useEffect } from 'inferno-hooks';

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

    useEffect(() => {
        const interval = setInterval(() => setSeconds(s => s + 1), 1000);
        return () => clearInterval(interval);
    }, []);
    
    return <div>{seconds}</div>;
}

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

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

import { useRef } from 'inferno-hooks';

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

    const focus = () => {
        if(inputRef.current) inputRef.current.focus();
    }

    return (
        <div>
            <input ref={inputRef} />
            <button onCl ick={focus}>Focus</button>
        </div>
    );
}

useRef часто применяют для доступа к DOM-элементам или сохранения каких-либо значений, которые не требуют рендеринга при изменении.

Функциональные паттерны компонентов

Композиция хуков

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

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

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

    return loading ? <div>Loading...</div> : <div>{JSON.stringify(data)}</div>;
}

Разделение состояния и побочных эффектов на отдельные хуки упрощает отладку и тестирование компонентов.

Кастомные хуки

Кастомные хуки позволяют инкапсулировать повторяющуюся логику и использовать её в нескольких компонентах:

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 WindowSizeDisplay() {
    const width = useWindowWidth();
    return <div>Window width: {width}px</div>;
}

Ключевое преимущество кастомных хуков — возможность создавать переиспользуемые абстракции без изменения структуры компонентов.

Работа с контекстом через хуки

Inferno поддерживает контекст для передачи данных между компонентами без явного пробрасывания через props. Хук useContext позволяет функциональному компоненту потреблять контекст:

import { createContext } from 'inferno';
import { useContext } from 'inferno-hooks';

const ThemeContext = createContext('light');

function ThemedButton() {
    const theme = useContext(ThemeContext);
    return <button className={theme}>Button</button>;
}

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

Паттерн эффектов с очисткой

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

useEffect(() => {
    const timer = setTimeout(() => console.log('Tick'), 1000);
    return () => clearTimeout(timer);
}, []);

Это предотвращает утечки памяти и ошибки при взаимодействии с асинхронными процессами.

Оптимизация рендеринга

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

import { useMemo } from 'inferno-hooks';

const sortedItems = useMemo(() => items.sort(), [items]);

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

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

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

function Form() {
    const [values, setValues] = useState({ name: '', email: '' });
    const [dirtyCount, setDirtyCount] = useState(0);

    useEffect(() => {
        setDirtyCount(dirtyCount + 1);
    }, [values]);

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

    return (
        <form>
            <input name="name" value={values.name} onCha nge={handleChange} />
            <input name="email" value={values.email} onCha nge={handleChange} />
            <div>Changes: {dirtyCount}</div>
        </form>
    );
}

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

Хуки и функциональные паттерны в Inferno формируют основу современного функционального программирования в UI-разработке, делая компоненты легкими, гибкими и легко тестируемыми.