Глобальное состояние в приложениях на Riot.js играет ключевую роль при организации взаимодействия компонентов, обмена данными и обеспечения предсказуемого поведения интерфейса. Riot.js предоставляет гибкие инструменты для управления состоянием, позволяя реализовать как простые централизованные хранилища, так и более сложные архитектуры по паттерну Flux.
Централизованное хранилище представляет собой объект, который хранит все данные приложения и предоставляет методы для их чтения и изменения. Основные принципы работы:
Единый источник правды Все данные, которые должны быть доступны нескольким компонентам, хранятся в одном объекте. Это упрощает отладку и предотвращает рассинхронизацию состояний.
Наблюдаемое состояние Riot.js позволяет реализовать реактивное хранилище с помощью событий. Изменения в store могут автоматически оповещать подписанные компоненты.
Пример реализации простого store:
const store = {
state: {
user: null,
todos: []
},
listeners: [],
subscribe(listener) {
this.listeners.push(listener);
},
notify() {
this.listeners.forEach(listener => listener(this.state));
},
setState(newState) {
this.state = { ...this.state, ...newState };
this.notify();
}
};
Компонент может подписываться на изменения:
import { component } from 'riot';
component('todo-list', {
onMounted() {
store.subscribe(state => {
this.update({ todos: state.todos });
});
}
});
Riot.js легко интегрируется с паттерном Flux. Основные элементы:
Пример Action и Dispatcher:
const Dispatcher = {
callbacks: [],
register(callback) {
this.callbacks.push(callback);
},
dispatch(action) {
this.callbacks.forEach(cb => cb(action));
}
};
const Actions = {
addTodo(text) {
Dispatcher.dispatch({ type: 'ADD_TODO', payload: text });
},
removeTodo(id) {
Dispatcher.dispatch({ type: 'REMOVE_TODO', payload: id });
}
};
Store с обработкой действий:
const TodoStore = {
todos: [],
listeners: [],
subscribe(listener) {
this.listeners.push(listener);
},
notify() {
this.listeners.forEach(listener => listener(this.todos));
}
};
Dispatcher.register(action => {
switch(action.type) {
case 'ADD_TODO':
TodoStore.todos.push({ id: Date.now(), text: action.payload });
TodoStore.notify();
break;
case 'REMOVE_TODO':
TodoStore.todos = TodoStore.todos.filter(todo => todo.id !== action.payload);
TodoStore.notify();
break;
}
});
Для небольших приложений можно использовать реактивные переменные без
полноценного store. Riot.js поддерживает синтаксис
observable:
import { observable } from 'riot';
const state = observable({
count: 0
});
state.on('update', () => console.log('State changed', state.count));
state.count++; // Автоматически вызовет событие 'update'
Компоненты могут подписываться на эти изменения и обновлять своё представление автоматически.
Для крупных приложений важно разделять глобальное состояние на модули, чтобы:
Пример модуля:
const userModule = observable({
user: null,
setUser(newUser) {
this.user = newUser;
this.trigger('change', this.user);
}
});
const todoModule = observable({
todos: [],
add(todo) {
this.todos.push(todo);
this.trigger('change', this.todos);
}
});
Компоненты могут подписываться на отдельные модули, не затрагивая другие:
userModule.on('change', user => {
console.log('Пользователь изменился', user);
});
Грамотно спроектированное глобальное состояние повышает предсказуемость поведения приложения, упрощает тестирование и облегчает масштабирование интерфейса.