Answers:
我认为setState()
不做递归合并。
您可以使用当前状态的值this.state.selected
构造一个新状态,然后调用setState()
该状态:
var newSelected = _.extend({}, this.state.selected);
newSelected.name = 'Barfoo';
this.setState({ selected: newSelected });
我在这里使用过函数_.extend()
function(来自underscore.js库),selected
通过创建状态的浅表副本来防止对该状态的现有部分进行修改。
另一个解决方案是编写setStateRecursively()
对新状态进行递归合并的新状态,然后对其进行调用replaceState()
:
setStateRecursively: function(stateUpdate, callback) {
var newState = mergeStateRecursively(this.state, stateUpdate);
this.replaceState(newState, callback);
}
this.setState({ selected: { ...this.state.selected, name: 'barfoo' } })
其翻译为this.setState({ selected: _extends({}, this.state.selected, { name: 'barfoo' }) });
underscore
/ lodash
。请把它分解成自己的答案。
不可变性助手最近已添加到React.addons中,因此,您现在可以执行以下操作:
var newState = React.addons.update(this.state, {
selected: {
name: { $set: 'Barfoo' }
}
});
this.setState(newState);
由于许多答案都使用当前状态作为合并新数据的基础,因此我想指出这可能会中断。状态更改已排队,并且不会立即修改组件的状态对象。因此,在处理队列之前引用状态数据将为您提供过时的数据,这些数据不会反映您在setState中进行的挂起更改。从文档:
setState()不会立即更改this.state,但会创建一个挂起的状态转换。调用此方法后访问this.state可能会返回现有值。
这意味着在随后对setState的调用中使用“当前”状态作为引用是不可靠的。例如:
如果需要使用当前状态(例如,将数据合并到嵌套对象中),则setState可以选择接受函数作为参数而不是对象;该函数在先前对状态的任何更新之后被调用,并将状态作为参数传递-因此可以用于进行保证遵循先前更改的原子更改。
this.state
,这可能是过时的(或者更确切地说,等待排队更新),当你访问它。
我不想安装另一个库,因此这里是另一个解决方案。
代替:
this.setState({ selected: { name: 'Barfoo' }});
而是这样做:
var newSelected = Object.assign({}, this.state.selected);
newSelected.name = 'Barfoo';
this.setState({ selected: newSelected });
或者,感谢评论中的@ icc97,它更加简洁但可读性较低:
this.setState({ selected: Object.assign({}, this.state.selected, { name: "Barfoo" }) });
另外,要明确一点,此答案没有违反@bgannonpl提到的任何问题。
this.setState({ selected: Object.assign(this.state.selected, { name: "Barfoo" }));
Object.assign(a, b)
从中获取属性b
,插入/覆盖属性,a
然后返回a
。如果您直接修改状态,React的人就会感到很沮丧。
根据@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"}
}
});
我针对这种情况的解决方案是使用,如另一个答案指出的那样,不变性助手。
由于深度设置状态是一种常见情况,因此我创建了以下混合:
var SeStateInDepthMixin = {
setStateInDepth: function(updatePath) {
this.setState(React.addons.update(this.state, updatePath););
}
};
这个mixin包含在我的大多数组件中,我通常不再setState
直接使用。
使用此mixin,要获得所需的效果,您所需要做的就是以setStateinDepth
以下方式调用该函数:
setStateInDepth({ selected: { name: { $set: 'Barfoo' }}})
想要查询更多的信息:
this.state.test.two
在您this.state.test.one
使用进行更新后仍然会存在setStateInDepth({test: {one: {$set: 'new-value'}}})
。我在所有组件中都使用了此代码,以避开React的有限setState
功能。
this.state.test
是一个数组。更改为对象,解决了它。
我正在使用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!"})
}
似乎可以很好地实现我的目的,请记住,尽管您不能更改分配给顶级组件上的包装组件的属性的状态,因为每个包装组件都在跟踪自己的状态,但会更新顶级组件上的状态每次改变。
截至目前,
如果下一个状态取决于上一个状态,我们建议使用updater函数形式,而不是:
根据文档https://reactjs.org/docs/react-component.html#setstate,使用:
this.setState((prevState) => {
return {quantity: prevState.quantity + 1};
});
编辑:此解决方案用于使用传播语法。目标是使对象没有任何对的引用prevState
,因此prevState
不会被修改。但是在我的用法中,prevState
有时似乎被修改了。因此,为了完美克隆而没有副作用,我们现在转换prevState
为JSON,然后再次返回。(使用JSON的灵感来自MDN。)
记得:
setState(prevState => stateChange)
脚步
state
,你想改变步骤3和步骤4可以合并为一行。
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' } }
React状态不会执行递归合并,setState
而期望不会同时存在就地状态成员更新。您要么自己复制包含的对象/数组(使用array.slice或Object.assign),要么使用专用库。
像这个。NestedLink直接支持复合React状态的处理。
this.linkAt( 'selected' ).at( 'name' ).set( 'Barfoo' );
另外,指向selected
或的链接selected.name
可以作为单个prop传递到任何地方,并使用进行修改set
。
您设置了初始状态吗?
我将使用一些自己的代码作为示例:
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
我使用tmp var进行更改。
changeTheme(v) {
let tmp = this.state.tableData
tmp.theme = v
this.setState({
tableData : tmp
})
}
let original = {a:1}; let tmp = original; tmp.a = 5; // original is now === Object {a: 5}
即使尚未给您问题,也不要这样做。