Redux Toolkit中如何通过Action Type调用createSlice内的Action?
我在slice中定义的Action带有Type,想通过dispatch({type:'filter/category', payload:'change state'})这样的方式调用该Action,但尝试后状态并未改变。请问在Redux Toolkit中是否可以实现这一需求,还是需要迁移至Redux?
补充信息
- 已遵循Redux官方代码分割文档实现动态注入Reducer
- 有可运行仓库用于复现问题
相关代码
通用Slice创建逻辑
export interface GenericState<T> {} export const createGenericSlice = <T, Reducers extends SliceCaseReducers<GenericState<T>>>({ name = 'filter', initialState, reducers, }: { name: string; initialState: GenericState<T>; reducers: ValidateSliceCaseReducers<GenericState<T>, Reducers>; }) => { return createSlice({ name, initialState, reducers: { loadFilterOptions: (state, action: PayloadAction<any>) => { state = action.payload; return state; }, ...reducers, }, }); }; export const wrappedSlice = (reducers: any, initialState: any) => createGenericSlice({ name: 'filter', initialState: initialState as GenericState<[]>, reducers: { ...reducers, }, });
Reducer合并逻辑
import { combineReducers } from 'redux'; const initialReducers = {}; export const createReducer = (asyncReducers = {}) => { return combineReducers({ ...initialReducers, ...asyncReducers, }); }; export default createReducer;
Store配置
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit'; import createReducer from './rootReducer'; export function initStore() { return configureStore({ reducer: createReducer(), middleware: (getDefaultMiddleware) => [...getDefaultMiddleware()], }); } const store = initStore(); export function injectAsyncReducer(key: any, asyncReducer: any) { asyncReducer[key] = asyncReducer; store.replaceReducer(createReducer(asyncReducer)); } export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch; export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, RootState, unknown, Action<string>>; export default store;
动态注入Reducer的调用
injectAsyncReducer('test', this.reducerCollection.caseReducers.category);
当前核心疑问:如何通过Action Type正确dispatch Action,例如调用dispatch({type:"filter/load", payload:data})?
解决方案
Redux Toolkit完全支持通过Action Type直接dispatch Action,无需迁移至原生Redux。你的问题源于代码实现中的几个错误,修正后即可正常工作:
1. 修复动态注入Reducer的逻辑错误
当前injectAsyncReducer函数存在循环引用问题:asyncReducer[key] = asyncReducer会导致传入的reducer对象被修改为包含自身的循环结构,导致combineReducers无法正确处理。
修正后的代码:
// 维护已注入的reducers集合,避免重复注入 let injectedReducers: Record<string, any> = {}; export function injectAsyncReducer(key: string, asyncReducer: any) { if (!injectedReducers[key]) { injectedReducers[key] = asyncReducer; // 传入新的reducers集合给createReducer store.replaceReducer(createReducer(injectedReducers)); } }
2. 确保Action Type与slice定义完全匹配
createSlice会自动生成[sliceName]/[reducerName]格式的Action Type。例如你在slice中定义的reducer是loadFilterOptions,slice名称为filter,则对应的Action Type是filter/loadFilterOptions,而非filter/load——这是常见的错误点。
正确的dispatch写法应为:
dispatch({type:"filter/loadFilterOptions", payload:data})
3. 修正slice中的状态更新逻辑
虽然直接返回新状态在Immer中是允许的,但更规范的写法是直接修改状态(Immer会自动处理不可变更新),或者确保返回的是全新的状态对象:
loadFilterOptions: (state, action: PayloadAction<any>) => { // 直接返回新状态,确保覆盖原状态 return { ...action.payload }; }
4. 确保注入的是完整slice reducer而非单个caseReducer
当前代码中注入的是this.reducerCollection.caseReducers.category(单个case reducer),但combineReducers需要的是完整的slice reducer。正确的注入方式应该是传入整个slice的reducer:
// 假设你创建了一个categorySlice const categorySlice = wrappedSlice(/* 你的reducers */, /* 初始状态 */); // 注入完整的slice reducer injectAsyncReducer('test', categorySlice.reducer);
内容的提问来源于stack exchange,提问作者Witold

