Написать пост

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

Аватар Никита Прияцелюк

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

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

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

Библиотека Redux — это способ управления состоянием приложения. Она основана на нескольких концепциях, изучив которые, можно с лёгкостью решать проблемы с состоянием. Вы узнаете о них далее, в этом руководстве по 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() в хранилище. Само хранилище передаёт действия редуктору и генерирует следующее состояние, а затем обновляет состояние и уведомляет об этом всех слушателей.

Советуем начинающим в Redux прочитать нашу статью о других способах передачи данных.

Следите за новыми постами
Следите за новыми постами по любимым темам
152К открытий152К показов