之前在有道雲筆記上寫過一篇 Redux 學習實踐的文章,附帶了一個小 Demo。由於一段時間沒看 Redux,加上之前缺乏足夠的練習,現在想重構個人部落格,使用 Redux 進行狀態。現在不得不回頭再看看 Redux 相關東西。這裡順便記錄下學習筆記,一來便於回顧,二來希望對他人有所幫助。筆記同步寫在個人部落格網站
這篇文章是知識點的梳理,如果你對 Redux 的基礎有一定了解,閱讀起來可能比較合適。
什麼是 Redux?
Redux 可以說是 Flux 架構思想的最佳實踐方案,用於對大型、複雜、多資料來源的 React 進行資料管理。遵循 Redux 的設計原則,再配合 React 容器元件、展示元件分離的程式碼組織原則,可以讓我們的專案結構清晰,資料流清晰,十分利於專案的維護開發。
Redux 設計的三大原則
- 單一的資料來源,所有的資料儲存在一個物件中。
- 狀態只讀,Reducer 根據 store dispatch 的 action 型別返回一個新的狀態,不修改原來的狀態。
- 狀態修改均由純函式完成。
關於這三大原則的深入理解建議參考 《深入React技術棧》、《深入淺出React和Redux》
淺談 Redux 的工作過程(和 MVC 的主要區別)
有 MVC 框架使用經驗的同學應該清楚,MVC 的工作過程是這樣:view 層將使用者行為傳遞到 controller 中的action, controller 中的 action 和 Model 層互動運算元據,返回到 view。通常 MVC 框架的實現 M 和 V 層是可以互動的,這樣資料就不是單向流動,這種設計很多場景下很靈活,寫起來很爽快,但是要專案過大,專案資料流複雜,專案維護就不爽了。
Redux 則嚴格遵循上面提到的設計三大原則,工作過程是這樣的:view 層觸發一個 action,這個 action 由 store.dispatch(action) 傳遞給 reducer,reducer 根據 previousState 和 action type 返回一個新的 state(不修改資料), 如此實現了資料的變更。
Redux 核心 API
上面 Redux 的工作過程提到了 Redux 有 store、action、reducer 這些概念,這些都是 Redux 對外提供的核心 API,我們來看看他們分別是幹嘛的。
-
store
store 作為整個應用資料的唯一來源,用於儲存資料和 dispatch action。store 使用 Redux 提供的 createStore 方法來生成。
import { createStore } from 'redux'; let store = createStore(reducer) 複製程式碼
createStore 接受一個 reducer 作為引數,返回一個 store 物件。store 物件包含 4 中方法:
getState():// 用於獲取 store 中當前的狀態 dispatch():// 用於分發 action,這個是改變 store 中資料的唯一方式 subscribe(listener):// 註冊一個監聽者,在 store 發生變化時被呼叫 replaceReducer(nextReducer):///更新當前 store 中的 reducer 複製程式碼
-
reducer
reducer 本質上是一個函式,這個函式能接受一個 action 和一箇舊的狀態(previousState),根據 previousState 和 action 返回新的狀態
reducer(previousState, action) => newState 複製程式碼
reducer 必須是一個純函式,不能修改原來狀態,關於純函式的概念請閱讀其他資料學習。
-
action
reducer 的 action 本質上是一個 javaScript 物件,這個物件必須有一個 type 屬性,用來表明 action 的型別,還可以有 payload, error, meta 屬性。關於 action 物件的屬性,社群有個規範,可以參考github.com/redux-utili…
const action = { type: 'ADD_TODO', payload: 'todo' } 複製程式碼
reducer 就是根據 action type 屬性來決定返回新的 state。
先睹為快
我在學習 Redux 的時候,看了很多文件,認為對 Redux 的概念瞭然於胸,但是動手寫第一個小 Demo 的時候仍然覺得很難下手,可能是 Redux 這些概念對我而言太新。這裡給出我寫的第一個小 Demo。
這個 Demo 實現了點選按鈕讓數字加一減一的功能,沒有任何複雜度,單純的是為了瞭解 action
、store
、reducer
的具體作用和協作關係。
Demo 地址: github.com/wewin11235/…
為了便於觀察,Demo 的程式碼都丟在了一個檔案裡,在實際開發中,一定要遵循好的程式碼組織原則。
import React from 'react';
import ReactDOM from 'react-dom';
// 建立 action
// action 是 javaScript 物件,是使用者行為的抽象,必須包含一個 stype 欄位
// 根據 Demo 需要實現記數功能,抽象出兩種 action, 一種表示加,一種表示減
const incream_action = { type: 'INCREAM' };
const decream_action = { type: 'DECREME' };
// 建立 reducer
// reducer 接受一個 previousState 和 action 返回一個新的 state
const reducer = (state=0, action) => {
switch (action.type) {
case incream_action.type:
return state + 1;
case decream_action.type:
return state - 1;
default:
console.log('default');
return state;
}
}
// 建立 store
// store 建立使用 createStore(reducers)
// createStore 由 redux 提供
import { createStore } from 'redux';
let store = createStore(reducer);
// 建立計陣列件
class Counter extends React.Component {
constructor(props) {
super(props);
}
render() {
const { count, doIncrement, doDecrement } = this.props
return(
<div>
<span>{count}</span>
<button onClick={doIncrement}>+</button>
<button onClick={doDecrement}>-</button>
</div>
)
}
}
const render = () => ReactDOM.render(
<Counter
count={store.getState()}
doIncrement={() => store.dispatch(incream_action)}
doDecrement={() => store.dispatch(decream_action)}
/>,
document.getElementById('root')
);
render();
// 為了能在 store 發生變化後能重新整理頁面,需要給 store 註冊一個監聽事件
// store 變化後,重新整理頁面
store.subscribe(render);
複製程式碼
最小 Redux 應用誕生。
歡迎指正,補充!