updater: 更新数据 FUNCTION/OBJECT callback: 更新成功后的回调 FUNCTION
// updater - Function this.setState((prevState, props) => { return {counter: prevState.counter + props.step}; }); // update - Object this.setState({quantity: 2})
1.异步:react一般会集齐一批须要更新的组件,而后一次性更新来保证渲染的性能 2.浅合并 Objecr.assign()
// setState回调函数 changeTitle: function (event) { this.setState({ title: event.target.value }, () => this.APICallFunction()); }, APICallFunction: function () { // Call API with the updated value }
onClick = () => { this.setState({ index: this.state.index + 1 }); this.setState({ index: this.state.index + 1 }); } // 最后解析为,后面的数据会覆盖前面的更改,因此最终只加了一次. Object.assign( previousState, {index: state.index+ 1}, {index: state.index+ 1}, ) //正确写法 onClick = () => { this.setState((prevState, props) => { return {quantity: prevState.quantity + 1}; }); this.setState((prevState, props) => { return {quantity: prevState.quantity + 1}; }); }
1.不要在render()函数里面写setstate(),除非你本身定制了shouldComponentUpdate方法,要否则会引发无限循环
render() { //this.setState return( //...dom ) }
2.为何不能使用第一个方式更新 react为了实现高效render, state实际上是一个队列,setState是将数据插入队列中,使用第一种不会触发渲染, react提供了setState的实例方法能够触发render,能够看后面的源码
// 1 this.state.num = 1 // 2 this.setState({ num: this.state.num + 1 })
3.对数组和对象等引用对象操做时,使用返回新对象的方法 array: 不要使用push、pop、shift、unshift、splice可以使用concat、slice、filter、扩展语法 object: Object.assgin/扩展语法
如图: pending queue 和 update queuereact
ReactComponent.prototype.setState = function (partialState, callback) { // 将setState事务放进队列中 // this.updater就是ReactUpdateQueue, this是组件的实例 this.updater.enqueueSetState(this, partialState); if (callback) { this.updater.enqueueCallback(this, callback, 'setState'); } };
enqueueSetState: function (publicInstance, partialState) { // 获取当前组件的instance // 实例中有两个很是重要的属性:_pendingStateQueue(待更新队列) 与 _pendingCallbacks(更新回调队列) var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState'); // 初始化待更新队列 var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []); // 将要更新的state放入一个数组里 queue.push(partialState); // 将要更新的component instance也放在一个队列里 enqueueUpdate(internalInstance); }
当前若是正处于建立/更新组件的过程,就不会马上去更新组件,而是先把当前的组件放在dirtyComponent里,因此不是每一次的setState都会更新组件
function enqueueUpdate(component) { // 若是没有处于批量建立/更新组件的阶段,则处理update state事务 if (!batchingStrategy.isBatchingUpdates) { batchingStrategy.batchedUpdates(enqueueUpdate, component); return; } // 若是正处于批量建立/更新组件的过程,将当前的组件放在dirtyComponents数组中 dirtyComponents.push(component); }
var ReactDefaultBatchingStrategy = { // 用于标记当前是否出于批量更新 isBatchingUpdates: false, // 当调用这个方法时,正式开始批量更新 batchedUpdates: function (callback, a, b, c, d, e) { var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; ReactDefaultBatchingStrategy.isBatchingUpdates = true; // 若是当前事务正在更新过程在中,则调用callback,既enqueueUpdate if (alreadyBatchingUpdates) { return callback(a, b, c, d, e); } else { // 不然执行更新事务 return transaction.perform(callback, null, a, b, c, d, e); } } };
initalize(空函数) -> perform(anyMethos) -> close
// 将isBatchingUpdates置为false var RESET_BATCHED_UPDATES = { initialize: emptyFunction, close: function () { ReactDefaultBatchingStrategy.isBatchingUpdates = false; } }; // 循环全部dirtyComponent,调用updateComponent来执行全部的生命周期方法,componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate, render, componentDidUpdate 最后实现组件的更新 var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction, close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates) }; var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
面试官:“react中setState是同步的仍是异步?” 我:“异步的,setState不能立马拿到结果。” 面试官:“那什么场景下是异步的,可不多是同步,什么场景下又是同步的?”
下题结果是:面试
class App extends React.Component { state = { val: 0 } componentDidMount() { this.setState({ val: this.state.val + 1 }) console.log(this.state.val) this.setState({ val: this.state.val + 1 }) console.log(this.state.val) setTimeout(_ => { this.setState({ val: this.state.val + 1 }) console.log(this.state.val); this.setState({ val: this.state.val + 1 }) console.log(this.state.val) }, 0) } render() { return <div>{this.state.val}</div> } } // 结果就为 0, 0, 2, 3
1.setState 只在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout 中都是同步的。 2.setState的“异步”并非说内部由异步代码实现,其实自己执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新以前,致使在合成事件和钩子函数中无法立马拿到更新后的值,形式了所谓的“异步”,固然能够经过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。 3.setState 的批量更新优化也是创建在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,在“异步”中若是对同一个值进行屡次 setState , setState 的批量更新策略会对其进行覆盖,取最后一次的执行,若是是同时 setState 多个不一样的值,在更新时会对其进行合并批量更新。
合成事件,react为了解决跨平台,兼容性问题,本身封装了一套事件机制,代理了原生的事件,像在jsx中常见的onClick、onChange这些都是合成事件 合成事件中也有batchedUpdates方法,是经过一样的事务完成的
class App extends Component { state = { val: 0 } increment = () => { this.setState({ val: this.state.val + 1 }) console.log(this.state.val) // 输出的是更新前的val --> 0 } render() { return ( <div onClick={this.increment}> {`Counter is: ${this.state.val}`} </div> ) } }
整个生命周期中就是一个事物操做,因此标识位isBatchingUpdates = true,因此流程到了enqueueUpdate()时,实例对象都会加入到dirtyComponents 数组中
class App extends Component { state = { val: 0 } componentDidMount() { this.setState({ val: this.state.val + 1 }) console.log(this.state.val) // 输出的仍是更新前的值 --> 0 } render() { return ( <div> {`Counter is: ${this.state.val}`} </div> ) } }
原生事件是指非react合成事件,原生自带的事件监听 addEventListener ,或者也能够用原生js、jq直接 document.querySelector().onclick 这种绑定事件的形式都属于原生事件 原生事件绑定不会经过合成事件的方式处理,天然也不会进入更新事务的处理流程。setTimeout也同样,在setTimeout回调执行时已经完成了原更新组件流程,不会放入dirtyComponent进行异步更新,其结果天然是同步的。
class App extends Component { state = { val: 0 } changeValue = () => { this.setState({ val: this.state.val + 1 }) console.log(this.state.val) // 输出的是更新后的值 --> 1 } componentDidMount() { document.body.addEventListener('click', this.changeValue, false) } render() { return ( <div> {`Counter is: ${this.state.val}`} </div> ) } }
基于event loop的模型下, setTimeout 中里去 setState 总能拿到最新的state值。api
class App extends Component { state = { val: 0 } componentDidMount() { setTimeout(_ => { this.setState({ val: this.state.val + 1 }) console.log(this.state.val) // 输出更新后的值 --> 1 }, 0) } render() { return ( <div> {`Counter is: ${this.state.val}`} </div> ) } }
在 setState 的时候react内部会建立一个 updateQueue ,经过 firstUpdate 、 lastUpdate 、 lastUpdate.next 去维护一个更新的队列,在最终的 performWork 中,相同的key会被覆盖,只会对最后一次的 setState 进行更新数组
class App extends Component { state = { val: 0 } batchUpdates = () => { this.setState({ val: this.state.val + 1 }) this.setState({ val: this.state.val + 1 }) this.setState({ val: this.state.val + 1 }) } render() { return ( <div onClick={this.batchUpdates}> {`Counter is ${this.state.val}`} // 1 </div> ) } }
引起感想来源:dom