Redux常见场景实践:如何优雅实现两个Reducer值的交换
Great question! I totally get why dispatching two separate actions feels clunky here—you’re breaking a single logical operation (swapping the two names) into two disconnected steps, which not only forces you to pass around values manually but also risks inconsistencies if something goes wrong mid-process. Let’s go through a few cleaner, more maintainable approaches.
Option 1: Handle the swap via a root reducer with a single action
Since your root reducer has access to the entire app state, it can coordinate the swap operation without requiring you to pass values through action payloads manually. Here’s how it works:
First, update your child reducers to respond to a SWAP_NAMES action:
// Boy reducer const boyReducer = (state = "Tom", action) => { switch(action.type) { case "BOY_EDIT": return action.value; case "SWAP_NAMES": return action.payload.newBoyName; default: return state; } }; // Girl reducer const girlReducer = (state = "Mary", action) => { switch(action.type) { case "GIRL_EDIT": return action.value; case "SWAP_NAMES": return action.payload.newGirlName; default: return state; } };
Then, modify your root reducer to orchestrate the swap when it receives the SWAP_NAMES action:
const rootReducer = (state, action) => { if (action.type === "SWAP_NAMES") { // Grab current values from the full state const currentBoy = state.boy; const currentGirl = state.girl; // Pass swapped values to each child reducer return { boy: boyReducer(state.boy, { type: "SWAP_NAMES", payload: { newBoyName: currentGirl } }), girl: girlReducer(state.girl, { type: "SWAP_NAMES", payload: { newGirlName: currentBoy } }) }; } // Delegate all other actions to child reducers as usual return { boy: boyReducer(state.boy, action), girl: girlReducer(state.girl, action) }; };
To trigger the swap, you only need to dispatch one action:
dispatch({ type: "SWAP_NAMES" });
This keeps the swap operation atomic—either both values update, or neither does—eliminating consistency risks.
Option 2: Use Redux Toolkit (Official Recommended Approach)
Redux Toolkit (RTK) simplifies cross-slice operations and reduces boilerplate. There are two sub-options here, depending on whether you want to keep boy and girl as separate slices.
Sub-option 2a: Keep separate slices with a thunk action
RTK’s thunks let you access the current state before dispatching an action, so you can grab the current names and pass them in a single swap action:
import { createSlice, configureStore, createAsyncThunk } from '@reduxjs/toolkit'; // Create boy slice const boySlice = createSlice({ name: 'boy', initialState: 'Tom', reducers: { editBoy: (state, action) => action.payload }, extraReducers: (builder) => { builder.addCase(swapNames.fulfilled, (state, action) => { return action.payload.newBoyName; }); } }); // Create girl slice const girlSlice = createSlice({ name: 'girl', initialState: 'Mary', reducers: { editGirl: (state, action) => action.payload }, extraReducers: (builder) => { builder.addCase(swapNames.fulfilled, (state, action) => { return action.payload.newGirlName; }); } }); // Define thunk to handle swap export const swapNames = createAsyncThunk( 'names/swap', async (_, { getState }) => { const { boy, girl } = getState(); return { newBoyName: girl, newGirlName: boy }; } ); // Configure store const store = configureStore({ reducer: { boy: boySlice.reducer, girl: girlSlice.reducer } });
To trigger the swap, just dispatch the thunk:
dispatch(swapNames());
Sub-option 2b: Combine related state into one slice (simplest!)
If boy and girl are part of the same logical feature (like a pair of users, a couple, etc.), merging them into a single slice eliminates cross-reducer coordination entirely:
import { createSlice, configureStore } from '@reduxjs/toolkit'; const namesSlice = createSlice({ name: 'names', initialState: { boy: 'Tom', girl: 'Mary' }, reducers: { editBoy: (state, action) => { state.boy = action.payload; }, editGirl: (state, action) => { state.girl = action.payload; }, swapNames: (state) => { // Swap values directly in the slice const temp = state.boy; state.boy = state.girl; state.girl = temp; } } }); export const { editBoy, editGirl, swapNames } = namesSlice.actions; const store = configureStore({ reducer: { names: namesSlice.reducer } });
Now swapping is as simple as:
dispatch(swapNames());
Final Takeaways
- If
boyandgirlbelong to the same logical domain, merging them into one slice is the cleanest, most maintainable solution. - If you need to keep them separate, using a root reducer handler or RTK thunk ensures your swap operation is atomic and avoids manual value passing.
内容的提问来源于stack exchange,提问作者Samuel Wong

