Обновить вложенный объект в хранилище Redux - PullRequest
0 голосов
/ 10 июля 2020

Я пытаюсь переключить конкретный элемент в массиве, который находится в массиве, находящемся в массиве в состоянии редукции, а затем заставить его отразить изменение в компоненте, который использует это состояние.

Я пытаюсь изменить полное состояние в значении true, но сейчас ничего не происходит с тем, как мой код написан.

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;

Ответы [ 2 ]

0 голосов
/ 11 июля 2020

Я считаю, что проблема в том, что вы возвращаете одни и те же экземпляры объектов workout. Вы должны клонировать объект «выбранная тренировка» в пределах workouts, чтобы система реагировала на какие-то изменения.

Есть 2 использования функции map, которые не применяются должным образом. map следует использовать для создания нового массива, возвращаемого функцией map. Но в двух случаях вы используете map, игнорируя его результат.

Ваш код должен делать что-то вроде этого (условие было инвертировано, чтобы прояснить проблему c код):

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
  };
});

Это делает вещи более подробными, но есть несколько библиотек, которые вы могли бы использовать, чтобы помочь вам с неизменяемостью, например Immer

0 голосов
/ 11 июля 2020

Ваш код использовал функцию карты, которая возвращает значение, однако в вашем коде это не предусмотрено. Также убраны все вопросительные знаки (?). Немного изменив код, вы увидите рабочий вариант ниже

...

// 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;
...