Javascript 此.setState不是';我不能像我所期望的那样合并各州

Javascript 此.setState不是';我不能像我所期望的那样合并各州,javascript,reactjs,Javascript,Reactjs,我有以下情况: this.setState({ selected: { id: 1, name: 'Foobar' } }); 然后我更新状态: this.setState({ selected: { name: 'Barfoo' }}); 由于setState被假定为合并,我希望它是: { selected: { id: 1, name: 'Barfoo' } }; 但它会吃掉id,状态是: { selected: { name: 'Barfoo' } }; 这是预期的行为吗?

我有以下情况:

this.setState({ selected: { id: 1, name: 'Foobar' } });  
然后我更新状态:

this.setState({ selected: { name: 'Barfoo' }});
由于
setState
被假定为合并,我希望它是:

{ selected: { id: 1, name: 'Barfoo' } }; 
但它会吃掉id,状态是:

{ selected: { name: 'Barfoo' } }; 
这是预期的行为吗?仅更新嵌套状态对象的一个属性的解决方案是什么?

我认为
setState()
不执行递归合并

您可以使用当前状态的值
this.state.selected
构造一个新状态,然后对该状态调用
setState()

var newSelected = _.extend({}, this.state.selected);
newSelected.name = 'Barfoo';
this.setState({ selected: newSelected });
在这里,我使用了函数
\uuu.extend()
函数(来自underline.js库),通过创建状态的浅层副本来防止对现有的
选定的
部分进行修改

另一种解决方案是编写
setStateRecursive()
,它在新状态上进行递归合并,然后用它调用
replaceState()

setStateRecursively: function(stateUpdate, callback) {
  var newState = mergeStateRecursively(this.state, stateUpdate);
  this.replaceState(newState, callback);
}

你设置好初始状态了吗

我将使用我自己的一些代码,例如:

    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});
    },

请记住,您必须在调用的
getInitialState
不可变帮助程序最近添加到React.addons的
React.createClass
函数中设置状态,这样,您现在可以执行以下操作:

var newState = React.addons.update(this.state, {
  selected: {
    name: { $set: 'Barfoo' }
  }
});
this.setState(newState);

.

我使用tmp变量进行更改

changeTheme(v) {
    let tmp = this.state.tableData
    tmp.theme = v
    this.setState({
        tableData : tmp
    })
}

对于这种情况,我的解决方案是使用,就像另一个答案指出的那样

由于深入设置状态是一种常见情况,我创建了以下mixin:

var SeStateInDepthMixin = {
   setStateInDepth: function(updatePath) {
       this.setState(React.addons.update(this.state, updatePath););
   }
};
这个mixin包含在我的大多数组件中,我通常不再直接使用
setState

使用此mixin,要达到预期效果,只需按以下方式调用函数
setStateinDepth

setStateInDepth({ selected: { name: { $set: 'Barfoo' }}})
有关更多信息:

  • 有关混合器在React中的工作方式,请参见
  • 关于传递给
    setStateinDepth
    的参数的语法,请参见不变性帮助程序

由于许多答案都使用当前状态作为合并新数据的基础,因此我想指出,这可能会导致中断。状态更改将排队,并且不会立即修改组件的状态对象。因此,在处理队列之前引用状态数据将提供不反映您在setState中所做的挂起更改的陈旧数据。从文档中:

setState()不会立即改变this.state,但会创建挂起的状态转换。调用此方法后访问this.state可能会返回现有值

这意味着在后续调用setState时使用“当前”状态作为引用是不可靠的。例如:

    getInitialState: function () {
        return {
            dragPosition: {
                top  : 0,
                left : 0
            },
            editValue : "",
            dragging  : false,
            editing   : false
        };
    }
  • 首先调用setState,将状态对象的更改排队
  • 第二次调用setState。您的州使用嵌套对象,因此您希望执行合并。在调用setState之前,获取当前状态对象。此对象不反映在上面第一次调用setState时所做的排队更改,因为它仍然是原始状态,现在应将其视为“过时”
  • 执行合并。结果是原始的“过时”状态加上您刚刚设置的新数据,从最初的setState调用所做的更改不会反映出来。您的setState调用将第二次更改排队
  • 反应进程队列。处理第一个setState调用,更新状态。处理第二个setState调用,更新状态。第二个setState的对象现在已经替换了第一个,并且由于您在进行该调用时拥有的数据是陈旧的,因此第二个调用中修改的陈旧数据已经破坏了第一个调用中所做的更改,这些更改将丢失
  • 当队列为空时,React决定是否渲染等。此时,您将渲染在第二个setState调用中所做的更改,就好像第一个setState调用从未发生过一样

  • 如果需要使用当前状态(例如,将数据合并到嵌套对象中),setState也可以接受函数作为参数而不是对象;该函数在之前对状态进行任何更新后调用,并将状态作为参数传递——因此可以使用该函数进行原子更改,以保证尊重以前的更改。

    我使用的是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()
        }
    }
    
    导出默认类StateWrapper{
    构造函数(setState,initialProps=[]){
    this.setState=props=>{
    this.state={…this.state,…props}
    setState(this.state)
    }
    this.props=初始值props
    }
    render(){
    返回(render()未定义)
    }
    组件=道具=>{
    this.props={…this.props,…props}
    返回这个
    }
    }
    
    然后,对于顶部状态上的每个复杂属性,我创建一个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"})
    
    
    }
    
    类WrappedFoo扩展了StateWrapper{
    构造函数(…props){
    超级(…道具)
    this.state={foo:bar}
    }
    render=()=>{this.state.foo}
    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!"})
    }
    
    类TopComponent扩展了React.Component{
    常数
    
    this.setState((previousState) => _.merge({}, previousState, { selected: { name: "Barfood"} }), () => alert(this.state.selected));
    
    import update from "react-addons-update";
    
    this.setState((previousState) => update(previousState, {
        selected:
        { 
            name: {$set: "Barfood"}
        }
    });
    
    this.linkAt( 'selected' ).at( 'name' ).set( 'Barfoo' );
    
    this.setState({ selected: { name: 'Barfoo' }});
    
    var newSelected = Object.assign({}, this.state.selected);
    newSelected.name = 'Barfoo';
    this.setState({ selected: newSelected });
    
    this.setState({ selected: Object.assign({}, this.state.selected, { name: "Barfoo" }) });
    
    this.setState((prevState) => {
        return {quantity: prevState.quantity + 1};
    });
    
    this.setState(prevState => {
        var newSelected = JSON.parse(JSON.stringify(prevState.selected)) //1
        newSelected.name = 'Barfoo'; //2
        var update = { selected: newSelected }; //3
        return update; //4
    });
    
    this.setState(prevState => {
        var selected = JSON.parse(JSON.stringify(prevState.selected)) //1
        selected.name = 'Barfoo'; //2
        return { selected }; //3, 4
    });
    
    this.setState({ selected: { id: 1, name: 'Foobar' } }); 
    //this.state: { selected: { id: 1, name: 'Foobar' } }
    
    const { selected: _selected } = this.state
    const  selected = { ..._selected, name: 'Barfoo' }
    this.setState({selected})
    //this.state: { selected: { id: 1, name: 'Barfoo' } }