Home > Blockchain >  NgRx add an object to the list of objects
NgRx add an object to the list of objects

Time:12-25

I have a state with the following structure. It contains a list of Workouts and each workout has a list of exercises related to this workout. I want to be able to do 2 things:

  1. add new exercises to the specific workout from the list of workouts
  2. delete a specific exercise from the specific workout

E.g. In my UI I can add new exercises to Workout with the name Day 2. So my action payload gets 2 params: workout index (so I can find it later in the state) and exercise that should be added to/deleted from the list of exercises of the specific workout.

State

state = {
 workouts: [
  {
    name: "Day 1",
    completed: false,
    exercises: [{
      name: "push-up",
      completed: false
    },
    {
      name: "running",
      completed: false
    }]
  },
  {
    name: "Day 2",
    completed: false,
    exercises: [{
      name: "push-up",
      completed: false
    }]
  },
  {
    name: "Day 3",
    completed: false,
    exercises: [{
      name: "running",
      completed: false
    }]
   }]
}

Actions

export class AddExercise implements Action {
  readonly type = ADD_EXERCISE
  constructor(public payload: {index: number, exercise: Exercise}) {}
}

export class DeleteExercise implements Action {
  readonly type = DELETE_EXERCISE
  constructor(public payload: {index: number, exercise: Exercise}) {}
}

And I am stuck on the reducer. Can you advise how it should be done properly? This is how it looks right now (not finalized yet):

Reducer

export function workoutsReducer(state = initialState, action: WorkoutActions.Actions) {
  switch(action.type) {
    case WorkoutActions.ADD_EXERCISE:
       const workout = state.workouts[action.payload.index];
       const updatedExercises = [
         ...workout.exercises,
         action.payload.exercise
       ]
       return {
         ...state,
          workouts: [...state.workouts, ]
       }
    return {};
    default:
      return state;
  }
}

Thank you!

CodePudding user response:

Please, try something like the following (I included comments within the code, I hope it makes it clear):

export function workoutsReducer(state = initialState, action: WorkoutActions.Actions) {
  switch(action.type) {
    case WorkoutActions.ADD_EXERCISE:
       // You can take advantage of the fact that array map receives 
       // the index as the second argument
       // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
       const workouts = state.workouts.map((workout, index) => {
         if (index != action.payload.index) {
           return workout;
         }

         // If it is the affected workout, add the new exercise
         const exercises = [
           ...workout.exercises,
           action.payload.exercise
         ]

         return { ...workout, exercises }
       })
       
       // return the updated state
       return {
         ...state,
         workouts
       }

    case WorkoutActions.DELETE_EXERCISE:
       // very similar to the previous use case
       const workouts = state.workouts.map((workout, index) => {
         if (index != action.payload.index) {
           return workout;
         }

         // the new exercises array will be composed by every previous
         // exercise except the provided one. I compared by name, 
         // I don't know if it is accurate. Please, modify it as you need to
         const exercises = workout.exercises.filter((exercise) => exercise.name !== action.payload.exercise.name);

         return { ...workout, exercises }
       })
       
       // return the new state 
       return {
         ...state,
         workouts
       }

    default:
      return state;
  }
}
  • Related