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

Как сделан Redux?

2.0 Middle🔥 181 комментариев
#State Management

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Архитектура Redux: как это работает

Redux - это предсказуемый контейнер состояния для приложений JavaScript. Рассмотрим его архитектуру и внутреннее устройство.

1. Три ключевых концепции Redux

Redux строится на трёх основных идеях:

// 1. STORE - единое хранилище всего состояния приложения
const store = createStore(reducer);

// 2. ACTION - объект, описывающий, что произошло
const action = {
  type: "INCREMENT",
  payload: 5
};

// 3. REDUCER - чистая функция, которая преобразует состояние
const reducer = (state = 0, action) => {
  if (action.type === "INCREMENT") {
    return state + action.payload;
  }
  return state;
};

2. Упрощённая реализация Redux

Вот как примерно работает Redux на самом низком уровне:

// Минимальная реализация Redux
function createStore(reducer) {
  let state;
  let listeners = [];

  // Получить текущее состояние
  function getState() {
    return state;
  }

  // Отправить действие
  function dispatch(action) {
    // Вызвать reducer с текущим состоянием и действием
    state = reducer(state, action);
    // Уведомить всех слушателей об изменении
    listeners.forEach((listener) => listener());
  }

  // Подписаться на изменения
  function subscribe(listener) {
    listeners.push(listener);
    // Возвращаем функцию для отписки
    return () => {
      listeners = listeners.filter((l) => l !== listener);
    };
  }

  // Инициализируем состояние
  dispatch({ type: "@@INIT" });

  return { getState, dispatch, subscribe };
}

// Использование
const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case "INCREMENT":
      return state + 1;
    case "DECREMENT":
      return state - 1;
    default:
      return state;
  }
};

const store = createStore(counterReducer);

store.subscribe(() => {
  console.log("Состояние изменилось:", store.getState());
});

store.dispatch({ type: "INCREMENT" }); // Выведет 1
store.dispatch({ type: "INCREMENT" }); // Выведет 2
store.dispatch({ type: "DECREMENT" }); // Выведет 1

3. Reducer и его правила

Reducer - это чистая функция, которая следует определённым правилам:

// Правильный reducer (чистая функция)
const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case "INCREMENT":
      // Возвращаем новое значение
      return state + 1;
    case "DECREMENT":
      return state - 1;
    default:
      return state;
  }
};

// Неправильный reducer (побочные эффекты)
const wrongReducer = (state = 0, action) => {
  // НЕПРАВИЛЬНО: мутирует состояние
  state.count++;
  return state;

  // НЕПРАВИЛЬНО: асинхронные операции
  fetch("/api/data").then((res) => res.json());

  // НЕПРАВИЛЬНО: вызов функций с побочными эффектами
  console.log("Действие:", action);
};

// Правильный способ обновления объектов
const userReducer = (state = {}, action) => {
  switch (action.type) {
    case "SET_NAME":
      // Создаём новый объект, не мутируя старый
      return { ...state, name: action.payload };
    case "SET_AGE":
      return { ...state, age: action.payload };
    default:
      return state;
  }
};

4. Middleware в Redux

Middleware позволяет расширить функциональность dispatch:

// Структура middleware
const middleware = (store) => (next) => (action) => {
  console.log("Действие:", action);
  const result = next(action); // Передаём действие дальше
  console.log("Новое состояние:", store.getState());
  return result;
};

// Примеры middleware
const loggerMiddleware = (store) => (next) => (action) => {
  console.log("Перед:", store.getState());
  const result = next(action);
  console.log("После:", store.getState());
  return result;
};

const crashReporterMiddleware = (store) => (next) => (action) => {
  try {
    return next(action);
  } catch (error) {
    console.error("Ошибка:", error);
    throw error;
  }
};

// Применение middleware
function applyMiddleware(...middlewares) {
  return (createStore) => {
    return (reducer, preloadedState) => {
      const store = createStore(reducer, preloadedState);
      let dispatch = store.dispatch;

      const middlewareAPI = {
        getState: store.getState,
        dispatch: (action) => dispatch(action)
      };

      const chain = middlewares.map((m) => m(middlewareAPI));
      dispatch = chain.reduce(
        (composed, m) => (action) => m(composed(action)),
        store.dispatch
      );

      return { ...store, dispatch };
    };
  };
}

5. Combineducers - объединение reducers

const counterReducer = (state = 0, action) => {
  if (action.type === "INCREMENT") return state + 1;
  return state;
};

const userReducer = (state = { name: "" }, action) => {
  if (action.type === "SET_NAME") {
    return { ...state, name: action.payload };
  }
  return state;
};

// Упрощённая реализация combineReducers
function combineReducers(reducers) {
  return (state = {}, action) => {
    const newState = {};
    for (const key in reducers) {
      newState[key] = reducers[key](state[key], action);
    }
    return newState;
  };
}

const rootReducer = combineReducers({
  counter: counterReducer,
  user: userReducer
});

const store = createStore(rootReducer);
// store.getState() => { counter: 0, user: { name: "" } }

6. Селекторы для получения данных

// Селектор - функция для получения части состояния
const selectCounter = (state) => state.counter;
const selectUserName = (state) => state.user.name;

// Мемоизированный селектор (для оптимизации)
const selectVisibleTodos = (() => {
  let lastState = null;
  let lastResult = null;

  return (state) => {
    if (lastState !== state.todos) {
      lastState = state.todos;
      lastResult = state.todos.filter((t) => !t.completed);
    }
    return lastResult;
  };
})();

7. Данные flow в Redux

// Цикл Redux
// 1. Компонент диспатчит действие
dispatch({ type: "INCREMENT" });

// 2. Действие проходит через middleware
// (если оно есть)

// 3. Reducer получает действие и текущее состояние
const newState = reducer(state, action);

// 4. Store обновляется новым состоянием
state = newState;

// 5. Уведомляются все подписчики
listeners.forEach((listener) => listener());

// 6. Компонент получает новое состояние и перерисовывается
const component = (state) => <div>{state.counter}</div>;

8. Redux Devtools Integration

const store = createStore(
  rootReducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ &&
    window.__REDUX_DEVTOOLS_EXTENSION__()
);

// Это позволяет использовать Redux DevTools для:
// - Time-travel debugging
// - Просмотра истории действий
// - Воспроизведения действий
// - Экспорта/импорта состояния

9. Сравнение с Flux

// Redux: одно хранилище, чистые reducers
const store = createStore(reducer);

// Flux: множество хранилищ, вызов методов напрямую
class Store {
  update(action) {
    // Логика изменения состояния
  }
}

10. Ключевые преимущества архитектуры Redux

  • Предсказуемость: чистые функции и одно состояние
  • Отладка: time-travel, логирование действий
  • Тестируемость: reducers - это просто функции
  • Масштабируемость: хорошо для больших приложений
  • Экосистема: множество middleware и инструментов
  • DevTools: мощные средства для разработки

Мой практический опыт: Redux иногда кажется verbose для простых приложений, но его архитектура становится преимуществом на больших проектах. Главное понять, что это функциональное программирование в действии - чистые функции, неизменяемость данных и единая точка истины для состояния.

Как сделан Redux? | PrepBro