我有一个关于React和如何更新状态的问题。 假设我们有一个Players类,在其状态下包含一个称为player的对象数组。我们要更新此阵列中的一个播放器。我本可以这样:
class Players extends Component {
state = {
players: []
}
updatePlayer = id => {
const players = this.state.players.map(player => {
player.updated = player.id === id ? true:false;
return player
});
this.setState({players: players});
}
}
但是我的同事就是这样做的,而且它还在工作:
updatePlayer = id => {
const playerObj = this.state.players.find(item => {
return item.id === id
})
if (playerObj) {
playerObj.updated = true
this.setState({ playerObj })
}
}
React的函数setState更新players数组,而无需明确告知要这样做。所以,我有两个问题:
谢谢大家的解释!
答案 0 :(得分:2)
区别在于第二个片段滥用setState
来触发更新,因为它使用了playerObj
虚拟属性。这可以通过forceUpdate
来实现。
这两种方法都不正确。不可变状态在React中被提升为惯例。突变现有状态可能会导致期望状态不变的组件出现错误行为。它们会突变现有的player
对象,并且新的player.update
值将在使用该对象的任何地方使用,即使这是不希望的。
惯用的方法是在状态下使用不可变对象:
updatePlayer = id => {
this.setState(({ players }) => ({
players: players.map(player => ({
...player,
updated: player.id === id
}));
});
}
请注意,setState
是异步的,必须使用updater function以避免可能出现的竞争情况。
答案 1 :(得分:0)
这两个都不正确,因为您正在突变状态。 最好的方法是为该数组创建一个深拷贝(只是clone),然后对该克隆数组进行一些更改
您还可以使用lodash _.cloneDeep();
例如
class Example extends React.Component {
state = {
players: [
{id: 0, name: 'John'};
]
};
updatePlayer = id => {
const { players } = this.state;
const clonePlayers = players.map(a => ({...a}));
const player = clonePlayers.find(playerId => playerId === id);
player.name = 'Jack';
this.setState(() => ({
players: clonePlayers
}));
}
render() {
return (
<div>some</div>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
答案 2 :(得分:0)
是的,所有使用的都是参考。所有的javascript对象都是引用,因此,只要执行查找操作,便会获得对该对象的引用,因此对它进行更改将对其进行更新。
MainModel
关于推荐的方法,应该坚持使用显式更新您关心的状态变量。
答案 3 :(得分:0)
嗯,基本上它们是不一样的,您的同事代码之所以起作用,仅仅是因为他正在使用该对象的旧引用。因此,让我们看一下:
updatePlayer = id => {
const players = this.state.players.map(player => {
player.updated = player.id === id ? true:false;
return player
});
this.setState({players: players});
}
在函数上,您正在使用旧数组创建新的array
,这是正确的方法。
updatePlayer = id => {
const playerObj = this.state.players.find(item => {
return item.id === id
})
if (playerObj) {
playerObj.updated = true
this.setState({ playerObj })
}
}
在这里,您的朋友正在编辑他使用find
获得的对象的引用,然后保存playerObj
,这仅是您要编辑的数组中玩家的引用。在此之后,您应该注意到新状态将为
this.state = {
players: [p1, p2 ,p3, p4], // each p is a player.
//notice that playerObj is now a reference to some of the P on the players array
playerObj: {
...playerstuff,
updated: true
}
}
希望它会有所帮助:)