最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Avoiding object mutation - Stack Overflow

programmeradmin0浏览0评论

I am working with React and ES6. So I arrivied to the following case: I have an state with an array of objects suppose a = [{id: 1, value: 1}, {id: 2, value: 2}] in the state of Object A, then I pass the list to Object B by props, Object B (in the constructor) copy the list to its own state and call a function which is using map function where I return b = [{id: 1, value: 1, text: 'foo'}, {id: 2, value: 2, text: 'foo'}] (added (text, value) to each object), so it though it was not mutating a in Object A but it was.

So I made some tests:

const a = [{id: 1, value: 1}, {id: 2, value: 2}] // suppose it is in object A

addText = (list) => {
    return list.map((item) => {item.text = "foo"; return item})
}

const b = addText(a) // suppose it is in object B

so under my assumption a !== b, but a was mutated by addText, so they were equal.

In a large scale project programmers make mistakes (I did here!) how it is supposed to be handled this kind of situations to avoid mutating objects in this way? (The example tries to represet a as an state for Object A which is a ponent from React)

I am working with React and ES6. So I arrivied to the following case: I have an state with an array of objects suppose a = [{id: 1, value: 1}, {id: 2, value: 2}] in the state of Object A, then I pass the list to Object B by props, Object B (in the constructor) copy the list to its own state and call a function which is using map function where I return b = [{id: 1, value: 1, text: 'foo'}, {id: 2, value: 2, text: 'foo'}] (added (text, value) to each object), so it though it was not mutating a in Object A but it was.

So I made some tests:

const a = [{id: 1, value: 1}, {id: 2, value: 2}] // suppose it is in object A

addText = (list) => {
    return list.map((item) => {item.text = "foo"; return item})
}

const b = addText(a) // suppose it is in object B

so under my assumption a !== b, but a was mutated by addText, so they were equal.

In a large scale project programmers make mistakes (I did here!) how it is supposed to be handled this kind of situations to avoid mutating objects in this way? (The example tries to represet a as an state for Object A which is a ponent from React)

Share Improve this question edited Feb 23, 2017 at 13:23 FacundoGFlores asked Feb 23, 2017 at 13:17 FacundoGFloresFacundoGFlores 8,11812 gold badges67 silver badges97 bronze badges 5
  • You could deep copy the list so that new list does not have references to old list. – Jesvin Jose Commented Feb 23, 2017 at 13:24
  • 1 Deep clone implementation lodash./docs#cloneDeep – Jesvin Jose Commented Feb 23, 2017 at 13:27
  • FWIW: I would not expect a copy when I call a function named addText. And imho that's part of the problem. You even write "added (text, value) to each object". That is mutation. Therefore it's hard to understand when you conclude _"so it though it was not mutating". – a better oliver Commented Feb 23, 2017 at 13:45
  • @zeroflagL I concluded that because I was mutating inside Object B and I supposed it was outside the scope of Object A, but sure you're right, that's why I said "programmers make mistakes" I did that one, so I wanted to know how to avoid this behavior for other programmers. – FacundoGFlores Commented Feb 23, 2017 at 14:00
  • 1 There is a great quote: "There are only two hard things in Computer Science: cache invalidation and naming things." My point was, that if the function was named e.g. "createClonesWithText" chances are that you wouldn't have made that mistake. And to be honest: map implies returning a new value. So I don't expect this very mistake to e up often. The only way to definitely prevent the mistake is to make the objects in a immutable. A more sensible solution would be to pass a copy to addText in the first place. – a better oliver Commented Feb 23, 2017 at 14:19
Add a ment  | 

2 Answers 2

Reset to default 5

If your change to the object is really that shallow (at the top level of the object), you can use Object.assign({}, oldObj, newObj), or if you have the Object spread proposal enabled in babel, { ...oldObj, newThing: 'thing' }

To enforce this on a team, you could use this ESLint plugin https://github./jhusain/eslint-plugin-immutable with the no-mutation rule enabled.

By directly referring to item.text you are changing it.

Map iterates over an array. Since your array has objects, unlike primitive values, it doesn't duplicate them in the function scope. Instead, it passes by reference.

The solution is to return new (duplicated) object values whilst iterating in the map function.

const a = [{id: 1, value: 1}, {id: 2, value: 2}]

addText = (list) => {
    return list.map((item) => {
        return {
            ...item,
            text: 'foo',
        };
    })
}

const b = addText(a)

You can simplify the syntax, and reduce indentation, by omitting the return statements as you are using arrow functions anyway.

const a = [{id: 1, value: 1}, {id: 2, value: 2}]

addText = list => list.map(item => ({
    ...item,
    text: 'foo',
}));

const b = addText(a)
发布评论

评论列表(0)

  1. 暂无评论