使用 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,它不包括 useDispatch
和 useSelector
。
尽管如此,我真的很想在我的应用程序中使用这些挂钩(或类似的东西)。
因此,我在应用程序的顶层创建了一个包装器组件,我使用 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 团队因为性能问题而推迟实施 useDispatch
和 useSelector
很长一段时间。
非常感谢任何意见!
这将导致严重的性能问题。您的 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;
}
我正在做一个 React 项目,我只能使用 React Redux v5,它不包括 useDispatch
和 useSelector
。
尽管如此,我真的很想在我的应用程序中使用这些挂钩(或类似的东西)。
因此,我在应用程序的顶层创建了一个包装器组件,我使用 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 团队因为性能问题而推迟实施 useDispatch
和 useSelector
很长一段时间。
非常感谢任何意见!
这将导致严重的性能问题。您的 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;
}