如何在Reducer中使用Context中的配置内容?
核心结论:Reducer里无法直接访问React Context
Redux的Reducer是纯函数,仅接收当前slice的state和触发的action作为参数,和React的Context机制完全隔离,没办法直接在Reducer里读取Context内容。
针对你的场景(静态配置、不想在action里传大体积配置),给你几个可行的解决方案:
方案1:把配置存入Redux Store(最推荐)
既然配置是静态的,加载后基本不会变,直接把它存入Redux的独立slice里,这样所有Reducer/Thunk都能通过getState()获取到配置,不用每次action都带配置。
步骤1:创建配置Slice
import { createSlice } from '@reduxjs/toolkit'; const initialState = {}; const configSlice = createSlice({ name: 'config', initialState, reducers: { setConfig: (state, action) => { // 这里可以直接做配置解析逻辑,代替原来的configurationReducer return { ...state, ...action.payload }; }, }, }); export const { setConfig } = configSlice.actions; export default configSlice.reducer;
步骤2:修改ConfigurationProvider,把配置存入Redux
import { useDispatch, useSelector } from 'react-redux'; import { setConfig } from './configSlice'; // 原来的配置解析逻辑抽成单独函数 const parseConfiguration = (rawData) => { // 这里写你原来在configurationReducer里的解析代码 return parsedConfig; }; export const ConfigurationProvider: FC = ({ children }) => { const { data } = api.useGetConfigurationQuery(); const dispatch = useDispatch(); const config = useSelector(state => state.config); useEffect(() => { if (data) { const parsedConfig = parseConfiguration(data); dispatch(setConfig(parsedConfig)); } }, [data, dispatch]); // 保留Context供组件用useConfiguration()获取 const value = useMemo(() => ({ configuration: config }), [config]); return ( <ConfigurationContext.Provider value={value}> {children} </ConfigurationContext.Provider> ); };
步骤3:在业务逻辑里获取配置(用Thunk或Listener Middleware)
因为Reducer只能访问自己slice的state,所以需要用Thunk或监听中间件来获取全局state里的配置:
用Thunk处理业务逻辑
import { createAsyncThunk } from '@reduxjs/toolkit'; // 封装带配置的addItem逻辑 export const addItemWithConfig = createAsyncThunk( 'mySlice/addItemWithConfig', async (item, { getState }) => { // 从全局state拿配置 const config = getState().config; // 用配置处理item const processedItem = handleItemWithConfig(item, config); return processedItem; } ); // 在mySlice里监听这个Thunk的完成事件 const mySlice = createSlice({ name: 'mySlice', initialState, reducers: {}, extraReducers: (builder) => { builder.addCase(addItemWithConfig.fulfilled, (state, action) => { state.items.push(action.payload); }); }, });
组件里直接调用:dispatch(addItemWithConfig(item)),不用手动传配置。
用Listener Middleware监听原Action
如果不想修改原Action,可以用RTK的监听中间件,拦截原quoteAction.addItem,获取配置后再更新业务state:
import { createListenerMiddleware } from '@reduxjs/toolkit'; const listenerMiddleware = createListenerMiddleware(); // 监听addItem动作 listenerMiddleware.startListening({ actionCreator: quoteAction.addItem, effect: (action, listenerApi) => { // 获取全局state里的配置 const config = listenerApi.getState().config; const processedItem = handleItemWithConfig(action.payload, config); // 触发业务slice的更新动作 listenerApi.dispatch(mySlice.actions.addProcessedItem(processedItem)); }, }); // 把中间件加到Store里 export const store = configureStore({ reducer: { config: configSlice.reducer, mySlice: mySlice.reducer, // 其他slice... }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().prepend(listenerMiddleware.middleware), });
方案2:封装带配置的自定义Dispatch
如果不想把配置存入Redux,可以在Context里封装一个自动附加配置的dispatch函数,组件用这个自定义dispatch发动作,Reducer就能从action的meta里拿到配置:
修改ConfigurationProvider
export const ConfigurationProvider: FC = ({ children }) => { const { data } = api.useGetConfigurationQuery(); const [state, dispatch] = useReducer(configurationReducer, initialState); const reduxDispatch = useDispatch(); // 如果用Redux的dispatch的话 useEffect(() => { if (data) { dispatch({ type: 'parse', payload: data }); } }, [data]); // 封装自定义dispatch,自动把配置加到action的meta里 const customDispatch = useCallback( (action) => { reduxDispatch({ ...action, meta: { ...action.meta, config: state }, }); }, [state, reduxDispatch] ); const value = useMemo( () => ({ configuration: state, dispatch: customDispatch }), [state, customDispatch] ); return ( <ConfigurationContext.Provider value={value}> {children} </ConfigurationContext.Provider> ); };
在Reducer里获取
const mySlice = createSlice({ name: 'mySlice', initialState, reducers: {}, extraReducers: (builder) => { builder.addCase(quoteAction.addItem, (state, action) => { // 从action.meta里拿配置 const config = action.meta.config; // 处理业务逻辑 }); }, });
这个方案的缺点是:每次action都会带上配置,还是会增加action的体积,而且Storybook里如果要模拟动作,还是需要手动加meta里的配置,所以不如方案1实用。
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

