1 )概述
ReactDOM.render
过程中,还需要创建一个 update 对象update
用于记录组件状态的改变的一个对象,它存放于Fiber对象的 updateQueue
中updateQueue
,它是一个单向链表的结构,一次整体的更新过程当中setState
setState
,就更新一下整个应用updateQueue
里面,然后再进行更新的操作batchUpdates
的原理, 这个暂时不管2 )源码
现在来看下 update 如何去创建以及它的一个数据结构
参考: https://github.com/facebook/react/blob/v16.6.0/packages/react-reconciler/src/ReactFiberReconciler.js
找到 scheduleRootUpdate
函数
function scheduleRootUpdate(
current: Fiber,
element: ReactNodeList,
expirationTime: ExpirationTime,
callback: ?Function,
) {
if (__DEV__) {
if (
ReactCurrentFiber.phase === 'render' &&
ReactCurrentFiber.current !== null &&
!didWarnAboutNestedUpdates
) {
didWarnAboutNestedUpdates = true;
warningWithoutStack(
false,
'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.',
getComponentName(ReactCurrentFiber.current.type) || 'Unknown',
);
}
}
const update = createUpdate(expirationTime);
// Caution: React DevTools currently depends on this property
// being called "element".
update.payload = {element};
callback = callback === undefined ? null : callback;
if (callback !== null) {
warningWithoutStack(
typeof callback === 'function',
'render(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callback,
);
update.callback = callback;
}
enqueueUpdate(current, update);
scheduleWork(current, expirationTime);
return expirationTime;
}
const update = createUpdate(expirationTime);
, 进入到 createUpdate
函数
import {createUpdate, enqueueUpdate} from './ReactUpdateQueue';
createUpdate
函数export function createUpdate(expirationTime: ExpirationTime): Update<*> {
return {
// 对应这一次创建的更新的过期时间,
expirationTime: expirationTime,
// tag 对应4种情况
// export const UpdateState = 0; // 更新 state
// export const ReplaceState = 1; // 替代 state
// export const ForceUpdate = 2; // 强制更新
// export const CaptureUpdate = 3; // 渲染过程中,如果有错误被捕获,会生成一个 update, 让我们重新渲染节点的方式, Error Boundary 在组件内部捕获渲染的错误的一个更新
// 指定更新的类型,值为以上几种:0, 1, 2, 3
tag: UpdateState,
// 更新内容, 对应实际执行的操作内容,比如,在上述 createUpdate中的 update.payload = {element}; 这里是把整个传进去的App, ReactElement 及其整棵树 渲染到 DomRoot 里面
// 初始渲染的 payload 就如上述
// 如果是更新,比如 setState 接收的第一个参数,可能是一个对象或方法作为 payload
payload: null,
callback: null,
// next 对应下一个 update, 因为,update 都是存放在 updateQueue 中的, 而 updateQueue 是一个单项列表的结构
// 每个 update 都有一个 next, 在 UpdateQueue 中有一个 firstUpdate 和 lastUpdate 记录的是单项列表的开头和结尾
// 从开头到结尾都是通过 next 串联起来的,把整个update 单链表的结构连接起来
// 通过拿到 firstUpdate 基于next一层层找到 lastUpdate
next: null,
// 指向下一个 side effect
nextEffect: null,
};
}
UpdateQueue
里面export type UpdateQueue<State> = {
// 每次更新应用渲染完成后,调用 UpdateQueue 计算出一个新的state 存放在 baseState 上
// 下一次节点有更新,产生了一个更新的队列,在计算新的state时,会在 baseState 基础上计算
baseState: State,
// firstUpdate 和 lastUpdate 记录单项链表的数据结构
// 队列中的第一个 Update
firstUpdate: Update<State> | null,
// 队列中的最后一个 Update
lastUpdate: Update<State> | null,
// 下面两个也是记录单项链表的数据结构,只不过对应有错误捕获的时候的 Update
firstCapturedUpdate: Update<State> | null,
lastCapturedUpdate: Update<State> | null,
// 第一个 side effect
firstEffect: Update<State> | null,
// 最后一个 side effect
lastEffect: Update<State> | null,
// 第一个和最后一个 捕获产生的 side effect
firstCapturedEffect: Update<State> | null,
lastCapturedEffect: Update<State> | null,
};
createUpdate
内部要调用一个 enqueueUpdate
, 如果在 setState 和 forceUpdate 里面的操作enqueueUpdate
,进入这个函数export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
// Update queues are created lazily.
const alternate = fiber.alternate;
let queue1;
let queue2;
if (alternate === null) {
// There's only one fiber.
queue1 = fiber.updateQueue;
queue2 = null;
if (queue1 === null) {
queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
}
} else {
// There are two owners.
queue1 = fiber.updateQueue;
queue2 = alternate.updateQueue;
if (queue1 === null) {
if (queue2 === null) {
// Neither fiber has an update queue. Create new ones.
queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
queue2 = alternate.updateQueue = createUpdateQueue(
alternate.memoizedState,
);
} else {
// Only one fiber has an update queue. Clone to create a new one.
queue1 = fiber.updateQueue = cloneUpdateQueue(queue2);
}
} else {
if (queue2 === null) {
// Only one fiber has an update queue. Clone to create a new one.
queue2 = alternate.updateQueue = cloneUpdateQueue(queue1);
} else {
// Both owners have an update queue.
}
}
}
if (queue2 === null || queue1 === queue2) {
// There's only a single queue.
appendUpdateToQueue(queue1, update);
} else {
// There are two queues. We need to append the update to both queues,
// while accounting for the persistent structure of the list — we don't
// want the same update to be added multiple times.
if (queue1.lastUpdate === null || queue2.lastUpdate === null) {
// One of the queues is not empty. We must add the update to both queues.
appendUpdateToQueue(queue1, update);
appendUpdateToQueue(queue2, update);
} else {
// Both queues are non-empty. The last update is the same in both lists,
// because of structural sharing. So, only append to one of the lists.
appendUpdateToQueue(queue1, update);
// But we still need to update the `lastUpdate` pointer of queue2.
queue2.lastUpdate = update;
}
}
if (__DEV__) {
if (
fiber.tag === ClassComponent &&
(currentlyProcessingQueue === queue1 ||
(queue2 !== null && currentlyProcessingQueue === queue2)) &&
!didWarnUpdateInsideUpdate
) {
warningWithoutStack(
false,
'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;
}
}
}
const alternate = fiber.alternate;
先读取 alternate,这个就是 current 到 workInProgress 的映射关系createUpdateQueue
export function createUpdateQueue<State>(baseState: State): UpdateQueue<State> {
const queue: UpdateQueue<State> = {
baseState,
firstUpdate: null,
lastUpdate: null,
firstCapturedUpdate: null,
lastCapturedUpdate: null,
firstEffect: null,
lastEffect: null,
firstCapturedEffect: null,
lastCapturedEffect: null,
};
return queue;
}
appendUpdateToQueue
function appendUpdateToQueue<State>(
queue: UpdateQueue<State>,
update: Update<State>,
) {
// Append the update to the end of the list.
if (queue.lastUpdate === null) {
// Queue is empty
queue.firstUpdate = queue.lastUpdate = update;
} else {
queue.lastUpdate.next = update; // 当前最后(后面会变成倒数第二位元素)的next指向update
queue.lastUpdate = update; // queue的lastUpdate 指向 update
}
}
lastUpdate
不存在,则说明 Queue 是空的,则进行处理enqueueUpdate
中的 if else 的处理enqueueUpdate
源码中updateQueue
, 以及进行更新Fiber
ReactDOM.render
就是我们的 RootFiber
FiberRoot
的 current
scheduleRootUpdate
函数的 updateContainerAtExpirationTime
函数中export function updateContainerAtExpirationTime(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
expirationTime: ExpirationTime,
callback: ?Function,
) {
// TODO: If this is a nested container, this won't be the root.
const current = container.current;
if (__DEV__) {
if (ReactFiberInstrumentation.debugTool) {
if (current.alternate === null) {
ReactFiberInstrumentation.debugTool.onMountContainer(container);
} else if (element === null) {
ReactFiberInstrumentation.debugTool.onUnmountContainer(container);
} else {
ReactFiberInstrumentation.debugTool.onUpdateContainer(container);
}
}
}
const context = getContextForSubtree(parentComponent);
if (container.context === null) {
container.context = context;
} else {
container.pendingContext = context;
}
return scheduleRootUpdate(current, element, expirationTime, callback);
}
const current = container.current;
container.current 就对应一个 Fiber 对象