React useReducer和上下文:如何提供状态选择器?

时间:2020-04-24 22:07:03

标签: reactjs react-hooks

在最初的问题中我从未明确表示清楚,但是我正在尝试在没有Redux的情况下完成所有这些操作,这意味着没有useSelector();。

这是仅使用本机React来实现选择器的练习。


我正在尝试使用钩子来构建类似Redux的安排,我是borrowing heavily from this guy

因此,我们有一家商店:

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可以通过state自动访问carReducer,这非常好。即时Redux!

无论如何,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();

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

1 个答案:

答案 0 :(得分:3)

这里最好的通用解决方案是提供一个由redux提供的名为useSelector的简单API,该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; })

}

EDIT-方法2:在上述情况下,您需要分别使用useContextuseSelector来避免这种情况,您也可以将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();
}