import React, { Component } from 'react';
class Counter extends Component {
state = { value: 0 };
increment = () => {
this.setState(prevState => ({
value: prevState.value + 1
}));
};
decrement = () => {
this.setState(prevState => ({
value: prevState.value - 1
}));
};
render() {
return (
<div>
{this.state.value}
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
)
}
}
通常我看到的是人们在构造函数中执行this.state,如果他使用了es6类。如果他不是他可能使用getinitialstate函数放置状态。但是上面的代码(是的,它是一个工作代码),并没有使用这两者。我有2个问题,这里的状态是什么?是一个局部变量?如果是,为什么它没有const
? prevState来自哪里?为什么在setState中使用箭头函数?是不是很容易做this.setState({value:'something'})
?
答案 0 :(得分:17)
我有两个问题,
state
这里是什么?
实例属性,例如在构造函数中设置this.state = {value: 0};
。它使用当前位于第2阶段的Public Class Fields proposal。increment
和decrement
也是如此,它们是值为箭头函数的实例字段,因此它们会关闭this
。)
是一个局部变量吗?
没有
prevState来自哪里?为什么在setState中使用箭头函数?是不是很容易做到这一点.setState({value:&#39; something&#39;})?
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
和{{1}每次点击被叫两次,而不是一次(一次是按钮,一次是包含按钮的跨度)。点击decrement
一次会将计数器增加到2,因为+
会被调用两次。但是因为我们还没有使用increment
的函数回调版本,所以它没有:其中一个对setState
的调用变为无操作,因为我们正在使用陈旧的increment
值:
this.state.value
&#13;
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")
);
&#13;
使用函数回调,它可以正常工作(没有调用<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
&#13;
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")
);
&#13;
在这种情况下,当然,我们可以查看<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>
方法并说出&#34;嘿,render
会在点击期间被调用两次,我最好使用回调版increment
。&#34;但是,在确定下一个状态时,不要假设使用setState
是安全的,最佳做法是不来假设。在一个复杂的组件中,它是一种易于使用的mutator方法,其方式是mutator方法的作者可能没有想到的。因此,React作者的陈述:
由于
this.state
和this.props
可能会异步更新,您不应该依赖其值来计算下一个状态。
答案 1 :(得分:3)
关于问题2,请参阅Dan在这里的好答案:Do I need to use setState(function) overload in this case?
不,它不是局部变量。它与在构造函数中声明this.state
相同。
是的,在这种情况下,您可以使用this.setState({ value: this.state.value + 1 })
,结果将是相同的。
但请注意,通过使用功能性setState
,您可以获得一些好处:
如果你在外面声明它,可以重用setState函数:
const increment = prevState => ({
value: prevState.value + 1
})
现在如果你有几个组件需要使用这个功能,你可以在任何地方导入和重用逻辑。
this.setState(increment)
React压缩了几个setState
并批量执行它们。这可能会导致一些意外行为。请参阅以下示例:
http://codepen.io/CodinCat/pen/pebLaZ
add3 () {
this.setState({ count: this.state.count + 1 })
this.setState({ count: this.state.count + 1 })
this.setState({ count: this.state.count + 1 })
}
执行此功能count
只会加1
如果使用功能setState:
http://codepen.io/CodinCat/pen/dvXmwX
const add = state => ({ count: state.count + 1 })
this.setState(add)
this.setState(add)
this.setState(add)
将按预期计算+3。
您可以在此处查看文档:{{3}}
答案 2 :(得分:1)
问题1:
是的,您可以在没有React类组件的构造函数的情况下初始化状态:
class Counter extends Component {
state = { value: 0 };
handleIncrement = () => {
this.setState(prevState => ({
value: prevState.value + 1
}));
};
handleDecrement = () => {
this.setState(prevState => ({
value: prevState.value - 1
}));
};
render() {
return (
<div>
{this.state.value}
<button onClick={this.handleIncrement}>+</button>
<button onClick={this.handleDecrement}>-</button>
</div>
)
}
}
它使用类字段声明。您可以通过here检出示例应用程序。您还可以在React函数组件中使用状态(不使用构造函数),但可以使用高阶组件或渲染prop组件。您可以找到有关here的更多信息。
问题2的答案:有两种方法可以使用this.setState
更新React中的本地状态。您可以使用对象或函数。如果您不依赖组件的属性或状态来更新其状态,则该对象就很好。如果您依赖道具或状态,则应改用函数,因为函数在执行时始终具有最新的道具和状态。这是因为this.setState()
是异步执行的,因此在使用对象而不是函数更新本地状态时,可以使用陈旧状态或道具。您可以详细了解here。