Во что пакетируется код в Redhub-подобных архитектурах
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Упаковка кода в Redux-подобных архитектурах
В Redux-подобных архитектурах (включая собственно Redux, Vuex, MobX, NgRx и другие реализации паттерна Flux) код организуется вокруг нескольких ключевых концепций, которые "пакетируются" определенным образом для обеспечения предсказуемости состояния приложения и одностороннего потока данных.
Основные архитектурные единицы
1. Store (Хранилище)
Централизованное хранилище состояния всего приложения. В Redux это всегда один объект, содержащий все состояние.
// Пример создания store в Redux
import { createStore } from 'redux';
import rootReducer from './reducers';
const store = createStore(
rootReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
2. Actions (Действия)
Объекты, которые описывают, что должно произойти в приложении. Это единственный способ изменить состояние.
// Action types
const ADD_TODO = 'ADD_TODO';
const TOGGLE_TODO = 'TOGGLE_TODO';
// Action creators
export const addTodo = (text) => ({
type: ADD_TODO,
payload: { text, id: Date.now(), completed: false }
});
export const toggleTodo = (id) => ({
type: TOGGLE_TODO,
payload: { id }
});
3. Reducers (Редюсеры)
Чистые функции, которые принимают предыдущее состояние и action, возвращая новое состояние.
// Reducer
const todosReducer = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return [...state, action.payload];
case TOGGLE_TODO:
return state.map(todo =>
todo.id === action.payload.id
? { ...todo, completed: !todo.completed }
: todo
);
default:
return state;
}
};
4. Middleware (Промежуточное ПО)
Функции, которые обрабатывают actions до того, как они достигнут редюсера.
// Пример middleware для логирования
const loggerMiddleware = store => next => action => {
console.log('Dispatching:', action);
const result = next(action);
console.log('Next state:', store.getState());
return result;
};
Организация кода на практике
По типам функциональности (Classic Redux)
src/
├── actions/
│ ├── todoActions.js
│ └── userActions.js
├── reducers/
│ ├── todoReducer.js
│ ├── userReducer.js
│ └── rootReducer.js
├── store/
│ └── configureStore.js
└── constants/
└── actionTypes.js
По предметным областям (Ducks/Redux Toolkit)
Современный подход, где связанные действия, редюсеры и типы группируются вместе:
// features/todos/todosSlice.js (Redux Toolkit)
import { createSlice } from '@reduxjs/toolkit';
const todosSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
addTodo: (state, action) => {
state.push(action.payload);
},
toggleTodo: (state, action) => {
const todo = state.find(t => t.id === action.payload);
if (todo) {
todo.completed = !todo.completed;
}
}
}
});
export const { addTodo, toggleTodo } = todosSlice.actions;
export default todosSlice.reducer;
Ключевые принципы упаковки
- Единственный источник истины - Все состояние приложения хранится в одном store
- State доступен только для чтения - Изменять состояние можно только через actions
- Изменения через чистые функции - Редюсеры должны быть чистыми функциями без side effects
- Односторонний поток данных - Data flow: View → Action → Reducer → Store → View
Современные тенденции
В современных реализациях, особенно с Redux Toolkit, код упаковывается более компактно:
- Slices - Автоматическая генерация actions и reducers
- RTK Query - Встроенное решение для работы с API
- EntityAdapter - Стандартизированная работа с нормализованными данными
// Modern Redux с RTK
import { configureStore, createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: state => { state.value += 1; },
decrement: state => { state.value -= 1; }
}
});
export const store = configureStore({
reducer: {
counter: counterSlice.reducer
}
});
Такой подход к упаковке кода обеспечивает масштабируемость, тестируемость и поддерживаемость крупных приложений, при этом сохраняя предсказуемость работы с состоянием.