1. Redux Toolkit 是個(gè)好東西

        共 6850字,需瀏覽 14分鐘

         ·

        2021-11-03 16:48

        前言

        hello 朋友們,我又來分享技術(shù)調(diào)研了,蕪湖~

        這次是 Readux Toolkit,它配合我上次調(diào)研的 proxy-memoize 一起來優(yōu)化一下我們項(xiàng)目現(xiàn)在的狀態(tài)管理。

        相信大部分小伙伴還是對(duì) redux 更了解,那這個(gè)Readux Toolkit又是個(gè)啥東西的,能帶來啥,怎么用。那這篇文章可能能幫你解決這幾個(gè)疑問。當(dāng)然如果你想更詳細(xì)的了解的話肯定是要看官網(wǎng)的啦。

        那么話不多說,進(jìn)入正題吧

        基于Redux優(yōu)化

        首先毫無疑問,Readux Toolkit是基于Redux的一系列優(yōu)化,那優(yōu)化了redux的什么呢,這里我就簡(jiǎn)要的講一講redux可能存在的缺點(diǎn)(從一些角度說它的缺點(diǎn)也是了優(yōu)點(diǎn),因場(chǎng)景而異啦):

        1. 我們很多狀態(tài)都要抽象到 store,一個(gè)變化就要對(duì)應(yīng)編寫 action,reducer
        2. 需要幾個(gè)軟件包來使Redux與React一起工作,例如redux-thunk、reselect
        3. Redux的一些理念導(dǎo)致我們需要寫很多樣板代碼
        4. 配置 Redux store太復(fù)雜

        當(dāng)然我們也不全是因?yàn)閞edux他的這些所謂的缺點(diǎn),而非要卷來優(yōu)化哈,其實(shí)也是因?yàn)檎{(diào)研Readux Toolkit我才發(fā)現(xiàn)這redux的缺點(diǎn),雖然是上級(jí)給的任務(wù),但是調(diào)研之后發(fā)現(xiàn)還是真香。

        需了解的知識(shí)

        首先當(dāng)然是要了解redux知識(shí)啦,有redux知識(shí)為了方便更迅速理解Readux Toolkit的實(shí)現(xiàn)或者他的妙用,還需要先了解他的核心依賴:

        • immer
        • redux
        • redux-thunk
        • reselect

        immer

        這幾個(gè)中我是不了解這個(gè)immer的,其他基本略知一二,那么就看看這個(gè)immer庫是個(gè)啥吧:

        這個(gè)庫,它允許我們把state的 不變的(immutable) 特性轉(zhuǎn)化為 可變的(mutable);

        具體上的實(shí)現(xiàn)它是利用了 proxy,當(dāng)我們對(duì) state 進(jìn)行修改,proxy對(duì)象會(huì)攔截,并且按順序替換上層對(duì)象,返回的新對(duì)象??瓷先ゾ秃孟褡詣?dòng)幫你直接修改了state

        api

        首先看看整體的Api,然后再詳細(xì)說說可能會(huì)常用的:

        • configureStore (): 包裝 createStore 以提供簡(jiǎn)化的配置選項(xiàng)和良好的默認(rèn)設(shè)置。它可以自動(dòng)組合你的slice reducers,添加你提供的任何 Redux 中間件,默認(rèn)包括 Redux-thunk,并啟用 Redux DevTools 擴(kuò)展。
        • createReducer () : 它允許您為 case reducer 函數(shù)提供一個(gè)動(dòng)作類型查找表,而不是編寫 switch 語句。此外,它還自動(dòng)使用 immer 庫,讓您使用普通的可變代碼編寫更簡(jiǎn)單的不可變更新,比如 state.todos [3].complete = true。
        • createAction () : 為給定的動(dòng)作類型字符串生成動(dòng)作創(chuàng)建器函數(shù)。函數(shù)本身定義了 toString () ,因此可以使用它來代替類型常量。
        • createSlice () : 接受 reducer 函數(shù)的對(duì)象、片名和初始狀態(tài)值,并自動(dòng)生成帶有相應(yīng)動(dòng)作創(chuàng)建器和動(dòng)作類型的 slice reducer。
        • createAsyncThunk: 接受一個(gè)操作類型字符串和一個(gè)返回promise函數(shù),并生成一個(gè) thunk,該 thunk 根據(jù)該promise dispatches pending/fulfilled/rejected的action types
        • createEntityAdapter: 生成一組可重用的還原器和選擇器來管理存儲(chǔ)中的規(guī)范化數(shù)據(jù)
        • reselect庫中的 createSelector utility,為了方便使用而re-exported。

        configureStore

        step1configureStore,這個(gè)必不可少,用來創(chuàng)建一個(gè)空的Redux store,同時(shí)這里呢會(huì)自動(dòng)配置 Redux DevTools 擴(kuò)展,以便檢查存儲(chǔ):

        import?{?configureStore?}?from?'@reduxjs/toolkit'
        export?const?store?=?configureStore({
        ????reducer:?{},
        })

        step2 是要< provider > 來使 redux 對(duì) React 組件可用,將導(dǎo)出的store當(dāng)作prop傳遞給它,這一塊不必多說

        createSlice

        step3 這里會(huì)有點(diǎn)不一樣了,我們要通過 createSlice 創(chuàng)建一個(gè)Redux狀態(tài)切片(Redux State Slice),創(chuàng)建這個(gè)slice需要:

        1. 一個(gè)字符串名來標(biāo)識(shí)該片
        2. 一個(gè)初始狀態(tài)值
        3. 一個(gè)或多個(gè) reducer 函數(shù)來定義如何更新該狀態(tài) 創(chuàng)建這個(gè)slice能干嘛?可以導(dǎo)出生成的 Redux 動(dòng)作創(chuàng)建器和整個(gè)片的 reducer 函數(shù):
        import?{?createSlice?}?from?'@reduxjs/toolkit'

        const?initialState?=?{
        ??value:?0,
        }

        export?const?counterSlice?=?createSlice({
        ??name:?'counter',
        ??initialState,
        ??reducers:?{
        ????increment:?(state)?=>?{
        ??????/**?
        ???????* Redux Toolkit 允許我們?cè)谶€原器中編寫“可變的(mutable)”邏輯。
        ???????*?它實(shí)際上并沒有改變狀態(tài),因?yàn)樗褂?Immer?庫,
        ???????*?它將檢測(cè)對(duì)"draft?state"?的更改,并根據(jù)這些更改生成
        ???????*?一個(gè)全新的不可變狀態(tài)
        ???????*/

        ??????state.value?+=?1
        ????},
        ????decrement:?(state)?=>?{
        ??????state.value?-=?1
        ????},
        ????incrementByAmount:?(state,?action)?=>?{
        ??????state.value?+=?action.payload
        ????},
        ??},
        })

        //?為每個(gè)?reducer?函數(shù)生成動(dòng)作創(chuàng)建器
        export?const?{?increment,?decrement,?incrementByAmount?}?=?counterSlice.actions

        export?default?counterSlice.reducer

        結(jié)合這個(gè)例子,可以清楚的看到這個(gè)createSlice接收的:一個(gè)字符串名來標(biāo)識(shí)該片也就是name,一個(gè)初始狀態(tài)值initialState,以及多個(gè)reducer行數(shù)。并且為每個(gè) reducer 函數(shù)生成動(dòng)作創(chuàng)建器。

        它有啥作用或者其他好處呢?可能一小部分人不看代碼,我把注釋給拿下來。

        我們知道 Redux 它是要求我們通過制作數(shù)據(jù)副本和更新副本來編寫所有狀態(tài)更新的。然而, createSlice 和 createReducer 在內(nèi)部使用 Immer 來允許我們編寫“可變的(mutable)”的更新邏輯,使其成為正確的不可變更的更新。

        Redux Toolkit 允許我們?cè)谶€原器中編寫“mutable”邏輯。它實(shí)際上并沒有改變狀態(tài),因?yàn)樗褂?Immer 庫,檢測(cè)對(duì)“draft state”的更改,并根據(jù)這些更改生成一個(gè)全新的不可變狀態(tài)

        step 4 ?我們需要從上面的創(chuàng)建的空的 store 導(dǎo)入 reducer 函數(shù)并將其添加到我們的存儲(chǔ)中,通過在 reducer 參數(shù)中定義一個(gè)字段,告訴 store 使用這個(gè) slice reducer 函數(shù)來處理該狀態(tài)的所有更新。

        import?{?configureStore?}?from?'@reduxjs/toolkit'
        import?counterReducer?from?'../features/counter/counterSlice'

        export?default?configureStore({
        ??reducer:?{
        ????counter:?counterReducer,
        ??},
        })

        step 5 現(xiàn)在我們可以使用 React-Redux hook 讓 React 組件與 Redux 存儲(chǔ)交互。我們可以使用 useSelector 從存儲(chǔ)中讀取數(shù)據(jù),并使用 useDispatch 分派操作。

        理解的話我們看這個(gè) counter 組件的例子:

        import?React?from?'react'
        import?{?useSelector,?useDispatch?}?from?'react-redux'
        import?{?decrement,?increment?}?from?'./counterSlice'

        export?function?Counter()?{
        ??const?count?=?useSelector((state)?=>?state.counter.value)
        ??const?dispatch?=?useDispatch()

        ??return?(
        ????<div>
        ??????<div>
        ????????<button?onClick={()?=>?dispatch(increment())}?>
        ??????????增加+
        ????????button>

        ????????<span>{count}span>
        ????????<button?onClick={()?=>?dispatch(decrement())}?>
        ??????????減少-
        ????????button>
        ??????div>
        ????div>
        ??)
        }

        當(dāng)點(diǎn)擊+、-按鈕時(shí)的動(dòng)作,分析:

        • 相應(yīng)的 Redux action 將被派發(fā)(dispatched)到存儲(chǔ)區(qū)(store)
        • 這個(gè) counter slice reducer將觀測(cè)actions并更新其狀態(tài)
        • < Counter > 組件將觀測(cè)到存儲(chǔ)(store)中新的狀態(tài)值,并使用新數(shù)據(jù)re-render自己

        例子

        這里也放一個(gè)簡(jiǎn)單的例子,可以訪問codesandbox的可以戳這里,也可以去官網(wǎng)找這個(gè)例子。

        store.js 文件

        import?{?configureStore?}?from?'@reduxjs/toolkit';
        import?counterReducer?from?'../features/counter/counterSlice';

        export?default?configureStore({
        ????reducer:?{
        ????????counter:?counterReducer,
        ????},
        });

        counterSlice.js 文件

        import?{?createSlice?}?from?'@reduxjs/toolkit';

        export?const?slice?=?createSlice({
        ????name:?'counter',
        ????initialState:?{
        ????????value:?0,
        ????},
        ????reducers:?{
        ????????increment:?state?=>?{
        ????????????state.value?+=?1;
        ????????},
        ????????decrement:?state?=>?{
        ????????????state.value?-=?1;
        ????????},
        ????????incrementByAmount:?(state,?action)?=>?{
        ????????????state.value?+=?action.payload;
        ????????},
        ????},
        });

        export?const?{?increment,?decrement,?incrementByAmount?}?=?slice.actions;

        export?const?incrementAsync?=?amount?=>?dispatch?=>?{
        ????setTimeout(()?=>?{
        ????????dispatch(incrementByAmount(amount));
        ????},?1000);
        };

        export?const?selectCount?=?state?=>?state.counter.value;
        export?default?slice.reducer;

        Counter.js 文件

        import?React,?{?useState?}?from?'react';
        import?{?useSelector,?useDispatch?}?from?'react-redux';
        import?{
        ????decrement,
        ????increment,
        ????incrementByAmount,
        ????incrementAsync,
        ????selectCount,
        }?from?'./counterSlice';
        import?styles?from?'./Counter.module.css';

        export?function?Counter()?{
        const?count?=?useSelector(selectCount);
        const?dispatch?=?useDispatch();
        const?[incrementAmount,?setIncrementAmount]?=?useState('2');

        return?(
        <div>
        ????<div>
        ????????<button?onClick={()?=>?dispatch(increment())}?>
        ????????+
        ????????button>

        ????????<span>{count}span>
        ????????<button?onClick={()?=>?dispatch(decrement())}?>
        ????????-
        ????????button>
        ????div>

        ????<div>
        ????????<input
        ????????value={incrementAmount}
        ????????onChange={e?=>
        ?setIncrementAmount(e.target.value)}
        ????????/>

        ????????<button
        ????????onClick={()?=>

        ????????dispatch(incrementByAmount(Number(incrementAmount)?||?0))
        ????????}
        ????????>
        ????????Add?Amount
        ????????button>

        ????????<button?onClick={()?=>?dispatch(incrementAsync(Number(incrementAmount)?||?0))}?>
        ????????Add?Async
        ????????button>
        ????div>
        div>
        );
        }

        index.js 文件

        import?React?from?'react';
        import?ReactDOM?from?'react-dom';
        import?{?Provider?}?from?'react-redux';
        import?'./index.css';
        import?App?from?'./App';
        import?store?from?'./app/store';

        ReactDOM.render(
        ????<Provider?store={store}>
        ????????<App?/>
        ????Provider>
        ,
        ????document.getElementById('root')
        );

        總結(jié)

        這里簡(jiǎn)要的講一下這個(gè)簡(jiǎn)單例子的整體的步驟:

        1. 使用 configureStore 創(chuàng)建 Redux 存儲(chǔ)
        • configureStore 接受作為命名參數(shù)的 reducer 函數(shù)
        • configureStore 自動(dòng)設(shè)置好了默認(rèn)設(shè)置
        向 React 應(yīng)用程序組件提供 Redux 存儲(chǔ)
        • 在 組件外包裹 React-Redux < Provider > 組件
        • < Provider store = { store } >
        使用 createSlice 創(chuàng)建一個(gè) Redux“ slice”reducer
        • 使用字符串名稱、初始 state 和 reducer 函數(shù)調(diào)用 createSlice
        • Reducer 函數(shù)可能使用 Immer“變異(mutate)”狀態(tài)
        • 導(dǎo)出生成的slice reducer 和 action creators
        在 React 組件中使用 redux useSelector/useDispatch 掛鉤
        • 使用 useSelector 鉤子從 store 中讀取數(shù)據(jù)
        • 使用 useDispatch 鉤子獲取 dispatch 函數(shù),并根據(jù)需要進(jìn)行 dispatch actions 操作

        OK,大概就總結(jié)道這里了,你會(huì)發(fā)現(xiàn)還有一些主要的api沒有講到,比如很重要的createReducer 和 createAction這些還沒講,但是這個(gè)小應(yīng)用也能實(shí)現(xiàn)了(這個(gè)例子的場(chǎng)景限制發(fā)揮了呀)。

        那其實(shí)你知道這些基本就能使用了,還有就是這篇也沒講到 use Redux Toolkit and React-Redux with TypeScript,下篇我們?cè)敿?xì)講一下搭配 TypeScript 如何使用以及他的好處吧。

        ??????????

        非常感謝你看到這,如果覺得不錯(cuò)的話點(diǎn)個(gè)贊 ? 吧

        今天也是在努力變強(qiáng)不變禿的 HearLing 呀 ??

        ??????????


        瀏覽 128
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. chinese上海videos麻豆 | 爱爱网站久久 | 草嫩逼 | 嫩草影院1234蜜桃视频 | 91偷情 |