likes
comments
collection
share

useReducer、useContext替代redux方案

作者站长头像
站长
· 阅读数 32

先简单复习一下redux工作流程

graph LR

A[action]-- dispatch.action-->B[store]
B--previousState,action -->C[reducers]

C-- newState -->B

B-- state -->D[组件]

react-hook替换redux方案

要求列表

  • useReducer、useContext函数
  • action:存放修改state的action,此处与redux的思想一致
  • reducer:用来处理不同action,此处我们不提供初始状态的话,默认会去action找。
  • rootReducer:当reducer过多的时候,我们可以拆分reducer,拆分reducer后,使用combinReducers合并处理成一个大单一的对象。
  • 顶级组件:

    1. 组件利用provider提供context给子组件 2.useReducer定义,引入reducer并提供初始化状态initialstate
  • 子组件:

    1. useContext使用顶级组件提供的context
    2. 如果需要异步请求,使用useEffect

实现逻辑关键代码:

  • cartReducer.js

    // 定义一个action type
    const CHANGE_CART_AMOUNT = "CHANGE_CART_AMOUNT";
    // 初始化状态数据
    const initialState ={
    cartList: []
    };
    // reducer处理action,返回newState
    export const cartReducer = (state, action) => {
    switch (action.type) {
      case CHANGE_CART_AMOUNT:
        let cartList = state?state.cart.cartList:[];
        let cartItem = action.payload;
        return {
          cartList: [...cartList, cartItem]
        };
    
      default:
        {
          return state;
        }
    }
    };
    当你除了一个cartReducer之外还有很多reducer,这个时候我们需要拆分并统一管理。
  • rootReducer.js

    import { cartReducer,initialStates } from './cartReducer';
    import combineReducers from './combineReducers';
    import { layoutInitialState, layoutReducer } from './layoutReducer';
    export const initialState = {
    layout: layoutInitialState,
    cart: initialStates()
    };
    //拆分reducer后,使用combinReducers合并处理成一个大单一的对象, 
    export const rootReducer = combineReducers({
    layout: layoutReducer,
    cart: cartReducer 
    });
    
    这里的combineReducer按照redux逻辑,直接写,不是用redux的。redux的combineReducer工作原理参考:https://www.cnblogs.com/wy193...
  • combineReducers.js

    //将多个reducer合并成一个reducer
    const combineReducers = reducers => {
    return (state = {}, action) => {
      const newState = {};
    
      for (let key in reducers) {
        newState[key] = reducers[key](state, action);
      }
    
      return newState;
    }; 
    };
    
    export default combineReducers;
    
  • 顶级组件.jsx关键代码

    const AppContext = createContext({
    state: initialState,
    dispatch: () => {} 
    });
    //由于创建的contenxt AppContext需要在各个组件都用uesContext使用到,所以需要导出。
    export default AppContext;
    
     //Redux 是通过 createStore(reducer, initialState) 来创建一个 store 实例,
    // 实例封装了 state 的读写接口和监听接口:getState 、dispatch、subscribe
    
    // 在Redux中,store.dispatch 触发事件动作时,Redux 并不会为我们主动重新渲染视图,而是需要我们调用 store.subscribe 在监听函数中手动 render 视图
    
    //但 useReducer Hook 是没有使用 store 实例,
    //而是遵循 Hook 总是返回读写接口的规则,直接通过 [state, dispatch] = useReducer(reducer, initialState) 的方式返回状态的读写接口。
    
    const [state, dispatch] = useReducer(rootReducer,initialState);
    
    
    //由于布局数据几乎不变化,我们可以使用缓存,等变化再更新,利用dispatch去触发我们上面定义的rootReducer
    const contextValue = useMemo(() => {
      return {
        state,
        dispatch
      };
    }, [state, dispatch]);
    
    //Provider传递参数
    return <AppContext.Provider value={contextValue}>
      </AppContext.Provider>;
      
    
  • 子组件.jsx:在react-hook中,我们只需要触发reducer就能实现像redux-store的dispatch效果一样。

    // 触发dispatch
    const {
      state,
      dispatch
    } = useAppContext();
    
    const handleCartAmountChange = useCallback((amount, product) => () => {
      dispatch({
        type: "CHANGE_CART_AMOUNT",
        payload: { ...product,
          qty: amount
        }
      });
    }, []);
    
    //获取state
    
    const {
      state
    } = useAppContext();
    
    useEffect(() => {
      setList(state?state.cart.cartList:[])
    },[state]);