Flux Pattern

Flux — архитектурный паттерн управления состоянием, предложенный Facebook, основанный на однонаправленном потоке данных: Action → Dispatcher → Store → View.

Зачем нужно

До Flux в крупных приложениях State Management был хаотичным: данные текли в обоих направлениях, компоненты напрямую вызывали методы друг друга, отследить источник изменений было сложно. Flux ввёл строгое правило: данные текут только в одном направлении. Это сделало изменения предсказуемыми и легко отлаживаемыми. Redux — самая известная реализация Flux-идей.

Где используется

  • Redux — самая популярная реализация, де-факто стандарт для сложного state management в React
  • Vuex (Vue 2) / Pinia (Vue 3) — Flux-вдохновлённые решения для Vue
  • NgRx — Flux/Redux для Angular
  • Zustand — упрощённая реализация без строгого Flux, но с похожей идеей

Однонаправленный поток данных

┌──────────┐    ┌────────────┐    ┌───────┐    ┌──────┐
│  Action  │───►│ Dispatcher │───►│ Store │───►│ View │
│(действие)│    │(диспетчер) │    │       │    │      │
└──────────┘    └────────────┘    └───────┘    └──────┘
                                                   │
                              Пользователь нажал   │
                              кнопку → создаётся   │
                              новый Action ────────┘

Элементы паттерна

// ACTION — описывает что произошло (объект)
const addToCartAction = {
  type: 'CART/ADD_ITEM',
  payload: {
    productId: '42',
    name: 'Кофе',
    price: 350,
    quantity: 1,
  },
};

// STORE — хранит состояние, реагирует на actions
// (В оригинальном Flux через Dispatcher, в Redux через reducer)
function cartReducer(state = { items:  }, action) {
  switch (action.type) {
    case 'CART/ADD_ITEM':
      const existing = state.items.find(i => i.productId === action.payload.productId);
      if (existing) {
        return {
          ...state,
          items: state.items.map(item =>
            item.productId === action.payload.productId
              ? { ...item, quantity: item.quantity + action.payload.quantity }
              : item
          ),
        };
      }
      return { ...state, items: [...state.items, action.payload] };

    case 'CART/REMOVE_ITEM':
      return {
        ...state,
        items: state.items.filter(i => i.productId !== action.payload),
      };

    default:
      return state;
  }
}

// DISPATCHER — уведомляет все store об action
// В Redux это store.dispatch(action)

// VIEW — React-компонент читает state из Store и диспатчит actions

Flux без Redux (ванильная реализация)

// Простейший Store с EventEmitter
class CartStore extends EventEmitter {
  #state = { items:  };

  dispatch(action) {
    this.#state = cartReducer(this.#state, action);
    this.emit('change'); // уведомляем подписчиков
  }

  getState {
    return this.#state;
  }
}

const store = new CartStore();

// View подписывается на изменения
store.on('change', () => {
  renderCart(store.getState);
});

// Диспатч action при клике
document.getElementById('add-btn').addEventListener('click', () => {
  store.dispatch({
    type: 'CART/ADD_ITEM',
    payload: { productId: '1', name: 'Кофе', price: 350, quantity: 1 },
  });
});

Частые ошибки

  • Flux для простого состояния — если компонент имеет 3 поля формы, Flux/Redux избыточен; используйте useState.
  • Слишком мелкие actionsSET_LOADING_TRUE, SET_LOADING_FALSE вместо FETCH_USERS_REQUEST / FETCH_USERS_SUCCESS / FETCH_USERS_FAILURE.
  • Мутация state в reducer — reducer должен возвращать новый объект, а не мутировать state.items.push(...).

Связанные темы

Ресурсы