组件状态(state)是一种持有,处理和使用信息的方式。state包含的信息仅做用于一个给定组件的内部,并容许你根据它实现组件的一些逻辑。state一般是一个POJO(Plain Old Java[Script] Object)对象,改变它是使得组件从新render本身的方式之一。react
state是react背后原理的重要基础概念之一,可是它也有一些特色使得它用起来会有点难以捉摸而且有可能会致使在你的应用中出现一些预料以外的行为。ajax
更新State
惟一你能直接写this.state的地方应该是组件的构造函数中。在其它全部地方你都应该使用this.setState函数,它接受一个对象做为参数,这个对象最终会被合并到组件的当前状态中。安全
而在技术上你是能够经过this.state={//a new object}这种方式直接修改状态的,可是它不会引发组件使用新的值去从新渲染,而后致使状态不一致的问题。异步
setState是异步的
事实上setState会引发的一致性处理(reconciliation)——从新渲染组件树的过程,是下一个属性的基础即setState是异步的。这容许咱们在单个做用域内屡次调用setState而不会触发没必要要的从新渲染整个树。ide
这就是为何在你更新state后并不能立马看见新的值。函数
// 假设 this.state = { value: 0 } this.setState({ value: 1 }); console.log(this.state.value); // 0
React 也会尝试将屡次setState调用组合或者批处理为一次调用:学习
// 假设 this.state = { value: 0 }; this.setState({ value: this.state.value + 1}); this.setState({ value: this.state.value + 1}); this.setState({ value: this.state.value + 1});
在上面全部的调用完成后,this.state.value将是1,而不是咱们的指望值3。那么怎么获得指望值3呢?this
setState接受一个函数做为它的参数
若是你传递一个函数做为setState的第一个参数,React将会使用在当前调用时刻的state去调用它并指望你返回一个对象合并到state中。因此你能够把咱们上面的代码改为下面这样便可:翻译
// 假设 this.state = { value: 0 }; this.setState((state) => ({ value: state.value + 1})); this.setState((state) => ({ value: state.value + 1})); this.setState((state) => ({ value: state.value + 1}));
这样this.state.value 的值就是 3了,就和上面咱们认为指望值应该是3相一致了。
记住当在更新state为一个值的时候应始终使用这种语法,它的计算是基于前面的一个状态的。code
setState是同步的吗???
记住你刚才学习到setState是异步的。事实证实并不是始终如此。这取决于执行上下文,请看下面的例子:
render() { return <button onClick={this.inc}>Click to update</button> } inc() { console.log('before: ' + this.state.test); this.setState({ test: this.state.test+1 }); console.log('after: ' + this.state.test); }
点击按钮元素将会致使你的console中显示:
// click! before: 1 after: 1 // click! before: 2 after: 2
但若是咱们添加如下代码:
componentDidMount() { setInterval(this.inc, 1000); }
咱们将在console中看到:
before: 1 after: 2 before: 2 after: 3
所以,咱们须要学习何时指望获得哪一种行为吗?并非如此。假设setState的确是异步是很是安全的,由于在将来它就是如此。
setState接受一个回调函数
若是你须要执行一些函数,或者验证状态是否真的有更新正确。你还能够给setState传递一个函数做为第二个参数,这个函数会在状态更新完毕后获得执行。请记住因为单个块内的全部更新会被合并成一个,这将致使每一个setState中的回调中获得的state值是全更新的state。
另一种能够保证你的代码执行是在更新完成之后的方式是将执行代码放在componentWillUpdate 或者 componentDidUpdate中。然而对比回调函数的方式,这两个方法会在shouldComponentUpdate中阻止你的组件更新时不会被调用。
常见错误
其中最多见的错误之一就是在构造函数中使用props设置state的值。考虑以下代码:
class Component extends React.Component { constructor(props) { super(props); this.state = { value: this.props.value }; } render() { return <div>The value is: {this.state.value}</div> } }
若是它的父组件这样render它:
<Component value={42} />
它将会正确渲染value为42,但若是父组件中修改为以下:
<Component value={13} />
那它仍会认为this.state.value是42,这是由于React并不会销毁组件并从新建立它——它会重用一旦渲染好的组件,而且不会从新执行构造函数。要避免这个问题,你应该不要将props赋值给state,而应在render方法中使用this.props.value。
若是你仍是想要使用state(若是你的props是以一种很是复杂的计算的使用模式,你不但愿每一次render都执行这些复杂的计算),你还能够实现一种在须要的时候才去更新state的解决方案,例如:
class Component extends React.Component { constructor(props) { super(props); this.state = { value: this.props.value }; } componentWillReceiveProps(nextProps) { if(nextProps.value !== this.props.value) { this.setState({value: nextProps.value}); } } render() { return <div>The value is: {this.state.value}</div> } }
请记住任何componentWill*函数都不是一个合适的地方去触发side effect(如ajax请求),因此请使用
componentDidUpdate(previousProps, previousState),一样也提供和上面相似的if防御语句确保在没有变化时不会执行相关代码。
写在最后的话:这篇文章在Medium中得到超过2.1K的赞,挺不错的,值得翻译! 翻译若有不正,欢迎留言指出!