回顾:初始化创建完FiberRootNode和hostRootFiber后,调用render方法进行更新root。
updateContainer()主流程
- updateContainer(入口)
- 获取 current Fiber(HostRoot)
- 获取事件时间(eventTime)、优先级(eventLane)
- 处理 context(大部分情况为默认)
- 创建 update 对象(payload: element)
- 入队并调度(enqueueUpdate,使用创建的update和初始化获得的fiber.updateQueue)
- 返回 lane(实际情况返回值无变量接收,基本不使用返回值)
数据结构1:update对象属性及其属性值来源
update对象封装在createUpdate方法中,工厂函数返回一个创建的update对象。
有6个属性。
//全局变量
var UpdateState = 0;
//对update的属性赋值
var eventTime = requestEventTime();
var lane = requestUpdateLane(current$1);
var update = createUpdate(eventTime, lane); // Caution: React DevTools currently depends on this property
// being called "element".
//工厂函数,返回一个update对象
function createUpdate(eventTime, lane) {
var update = {
eventTime: eventTime,
lane: lane,
tag: UpdateState,
payload: null,
callback: null,
next: null,
};
return update;
}
update.payload = {
element: element,
};
callback = callback === undefined ? null : callback;
if (callback !== null) {
{
if (typeof callback !== 'function') {
error(
'render(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callback
);
}
}
update.callback = callback;
}
//使用requestEventTime返回eventTime对象;
function requestEventTime() {
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
// We're inside React, so it's fine to read the actual time.
return now();
} // We're not inside React, so we may be in the middle of a browser event.
if (currentEventTime !== NoTimestamp) {
// Use the same start time for all updates until we enter React again.
return currentEventTime;
} // This is the first update since React yielded. Compute a new start time.
currentEventTime = now();
return currentEventTime;
}
//使用requestUpdateLane创建一个eventLane变量
function requestUpdateLane(fiber) {
// Special cases
var mode = fiber.mode;
if ((mode & ConcurrentMode) === NoMode) {
return SyncLane;
} else if (
(executionContext & RenderContext) !== NoContext &&
workInProgressRootRenderLanes !== NoLanes
) {
// This is a render phase update. These are not officially supported. The
// old behavior is to give this the same "thread" (lanes) as
// whatever is currently rendering. So if you call `setState` on a component
// that happens later in the same render, it will flush. Ideally, we want to
// remove the special case and treat them as if they came from an
// interleaved event. Regardless, this pattern is not officially supported.
// This behavior is only a fallback. The flag only exists until we can roll
// out the setState warning, since existing code might accidentally rely on
// the current behavior.
return pickArbitraryLane(workInProgressRootRenderLanes);
}
var isTransition = requestCurrentTransition() !== NoTransition;
if (isTransition) {
if (ReactCurrentBatchConfig$3.transition !== null) {
var transition = ReactCurrentBatchConfig$3.transition;
if (!transition._updatedFibers) {
transition._updatedFibers = new Set();
}
transition._updatedFibers.add(fiber);
} // The algorithm for assigning an update to a lane should be stable for all
// updates at the same priority within the same event. To do this, the
// inputs to the algorithm must be the same.
//
// The trick we use is to cache the first of each of these inputs within an
// event. Then reset the cached values once we can be sure the event is
// over. Our heuristic for that is whenever we enter a concurrent work loop.
if (currentEventTransitionLane === NoLane) {
// All transitions within the same event are assigned the same lane.
currentEventTransitionLane = claimNextTransitionLane();
}
return currentEventTransitionLane;
} // Updates originating inside certain React methods, like flushSync, have
// their priority set by tracking it with a context variable.
//
// The opaque type returned by the host config is internally a lane, so we can
// use that directly.
// TODO: Move this type conversion to the event priority module.
var updateLane = getCurrentUpdatePriority();
if (updateLane !== NoLane) {
return updateLane;
} // This update originated outside React. Ask the host environment for an
// appropriate priority, based on the type of event.
//
// The opaque type returned by the host config is internally a lane, so we can
// use that directly.
// TODO: Move this type conversion to the event priority module.
var eventLane = getCurrentEventPriority();
return eventLane;
}
数据结构2:hostRootFiber.updateQueue属性
fiber.updateQueue属性在创建root的时候初始化(在类组件挂载使用该方法初始化),数据结构如下:
function initializeUpdateQueue(fiber) {
var queue = {
baseState: fiber.memoizedState,
firstBaseUpdate: null,//单链表
lastBaseUpdate: null,
shared: {
pending: null, //循环单项链表
interleaved: null,//循环单项链表
lanes: NoLanes //位掩码
},
effects: null
};
fiber.updateQueue = queue; //将创建的update对象赋值给fiber属性
}
一、root.render()方法
输入变量:
- children:组件
- root:根容器节点
无返回变量。
ReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render = function (children) {
var root = this._internalRoot;
if (root === null) {
throw new Error('Cannot update an unmounted root.');
}
{
if (typeof arguments[1] === 'function') {
error('render(...): does not support the second callback argument. ' + 'To execute a side effect after rendering, declare it in a component body with useEffect().');
} else if (isValidContainer(arguments[1])) {
error('You passed a container to the second argument of root.render(...). ' + "You don't need to pass it again since you already passed it to create the root.");
} else if (typeof arguments[1] !== 'undefined') {
error('You passed a second argument to root.render(...) but it only accepts ' + 'one argument.');
}
var container = root.containerInfo;
if (container.nodeType !== COMMENT_NODE) {
var hostInstance = findHostInstanceWithNoPortals(root.current);
if (hostInstance) {
if (hostInstance.parentNode !== container) {
error('render(...): It looks like the React-rendered content of the ' + 'root container was removed without using React. This is not ' + 'supported and will cause errors. Instead, call ' + "root.unmount() to empty a root's container.");
}
}
}
}
updateContainer(children, root, null, null);
};
二、updateContainer()方法
输入变量:
- element:root.render()的children变量,存入update.payload属性里。
- cotainer:等价于root
- parentCompoen:为null
- callback:为null
返回变量:
- lane(实际使用中并没有变量接收lane返回值)
function updateContainer(element, container, parentComponent, callback) {
{
onScheduleRoot(container, element);
}
var current$1 = container.current;
var eventTime = requestEventTime();
var lane = requestUpdateLane(current$1);
{
markRenderScheduled(lane);
}
var context = getContextForSubtree(parentComponent);
if (container.context === null) {
container.context = context;
} else {
container.pendingContext = context;
}
{
if (isRendering && current !== null && !didWarnAboutNestedUpdates) {
didWarnAboutNestedUpdates = true;
error('Render methods should be a pure function of props and state; ' + 'triggering nested component updates from render is not allowed. ' + 'If necessary, trigger nested updates in componentDidUpdate.\n\n' + 'Check the render method of %s.', getComponentNameFromFiber(current) || 'Unknown');
}
}
var update = createUpdate(eventTime, lane); // Caution: React DevTools currently depends on this property
// being called "element".
update.payload = {
element: element
};
callback = callback === undefined ? null : callback;
if (callback !== null) {
{
if (typeof callback !== 'function') {
error('render(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callback);
}
}
update.callback = callback;
}
var root = enqueueUpdate(current$1, update, lane);
if (root !== null) {
scheduleUpdateOnFiber(root, current$1, lane, eventTime);
entangleTransitions(root, current$1, lane);
}
return lane;
}
三、enqueueUpdate()
将创建的update对象添加到update。
输入变量:
- fiber:container指向的current,也就是hostRootFiber
- update:createUpdate创建的update对象
- lane:var lane = requestUpdateLane(current$1) (具体方法见update对象)
返回变量:
- 返回enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane)
function enqueueUpdate(fiber, update, lane) {
var updateQueue = fiber.updateQueue;
if (updateQueue === null) {
// Only occurs if the fiber has been unmounted.
return null;
}
var sharedQueue = updateQueue.shared;
{
if (currentlyProcessingQueue === sharedQueue && !didWarnUpdateInsideUpdate) {
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;
}
}
if (isUnsafeClassRenderPhaseUpdate()) {
// This is an unsafe render phase update. Add directly to the update
// queue so we can process it immediately during the current render.
var 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; // Update the childLanes even though we're most likely already rendering
// this fiber. This is for backwards compatibility in the case where you
// update a different component during render phase than the one that is
// currently renderings (a pattern that is accompanied by a warning).
return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
} else {
return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
}
}
四、enqueueConcurrentClassUpdate()
输入变量:
- fiber:container指向的current,也就是hostRootFiber
- queue:enqueueUpdate()的sharedQueue变量,fiber.updateQueue的Shared属性。
- update:createUpdate创建的update对象
- lane:var lane = requestUpdateLane(current$1) (具体方法见update对象)
function enqueueConcurrentClassUpdate(fiber, queue, update, lane) {
//取出当前队列的 interleaved 链表(循环链表的最后一个节点,或 null)。
var interleaved = queue.interleaved;
// 如果 interleaved 为空,说明这是本次渲染的第一个交错更新:
// update.next = update;
// 创建一个只有自己一个节点的循环链表。
// pushConcurrentUpdateQueue(queue);
// 把这个队列加入全局的 concurrentQueues,方便渲染结束时统一处理。
// 如果 interleaved 不为空,说明已经有交错更新:
// update.next = interleaved.next;
// 新 update 指向链表头(第一个节点)。
// interleaved.next = update;
// 原链表尾(last)指向新 update,插入到链表头部。
// 这样链表依然是循环的,且新 update 成为最后一个节点。
if (interleaved === null) {
update.next = update;
pushConcurrentUpdateQueue(queue);
} else {
update.next = interleaved.next;
interleaved.next = update;
}
//更新队列的 interleaved 指针,始终指向循环链表的最后一个节点。
queue.interleaved = update;
//追溯 fiber 到根节点,标记本次更新的优先级(lane),并返回根节点
return markUpdateLaneFromFiberToRoot(fiber, lane);
}
五、pushConcurrentUpdateQueue()和markUpdateLaneFromeFiberToRoot()
markUpdateLaneFromeFiberToRoot():从当前 fiber(组件节点)向上遍历到根节点(HostRoot),沿途合并/标记本次更新的优先级(lane),并返回根节点(FiberRoot)。
输入变量:
- sourceFiber:container指向的current,也就是hostRootFiber
- lane:var lane = requestUpdateLane(current$1) (具体方法见update对象)
function mergeLanes(a, b) {
return a | b;
}
var concurrentQueues = null;
function pushConcurrentUpdateQueue(queue) {
if (concurrentQueues === null) {
concurrentQueues = [queue];
} else {
concurrentQueues.push(queue);
}
}
function markUpdateLaneFromFiberToRoot(sourceFiber, lane) {
// 1. 当前 fiber 标记本次更新的 lane
sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
var alternate = sourceFiber.alternate;
if (alternate !== null) {
alternate.lanes = mergeLanes(alternate.lanes, lane);
}
// 2. 向上遍历父节点,沿途合并 childLanes
var node = sourceFiber;
var parent = sourceFiber.return;
while (parent !== null) {
parent.childLanes = mergeLanes(parent.childLanes, lane);
alternate = parent.alternate;
if (alternate !== null) {
alternate.childLanes = mergeLanes(alternate.childLanes, lane);
}
node = parent;
parent = parent.return;
}
// 3. 如果到达 HostRoot,返回根节点
if (node.tag === HostRoot) {
var root = node.stateNode;
return root;
} else {
return null;
}
}