I have a React app which uses Redux for state management. The initial structure of the store is as follows:

const initialState = fromJS({
  visitorRecords: [],
  approvedBookingId: null,
});

At some point my visitorRecords becomes something similar to this:

[  
   Map {  
      "id":"1556008644569",
      "expectedArrival":"Thu, 25 Apr 2019 08:31:00 GMT",
      "lastName":"Vath",
      "notes":"",
      "actualDeparture":"",
      "actualArrival":"",
      "visitor":"Sven Vath",
      "status":2,
      "cardNumber":2,
      "employeeBeingVisited":"John Doe",
      "firstName":"Sven",
      "expectedDeparture":"Fri, 26 Apr 2019 08:31:00 GMT",
      "verbalStatus":"Visitor returned pass",
      "company":"Disney"
   },
   Map {  
      "id":"1555586876781",
      "expectedArrival":"Thu, 18 Apr 2019 06:30:00 GMT",
      "lastName":"A.",
      "notes":"",
      "actualDeparture":"",
      "actualArrival":"",
      "visitor":"Christoph.",
      "status":0,
      "cardNumber":1,
      "employeeBeingVisited":"Jean Pier",
      "firstName":"Pablo",
      "expectedDeparture":"Sat, 20 Apr 2019 15:30:00 GMT",
      "verbalStatus":"Waiting for visitor arrival",
      "company":"Disney"
   }
]

What I want to do in my reducer is to update a specific record by its id. I have the id and the values for the edit fields coming from my action but I don't know how to handle them following the best practices for update.

This is what my reducer looks like:

export default (state = initialState, action) => {
  switch (action.type) {
    case ON_VISITOR_RECORDS_FETCHED:
      return state.merge({
        visitorRecords: fromJS(action.visitorRecords),
      });
    case ON_RETURN_APPROVED:
      return state.merge({
        approvedBookingId: action.approvedBookingId,
      });
    case ON_VISITOR_RECORD_UPDATED:
      console.log(JSON.stringify(state.get("visitorRecords")));
      // console.log('action.bookingBeingUpdated: ' + action.bookingBeingUpdated)
      // console.log('JSON.stringify(action.updatedTextFields)' + JSON.stringify(action.updatedTextFields))
      // console.log('updatedExpectedArrival: ' + action.updatedExpectedArrival)
      // console.log('updatedExpectedDeparture: ' + action.updatedExpectedDeparture)
      // console.log('updatedStatus: ' + action.updatedStatus)
      return state;
    default:
      return state;
  }
};

From here, I want to use action.bookingBeingUpdated, then find the corresponding record in visitorRecords and update it with the new action.updatedTextFields, action.updatedExpectedArrival, action.updatedExpectedDeparture, action.updatedStatus

I was reading this article but I don't quite understand how the update happens there. Plus, the structure of the container there is different.

EDIT: Type of the elements inside visitorRecords ins not an anonymous object but a Map

5 Answers

1
kushalvm On Best Solutions

Here's how you will do using immutablejs

case ON_VISITOR_RECORD_UPDATED:
    const newRecordsCollection = visitorRecords.map(record => {
        if (record.get('id') === action.bookingBeingUpdated) {
            return record.merge({
                updatedTextFields: action.updatedTextFields, 
                updatedExpectedArrival: action.updatedExpectedArrival, 
                updatedExpectedDeparture: action.updatedExpectedDeparture,
                updatedStatus: action.updatedStatus
            });
        }
        return record;
    });

    return state.merge({
        visitorRecords: newRecordsCollection
    });
0
Sahil Mittal On

You can update your record something like this:

case ON_VISITOR_RECORD_UPDATED:
  return {
      ...state, 
      visitorRecords: state.visitorRecords.map(record => {
        if(record.id.toString===action.bookingBeingUpdated.toString()){
          return {
            ...record, 
            updatedExpectedArrival: action.updatedExpectedArrival, 
            updatedExpectedDeparture: action.updatedExpectedDeparture, 
            updatedStatus: action.updatedStatus
          }
        } else {
          return record
        }
      })
    }
0
yourfavoritedev On

Best practice is to actually map() the existing state and return a new one that includes the updated item-values that you desire.

Let's say you dispatch an action and a payload that includes the id and new values you want to use:

dispatch({
   type: "ON_VISITOR_RECORD_UPDATED"
   payload: newRecordData
})

Now in your reducer:

export default (state = initialState, action) => {
  switch (action.type) {
    case ON_VISITOR_RECORDS_FETCHED:
      return state.merge({
        visitorRecords: fromJS(action.visitorRecords),
      });
    case ON_RETURN_APPROVED:
      return state.merge({
        approvedBookingId: action.approvedBookingId,
      });
    case ON_VISITOR_RECORD_UPDATED:
      return {
      ...state,
      visitorRecords: state.visitorRecords.map((record) => {
          if(record.id == action.payload.id){
             return{
                ...record,
                ...action.payload
             }
          }
      })
      }
    default:
      return state;
  }
};

This ensures that you have a completely new state-value for your reducer

1
dashton On

You would generally use map on your array of vistorRecords, and pass the id and fields to update in the action, so for example it would look like this:

case ON_VISITOR_RECORD_UPDATED:
     console.log(JSON.stringify(state.get("visitorRecords")));
     const updatedRecords = state.visitorRecords.map(record => {
         if (record.id === action.bookingBeingUpdated) {
             return new Map({
                 ...record,
                 expectedArrival: action.updatedExpectedArrival
                 // other fields...
             });
         }
         return record;
     });
return state.merge({
    state,
    fromJS({visitorRecords: updatedRecords})
});
0
Salman Mehmood On
Please don't use state.merge you can use 
```return {
       ...state,
       visitorRecords: [...state.visitorRecords,action.visitorRecords]

}```
Please send Id from action after and compare this id in ON_VISITOR_RECORD_UPDATED type like
```visitorRecords: {
       ...state.visitorRecords,
       data: state.visitorRecords.data.map((items) => {
            if (items.id=== action.id) {
               return { ...items, name: "updated name" };
             }
              return items;
            })
     }```