Redux с Preact

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

Установка и подключение

Для работы с Redux в Preact необходимы следующие пакеты:

npm install redux preact-redux
  • redux — сама библиотека для управления состоянием.
  • preact-redux — обертка для интеграции Redux с Preact, аналогичная react-redux.

Создаётся основной стор:

import { createStore } from 'redux';

// Изначальное состояние
const initialState = {
    count: 0
};

// Редьюсер
function counterReducer(state = initialState, action) {
    switch (action.type) {
        case 'INCREMENT':
            return { ...state, count: state.count + 1 };
        case 'DECREMENT':
            return { ...state, count: state.count - 1 };
        default:
            return state;
    }
}

// Создание стора
const store = createStore(counterReducer);

export default store;

Провайдер Redux в Preact

Для доступа к стору из компонентов Preact используется Provider из preact-redux:

import { h } from 'preact';
import { Provider } from 'preact-redux';
import store from './store';
import App from './App';

const Root = () => (
    <Provider store={store}>
        <App />
    </Provider>
);

export default Root;

Provider оборачивает корневой компонент приложения и передаёт стор всем дочерним компонентам через контекст.

Соединение компонентов с Redux

Хук useSelector

useSelector позволяет выбирать данные из глобального состояния:

import { h } from 'preact';
import { useSelector } from 'preact-redux';

const CounterDisplay = () => {
    const count = useSelector(state => state.count);
    return <div>Текущее значение: {count}</div>;
};

Хук useDispatch

useDispatch используется для отправки действий в Redux:

import { h } from 'preact';
import { useDispatch } from 'preact-redux';

const CounterControls = () => {
    const dispatch = useDispatch();

    const increment = () => dispatch({ type: 'INCREMENT' });
    const decrement = () => dispatch({ type: 'DECREMENT' });

    return (
        <div>
            <button onCl ick={increment}>Увеличить</button>
            <button onCl ick={decrement}>Уменьшить</button>
        </div>
    );
};

Комбинация useSelector и useDispatch обеспечивает полное взаимодействие компонента с глобальным состоянием, сохраняя при этом декларативный подход Preact.

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

В Preact, как и в React, каждый ререндер может быть дорогим при большом дереве компонентов. Для предотвращения ненужных обновлений используют:

  • useMemo для мемоизации вычислений;
  • useCallback для сохранения функций между рендерами;
  • селекторы через reselect для кэширования выбранных данных.

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

import { createSelector } from 'reselect';

const selectCount = state => state.count;

const SELECTDoubleCount = createSelector(
    [selectCount],
    count => count * 2
);

Компонент затем использует мемоизированный селектор:

const DoubleCounterDisplay = () => {
    const doubleCount = useSelector(SELECTDoubleCount);
    return <div>Удвоенное значение: {doubleCount}</div>;
};

Middleware в Preact Redux

Redux поддерживает middleware для обработки асинхронных действий или логирования. Наиболее популярный — redux-thunk:

npm install redux-thunk

Подключение middleware при создании стора:

import { createStore, applyMiddleware } FROM 'redux';
import thunk FROM 'redux-thunk';
import counterReducer from './reducer';

const store = createStore(
    counterReducer,
    applyMiddleware(thunk)
);

export default store;

Асинхронное действие:

export const incrementAsync = () => (dispatch) => {
    setTimeout(() => {
        dispatch({ type: 'INCREMENT' });
    }, 1000);
};

Использование в компоненте:

const AsyncCounter = () => {
    const dispatch = useDispatch();
    return <button onCl ick={() => dispatch(incrementAsync())}>Увеличить через 1 сек</button>;
};

Связывание компонентов через connect

Помимо хуков, preact-redux поддерживает connect:

import { connect } from 'preact-redux';

const Counter = ({ count, increment }) => (
    <div>
        <div>{count}</div>
        <button onCl ick={increment}>Увеличить</button>
    </div>
);

const mapStateToProps = state => ({
    count: state.count
});

const mapDispatchToProps = dispatch => ({
    increment: () => dispatch({ type: 'INCREMENT' })
});

export default connect(mapStateToProps, mapDispatchToProps)(Counter);

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

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

  • Модуляризация редьюсеров: при больших проектах разделять редьюсеры на функциональные модули и объединять их через combineReducers.
  • Типизация действий: для предотвращения ошибок лучше определять константы для типов действий.
  • Использование селекторов: для изоляции логики выбора данных из глобального состояния.
  • Лёгкость компонентов: избегать тяжёлых вычислений в рендерах, выносить их в селекторы или хуки.

Эта структура обеспечивает стабильное и масштабируемое приложение на Preact с Redux, сохраняя высокую производительность и предсказуемость состояния.