2
[
   {
       "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];
1
  • case CREATE_CAPABILITY: return [...state, payload]; Commented May 16, 2021 at 18:09

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Nice Solution but what if you don't know the actual path and just the ID ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.