We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
本篇文章介绍以下知识点:
setState
fiber.memoizedState
React
准备工作
constructor
debugger
Counter
handleClick
import React, { Component } from "react"; import ReactDOM from "react-dom"; class Counter extends Component { constructor(props) { debugger; super(props); this.state = { number: 0, }; } handleClick = (event) => { debugger; this.setState({ number: 1 }); this.setState({ number: 2 }); }; render() { console.log("render===", this.state); return <button onClick={this.handleClick}>{this.state.number}</button>; } } ReactDOM.render(<Counter />, document.getElementById("root"));
我们知道类组件一定要继承于 React.Component 或者 React.PureComponent,这两个类位于 packages/react/src/ReactBaseClasses.js 文件中,React.Component 做的事情很简单。下面一步一步 debug 一下
React.Component
React.PureComponent
packages/react/src/ReactBaseClasses.js
刷新页面,首先进入我们的构造函数断点处
注意这个函数调用栈的顺序,可以在每个函数都打一个断点,多看几次类组件初始化的过程
点击下一步,进入 super(props) 函数,实际上就是我们的 React.Component
super(props)
这里有三个需要注意的地方:
this.updater = updater || ReactNoopUpdateQueue;
this.setState
this.updater.enqueueSetState
this.updater
ReactNoopUpdateQueue
react-dom
react-native
Component.prototype.isReactComponent = {};
fiber
isReactComponent
Component.prototype.setState
在创建 fiber 节点时需要判断当前组件是类组件还是函数组件
shouldConstruct 实现如下:
shouldConstruct
React.Component 的简单实现如下:
const ReactNoopUpdateQueue = { isMounted: function (publicInstance) { return false; }, enqueueForceUpdate: function (publicInstance, callback, callerName) {}, enqueueReplaceState: function ( publicInstance, completeState, callback, callerName ) {}, enqueueSetState: function ( publicInstance, partialState, callback, callerName ) {}, }; class Component { constructor(props, context, updater) { this.props = props; this.updater = updater || ReactNoopUpdateQueue; this.isReactComponent = {}; } setState(partialState, callback) { this.updater.enqueueSetState(this, partialState); } forceUpdate(callback) { this.updater.enqueueForceUpdate(this, callback, "forceUpdate"); } }
实际上真正的 this.updater 的初始化在 adoptClassInstance 方法中:
adoptClassInstance
我们在React.useReducer 原理及源码主流程章节中已经知道,函数组件对应的 fiber.memoizedState 是用来保存 hook 链表的。
hook
在类组件中,其对应的 fiber.memoizedState 保存的是上一次更新的 this.state 的值
this.state
在React.useReducer 原理及源码主流程中我们知道如果多次调用 setCount,更新的队列会保存在 hook.queue 链表中
setCount
hook.queue
在类组件中,如果我们连续调用多次
this.setState({ number: 1 }); this.setState({ number: 2 }); this.setState({ number: 3 });
实际上更新队列保存在 fiber.updateQueue中,fiber.updateQueue.shared.pending 指向最后一个 this.setState() 生成的更新对象
fiber.updateQueue
fiber.updateQueue.shared.pending
this.setState()
fiber.updateQueue.shared 和 hook.queue 一样也是环状链表
fiber.updateQueue.shared
调用 this.setState 时,实际上调用的是 this.updater.enqueueSetState
function get(key) { return key._reactInternals; } function createUpdate(eventTime, lane) { var update = { eventTime: eventTime, lane: lane, tag: UpdateState, payload: null, callback: null, next: null, }; return update; } // enqueueUpdate构造环状列表 function enqueueUpdate(fiber, update) { var updateQueue = fiber.updateQueue; if (updateQueue === null) { return; } var sharedQueue = updateQueue.shared; var pending = sharedQueue.pending; if (pending === null) { update.next = update; } else { update.next = pending.next; pending.next = update; } sharedQueue.pending = update; } function requestEventTime() { // 任务是有优先级的,优先级高的会打断优先级低的 // 如果低优先级任务超时了,则优先级高的不能再打断优先级低的任务 return performance.now(); // 程序从启动到现在的时间,是用来计算任务的过期时间的 } var classComponentUpdater = { isMounted: isMounted, enqueueSetState: function (inst, payload, callback) { var fiber = get(inst); var eventTime = requestEventTime(); var lane = requestUpdateLane(fiber); var update = createUpdate(eventTime, lane); update.payload = payload; if (callback !== undefined && callback !== null) { update.callback = callback; } enqueueUpdate(fiber, update); scheduleUpdateOnFiber(fiber, lane, eventTime); }, }; class Component { constructor() { // 这里为了简化流程,直接初始化this.updater为classComponentUpdater this.updater = classComponentUpdater; } setState(partialState, callback) { this.updater.enqueueSetState(this, partialState); } }
主流程图:
fiber.updateQueue 会在下一个微任务中在 processUpdateQueue 函数中处理
processUpdateQueue
The text was updated successfully, but these errors were encountered:
No branches or pull requests
setState 主流程及源码
本篇文章介绍以下知识点:
setState
主流程及源码fiber.memoizedState
的区别React
是如何区分函数组件和类组件的demo
准备工作
constructor
中第一行添加debugger
,类组件Counter
初始化时会调用构造函数handleClick
中第一行添加debugger
,当我们点击按钮时,setState
主流程便从这里开始React.Component
我们知道类组件一定要继承于
React.Component
或者React.PureComponent
,这两个类位于packages/react/src/ReactBaseClasses.js
文件中,React.Component
做的事情很简单。下面一步一步 debug 一下刷新页面,首先进入我们的构造函数断点处
注意这个函数调用栈的顺序,可以在每个函数都打一个断点,多看几次类组件初始化的过程
点击下一步,进入
super(props)
函数,实际上就是我们的React.Component
这里有三个需要注意的地方:
this.updater = updater || ReactNoopUpdateQueue;
。当我们调用this.setState
时,使用的就是this.updater.enqueueSetState
。这里只是简单的将this.updater
初始化为空的ReactNoopUpdateQueue
。实际上真正的this.updater
在react-dom
中初始化。react-dom
、react-native
等对于this.updater
的实现都不尽相同。Component.prototype.isReactComponent = {};
isReactComponent 用于后续在创建fiber
节点时判断是不是类组件。如果函数原型存在isReactComponent
则说明是类组件Component.prototype.setState
这是我们调用this.setState
时的逻辑在创建
fiber
节点时需要判断当前组件是类组件还是函数组件shouldConstruct
实现如下:React.Component
的简单实现如下:实际上真正的
this.updater
的初始化在adoptClassInstance
方法中:类组件和函数组件的 fiber.memoizedState 的区别
我们在React.useReducer 原理及源码主流程章节中已经知道,函数组件对应的
fiber.memoizedState
是用来保存hook
链表的。在类组件中,其对应的
fiber.memoizedState
保存的是上一次更新的this.state
的值类组件的更新队列
在React.useReducer 原理及源码主流程中我们知道如果多次调用
setCount
,更新的队列会保存在hook.queue
链表中在类组件中,如果我们连续调用多次
实际上更新队列保存在
fiber.updateQueue
中,fiber.updateQueue.shared.pending
指向最后一个this.setState()
生成的更新对象fiber.updateQueue.shared
和hook.queue
一样也是环状链表类组件初始化流程
setState 主流程及源码
调用
this.setState
时,实际上调用的是this.updater.enqueueSetState
主流程图:
fiber.updateQueue
会在下一个微任务中在processUpdateQueue
函数中处理The text was updated successfully, but these errors were encountered: