3

Let's say I have the variables 'state' and 'newState'. I would like to create a pure function that returns 'state' updated with the properties (and sub properties) of 'newState'. Here is an example:

const state = {id:1, name:'aName', description: 'aDescription'};

const newState = {id:1, name:'newName', subItems: {id:3, type:'whatever'}};

The function would return:

{id:1, name:'newName', subItems: {id:3, type:'whatever'}}

I could use rest parameters but I don't know how to append as opposed to override. I can't just loop through the properties because I want the function to be pure (redux reducer).

Anyone have any ideas?

nicholaswmin
  • 21,686
  • 15
  • 91
  • 167
Eitan
  • 1,434
  • 6
  • 21
  • 53
  • 1
    For whatever it is worth the common ES6 pattern you will see `this.setState({...this.state, ...updates})` where updates is any changes. This is really just syntax sugar and is basically Object.assign, but it helps the clarity in my opinion. Take a look at the redux tutorials. What you're asking is basically the essence of redux. https://redux.js.org/api/store. As to your function question: a pure function just means the output is always the same with the same input and the input isn't altered or-> `Object.assign({}, oldObj, newObj)` – Daniel B. Chapman Sep 01 '18 at 01:49
  • @DanielB.Chapman the weird thing is that when I used the rest/spread syntax and overrides the values of the original object so when I used rest for the example above, it was missing description. – Eitan Sep 01 '18 at 01:55
  • 1
    *"The function would return: { ... }"* – how come `description` is absent in the expected output? – Mulan Sep 01 '18 at 05:07
  • Possible duplicate of [How to deep merge instead of shallow merge?](https://stackoverflow.com/questions/27936772/how-to-deep-merge-instead-of-shallow-merge) – nicholaswmin Sep 01 '18 at 07:57

2 Answers2

6

You can use the spread syntax:

The Rest/Spread Properties for ECMAScript proposal (stage 4) adds spread properties to object literals. It copies own enumerable properties from a provided object onto a new object.

... or Object.assign with the 1st argument being an empty object:

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.

const state = {id:1, name:'aName', description: 'aDescription'};
const newState = {id:1, name:'newName', subItems: {id:3, type:'whatever'}};

// Example with spread syntax:
const result1 = { ...state, ...newState };

// Example with Object.assign:
// @NOTE: Providing an empty `{}` as the 1st argument,
// ensures you don't mutate `state`.
const result2 = Object.assign({}, state, newState);

console.log(result1);
console.log(result2);

Side notes:

  • Expanding on @user633183's comment, both ways produce a shallow merge. They only go 1-level deep. If you're looking to do a deep merge, I suggest you check out this answer.

  • At the time of writing this, Object.assign has better cross-browser support than spread syntax on object literals, as you can see here. While this shouldn't be a problem if you correctly use Babel or any other transpiler, it is worth mentioning.

  • We don't usually refer to functions as immutable (actually they can be since they are values themselves but that's probably not what you mean in the context of your question). Values can be immutable, functions can be pure; meaning they don't cause side-effects such as mutating their arguments.

nicholaswmin
  • 21,686
  • 15
  • 91
  • 167
  • Cool, thanks! Just out of curiousity, is there any way to do this using only ES6 rest/spread syntax? – Eitan Sep 01 '18 at 01:53
  • @Eitan Yup, added an example in my snippet. – nicholaswmin Sep 01 '18 at 01:55
  • that's how I though it would work. I tried it and it overrides the description but maybe I had a typo. Thanks! – Eitan Sep 01 '18 at 01:55
  • *"... with the properties (and sub properties)"* – this is a shallow merge – Mulan Sep 01 '18 at 05:10
  • @user633183 Added that info in the side notes. In fact your comment led me down to this https://stackoverflow.com/questions/27936772/how-to-deep-merge-instead-of-shallow-merge. This question is more of a duplicate of this one so I'll mark it as such. – nicholaswmin Sep 01 '18 at 07:53
3

Object.assign could be useful to update state:

const state = {id:1, name:'aName', description: 'aDescription'};

const newState = {id:1, name:'newName', subItems: {id:3, type:'whatever'}};

Object.assign(state,newState)//<-- updates state

console.log(state)

As NicholasKyriakides points out, to create a new object you should add {} in the beginning.

Emeeus
  • 5,072
  • 2
  • 25
  • 37