function createPortal(
children: ReactNodeList,
container: DOMContainer,
key: ?string = null,
) {
invariant(
isValidContainer(container),
'Target container is not a DOM element.',
);
// TODO: pass ReactDOM portal implementation as third argument
return ReactPortal.createPortal(children, container, null, key);
}
function createPortal(
children: ReactNodeList,
containerInfo: any,
// TODO: figure out the API for cross-renderer implementation.
implementation: any,
key: ?string = null,
): ReactPortal {
return {
// This tag allow us to uniquely identify this as a React Portal
$$typeof: REACT_PORTAL_TYPE,
key: key == null ? null : '' + key,
children,
containerInfo,
implementation,
};
}
case HostPortal:
return updatePortalComponent(
current,
workInProgress,
renderExpirationTime,
);
function updatePortalComponent(
current: Fiber | null,
workInProgress: Fiber,
renderExpirationTime: ExpirationTime,
) {
// 有单独的挂载点,container 需要 push
pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
// 获取新的 children
const nextChildren = workInProgress.pendingProps;
// 首次调用
if (current === null) {
// Portals are special because we don't append the children during mount
// but at commit. Therefore we need to track insertions which the normal
// flow doesn't do during mount. This doesn't happen at the root because
// the root always starts with a "current" with a null child.
// TODO: Consider unifying this with how the root works.
workInProgress.child = reconcileChildFibers(
workInProgress,
null,
nextChildren,
renderExpirationTime,
);
} else {
// 相当于 mountChildFibers
// 更新渲染
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
}
return workInProgress.child;
}
function forwardRef<Props, ElementType: React$ElementType>(
render: (props: Props, ref: React$Ref<ElementType>) => React$Node,
) {
if (__DEV__) {
if (typeof render !== 'function') {
warningWithoutStack(
false,
'forwardRef requires a render function but was given %s.',
render === null ? 'null' : typeof render,
);
} else {
warningWithoutStack(
// Do not warn for 0 arguments because it could be due to usage of the 'arguments' object
render.length === 0 || render.length === 2,
'forwardRef render functions accept exactly two parameters: props and ref. %s',
render.length === 1
? 'Did you forget to use the ref parameter?'
: 'Any additional parameter will be undefined.',
);
}
if (render != null) {
warningWithoutStack(
render.defaultProps == null && render.propTypes == null,
'forwardRef render functions do not support propTypes or defaultProps. ' +
'Did you accidentally pass a React component?',
);
}
}
return {
$$typeof: REACT_FORWARD_REF_TYPE,
render, // render 即 function component
};
}
updateForwardRef 更新
case ForwardRef: {
const type = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === type
? unresolvedProps
: resolveDefaultProps(type, unresolvedProps);
return updateForwardRef(
current,
workInProgress,
type,
resolvedProps,
renderExpirationTime,
);
}
// updateForwardRef
function updateForwardRef(
current: Fiber | null,
workInProgress: Fiber,
type: any,
nextProps: any,
renderExpirationTime: ExpirationTime,
) {
// render 即 React.forwardRef 传入的 function component
const render = type.render;
// 调用组件产生的 ref
const ref = workInProgress.ref;
if (hasLegacyContextChanged()) {
// Normally we can bail out on props equality but if context has changed
// we don't do the bailout and we have to reuse existing props instead.
} else if (workInProgress.memoizedProps === nextProps) {
// 之前的 ref
const currentRef = current !== null ? current.ref : null;
// 两个ref 相同则跳过更新
if (ref === currentRef) {
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderExpirationTime,
);
}
}
let nextChildren;
if (__DEV__) {
ReactCurrentOwner.current = workInProgress;
ReactCurrentFiber.setCurrentPhase('render');
nextChildren = render(nextProps, ref);
ReactCurrentFiber.setCurrentPhase(null);
} else {
// React.forwardRef((props, ref) => {})
// 传递 ref 并获取新 children,没有传 context
nextChildren = render(nextProps, ref);
}
// 调和子节点
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
return workInProgress.child;
}
portalComponent
createPortal
创建portal
,返回一个类似reactElement
的对象portal
类型组件创建fiber
节点createFiberFromPortal 创建 portal 类型的 fiber
updatePortalComponent 更新 fiber 节点
ForwardRef
forwardRef
用来传递ref
fiber
是在reconcileSingleElement -> createFiberFromElement -> createFiberFromTypeAndProps
中将fiberTag
标记为ForwardRef
updateForwardRef 更新
Mode 组件
<ConCurrentmode />
和<StrictMode />
Fiber
对象上记录所有子节点所对应的渲染模式,在创建更新时候根据mode
创建expirationTime
typeofMode
createFiberFromElement -> createFiberFromTypeAndProps
中为mode
组件创建fiber
节点,这里的传入的mode
为returnFiber(即父组件)
上的mode
,而父组件上的mode
最终来自hostRootFiber
上,默认为NoContext
Mode组件
它会有一个特殊的的Fiber
对象上的mode
属性conCurrentMode
传入的mode
为NoContext | ConcurrentMode | StrictMode -> 0b011
StrictMode
传入的Mode
为NoContext | StrictMode -> 0b010
Mode 组件
的子节点过程中,传入的mode
则是这个Mode 组件
它fiber
对象上的mode
属性,对于Mode 组件
它所有子树上的子节点都会具有这个Mode 组件
它fiber
对象上的mode
属性,以此来记录这个子树处于那个渲染模式下,才会有后期创建更新时根据mode
属性如ConcurrentMode
会对expirationTime
有个特殊的计算过程createFiberFromMode 根据 mode 创建 Fiber 节点
type
赋值:mode & ConcurrentMode
等于NoContext
则为StrictMode
,否则为ConCurrentMode
依次给子节点也添加上父组件
mode
对应的type
updateMode更新 Mode
Memo 组件
React.memo(com, compare)
创建一个具有pureComponent
特性的functionComponent
React.memo()
$$typeof
标记fiberTag
为MemoComponent
updateMemoComponent 更新 memo
memo
组件的props
都是作用于传入的function
组件内,memo
组件的意义在于进行一次组件包裹,可传入自定义比较函数child
没有使用调和子节点方式,而是直接创建compare
函数如果没传入,则使用shallowEqual
,返回true
则跳过更新。case MemoComponent: { const type = workInProgress.type; const unresolvedProps = workInProgress.pendingProps; const resolvedProps = resolveDefaultProps(type.type, unresolvedProps); return updateMemoComponent( current, workInProgress, type, resolvedProps, updateExpirationTime, renderExpirationTime, ); }
function updateMemoComponent( current: Fiber | null, workInProgress: Fiber, Component: any, nextProps: any, updateExpirationTime, renderExpirationTime: ExpirationTime, ): null | Fiber { // 首次渲染 if (current === null) { // 拿到传入的那个 function component let type = Component.type; // 如果是纯函数组件没有 contruct、defaultProps 的组件且没有比较函数,则按 SimpleMemoComponent 更新 if (isSimpleFunctionComponent(type) && Component.compare === null) { // If this is a plain function component without default props, // and with only the default shallow comparison, we upgrade it // to a SimpleMemoComponent to allow fast path updates. // 更新 tag,下次更新直接走 updateSimpleMemoComponent workInProgress.tag = SimpleMemoComponent; workInProgress.type = type; return updateSimpleMemoComponent( current, workInProgress, type, nextProps, updateExpirationTime, renderExpirationTime, ); } // reconcileChildFibers 会把 props 用于当前组件本身的 children 调和的过程 // 而对于 memo 组件的 child 来自于传入的那个 function component ,因此要将 props 传递给 function component,来创建 children // 直接创建,而不是调和子节点, let child = createFiberFromTypeAndProps( Component.type, null, nextProps, null, workInProgress.mode, renderExpirationTime, ); child.ref = workInProgress.ref; child.return = workInProgress; workInProgress.child = child; return child; } let currentChild = ((current.child: any): Fiber); // This is always exactly one child // 如果没有必要更新 if ( updateExpirationTime === NoWork || updateExpirationTime > renderExpirationTime ) { // This will be the props with resolved defaultProps, // unlike current.memoizedProps which will be the unresolved ones. const prevProps = currentChild.memoizedProps; // Default to shallow comparison let compare = Component.compare; // compare 有则用,否则为 shallowEqual compare = compare !== null ? compare : shallowEqual; // 相同则跳过更新 if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) { return bailoutOnAlreadyFinishedWork( current, workInProgress, renderExpirationTime, ); } } let newChild = createWorkInProgress( currentChild, nextProps, renderExpirationTime, ); newChild.ref = workInProgress.ref; newChild.return = workInProgress; workInProgress.child = newChild; return newChild; }