溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何使用useReducer Hook

發(fā)布時(shí)間:2020-05-23 15:03:45 來(lái)源:網(wǎng)絡(luò) 閱讀:340 作者:可樂(lè)程序員 欄目:web開發(fā)

看到“reducer”這個(gè)詞,容易讓人聯(lián)想到Redux,但是在本文中,不必先理解Redux才能閱讀這篇文章。咱們將一起討論“reducer”實(shí)際上是什么,以及如何利用useReducer來(lái)管理組件中的復(fù)雜狀態(tài),以及這個(gè)新鉤子對(duì)Redux意味著什么?

Reducer 是什么鬼

如果你熟悉Redux或數(shù)組上中的reduce方法,你大概就知道“reducer”是什么。 如果不熟悉,“reducer”大概是一個(gè)帶有2個(gè)值并返回1個(gè)值的函數(shù)這么個(gè)意思。

如果你有一系列的東西,并且想將這些東西組合成一個(gè)單獨(dú)的物體?!昂瘮?shù)式編程”中就是使用Array的reduce函數(shù)。 例如,如果你有一個(gè)數(shù)字?jǐn)?shù)組并且想得到數(shù)組中所有數(shù)字的總和,咱們就可以寫一個(gè)reducer函數(shù)并將它傳遞給reduce,如下所示:

let?numbers?=?[1,?2,?3];
let?sum?=?numbers.reduce((total,?number)?=>?{
?return?total?+?number;
},0)
復(fù)制代碼

如果你以前沒(méi)用過(guò)這個(gè),它可能看起來(lái)有點(diǎn)神秘。它所做的是為數(shù)組的每個(gè)元素調(diào)用函數(shù),傳入前一個(gè)total和當(dāng)前元素 number。無(wú)論你返回什么,都會(huì)成為新的total。reduce的第二個(gè)參數(shù)(在本例中為0)是total的初始值。在本例中,reduce函數(shù)會(huì)被調(diào)用3次:

  • 調(diào)用 (0, 1) 返回 1

  • 調(diào)用 (1, 2) 返回 3

  • 調(diào)用 (3, 4) 返回 6

reduce返回6,它保存在sum中。

使用useReducer又會(huì)是什么樣的?

各位花了在半篇幅來(lái)解釋Array的reduce函數(shù),因?yàn)?useReducer 參數(shù)與 reduce 相同,并且工作方式基本一樣。 useReducer接收 (state, action) => newState,并且返回了一個(gè)與當(dāng)前state成對(duì)的dispatch的方法。 咱們使用 useReducer 來(lái)編寫上面的求和例子。

useReducer((state,?acton)?=>?{
?return?state?+?action
},?0)
復(fù)制代碼

useReducer返回一個(gè)包含2個(gè)元素的數(shù)組,類似于useState hook。 第一個(gè)是當(dāng)前狀態(tài),第二個(gè)是dispatch方法,如下所示:

const?[sum,?dispatch]?=?useReducer((state,?action)?=>?{
?return?state?+?action
},?0)
復(fù)制代碼

注意,state可以是任何值,它不一定是一個(gè)對(duì)象,可以是一個(gè)數(shù)字,一個(gè)數(shù)組,或者其他任何類型。

盡管 useReducer 是擴(kuò)展的 hook, 而 useState 是基本的 hook,但 useState 實(shí)際上執(zhí)行的也是一個(gè) useReducer。這意味著 useReducer 是更原生的,你能在任何使用 useState 的地方都替換成使用 useReducer。

import?React,?{?useReducer?}?from?'react';
function?Counter()?{
?//?First?render?will?create?the?state,?and?it?will
?//?persist?through?future?renders
?const?[sum,?dispatch]?=?useReducer((state,?action)?=>?{
?return?state?+?action;
?},?0);
?return?(
?<>
?{sum}
?<button?onClick={()?=>?dispatch(1)}>
?Add?1
?</button>
?</>
?);
}
復(fù)制代碼

