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:
- add new exercises to the specific workout from the list of workouts
- 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;
}
}