Changing a child components state changes the pare

2019-07-21 13:26发布

问题:

Parent component is a header

Child component is a form which is used to change values appearing in the header after a save which fires a redux action.

I set the child state with

constructor(props) {
    super(props);
    this.state = {
      object: { ...props.object },
      hidden: props.hidden,
    };
}

The form is used to render the state.object and modify the state.object. When I modify state.object, the props from the parent component change as well.

handleObjectChange = (event, key, subkey) => {
    console.log('props', this.props.object.params);
    console.log('state', this.state.object.params);
    event.preventDefault();
    const value = this.handlePeriod(event.target.value);
    this.setState((prevState) => {
      const object = { ...prevState.object };
      object[key][subkey] = value;
      return { object };
    });
}

Console output:

newvalueijusttyped
newvalueijusttyped

This behavior actually goes all the way up to modifying the redux store without ever having dispatched an action.

Would appreciate a solution for this issue

Update:

Changing the constructor to this solved the issue

constructor(props) {
    super(props);
    this.state = {
      object: JSON.parse(JSON.stringify(props.object)),
      hidden: props.hidden,
    };
 }

Why doesn't the object spread operator achieve what I'm trying to accomplish?

回答1:

Javascript object are assigned by reference so when you do

constructor(props) {
    super(props);
    this.state = {
      object: props.object,
      hidden: props.hidden,
    };
}

state is referencing the redux state object(if it is a redux state). So now when you use

this.setState((prevState) => {
  const object = { ...prevState.object };
  object[key][subkey] = value;
  return { object };
});

Although you would assume that you have cloned the object value into a new object. However Spread syntax does only a one level copy of the object.

From the Spread Syntax MDN docs:

Note: Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays as the following example shows (it's the same with Object.assign() and spread syntax).

var a = [1, [2], [3]]; var b = [...a]; b.shift().shift(); // 1 // Now array a is affected as well: [[], [2], [3]]

So effectively

object[key][subkey] = value;

changes the value directly in redux store.

Solution is create a nested copy like

  const object = { ...prevState.object,
                      [key]: {
                          ...prevState[key],
                          [subkey]: { ...prevState[key][subkey]}
                      }
                   };
  object[key][subkey] = value;


回答2:

Objects in javascript are 'passed by reference'.

If you are passing the parent's props as state to the children, then when you change the state, you are in effect mutating the very same object that was passed to it.

Use Object.assign() to create a clone of the data before you make it part of the child's state.