Redux 是一個狀態管理器,通常與 React 一起使用,但並不限於該庫。通過閱讀這個簡單且易於理解的指南,來學習 Redux。

為什麼需要 Redux

Redux 是一個狀態管理器,通常與 React 一起使用,但它並不限於該庫 - 它也可以與其他技術一起使用。React 有自己的狀態管理方式,你可以在 React 初學者指南 中了解有關在 React 中管理狀態的方法。

將狀態向上移動到樹中適用於簡單的情况,但在複雜的應用程序中,你可能會發現你需要將幾乎所有的狀態向上移動,然後再使用 props 將其向下傳遞。

React 在 16.3.0 版本中引入了上下文 API,從不同部分訪問狀態的用例中,這使得 Redux 變得多餘,因此可以考慮使用上下文 API 而不是 Redux,除非你需要 Redux 提供的特定功能。

Redux 是一種管理應用程序狀態並將其移至外部全局存儲區的方法。有一些概念需要掌握,但一旦掌握,Redux 可以非常簡單地解決問題。

Redux 在 React 應用程序中非常流行,但它不僅僅適用於 React:幾乎有任何流行框架的綁定。那麼,我將使用 React 做一些示例,因為這是它的主要用例。

何時應該使用 Redux?

Redux 適用於中大型應用程序,在使用 React 或其他庫的默認狀態管理時遇到問題時,應把它用於項目中。

簡單的應用程式不需要它(簡單的應用程式沒有問題)。

不可變的狀態樹

在 Redux 中,應用程序的整個狀態由一個名為狀態狀態樹的 JavaScript 對象表示。

我們稱其為不可變的狀態樹,因為它是只讀的:不能直接更改它。狀態只能通過分發動作來更改。

Actions

動作是一個以最小方式描述變更的 JavaScript 對象(僅帶有所需的信息):

{
 type: 'CLICKED\_SIDEBAR'
}

// 例如,帶有更多數據的動作
{
 type: 'SELECTED\_USER',
 userId: 232
}

動作對象的唯一要求是具有type屬性,其值通常是一個字符串。

Action types 應該是常數

在簡單的應用程序中,動作類型可以定義為字符串。

當應用程序增長時,最好使用常數:

const ADD\_ITEM = 'ADD\_ITEM'
const action = { type: ADD\_ITEM, title: 'Third item' }

並將動作分離到自己的文件中,並導入它們:

import { ADD\_ITEM, REMOVE\_ITEM } from './actions'

Action creators

動作創建器是創建動作的函數:

function addItem(t) {
 return {
 type: ADD\_ITEM,
 title: t
 }
}

通常與觸發調度程序一起運行動作創建器:

dispatch(addItem('Milk'))

或通過定義動作調度程序函數來運行它:

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

Reducers

當動作被觸發時,必須發生某些事情,應用程序的狀態必須發生變化。這是reducer的工作。

什麼是 reducer

reducer是一個純函數,它根據先前的狀態樹和分發的動作計算下一個狀態樹:

;(currentState, action) => newState

純函數在不更改輸入或其他任何東西的情況下接收輸入並返回輸出。因此,reducer 返回一個完全替換先前狀態的全新狀態。

Reducer 不應該做什麼

reducer 應該是一個純函數,因此它應該:

  • 永遠不要更改它的參數
  • 永遠不要更改狀態,而是使用Object.assign({}, ...)創建一個新的狀態
  • 永遠不要生成副作用(不更改任何東西的 API 調用)
  • 永遠不要調用非純函數,這些函數根據與其輸入不同的因素改變其輸出(例如,Date.now()Math.random()

沒有強制執行,但你應該遵守這些規則。

多個 reducer

由於複雜應用的狀態可能非常廣泛,因此不只有一個 reducer,而有多個用於任何類型的動作。

reducer 的模擬

基本上,Redux 可以用這個簡單模型來簡化:

狀態

{
 list: [
 { title: "First item" },
 { title: "Second item" },
 ],
 title: 'Groceries list'
}

一系列的 actions

{ type: 'ADD\_ITEM', title: 'Third item' }
{ type: 'REMOVE\_ITEM', index: 1 }
{ type: 'CHANGE\_LIST\_TITLE', title: 'Road trip list' }

每個部分的 reducer

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.filter(item =>
 action.index !== item.index)
 default:
 return state
 }
}

整個狀態的 reducer

const listManager = (state = {}, action) => {
 return {
 title: title(state.title, action),
 list: list(state.list, action)
 }
}

Store

Store 是一個對象,它:

  • 保持 app 的狀態
  • 通過 getState() 公開狀態
  • 通過 dispatch() 允許我們更新狀態
  • 通過 subscribe() 允許我們(取消)註冊狀態變化監聽器

store 在應用程序中是唯一的

下面是如何創建 listManager app 的 store:

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

我可以使用服務器端數據初始化 store 嗎?

當然可以,只需傳遞起始狀態

let store = createStore(listManager, preexistingState)

獲取狀態

store.getState()

更新狀態

store.dispatch(addItem('Something'))

監聽狀態變化

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

unsubscribe()

數據流

Redux 中的數據流始終是單向的。

在 store 上調用 dispatch(),傳遞一個 action。

store 負責將 action 傳遞給 reducer,生成下一個狀態。

store 更新狀態並通知所有的監聽器。tags:Redux, JavaScript