Home > Back-end >  Redux: how to define logic between states?
Redux: how to define logic between states?

Time:10-20

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:

Edit redux-how-to-define-logic-between-states

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 }
  • Related