看到“reducer”這個詞,容易讓人聯(lián)想到Redux,但是在本文中,不必先理解Redux才能閱讀這篇文章。咱們將一起討論“reducer”實際上是什么,以及如何利用useReducer來管理組件中的復雜狀態(tài),以及這個新鉤子對Redux意味著什么?
金寨ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應用場景,ssl證書未來市場廣闊!成為成都創(chuàng)新互聯(lián)公司的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:13518219792(備注:SSL證書合作)期待與您的合作!
Reducer 是什么鬼
如果你熟悉Redux或數(shù)組上中的reduce方法,你大概就知道“reducer”是什么。 如果不熟悉,“reducer”大概是一個帶有2個值并返回1個值的函數(shù)這么個意思。
如果你有一系列的東西,并且想將這些東西組合成一個單獨的物體?!昂瘮?shù)式編程”中就是使用Array的reduce函數(shù)。 例如,如果你有一個數(shù)字數(shù)組并且想得到數(shù)組中所有數(shù)字的總和,咱們就可以寫一個reducer函數(shù)并將它傳遞給reduce,如下所示:
let?numbers?=?[1,?2,?3]; let?sum?=?numbers.reduce((total,?number)?=>?{ ?return?total?+?number; },0) 復制代碼
如果你以前沒用過這個,它可能看起來有點神秘。它所做的是為數(shù)組的每個元素調(diào)用函數(shù),傳入前一個total和當前元素 number。無論你返回什么,都會成為新的total。reduce的第二個參數(shù)(在本例中為0)是total的初始值。在本例中,reduce函數(shù)會被調(diào)用3次:
調(diào)用 (0, 1) 返回 1
調(diào)用 (1, 2) 返回 3
調(diào)用 (3, 4) 返回 6
reduce返回6,它保存在sum中。
使用useReducer又會是什么樣的?
各位花了在半篇幅來解釋Array的reduce函數(shù),因為 useReducer 參數(shù)與 reduce 相同,并且工作方式基本一樣。 useReducer接收 (state, action) => newState,并且返回了一個與當前state成對的dispatch的方法。 咱們使用 useReducer 來編寫上面的求和例子。
useReducer((state,?acton)?=>?{ ?return?state?+?action },?0) 復制代碼
useReducer返回一個包含2個元素的數(shù)組,類似于useState hook。 第一個是當前狀態(tài),第二個是dispatch方法,如下所示:
const?[sum,?dispatch]?=?useReducer((state,?action)?=>?{ ?return?state?+?action },?0) 復制代碼
注意,state可以是任何值,它不一定是一個對象,可以是一個數(shù)字,一個數(shù)組,或者其他任何類型。
盡管 useReducer 是擴展的 hook, 而 useState 是基本的 hook,但 useState 實際上執(zhí)行的也是一個 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} ? ?> ?); } 復制代碼
點擊按鈕dispatch一個值為1的action,該action將被添加到當前狀態(tài),然后組件使用新的狀態(tài)重新渲染。
這里故意展示了,派發(fā)action沒有遵循Redux的典型模式{type: "INCREMENT BY"、value: 1}或其他類似的東西。hook 的世界是一個新的世界:值得考慮的是,你是否發(fā)現(xiàn)舊的模式有價值并希望保留它們,或者你是否愿意更改它們。
一些更復雜的例子
再來看看更接近典型Redux reducer 的例子。創(chuàng)建一個組件來管理購物列表,這里看還會使用另外一個 hook:useRef。
首先,導入兩個 hook
import?React,?{?useReducer,?useRef?}?from?'react'; 復制代碼
然后創(chuàng)建一個設(shè)置ref和reducer的組件。 ref保存對表單的引用,以便咱們可以提取其值。
function?ShoppingList()?{ ?const?inputRef?=?useRef(); ?const?[items,?dispatch]?=?useReducer((state,?action)?=>?{ ?switch?(action.type)?{ ?//?do?something?with?the?action ?} ?},?[]); ?return?( ?<> ??
請注意,在這種情況下,咱們的“state”是一個數(shù)組。 咱們通過useReducer第二個參數(shù)將它初始化為一個空數(shù)組,并從reducer函數(shù)返回一個數(shù)組。
useRef Hook
useRef hook為DOM節(jié)點創(chuàng)建持久引用。 調(diào)用useRef會創(chuàng)建一個空的節(jié)點。它返回的對象具有current屬性,因此在上面的示例中,咱們可以使用inputRef.current訪問輸入的DOM節(jié)點。 如果你熟悉React.createRef(),則其工作原理非常相似。
但是,useRef返回的對象不僅僅是一種保存DOM引用的方法。 它可以保存特定于此組件實例的任何值,并且它在渲染之間保持不變。
useRef可用于創(chuàng)建通用實例變量,就像使用具有this.whatever = value的React類組件一樣。 唯一的問題是,寫入它會被視為“副作用”,因此咱們無法在渲染過程中更改它,需要在useEffect hook 中才能修改。
回到useReducer示例
我們用表單來處理用戶的輸入,按回車提交表彰。 現(xiàn)在來編寫handleSubmit函數(shù),該函數(shù)主要做的是將一個項添加到列表中,以及處理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?... ?); } 復制代碼
在reducer函數(shù)中主要判斷兩種情況:一種用于action.type==='add'的情況,還有就是默認下的情況。
當action.type為 add 時,它返回一個包含所有舊元素的新數(shù)組,以及最后的新元素。
這里有一點需要注意的是,咱們使用數(shù)組的length作為一種自動遞增的 ID 方便演示,但是對于一個真正的應用程序來說這是不可靠,因為它可能導致重復的ID和bug。(最好使用uuid這樣的庫,或者讓服務器生成一個惟一的ID!)
當用戶在輸入框中按Enter鍵時會調(diào)用handleSubmit函數(shù),因此咱們需要調(diào)用preventDefault以避免在發(fā)生這種情況時重新加載整頁。 然后dispatch派發(fā)一個 action。
刪除項
現(xiàn)在來看看如何從列表中刪除項的。
在項目中添加一個刪除
然后咱們只需要在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?( ?<> ??
練習:清空列表
試著添加一個功能:添加一個清空列表的按鈕。
在
可以在前面 CodeSandbox的基礎(chǔ)上完成。
Redux 會死嗎
大部分人看到useReducer hook, React 現(xiàn)在已經(jīng)內(nèi)置了reducer ,它有Context傳遞數(shù)據(jù),所以可能會想到 Redux 會不會因此就死了,以下是原作者給出的一些看法。
作者不認為useReducer會殺死Redux。React Hook 擴展了React在狀態(tài)管理方面的能力,所以會讓使用 Redux的情況減少。
Redux仍然比Context + useReducer的組合做得更多,它具有Redux DevTools 用于調(diào)試,可定制中間件、,以及整個相關(guān)庫生態(tài)系統(tǒng),當然 Redu x在很多地方都被過度使用了,但它仍然具有強大的功能。
Redux提供了一個全局存儲,可以在其中集中保存應用程序數(shù)據(jù)。useReducer本地化到特定組件。但是,沒有什么可以阻止咱們使用useReducer和useContext構(gòu)建自己的迷你redux 。如果你想這么做,而且符合你的需要,那就去做吧!
代碼部署后可能存在的BUG沒法實時知道,事后為了解決這些BUG,花了大量的時間進行l(wèi)og 調(diào)試,這邊順便給大家推薦一個好用的BUG監(jiān)控工具 Fundebug。