在React中,数据是自顶向下单向流动的,即从父组件到子组件。这样组件之间的关系变得简单并且可预测。
state和props是React组件中最重要的改建,如果顶层组件初始化props, 那么React会向下遍历整颗组件树,重新尝试渲染所有的子组件。而state只关心每个组件自己内部的状态,这些状态只能在组件内部改变。
当组件内部使用内置方法setState时,该组件就会尝试重新渲染,因为我们改变了内部状态,组件需要更新。
改变组件内部状态 setState
1. 不能直接修改State, 要使用setState构造函数时唯一可以给this.state赋值的地方
//构造函数时唯一可以给this.state赋值的地方
class MyComponent extends React.Component {
constructor(){
this.state = {
name: 'test'
}
}
}
//修改组件内部状态
this.setState({
name: 'newName'
})
2. setState是一个异步方法,一个生命周期内所有的setState会合并操作
可以让setState接受一个函数,这个函数用上一个state作为参数,本次更新被应用时的props作为第二个参数。
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
3. setState 传入一个对象或函数,和 一个函数赋值结束后执行该函数
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
},function(){
console.log(123)
});
//或者
this.setState({
name: 'newName'
},function(){
console.log(123)
})
setState源码
Component.prototype.setState = function (partialState, callback) {
//第一个参数可以是对象,函数或者null
!(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : void 0;
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
为什么setState是异步更新?
React通过this.state来访问state, 通过this.setState()方法来更新state, 当this.setState()被调用时,React会重新调用render方法来重新渲染UI
1. 为什么不能用this.state = {}来修改state?
setState通过队列机制实现state更新, React也正是通过状态队列实现了setState的异步更新,避免频繁的重复更新state
当执行setState
时,会将需要更新的state
合并后放入状态队列,不会立即更新。
如果直接修改this.state
就不会被放入状态队列。
当下次调用setState
并对状态队列合并时, 会忽略掉之前修改的state
,造成错误。
2. setState源码
从上面源码可知,使用setState方法后,调用了enqueueSetState()
源码位置: react-dom\cjs\react-dom.development.js
enqueueUpdate()将当前组件实例对应的fiber更新到执行队列
scheduleWork()执行一次更新, 会判断isBatchingUpdates,如果true, 执行批量修改,存入队列,如果是false,继续执行更新
例如:
返回结果是0,0,2,3;
isBatchingUpdates
初始是false
。
componentDidMount
开始时,isBatchingUpdates
已经变成true
了,scheduleWork()
方法中会判断本次操作是setState的批量操作,会合并state并存入队列。
componentDidMount
接受后,最终会把isBatchingUpdates
重置回之前的状态。
而setTimeout
是宏任务,不在本次执行栈中执行,所以前两次都打印出的是0;
所以setTimeout
执行时,isBatchingUpdates
是false,scheduleWork()
会直接执行更新