React.js: setState覆盖,而不是合并

React.js: setState overwriting, not merging

本文关键字:合并 覆盖 js setState React      更新时间:2023-09-26

我是React.JS的新手,我正在尝试构建一个砖石风格的布局。

我将每个元素呈现给DOM,然后我需要循环遍历每个项目,并根据前面的元素应用x和y位置。

初始模型是这样的:

[
  {
    "title": "The Forrest",
    "description": "some cool text",
    "imgSmallSrc": "/img/img4-small.jpg",
    "imgAlt": "Placeholder image",
    "tags": [
        "Design",
        "Mobile",
        "Responsive"
    ],
    "date": 1367154709885,
    "podStyle": {
      "width": 253
    }
  }
]

(为了简短起见,我只展示了一个项目)。

一旦我完成循环并获得x和y数据,我想将其应用于podStyle对象。我用以下数据调用setState():

[
  {
    "podStyle": {
      "x": 0,
      "y": 0,
      "height": 146,
      "width": 253
    }
  }
]

这似乎从模型中删除了所有当前数据,只留下podStyle数据。我是不是误解了合并的原理?

如果你的状态是一个对象:

getInitialState: function() {
  return { x: 0, y: 0 };
}

您可以使用setState在该对象上设置单个键:

this.setState({ x: 1 }); // y still == 0

React不智能合并你的状态;例如,这行不通:

getInitialState: function() {
  return {
    point: { x: 0, y: 0 },
    radius: 10
  };
}
this.setState({point: {x: 1}});
// state is now == {point: {x: 1}, radius: 10} (point.y is gone)
[编辑]

正如@ssorallen提到的,您可以使用不变性帮助器来获得您想要的效果:

var newState = React.addons.update(this.state, {
  point: { x: {$set: 10} }
});
this.setState(newState);

请看这个JSFiddle的例子:http://jsfiddle.net/BinaryMuse/HW6w5/

合并是浅的,所以this.setState({point})保留(ed: this.state.radius)不变,但完全替换(ed: this.state.point)。

https://facebook.github.io/react/docs/state-and-lifecycle.html state-updates-are-merged

从ES7+的角度来看已经给出的答案,使用transform-object-rest-spread代替Object.assign():

class MyComponent extends React.Component {
    state = {
        point: { 
            x: 0, 
            y: 0,
        },
        radius: 10,
    }
    handleChange = () => {
        this.setState((prevState, props) => ({
            point: {
                // rest operator (...) expands out to:
                ...prevState.point, // x:0, y:0,
                y: 1, // overwrites old y
            },
            // radius is not overwritten by setState
        }));
    }
    render() {
        // omitted
    }
}

。Babelrc(也需要Babel预设阶段2的转换类属性)

{
    "presets": ["es2015", "stage-2", "react"],
    "plugins": ["transform-object-rest-spread"],
}

2018-04-22

更新

正如@sheljohn指出的(谢谢!),在setState中引用this.state是不可靠的:

因为this.propsthis.state可能是异步更新的,所以你不应该依赖它们的值来计算下一个状态。

要解决这个问题,使用setState()的第二种形式,它接受函数而不是对象。该函数将接收前一个状态作为第一个参数,并将应用更新时的道具作为第二个参数

https://reactjs.org/docs/state-and-lifecycle.html state-updates-may-be-asynchronous

类似于

getInitialState: function() {
    return {
        something: { x: 0, y: 0 },
        blah: 10
    };
}
var state = Object.assign(this.state, {
    something: Object.assign(this.state.something, { y: 50 }),
});
this.setState(state);

如果是递归/深度的,而不是硬编码树,那就更好了,但我将把这个留给读者:)