使用useReducer+useContext模擬一個redux

背景介紹

在使用React開發項目的時候,一般對於一些全局數據咱們都會選擇使用redux來進行管理,可是在有些場景卻不必定非要使用redux,好比下面的幾個場景react

  1. 部分複雜頁面狀態須要進行祖孫傳遞
  2. 項目很小,至少不多量的全局數據須要維護

對於這兩個 場景就能夠使用useReducer + useContext來模擬一個redux來進行狀態管理redux

封裝一個useReducer+useContext組件

import React, {
  Dispatch,
  useReducer,
  createContext,
  useContext,
  useEffect,
  Reducer,
} from 'react';

export interface ReducerContextResult<T, A> {
  useStore: () => T;
  useDispatch: () => Dispatch<A>;
  StoreProvider: React.FC<any>;
}

function createReducerContext<T, A>( initState: T, reducer: Reducer<T, A>, initData?: (state: T, dispatch: Dispatch<A>) => void, ): ReducerContextResult<T, A> {
  const StateContext = createContext(initState);
  const DispatchContext = createContext({} as Dispatch<A>);

  function useStore(): T {
    return useContext(StateContext);
  }

  function useDispatch(): Dispatch<A> {
    return useContext(DispatchContext);
  }

  function StoreProvider({ children }: { children: React.ReactNode }) {
    const [state, dispatch] = useReducer<Reducer<T, A>>(reducer, initState);

    useEffect(() => {
      /*eslint-disable*/
      initData && initData(initState, dispatch);
    }, []);
    return (
      <StateContext.Provider value={state}> <DispatchContext.Provider value={dispatch}> {children} </DispatchContext.Provider> </StateContext.Provider>
    );
  }
  return {
    useStore,
    useDispatch,
    StoreProvider,
  };
}

export default createReducerContext;
複製代碼

如上代碼,咱們封裝了一個通用的createReducerContext,接下來看一下如何使用這個組件呢markdown

使用封裝的組件

  1. 初始化context
// 聲明一個action_type
export enum ACTION_TYPE {
  INIT = 'init',
  UPDATE = 'update'
  // 添加其餘TYPE
}

export interface IUser{
  name: string;
  sex: number;
}

export interface IState {
  userInfo: IUser[];
  // 其餘state
}

export default IAction{
  type: ACTION_TYPE;
  data: any;
}

const initState: IState {
  userInfo: [];
}

function reducer(state: IState, action: IAction): IState {
  switch (action.type) {
    case ACTION_TYPE.INIT:
      return { ...action.data };
    default:
      return state;
  }
}


const contextResult: ReducerContextResult<IState, IAction> = createReducerContext<
  IState,
  IAction
>(initState, reducer, (state: IState, dispatch: Dispatch<IAction>) => {
	// 在這裏能夠初始化數據
	loadData().then(data => {
    dispatch({
       type: ACTION_TYPE.INIT,
       data
    })
  })
});

export const { useStore, useDispatch, StoreProvider } = contextResult;

複製代碼
  1. 在容器頁面使用StoreProvideride

    import {StoreProvider} from './context'
    
    export default function() { 
      return <StoreProvider> <!--容器內容--> </StoreProvider>
    }
    複製代碼
  2. 在子組件使用useStoreuseDispatchui

    import {useStore, useDispatch, ACTION_TYPE} from './context'
    
    const UserList = (props) => {
      // 數據
      const {data} = useStore()
      // 經過dispatch 更新數據
      const dispatch = useDispatch()
      
      // 使用dispatch更新數據
      function updateData(result) {
        dispatch({
          type: ACTION_TYPE.UPDATE,
          data: result
        })
      }
    }
    複製代碼
相關文章
相關標籤/搜索