Pushing Object Into Nested Reducer Array?

Viewed 130

QUESTION: What's the best way to push a new payload object, to a nested redux reducers state?

I would like to push a new item location to the current lists array within the reducers function, using the current state index values.

MY CURRENT REDUX ACTION:

export const addLocation = () => {
  return (dispatch) => {
    axios.post("/inventory/lists/addlocation", locationData).then((res) => {
        dispatch({
          type: ADD_POSTED_LOCATION,
          payload: {location: "test", count: 0, _id: "6a5e4f6ae51g6ea5r1ga6e1rf"},
        });
    });
  };
};

MY CURRENT REDUCER:

const initialState = {
  lists: [],
  currentIndex: {
    listIndex: null,
    itemIndex: null,
    locationIndex: null
  },
};

export default function(state = initialState, action) {
  switch (action.type) {
    case ADD_POSTED_LOCATION:
      const listIndex = state.currentIndex.listIndex;
      const itemIndex = state.currentIndex.listIndex;
      return {
        ...state,
        lists: state.lists[listIndex ].items[itemIndex].locations.push(action.payload)
      };
My current result is simple:

lists: [a number, excluding the list objects and values]

DESIRED RESULT:

Lists: [

    {
      _id: "L1",
      items: [{
          number: "n1",
          locations: [{
            location: "L1"
          }, {
            location: "L2"
          }]
        },
        {
          number: "n2",
          locations: [{
              location: "L1"
            }, {
              location: "L2"
            },
            {
              location: "L3"
            },
            {
              location: "L4"
            }, {
              location: "L5"
            }
          ]
        }
      ]

    ]

2 Answers

You should always shallow copy any part of state you are updating, including nested arrays and objects.

export default function (state = initialState, action) {
  switch (action.type) {
    case ADD_POSTED_LOCATION:
      const listIndex = state.currentIndex.listIndex;
      const itemIndex = state.currentIndex.listIndex;
      return {
        ...state,
        lists: state.lists.map((list, lIndex) =>
          lIndex === listIndex
            ? {
                ...list,
                items: list.items.map((item, iIndex) =>
                  iIndex === itemIndex
                    ? {
                        ...item,
                        locaitons: [...item.locations, action.payload]
                      }
                    : item
                )
              }
            : list
        )
      };
  }
}

Fixed a few simple errors, and grammar. This is the Solution, Thanks Drew!

I learned that you must make shallow copies of your nested object structure using the spread operator (...). Also, utilizing the optional .map() index parameter allows you to use the mapped array's index values to compare with other index variables. Very Nice!

export default function(state = initialState, action) {
  switch (action.type) {
    case ADD_POSTED_LOCATION:
      return {
        ...state,
        lists: state.lists.map((list, lIndex) =>
          lIndex === state.currentIndex.listIndex ?
          {
            ...list,
            items: list.items.map((item, iIndex) =>
              iIndex === state.currentIndex.itemIndex ?
              {
                ...item,
                locations: [...item.locations, action.payload]
              } :
              item
            )
          } :
          list
        )
      };
  }
}

Related