Visual Quran
Visual Quran

Reputation: 21

How to update deeply, recursively nested array of objects in react

[
   {
       "id": 339,
       "children": [
           {
               "id": 381,
               "children": [
                   {
                       "id": 383,
                       "children": [],
                       "name": "Capability_C",
                       "level": 3,
                   }
               ],
               "name": "Capability_B",
               "level": 2,
           }
       ],
       "name": "Capability_A",
       "level": 1,
   }

] 

How do i correctly update this nested object within my reducer (the state has a list of these objects, while a payload has a specific object that can be a nested object

        return [...state, payload];

Upvotes: 2

Views: 1194

Answers (1)

lawrence-witt
lawrence-witt

Reputation: 9354

The recursiveness of your data structure makes this is a non-trivial task. You need a way to path into it, handle bad paths, and dynamically clone your state for any arbitrary depth that you update.

You'll need an action that looks something like this, where the path array is a list of id numbers that gives you an entry into your data:

{
  type: "CREATE_CAPABILITY";
  payload: {
    path: Array<number>;
    capability: CapabilityObject;
  }
}

Then in your reducer, pass this action to a recursive function alongside the current reducer state:

// function

const insertCapability = (state, action) => {
  const { path, capability } = action.payload;

  if (path.length === 0) return [...state, capability];

  const nextId = path.shift();
  const childIdx = state.findIndex(cap => cap.id === nextId);

  if (childIdx < 0) return state;

  const nextChild = {
    ...state[childIdx],
    children: insertCapability(state[childIdx].children, action)
  }
  
  return (s => {s[childIdx] = nextChild; return s;})([...state]);
};

// test case

const state = [
   {
       "id": 339,
       "children": [
           {
               "id": 381,
               "children": [
                   {
                       "id": 383,
                       "children": [],
                       "name": "Capability_C",
                       "level": 3,
                   }
               ],
               "name": "Capability_B",
               "level": 2,
           }
       ],
       "name": "Capability_A",
       "level": 1,
   }
];

const action = {
  type: "CREATE_CAPABILITY",
  payload: {
    path: [339, 381, 383],
    capability: {
      id: 400,
      children: [],
      name: "New Capability",
      level: 4, 
    }
  }
}

console.log(insertCapability(state, action));

Notice how every time state is returned with new data, it is done so in a new array - [...state] - and every new child pathed into is cloned as well - ...state[childIdx]. It is important to do this so that your state structure stays immutable. Failing to clone state properly and accidently mutating it can lead to some ugly bugs later on down the line.

Upvotes: 2

Related Questions