2

One of the basic ideas of React is that state changes should always and only occur via this.setState(...) and not manipulate the state manually. But for the case of a state being an deep array, so an array of object literals (some JSON data really), updating that State becomes really expensive. If I only want to update one element of such an array, the code would be something like the following.

handleChange(index, newElement){
    var newStateArray = _.cloneDeep(this.state.myArray);
    newStateArray[index] = newElement;
    this.setState({myArray: newStateArray });
}

I know the standard solution would be using shallow copying, but that only really is a copy for shallow arrays. In my case it is not, so shallow copying would be "wrong" in a react sense as it would change the state. So really how is one supposed to handle this? I can just use a shallow copy and it works, but it feels dirty and is technically wrong.

edit: Or to clarify: Is there anything bad that could happen if I would just use the shallow copy thing and to be sure tell React to update manually?

edit: Oh this seems to have solved itself. Shallow copying doesn't work as I thought it would. I'm new to JS, pardon. For reference:

var a = [{a:1}, {b:2}, {c:3}]
var b = a.slice();
b[0] = 42; // I thought this would change a, but it doesn't!
2
  • 1
    To comment on your latest edit, while b[0]=42 will not change a (because a and b are separate arrays), b[0].a=42 will change a, because the object which lives in b[0] is the same one which lives in a[0] (same for b[1]=a[1], etc.). That's what a shallow copy is all about. See my answer for how to handle this situation safely. Commented Jan 18, 2015 at 14:23
  • Yeah I figured that out. Thanks though! Commented Jan 18, 2015 at 14:24

2 Answers 2

1

Provided you treat all the objects as immutable (i.e. if you need to change a property, make a shallow copy first), you can't go wrong. For example:

Starting state:

var A = {
    foo: 3,
    bar: 7
};
var B = {
    baz: 11,
    boop: 5
};
var C = {
    myA: A,
    myB: B
};

Suppose we want to change C->myB->boop. Nothing changes in A so we don't need to make a copy, but we do need to make a copy of both B and C:

var newB = {
    baz: B.baz,
    boop: 1000000 // we have to update this in B, so we need a new B
};
var newC = {
    myA: C.myA,
    myB: newB    // we have to update this in C, so we need a new C
};

(obviously you'd use shallow copying to make the copy of each part then assign the change on top; I'm just writing out the copy manually for clarity)

When we commit newC as our new state, it will share only the parts which have not changed (in this case A), which is fine because they're always handled as immutable objects.

Generalising, when you change any property you will need to make a shallow copy of the object which holds that property, as well as the parent of the object, and its parent's parent, etc. all the way back to your root node. Also you can consider arrays in exactly the same way; for this purpose they're just objects with numbered properties.

If you follow these rules everywhere that you make changes, your shallow array copy will work just fine. And this applies to any situation where you have a history of states, not just reactjs.

Sign up to request clarification or add additional context in comments.

Comments

1

See React's Immutability Helpers.

handleChange(index, newElement){
    var newStateArray = update(this.state.myArray, {
        [index]: {b: {$set: newElement.target.value} }
    });
    this.setState({myArray: newStateArray });
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.