Краткое руководство по Redux для начинающих

Redux — это менеджер состояний. Чаще всего его используют с React, но его возможности не ограничиваются одной этой библиотекой. Хотя в React есть собственный метод управления состояниями (почитать о нём можно в руководстве по React для начинающих), он плохо масштабируется. Перемещение состояния вверх по дереву работает для простых приложений, но в более сложных архитектурах изменение состояния производится через свойства (props). Ещё лучше делать это через внешнее глобальное хранилище.

Redux — это способ управления состоянием приложения. Он основан на нескольких концепциях, изучив которые, можно с лёгкостью решать проблемы с состоянием.

Когда нужно пользоваться Redux?

Redux идеально подходит для средних и крупных приложений. Им стоит пользоваться только в случаях, когда невозможно управлять состоянием приложения с помощью стандартного менеджера состояний в React или любой другой библиотеке.

Простым приложениям Redux не нужен.

Использование Redux

Разберём устройство Redux и механизм его работы.

Неизменяемое дерево состояний

В Redux общее состояние приложения представлено одним объектом JavaScript — state (состояние) или state tree (дерево состояний). Неизменяемое дерево состояний доступно только для чтения, изменить ничего напрямую нельзя. Изменения возможны только при отправке action (действия).

Действия

Действие (action) — это JavaScript-объект, который лаконично описывает суть изменения:

{ 
  type: 'CLICKED_SIDEBAR' 
} 
// подробности об изменении 
{ 
  type: 'SELECTED_USER', 
  userId: 232 
}

Единственное требование к объекту действия — это наличие свойства type, значением которого обычно является строка.

Типы действий должны быть константами

В простом приложении тип действия задаётся строкой. По мере разрастания функциональности приложения лучше переходить на константы:

const ADD_ITEM = 'ADD_ITEM' 
const action = { type: ADD_ITEM, title: 'Third item' }

и выносить действия в отдельные файлы. А затем их импортировать:

import { ADD_ITEM, REMOVE_ITEM } from './actions'

Генераторы действий

Генераторы действий (actions creators) — это функции, создающие действия.

function addItem(t) { 
  return { 
    type: ADD_ITEM, 
    title: t 
  } 
}

Обычно инициируются вместе с функцией отправки действия:

dispatch(addItem('Milk'))

Или при определении этой функции:

const dispatchAddItem = i => dispatch(addItem(i))
dispatchAddItem('Milk')

Редукторы

При запуске действия обязательно что-то происходит и состояние приложения изменяется. Это работа редукторов.

Что такое редуктор

Редуктор (reducer) — это чистая функция, которая вычисляет следующее состояние дерева на основании его предыдущего состояния и применяемого действия.

(currentState, action) => newState

Чистая функция работает независимо от состояния программы и выдаёт выходное значение, принимая входное и не меняя ничего в нём и в остальной программе. Получается, что редуктор возвращает совершенно новый объект дерева состояний, которым заменяется предыдущий.

Чего не должен делать редуктор

Редуктор — это всегда чистая функция, поэтому он не должен:

  • мутировать аргументы;
  • мутировать состояние. Вместо этого создаётся новое состояние с помощью Object.assign({}, ...);
  • иметь побочные эффекты (никаких API-вызовов с какими-либо изменениями);
  • вызывать нечистые функции. Это функции, результат которых зависит от чего-то кроме их аргументов (например, Date.now() или Math.random()).

Поскольку состояние в сложных приложениях может сильно разрастаться, к каждому действию применяется не один, а сразу несколько редукторов.

Симулятор редуктора

Упрощённо базовую структуру Redux можно представить так:

Состояние
{ 
  list: [ 
    { title: "First item" }, 
    { title: "Second item" }, 
  ], 
  title: 'Grocieries list' 
}
Список действий
{ type: 'ADD_ITEM', title: 'Third item' } 
{ type: 'REMOVE_ITEM', index: 1 } 
{ type: 'CHANGE_LIST_TITLE', title: 'Road trip list' }
Редуктор для каждой части состояния
const title = (state = '', action) => {
  if (action.type === 'CHANGE_LIST_TITLE') { 
    return action.title 
  } else { 
    return state 
  } 
} 
const list = (state = [], action) => { 
  switch (action.type) { 
    case 'ADD_ITEM': 
      return state.concat([{ title: action.title }]) 
    case 'REMOVE_ITEM': 
      return state.map((item, index) => 
        action.index === index 
          ? { title: item.title } 
          : item 
    default: 
      return state 
  } 
}
Редуктор для общего состояния
const listManager = (state = {}, action) => { 
  return { 
    title: title(state.title, action), 
    list: list(state.list, action), 
  } 
}

Хранилище

Хранилище (store) — это объект, который:

  • содержит состояние приложения;
  • отображает состояние через getState();
  • может обновлять состояние через dispatch();
  • позволяет регистрироваться (или удаляться) в качестве слушателя изменения состояния через subscribe().

Хранилище в приложении всегда уникально. Так создаётся хранилище для приложения listManager:

import { createStore } from 'redux' 
import listManager from './reducers' 
let store = createStore(listManager)

Хранилище можно инициировать через серверные данные:

let store = createStore(listManager, preexistingState)

Функции хранилища

Получение состояния:

store.getState()

Обновление состояния:

store.dispatch(addItem('Something'))

Прослушивание изменений состояния:

const unsubscribe = store.subscribe(() => 
  const newState = store.getState() 
) 
unsubscribe()

Поток данных

Поток данных в Redux всегда однонаправлен.

Передача действий с потоками данных происходит через вызов метода dispatch() в хранилище. Само хранилище передаёт действия редуктору и генерирует следующее состояние, а затем обновляет состояние и уведомляет об этом всех слушателей.

Перевод статьи «A quick guide to Redux for beginners»

Ольга Сайфудинова

Подобрали два теста для вас:
— А здесь можно применить блокчейн?
Серверы для котиков: выберите лучшее решение для проекта и проверьте себя.