'prevent duplicate objects being added to state react redux

I have a question regarding preventing duplicates from being added to my redux store.

It should be straight forward but for some reason nothing I try is working.

export const eventReducer = (state = [], action) => {

    switch(action.type) {

        case "ADD_EVENT":

            return [...state, action.event].filter(ev => {
                if(ev.event_id !== action.event.event_id){
                   return ev;
                }
            });


        default:
            return state;

    }

};

The action looks something like the below:

{
   type: "ADD_EVENT",
   event: { event_id: 1, name: "Chelsea v Arsenal" }
}

The issue is that on occasions the API I am working with is sending over identical messages through a websocket, which means that two identical events are getting added to my store.

I have taken many approaches but cannot figure out how to get this to work. I have tried many SO answers,



Solution 1:[1]

You can use Array.prototype.find().

Example (Not tested)

const eventExists = (events, event) => {
  return evets.find((e) => e.event_id === event.event_id);
}
export const eventReducer = (state = [], action) = > {
    switch (action.type) {
    case "ADD_EVENT":
      if (eventExists(state, action.event)) {
        return state;
      } else {
        return [...state, action.event];
      }
    default:
        return state;
    }
};

Update (@CodingIntrigue's comment)

You can also use Array.prototype.some() for a better approach

const eventExists = (events, event) => {
  return evets.some((e) => e.event_id === event.event_id);
}
export const eventReducer = (state = [], action) = > {
    switch (action.type) {
    case "ADD_EVENT":
      if (eventExists(state, action.event)) {
        return state;
      } else {
        return [...state, action.event];
      }
    default:
        return state;
    }
};

Solution 2:[2]

Solution:

const eventReducer = ( state = [], action ) => {
    switch (action.type) {
        case 'ADD_EVENT':
            return state.some(( { event_id } ) => event_id === action.event.event_id)
                ? state
                : [...state, action.event];
        default:
            return state;
    }
};

Test:

const state1 = eventReducer([], {
    type: 'ADD_EVENT',
    event: { event_id: 1, name: 'Chelsea v Arsenal' }
});

const state2 = eventReducer(state1, {
    type: 'ADD_EVENT',
    event: { event_id: 2, name: 'Chelsea v Manchester' }
});

const state3 = eventReducer(state2, {
    type: 'ADD_EVENT',
    event: { event_id: 1, name: 'Chelsea v Arsenal' }
});

console.log(state1, state2, state3);

Solution 3:[3]

You can something like this, for the logic part to ensure you don't get the same entry twice.

const x = filter.arrayOfData(item => item.event_id !== EVENT_FROM_SOCKET);
if (x.length === 0) {
  // dispatch action here
} else {
  // ignore and do nothing
}

Solution 4:[4]

You need to be careful when using Arrays in reducers. You are essentially adding more items to the list when you call:

[...state, action.event]

If you instead use a map then you can prevent duplicates

const events = { ...state.events }
events[action.event.event_id] = action.event.name]
{...state, events }

Solution 5:[5]

If duplicate exist in previous state then we should return same state else update the state

case "ADDPREVIEW":
     let index = state.preview.findIndex(dup => dup.id == action.payload.id);
     return {
             ...state,
             preview: index == -1 ? [...state.preview,action.payload]:[...state.preview]
     };

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Ivan Nikitovic
Solution 3 Adeel Imran
Solution 4 Peter
Solution 5 KARTHIKEYAN.A