首页 > 解决方案 > React useReducer 和 context:如何提供状态选择器?

问题描述

在最初的问题中我从来没有明确表示过,但我试图在没有Redux 的情况下完成所有这些,这意味着没有 useSelector();

这是一个使用原生 React 来实现选择器的练习。


我正在尝试使用钩子构建类似 Redux 的安排,并且我从这个人那里大量借用

所以,我们有一个商店:

const CarStore = ({ children }) => {
  const [state, dispatch] = useReducer(
    carReducer,
    [
      {
        type: 'fast',
        mpg: 'bad',
      },
    ],
  );

  return (
    <CarStateContext.Provider value={selectors}>
      <CarDispatchContext.Provider value={dispatch}>
        {children}
      </CarDispatchContext.Provider>
    </CarStateContext.Provider>
  );
};
export const CarStateContext = createContext();
export const CarDispatchContext = createContext();

现在,dispatch自动可以访问statevia carReducer,这非常好。即时还原!

好吧,无论如何,半个 Redux。我仍然缺少选择器,这是我的问题。鉴于上述上下文,我怎样才能最好地构建,例如,像getCarType()这样的选择器只会“快速”返回到组件?

一个模糊的想法是这样的:

const CarStore = ({ children }) => {
  const [state, dispatch] = useReducer(
    carReducer,
    [
      {
        type: 'fast',
        mpg: 'bad',
      },
    ],
  );

  // selectors, kind of. 
  const selectors = {
    getType: () => { return state.type; },
    getMpg: () => { return state.mpg; },
  };

  return (
    <CarStateContext.Provider value={selectors}> // <- don't return state, return selectors
      <CarDispatchContext.Provider value={dispatch}>
        {children}
      </CarDispatchContext.Provider>
    </CarStateContext.Provider>
  );
};
export const CarStateContext = createContext();
export const CarDispatchContext = createContext();

我猜这确实有效,但可能有更好的解决方案?

标签: reactjsreact-hooks

解决方案


这里最好和通用的解决方案是提供一个useSelector由 redux 提供的简单 API,它接受一个回调并使用当前状态调用它并返回结果。

const CarStore = ({ children }) => {
  const [state, dispatch] = useReducer(
    carReducer,
    [
      {
        type: 'fast',
        mpg: 'bad',
      },
    ],
  );


  const useSelector = (callback) => {
    return callback(state)
  };

  return (
    <CarStateContext.Provider value={{useSelector}}> // <- don't return state, return selectors
      <CarDispatchContext.Provider value={dispatch}>
        {children}
      </CarDispatchContext.Provider>
    </CarStateContext.Provider>
  );
};
export const CarStateContext = createContext();
export const CarDispatchContext = createContext();

一旦你这样做了,你就可以将它用作任何组件,比如

const Comp = () => {
   const {useSelector} = useContext(CarStateContext);
   const type = useSelector((state) => { return state.type; })
   const mpg = useSelector((state) => { return state.mpg; })

}

编辑 - 方法 2:在上述场景中,您需要useContext单独使用,useSelector为了避免它,您也可以将 useSelector 实现为钩子。此外,调度和状态不需要两个单独的上下文,您可以只使用一个

const CarStore = ({ children }) => {
  const [state, dispatch] = useReducer(
    carReducer,
    [
      {
        type: 'fast',
        mpg: 'bad',
      },
    ],
  );


  const getValue = (callback) => {
    return callback(state)
  };

  return (
    <CarStateContext.Provider value={{getValue, dispatch}}> // <- don't return state, return selectors
        {children}
    </CarStateContext.Provider>
  );
};
export const CarStateContext = createContext();

export const useSelector = (callback) => {
   const {getValue} = useContext(CarStateContext);
   return getValue(callback)
}
// Similarly dispatch can be provided like below
export const useDispatch = () => {
   const {dispatch} = useContext(CarStateContext);
   return dispatch
}

并在您的组件中使用它

import { useSelector, useDispatch } from 'path/to/useSelector'
const Comp = () => {
   const type = useSelector((state) => { return state.type; })
   const mpg = useSelector((state) => { return state.mpg; })
   const dispatch = useDispatch();
}

推荐阅读