如何将Redux Store切片归组到同一属性下?
Redux Toolkit + TypeScript:分组切片时的类型兼容问题
我是Redux/Redux Toolkit新手,正在尝试组织Store状态,希望将多个切片归组到同一属性下,但遇到TypeScript类型兼容问题。
期望实现的效果
const fooSlice = createSlice(...); const barSlice = createSlice(...); const eggzSlice = createSlice(...); const store = configureStore({ reducer: { myGroupedSlice: { // 将这些归组到同一属性下 foo: fooSlice.reducer, bar: barSlice.reducer, }, eggz: eggzSlice.reducer } }); // 类型化钩子(按文档实现) type RootState = ReturnType<typeof store.getState>; export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector; type AppDispatch = typeof store.dispatch; export const useAppDispatch: () => AppDispatch = useDispatch; // 期望能这样使用选择器 const barValue = useAppSelector((state) => state.myGroupedSlice.bar); const eggzValue = useAppSelector((state) => state.eggz);
尝试过的方案及问题
- 将
combineReducers作为myGroupedSlice的值:
添加myGroupedSlice: combineReducers({ foo: fooSlice.reducer, bar: barSlice.reducer})eggzreducer时TypeScript报错,且两种情况下useAppSelector的类型都变为any。 - 用
combineReducers包裹所有reducer:
TypeScript无即时错误,但reducer: combineReducers({ myGroupedSlice: combineReducers(...), eggz: eggzSlice.reducer})useAppSelector类型仍变为any。
补充说明
实验后发现,问题仅在添加中间件时出现,此时创建Store的代码如下:
export const listenerMiddleware = createListenerMiddleware(); const store = configureStore({ reducer: { myGroupedSlice: combineReducers({ foo: fooSlice.reducer, bar: barSlice.reducer, }), eggz: eggzSlice.reducer, }, /* 中间件`getDefaultMiddleware`似乎将`myGroupedSlice`类型化为unknown,导致类型冲突 */ middleware: (getDefaultMiddleware) => getDefaultMiddleware().prepend(listenerMiddleware.middleware), });
解决方案
问题出在createListenerMiddleware的类型推导和combineReducers的结合上,以下是几种可行的修复方式:
方案1:直接嵌套reducer(无需combineReducers)
Redux Toolkit的configureStore支持直接嵌套reducer对象,无需手动调用combineReducers,这种方式类型推导最准确:
const store = configureStore({ reducer: { myGroupedSlice: { foo: fooSlice.reducer, bar: barSlice.reducer, }, eggz: eggzSlice.reducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().prepend(listenerMiddleware.middleware), });
这种写法下,TypeScript能自动正确推导RootState的类型,useAppSelector也能正常获取类型提示。
方案2:给combineReducers显式指定泛型
如果必须使用combineReducers,可以手动定义分组状态类型并传递给泛型参数:
// 定义分组切片的状态类型 type MyGroupedState = { foo: ReturnType<typeof fooSlice.reducer>; bar: ReturnType<typeof barSlice.reducer>; }; const store = configureStore({ reducer: { myGroupedSlice: combineReducers<MyGroupedState>({ foo: fooSlice.reducer, bar: barSlice.reducer, }), eggz: eggzSlice.reducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().prepend(listenerMiddleware.middleware), }); // 后续RootState和钩子定义保持不变 type RootState = ReturnType<typeof store.getState>; export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
方案3:给listenerMiddleware指定RootState泛型
显式给createListenerMiddleware传递RootState类型,帮助TypeScript完成推导:
// 先定义完整的RootState类型 type RootState = { myGroupedSlice: { foo: ReturnType<typeof fooSlice.reducer>; bar: ReturnType<typeof barSlice.reducer>; }; eggz: ReturnType<typeof eggzSlice.reducer>; }; // 给listenerMiddleware指定泛型 export const listenerMiddleware = createListenerMiddleware<RootState>(); // 创建store const store = configureStore({ reducer: { myGroupedSlice: combineReducers({ foo: fooSlice.reducer, bar: barSlice.reducer, }), eggz: eggzSlice.reducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().prepend(listenerMiddleware.middleware), });
关键原因
当添加listenerMiddleware时,TypeScript需要明确知道RootState的类型才能正确推导中间件的类型。手动嵌套reducer时,configureStore能自动完成类型推导;而使用combineReducers时,类型推导会出现延迟,导致中间件无法正确识别状态类型,最终让useAppSelector的类型变为any或出现冲突。
内容的提问来源于stack exchange,提问作者Pedro Perpétua
相关产品推荐
相关产品推荐

