this.setState不像我期望的那样合并状态

我有以下状态:

this.setState({ selected: { id: 1, name: 'Foobar' } });  

然后我更新状态:

this.setState({ selected: { name: 'Barfoo' }});

由于setState是假设要合并的,所以我希望它是:

{ selected: { id: 1, name: 'Barfoo' } }; 

但是它吃掉了id,状态为:

{ selected: { name: 'Barfoo' } }; 

这是预期的行为吗?仅更新嵌套状态对象的一个​​属性的解决方案是什么?

GreenSamA2020/03/11 12:20:36

我使用tmp var进行更改。

changeTheme(v) {
    let tmp = this.state.tableData
    tmp.theme = v
    this.setState({
        tableData : tmp
    })
}
樱Eva2020/03/11 12:20:36

您设置了初始状态吗?

我将使用一些自己的代码作为示例:

    getInitialState: function () {
        return {
            dragPosition: {
                top  : 0,
                left : 0
            },
            editValue : "",
            dragging  : false,
            editing   : false
        };
    }

在我正在开发的应用程序中,这就是设置和使用状态的方式。我相信setState您可以随便编辑任何您想要的状态,我一直这样称呼它:

    onChange: function (event) {
        event.preventDefault();
        event.stopPropagation();
        this.setState({editValue: event.target.value});
    },

请记住,您必须在React.createClass调用函数中设置状态getInitialState

長ぐつをはいたネコ2020/03/11 12:20:36

截至目前,

如果下一个状态取决于上一个状态,我们建议使用updater函数形式,而不是:

根据文档https://reactjs.org/docs/react-component.html#setstate,使用:

this.setState((prevState) => {
    return {quantity: prevState.quantity + 1};
});
Stafan路易2020/03/11 12:20:36

我使用的是es6类,最终在顶部状态出现了几个复杂的对象,并试图使我的主要组件更加模块化,因此我创建了一个简单的类包装器,将状态保留在顶部组件上,但允许使用更多本地逻辑。

包装类将一个函数用作其构造函数,该函数在主要组件状态上设置属性。

export default class StateWrapper {

    constructor(setState, initialProps = []) {
        this.setState = props => {
            this.state = {...this.state, ...props}
            setState(this.state)
        }
        this.props = initialProps
    }

    render() {
        return(<div>render() not defined</div>)
    }

    component = props => {
        this.props = {...this.props, ...props}
        return this.render()
    }
}

然后,对于顶部状态上的每个复杂属性,我创建一个StateWrapped类。您可以在此处的构造函数中设置默认道具,这些道具将在类初始化时进行设置,您可以引用局部状态获取值并设置局部状态,引用局部函数,并将其沿链传递:

class WrappedFoo extends StateWrapper {

    constructor(...props) { 
        super(...props)
        this.state = {foo: "bar"}
    }

    render = () => <div onClick={this.props.onClick||this.onClick}>{this.state.foo}</div>

    onClick = () => this.setState({foo: "baz"})


}

因此,然后我的顶级组件只需要构造函数将每个类设置为其顶级状态属性,一个简单的呈现器,以及任何用于通信跨组件的函数。

class TopComponent extends React.Component {

    constructor(...props) {
        super(...props)

        this.foo = new WrappedFoo(
            props => this.setState({
                fooProps: props
            }) 
        )

        this.foo2 = new WrappedFoo(
            props => this.setState({
                foo2Props: props
            }) 
        )

        this.state = {
            fooProps: this.foo.state,
            foo2Props: this.foo.state,
        }

    }

    render() {
        return(
            <div>
                <this.foo.component onClick={this.onClickFoo} />
                <this.foo2.component />
            </div>
        )
    }

    onClickFoo = () => this.foo2.setState({foo: "foo changed foo2!"})
}

似乎可以很好地实现我的目的,但请记住,尽管您不能更改分配给顶级组件上包装组件的属性的状态,因为每个包装组件都在跟踪自己的状态,但会更新顶级组件上的状态每次改变。

Jim老丝梅2020/03/11 12:20:36

根据@bgannonpl答案保留先前的状态:

Lodash示例:

this.setState((previousState) => _.merge({}, previousState, { selected: { name: "Barfood"} }));

要检查其是否正常工作,可以使用第二个参数函数回调:

this.setState((previousState) => _.merge({}, previousState, { selected: { name: "Barfood"} }), () => alert(this.state.selected));

merge之所以使用,是因为extend否则丢弃其他属性。

React不变性示例:

import update from "react-addons-update";

this.setState((previousState) => update(previousState, {
    selected:
    { 
        name: {$set: "Barfood"}
    }
});