Flux — это архитектурный паттерн для построения клиентских приложений с предсказуемым управлением состоянием. Он отличается однонаправленным потоком данных, что упрощает отладку и поддержку крупных приложений. Основные компоненты Flux:
Главная идея — данные всегда текут в одном направлении: Action → Dispatcher → Store → View.
Actions представляют собой объекты с типом и полезной
нагрузкой (type и payload). В Preact
можно создавать Actions через простые функции:
export const addItem = (item) => ({
type: 'ADD_ITEM',
payload: item
});
Использование функций для создания Actions упрощает их тестирование и поддержку. Actions не изменяют состояние напрямую, они только описывают, что должно произойти.
Dispatcher является центральной шиной, на которую подписаны все Stores. Его задача — получать Actions и уведомлять Stores. В Preact часто используется легковесная реализация:
class Dispatcher {
constructor() {
this.callbacks = [];
}
register(callback) {
this.callbacks.push(callback);
}
dispatch(action) {
this.callbacks.forEach(cb => cb(action));
}
}
export const dispatcher = new Dispatcher();
Каждый Store регистрирует в Dispatcher свой колбэк, который будет вызываться при каждом Action. Это обеспечивает централизованное управление потоком данных.
Stores содержат состояние приложения и логику его изменения. Они слушают Actions через Dispatcher и уведомляют компоненты о необходимости обновления.
Пример Store в Preact:
import { dispatcher } from './dispatcher';
class TodoStore {
constructor() {
this.todos = [];
this.listeners = [];
dispatcher.register(this.handleAction.bind(this));
}
handleAction(action) {
switch(action.type) {
case 'ADD_ITEM':
this.todos.push(action.payload);
this.emitChange();
break;
case 'REMOVE_ITEM':
this.todos = this.todos.filter(todo => todo.id !== action.payload.id);
this.emitChange();
break;
}
}
emitChange() {
this.listeners.forEach(listener => listener());
}
subscribe(listener) {
this.listeners.push(listener);
}
getTodos() {
return this.todos;
}
}
export const todoStore = new TodoStore();
В этом примере каждый Store полностью контролирует своё состояние, а компоненты лишь подписываются на изменения.
В Preact компоненты получают данные из Stores через подписку и обновляются при изменении состояния. Пример компонента TodoList:
import { h } from 'preact';
import { useEffect, useState } from 'preact/hooks';
import { todoStore } from './todoStore';
export const TodoList = () => {
const [todos, setTodos] = useState(todoStore.getTodos());
useEffect(() => {
const update = () => setTodos(todoStore.getTodos());
todoStore.subscribe(update);
}, []);
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
};
Ключевое преимущество — компонент не знает о логике изменения данных, он просто реагирует на обновления Store.
Для упрощения работы с Actions часто используют Action Creators — функции, которые создают и диспатчат Actions:
import { dispatcher } from './dispatcher';
import { addItem } from './actions';
export const addItemAction = (item) => {
dispatcher.dispatch(addItem(item));
};
Это позволяет разделять создание и отправку Actions, повышая читаемость кода.
Существуют облегчённые библиотеки для Flux-подхода:
Использование этих библиотек позволяет сократить шаблонный код и внедрить дополнительные возможности, такие как middleware, асинхронные действия и DevTools.
Основное преимущество Flux — предсказуемость. Поскольку данные всегда движутся по одному пути, легко:
Даже при увеличении размера приложения структура остаётся ясной: Action инициирует изменение → Store обрабатывает → View обновляется.
Использование хуков позволяет сделать Flux-компоненты более декларативными и компактными. Пример с использованием кастомного хука:
import { useState, useEffect } from 'preact/hooks';
import { todoStore } from './todoStore';
export const useTodos = () => {
const [todos, setTodos] = useState(todoStore.getTodos());
useEffect(() => {
const update = () => setTodos(todoStore.getTodos());
todoStore.subscribe(update);
}, []);
return todos;
};
Теперь компоненты могут получать состояние просто через:
const todos = useTodos();
Это делает интеграцию Flux с Preact максимально удобной и идиоматичной.
Хотите, я могу дополнительно сделать схему полного потока данных Flux в Preact с Dispatcher, Stores и компонентами, чтобы визуально закрепить структуру?