← React/Redux: основы и концепции#301 из 383← ПредыдущийСледующий →+25 XP
Полезно по теме:Гайд: React или VueПрактика: React setТермин: React HooksТема: React: хуки и экосистема

Redux: основы и концепции

Зачем нужен Redux

Redux решает проблему управления состоянием в больших приложениях:

  • Единый источник правды — всё состояние хранится в одном месте
  • Предсказуемость — состояние изменяется только через экшены
  • Отладка — легко отслеживать что и когда изменилось (Redux DevTools)
  • Тестируемость — чистые функции легко тестировать
  • Три принципа Redux

    1. Single Source of Truth

    Всё состояние приложения хранится в одном объекте — store:

    const state = {
      user: { name: 'Алексей', role: 'admin' },
      todos: [{ id: 1, text: 'Изучить Redux', done: false }],
      ui: { theme: 'dark', sidebarOpen: true }
    }

    2. State is Read-Only

    Единственный способ изменить состояние — отправить action (экшен):

    // Action — простой объект с полем type
    const action = {
      type: 'todos/add',
      payload: { id: 2, text: 'Практика Redux', done: false }
    }
    
    // Отправляем экшен в store
    store.dispatch(action)

    3. Changes are Made with Pure Functions

    Изменения обрабатываются чистыми функциями — reducers (редьюсеры):

    function todosReducer(state = [], action) {
      switch (action.type) {
        case 'todos/add':
          return [...state, action.payload]
        case 'todos/toggle':
          return state.map(todo =>
            todo.id === action.payload
              ? { ...todo, done: !todo.done }
              : todo
          )
        default:
          return state
      }
    }

    Поток данных в Redux

    UI → dispatch(action) → Reducer → New State → UI обновляется

    1. Пользователь кликает кнопку

    2. Компонент вызывает dispatch({ type: 'counter/increment' })

    3. Redux передаёт экшен в редьюсер

    4. Редьюсер возвращает новое состояние

    5. Store уведомляет подписчиков

    6. React перерисовывает компоненты с новыми данными

    Создание Store (классический Redux)

    import { createStore } from 'redux'
    
    // Начальное состояние
    const initialState = { count: 0 }
    
    // Редьюсер
    function counterReducer(state = initialState, action) {
      switch (action.type) {
        case 'INCREMENT':
          return { count: state.count + 1 }
        case 'DECREMENT':
          return { count: state.count - 1 }
        case 'INCREMENT_BY':
          return { count: state.count + action.payload }
        default:
          return state
      }
    }
    
    // Создаём store
    const store = createStore(counterReducer)
    
    // Подписываемся на изменения
    store.subscribe(() => {
      console.log('State:', store.getState())
    })
    
    // Отправляем экшены
    store.dispatch({ type: 'INCREMENT' })        // { count: 1 }
    store.dispatch({ type: 'INCREMENT_BY', payload: 5 }) // { count: 6 }

    Проблема классического Redux

    Много шаблонного кода:

  • Action types как константы
  • Action creators как функции
  • Switch/case в редьюсерах
  • Иммутабельные обновления вручную
  • Redux Toolkit решает эти проблемы — следующий урок.

    Когда использовать Redux

    Нужен Redux если:

  • Много компонентов используют одни данные
  • Состояние часто обновляется
  • Сложная логика обновлений
  • Нужна история изменений (undo/redo)
  • Большая команда разработчиков
  • Не нужен Redux если:

  • Маленькое приложение
  • Состояние локальное для компонентов
  • Простая логика
  • Примеры

    Принцип работы Redux: store, dispatch, subscribe

    // Упрощённая реализация Redux для понимания концепции
    
    function createStore(reducer) {
      let state = reducer(undefined, { type: '@@INIT' })
      const listeners = []
    
      return {
        getState() {
          return state
        },
        dispatch(action) {
          console.log('Dispatching:', action.type)
          state = reducer(state, action)
          listeners.forEach(listener => listener())
          return action
        },
        subscribe(listener) {
          listeners.push(listener)
          return () => {
            const index = listeners.indexOf(listener)
            listeners.splice(index, 1)
          }
        }
      }
    }
    
    // Редьюсер для списка задач
    function todosReducer(state = [], action) {
      switch (action.type) {
        case 'ADD_TODO':
          return [...state, { id: Date.now(), text: action.payload, done: false }]
        case 'TOGGLE_TODO':
          return state.map(todo =>
            todo.id === action.payload ? { ...todo, done: !todo.done } : todo
          )
        case 'REMOVE_TODO':
          return state.filter(todo => todo.id !== action.payload)
        default:
          return state
      }
    }
    
    const store = createStore(todosReducer)
    
    // Подписываемся на изменения
    store.subscribe(() => {
      console.log('Todos:', store.getState())
    })
    
    // Dispatch экшенов
    store.dispatch({ type: 'ADD_TODO', payload: 'Изучить Redux' })
    store.dispatch({ type: 'ADD_TODO', payload: 'Практика Redux Toolkit' })
    
    const todos = store.getState()
    store.dispatch({ type: 'TOGGLE_TODO', payload: todos[0].id })
    
    console.log('Финальное состояние:', store.getState())

    Redux: основы и концепции

    Зачем нужен Redux

    Redux решает проблему управления состоянием в больших приложениях:

  • Единый источник правды — всё состояние хранится в одном месте
  • Предсказуемость — состояние изменяется только через экшены
  • Отладка — легко отслеживать что и когда изменилось (Redux DevTools)
  • Тестируемость — чистые функции легко тестировать
  • Три принципа Redux

    1. Single Source of Truth

    Всё состояние приложения хранится в одном объекте — store:

    const state = {
      user: { name: 'Алексей', role: 'admin' },
      todos: [{ id: 1, text: 'Изучить Redux', done: false }],
      ui: { theme: 'dark', sidebarOpen: true }
    }

    2. State is Read-Only

    Единственный способ изменить состояние — отправить action (экшен):

    // Action — простой объект с полем type
    const action = {
      type: 'todos/add',
      payload: { id: 2, text: 'Практика Redux', done: false }
    }
    
    // Отправляем экшен в store
    store.dispatch(action)

    3. Changes are Made with Pure Functions

    Изменения обрабатываются чистыми функциями — reducers (редьюсеры):

    function todosReducer(state = [], action) {
      switch (action.type) {
        case 'todos/add':
          return [...state, action.payload]
        case 'todos/toggle':
          return state.map(todo =>
            todo.id === action.payload
              ? { ...todo, done: !todo.done }
              : todo
          )
        default:
          return state
      }
    }

    Поток данных в Redux

    UI → dispatch(action) → Reducer → New State → UI обновляется

    1. Пользователь кликает кнопку

    2. Компонент вызывает dispatch({ type: 'counter/increment' })

    3. Redux передаёт экшен в редьюсер

    4. Редьюсер возвращает новое состояние

    5. Store уведомляет подписчиков

    6. React перерисовывает компоненты с новыми данными

    Создание Store (классический Redux)

    import { createStore } from 'redux'
    
    // Начальное состояние
    const initialState = { count: 0 }
    
    // Редьюсер
    function counterReducer(state = initialState, action) {
      switch (action.type) {
        case 'INCREMENT':
          return { count: state.count + 1 }
        case 'DECREMENT':
          return { count: state.count - 1 }
        case 'INCREMENT_BY':
          return { count: state.count + action.payload }
        default:
          return state
      }
    }
    
    // Создаём store
    const store = createStore(counterReducer)
    
    // Подписываемся на изменения
    store.subscribe(() => {
      console.log('State:', store.getState())
    })
    
    // Отправляем экшены
    store.dispatch({ type: 'INCREMENT' })        // { count: 1 }
    store.dispatch({ type: 'INCREMENT_BY', payload: 5 }) // { count: 6 }

    Проблема классического Redux

    Много шаблонного кода:

  • Action types как константы
  • Action creators как функции
  • Switch/case в редьюсерах
  • Иммутабельные обновления вручную
  • Redux Toolkit решает эти проблемы — следующий урок.

    Когда использовать Redux

    Нужен Redux если:

  • Много компонентов используют одни данные
  • Состояние часто обновляется
  • Сложная логика обновлений
  • Нужна история изменений (undo/redo)
  • Большая команда разработчиков
  • Не нужен Redux если:

  • Маленькое приложение
  • Состояние локальное для компонентов
  • Простая логика
  • Примеры

    Принцип работы Redux: store, dispatch, subscribe

    // Упрощённая реализация Redux для понимания концепции
    
    function createStore(reducer) {
      let state = reducer(undefined, { type: '@@INIT' })
      const listeners = []
    
      return {
        getState() {
          return state
        },
        dispatch(action) {
          console.log('Dispatching:', action.type)
          state = reducer(state, action)
          listeners.forEach(listener => listener())
          return action
        },
        subscribe(listener) {
          listeners.push(listener)
          return () => {
            const index = listeners.indexOf(listener)
            listeners.splice(index, 1)
          }
        }
      }
    }
    
    // Редьюсер для списка задач
    function todosReducer(state = [], action) {
      switch (action.type) {
        case 'ADD_TODO':
          return [...state, { id: Date.now(), text: action.payload, done: false }]
        case 'TOGGLE_TODO':
          return state.map(todo =>
            todo.id === action.payload ? { ...todo, done: !todo.done } : todo
          )
        case 'REMOVE_TODO':
          return state.filter(todo => todo.id !== action.payload)
        default:
          return state
      }
    }
    
    const store = createStore(todosReducer)
    
    // Подписываемся на изменения
    store.subscribe(() => {
      console.log('Todos:', store.getState())
    })
    
    // Dispatch экшенов
    store.dispatch({ type: 'ADD_TODO', payload: 'Изучить Redux' })
    store.dispatch({ type: 'ADD_TODO', payload: 'Практика Redux Toolkit' })
    
    const todos = store.getState()
    store.dispatch({ type: 'TOGGLE_TODO', payload: todos[0].id })
    
    console.log('Финальное состояние:', store.getState())

    Задание

    Создай простой Redux store для управления корзиной покупок. Реализуй редьюсер с экшенами ADD_ITEM, REMOVE_ITEM, CLEAR_CART. Каждый товар имеет id, name, price, quantity.

    Подсказка

    ADD_ITEM: item.quantity + 1. REMOVE_ITEM: filter(item => item.quantity > 0). CLEAR_CART: return [].

    Загружаем среду выполнения...
    Загружаем AI-помощника...