點(diǎn)擊按鈕dispatch一個(gè)值為1的action,該action將被添加到當(dāng)前狀態(tài),然后組件使用新的狀態(tài)重新渲染。

這里故意展示了,派發(fā)action沒(méi)有遵循Redux的典型模式{type: "INCREMENT BY"、value: 1}或其他類似的東西。hook 的世界是一個(gè)新的世界:值得考慮的是,你是否發(fā)現(xiàn)舊的模式有價(jià)值并希望保留它們,或者你是否愿意更改它們。

一些更復(fù)雜的例子

再來(lái)看看更接近典型Redux reducer 的例子。創(chuàng)建一個(gè)組件來(lái)管理購(gòu)物列表,這里看還會(huì)使用另外一個(gè) hook:useRef。

首先,導(dǎo)入兩個(gè) hook

import?React,?{?useReducer,?useRef?}?from?'react';
復(fù)制代碼

然后創(chuàng)建一個(gè)設(shè)置ref和reducer的組件。 ref保存對(duì)表單的引用,以便咱們可以提取其值。

function?ShoppingList()?{
?const?inputRef?=?useRef();
?const?[items,?dispatch]?=?useReducer((state,?action)?=>?{
?switch?(action.type)?{
?//?do?something?with?the?action
?}
?},?[]);
?return?(
?<>
?<form?onSubmit={handleSubmit}>
?<input?ref={inputRef}?/>
?</form>
?<ul>
?{items.map((item,?index)?=>?(
?<li?key={item.id}>
?{item.name}
?</li>
?))}
?</ul>
?</>
?);
}
復(fù)制代碼

請(qǐng)注意,在這種情況下,咱們的“state”是一個(gè)數(shù)組。 咱們通過(guò)useReducer第二個(gè)參數(shù)將它初始化為一個(gè)空數(shù)組,并從reducer函數(shù)返回一個(gè)數(shù)組。

useRef Hook

useRef hook為DOM節(jié)點(diǎn)創(chuàng)建持久引用。 調(diào)用useRef會(huì)創(chuàng)建一個(gè)空的節(jié)點(diǎn)。它返回的對(duì)象具有current屬性,因此在上面的示例中,咱們可以使用inputRef.current訪問(wèn)輸入的DOM節(jié)點(diǎn)。 如果你熟悉React.createRef(),則其工作原理非常相似。

但是,useRef返回的對(duì)象不僅僅是一種保存DOM引用的方法。 它可以保存特定于此組件實(shí)例的任何值,并且它在渲染之間保持不變。

useRef可用于創(chuàng)建通用實(shí)例變量,就像使用具有this.whatever = value的React類組件一樣。 唯一的問(wèn)題是,寫入它會(huì)被視為“副作用”,因此咱們無(wú)法在渲染過(guò)程中更改它,需要在useEffect hook 中才能修改。

回到useReducer示例

我們用表單來(lái)處理用戶的輸入,按回車提交表彰。 現(xiàn)在來(lái)編寫handleSubmit函數(shù),該函數(shù)主要做的是將一個(gè)項(xiàng)添加到列表中,以及處理reducer中的 action。

function?ShoppingList()?{
?const?inputRef?=?useRef();
?const?[items,?dispatch]?=?useReducer((state,?action)?=>?{
?switch?(action.type)?{
?case?'add':
?return?[
?...state,
?{
?id:?state.length,
?name:?action.name
?}
?];
?default:
?return?state;
?}
?},?[]);
?function?handleSubmit(e)?{
?e.preventDefault();
?dispatch({
?type:?'add',
?name:?inputRef.current.value
?});
?inputRef.current.value?=?'';
?}
?return?(
?//?...?same?...
?);
}
復(fù)制代碼

在reducer函數(shù)中主要判斷兩種情況:一種用于action.type==='add'的情況,還有就是默認(rèn)下的情況。

當(dāng)action.type為 add 時(shí),它返回一個(gè)包含所有舊元素的新數(shù)組,以及最后的新元素。

