What is the shortest way to modify immutable objec

2020-05-14 07:57发布

问题:

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

回答1:

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);
}


回答2:

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

const updateState = (state, item) => Object.assign({}, state, { [item.id]: item });


回答3:

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.



回答4:

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:

  • https://github.com/cah4a/immutable-modify
  • https://github.com/kolodny/immutability-helper
  • https://github.com/M6Web/immutable-set
  • https://github.com/bormind/immutable-setter

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()
   }))
}


回答5:

Try:

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

And test:

console.log(noId);


回答6:

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
      }