我的代码有效,但我有一个最佳实践问题:我在状态中有一个对象数组,用户交互将一次更改一个对象的值。据我所知,我不应该直接改变状态,我应该总是使用setState
。如果我想以任何代价避免这种情况,我将通过迭代深度克隆数组,并更改克隆。然后将状态设置为克隆。在我看来,避免改变我以后会改变的状态只会降低我的表现。
详细版本:
this.state.data是一个对象数组。它代表论坛中的主题列表,收藏夹按钮将切换,调用clickCollect()
。
由于我在状态中有一个数组,当我更改一个项的is_collected属性时,我需要创建一个要使用的数组的副本,并在更改为新值后,我可以将其设置为状态。
var data = this.state.data.slice(0); data[index].is_collected = !data[index].is_collected; this.setState({data: data});
var data = this.state.data
:这会将指针复制到数组,而push(),shift()等会直接改变状态。 data
和this.state.data
都会受到影响。
var data = this.state.data.slice(0)
:这使得浅层克隆,推送和移位不会改变状态,但在我的克隆中,我仍然有指向状态数组元素的指针。因此,如果我更改data[0].is_collected
,this.state.data[0].is_collected
也会更改。这是在我致电setState()
之前发生的。
通常我应该这样做:
var data = []; for (var i in this.state.data) { data.push(this.state.data[i]); }
然后我更改index的值,当它为false时将其设置为true,否则为true:
data[index].is_collected = !data[index].is_collected;
改变状态:
this.setState({data: data});
考虑我的阵列相对较大或非常大,我想这次迭代会降低我APP的性能。如果我知道这是出于任何原因的正确方法,我会支付这笔费用。但是,在这个函数(clickCollect
)中,我总是将新值设置为状态,我不是在等待一个错误的API响应,它会说停止进行更改。在所有情况下,新值都将进入状态。实际上,我只是为了再次渲染UI而调用setState
。所以问题是:
for var i in ...
).slice(0)
)是否有意义?正在对数组内部的对象进行更改,因此浅层克隆仍会更改我的状态,就像副本(data = this.state.data
)一样。 我的代码已经简化,为了简单起见,API调用被删除了。
这是一个初学者的问题,所以也欢迎采用完全不同的方法。或链接到其他Q&甲
import React from 'react'; var ForumList = React.createClass({ render: function() { return <div className="section-inner"> {this.state.data.map(this.eachBox)} </div> }, eachBox: function(box, i) { return <div key={i} className="box-door"> <div className={"favorite " + (box.is_collected ? "on" : "off")} onTouchStart={this.clickCollect.bind(null, i)}> {box.id} </div> </div> }, getInitialState: function() { return {data: [ { id: 47, is_collected: false }, { id: 23, is_collected: false }, { id: 5, is_collected: true } ]}; }, clickCollect: function(index) { var data = this.state.data.slice(0); data[index].is_collected = !data[index].is_collected; this.setState({data: data}); } }); module.exports = ForumList;
答案 0 :(得分:3)
我个人并不总是遵循这个规则,如果你真的明白你想要做什么,那么我不认为这是一个问题。
var data = this.state.data.slice(0);
data[index].is_collected = !data[index].is_collected;
this.setState({data: data});
在这种情况下,改变状态并再次调用setState
就好了
this.state.data[index].is_collected = !this.state.data[index].is_collected;
this.setState({data: this.state.data});
你应该避免改变你的状态的原因是,如果你引用了this.state.data
并多次调用setState
,你可能会丢失数据:
const myData = this.state.data
myData[0] = 'foo'
this.setState({ data: myData })
// do something...
// ...
const someNewData = someFunc()
this.setState({ data: someNewData })
myData[1] = 'bar' // myData is still referencing to the old state
this.setState({ data: myData }) // you lose everything of `someNewData`
如果您真的关心这一点,请转到immutable.js
答案 1 :(得分:1)
如果您想要遵循最佳做法,则在更改任何属性时,应该对所有数组进行浅层复制。请查看“不可变”库实现。
但是,根据我的经验,从我的观点来看,如果您有“shouldCompomenentUpdate”实现,则应调用setState
方法。如果你认为,你的浅拷贝将消耗更多的资源,然后对虚拟dom检查作出反应,你可以这样做:
this.state.data[0].property = !this.state.data[0].property;
this.forceUpdate();
答案 2 :(得分:0)
使状态静音直接打破了React数据流的主要原则(使其成为单向),使您的应用程序非常脆弱,基本上忽略了整个组件的生命周期。
所以,虽然没有什么能阻止你在没有setState({})的情况下改变组件状态,但如果你想真正利用React,你将不得不不惜一切代价避免这种情况,否则你将跳过其中一个库的核心功能。
答案 3 :(得分:0)
如果我理解你的问题,你就有一个对象数组,当数组中单个对象的属性发生变化时,
- 创建数组的深层克隆并传递给setState
- 创建浅层克隆并传递给setState
醇>
我刚刚使用redux
sample todo app进行了检查,如果对象的单个属性发生更改,则需要创建该单个对象的新副本,而不是整个数组。我建议您阅读redux
,如果可能,请使用管理应用的状态。