详解react setState
目录
- setState是同步还是异步
- 自定义合成事件和react钩子函数中异步更新state
- 原生事件和setTimeout中同步更新state
- setState相关源码
- 总结
setstate是同步还是异步
自定义合成事件和react钩子函数中异步更新state
以在自定义click事件中的setState为例
import React, { component } From 'react'; class test extends Component { constructor(PRops) { suPEr(props); this.state = { count: 1 } ; } handleClick = () => { this.setState({ count: this.state.count + 1 } ); this.setState({ count: this.state.count + 1 } ); this.setState({ count: this.state.count + 1 } ); console.LOG(this.state.count); } render() { return ( div style={ { width: '100px', height: '100px', backgroundColor: "yellow" } } > { this.state.count} /div> ) } } export default Test;
点击一次,最终this.state.count的打印结果是1,页面展示的是2。通过现象看,三次setState只是最后一次setState生效了,前两次都setState无效果。因为假如把第一次setState改为+3,count打印结果为1,展示结果为2,没有发生变化。而且没有同步获得count的结果。
此时,我们可以调整代码,通过setState的第二个参数,来获得更新后的state:
import React, { Component } from 'react'; class Test extends Component { constructor(props) { super(props); this.state = { count: 1 } ; } handleClick = () => { this.setState({ count: this.state.count + 3 } , () => { console.log('1', this.state.count) } ); this.setState({ count: this.state.count + 1 } , () => { console.log('2', this.state.count); } ); this.setState({ count: this.state.count + 1 } , () => { console.log('3', this.state.count); } ); console.log(this.state.count); } render() { return ( div style={ { width: '100px', height: '100px', backgroundColor: "yellow" } } > { this.state.count} /div> ) } } export default Test;
此时,点击一次,三个setState的回调函数中,打印结果分别是。
1
1: 2
2: 2
3: 2
首先,最后一行直接打印1。然后,在setState的回调中,打印出的结果都是最新更新的2。虽然前两次setState未生效,但是它们第二个参数中还是会打印出2。
此时将setState的第一个参数换成函数,通过函数的第一个参数可以获得更新前的state。
import React, { Component } from 'react'; class Test extends Component { constructor(props) { super(props); this.state = { count: 1 } ; } handleClick = () => { this.setState((prevState, props) => { return { count: prevState.count + 1 } } ); this.setState((prevState, props) => { return { count: prevState.count + 1 } } ); this.setState((prevState, props) => { return { count: prevState.count + 1 } } ); console.log(this.state.count); } render() { return ( div style={ { width: '100px', height: '100px', backgroundColor: "yellow" } } > { this.state.count} /div> ) } } export default Test;
此时,打印出的结果为1,但是页面展示出来的count为4。可以发现,如果setState以传参的方式去更新state,几次setState并不会只更新最后一次,而是几次更新state都会生效。
接下来看下第二个函数中打印的count是多少:
import React, { Component } from 'react'; class Test extends Component { constructor(props) { super(props); this.state = { count: 1 } ; } handleClick = () => { this.setState((prevState, props) => { return { count: prevState.count + 1 } } , () => { console.log('1', this.state.count); } ); this.setState((prevState, props) => { return { count: prevState.count + 1 } } , () => { console.log('2', this.state.count); } ); this.setState((prevState, props) => { return { count: prevState.count + 1 } } , () => { console.log('3', this.state.count); } ); console.log(this.state.count); } render() { return ( div style={ { width: '100px', height: '100px', backgroundColor: "yellow" } } > { this.state.count} /div> ) } } export default Test;
此时,点击一次,三个setState的回调函数中,打印结果如下,可想而知,页面的展示结果也为4
1
1: 4
2: 4
3: 4
将上边代码放入如componentDidmount中,输出结果跟上边一致。
因为,可以得知,在自定义合成事件和钩子函数中,state的更新是异步的。
原生事件和setTimeout中同步更新state
以在setTimeout中setState为例
import React, { Component } from 'react'; class Test extends Component { constructor(props) { super(props); this.state = { count: 1 } ; } componentDidMount() { setTimeout(() => { this.setState({ count: this.state.count + 1 } , () => { console.log('1:', this.state.count); } ); this.setState({ count: this.state.count + 1 } , () => { console.log('2:', this.state.count); } ); this.setState({ count: this.state.count + 1 } , () => { console.log('3:', this.state.count); } ); console.log(this.state.count); } , 0); } render() { return ( div style={ { width: '100px', height: '100px', backgroundColor: "yellow" } } > { this.state.count} /div> ) } } export default Test;
此时,打印出的结果如下:
1: 2
2: 3
3: 4
4
将setState第一个参数换为函数:
componentDidMount() { setTimeout(() => { this.setState((prevState, props) => { return { count: prevState.count + 1 } } , () => { console.log('1', this.state.count); } ); this.setState((prevState, props) => { return { count: prevState.count + 1 } } , () => { console.log('2', this.state.count); } ); this.setState((prevState, props) => { return { count: prevState.count + 1 } } , () => { console.log('3', this.state.count); } ); console.log(this.state.count); } , 0); }
打印出的结果和上边一致。
是不是有一种state完全可控的感觉,在setTimeout中,多次setState都会生效,而且在每一个setState的第二个参数中都可以得到更新后的state。
同样地,在原生事件中输出地结果和setTimeout中一致,也是同步的。
import React, { Component } from 'react'; class Test extends Component { constructor(props) { super(props); this.state = { count: 1 } ; } componentDidMount() { document.body.addEventListener('click', this.handleClick, false); } componentWillUnmount() { document.body.removeEventListener('click', this.handleClick, false); } handleClick = () => { this.setState((prevState, props) => { return { count: prevState.count + 1 } } , () => { console.log('1', this.state.count); } ); this.setState((prevState, props) => { return { count: prevState.count + 1 } } , () => { console.log('2', this.state.count); } ); this.setState((prevState, props) => { return { count: prevState.count + 1 } } , () => { console.log('3', this.state.count); } ); console.log(this.state.count); } render() { return ( div style={ { width: '100px', height: '100px', backgroundColor: "yellow" } } > { this.state.count} /div> ) } } export default Test;
setState相关源码
如下代码均来自react17.0.2版本
目录 ./packages/react/src/ReactBaseClasses.js
function Component(props, context, updater) { this.props = props; this.context = context; // If a component has string refs, we will assign a different object later. this.refs = emptyObject; // We inITialize the default updater but the real one gets injected by the // renderer. this.updater = updater || ReactNoopUpdateQueue; } Component.prototype.isReactComponent = { } ; Component.prototype.setState = function(partialState, callback) { inVARiant( typeof partialState === 'object' || typeof partialState === 'function' || partialState == null, 'setState(...): takes an object of state variables to update or a ' + 'function which returns an object of state variables.', ); this.updater.enqueueSetState(this, partialState, callback, 'setState'); } ;
setState可以接收两个参数,第一个参数可以是object,function,和null,undefined,就不会抛出错误。执行下边的this.updater.enqueueSetState方法。全局查找enqueueSetState,找到两组目录下有这个变量。
首先是第一组目录:
目录 ./packages/react/src/ReactNoopUpdateQueue.js 第100行enqueueSetState方法,参数分别为this,初始化state,回调,和字符串setState,this是指当前React实例。
enqueueSetState: function( publicInstance, partialState, callback, callerName,) { warnNoop(publicInstance, 'setState'); }
接着看warnNoop方法:
const didWarnStateUpdateForUnmountedComponent = { } ; function warnNoop(publicInstance, callerName) { if (__DEV__) { const constructor = publicInstance.constructor; const componentName = (constructor & & (constructor.displayName || constructor.name)) || 'ReactClass'; const warningKey = `${ componentName} .${ callerName} `; if (didWarnStateUpdateForUnmountedComponent[warningKey]) { return; } console.error( "Can't call %s on a component that is not yet mounted. " + 'This is a no-op, but it might indicate a bug in your application. ' + 'Instead, assign to `this.state` directly or define a `state = { } ; ` ' + 'class property with the desired state in the %s component.', callerName, componentName, ); didWarnStateUpdateForUnmountedComponent[warningKey] = true; } }
这段代码相当于给didWarnStateUpdateForUnmountedComponent对象中加入属性,属性的key为React 当前要setState的组件.setState,如果当前有这个属性则返回;如果当前没这个属性或者这个属性值为false,则设置这个属性的值为true。
再去看另外一个目录:
目录 ./react-reconciler/src/ReactFiberClassComponent.new.js和ReactFiberClassComponent.old.js
const classComponentUpdater = { enqueueSetState(inst, payload, callback) { const fiber = getInstance(inst); const eventTime = requestEventTime(); const lane = requestUpdateLane(fiber); const update = createUpdate(eventTime, lane); update.payload = payload; if (callback !== undefined & & callback !== null) { if (__DEV__) { warnOnInvalIDCallback(callback, 'setState'); } update.callback = callback; } enqueueUpdate(fiber, update, lane); const root = scheduleUpdateOnFiber(fiber, lane, eventTime); if (root !== null) { entangleTransitions(root, fiber, lane); } if (__DEV__) { if (enableDebugTracing) { if (fiber.mode & DebugTracingMode) { const name = getComponentNameFromFiber(fiber) || 'Unknown'; logStateUpdateScheduled(name, lane, payload); } } } if (enableSchedulingProfiler) { markStateUpdateScheduled(fiber, lane); } } }
其中主要看 enqueueUpdate 这个函数
目录 ./react-reconciler/src/ReactUpdateQueue.new.js和ReactUpdateQueue.old.js
export function enqueueUpdateState> ( fiber: Fiber, update: UpdateState> , lane: Lane,) { const updateQueue = fiber.updateQueue; if (updateQueue === null) { // Only occurs if the fiber has been unmounted. return; } const sharedQueue: SharedQueueState> = (updateQueue: any).shared; if (isInterleavedUpdate(fiber, lane)) { const interleaved = sharedQueue.interleaved; if (interleaved === null) { // This is the First update. Create a circular list. update.next = update; // At the end of the current render, this queue's interleaved updates will // be transfered to the pending queue. pushInterleavedQueue(sharedQueue); } else { update.next = interleaved.next; interleaved.next = update; } sharedQueue.interleaved = update; } else { const pending = sharedQueue.pending; if (pending === null) { // This is the first update. Create a circular list. update.next = update; } else { update.next = pending.next; pending.next = update; } sharedQueue.pending = update; } if (__DEV__) { if ( currentlyProcessingQueue === sharedQueue & & !didWarnUpdateInsideUpdate ) { console.error( 'An update (setState, replaceState, or forceUpdate) was scheduled ' + 'from inside an update function. Update functions should be pure, ' + 'with zero side-effects. Consider using componentDidUpdate or a ' + 'callback.', ); didWarnUpdateInsideUpdate = true; } } }
看到这里,发现这个方法是将此次更新的update加入到更新队列中,而在这个版本中并没有发现isBatchingUpdates这个属性的出现。貌似React Fiber改动还挺大,暂时先写到这里,如果有新的发现会补充到这里。
总结
- 自定义合成事件和react钩子函数中异步更新state
- 原生事件和setTimeout中同步更新state
以上就是详解react setState的详细内容,更多关于react setState的资料请关注其它相关文章!
您可能感兴趣的文章:- 深入理解React State 原理
- 浅析React 对state的理解
- 详解react中的state的简写方式
- 深入研究React中setState源码
- 深入掌握 react的 setState的工作机制
- React 组件中的state和setState()你知道多少
声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: 详解react setState
本文地址: https://pptw.com/jishu/595198.html