Using object rest to delete nested object

449 views Asked by At

I have a react app with some redux state that looks like:

{
    shape1: {
        constraints: {
            constraint1: {
                key: value
            },
            constraint2: {
                key: value
            }
        }
    }, 
    shape2: {
        constraints: {
            constraint1: {
                key: value
            },
            constraint2: {
                key: value
            }
        }            
    }
}

I dispatch an action and want to delete one of the constraint objects, ie. constraint1 for shape1. Here is what my reducer looks like for this action, say I'm trying to delete constraint1 from shape1:

case DELETE_CONSTRAINT:
    shape = action.payload;    // ie. shape1, the parent of the constraint I 
                               // am trying to delete
    let {
        [shape]: {'constraints': 
            {'constraint1': deletedItem}
        }, ...newState  
    } = state;
    return newState;

This removes the entire shape1 object from the state instead of just the individual constraint1 object. Where I am going wrong/ what is the best approach for doing this? I'd prefer to use object rest in order to be consistent with the rest of my code.

Thanks.

2

There are 2 answers

0
Ori Drori On BEST ANSWER

When using the rest syntax in destructuring to get a slice of the object, you'll get everything else on the same "level".

let {
    [shape]: {'constraints': 
        {'constraint1': deletedItem}
    }, ...newState  
} = state;

In this case newState takes everything else is everything but [shape].

Since your state has multiple nesting levels, you'll have to extract the new constraints using destructuring and rest syntax, and then create a new state.

const state = {
    shape1: {
        constraints: {
            constraint1: {
                key: 'value'
            },
            constraint2: {
                key: 'value'
            }
        }
    }, 
    shape2: {
        constraints: {
            constraint1: {
                key: 'value'
            },
            constraint2: {
                key: 'value'
            }
        }            
    }
};

const shape = 'shape1';
const constraint = 'constraint1';
  
// extract constraints
const {
  [shape]: {
    constraints: {
      [constraint]: remove,
      ...constraints
    }  
  }
} = state;

// create the next state
const newState = {
  ...state,
  [shape]: {
    ...state[shape], // if shape contains only constraints, you keep skip this
    constraints
  }
}

console.log(newState);

0
Chris On

In short, no not with an object - not with the spread operator.

You can do it other ways without mutating your state though, such as a filter, for example:

return state.filter((element, key) => key !== action.payload);

Consistency sidenote

As a sidenote - there is a vast difference between consistency in approach and style vs consistency of actual code. Don't feel the need to shoe horn something for consistency if it makes more logical sense to do it a different way. If it truely breaks the consistency of the application that other developers are working on, document why it's different.