<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      redux vs redux-toolkit 及源碼實現(xiàn)

      我們是袋鼠云數(shù)棧 UED 團隊,致力于打造優(yōu)秀的一站式數(shù)據(jù)中臺產(chǎn)品。我們始終保持工匠精神,探索前端道路,為社區(qū)積累并傳播經(jīng)驗價值。

      本文作者:霜序

      前言

      為何講這個內(nèi)容?以為后續(xù)大家會使用 redux-toolkit,資產(chǎn)上周做了 redux-toolkit 的升級順便了解了相關(guān)內(nèi)容,產(chǎn)出了這篇文章。

      另外補齊一下在 React 數(shù)據(jù)流這個知識板塊的完整性。

      在之前的周分享中已經(jīng)分享過了React 中的數(shù)據(jù)流,react-redux 的一些實現(xiàn),redux 中中間件的實現(xiàn),以及 Mobx 的使用以及丐版實現(xiàn)。

      對于 Redux 本身尚未涉及,趁著使用 redux-toolkit 的機會一起了解一下 Redux 的實現(xiàn)。

      Redux-Toolkit

      Redux-Toolkit 是 基于 Redux 的二次封裝,開箱即用的 Redux 工具,比 Redux 更加簡單方便。

      ?? Why to use Redux-Toolkit?

      • "Configuring a Redux store is too complicated"
      • "I have to add a lot of packages to get Redux to do anything useful"
      • "Redux requires too much boilerplate code"

      Toolkit 使用

      Redux 該有的概念,Toolkit 其實都擁有的,只是他們使用的方式不同,例如 reducer / actions 等等,在 Toolkit 中都是隨處可見的。

      configureStore

      創(chuàng)建 store,代碼內(nèi)部還是調(diào)用的 Redux 的 createStore 方法

      const store = configureStore({
          reducer: {
              counter: counterReducer,
              user: userReducer,
          },
      });
      

      createAction + createReducer

      • createAction
        創(chuàng)建 Redux 中的 action 創(chuàng)建函數(shù)
      function createAction(type, prepareAction?)
      

      redux 中 action 的創(chuàng)建以及使用

      const updateName = (name: string) => ({ type: "user/UPDATE_NAME", name });
      const updateAge = (age: number) => ({ type: "user/UPDATE_AGE", age });
      

      Toolkit 中 action 的創(chuàng)建以及使用

      // 第一種
      const updateName = createAction<{ name: string }>("user/UPDATE_NAME");
      const updateAge = createAction<{ age: number }>("user/UPDATE_AGE");
      
      updateName();  // { type: 'user/UPDATE_NAME', payload: undefined }
      updateName({ name: "FBB" }); // { type: 'user/UPDATE_NAME', payload: { name: 'FBB' } }
      updateAge({ age: 18 });
      
      // 第二種
      const updateName = createAction("user/UPDATE_NAME", (name: string) => ({
        payload: {
          name,
        },
      }));
      const updateAge = createAction("user/UPDATE_AGE", (age: number) => ({
        payload: {
          age,
        },
      }));
      
      updateName("FBB");
      updateAge(18);
      
      • createReducer
        創(chuàng)建 Redux reducer 的函數(shù)

      :::info
      ?? createReducer 使用 Immer 庫,可以在 reducer 中直接對狀態(tài)進行修改,而不需要手動編寫不可變性的邏輯  
      :::

      Redux 中 reducer 的創(chuàng)建

      export const userReducer = (
        state = initialUserState,
        action: { type: string; [propName: string]: any }
      ) => {
        switch (action.type) {
          case "user/UPDATE_NAME":
            return { ...state, name: action.name };
          case "user/UPDATE_AGE":
            return { ...state, age: action.age };
          default:
            return state;
        }
      };
      

      Toolkit 中 reducer 的創(chuàng)建

      export const userReducer = createReducer(initialUserState, (builder) => {
        builder
          .addCase(updateAge, (state, action) => {
            state.age = action.payload.age;
          })
          .addCase(updateName, (state, action) => {
            state.name = action.payload.name;
          });
      });
      

      toolkit 提供的 createAction 和 createReducer 能夠幫我們簡化 Redux 中一些模版語法,但是整體的使用還是差不多的,我們依舊需要 action 文件和 reducer 文件,做了改善但是不多。

      redux demo   toolkit createReducer demo

      createSlice

      接受初始狀態(tài)、reducer 函數(shù)對象和 slice name 的函數(shù),并自動生成與 reducer 和 state 對應(yīng)的動作創(chuàng)建者和動作類型

      const userSlice = createSlice({
        name: "user",
        initialState: {
          age: 22,
          name: "shuangxu",
        },
        reducers: {
          updateName: (state, action: PayloadAction<string>) => {
            state.name = action.payload;
          },
          updateAge: (state, action: PayloadAction<number>) => {
            state.age = action.payload;
          },
        },
      })
      

      使用 createSlice 創(chuàng)建一個分片,每一個分片代表某一個業(yè)務(wù)的數(shù)據(jù)狀態(tài)處理。在其中可以完成 action 和 reducer 的創(chuàng)建。

      export const userSliceName = userSlice.name;
      export const { updateAge, updateName } = userSlice.actions;
      export const userReducer = userSlice.reducer;
      
      const store = configureStore({
        reducer: {
          [counterSliceName]: counterReducer,
          [userSliceName]: userReducer,
        },
      });
      

      toolkit slice demo

      在 Toolkit 中直接使用 createSlice 更加方便,能夠直接導(dǎo)出 reducer 和 action,直接在一個方法中能夠獲取到對應(yīng)內(nèi)容不在需要多處定義。

      Redux 源碼實現(xiàn)

      簡單的狀態(tài)管理

      所謂的狀態(tài)其實就是數(shù)據(jù),例如用戶中的 name

      let state = {
        name: "shuangxu"
      }
      
      // 使用狀態(tài)
      console.log(state.name)
      
      // 更改狀態(tài)
      state.name = "FBB"
      

      上述代碼中存在問題,當(dāng)我們修改了狀態(tài)之后無法通知到使用狀態(tài)的函數(shù),需要引入發(fā)布訂閱模式來解決這個問題

      const state = {
        name: "shuangxu",
      };
      const listeners = [];
      
      const subscribe = (listener) => {
        listeners.push(listener);
      };
      
      const changeName = (name) => {
        state.name = name;
        listeners.forEach((listener) => {
          listener?.();
        });
      };
      
      subscribe(() => console.log(state.name));
      
      changeName("FBB");
      changeName("LuckyFBB");
      

      在上述代碼中,我們已經(jīng)實現(xiàn)了更改變量能夠通知到對應(yīng)的監(jiān)聽函數(shù)。但是上述代碼并不通用,需要將公共方法封裝起來。

      const createStore = (initialState) => {
        let state = initialState;
        let listeners = [];
      
        const subscribe = (listener) => {
          listeners.push(listener);
          return () => {
            listeners = listeners.filter((fn) => fn !== listener);
          };
        };
      
        const changeState = (newState) => {
          state = { ...state, ...newState };
          listeners.forEach((listener) => {
            listener?.();
          });
        };
      
        const getState = () => state;
      
        return {
          subscribe,
          changeState,
          getState,
        };
      };
      
      // example
      const { getState, changeState, subscribe } = createStore({
        name: "shuangxu",
        age: 19,
      });
      
      subscribe(() => console.log(getState().name, getState().age));
      
      changeState({ name: "FBB" });   // FBB 19
      changeState({ age: 26 });       // FBB 26
      
      changeState({ sex: "female" });
      

      約束狀態(tài)管理器

      上述的實現(xiàn)能夠更改狀態(tài)和監(jiān)聽狀態(tài)的改變。但是上述改變 state 的方式過于隨便了,我們可以任意修改 state 中的數(shù)據(jù),changeState({ sex: "female" }),即使 sex 不存在于 initialState 中,所以我們需要約束只能夠修改 name/age 屬性

      通過一個 plan 函數(shù)來規(guī)定UPDATE_NAMEUPDATE_AGE方式更新對應(yīng)屬性

      const plan = (state, action) => {
        switch (action.type) {
          case "UPDATE_NAME":
            return {
              ...state,
              name: action.name,
            };
          case "UPDATE_AGE":
            return {
              ...state,
              age: action.age,
            };
          default:
            return state;
        }
      };
      

      更改一下 createStore 函數(shù)

      const createStore = (plan, initialState) => {
        let state = initialState;
        let listeners = [];
      
        const subscribe = (listener) => {
          listeners.push(listener);
          return () => {
            listeners = listeners.filter((fn) => fn !== listener);
          };
        };
      
        const changeState = (action) => {
          state = plan(state, action);
          listeners.forEach((listener) => {
            listener?.();
          });
        };
      
        const getState = () => state;
      
        return {
          subscribe,
          changeState,
          getState,
        };
      };
      
      const { getState, changeState, subscribe } = createStore(plan, {
        name: "shuangxu",
        age: 19,
      });
      
      subscribe(() => console.log(getState().name, getState().age));
      
      changeState({ type: "UPDATE_NAME", name: "FBB" });
      changeState({ type: "UPDATE_AGE", age: "28" });
      changeState({ type: "UPDATE_SEX", sex: "female" });
      

      代碼中的 plan 就是 redux 中的 reducer,changeState 就是 dispatch。

      拆分 reducer

      reducer 做的事情比較簡單,接收 oldState,通過 action 更新 state。

      但是實際項目中可能存在不同模塊的 state,如果都把 state 的執(zhí)行計劃寫在同一個 reducer 中龐大有復(fù)雜。

      因此在常見的項目中會按模塊拆分不同的 reducer,最后在一個函數(shù)中將 reducer 合并起來。

      const initialState = {
        user: { name: "shuangxu", age: 19 },
        counter: { count: 1 },
      };
      
      // 對于上述 state 我們將其拆分為兩個 reducer
      const userReducer = (state, action) => {
        switch (action.type) {
          case "UPDATE_NAME":
            return {
              ...state,
              name: action.name,
            };
          case "UPDATE_AGE":
            return {
              ...state,
              age: action.age,
            };
          default:
            return state;
        }
      };
      
      const counterReducer = (state, action) => {
        switch (action.type) {
          case "INCREMENT":
            return {
              count: state.count + 1,
            };
          case "DECREMENT":
            return {
              ...state,
              count: state.count - 1,
            };
          default:
            return state;
        }
      };
      
      // 整合 reducer
      const combineReducers = (reducers) => {
        // 返回新的 reducer 函數(shù)
        return (state = {}, action) => {
          const newState = {};
          for (const key in reducers) {
            const reducer = reducers[key];
            const preStateForKey = state[key];
            const nextStateForKey = reducer(preStateForKey, action);
            newState[key] = nextStateForKey;
          }
          return newState;
        };
      };
      

      代碼跑起來!!

      const reducers = combineReducers({
        counter: counterReducer,
        user: userReducer,
      });
      
      const store = createStore(reducers, initialState);
      store.subscribe(() => {
        const state = store.getState();
        console.log(state.counter.count, state.user.name, state.user.age);
      });
      store.dispatch({ type: "UPDATE_NAME", name: "FBB" });  // 1 FBB 19
      store.dispatch({ type: "UPDATE_AGE", age: "28" });     // 1 FBB 28
      store.dispatch({ type: "INCREMENT" });                 // 2 FBB 28
      store.dispatch({ type: "DECREMENT" });                 // 1 FBB 28
      

      拆分 state

      在上一節(jié)的代碼中,我們 state 還是定義在一起的,會造成 state 樹很龐大,在項目中使用的時候我們都在 reducer 中定義好 initialState 的。

      在使用 createStore 的時候,我們可以不傳入 initialState,直接使用store = createStore(reducers)。因此我們要對這種情況作處理。

      拆分 state 和 reducer 寫在一起。

      const initialUserState = { name: "shuangxu", age: 19 };
      
      const userReducer = (state = initialUserState, action) => {
        switch (action.type) {
          case "UPDATE_NAME":
            return {
              ...state,
              name: action.name,
            };
          case "UPDATE_AGE":
            return {
              ...state,
              age: action.age,
            };
          default:
            return state;
        }
      };
      
      const initialCounterState = { count: 1 };
      
      const counterReducer = (state = initialCounterState, action) => {
        switch (action.type) {
          case "INCREMENT":
            return {
              count: state.count + 1,
            };
          case "DECREMENT":
            return {
              ...state,
              count: state.count - 1,
            };
          default:
            return state;
        }
      };
      

      更改 createStore 函數(shù),可以自動獲取到每一個 reducer 的 initialState

      const createStore = (reducer, initialState = {}) => {
        let state = initialState;
        let listeners = [];
      
        const subscribe = (listener) => {
          listeners.push(listener);
          return () => {
            listeners = listeners.filter((fn) => fn !== listener);
          };
        };
      
        const dispatch = (action) => {
          state = reducer(state, action);
          listeners.forEach((listener) => {
            listener?.();
          });
        };
      
        const getState = () => state;
      
        // 僅僅用于獲取初始值
        dispatch({ type: Symbol() });
      
        return {
          subscribe,
          dispatch,
          getState,
        };
      };
      

      dispatch({ type: Symbol() })代碼能夠?qū)崿F(xiàn)如下效果:

      • createStore 的時候,一個不匹配任何 type 的 action,來觸發(fā)state = reducer(state, action)
      • 每個 reducer 都會進到 default 項,返回 initialState

      Redux-Toolkit 源碼實現(xiàn)

      configureStore

      接受一個含有 reducer 的對象作為參數(shù),內(nèi)部調(diào)用 redux 的 createStore 創(chuàng)建出 store

      import { combineReducers, createStore } from "redux";
      
      export function configureStore({ reducer }: any) {
        const rootReducer = combineReducers(reducer);
        const store = createStore(rootReducer);
        return store;
      }
      

      createAction

      const updateName = createAction<string>("user/UPDATE_NAME");
      const updateName = createAction("user/UPDATE_NAME", (name: string) => ({
        payload: {
          name,
        },
      }));
      
      updateName("FBB");
      

      通過上面的示例,能夠分析出來 createAction 返回的是一個函數(shù),接受第一個參數(shù) type 返回{ type: 'user/UPDATE_NAME', payload: undefined };對于具體的 payload 值需要傳入第二個參數(shù)來改變

      export const createAction = (type: string, preAction?: Function) => {
        function actionCreator(...args: any[]) {
          if (!preAction)
            return {
              type,
              payload: args[0],
            };
          const prepared = preAction(...args);
          if (!prepared) {
            throw new Error("prepareAction did not return an object");
          }
          return {
            type,
            payload: prepared.payload,
          };
        }
        actionCreator.type = type;
        return actionCreator;
      };
      

      createReducer

      export const userReducer = createReducer(initialUserState, (builder) => {
        builder
          .addCase(updateAge, (state, action) => {
            state.age = action.payload.age;
          })
          .addCase(updateName, (state, action) => {
            state.name = action.payload.name;
          });
      });
      

      每一個 reducer 都是一個函數(shù)(state = initialState, action) => {},因此 createReducer 返回值為函數(shù)

      通過一個 createReducer 函數(shù),內(nèi)部還需要知道每一個 action 對應(yīng)的操作

      import { produce as createNextState } from "immer";
      
      export const createReducer = (
        initialState: any,
        builderCallback: (builder: any) => void
      ) => {
        const actionsMap = executeReducerBuilderCallback(builderCallback);
        return function reducer(state = initialState, action: any) {
          const caseReducer = actionsMap[action.type];
          if (!caseReducer) return state;
          return createNextState(state, (draft: any) =>
            caseReducer(draft, action)
                                );
        };
      };
      
      // 通過 createReducer 的第二個參數(shù),構(gòu)建出 action 對應(yīng)的操作方法
      export const executeReducerBuilderCallback = (
        builderCallback: (builder: any) => void
      ) => {
        const actionsMap: any = {};
        const builder = {
          addCase(typeOrActionCreator: any, reducer: any) {
            const type =
              typeof typeOrActionCreator === "string"
              ? typeOrActionCreator
              : typeOrActionCreator.type;
            actionsMap[type] = reducer;
            return builder;
          },
        };
        builderCallback(builder);
        return actionsMap;
      };
      

      createSlice

      const counterSlice = createSlice({
        name: "counter",
        initialState: {
          count: 1,
        },
        reducers: {
          increment: (state: any) => {
            state.count += 1;
          },
          decrement: (state: any) => {
            state.count -= 1;
          },
        },
      });
      
      const counterSliceName = counterSlice.name;
      const { increment, decrement } = counterSlice.actions;
      const counterReducer = counterSlice.reducer;
      

      createSlice 返回的是一個對象{ name, actions, reducer },接受{ name, initialState, reducers }三個參數(shù)。通過 reducers 中相關(guān)參數(shù)得到對應(yīng)的 actions 和 reducer。

      在 createSlice 中主要還是靠 createAction 和 createReducer 方法。通過 name 和 reducers 的每一個屬性拼接成為 action.type,調(diào)用 createReducer 遍歷 reducers 的屬性添加 case

      import { createAction } from "./createAction";
      import { createReducer } from "./createReducer";
      
      export default function createSlice({ name, initialState, reducers }: any) {
        const reducerNames = Object.keys(reducers);
      
        const actionCreators: any = {};
        const sliceCaseReducersByType: any = {};
      
        reducerNames.forEach((reducerName) => {
          const type = `${name}/${reducerName}`;
          const reducerWithPrepare = reducers[reducerName];
          actionCreators[reducerName] = createAction(type);
          sliceCaseReducersByType[type] = reducerWithPrepare;
        });
      
        function buildReducer() {
          return createReducer(initialState, (builder) => {
            for (let key in sliceCaseReducersByType) {
              builder.addCase(key, sliceCaseReducersByType[key]);
            }
          });
        }
      
        return {
          name,
          actions: actionCreators,
          reducer: (state: any, action: any) => {
            const _reducer = buildReducer();
            return _reducer(state, action);
          },
        };
      }
      

      總結(jié)

      在本文講解了 Redux-Toolkit 基礎(chǔ)使用,從 redux 的源碼出發(fā)解析了 redux-toolkit 的源碼,從源碼中也能夠看出來 toolkit 的實現(xiàn)是基于 redux 來實現(xiàn)的,且使用上也大同小異,無破壞性變更。

      最后

      歡迎關(guān)注【袋鼠云數(shù)棧UED團隊】~
      袋鼠云數(shù)棧 UED 團隊持續(xù)為廣大開發(fā)者分享技術(shù)成果,相繼參與開源了歡迎 star

      posted @ 2025-03-04 10:22  袋鼠云數(shù)棧前端  閱讀(211)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 蜜臀精品视频一区二区三区| 亚洲的天堂在线中文字幕| 午夜成人无码免费看网站| 国产99青青成人A在线| 亚洲成av人片无码天堂下载| 国产一区二区四区不卡| 国产免费高清69式视频在线观看| 亚洲色欲或者高潮影院| 99久久亚洲综合精品成人| 在线观看视频一区二区三区| 久久精品免视看国产成人| 无码日韩精品一区二区人妻| 国产成人午夜精品永久免费| 日韩中文字幕人妻精品 | 马公市| 亚洲综合视频一区二区三区| 中国女人高潮hd| 少妇又爽又刺激视频| 国内熟妇人妻色在线三级| 亚洲精品国偷拍自产在线观看蜜臀| 国产精品美女黑丝流水| 欧美丰满熟妇xxxx性| 成人午夜免费一区二区三区| 国产中文字幕精品免费| 国产欧美在线一区二区三| 中文有码字幕日本第一页| 欧美videosdesexo吹潮| 中文字幕乱妇无码AV在线| 4hu亚洲人成人无码网www电影首页| 在线看国产精品三级在线| 亚洲精品一区久久久久一品av | 国产精品成人综合色在线| 久久国产乱子伦免费精品无码| 国产无遮挡又黄又爽不要vip软件| 最新亚洲人成网站在线观看| 亚洲国产成人va在线观看天堂| 国产精品一二三区蜜臀av| 成人免费A级毛片无码片2022| 国产自拍在线一区二区三区| 在线播放亚洲人成电影| 国产高清在线不卡一区|