React的核心思想是组件化,而组件中最重要的概念是State(状态),State是一个组件的UI数据模型,是组件渲染时的数据依据。默认情况下,此State状态不会与组件的父组件或子组件共享。它完全由组件本身拥有和控制。下面是一个具有State状态的简单组件。
class Component extends React.Component {
constructor(props){
super(props);
this.state = {
counter: 0
}
}
render() {
return <div>{this.state.counter}</div>
}
}
如何更新State状态
React提供了一个名为setState的函数。您应该始终使用setstate函数来更改状态,而不要直接改变它。
//千万不要这样做
this.state.counter = 2;
//总是这样做
this.setState({
counter: 2
});
setState
setState函数的第一个参数可以采用新的状态对象或函数。它还有一个可选的第二个参数,这是一个在状态更新时执行的回调。
setState(newState || function, optional callback)
setState更新状态的方式是:
如果setstate函数的第一个参数是一个对象,它将当前状态对象与传递给setstate函数的任何对象合并。例如:
state = { a: 1, b: 2, c: 3} //当前状态
this.setState({ a: 3 }); //参数是一个对象
//它将**合并**初始状态与传递给setState的对象
//结果只更新该键的值
state = { a: 3, b: 2, c: 3 } //SetState刷新后的状态
如果第一个参数是一个函数,那么它首先通过将当前状态作为参数传递来执行该函数。函数必须返回一个对象。然后它将这个输出与当前状态合并,就像上面所做的那样。例如:
state = { a: 1, b: 2, c: 3} //初始状态
//我们用函数调用了setstate
this.setState(currentState => ({
a: currentState.a + 1
}));
//通过将当前状态对象作为参数传递来执行函数。
//由于currentState.a为1,函数返回a:2
//现在它将返回的对象与原始状态合并
state = { a: 2, b: 2, c: 3 } //调用setState后的状态
关于setState函数,您必须知道的一件事是它可能是异步的。所以不要依赖它来立即更新状态。这不是一个bug,它是按设计的。如果您想了解setstate调用背后的异步设计决策,这里有一个很好的解释。
由于setState可以是异步的,所以下面的代码不会给您正确的结果,因为到了console.log state.counter值时,它将不会被更新。
//错误的结果。不要依靠setstate来同步
console.log(this.state.counter);//打印 0
this.setState({
counter: this.state.counter + 1
}); //这是异步调用
console.log(this.state.counter);//静态打印 0
另外,如果要使用当前状态值更新状态,请始终在setstate内使用updater函数,而不是传递对象。例如,下面的代码将不起作用。
//总是这样做
//如果使用当前状态值更新状态,请始终使用updater函数
console.log(this.state.counter); //prints 0
this.setState((state) => ({ counter: state.counter + 1}) );
this.setState((state) => ({ counter: state.counter + 1}) );
this.setState((state) => ({ counter: state.counter + 1}) );
//这是保证工作的!
//当所有三个调用都刷新时,
//this.state.counter将为3
State注意事项:
- 不要直接改变State状态。始终使用this.setState更新状态。
- 如果新State状态不依赖于旧State状态,则可以使用this.setState(object)构建。
- 如果新状态依赖于旧状态,则使用this.setState(function(currentState){ .. })构建。
Props属性与State状态有什么不同,又有什么相似之处?
- State状态和props之间的区别在于,State状态是私有的,并且完全受控于当前组件,而props是由组件的父组件传递给子组件的东西。
- 相似性(有点类似)是,当组件的State状态更改或组件的props属性更改时,响应地会自动重新render组件。
- 组件的render函数是一个同时具有状态和属性的函数,这意味着它定义了给定状态和属性时组件的外观。
- 它应该是纯粹的函数,从某种意义上说,如果组件具有相同的状态和属性,那么无论调用多少次,它都应该呈现完全相同的内容,并且不应该有任何副作用。
延伸:
啥时候用到State
- 组件中用到的一个变量是不是应该作为组件State,可以通过下面的4条依据进行判断:
- 这个变量是否是通过Props从父组件中获取?如果是,那么它不是一个state状态。
- 这个变量是否在组件的整个生命周期中都保持不变?如果是,那么它不是一个state状态。
- 这个变量是否可以通过其他状态(State)或者属性(Props)计算得到?如果是,那么它不是一个state状态。
- 这个变量是否在组件的render方法中使用?如果不是,那么它不是一个state状态。这种情况下,这个变量更适合定义为组件的一个普通属性,例如组件中用到的定时器,就应该直接定义为this.timer,而不是this.state.timer。
- 并不是组件中用到的所有变量都是组件的state状态!当存在多个组件共同依赖一个state状态时,一般的做法是state状态上移,将这个state状态放到这几个组件的公共父组件中。
为啥说setState函数有可能是异步的
要知道setState本质是通过一个队列机制实现state更新的。 执行setState时,会将需要更新的state合并后放入状态队列,而不会立刻更新state,队列机制可以批量更新state。
如果不通过setState而直接修改this.state,那么这个state不会放入状态队列中,下次调用setState时对状态队列进行合并时,会忽略之前直接被修改的state,这样我们就无法合并了,而且实际也没有把你想要的state更新上去。