← Назад к вопросам
Как работает поток данных между Redux и компонентом?
1.8 Middle🔥 192 комментариев
#State Management
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает поток данных между Redux и компонентом
Redux — это библиотека управления состоянием с однонаправленным потоком данных. Понимание этого потока критично для правильного использования Redux.
1. Redux архитектура (однонаправленный поток)
Компонент
|
v (dispatch action)
Action Creator
|
v (returns action object)
Reducer
|
v (returns new state)
Store (состояние)
|
v (subscribe/notify)
Компонент (re-render)
2. Store - источник истины
Store содержит всё состояние приложения:
// Store это один большой объект
const store = {
users: [
{ id: 1, name: 'Иван' },
{ id: 2, name: 'Мария' }
],
ui: {
isLoading: false,
theme: 'dark'
},
auth: {
isLoggedIn: true,
token: 'abc123'
}
};
// Komponenty читают из store через селекторы
const userName = store.users[0].name;
const isLoading = store.ui.isLoading;
3. Actions - описание изменений
Action это простой объект, описывающий что должно измениться:
// Action это объект с type
const addUserAction = {
type: 'ADD_USER',
payload: { name: 'Петр', email: 'petr@example.com' }
};
// Action Creator возвращает Action
const addUser = (name, email) => ({
type: 'ADD_USER',
payload: { name, email }
});
// Или с Redux Toolkit (современный подход)
import { createAction } from '@reduxjs/toolkit';
const addUser = createAction('users/add');
4. Reducers - чистые функции для обновления состояния
Reducer принимает текущее состояние и action, возвращает новое состояние:
// Чистая функция: тот же input => тот же output
const userReducer = (state = [], action) => {
switch (action.type) {
case 'ADD_USER':
// ВАЖНО: не мутируй состояние, создавай новое
return [
...state,
{ id: state.length + 1, ...action.payload }
];
case 'REMOVE_USER':
return state.filter(user => user.id !== action.payload);
case 'UPDATE_USER':
return state.map(user =>
user.id === action.payload.id
? { ...user, ...action.payload }
: user
);
default:
return state;
}
};
// Redux Toolkit (рекомендуется)
import { createSlice } from '@reduxjs/toolkit';
const userSlice = createSlice({
name: 'users',
initialState: [],
reducers: {
addUser: (state, action) => {
// RTK использует Immer, можно мутировать
state.push({ id: state.length + 1, ...action.payload });
},
removeUser: (state, action) => {
return state.filter(user => user.id !== action.payload);
}
}
});
5. Поток данных: Dispatch
Компонент dispatch action, что запускает цепочку:
// 1. Компонент вызывает dispatch
const MyComponent = () => {
const dispatch = useDispatch();
const handleAddUser = () => {
// Отправляем action в reducer
dispatch(addUser('Петр', 'petr@example.com'));
};
return <button onClick={handleAddUser}>Добавить пользователя</button>;
};
// 2. Reducer получает action и обновляет state
// (происходит автоматически)
// 3. Store уведомляет компоненты об изменении
// (происходит автоматически)
// 4. Компонент перерисовывается с новыми данными
6. Поток данных: Select (Selectors)
Компоненты читают данные из Store через селекторы:
// Селектор - функция, которая берет данные из состояния
const selectUsers = (state) => state.users;
const selectUserCount = (state) => state.users.length;
const selectUserById = (state, id) =>
state.users.find(user => user.id === id);
// В компоненте используем useSelector
const MyComponent = () => {
// useSelector подписывает компонент на изменения
const users = useSelector(selectUsers);
const userCount = useSelector(selectUserCount);
// Компонент перерисуется только если users или userCount изменилась
// (shallow comparison)
return (
<div>
<p>Всего пользователей: {userCount}</p>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
};
7. Полный цикл данных
// Инициальное состояние
const initialState = {
users: [{ id: 1, name: 'Иван' }],
ui: { isLoading: false }
};
// 1. Компонент просит данные
const UserList = () => {
// useSelector подписывается на изменения
const users = useSelector(state => state.users);
const isLoading = useSelector(state => state.ui.isLoading);
const dispatch = useDispatch();
useEffect(() => {
// 2. Компонент говорит: "нужны пользователи"
dispatch({ type: 'FETCH_USERS_START' });
// 3. Reducer обновляет isLoading = true
// 4. Store уведомляет компонент
// 5. Компонент перерисуется и покажет spinner
// Затем загружаем данные
fetch('/api/users')
.then(res => res.json())
.then(data => {
// 6. Dispatch action с данными
dispatch({ type: 'FETCH_USERS_SUCCESS', payload: data });
// 7. Reducer обновляет users и isLoading = false
// 8. Store уведомляет компонент
// 9. Компонент перерисуется с новыми данными
})
.catch(error => {
dispatch({ type: 'FETCH_USERS_ERROR', payload: error });
});
}, [dispatch]);
if (isLoading) return <p>Загрузка...</p>;
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
};
8. Middleware (побочные эффекты)
Для асинхронных операций используй middleware:
// Redux Thunk - функция вместо объекта
const fetchUsers = () => async (dispatch) => {
dispatch({ type: 'FETCH_START' });
try {
const response = await fetch('/api/users');
const data = await response.json();
// Теперь можешь dispatch несколько actions
dispatch({ type: 'FETCH_SUCCESS', payload: data });
} catch (error) {
dispatch({ type: 'FETCH_ERROR', payload: error });
}
};
// В компоненте
const MyComponent = () => {
const dispatch = useDispatch();
useEffect(() => {
// Dispatch возвращает promise
dispatch(fetchUsers()).then(() => {
console.log('Done!');
});
}, [dispatch]);
};
// Redux Toolkit Query (рекомендуется для API)
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
const usersApi = createApi({
reducerPath: 'usersApi',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => '/users'
}),
addUser: builder.mutation({
query: (user) => ({
url: '/users',
method: 'POST',
body: user
})
})
})
});
// В компоненте
const { data: users, isLoading } = usersApi.useGetUsersQuery();
const [addUser] = usersApi.useAddUserMutation();
9. Оптимизация: Memoization селекторов
import { createSelector } from '@reduxjs/toolkit';
// Базовый селектор
const selectUsers = (state) => state.users;
const selectUserId = (state, id) => id;
// Мемоизированный селектор - пересчитывается только если users изменилась
const selectUserById = createSelector(
[selectUsers, selectUserId],
(users, id) => users.find(user => user.id === id)
);
// useSelector с мемоизированным селектором не вызывает лишние re-render
const user = useSelector(state => selectUserById(state, userId));
Итоговая схема потока
Компонент
|
| dispatch(action)
v
Store → Reducer → NewState
|
| subscribe: state changed!
v
useSelector
|
| Shallow comparison
v
Перерендер компонента (если данные изменились)
Этот однонаправленный поток делает Redux предсказуемым и легко отлаживаемым. Данные всегда течут в одну сторону: action -> reducer -> store -> component.