← Назад к вопросам
Как между собой связаны сущности в Redux?
2.2 Middle🔥 201 комментариев
#State Management
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Связь сущностей в Redux
Redux построен на четких архитектурных принципах, где каждая сущность играет определенную роль в управлении состоянием приложения. Эти сущности тесно взаимосвязаны и образуют единый цикл обновления состояния.
Основные сущности Redux
┌─────────────────────────────────────────────────────┐
│ Component (Компонент React) │
│ - использует useSelector для чтения состояния │
│ - использует useDispatch для отправки экшенов │
└────────────┬──────────────────────┬────────────────┘
│ │
select dispatch(action)
│ │
v v
┌──────────────┐ ┌──────────────┐
│ Selector │ │ Action │
│ (селектор) │ │ (экшен) │
└──────────────┘ └──────────────┘
^ │
│ │
читает из описывает
store.state what
│ │
│ v
┌────────────────────────────────────┐
│ Store (хранилище) │
│ - содержит всё состояние приложения│
└────────────────────────────────────┘
^
│
обновляется
│
└─────────────────────┐
│
┌──────v──────┐
│ Reducer │
│ (редьюсер) │
└──────^──────┘
│
(state, action)
Детальная связь сущностей
1. Store и Reducer
import { createStore } from 'redux';
// Reducer - функция, которая описывает как изменяется состояние
function todoReducer(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{ id: action.payload.id, text: action.payload.text }
];
case 'REMOVE_TODO':
return state.filter(todo => todo.id !== action.payload);
default:
return state;
}
}
// Store - корневой объект, который содержит состояние
const store = createStore(todoReducer);
// Store имеет методы: getState(), dispatch(), subscribe()
const state = store.getState();
2. Action и Reducer
// Action - простой объект, описывающий что произошло
const action = {
type: 'ADD_TODO',
payload: { id: 1, text: 'Learn Redux' }
};
// Reducer принимает действие и обновляет состояние
function reducer(state = { todos: [], filter: 'all' }, action) {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, action.payload]
};
case 'SET_FILTER':
return {
...state,
filter: action.payload
};
default:
return state;
}
}
3. Action Creator
// Action Creator - функция, которая создаёт actions
function addTodo(id, text) {
return {
type: 'ADD_TODO',
payload: { id, text }
};
}
function removeTodo(id) {
return {
type: 'REMOVE_TODO',
payload: id
};
}
// Использование
const action1 = addTodo(1, 'Buy milk');
const action2 = removeTodo(1);
store.dispatch(action1);
store.dispatch(action2);
4. Selector и Store
// Selector - функция, которая извлекает данные из состояния
const getTodos = (state) => state.todos;
const getFilter = (state) => state.filter;
// Derived selector - комбинирует несколько селекторов
const getFilteredTodos = (state) => {
const todos = getTodos(state);
const filter = getFilter(state);
switch (filter) {
case 'completed':
return todos.filter(t => t.completed);
case 'active':
return todos.filter(t => !t.completed);
default:
return todos;
}
};
const todos = getTodos(store.getState());
const filtered = getFilteredTodos(store.getState());
Полный пример с Redux в React
import { createStore } from 'redux';
import { useSelector, useDispatch } from 'react-redux';
// 1. Определяем initial state
const initialState = {
todos: [],
loading: false,
error: null
};
// 2. Создаём reducer
function todoReducer(state = initialState, action) {
switch (action.type) {
case 'FETCH_TODOS_START':
return { ...state, loading: true, error: null };
case 'FETCH_TODOS_SUCCESS':
return {
...state,
todos: action.payload,
loading: false
};
case 'FETCH_TODOS_ERROR':
return {
...state,
loading: false,
error: action.payload
};
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, action.payload]
};
default:
return state;
}
}
// 3. Создаём store
const store = createStore(todoReducer);
// 4. Создаём селекторы
const selectTodos = (state) => state.todos;
const selectLoading = (state) => state.loading;
const selectError = (state) => state.error;
// 5. Используем в компоненте
function TodoList() {
// useSelector связывает компонент с store и selector
const todos = useSelector(selectTodos);
const loading = useSelector(selectLoading);
const error = useSelector(selectError);
// useDispatch позволяет отправлять actions
const dispatch = useDispatch();
const handleAddTodo = () => {
dispatch({
type: 'ADD_TODO',
payload: { id: Date.now(), text: 'New todo' }
});
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<div>
<button onClick={handleAddTodo}>Add Todo</button>
<ul>
{todos.map(todo => <li key={todo.id}>{todo.text}</li>)}
</ul>
</div>
);
}
Middleware и асинхронные операции
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
// Middleware thunk позволяет отправлять функции вместо actions
const fetchTodos = () => (dispatch) => {
dispatch({ type: 'FETCH_START' });
fetch('https://api.example.com/todos')
.then(res => res.json())
.then(data => {
dispatch({
type: 'FETCH_SUCCESS',
payload: data
});
})
.catch(error => {
dispatch({
type: 'FETCH_ERROR',
payload: error.message
});
});
};
const store = createStore(todoReducer, applyMiddleware(thunk));
// Теперь можем использовать:
dispatch(fetchTodos());
Redux DevTools integration
import { createStore, compose } from 'redux';
const composeEnhancers =
typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
: compose;
const store = createStore(
todoReducer,
composeEnhancers(applyMiddleware(thunk))
);
// Теперь можешь смотреть весь history actions в DevTools
Нормализованное состояние (для сложных приложений)
// Плохо - дублирование и сложность обновления
const state = {
users: [
{ id: 1, name: 'John', posts: [
{ id: 1, title: 'Post 1' },
{ id: 2, title: 'Post 2' }
]}
]
};
// Хорошо - нормализованная форма
const state = {
entities: {
users: {
1: { id: 1, name: 'John', postIds: [1, 2] },
2: { id: 2, name: 'Jane', postIds: [3] }
},
posts: {
1: { id: 1, title: 'Post 1', userId: 1 },
2: { id: 2, title: 'Post 2', userId: 1 },
3: { id: 3, title: 'Post 3', userId: 2 }
}
}
};
// Селектор для получения полных данных пользователя
const getUserWithPosts = (state, userId) => {
const user = state.entities.users[userId];
const posts = user.postIds.map(id => state.entities.posts[id]);
return { ...user, posts };
};
Связь всех компонентов в цикл
1. Пользователь кликает на кнопку в Component
↓
2. Component вызывает dispatch(action) через useDispatch
↓
3. Action отправляется в Store
↓
4. Store передаёт (state, action) в Reducer
↓
5. Reducer вычисляет новое состояние
↓
6. Store обновляется с новым состоянием
↓
7. Store уведомляет все подписчиков (через subscribe)
↓
8. useSelector в Component получает новое значение
↓
9. Component перерендеривается с новыми данными
Заключение
В Redux все сущности тесно связаны: Component диспатчит Actions, которые Reducer обрабатывает и обновляет Store, Selectors извлекают данные из Store, и Component переподписывается на изменения через useSelector. Это создает однонаправленный поток данных, который делает состояние предсказуемым и легким в отладке. Правильное понимание этих связей критично для масштабируемой архитектуры приложения.