
大家好,我是小丞同學,一名大二的前端愛好者
這篇文章是學習 Redux 的學習筆記
非常感謝你的閱讀,不對的地方歡迎指正
願你忠于自己,熱愛生活
引言
在了解了 Antd 元件庫之後,我們現在開始學習了 Redux ,在我們之前寫的案例當中,例如:todolist 案例,GitHub 搜尋案例當中,我們對于狀态的管理,都是通過 state 來實作的,比如,我們在給兄弟元件傳遞資料時,需要先将資料傳遞給父元件,再由父元件轉發 給它的子元件。這個過程十分的複雜,後來我們又學習了消息的釋出訂閱,我們通過 pubsub 庫,實作了消息的轉發,直接将資料釋出,由兄弟元件訂閱,實作了兄弟元件間的資料傳遞。但是,随着我們的需求不斷地提升,我們需要進行更加複雜的資料傳遞,更多層次的資料交換。是以我們為何不可以将所有的資料交給一個中轉站,這個中轉站獨立于所有的元件之外,由這個中轉站來進行資料的分發,這樣不管哪個元件需要資料,我們都可以很輕易的給他派發。
而有這麼一個庫就可以幫助我們來實作,那就是 Redux ,它可以幫助我們實作集中式狀态管理
1. 什麼情況使用 Redux ?
首先,我們先明晰 Redux 的作用 ,實作集中式狀态管理。
Redux 适用于多互動、多資料源的場景。簡單了解就是複雜
從元件角度去考慮的話,當我們有以下的應用場景時,我們可以嘗試采用 Redux 來實作
1.某個元件的狀态需要共享時
2.一個元件需要改變其他元件的狀态時
3.一個元件需要改變全局的狀态時
除此之外,還有很多情況都需要使用 Redux 來實作(還沒有學 hook,或許還有更好的方法)
(從掘友的文章裡截的圖)
這張圖,非常形象的将純 React 和 采用 Redux 的差別展現了出來
2. Redux 的工作流程
首先元件會在 Redux 中派發一個 action 方法,通過調用 store.dispatch 方法,将 action 對象派發給 store ,當 store 接收到 action 對象時,會将先前的 state 與傳來的 action 一同發送給 reducer ,reducer 在接收到資料後,進行資料的更改,傳回一個新的狀态給 store ,最後由 store 更改 state
(圖來自掘金社群,侵删)
3. Redux 三個核心概念
1. store
store 是 Redux 的核心,可以了解為是 Redux 的資料中台,我們可以将任何我們想要存放的資料放在 store 中,在我們需要使用這些資料時,我們可以從中取出相應的資料。是以我們需要先建立一個 store ,在 Redux 中可以使用 createStore API 來建立一個 store
在生産中,我們需要在 src 目錄下的 redux 檔案夾中新增一個 store.js 檔案,在這個檔案中,建立一個 store 對象,并暴露它
是以我們需要從 redux 中暴露兩個方法
import {
createStore,
applyMiddleware
} from 'redux'
并引入為 count 元件服務的 reducer
import countReducer from './count_reducer'
最後調用 createStore 方法來暴露 store
export default createStore(countReducer, applyMiddleware(thunk))
這裡采用了中間件,本文應該不會寫到~
在 store 對象下有一些常用的内置方法
擷取目前時刻的 store ,我們可以采用 getStore 方法
const state = store.getState();
在前面我們的流程圖中,我們需要通過 store 中的 dispatch 方法來派生一個 action 對象給 store
store.dispatch(`action對象`)
最後還有一個 subscribe 方法,這個方法可以幫助我們訂閱 store 的改變,隻要 store 發生改變,這個方法的回調就會執行
為了監聽資料的更新,我們可以将 subscribe 方法綁定在元件挂載完畢生命周期函數上,但是這樣,當我們的元件數量很多時,會比較的麻煩,是以我們可以直接将 subscribe 函數用來監聽整個 App元件的變化
store.subscribe(() => {
ReactDOM.render( < App /> , document.getElementById('root'))
})
2. action
action 是 store 中唯一的資料來源,一般來說,我們會通過調用 store.dispatch 将 action 傳到 store
我們需要傳遞的 action 是一個對象,它必須要有一個 type 值
例如,這裡我們暴露了一個用于傳回一個 action 對象的方法
export const createIncrementAction = data => ({
type: INCREMENT,
data
})
我們調用它時,會傳回一個 action 對象
3. reducer
在 Reducer 中,我們需要指定狀态的操作類型,要做怎樣的資料更新,是以這個類型是必要的。
reducer 會根據 action 的訓示,對 state 進行對應的操作,然後傳回操作後的 state
如下,我們對接收的 action 中傳來的 type 進行判斷
export default function countReducer(preState = initState, action) {
const {
type,
data
} = action;
switch (type) {
case INCREMENT:
retun preState + data
case DECREMENT:
return preState - data
default:
return preState
}
}
更改資料,傳回新的狀态
4. 建立 constant 檔案
在我們正常的編碼中,有可能會出現拼寫錯誤的情況,但是我們會發現,拼寫錯誤了不一定會報錯,是以就會比較難搞。
我們可以在 redux 目錄下,建立一個 constant 檔案,這個檔案用于定義我們代碼中常用的一些變量,例如
export const INCREMENT = 'increment'export const DECREMENT = 'decrement'
将這兩個單詞寫在 constant 檔案中,并對外暴露,當我們需要使用時,我們可以引入這個檔案,并直接使用它的名稱即可
直接使用 INCREMENT 即可
5. 實作異步 action
一開始,我們直接調用一個異步函數,這雖然沒有什麼問題,但是難道 redux 就不可以實作了嗎?
incrementAsync = () => { const { value } = this.selectNumber const { count } = this.state; setTimeout(() => { this.setState({ count: count + value * 1 }) }, 500);}
我們可以先嘗試将它封裝到 action 對象中調用
export const createIncrementAsyncAction = (data, time) => { // 無需引入 store ,在調用的時候是由 store 調用的 return (dispatch) => { setTimeout(() => { dispatch(createIncrementAction(data)) }, time) }}
當我們點選異步加操作時,我們會調用這個函數,在這個函數裡接收一個延時加的時間,還有action所需的資料,和原先的差別隻在于傳回的時一個定時器函數
但是如果僅僅這樣,很顯然是會報錯的,它預設需要接收一個對象
如果我們需要實作傳入函數,那我們就需要告訴:你隻需要默默的幫我執行以下這個函數就好!
這時我們就需要引入中間件,在原生的 redux 中暴露出 applyMiddleware 中間件執行函數,并引入 redux-thunk 中間件(需要手動下載下傳)
import thunk from 'redux-thunk'
通過第二個參數傳遞下去就可以了
export default createStore(countReducer, applyMiddleware(thunk)
注意:異步 action 不是必須要寫的,完全可以自己等待異步任務的結果後再去分發同步action
6. Redux 三大原則
了解好 Redux 有助于我們更好的了解接下來的 React -Redux
第一個原則
單向資料流:整個 Redux 中,資料流向是單向的
UI 元件 —> action —> store —> reducer —> store
第二個原則
state 隻讀:在 Redux 中不能通過直接改變 state ,來控制狀态的改變,如果想要改變 state ,則需要觸發一次 action。通過 action 執行 reducer
第三個原則
純函數執行:每一個reducer 都是一個純函數,不會有任何副作用,傳回是一個新的 state,state 改變會觸發 store 中的 subscribe