基于Hook的React狀態(tài)管理系列-基礎篇

useReducer-基礎概念篇

useReducer-使用篇

useReducer-配合useContext使用

useReducer是React提供的一個高級Hook,它不像useEffect、useState、useRef等必須hook一樣,沒有它我們也可以正常完成需求的開發(fā),但useReducer可以使我們的代碼具有更好的可讀性、可維護性、可預測性。

下面我們會分三篇文章詳細介紹如何在項目中使用useReducer

  • 第一篇:主要介紹JavaScript中reducer的概念以及它的特點,對reducer、redux等比較熟悉的小伙伴可以跳過本篇
  • 第二篇:主要介紹useReducer的使用方式和它的場景,以及useReducer帶來的好處
  • 第三篇:會進一步介紹復雜項目、復雜頁面中的useReducer使用

什么是reducer

reducer的概念是伴隨著Redux的出現(xiàn)逐漸在JavaScript中流行起來。但我們并不需要學習Redux去了解Reducer。簡單來說 reducer是一個函數(shù)(state, action) => newState:接收當前應用的state和觸發(fā)的動作action,計算并返回最新的state。下面是一段偽代碼:

    // 舉個栗子 計算器reducer,根據(jù)state(當前狀態(tài))和action(觸發(fā)的動作加、減)參數(shù),計算返回newState
    function countReducer(state, action) {
        switch(action.type) {
            case 'add':
                return state + 1;
            case 'sub':
                return state - 1;
            default: 
                return state;
        }
    }

上面例子:state是一個number類型的數(shù)值,reducer根據(jù)action的類型(加、減)對應的修改state,并返回最終的state。為了剛接觸到reducer概念的小伙伴更容易理解,可以將state改為count,但請始終牢記count仍然是state。

    function countReducer(count, action) {
        switch(action.type) {
            case 'add':
                return count + 1;
            case 'sub':
                return count - 1;
            default: 
                return count;
        }
    }

reducer 的冪等性

從上面的示例可以看到reducer本質(zhì)是一個純函數(shù),沒有任何UI和副作用。這意味著相同的輸入(state、action),reducer函數(shù)無論執(zhí)行多少遍始終會返回相同的輸出(newState)。因此通過reducer函數(shù)很容易推測state的變化,并且也更加容易單元測試。

    expect(countReducer(1, { type: 'add' })).equal(2); // 成功
    expect(countReducer(1, { type: 'add' })).equal(2); // 成功
    expect(countReducer(1, { type: 'sub' })).equal(0); // 成功

state 和 newState 的理解

state是當前應用狀態(tài)對象,可以理解就是我們熟知的React里面的state。

在上面的例子中state是一個基礎數(shù)據(jù)類型,但很多時候state可能會是一個復雜的JavaScript對象,如上例中count有可能只是 state中的一個屬性。針對這種場景我們可以使用ES6的結構賦值:

    // 返回一個 newState (newObject)
    function countReducer(state, action) {
        switch(action.type) {
            case 'add':
                return { ...state, count: state.count + 1; }
            case 'sub':
                return { ...state, count: state.count - 1; }
            default: 
                return count;
        }
    }

關于上面這段代碼有兩個重要的點需要我們記?。?/p>

  1. reducer處理的state對象必須是immutable,這意味著永遠不要直接修改參數(shù)中的state對象,reducer函數(shù)應該每次都返回一個新的state object

  2. 既然reducer要求每次都返回一個新的對象,我們可以使用ES6中的解構賦值方式去創(chuàng)建一個新對象,并復寫我們需要改變的state屬性,如上例。

看上去很完美,但如果我們的state是多層嵌套,解構賦值實現(xiàn)就非常復雜:

    function bookReducer(state, action) {
        switch(action.type) {
            // 添加一本書
            case 'addBook':
                return {
                    ...state,
                    books: {
                        ...state.books,
                        [bookId]: book,
                    }
                };
            case 'sub':
                // ....
            default: 
                return state;
        }
    }

對于這種復雜state的場景推薦使用immer等immutable庫解決。

state為什么需要immutable?

  • reducer的冪等性

我們上文提到過reducer需要保持冪等性,更加可預測、可測試。如果每次返回同一個state,就無法保證無論執(zhí)行多少次都是相同的結果

  • React中的state比較方案

React在比較oldStatenewState的時候是使用Object.is函數(shù),如果是同一個對象則不會出發(fā)組件的rerender。
可以參考官方文檔bailing-out-of-a-dispatch。

action 理解

action:用來表示觸發(fā)的行為。

  1. 用type來表示具體的行為類型(登錄、登出、添加用戶、刪除用戶等)
  2. 用payload攜帶的數(shù)據(jù)(如增加書籍,可以攜帶具體的book信息),我們用上面addBook的action為例:
    const action = {
        type: 'addBook',
        payload: {
            book: {
                bookId,
                bookName,
                author,
            }
        }
    }
    function bookReducer(state, action) {
        switch(action.type) {
            // 添加一本書
            case 'addBook':
                const { book } = action.payload;
                return {
                    ...state,
                    books: {
                        ...state.books,
                        [book.bookId]: book,
                    }
                };
            case 'sub':
                // ....
            default: 
                return state;
        }
    }

總結

至此基本介紹完了reducer相關的內(nèi)容,簡單總結一下:reducer是一個利用action提供的信息,將state從A轉換到B的一個純函數(shù),具有一下幾個特點:

  • 語法:(state, action) => newState
  • Immutable:每次都返回一個newState, 永遠不要直接修改state對象
  • Action:一個常規(guī)的Action對象通常有type和payload(可選)組成
    • type: 本次操作的類型,也是 reducer 條件判斷的依據(jù)
    • payload: 提供操作附帶的數(shù)據(jù)信息

下篇文章我們會進入正題:如何使用useReducer簡化我們的state管理。

最后慣例,歡迎大家star我們的人人貸大前端團隊博客,所有的文章還會同步更新到知乎專欄掘金賬號,我們每周都會分享幾篇高質(zhì)量的大前端技術文章。如果你喜歡這篇文章,希望能動動小手給個贊。

參考資料

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容