這一次完全搞定 useReducer - 基礎概念

useReducer-基礎概念篇html

useReducer-使用篇前端

useReducer-配合useContext使用react

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

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

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

什麼是reducer

reducer的概念是伴隨着Redux的出現逐漸在JavaScript中流行起來。但咱們並不須要學習Redux去了解Reducer。簡單來講 reducer是一個函數(state, action) => newState:接收當前應用的state和觸發的動做action,計算並返回最新的state。下面是一段僞代碼:redux

// 舉個栗子 計算器reducer,根據state(當前狀態)和action(觸發的動做加、減)參數,計算返回newState
    function countReducer(state, action) {
        switch(action.type) {
            case 'add':
                return state + 1;
            case 'sub':
                return state - 1;
            default: 
                return state;
        }
    }
複製代碼

上面例子:state是一個number類型的數值,reducer根據action的類型(加、減)對應的修改state,並返回最終的state。爲了剛接觸到reducer概念的小夥伴更容易理解,能夠將state改成count,但請始終牢記count仍然是stateapp

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

reducer 的冪等性

從上面的示例能夠看到reducer本質是一個純函數,沒有任何UI和反作用。這意味着相同的輸入(state、action),reducer函數不管執行多少遍始終會返回相同的輸出(newState)。所以經過reducer函數很容易推測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是當前應用狀態對象,能夠理解就是咱們熟知的React裏面的state。post

在上面的例子中state是一個基礎數據類型,但不少時候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;
        }
    }
複製代碼

關於上面這段代碼有兩個重要的點須要咱們記住:

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

  2. 既然reducer要求每次都返回一個新的對象,咱們可使用ES6中的解構賦值方式去建立一個新對象,並複寫咱們須要改變的state屬性,如上例。

看上去很完美,但若是咱們的state是多層嵌套,解構賦值實現就很是複雜:

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,就沒法保證不管執行多少次都是相同的結果

  • React中的state比較方案

React在比較oldStatenewState的時候是使用Object.is函數,若是是同一個對象則不會出發組件的rerender。 能夠參考官方文檔bailing-out-of-a-dispatch

action 理解

action:用來表示觸發的行爲。

  1. 用type來表示具體的行爲類型(登陸、登出、添加用戶、刪除用戶等)
  2. 用payload攜帶的數據(如增長書籍,能夠攜帶具體的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相關的內容,簡單總結一下:reducer是一個利用action提供的信息,將state從A轉換到B的一個純函數,具備一下幾個特色:

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

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

最後慣例,歡迎你們star咱們的人人貸大前端團隊博客,全部的文章還會同步更新到知乎專欄掘金帳號,咱們每週都會分享幾篇高質量的大前端技術文章。若是你喜歡這篇文章,但願能動動小手給個贊。

參考資料

相關文章
相關標籤/搜索