跳到主要内容

一、作用

二、创建根

备注
export function createRoot(
container: Element | Document | DocumentFragment,
options?: CreateRootOptions,
): RootType {
return createRootImpl(
container,
assign(
{
onUncaughtError: wwwOnUncaughtError,
onCaughtError: wwwOnCaughtError,
onDefaultTransitionIndicator: noopOnDefaultTransitionIndicator,
} as any,
options,
),
);
}

三、水合根

备注
export function hydrateRoot(
container: Document | Element,
initialChildren: ReactNodeList,
options?: HydrateRootOptions,
): RootType {
return hydrateRootImpl(
container,
initialChildren,
assign(
{
onUncaughtError: wwwOnUncaughtError,
onCaughtError: wwwOnCaughtError,
onDefaultTransitionIndicator: noopOnDefaultTransitionIndicator,
} as any,
options,
),
);
}

四、查找 DOM 节点

备注
export function findDOMNode(
// componentOrElement: Element | ?component(...props: any),
componentOrElement: Element | ?Component,
): null | Element | Text {
if (__DEV__) {
const owner = currentOwner;
if (owner !== null && isRendering && owner.stateNode !== null) {
const warnedAboutRefsInRender = owner.stateNode._warnedAboutRefsInRender;
if (!warnedAboutRefsInRender) {
console.error(
'%s is accessing findDOMNode inside its render(). ' +
'render() should be a pure function of props and state. It should ' +
'never access something that requires stale data from the previous ' +
'render, such as refs. Move this logic to componentDidMount and ' +
'componentDidUpdate instead.',
getComponentNameFromType(owner.type) || 'A component',
);
}
owner.stateNode._warnedAboutRefsInRender = true;
}
}
if (componentOrElement == null) {
return null;
}
if ((componentOrElement as any).nodeType === ELEMENT_NODE) {
return componentOrElement as any;
}
if (__DEV__) {
return findHostInstanceWithWarning(componentOrElement, 'findDOMNode');
}
return findHostInstance(componentOrElement);
}

五、渲染

备注
export function render(
element: React$Element<any>,
container: Container,
callback: ?Function,
// ): component(...props: any) | PublicInstance | null {
): Component | PublicInstance | null {
if (disableLegacyMode) {
if (__DEV__) {
console.error(
'ReactDOM.render was removed in React 19. Use createRoot instead.',
);
}
throw new Error('ReactDOM: Unsupported Legacy Mode API.');
}
if (__DEV__) {
console.error(
'ReactDOM.render has not been supported since React 18. Use createRoot ' +
'instead. Until you switch to the new API, your app will behave as ' +
"if it's running React 17. Learn " +
'more: https://react.dev/link/switch-to-createroot',
);
}

if (!isValidContainer(container)) {
throw new Error('Target container is not a DOM element.');
}

if (__DEV__) {
const isModernRoot =
isContainerMarkedAsRoot(container) &&
container._reactRootContainer === undefined;
if (isModernRoot) {
console.error(
'You are calling ReactDOM.render() on a container that was previously ' +
'passed to ReactDOMClient.createRoot(). This is not supported. ' +
'Did you mean to call root.render(element)?',
);
}
}
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}

六、在节点卸载组件

备注
export function unmountComponentAtNode(container: Container): boolean {
if (disableLegacyMode) {
if (__DEV__) {
console.error(
'unmountComponentAtNode was removed in React 19. Use root.unmount() instead.',
);
}
throw new Error('ReactDOM: Unsupported Legacy Mode API.');
}
if (!isValidContainer(container)) {
throw new Error('Target container is not a DOM element.');
}

if (__DEV__) {
const isModernRoot =
isContainerMarkedAsRoot(container) &&
container._reactRootContainer === undefined;
if (isModernRoot) {
console.error(
'You are calling ReactDOM.unmountComponentAtNode() on a container that was previously ' +
'passed to ReactDOMClient.createRoot(). This is not supported. Did you mean to call root.unmount()?',
);
}
}

if (container._reactRootContainer) {
const root = container._reactRootContainer;

if (__DEV__) {
const rootEl = getReactRootElementInContainer(container);
const renderedByDifferentReact = rootEl && !getInstanceFromNode(rootEl);
if (renderedByDifferentReact) {
console.error(
"unmountComponentAtNode(): The node you're attempting to unmount " +
'was rendered by another copy of React.',
);
}
}

updateContainerSync(null, root, null, null);
flushSyncWork();
container._reactRootContainer = null;
unmarkContainerAsRoot(container);
return true;
} else {
if (__DEV__) {
const rootEl = getReactRootElementInContainer(container);
const hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl));

// Check if the container itself is a React root node.
// 检查容器本身是否是 React 根节点。
const isContainerReactRoot =
container.nodeType === ELEMENT_NODE &&
isValidContainer(container.parentNode) &&
!!container.parentNode._reactRootContainer;

if (hasNonRootReactChild) {
console.error(
"unmountComponentAtNode(): The node you're attempting to unmount " +
'was rendered by React and is not a top-level container. %s',
isContainerReactRoot
? 'You may have accidentally passed in a React root node instead ' +
'of its container.'
: 'Instead, have the parent component update its state and ' +
'rerender in order to remove this component.',
);
}
}

return false;
}
}

七、常量

1. ReactFiber错误对话框 WWW

备注

源码中 75 - 82 行

// Provided by www
// 由 www 提供
const ReactFiberErrorDialogWWW = require('ReactFiberErrorDialog');

if (typeof ReactFiberErrorDialogWWW.showErrorDialog !== 'function') {
throw new Error(
'Expected ReactFiberErrorDialog.showErrorDialog to be a function.',
);
}

2. 默认过渡指示器上不执行操作

备注

源码中 - 行

  • noop()noop 实现
const noopOnDefaultTransitionIndicator = noop;

3. 在可恢复错误上不操作

备注

源码中 214 - 216 行

  • noop()noop 实现
// This isn't reachable because onRecoverableError isn't called in the
// legacy API.
// 这是无法到达的,因为在旧版本 API 中不会调用 onRecoverableError。
const noopOnRecoverableError = noop;

八、变量

1. 顶层更新警告

源码中 167 - 200 行

备注
let topLevelUpdateWarnings;

if (__DEV__) {
topLevelUpdateWarnings = (container: Container) => {
if (container._reactRootContainer && container.nodeType !== COMMENT_NODE) {
const hostInstance = findHostInstanceWithNoPortals(
container._reactRootContainer.current,
);
if (hostInstance) {
if (hostInstance.parentNode !== container) {
console.error(
'It looks like the React-rendered content of this ' +
'container was removed without using React. This is not ' +
'supported and will cause errors. Instead, call ' +
'ReactDOM.unmountComponentAtNode to empty a container.',
);
}
}
}

const isRootRenderedBySomeReact = !!container._reactRootContainer;
const rootEl = getReactRootElementInContainer(container);
const hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl));

if (hasNonRootReactChild && !isRootRenderedBySomeReact) {
console.error(
'Replacing React-rendered children with a new root ' +
'component. If you intended to update the children of this node, ' +
'you should instead have the existing children update their state ' +
'and render the new components instead of calling ReactDOM.render.',
);
}
};
}

九、工具

1. www 未捕获错误

备注
function wwwOnUncaughtError(
error: mixed,
// errorInfo: {+componentStack?: ?string},
errorInfo: { componentStack?: ?string },
): void {
const componentStack =
errorInfo.componentStack != null ? errorInfo.componentStack : '';
const logError = ReactFiberErrorDialogWWW.showErrorDialog({
errorBoundary: null,
error,
componentStack,
});

// Allow injected showErrorDialog() to prevent default console.error logging.
// 允许注入的 showErrorDialog() 来阻止默认的 console.error 日志记录。
// This enables renderers like ReactNative to better manage redbox behavior.
// 这使得像 ReactNative 这样的渲染器能够更好地管理红框(redbox)行为。
if (logError === false) {
return;
}

defaultOnUncaughtError(error, errorInfo);
}

2. www 捕获错误

备注
function wwwOnCaughtError(
error: mixed,
errorInfo: {
// +componentStack?: ?string,
componentStack?: ?string;
// +errorBoundary?: ?component(),
errorBoundary?: ?Component;
},
): void {
const errorBoundary = errorInfo.errorBoundary;
const componentStack =
errorInfo.componentStack != null ? errorInfo.componentStack : '';
const logError = ReactFiberErrorDialogWWW.showErrorDialog({
errorBoundary,
error,
componentStack,
});

// Allow injected showErrorDialog() to prevent default console.error logging.
// 允许注入的 showErrorDialog() 来阻止默认的 console.error 日志。
// This enables renderers like ReactNative to better manage redbox behavior.
// 这使得像 ReactNative 这样的渲染器能够更好地管理红屏行为。
if (logError === false) {
return;
}

defaultOnCaughtError(error, errorInfo);
}

