0

I have individual users for which an enthusiasm value can be in/decremented. After this I want updated the overall enthusiasm level (globalEnthusiasmLevel) as wel, the sum of individual enthusiasm integers (per user).

I have everything I need to know to update the overall value at the time the individual value is being processed (action > reducer), however I want it to be a separate action for the sake of practice purposes.

export function enthusiasm(state: StoreState, action: UsersAction): StoreState {

  let globalEnthusiasmLevel: number;

  switch (action.type) {

    case INCREMENT_ENTHUSIASM:

      state.users = state.users.map((user: UserType) => {

        if (user._id === action.payload._id) {
          user.enthusiasmLevel = user.enthusiasmLevel + 1;
        }

        return user;
      });

      globalEnthusiasmLevel = state.globalEnthusiasmLevel + 1;
      return { ...state, globalEnthusiasmLevel };

    case DECREMENT_ENTHUSIASM:

      const users: UserType[] = state.users.map((user: UserType) => {

        if (user._id === action.payload._id) {
          globalEnthusiasmLevel = (user.enthusiasmLevel > 0 ) ? state.globalEnthusiasmLevel - 1 : state.globalEnthusiasmLevel;

          user.enthusiasmLevel = user.enthusiasmLevel - 1;
          user.enthusiasmLevel = Math.max(0, user.enthusiasmLevel);
        }

        return user;
      });

      return { ...state, ...users, globalEnthusiasmLevel };

    case STORE_USERS:
      return { ...state, users: action.payload };

    case SET_GLOBAL_ENTHUSIASM:
      return { ...state, globalEnthusiasmLevel: action.payload };

    default:
      return state;
  } 
  1. What would be the best approach to dispatch an action after an acion?
  2. is it wise to separate STORE_USERS and SET_GLOBAL_ENTHUSIASM into a different reducer?
San Jay Falcon
  • 993
  • 1
  • 9
  • 20

1 Answers1

1

1 - You can write a middleware to handle side-effect of action type INCREMENT_ENTHUSIASM and DECREMENT_ENTHUSIASM. Following example is written in ES6, so you would need to translate to Typescript.

const middleware = store => next => action => {
  next(action);
  switch (action.type) {
    case INCREMENT_ENTHUSIASM:
      store.dispatch({
        type: INCREMENT_GLOBAL_ENTHUSIASM // increment global value
      });
      break;
    case DECREMENT_ENTHUSIASM:
      store.dispatch({
        type: DECREMENT_GLOBAL_ENTHUSIASM // decrement global value
      });
      break;
    default:
      break;
  }
}

...
import { createStore, combineReducers, applyMiddleware } from 'redux';

const store = createStore(
  combineReducers({
    enthusiasm
  }),
  applyMiddleware(middleware)
);

But if globalEnthusiasmLevel can be calculated from all users' enthusiasm levels, then how about not storing them in store, but calculated it in mapStateToProps when you need to use in React component instead? It will be easier for you.

2 - If you intend to store globalEnthusiasmLevel in a different reducer, then yes it should be. But see my point above about not storing but calculating it instead.

blaz
  • 3,981
  • 22
  • 37
  • 1. this works like a charm. As i understand the middleware functionality is executed AFTER the initial action right? So the later always operates on the latest/current state. 2. what is best practice here? For instance i'm storing users coming from the backend in the same reducer as well (STORE_USERS). This doesnt feel right, guess it should be seperated? – San Jay Falcon Apr 30 '19 at 12:37
  • about your remark `But if globalEnthusiasmLevel can be calculated from all users' enthusiasm levels, then how about not storing them in store, but calculated it in mapStateToProps when you need to use in React component instead?` >> true, but this is for practice and understanding purposes. – San Jay Falcon Apr 30 '19 at 12:52
  • @SanJayFalcon about your 2nd question, it's purely personal preference imo. Personally I will normalize API data until it's flat like DB tables, and create reducers for every "table", i.e. array of data, and create distinctive action types for each reducer. But this guy has a different opinion and I also find it valid: https://stackoverflow.com/a/35494345/2404452. So in the end, just structure your store how you find it convenient and make sense. – blaz Apr 30 '19 at 13:07
  • @SanJayFalcon about 1 - since I put `next(action)` first, the middleware will wait until 1st action is done to continue. You can use middleware to execute something before, modify action or terminate action - it is that flexible. – blaz Apr 30 '19 at 13:11
  • much obliged mister! very helpful – San Jay Falcon May 01 '19 at 15:10