Home > Mobile >  How to update array in array in nested state in react?
How to update array in array in nested state in react?

Time:08-10

I'm trying to figure out how to update an array in an array in react nested state. I already learned about shallow copies but don't get how to implement it in this case.

There is a dispatch function with the useReducer hook to manage an array of objects called "Layer"

interface Layer {
  name: string,
  data: Item[],
  ...
}

every Layer holds an array of Items

class Item {
  id: number;
  ...
}

this is my dispatch function:

type ActionType =
  | { type: "ADD LAYER"; layer: Layer }
  | { type: "ADD ITEM"; item: Item; layer: Layer }
  | { type: "UPDATE ITEM"; item: Item; layer: Layer }
  | { type: "REMOVE ITEM"; item: Item; layer: Layer };

 const [layers, dispatch] = useReducer((state: Layer[], action: ActionType) => {
    switch (action.type) {
      case "ADD LAYER":
        {
          if (!state.includes(action.layer))
            state.push(action.layer);
          return state;
        }
      case "ADD ITEM":
        {
          if(!state.find(layer => layer.name === action.layer.name)?.data.find(item => item.id === action.item.id))
          state.find(layer => layer.name === action.layer.name)?.data.push(action.item)
          return state;
        }
      case "UPDATE ITEM": {
        //implementation missing
        return state;
      }
      case "REMOVE ITEM":
        {
          const newState = {
            ...state.map(layer =>
              layer.name == action.layer.name
                ? { ...layer, data: {
                  ...layer.data.filter(({ id }) => id !== action.item.id)
                } }
            : layer )
          };
          return newState;
        }
      default:
        throw new Error();
    }
  }, initialLayers);

look at the REMOVE ITEM case to see what I tried. But this approach make my application crash with the message: Uncaught TypeError: layers.map is not a function

case ADD LAYER and ADD ITEM are working

CodePudding user response:

State is an array of objects:

case "REMOVE ITEM": {
  const newState = {};
  return newState;  // <- return an object
}

But this is returning an single object, so this will not work.

As you are using map() function, and it will create a new array, you might as well do this instead:

case "REMOVE ITEM": {
  return state.map((layer) =>
        layer.name == action.layer.name
          ? {
              ...layer,
              data: {
                ...layer.data.filter(({ id }) => id !== action.item.id),
              },
            }
          : layer
      );
}

CodePudding user response:

How about that?

case "REMOVE ITEM":
{
  let clonedState = JSON.parse(JSON.stringify(state))

  const layerIndex = clonedState.findIndex(layer => layer.name == action.layer.name);
  
  if (layerIndex > -1) {
    if (clonedState[layerIndex].data.filter(d => d.id == action.item.id).length > 0)
      clonedState[layerIndex].data = clonedState[layerIndex].data.filter(d => d.id !== action.item.id)
  }

  return clonedState;
}

NOTE: You can also modify data array like below (which is more elegant)

const itemIndex = clonedState[layerIndex].data.findIndex(d => d.id == action.item.id)
clonedState[layerIndex].data = [
  ...clonedState[layerIndex].data.slice(0, itemIndex),
  ...clonedState[layerIndex].data.slice(itemIndex   1),
]

instead of

clonedState[layerIndex].data = clonedState[layerIndex].data.filter(d => d.id !== action.item.id)
  • Related