← Назад к вопросам

Как между собой связаны сущности в 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. Это создает однонаправленный поток данных, который делает состояние предсказуемым и легким в отладке. Правильное понимание этих связей критично для масштабируемой архитектуры приложения.