我使用的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>
);
}
}
渲染时,它看起来如下所示:
发布于 2018-10-15 05:35:50
如果要修改状态中的现有值,则永远不应直接从状态获取值并更新状态对象,而应使用setState
中的updater函数,以便确保状态值是更新状态时所需的状态值。这就是React的状态是如何工作的,这是一个非常常见的React错误。
setState()并不总是立即更新组件。它可能会批量或将更新推迟到以后。这使得在调用setState()之后立即读取this.state成为一个潜在的陷阱。相反,可以使用componentDidUpdate或setState回调(setState(updater,callback)),这两个回调都保证在应用更新后触发。如果您需要根据之前的状态设置状态,请阅读下面的更新程序参数。
除非shouldComponentUpdate()返回false,否则setState()总是会导致重新呈现。如果正在使用可变对象,并且无法在shouldComponentUpdate()中实现条件呈现逻辑,则仅在新状态与以前的状态不同时调用setState()可避免不必要的重新呈现。
第一个参数是一个带有签名的更新函数:
(state, props) => stateChange
状态是对应用更改时的组件状态的引用。它不应该被直接变异。相反,应该通过基于state和props的输入构建一个新对象来表示更改。
由更新器函数接收的状态和属性都保证是最新的。更新器的输出与状态进行了简单的合并。
因此,当您想要使用更新程序函数的第一个参数更新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>
);
}
}
发布于 2018-10-15 04:12:46
你已经回答了你自己的问题:不要改变状态。此外,最佳实践建议使用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
设置为对象而不是数组,则可以使其更整洁:
lowerPlayerHealth = (indexToUpdate) => () => {
this.setState(state => ({
...state,
playerState: {
...state.playerState,
[indexToUpdate]: {
...state.playerState[indexToUpdate],
hp: state.playerState[idToindexToUpdatepdate].hp - 1
}
}
}));
}
https://stackoverflow.com/questions/52806627
复制相似问题