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

useReducer-基礎(chǔ)概念篇

useReducer-使用篇

useReducer-配合useContext使用

useReducer是React提供的一個(gè)高級(jí)Hook,它不像useEffect苏潜、useState、useRef等必須hook一樣,沒(méi)有它我們也可以正常完成需求的開(kāi)發(fā)蔼水,但useReducer可以使我們的代碼具有更好的可讀性萍启、可維護(hù)性、可預(yù)測(cè)性肋杖。

下面我們會(huì)分三篇文章詳細(xì)介紹如何在項(xiàng)目中使用useReducer

  • 第一篇:主要介紹JavaScript中reducer的概念以及它的特點(diǎn)溉仑,對(duì)reducer、redux等比較熟悉的小伙伴可以跳過(guò)本篇
  • 第二篇:主要介紹useReducer的使用方式和它的場(chǎng)景状植,以及useReducer帶來(lái)的好處
  • 第三篇:會(huì)進(jìn)一步介紹復(fù)雜項(xiàng)目浊竟、復(fù)雜頁(yè)面中的useReducer使用

什么是reducer

reducer的概念是伴隨著Redux的出現(xiàn)逐漸在JavaScript中流行起來(lái)怨喘。但我們并不需要學(xué)習(xí)Redux去了解Reducer。簡(jiǎn)單來(lái)說(shuō) reducer是一個(gè)函數(shù)(state, action) => newState:接收當(dāng)前應(yīng)用的state和觸發(fā)的動(dòng)作action振定,計(jì)算并返回最新的state必怜。下面是一段偽代碼:

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

上面例子:state是一個(gè)number類型的數(shù)值,reducer根據(jù)action的類型(加卑惜、減)對(duì)應(yīng)的修改state膏执,并返回最終的state。為了剛接觸到reducer概念的小伙伴更容易理解,可以將state改為count露久,但請(qǐng)始終牢記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ì)是一個(gè)純函數(shù),沒(méi)有任何UI和副作用抱环。這意味著相同的輸入(state壳快、action),reducer函數(shù)無(wú)論執(zhí)行多少遍始終會(huì)返回相同的輸出(newState)镇草。因此通過(guò)reducer函數(shù)很容易推測(cè)state的變化眶痰,并且也更加容易單元測(cè)試。

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

state 和 newState 的理解

state是當(dāng)前應(yīng)用狀態(tài)對(duì)象梯啤,可以理解就是我們熟知的React里面的state竖伯。

在上面的例子中state是一個(gè)基礎(chǔ)數(shù)據(jù)類型,但很多時(shí)候state可能會(huì)是一個(gè)復(fù)雜的JavaScript對(duì)象因宇,如上例中count有可能只是 state中的一個(gè)屬性七婴。針對(duì)這種場(chǎng)景我們可以使用ES6的結(jié)構(gòu)賦值:

    // 返回一個(gè) 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;
        }
    }

關(guān)于上面這段代碼有兩個(gè)重要的點(diǎn)需要我們記住:

  1. reducer處理的state對(duì)象必須是immutable察滑,這意味著永遠(yuǎn)不要直接修改參數(shù)中的state對(duì)象打厘,reducer函數(shù)應(yīng)該每次都返回一個(gè)新的state object

  2. 既然reducer要求每次都返回一個(gè)新的對(duì)象,我們可以使用ES6中的解構(gòu)賦值方式去創(chuàng)建一個(gè)新對(duì)象贺辰,并復(fù)寫(xiě)我們需要改變的state屬性户盯,如上例。

看上去很完美饲化,但如果我們的state是多層嵌套莽鸭,解構(gòu)賦值實(shí)現(xiàn)就非常復(fù)雜:

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

對(duì)于這種復(fù)雜state的場(chǎng)景推薦使用immer等immutable庫(kù)解決。

state為什么需要immutable吃靠?

  • reducer的冪等性

我們上文提到過(guò)reducer需要保持冪等性硫眨,更加可預(yù)測(cè)、可測(cè)試巢块。如果每次返回同一個(gè)state礁阁,就無(wú)法保證無(wú)論執(zhí)行多少次都是相同的結(jié)果

  • React中的state比較方案

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

action 理解

action:用來(lái)表示觸發(fā)的行為裂逐。

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

總結(jié)

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

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

下篇文章我們會(huì)進(jìn)入正題:如何使用useReducer簡(jiǎn)化我們的state管理伞广。

最后慣例拣帽,歡迎大家star我們的人人貸大前端團(tuán)隊(duì)博客,所有的文章還會(huì)同步更新到知乎專欄掘金賬號(hào)嚼锄,我們每周都會(huì)分享幾篇高質(zhì)量的大前端技術(shù)文章减拭。如果你喜歡這篇文章,希望能動(dòng)動(dòng)小手給個(gè)贊区丑。

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拧粪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子沧侥,更是在濱河造成了極大的恐慌可霎,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宴杀,死亡現(xiàn)場(chǎng)離奇詭異癣朗,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)旺罢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)斯棒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人主经,你說(shuō)我怎么就攤上這事⊥ハВ” “怎么了罩驻?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)护赊。 經(jīng)常有香客問(wèn)我惠遏,道長(zhǎng)砾跃,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任节吮,我火速辦了婚禮抽高,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘透绩。我一直安慰自己翘骂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布帚豪。 她就那樣靜靜地躺著碳竟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狸臣。 梳的紋絲不亂的頭發(fā)上莹桅,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音烛亦,去河邊找鬼诈泼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛煤禽,可吹牛的內(nèi)容都是我干的铐达。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼呜师,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼娶桦!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起汁汗,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤衷畦,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后知牌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體祈争,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年角寸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菩混。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扁藕,死狀恐怖沮峡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情亿柑,我是刑警寧澤邢疙,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響疟游,放射性物質(zhì)發(fā)生泄漏呼畸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一颁虐、第九天 我趴在偏房一處隱蔽的房頂上張望蛮原。 院中可真熱鬧,春花似錦另绩、人聲如沸儒陨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)框全。三九已至,卻和暖如春干签,著一層夾襖步出監(jiān)牢的瞬間津辩,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工容劳, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留喘沿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓竭贩,卻偏偏與公主長(zhǎng)得像蚜印,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子留量,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345