Redux reducer, check if value exists in state array and update state

James111 picture James111 · Mar 4, 2016 · Viewed 18.6k times · Source

So I've got an array chosenIds[] which will essentially hold a list of ids (numbers). But I'm having trouble accessing the state in my reducer to check whether the ID I parsed to my action is in the array.

  const initialState = {
  'shouldReload': false,
  'chosenIds': [],
};

export default function filter(state = initialState, action) {
  switch (action.type) {


 case ADD_TYPE:
      console.log(state.chosenIds, "Returns undefined???!!!");

      // Check if NUMBER parsed is in state
      let i = state.chosenIds.indexOf(action.chosenId);

      //If in state then remove it
      if(i) {
        state.chosenIds.splice(i, 1);
        return {
          ...state.chosenIds,
          ...state.chosenIds
        }
      }
      // If number not in state then add it 
      else {
        state.chosenIds.push(action.chosenId)
        return { ...state.chosenIds, ...state.chosenIds }
      }

I'm not to sure what's going on...But when I log state.chosenIds, it returns undefined? It doesn't even return the initial empty array [] .

Basically what this function is suppose to do is check to see if the action.chosenId is in the state.chosenIds, If it is, then remove the action.chosenId value, if it's not, then add the action.chosenId to the state.

Answer

markerikson picture markerikson · Mar 4, 2016

I'm seeing a few different issues here.

First, you're using splice() and push() on the array that's already in the state. That's direct mutation, which breaks Redux. You need to make a copy of the array, and modify that copy instead.

Second, the object spread usage doesn't look right. You're using it as if "chosenIds" was an object, but it's an array. Also, you're duplicating the spreads. That's causing the returned state to no longer have a field named "chosenIds".

Third, Array.indexOf() returns -1 if not found, which actually counts as "truthy" because it's not 0. So, the current if/else won't do as you expect.

I would rewrite your reducer to look like this:

export default function reducer(state = initialState, action) {
    switch(action.type) {
        case ADD_TYPE:
            let idAlreadyExists = state.chosenIds.indexOf(action.chosenId) > -1;
            // make a copy of the existing array
            let chosenIds = state.chosenIds.slice();

            if(idAlreadyExists) {
                chosenIds = chosenIds.filter(id => id != action.chosenId);                
            }     
            else {
                // modify the COPY, not the original
                chosenIds.push(action.chosenId);            
            }      

            return {
                // "spread" the original state object
                ...state,
                // but replace the "chosenIds" field
                chosenIds
            };
        default:
            return state;
    }    
}