27

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

Tarion
  • 16,283
  • 13
  • 71
  • 107

6 Answers6

73

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);
}
Jake
  • 143
  • 2
  • 8
Tarion
  • 16,283
  • 13
  • 71
  • 107
  • 5
    Answering your own question seconds after asking?! – ColinE Apr 11 '16 at 15:38
  • 16
    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. – Tarion Apr 11 '16 at 15:39
  • 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 ;) – Tarion Apr 11 '16 at 15:40
  • 1
    What you claim to be ES7 is not ES7. Object spread and rest is still a proposal. It will likely come in ES2017. – Felix Kling Apr 29 '16 at 03:26
  • 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 :) – Tarion May 03 '16 at 15:33
  • 3
    I wish I could vote for this more. I've come back to this post so many times XD – comfortablejohn Sep 05 '17 at 13:16
  • deleted is undefined. what am I missing? – praHoc Oct 18 '18 at 05:48
  • @praHoc 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 property will not be copied to newState – MeanMan Nov 26 '18 at 09:53
8

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

const updateState = (state, item) => Object.assign({}, state, { [item.id]: item });
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
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.

Aidan Ewen
  • 13,049
  • 8
  • 63
  • 88
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()
   }))
}
1

Try:

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

And test:

console.log(noId);
Andry
  • 16,172
  • 27
  • 138
  • 246
ali
  • 350
  • 2
  • 15
-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
      }
webmaster
  • 1,960
  • 24
  • 29