【问题标题】:React useReducer and context: how to provide state selectors?React useReducer 和 context:如何提供状态选择器?
【发布时间】:2020-04-24 22:07:03
【问题描述】:

我在最初的问题中从未明确明确,但我正在尝试在没有 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 自动通过carReducer 访问state,这非常好。即时还原!

好吧,反正是半个 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();

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

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

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

    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();
    }
    

    【讨论】:

    • 这是一个非常好的和彻底的答案。谢谢。然而,当我写下这个问题时,这在我的脑海中是很清楚的,但我现在意识到在我写的内容中从未明确说明过,我正在尝试在 没有 redux 的情况下完成所有这些工作。我打算暂时搁置这个问题,如果没有其他建议,请接受您的回答,因为我怀疑这是普遍采用的解决方案。
    • @crowhill 这个答案使用redux。它只是模仿 redux 模式。
    • 如果我理解正确的话,这个解决方案会导致所有子组件在每次状态更改时重新渲染,对吧?
    猜你喜欢
    • 2021-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多