Given the following 2 states as an example:
state_a: 'mode_1' | 'mode_2' | 'mode_3'
state_b: boolean
state_b
can only be false
whenever state_a === 'mode_2'
, and when state_a !== 'mode_2
', state_b
should return to the previous value (i.e. it's limits opened up).
What's the usual practice/style to define such a behavior in Redux?
CodePudding user response:
If I'm understanding your question correctly you want, from the app's perspective, state_b
to always be false when state_a === 'mode_2'
, and when state_a !== 'mode_2'
state_b
is whatever is stored in state.
On the surface your questions is posed in such a way that it sounds like you want to implement some logic in the reducer functions that coordinates the values between these two states when either of them update. While you could do this I suspect a simpler solution is to derive the provided state when consuming it. In other words, use a selector function to compute derived state, when the state_a
value is "mode_2"
then the selector function selecting state_b
returns false, otherwise it returns the actual state_b
state value.
Example:
import {
combineReducers,
configureStore,
createSlice,
createSelector
} from "@reduxjs/toolkit";
import { Provider, useDispatch, useSelector } from "react-redux";
const MODES = {
mode_1: "mode_1",
mode_2: "mode_2",
mode_3: "mode_3"
};
const state = createSlice({
initialState: {
state_a: MODES.mode_1,
state_b: true
},
name: "state",
reducers: {
setMode: (state, action) => {
state.state_a = action.payload;
},
toggleB: (state, action) => {
state.state_b = !state.state_b;
}
}
});
const { setMode, toggleB } = state.actions;
const selectState = (state) => state.state;
const select_a = createSelector([selectState], (state) => state.state_a);
const select_b = createSelector([select_a, selectState], (state_a, state) =>
state_a !== MODES.mode_2 ? state.state_b : false
);
Selecting the state in a component:
const state_a = useSelector(select_a);
const state_b = useSelector(select_b);
Demo:
CodePudding user response:
You are looking for Sharing data between slice reducers,
import { combineReducers, createStore } from "redux";
type StateA = 'mode_1' | 'mode_2' | 'mode_3';
const stateAReducer = (state: StateA = 'mode_1', action) => {
switch (action.type) {
case 'SWITCH_MODE':
return action.payload;
default:
return state;
}
}
type StateB = boolean;
const stateBReducer = (state: StateB = true, action) => {
return state;
}
const combinedReducer = combineReducers({
stateA: stateAReducer,
stateB: stateBReducer
});
type CrossSliceState = ReturnType<typeof combinedReducer>;
const crossSliceReducer = (state: CrossSliceState, action) => {
switch (action.type) {
case 'SWITCH_MODE':
const nextStateA = stateAReducer(state.stateA, action);
return {
stateA: nextStateA,
stateB: nextStateA === 'mode_2' ? false : state.stateB
}
default:
return state;
}
}
const rootReducer = (state, action) => {
const intermediateState = combinedReducer(state, action);
const finalState = crossSliceReducer(intermediateState, action);
return finalState
}
const store = createStore(rootReducer);
store.subscribe(() => {
console.log(store.getState());
})
store.dispatch({ type: 'SWITCH_MODE', payload: 'mode_1' });
store.dispatch({ type: 'SWITCH_MODE', payload: 'mode_2' });
store.dispatch({ type: 'SWITCH_MODE', payload: 'mode_1' });
Output:
{ stateA: 'mode_1', stateB: true }
{ stateA: 'mode_2', stateB: false }
{ stateA: 'mode_1', stateB: false }