Redux(一)
前言
在大型 React 應用中隨著狀態管理變得複雜,像是多個頁面或是元件需要共用同一份資料,為了管理這樣複雜的 UI 畫面與資料之間的對應關係,會需要一個可靠的全域狀態管理工具。
Redux 的核心概念
在 Redux 中有三個需要先理解的名詞:
- Store : Redux 使用一個單一的 Store 來保存整個應用程式的 State,唯一改變 Store 的方式是透過發送一個 Action。
- Action : Action 描述發生了什麼事情(數字加減、改變文字),的純 JavaScript Object 物件。它擁有一個
type
屬性,用於標識動作的類型。動作可以攜帶一些數據。 - 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。