1

I am trying to toggle a particular element in an array that's in an array that's in an array in redux state, and then have it reflect the change in the component that is using that state.

I am trying to change the complete state in set to true but nothing happens with the way my code is written right now.

export const initialState: WorkoutList = {
  workouts: [
    {
      id: "newId",
      name: "Leg Day",
      date: new Date(),
      duration: 60,
      started: false,
      completed: false,
      exercises: [
        {
          id: "e1",
          name: "Squats",
          completed: false,
          sets: [
            {
              id: "e1s1",
              reps: 12,
              weight: 60,
              completed: false,
            },
            {
              id: "e1s2",
              reps: 8,
              weight: 60,
              completed: false,
            },
            {
              id: "e1s3",
              reps: 10,
              weight: 60,
              completed: false,
            },
          ],
        },
        {
          id: "e2",
          name: "Leg Press",
          completed: false,
          sets: [
            {
              id: "e2s1",
              reps: 12,
              weight: 60,
              completed: false,
            },
            {
              id: "e2s2",
              reps: 8,
              weight: 60,
              completed: false,
            },
            {
              id: "e2s3",
              reps: 10,
              weight: 60,
              completed: false,
            },
          ],
        },
      ],
    },
  ],
  selectedWorkout: {
    id: "newId",
    name: "Leg Day",
    date: new Date(),
    duration: 60,
    started: false,
    completed: false,
    exercises: [
      {
        id: "e1",
        name: "Squats",
        completed: false,
        sets: [
          {
            id: "e1s1",
            reps: 12,
            weight: 60,
            completed: false,
          },
          {
            id: "e1s2",
            reps: 8,
            weight: 60,
            completed: false,
          },
          {
            id: "e1s3",
            reps: 10,
            weight: 60,
            completed: false,
          },
        ],
      },
      {
        id: "e2",
        name: "Leg Press",
        completed: false,
        sets: [
          {
            id: "e2s1",
            reps: 12,
            weight: 60,
            completed: false,
          },
          {
            id: "e2s2",
            reps: 8,
            weight: 60,
            completed: false,
          },
          {
            id: "e2s3",
            reps: 10,
            weight: 60,
            completed: false,
          },
        ],
      },
    ],
  },
  selectedExercise: {
    id: "e1",
    name: "Squats",
    completed: false,
    sets: [
      {
        id: "e1s1",
        reps: 12,
        weight: 60,
        completed: false,
      },
      {
        id: "e1s2",
        reps: 8,
        weight: 60,
        completed: false,
      },
      {
        id: "e1s3",
        reps: 10,
        weight: 60,
        completed: false,
      },
    ],
  },
};

const workoutListReducer = (
  state = initialState,
  action: WorkoutActionTypes
): WorkoutList => {
  switch (action.type) {

    case TOGGLE_SET_COMPLETE:
      const updatedWorkoutState = state.workouts.map((workout) => {
        if (workout.id === state.selectedWorkout?.id) {
          workout.exercises?.map((exercise) => {
            if (exercise.id === state.selectedExercise?.id) {
              exercise.sets?.map((set) => {
                if (set.id === action.payload.id) {
                  // console.log(set, !set.completed);
                  return { ...set, completed: true };
                }
                return set;
              });
            }
            return exercise;
          });
        }
        return workout;
      });
      const updatedSelectedWorkoutState = updatedWorkoutState.find(
        (workout) => workout.id === state.selectedWorkout?.id
      );
      const updatedSelectedExerciseState = updatedSelectedWorkoutState?.exercises?.find(
        (workout) => workout.id === state.selectedExercise?.id
      );

      console.log(updatedSelectedExerciseState);
      return {
        ...state,
        workouts: updatedWorkoutState,
        selectedWorkout: updatedSelectedWorkoutState,
        selectedExercise: updatedSelectedExerciseState,
      };

    default:
      return state;
  }
};

export default workoutListReducer;

1

2 Answers 2

1

I believe the issue is that you're returning the same instances of workout objects. You should be cloning the "selected workout" object within the workouts so react can detect that something has been changed.

There are 2 usages of the map function that are not being properly applied. The map should be used to generate a new array that is returned by the map function. But in 2 cases you use the map ignoring its result.

Your code should be doing something like this (the condition has been inverted to make the problematic code clearer):

const updatedWorkoutState = state.workouts.map((workout) => {
  // Happy path, returns same instance
  if (workout.id !== state.selectedWorkout?.id) {
    return workout;
  }
  
  // Obtain the updated exercises for the selected workout
  const updatedExercises = workout.exercises?.map((exercise) => {

    // just return the exercise we don't care about as-is:
    if (exercise.id !== state.selectedExercise?.id) {
      return exercise;
    }

    // get the updated array of sets
    const updatedExerciseSets = exercise.sets?.map((set) => {
      if (set.id === action.payload.id) {
        // console.log(set, !set.completed);
        return { ...set, completed: true };
      }
      return set;
    });

    // Clone the selected exercise setting the updated array of sets:
    return {
      ...exercise,
      sets: updatedExerciseSets
    };
  });

  // Now the selected workout should be cloned and its exercises array replaced
  // with the updated one:
  return {
    ...workout,
    exercises: updatedExercises
  };
});

This makes things a bit more verbose, but there are a few libraries that you could use to help you with the immutability like Immer

Sign up to request clarification or add additional context in comments.

Comments

0

Your code used the map function which returns a value, however, your code has no provision for this. Also, removed all the question marks(?). With just a little tweak to your code see a working option below

...

// The initialState part of your code goes here
...

const workoutListReducer = (
  state = initialState,
  action: WorkoutActionTypes
): WorkoutList => {
  switch (action.type) {

    case TOGGLE_SET_COMPLETE:
      // create a copy of workout state 
      const tempWorkoutState = [...state.workouts]; 
      const updatedWorkoutState = tempWorkoutState.map((workout) => {
        if (workout.id === state.selectedWorkout?.id) {

          // Update workout.exercises with the result of the map function
          workout.exercises = workout.exercises.map((exercise) => {
            if (exercise.id === state.selectedExercise.id) {

              // Update exercise.sets with the result of the map function
              exercise.sets = exercise.sets.map((set) => {
                if (set.id === action.payload.id) {
                  // console.log(set, !set.completed);
                  return { ...set, completed: true };
                }
                return set;
              });
            }
            return exercise;
          });
        }
        return workout;
      });
      const updatedSelectedWorkoutState = updatedWorkoutState.find(
        (workout) => workout.id === state.selectedWorkout.id
      );
      const updatedSelectedExerciseState = updatedSelectedWorkoutState.exercises.find(
        (workout) => workout.id === state.selectedExercise.id
      );

      console.log(updatedSelectedExerciseState);
      return {
        ...state,
        workouts: updatedWorkoutState,
        selectedWorkout: updatedSelectedWorkoutState,
        selectedExercise: updatedSelectedExerciseState,
      };

    default:
      return state;
  }
};

export default workoutListReducer;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.