1

I have a reducer working with an object with arrays. I want to change a value on the nested arrays based on a given index. This code works but I can't seem to get my test to work using deep freeze. I was trying to look at the redux example here http://redux.js.org/docs/basics/Reducers.html using .map to find the index with no luck. Any ideas?

export default function myReducer(state = { toDisplay: [] }, action) {
  const { type, groupIndex, itemIndex } = action;
  const newObject = Object.assign({}, state);
  switch (type) {
    case actionTypes.TOGGLE_GROUP:
      newObject.toDisplay[groupIndex].isSelected = newObject.toDisplay[groupIndex].isSelected ? false : 'selected';
      return newObject;
    case actionTypes.TOGGLE_ITEM:
      newObject.toDisplay[groupIndex].values[itemIndex].isSelected = newObject.toDisplay[groupIndex].values[itemIndex].isSelected ? false : true;
      return newObject;
    default:
      return state;
  }
}

EDIT:

For anyone curious after watching a helpful redux video I came up with this:

export default function myReducer(state = { toDisplay: [] }, action) {
  const { type, groupIndex, itemIndex } = action;
  switch (type) {
    case actionTypes.TOGGLE_GROUP:
      return {
        ...state,
        toDisplay: [
          ...state.toDisplay.slice(0, groupIndex),
          {
            ...state.toDisplay[groupIndex],
            isSelected: state.toDisplay[groupIndex].isSelected ? false : 'selected'
          },
          ...state.toDisplay.slice(groupIndex + 1)
        ]
      };
    case actionTypes.TOGGLE_ITEM:
      return {
        ...state,
        toDisplay: [
          ...state.toDisplay.slice(0, groupIndex),
          {
            ...state.toDisplay[groupIndex],
            values: [
              ...state.toDisplay[groupIndex].values.slice(0, itemIndex),
              {
                ...state.toDisplay[groupIndex].values[itemIndex],
                isSelected: state.toDisplay[groupIndex].values[itemIndex].isSelected ? false : true
              },
              ...state.toDisplay[groupIndex].values.slice(itemIndex + 1)
            ]
          },
          ...state.toDisplay.slice(groupIndex + 1)
        ]
      };
    default:
      return state;
  }
}

Using a helper/library like suggested is probably the best route but my team wishes to not add another dependency.

anthony-dandrea
  • 2,583
  • 7
  • 26
  • 46
  • Check React ```update``` utility function: https://facebook.github.io/react/docs/update.html – Rafael Quintanilha Mar 28 '16 at 23:41
  • Have you thought about using Immutable.js? Right off the bat, it seems as though it could be helpful for what you are trying to do here. – marcacyr Mar 29 '16 at 00:40
  • @RafaelQuintanilha - I was hoping to avoid adding another dependency if possible. But I'm open to it if it's really necessary. I couldn't seem to find any examples show casing solutions similar to the problem I have now for either of those. I may just not understand the libraries enough yet however... – anthony-dandrea Mar 29 '16 at 02:32

1 Answers1

1

Firstly, Object.assign(...) only does a shallow copy. See here.

As you have objects nested inside arrays nested inside objects I would highly recommend the immutability helpers from react (as mentioned by Rafael). These allow you to do something like this:

case actionTypes.TOGGLE_GROUP:
  return update(state, {
    toDisplay: {
      [groupIndex]: {
        isSelected: {$set: newObject.toDisplay[groupIndex].isSelected ? false : 'selected'}
      }
    }
  });

If you are looking to modify a simple value inside an array with raw js then you can use something like this:

return list
  .slice(0,index)
  .concat([list[index] + 1])
  .concat(list.slice(index + 1));

(source)

Community
  • 1
  • 1
Clarkie
  • 7,490
  • 9
  • 39
  • 53