BG:
目前的Component仍然在react框架中,也就是说React Native使用的Component是react框架中的组件,而Component有两大数据管理核心State和Props。也就是说即使你仅仅想用React Native开发APP,你也需要去了解React的相关知识,比如Component、State和Props,本文主要介绍State的使用。
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
一.初始化 this.state
在组件的 类构造函数(class constructor) 中初始化 this.state
:
export default class StatePage extends Component {
constructor(props) {
super(props);
this.state = {
name : '张三',
age:0,
};
};
}
注意:唯一可以分配 this.state 的地方是构造函数。
二、this.setState()
1.setState的原理:
这只是一个简单的原理图,setState背后的实现的详细过程需要借助React.js源代码和调用堆栈去查看。
2.setState引发的Component的更新过程:
setState会触发Component的以下4个生命周期方法,并依次执行:
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
shouldComponentUpdate
返回的是true
orfalse
决定了当前组件是否在state或props改变后是否进行render。shouldComponentUpdate
默认返回的是true
,也就是当前组件在setState或props改变后会进行render,刷新UI。
3.this.state何时才被更新?
要想观察this.state何时被更新,需要我们去在相关生命周期方法中去进行打印,而观察的结果是这样的:
当shouldComponentUpdate
返回true时:
1.当shouldComponentUpdate
函数被调用的时候,this.state没有得到更新;
2.当componentWillUpdate
函数被调用的时候,this.state依然没有得到更新;
3.直到render
函数被调用的时候,this.state才得到更新。
当shouldComponentUpdate
返回false时:
1.本次shouldComponentUpdate
函数被调用的时候,this.state没有得到更新;
2.当shouldComponentUpdate函数返回false,此时更新过程会被中断,render函数也不会被调用,但这时候React不会放弃掉对this.state的更新的,所以虽然不调用render,依然会更新this.state,在下次触发shouldComponentUpdate
时可以看到打印的this.state已经更新。
综上所述:直到render函数调用时(或者shouldComponentUpdate返回false,再次在shouldComponentUpdate函数中)才得到更新后的this.state。
三、setState使用:
执行setState时,会将需要更新的state合并后放入状态队列,而不会立刻更新state,队列机制可以批量更新state。
1. setState的几种场景:
(1).批量更新的典型,合并、异步setState:
clickBtn=()=>{
this.setState({
name:'王五',
});
this.setState({
age:30
});
this.setState({
age:20
});
}
合并是浅合并,所以 第二次this.setState({age }) 不会把 this.state.name 冲掉,但会完全替换上一次this.state.age 的值。
以上代码只会触发1次render,并且在render函数调用时this.state.age的值才被更新且是20。也就是多次的setState会被合并,并且单个属性的多次setState只有最后一次的更新会生效。
(2).非批量更新的典型,定时器中setState:
componentWillMount() {
console.log(`StatePage--componentWillMount`);
this.testTimer = setTimeout(
()=>{
this.setState({
age: this.state.age + 1,
});
this.setState({
age: this.state.age + 1,
});
this.setState({
age: this.state.age + 1,
});
},
0,
);
}
以上代码会触发3次render,并且每次this.state.age的值都会加1。
定时器中setState,对应了上面setState的原理图中的非bathUpdate分支。
(3)、依赖this.props 和 this.state的值更新this.state:
因为 this.props 和 this.state 可能是异步更新的,你不能依赖他们的值计算下一个state(状态)。
错误写法:
this.setState({
age: this.state.age + this.props.baseAge,
});
以上代码可能导致 age(年龄)更新失败!
正确写法:
setState()接收一个函数,而不是一个对象。该函数接收前一个状态值作为第 1 个参数,前一个props值作为第 2 个参数, 并将更新后的值进行回调,也就是利用上一次状态的age和props中的age进行计算来更新的age:
this.setState((state, props) => ({
age: state.age + props.baseAge,
}));
在上面使用了一个箭头函数,但是也可以使用一个常规的函数:
// 正确
this.setState(function(state, props) {
return {
counter:state.age + props.baseAge,
};
});
(4)、setState回调函数:
this.setState({age: 23}, ()=> {
console.log(this.state.age);//23
});
`setState的回调函数中可以获取到已经更新后的state,相当于componentDidUpdate函数或render函数中获取更新后的state。
四、数据自顶向下流动
无论作为父组件还是子组件,它都无法获悉一个组件是否有状态,同时也不需要关心另一个组件是定义为函数组件还是类组件。
这就是 state(状态) 经常被称为 本地状态 或 封装状态的原因。 它不能被拥有并设置它的组件 以外的任何组件访问。
一个组件可以选择将 state(状态) 向下传递,作为其子组件的 props(属性):
子组件StateComponent
通过 props(属性) 接收了父组件
传递的name和age的值,但它仍然不能获知该值是来自于父组件
的 state(状态) ,还是 父组件
的 props(属性),或者是父组件
中直接手动创建的。
五、总结:
1.不要使用 this.state 来直接修改 state,state值虽然会被更改,但不会触发render;
2.setState可能是异步的,不会立刻改变React组件中state的值;
3.React 为了优化性能,有可能会将多个 setState() 调用合并为一次更新
4.setState通过引发一次组件的更新过程来引发重新绘制;
5.不能依赖this.props 和 this.state的值计算下一个state(状态);
6.定时器中多次setState不会被合并,state的值会被立即更新;
7.setState可能会引发不必要的渲染,你可以在shouldComponentUpdate(object nextProps, object nextState)函数中根据实际情况决定是否触发render。