Reputation: 181
I am trying to access state of another slice in reducer of another slice using getState() method but looks like this is not allowed and hence the web application breaks.
Does anyone know whats the recommended way to access state of a slice inside reducer of another slice? I need this for my web application.
Thanks in advance for your any help
Upvotes: 12
Views: 13202
Reputation: 6163
According to the Redux docs, there are 3 different approaches you could use:
Many users later want to try to share data between two reducers, but find that combineReducers does not allow them to do so. There are several approaches that can be used:
- If a reducer needs to know data from another slice of state, the state tree shape may need to be reorganized so that a single reducer is handling more of the data.
- You may need to write some custom functions for handling some of these actions. This may require replacing combineReducers with your own top-level reducer function. You can also use a utility such as reduce-reducers to run combineReducers to handle most actions, but also run a more specialized reducer for specific actions that cross state slices.
- Middleware with async logic such as redux-thunk have access to the entire state through getState(). An action creator can retrieve additional data from the state and put it in an action, so that each reducer has enough information to update its own state slice.
Example
Lets assume we have the following slices:
const namesSlice = createSlice({
name: "Names",
initialState: {
value: [],
name: "Names"
},
reducers: {
...
}
};
const counterSlice = createSlice({
name: "Counter",
initialState: {
value: 0,
name: "Names"
},
reducers: {
...
}
};
const reducer = combineReducers({
counter: counterReducer,
names: namesReducer
});
And we want to define an action addName
which will add a name (entered by the user) into the names
array, but will also append the current value of the counter
state onto the name before adding it.
Option 1: Restructure Slices
This involves merging the 2 slices into a single slice, so you'd end up with something like:
const namesSlice = createSlice({
name: "NamesAndCounter",
initialState: {
value: {
names: [],
counter: 0
},
name: "NamesAndCounter"
},
...
};
which would allow you to access both names
and counter
in the reducer.
If you don't want to restructure your state/slices, then there are options 2 and 3:
Option 2: Use reduce-reducers
A third party library reduce-reducers can be used
Here you would define a cross slice reducer which is capable of accessing both the counter
and names
slices:
export const crossSliceReducer = (state, action) => {
if (action.type === "CROSS_SLICE_ACTION") {
const newName = action.payload + state.counter.value;
const namesState = state.names;
state = {
...state,
names: { ...namesState, value: [...state.names.value, newName] }
};
}
return state;
};
// Combine reducers
const reducer = combineReducers({
counter: counterReducer,
names: namesReducer
});
// Add the cross-slice reducer to the root reducer
const rootReducer = reduceReducers(reducer, crossSliceReducer);
// Create store
const store = configureStore({
reducer: rootReducer
});
Then you can dispatch the following action to invoke the reducer:
dispatch({ type: "CROSS_SLICE_ACTION", payload: name });
Note: The reduce-reducers
library is no longer being maintained
Option 3: Use Thunks
Using thunks (meant for async actions like calling an API) allows you to get at the entire state. You can define a thunk that references the getState
function that allows you to get at any slice in the global state:
export const addWithThunk = createAsyncThunk(
"names/addWithThunk",
async (name, { getState }) => {
return name + getState().counter.value;
}
);
Thunks are defined in the extraReducers
property of the argument passed to createSlice()
:
extraReducers: (builder) => {
builder.addCase(addWithThunk.fulfilled, (state, action) => {
state.value.push(action.payload);
});
}
And can be invoked in the same way you'd invoke a plain action:
dispatch(addWithThunk(name));
There's a CodeSandbox demo showing options 2 and 3. When you add a name using one of the Submit
buttons, it will access the counter
state and append the current value of the counter to the name you input before adding the name into the names
state.
Upvotes: 18