我正在使用看起来像这样的React组件(下面是我使用的组件的简化版本)。
我的问题是:如何使用this.setState使其相同? 以下代码有效,但是我直接更改了状态,并且收到以下警告:
请勿直接更改状态。使用setState()
class App extends Component {
constructor(props) {
super(props);
this.state = {
playerState: [
{
name: 'Jack',
hp: 30
},{
name: 'Alan',
hp: 28
}
],
};
}
lowerPlayerHealth = (index) => () => {
this.state.playerState[index].hp = this.state.playerState[index].hp - 1
this.forceUpdate();
}
render() {
return (
<div className="App">
<p>Player 1: {this.state.playerState[0].name}</p>
<p>Health: {this.state.playerState[0].hp}</p>
<button onClick={this.lowerPlayerHealth(0)}>Hit player 1</button>
<p>Player 2: {this.state.playerState[1].name}</p>
<p>Health: {this.state.playerState[1].hp}</p>
<button onClick={this.lowerPlayerHealth(1)}>Hit player 2</button>
</div>
);
}
}
渲染后,它看起来像这样:
答案 0 :(得分:2)
如果要修改状态中的现有值,则永远不要直接从状态中获取值并更新状态对象,而应使用setState
中的updater函数,以便可以保证状态值是您在更新状态时需要的。这就是React状态的工作方式,这是一个非常常见的React错误。
setState()并不总是立即更新组件。它可能 批处理或将更新推迟到以后。这使得阅读this.state 在调用setState()之后立即发生潜在的陷阱。相反,使用 componentDidUpdate或setState回调(setState(updater, 回调)),保证在更新后都会触发 已应用。如果您需要根据之前的状态设置状态 状态,请阅读下面的updater参数。
setState()将始终导致重新渲染,除非 shouldComponentUpdate()返回false。如果存在易变的物体 使用和条件渲染逻辑无法在 shouldComponentUpdate(),仅在新状态时调用setState() 与以前的状态有所不同,将避免不必要的重新渲染。
第一个参数是带有签名的更新程序函数
(state, props) => stateChange
state是对更改时组件状态的引用 正在应用。它不应该直接突变。相反,变化 应该通过根据来自 状态和道具。
由updater函数接收的状态和道具均得到保证 是最新的。更新程序的输出与 状态。
因此,当您要使用updater函数的第一个参数更新setState函数中的组件时,必须准确获取值。
lowerPlayerHealth = (index) => () => {
// use setState rather than mutating the state directly
this.setState((state, props) => {
// state here is the current state. Use it to update the current value found in state and ensure that it will be set correctly
return (state); // update state ensuring the correct values
});
}
解决方案
要降低在状态下找到的值:
class App extends React.Component {
constructor(props){
super(props);
this.state = {
playerState: [
{
name: 'Jack',
hp: 30
}, {
name: 'Alan',
hp: 28
}
],
};
}
lowerPlayerHealth = (index) => () => {
this.setState((state, props) => {
state.playerState[index].hp -=1; //update the current value found in state
return (state); // update state ensuring the correct values
});
}
render() {
return (
<div className="App">
<p>Player 1: {this.state.playerState[0].name}</p>
<p>Health: {this.state.playerState[0].hp}</p>
<button onClick={this.lowerPlayerHealth(0)}>Hit player 1</button>
<p>Player 2: {this.state.playerState[1].name}</p>
<p>Health: {this.state.playerState[1].hp}</p>
<button onClick={this.lowerPlayerHealth(1)}>Hit player 2</button>
</div>
);
}
}
答案 1 :(得分:1)
您已经回答了自己的问题:不要改变状态。另外,最佳做法建议使用setState
的函数版本。
由于playerState
是一个数组,请使用Array.map
to create a new array containing the same objects,仅替换您要更改的数组:
lowerPlayerHealth = (indexToUpdate) => () => {
this.setState(state => ({
...state,
playerState: state.playerState.map(
(item, index) => index === indexToUpdate
? {
...item,
hp: item.hp - 1
}
: oldItem
)
}));
}
如果将playerState
设为 object 而不是数组,则可以使其更整洁:
lowerPlayerHealth = (indexToUpdate) => () => {
this.setState(state => ({
...state,
playerState: {
...state.playerState,
[indexToUpdate]: {
...state.playerState[indexToUpdate],
hp: state.playerState[idToindexToUpdatepdate].hp - 1
}
}
}));
}