3. 在容器中获取 React 根元素

function getReactRootElementInContainer(container: any) {
if (!container) {
return null;
}

if (container.nodeType === DOCUMENT_NODE) {
return container.documentElement;
} else {
return container.firstChild;
}
}

4. 从 DOM 容器创建旧版根

备注
function legacyCreateRootFromDOMContainer(
container: Container,
initialChildren: ReactNodeList,
// parentComponent: ?component(...props: any),
parentComponent: ?Component,
callback: ?Function,
isHydrationContainer: boolean,
): FiberRoot {
if (isHydrationContainer) {
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function () {
const instance = getPublicRootInstance(root);
originalCallback.call(instance);
};
}

const root: FiberRoot = createHydrationContainer(
initialChildren,
callback,
container,
LegacyRoot,
// 水合回调
null, // hydrationCallbacks
// 严格模式
false, // isStrictMode
// 默认覆盖的并发更新
false, // concurrentUpdatesByDefaultOverride,
// 标识符前缀
'', // identifierPrefix
wwwOnUncaughtError,
wwwOnCaughtError,
noopOnRecoverableError,
noopOnDefaultTransitionIndicator,
// TODO(luna) Support hydration later
// TODO(luna) 稍后支持水合
null,
null,
);
container._reactRootContainer = root;
markContainerAsRoot(root.current, container);

const rootContainerElement =
!disableCommentsAsDOMContainers && container.nodeType === COMMENT_NODE
? container.parentNode
: container;
listenToAllSupportedEvents(rootContainerElement);

flushSyncWork();
return root;
} else {
// First clear any existing content.
// 首先清除任何现有内容。
clearContainer(container);

if (typeof callback === 'function') {
const originalCallback = callback;
callback = function () {
const instance = getPublicRootInstance(root);
originalCallback.call(instance);
};
}

const root = createContainer(
container,
LegacyRoot,
// 水合回调
null, // hydrationCallbacks
// 严格模式
false, // isStrictMode
// 默认并发更新覆盖
false, // concurrentUpdatesByDefaultOverride,
// 标识符前缀
'', // identifierPrefix
wwwOnUncaughtError,
wwwOnCaughtError,
noopOnRecoverableError,
noopOnDefaultTransitionIndicator,
// 过渡回调
null, // transitionCallbacks
);
container._reactRootContainer = root;
markContainerAsRoot(root.current, container);

const rootContainerElement =
!disableCommentsAsDOMContainers && container.nodeType === COMMENT_NODE
? container.parentNode
: container;
listenToAllSupportedEvents(rootContainerElement);

// Initial mount should not be batched.
// 初始挂载不应被批处理。
updateContainerSync(initialChildren, root, parentComponent, callback);
flushSyncWork();

return root;
}
}

5. 在无效回调时发出警告

function warnOnInvalidCallback(callback: mixed): void {
if (__DEV__) {
if (callback !== null && typeof callback !== 'function') {
console.error(
'Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callback,
);
}
}
}

6. 传统渲染子树到容器

备注
function legacyRenderSubtreeIntoContainer(
// parentComponent: ?component(...props: any),
parentComponent: ?Component,
children: ReactNodeList,
container: Container,
forceHydrate: boolean,
callback: ?Function,
// ): component(...props: any) | PublicInstance | null {
): Component | PublicInstance | null {
if (__DEV__) {
topLevelUpdateWarnings(container);
warnOnInvalidCallback(callback === undefined ? null : callback);
}

const maybeRoot = container._reactRootContainer;
let root: FiberRoot;
if (!maybeRoot) {
// Initial mount
// 初始挂载
root = legacyCreateRootFromDOMContainer(
container,
children,
parentComponent,
callback,
forceHydrate,
);
} else {
root = maybeRoot;
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function () {
const instance = getPublicRootInstance(root);
originalCallback.call(instance);
};
}
// Update
// 更新
updateContainer(children, root, parentComponent, callback);
}
return getPublicRootInstance(root);
}

十、转导