What is the shortest way to modify immutable objects using spread and destructuring operators
Asked Answered
D

6

28

I'm looking for a pure function, to modify my immutable state object. The original state given as parameter must stay untouched. This is especially useful when working with frameworks like Redux and makes working with immutable object in javascript much easier. Especially since working with the object spread operator using Babel is already possible.

I did not found anything better than first copy the object, and than assign/delete the property I want like this:

function updateState(state, item) {
  newState = {...state};
  newState[item.id] = item;
  return newState;
}

function deleteProperty(state, id) {
    var newState = {...state};
    delete newState[id];
    return newState;
}

I feel like it could be shorter

Debussy answered 11/4, 2016 at 15:36 Comment(0)
D
74

Actions on state, where state is considered immutable.

Adding or Updating the value of a property:

// ES6:
function updateState(state, item) {
    return Object.assign({}, state, {[item.id]: item});
}

// With Object Spread:
function updateState(state, item) {
  return {
     ...state,
     [item.id]: item
  };
}

Deleting a property

// ES6:
function deleteProperty(state, id) {
    var newState = Object.assign({}, state);
    delete newState[id];
    return newState; 
}

// With Object Spread:
function deleteProperty(state, id) {
    let  {[id]: deleted, ...newState} = state;
    return newState;
}

// Or even shorter as helper function:
function deleteProperty({[id]: deleted, ...newState}, id) {
    return newState;
}

// Or inline:
function deleteProperty(state, id) {
    return (({[id]: deleted, ...newState}) => newState)(state);
}
Debussy answered 11/4, 2016 at 15:36 Comment(8)
Answering your own question seconds after asking?!Ribal
I used the field below the question form to answer it, since I found the solution before posting the question. Since googling for it is pretty hard I put it for everyone here this was.Debussy
But actually, removing a property is also an interesting question. Since setting a property to undefined, does not remove the key, and delete seems not to work inside object definitions ;)Debussy
What you claim to be ES7 is not ES7. Object spread and rest is still a proposal. It will likely come in ES2017.Geniagenial
Ok thanks, did not looked it up and just followed Ori Drori hint in the comments. I will edit it. - Done, no reference to the ES-whatever more :)Debussy
I wish I could vote for this more. I've come back to this post so many times XDMascle
deleted is undefined. what am I missing?Overpowering
@Overpowering idea there was assigning a random value to the key that has to be deleted, you could give a string value "deleted", let {[id]: "deleted", ...newState} = state; by doing so the <id> property will not be copied to newStateIndebtedness
S
8

An ES6 solution, that has a bit more support is Object.assign:

const updateState = (state, item) => Object.assign({}, state, { [item.id]: item });
Shih answered 11/4, 2016 at 15:40 Comment(3)
Sure, that's the same in ES5 style.Debussy
Sorry have to update the question then. Using babel es6 + plugin for this ;) Got a little confused.Debussy
Actually arrow functions are available only in ES6, so this snippet is ES6.Standice
N
2

In a Map Function

To do this process within a map function (remove an attribute and add a new attribute on each object), given an array of objects -

const myArrayOfObjects = [
    {id: 1, keyToDelete: 'nonsense'},
    {id: 2, keyToDelete: 'rubbish'}
];

Delete the attribute keyToDelete, and add a new key newKey with the value "someVar".

myArrayOfObjects.map(({ keyToDelete, ...item}) => { ...item, newKey:'someVar'});

Updating the array to

[
    {id: 1, newKey:'someVar'},
    {id: 2, newKey:'someVar'}
]

See this great post for more information on the deletion method.

Nucleoplasm answered 25/7, 2018 at 11:1 Comment(0)
M
1

Instead of writing boilerplate code (as answered above: (({[id]: deleted, ...state}) => state)(state)) which is hard to read, you could use some library to do the same:

For example:

import {remove} from 'immutable-modify'

function updateState(state, item) {
   return remove(state, item.id)
}

It's also supports any nested updates:

import {set} from 'immutable-modify'

function updateState(state, item) {
   return set(state, 'user.products', (products) => ({
      ...products,
      items: products.items.concat(item),
      lastUpdate: Date.now()
   }))
}
Marrissa answered 19/7, 2018 at 12:20 Comment(0)
C
1

Try:

const { id, ...noId } = state;

And test:

console.log(noId);
Compensatory answered 5/3, 2019 at 5:30 Comment(0)
R
-2

Removing item from an array, just use filter ;)

CASE 'REMOVE_ITEM_SUCCESS':

      let items = state.items.filter(element => element._id !== action.id);

      return {
            ...state, 
            items
      }
Rope answered 1/2, 2018 at 23:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.