這里有一點(diǎn)需要注意的是,咱們使用數(shù)組的length作為一種自動(dòng)遞增的 ID 方便演示,但是對(duì)于一個(gè)真正的應(yīng)用程序來(lái)說(shuō)這是不可靠,因?yàn)樗赡軐?dǎo)致重復(fù)的ID和bug。(最好使用uuid這樣的庫(kù),或者讓服務(wù)器生成一個(gè)惟一的ID!)

當(dāng)用戶在輸入框中按Enter鍵時(shí)會(huì)調(diào)用handleSubmit函數(shù),因此咱們需要調(diào)用preventDefault以避免在發(fā)生這種情況時(shí)重新加載整頁(yè)。 然后dispatch派發(fā)一個(gè) action。

刪除項(xiàng)

現(xiàn)在來(lái)看看如何從列表中刪除項(xiàng)的。

在項(xiàng)目中添加一個(gè)刪除<button>,點(diǎn)擊該按鈕派發(fā) 它將發(fā)送一個(gè) action type === "remove"的操作,以及要?jiǎng)h除的項(xiàng)的索引。

然后咱們只需要在reducer中處理該action

function?ShoppingList()?{
?const?inputRef?=?useRef();
?const?[items,?dispatch]?=?useReducer((state,?action)?=>?{
?switch?(action.type)?{
?case?'add':
?//?...?same?as?before?...
?case?'remove':
?//?keep?every?item?except?the?one?we?want?to?remove
?return?state.filter((_,?index)?=>?index?!=?action.index);
?default:
?return?state;
?}
?},?[]);
?function?handleSubmit(e)?{?/*...*/?}
?return?(
?<>
?<form?onSubmit={handleSubmit}>
?<input?ref={inputRef}?/>
?</form>
?<ul>
?{items.map((item,?index)?=>?(
?<li?key={item.id}>
?{item.name}
?<button
?onClick={()?=>?dispatch({?type:?'remove',?index?})}
?>
?X
?</button>
?</li>
?))}
?</ul>
?</>
?);
}
復(fù)制代碼

練習(xí):清空列表

試著添加一個(gè)功能:添加一個(gè)清空列表的按鈕。

在<ul>上方插入一個(gè)按鈕,并為其提供一個(gè)onClick prop,派發(fā)一個(gè)action ,type 為“clear”的動(dòng)作,并在 reducer 方法執(zhí)行清空列表的動(dòng)作。

可以在前面 CodeSandbox的基礎(chǔ)上完成。

Redux 會(huì)死嗎

大部分人看到useReducer hook, React 現(xiàn)在已經(jīng)內(nèi)置了reducer ,它有Context傳遞數(shù)據(jù),所以可能會(huì)想到 Redux 會(huì)不會(huì)因此就死了,以下是原作者給出的一些看法。

作者不認(rèn)為useReducer會(huì)殺死Redux。React Hook 擴(kuò)展了React在狀態(tài)管理方面的能力,所以會(huì)讓使用 Redux的情況減少。

Redux仍然比Context + useReducer的組合做得更多,它具有Redux DevTools 用于調(diào)試,可定制中間件、,以及整個(gè)相關(guān)庫(kù)生態(tài)系統(tǒng),當(dāng)然 Redu x在很多地方都被過(guò)度使用了,但它仍然具有強(qiáng)大的功能。

Redux提供了一個(gè)全局存儲(chǔ),可以在其中集中保存應(yīng)用程序數(shù)據(jù)。useReducer本地化到特定組件。但是,沒(méi)有什么可以阻止咱們使用useReducer和useContext構(gòu)建自己的迷你redux 。如果你想這么做,而且符合你的需要,那就去做吧!

代碼部署后可能存在的BUG沒(méi)法實(shí)時(shí)知道,事后為了解決這些BUG,花了大量的時(shí)間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個(gè)好用的BUG監(jiān)控工具 Fundebug。

如何使用useReducer Hook


向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI