React - 在不使用 setState 的情况下更改状态:必须避免吗?
React - Changing the state without using setState: Must avoid it?
我的代码有效,但我有一个最佳实践问题:我在状态中有一个 object 数组,用户交互将更改一个值 object一次。据我所知,我不应该直接改变状态,我应该总是使用 setState
代替。如果我想以任何代价避免这种情况,我将通过迭代深度克隆数组,并更改克隆。然后将状态设置为克隆。在我看来,避免改变我以后无论如何都会改变的状态只会降低我的表现。
详细版本:
this.state.data 是 object 的数组。它表示论坛中的主题列表,收藏按钮将切换,调用 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]);
}
然后我更改索引处的值,当它为假时将其设置为真,当它为真时将其设置为假:
data[index].is_collected = !data[index].is_collected;
并更改状态:
this.setState({data: data});
考虑到我的数组比较大或者非常大,我想这个迭代会降低我的APP的性能。如果出于任何原因我知道这是正确的方法,我会支付这笔费用。但是,在这个函数 (clickCollect
) 中,我总是将新值设置为状态,我不是在等待一个错误的 API 响应,它会说停止进行更改。在所有情况下,新值都会进入状态。实际上,我调用 setState
只是为了让 UI 再次渲染。所以问题是:
- 在这种情况下我必须创建深层克隆吗? (
for var i in ...
)
- 如果不是,如果我的数组包含 object,那么制作一个浅层克隆 (
.slice(0)
) 是否有意义?更改是在数组内部的 objects 上进行的,因此浅克隆仍然会更改我的状态,就像副本 (data = this.state.data
) 一样。
我的代码已简化,为了简单起见,API 调用被删除。
这是一个初学者的问题,所以也欢迎使用完全不同的方法。或其他问答链接。
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;
直接屏蔽状态打破了 React 数据流的主要原则(它被设计成单向的),使你的应用程序非常脆弱并且基本上忽略了整个组件生命周期。
因此,虽然没有什么能真正阻止您在不使用 setState({}) 的情况下改变组件状态,但如果您想真正利用 React,则必须不惜一切代价避免这种情况,否则您将跳过其中一个库的核心功能。
就我个人而言,我并不总是遵守规则,如果你真的明白你想做什么,那么我认为这不是问题。
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
如果我对你的问题理解正确,你有一个对象数组,当数组中单个对象的 属性 发生变化时,
- Create a deep clone of the array and pass to setState
- Create a shallow clone and pass to setState
我刚刚检查了 redux
sample todo app,如果对象的单个 属性 发生更改,您必须创建该单个对象的新副本而不是整个对象大批。我建议您阅读 redux
并在可能的情况下使用它来管理您的应用程序的状态。
如果您想遵循 React 最佳实践,您应该在更改任何内容时对所有数组进行浅拷贝 属性。请查看 "immutable" 库实现。
但是,根据我的经验和我的意见,如果您有 "shouldCompomenentUpdate" 实现,则应该调用 setState
方法。如果你认为你的浅拷贝会消耗更多的资源,那么 react virtual dom 检查,你可以这样做:
this.state.data[0].property = !this.state.data[0].property;
this.forceUpdate();
我的代码有效,但我有一个最佳实践问题:我在状态中有一个 object 数组,用户交互将更改一个值 object一次。据我所知,我不应该直接改变状态,我应该总是使用 setState
代替。如果我想以任何代价避免这种情况,我将通过迭代深度克隆数组,并更改克隆。然后将状态设置为克隆。在我看来,避免改变我以后无论如何都会改变的状态只会降低我的表现。
详细版本:
this.state.data 是 object 的数组。它表示论坛中的主题列表,收藏按钮将切换,调用 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]); }
然后我更改索引处的值,当它为假时将其设置为真,当它为真时将其设置为假:
data[index].is_collected = !data[index].is_collected;
并更改状态:
this.setState({data: data});
考虑到我的数组比较大或者非常大,我想这个迭代会降低我的APP的性能。如果出于任何原因我知道这是正确的方法,我会支付这笔费用。但是,在这个函数 (clickCollect
) 中,我总是将新值设置为状态,我不是在等待一个错误的 API 响应,它会说停止进行更改。在所有情况下,新值都会进入状态。实际上,我调用 setState
只是为了让 UI 再次渲染。所以问题是:
- 在这种情况下我必须创建深层克隆吗? (
for var i in ...
) - 如果不是,如果我的数组包含 object,那么制作一个浅层克隆 (
.slice(0)
) 是否有意义?更改是在数组内部的 objects 上进行的,因此浅克隆仍然会更改我的状态,就像副本 (data = this.state.data
) 一样。
我的代码已简化,为了简单起见,API 调用被删除。
这是一个初学者的问题,所以也欢迎使用完全不同的方法。或其他问答链接。
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;
直接屏蔽状态打破了 React 数据流的主要原则(它被设计成单向的),使你的应用程序非常脆弱并且基本上忽略了整个组件生命周期。
因此,虽然没有什么能真正阻止您在不使用 setState({}) 的情况下改变组件状态,但如果您想真正利用 React,则必须不惜一切代价避免这种情况,否则您将跳过其中一个库的核心功能。
就我个人而言,我并不总是遵守规则,如果你真的明白你想做什么,那么我认为这不是问题。
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
如果我对你的问题理解正确,你有一个对象数组,当数组中单个对象的 属性 发生变化时,
- Create a deep clone of the array and pass to setState
- Create a shallow clone and pass to setState
我刚刚检查了 redux
sample todo app,如果对象的单个 属性 发生更改,您必须创建该单个对象的新副本而不是整个对象大批。我建议您阅读 redux
并在可能的情况下使用它来管理您的应用程序的状态。
如果您想遵循 React 最佳实践,您应该在更改任何内容时对所有数组进行浅拷贝 属性。请查看 "immutable" 库实现。
但是,根据我的经验和我的意见,如果您有 "shouldCompomenentUpdate" 实现,则应该调用 setState
方法。如果你认为你的浅拷贝会消耗更多的资源,那么 react virtual dom 检查,你可以这样做:
this.state.data[0].property = !this.state.data[0].property;
this.forceUpdate();