简体   繁体   中英

Uncaught Invariant Violation: A state mutation was detected between dispatches when using arrayMove

I'm trying to build a React/Redux app and I am a beginner. I want to use the React Sortable HOC to have a rearrangeable list but I cannot get the new arrangement to stick. I have a functional component where I get the list of items. The item structure is like this: items [ {name, courseList}, {name, courseList}, ...].

To populate the table I make an api call and update the prop variable using MapStateToProps. Here's a bit of code:

function CoursesPage({
  student,
  studentCourses,
  loadStudentCourses,
  updateCoursesOrder,
  ...props
}) {
  useEffect(() => {
    if (studentCourses.length === 0) {
      loadStudentCourses(student.id).catch((error) => {
        alert("Loading student courses failed" + error);
      });
    }
  }, []);
...
}

and this is the mapStateToProps function:

function mapStateToProps(state) {
  return {
    student: state.student,
    studentCourses: state.studentCourses,
  };
}

This bit works fine, and everything appears. The problem is when I try to rearrange and save it in onSortEnd function:

  function onSortEnd({ oldIndex, newIndex, collection }) {
    const newCollections = [...studentCourses];

    newCollections[collection].courseList = arrayMove(
      newCollections[collection].courseList,
      oldIndex,
      newIndex
    );

    updateCoursesOrder(newCollections);
  }

The newCollection gets populated and modified correctly and I am calling updateCoursesOrder with the items correctly arranged. That function is an action that calls a dispatch.

export function updateCoursesOrder(courseList) {
  return function (dispatch) {
    dispatch(setNewCourseOrderSuccess(courseList));
  };
}

export function setNewCourseOrderSuccess(studentCourses) {
  return { type: types.SET_NEW_COURSE_ORDER, studentCourses };
}

Using the debugger I can see that the code is running well up till the dispatch return from setNewCourseOrderSuccess().

This should go to the reducer, but instead throws an error: Uncaught Invariant Violation: A state mutation was detected between dispatches.

This is how the reducer looks like:

export default function courseReducer(
  state = initialState.studentCourses,
  action
) {
  switch (action.type) {
    case type.LOAD_STUDENT_COURSES_SUCCESS:
      return action.studentCourses;
    case type.SET_NEW_COURSE_ORDER:
      return {
        ...state,
        studentCourses: action.payload,
      };
    default:
      return state;
  }
}

How could I solve this issue? Thank you very much!

With this:

const newCollections = [...studentCourses];

newCollections[collection].courseList =

Although newCollections is a new array, it's only a shallow copy of the studentCourses ; the array items are not clones, they're references to current objects in state. So, assigning to a courseList property of one of those objects is mutating the state.

Replace the object at the index instead of mutating it:

function onSortEnd({ oldIndex, newIndex, collection }) {
    updateCoursesOrder(studentCourses.map((item, i) => i !== collection ? item : ({
        ...item,
        courseList: arrayMove(item.courseList, oldIndex, newIndex)
    })));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM