我有两个问题,这里的state 是什么?
实例属性,例如在构造函数中设置this.state = {value: 0};。它使用的是目前处于第 2 阶段的 Public Class Fields proposal。(increment 和 decrement 也是如此,它们是实例字段,其值是箭头函数,因此它们关闭 this。)
这是一个局部变量吗?
没有。
prevState 来自哪里?为什么在 setState 中使用箭头函数?做 this.setState({value:'something'}) 不是很容易吗?
来自the documentation:
React 可以将多个 setState() 调用批处理到单个更新中以提高性能。
由于this.props 和this.state 可能会异步更新,因此您不应依赖它们的值来计算下一个状态。
例如,这段代码可能无法更新计数器:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
要修复它,请使用setState() 的第二种形式,它接受函数而不是对象。该函数将接收先前的状态作为第一个参数,并将应用更新时的道具作为第二个参数:
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
...这正是引用的代码正在做的事情。这是错误的:
// Wrong
increment = () => {
this.setState({
value: this.state.value + 1
});
};
...因为它依赖于this.state 的状态,上面告诉我们不要这样做;所以引用的代码会这样做:
increment = () => {
this.setState(prevState => ({
value: prevState.value + 1
}));
};
这里证明了 React 可能以不明显的方式批量调用,以及为什么我们需要使用 setState 的回调版本:这里,我们有 increment 和 decrement 被调用两次 每次点击而不是一次(一次按按钮,一次按包含按钮的跨度)。单击+ 一次 应该将计数器增加到2,因为increment 被调用了两次。但是因为我们没有使用 setState 的函数回调版本,所以它没有:其中一个对 increment 的调用变成了无操作,因为我们使用了一个陈旧的 this.state.value 值:
class Counter extends React.Component {
state = { value: 0 };
increment = () => {
/*
this.setState(prevState => ({
value: prevState.value + 1
}));
*/
console.log("increment called, this.state.value = " + this.state.value);
this.setState({
value: this.state.value + 1
});
};
fooup = () => {
this.increment();
};
decrement = () => {
/*
this.setState(prevState => ({
value: prevState.value - 1
}));
*/
console.log("decrement called, this.state.value = " + this.state.value);
this.setState({
value: this.state.value - 1
});
};
foodown = () => {
this.decrement();
};
render() {
return (
<div>
{this.state.value}
<span onClick={this.fooup}>
<button onClick={this.increment}>+</button>
</span>
<span onClick={this.foodown}>
<button onClick={this.decrement}>-</button>
</span>
</div>
)
}
}
ReactDOM.render(
<Counter />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
使用函数回调,它可以正常工作(没有对increment 的调用变为无操作):
class Counter extends React.Component {
state = { value: 0 };
increment = () => {
this.setState(prevState => {
console.log("Incrementing, prevState.value = " + prevState.value);
return {
value: prevState.value + 1
};
});
};
fooup = () => {
this.increment();
};
decrement = () => {
this.setState(prevState => {
console.log("Decrementing, prevState.value = " + prevState.value);
return {
value: prevState.value - 1
};
});
};
foodown = () => {
this.decrement();
};
render() {
return (
<div>
{this.state.value}
<span onClick={this.fooup}>
<button onClick={this.increment}>+</button>
</span>
<span onClick={this.foodown}>
<button onClick={this.decrement}>-</button>
</span>
</div>
)
}
}
ReactDOM.render(
<Counter />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
在这种情况下,当然,我们可以查看render 方法并说“嘿,increment 在单击期间会被调用两次,我最好使用回调版本的setState。”但是,与其假设在确定下一个状态时使用this.state 是安全的,不如不 假设这一点。在一个复杂的组件中,使用 mutator 方法很容易以一种 mutator 方法的作者可能没有想到的方式。因此,React 作者的声明:
因为this.props 和this.state 可能会异步更新,您不应依赖它们的值来计算下一个状态。