Home > OS >  Redux: A state mutation was detected inside a dispatch
Redux: A state mutation was detected inside a dispatch

Time:03-16

I realise there are probably a million and one different causes for this error but I am struggling to find the cause of mine. I am relatively new to Redux and state handling in the Reducer and have learned from a few examples online to come up with the code sample below which is called within the Reducer:

const updateTripsWhereVenueDeleted = (state, action) => {
  debugger;
  const deletedVenueId = action.venue.Id;
  const originalTrips = [...state];

  const newTrips = originalTrips.map((trip) => {
    if (trip.VenueId === deletedVenueId) {
      trip.VenueId = 0;
      trip.VenuePegId = 0;
    }
    return trip;
  });
  debugger;
  return [...newTrips];
};

I have state which is an array of Trips:

enter image description here

And Action contains a 'Venue' record. Basically, the Venue being passed was deleted and I want to update the relevant fields on all Trips that reference the deleted Venue.

When I run the above code in the Reducer things seem to go in the right direction until the browser crashes with the following error:

Unhandled Rejection (Invariant Violation): A state mutation was detected inside a dispatch, in the path: trips.0.VenueId. Take a look at the reducer(s) handling the action {"type":"UPDATE_TRIPS_VENUE_DELETED","venue": {.... the object}

UPDATE_TRIPS_VENUE_DELETED is the action.type that calls my method above.

Is there an obvious mis-use of handling (spread) arrays in a state or something. I feel this should be an easy thing to do but nothing I have tried has so far worked correctly.

CodePudding user response:

Spreading an object or array ([...state] here) does not break nested references. So you are mutating state by not making a copy of the nested object within your map -- trip.VenueId = 0;.

This leads to another observation, map returns a new array, so this negates the need to use originalTrips altogether. It is just as safe to do state.map(). The final spread [...newTrips] is definitely unnecessary as well.

To fix your mutation create a clone of the objects to be updated:

const updateTripsWhereVenueDeleted = (state, action) => {

  const deletedVenueId = action.venue.Id;

  const newTrips = state.map((trip) => {
    if (trip.VenueId === deletedVenueId) {
      // Copy other trip values into NEW object and return it
      return { ...trip, VenueId: 0, VenuePegId: 0 };
    }
    // If not matched above, just return the old trip
    return trip;
  });

  return newTrips;
};

CodePudding user response:

Spread operator ... only does shallow copy. If the array is nested or multi-dimensional, it won't work.

There are plenty of way we can do this, below are some of them

1] Deep copy using JSON

  const originalTrips = JSON.parse(JSON.stringify(state));;

2]

const newTrips = originalTrips.map((trip) => {
  let newTrip = Object.assign({},trip);
    if (newTrip.VenueId === deletedVenueId) {
      newTrip.VenueId = 0;
      newTrip.VenuePegId = 0;
    }
    return newTrip;
  });
  • Related