使用 Context API 作为模仿 useSelector 和 useDispatch 与 redux v5 的方式

Using the Context API as a way of mimicking useSelector and useDispatch with redux v5

我正在做一个 React 项目,我只能使用 React Redux v5,它不包括 useDispatchuseSelector

尽管如此,我真的很想在我的应用程序中使用这些挂钩(或类似的东西)。 因此,我在应用程序的顶层创建了一个包装器组件,我使用 redux 的 connect 函数连接它。 我的 mapStateToProps 和 mapDispatchToProps 看起来像这样:

const mapDispatchToProps = (dispatch: DispatchType) => {
    return {
        dispatch,
    };
};

const mapStateToProps = (state: StateType) => {
    return {
        state,
    };
};

export default connect(mapStateToProps, mapDispatchToProps)(MainLayout);

在我的包装器组件中,我将调度和状态传递给值:

  <DispatchContext.Provider value={{ state, dispatch }}>
{children}
</DispatchContext.Provider>

最后,我有一个看起来像这样的钩子:

const useSelectAndDispatch = () => {
    const context = useContext(DispatchContext);
    if (context === null) {
        throw new Error("Please use useDispatch within DispatchContext");
    }
    const { state, dispatch } = context;

    function select(selector) {
        return selector(state);
    }
    return { dispatch, select };
};

然后我通过 useSelectAndDispatch 在我的组件中使用分派和选择器。

我想知道这是否是解决此问题的适当方法,以及我是否会遇到任何性能问题。我正在使用 reselect,并且对记忆化有很好的理解。我只是在征求意见,因为我听说 redux 团队因为性能问题而推迟实施 useDispatchuseSelector 很长一段时间。

非常感谢任何意见!

这将导致严重的性能问题。您的 mapStateToProps 正在使用整个状态对象,因此每次状态发生任何变化时,提供者都必须重新呈现。由于提供者使用新值重新呈现,因此每个使用上下文的组件也必须重新呈现。简而言之,您将强制您的大部分应用程序在任何更改时重新呈现。

我不使用 mapStateToProps 和 mapDispatchToProps,而是回到实际的商店对象,并从中构建您的挂钩。您的应用程序中的某处可能有一行代码显示 const store = createStore(/* some options */).

使用那个 store 变量,你可以做一些钩子。如果我可以假设你的应用程序中只有一个商店,那么调度挂钩是微不足道的:

import { store } from 'wherever the store is created'

export const useMyDispatch = () => {
  return store.dispatch;
}

而选择器应该是这样的。它使用 .subscribe 在商店中的某些内容发生变化时得到通知,然后它使用选择器提取您关心的状态部分。如果什么都没有改变,那么旧状态和新状态将通过 === 检查,并且反应跳过渲染。如果它发生了变化,组件将呈现(仅调用 useMySelect 的组件及其子组件,而不是整个应用程序)

export const useMySelector = (selector) => {
  const [value, setValue] = useState(() => {
    return selector(store.getState());
  });
  useEffect(() => {
    const unsubscribe = store.subscribe(() => {
      const newValue = selector(store.getState());
      setValue(newValue);
    });
    return unsubscribe;
  }, []);
  return value;
}