跳至主要内容

Redux(一)

前言​

在大型 React 應用中隨著狀態管理變得複雜,像是多個頁面或是元件需要共用同一份資料,為了管理這樣複雜的 UI 畫面與資料之間的對應關係,會需要一個可靠的全域狀態管理工具。

Redux 的核心概念​

在 Redux 中有三個需要先理解的名詞:

  1. Store : Redux 使用一個單一的 Store 來保存整個應用程式的 State,唯一改變 Store 的方式是透過發送一個 Action。
  2. Action : Action 描述發生了什麼事情(數字加減、改變文字),的純 JavaScript Object 物件。它擁有一個 type 屬性,用於標識動作的類型。動作可以攜帶一些數據。
  3. Reducer : Reducer 是一個 pure function(純函數),根據先前的狀態和動作來計算新的狀態。每個 Reducer 都負責管理 Store 中的一部分狀態。

概念​

應用程式的完整 state 被以一個 object tree 的形式儲存在單一一個的 store 裡面。 改變 state tree 的唯一方式是去發送一個 action,action 是一個描述發生什麼事的物件。 要指定 actions 要如何轉換 state tree 的話,你必須撰寫 pure reducers。

Store​

  • 會擁有整個 App 的 state,在 Redux 中總是只會有一個 store,但可以有多可 reducer。
  • 當 state 有更新時,store 會通知有訂閱的 container 有新的 state。
  • 透過 getState() 可以取得 state 的資料。
  • 透過 dispatch(action) 可以更新 state 的資料。
  • 透過 subscribe(listener) 可以註冊監聽事件。
import { createStore } from 'redux'

/**
* 這是一個 reducer,一個有 (state, action) => state signature 的 pure function。
* 它描述一個 action 如何把 state 轉換成下一個 state。
*
* state 的形狀取決於你:它可以是基本類型、一個陣列、一個物件,
* 或甚至是一個 Immutable.js 資料結構。唯一重要的部分是你
* 不應該改變 state 物件,而是當 state 變化時回傳一個新的物件。
*
* 在這個範例中,我們使用一個 `switch` 陳述句和字串,不過你可以使用一個 helper,
* 來遵照一個不同的慣例 (例如 function maps),如果它對你的專案有意義。
*/
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}

// 建立一個 Redux store 來掌管你的應用程式的 state。
// 它的 API 是 { subscribe, dispatch, getState }。
let store = createStore(counter)

// 你可以手動的去訂閱更新,或是使用跟你的 view layer 之間的綁定。
// 通常你會使用一個 view 綁定 library(例如:React Redux),而不是直接 subscribe()。
// 然而也可以很方便的將目前狀態儲存在 localStorage。
store.subscribe(() =>
console.log(store.getState())
)

// 變更內部 state 的唯一方法是 dispatch 一個 action。
// actions 可以被 serialized、logged 或是儲存並在之後重播。
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1

你必須指定你想要隨著被稱作 actions 的一般物件而發生的變更,而不是直接改變 state。接著你會寫一個被稱作 reducer 的特別 function,來決定每個 action 如何轉變整個應用程式的 state。

@reduxjs/toolkit 簡化的部分​

使用 createSlice 以用更少的程式碼來定義 action 和 reducer,並且它會自動生成 action 的 type 字串。而 PayloadAction 則可以用來定義這些自動生成的 action 的類型。

1. Slice​

@reduxjs/toolkit 引入了 Slice,這是一個包含 Reducer 和 Action 的集合。這樣可以更容易地組織代碼,並且減少樣板代碼的使用。

// 使用 @reduxjs/toolkit 創建片段
import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
name: 'counter',
initialState: 0,
reducers: {
increment: state => state + 1,
decrement: state => state - 1,
},
});

// 導出片段的動作
export const { increment, decrement } = counterSlice.actions;
// 導出片段的減速器
export default counterSlice.reducer;

2. configureStore​

// 使用 @reduxjs/toolkit 配置儲存庫
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';

const store = configureStore({
reducer: {
counter: counterReducer,
},
});

3. Provider​

// 使用 @reduxjs/toolkit 的提供者
import React from 'react';
import { Provider } from 'react-redux';
import { store } from './store';
import App from './App';

const Root = () => (
<Provider store={store}>
<App />
</Provider>
);

export default Root;

本篇 Redux(一)介紹了 Redux 中的核心概念以及 @reduxjs/toolkit 如何簡化開發流程,下一篇會介紹共享狀態的觀念與介紹如何使用 Redux Devtools。