更新Redux存储内的嵌套对象

时间:2020-07-10 20:13:25

标签: javascript reactjs typescript redux react-redux

我试图切换数组中处于Redux状态的数组中的特定元素,然后使其反映使用该状态的组件中的更改。

我正在尝试将set中的完整状态更改为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 :(得分:0)

您的代码使用了返回值的map函数,但是,您的代码对此没有任何规定。此外,删除所有问号(?)。稍微修改一下代码,即可在下面看到一个可行的选择

...

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

答案 1 :(得分:0)

我认为问题在于您返回的是workout对象的相同实例。您应该在workouts中克隆“选定的锻炼”对象,以便react可以检测到某些更改。

map函数有2种用法未正确应用。 map应该用于生成由map函数返回的新数组。但是在两种情况下,您使用map忽略其结果。

您的代码应该做这样的事情(条件已经被反转以使问题代码更清晰):

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

这样的不变性。