import type {
Fiber,
FiberRoot,
SuspenseHydrationCallbacks,
TransitionTracingCallbacks,
} from './ReactInternalTypes';
import type {RootTag} from './ReactRootTags';
import type {
Container,
PublicInstance,
RendererInspectionConfig,
} from './ReactFiberConfig';
import type {ReactNodeList, ReactFormState} from 'shared/ReactTypes';
import type {Lane} from './ReactFiberLane';
import type {SuspenseState} from './ReactFiberSuspenseComponent';
import {LegacyRoot} from './ReactRootTags';
import {
findCurrentHostFiber,
findCurrentHostFiberWithNoPortals,
} from './ReactFiberTreeReflection';
import {get as getInstance} from 'shared/ReactInstanceMap';
import {
HostComponent,
HostSingleton,
ClassComponent,
HostRoot,
SuspenseComponent,
} from './ReactWorkTags';
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
import isArray from 'shared/isArray';
import {enableSchedulingProfiler} from 'shared/ReactFeatureFlags';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import {
getPublicInstance,
getInstanceFromNode,
rendererVersion,
rendererPackageName,
extraDevToolsConfig,
} from './ReactFiberConfig';
import {
findCurrentUnmaskedContext,
processChildContext,
emptyContextObject,
isContextProvider as isLegacyContextProvider,
} from './ReactFiberContext';
import {createFiberRoot} from './ReactFiberRoot';
import {isRootDehydrated} from './ReactFiberShellHydration';
import {
injectInternals,
markRenderScheduled,
onScheduleRoot,
injectProfilingHooks,
} from './ReactFiberDevToolsHook';
import {startUpdateTimerByLane} from './ReactProfilerTimer';
import {
requestUpdateLane,
scheduleUpdateOnFiber,
scheduleInitialHydrationOnRoot,
flushRoot,
batchedUpdates,
flushSyncFromReconciler,
flushSyncWork,
isAlreadyRendering,
deferredUpdates,
discreteUpdates,
flushPassiveEffects,
} from './ReactFiberWorkLoop';
import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates';
import {
createUpdate,
enqueueUpdate,
entangleTransitions,
} from './ReactFiberClassUpdateQueue';
import {
isRendering as ReactCurrentFiberIsRendering,
current as ReactCurrentFiberCurrent,
runWithFiberInDEV,
} from './ReactCurrentFiber';
import {StrictLegacyMode} from './ReactTypeOfMode';
import {
SyncLane,
SelectiveHydrationLane,
getHighestPriorityPendingLanes,
higherPriorityLane,
} from './ReactFiberLane';
import {
scheduleRefresh,
scheduleRoot,
setRefreshHandler,
} from './ReactFiberHotReloading';
import ReactVersion from 'shared/ReactVersion';
export {createPortal} from './ReactPortal';
export {
createComponentSelector,
createHasPseudoClassSelector,
createRoleSelector,
createTestNameSelector,
createTextSelector,
getFindAllNodesFailureDescription,
findAllNodes,
findBoundingRects,
focusWithin,
observeVisibleRects,
} from './ReactTestSelectors';
export {startHostTransition} from './ReactFiberHooks';
export {
defaultOnUncaughtError,
defaultOnCaughtError,
defaultOnRecoverableError,
} from './ReactFiberErrorLogger';
import {getLabelForLane, TotalLanes} from 'react-reconciler/src/ReactFiberLane';
type OpaqueRoot = FiberRoot;
let didWarnAboutNestedUpdates;
let didWarnAboutFindNodeInStrictMode;
if (__DEV__) {
didWarnAboutNestedUpdates = false;
didWarnAboutFindNodeInStrictMode = ({}: {[string]: boolean});
}
function getContextForSubtree(
parentComponent: ?React$Component<any, any>,
): Object {
if (!parentComponent) {
return emptyContextObject;
}
const fiber = getInstance(parentComponent);
const parentContext = findCurrentUnmaskedContext(fiber);
if (fiber.tag === ClassComponent) {
const Component = fiber.type;
if (isLegacyContextProvider(Component)) {
return processChildContext(fiber, Component, parentContext);
}
}
return parentContext;
}
function findHostInstance(component: Object): PublicInstance | null {
const fiber = getInstance(component);
if (fiber === undefined) {
if (typeof component.render === 'function') {
throw new Error('Unable to find node on an unmounted component.');
} else {
const keys = Object.keys(component).join(',');
throw new Error(
`Argument appears to not be a ReactComponent. Keys: ${keys}`,
);
}
}
const hostFiber = findCurrentHostFiber(fiber);
if (hostFiber === null) {
return null;
}
return getPublicInstance(hostFiber.stateNode);
}
function findHostInstanceWithWarning(
component: Object,
methodName: string,
): PublicInstance | null {
if (__DEV__) {
const fiber = getInstance(component);
if (fiber === undefined) {
if (typeof component.render === 'function') {
throw new Error('Unable to find node on an unmounted component.');
} else {
const keys = Object.keys(component).join(',');
throw new Error(
`Argument appears to not be a ReactComponent. Keys: ${keys}`,
);
}
}
const hostFiber = findCurrentHostFiber(fiber);
if (hostFiber === null) {
return null;
}
if (hostFiber.mode & StrictLegacyMode) {
const componentName = getComponentNameFromFiber(fiber) || 'Component';
if (!didWarnAboutFindNodeInStrictMode[componentName]) {
didWarnAboutFindNodeInStrictMode[componentName] = true;
runWithFiberInDEV(hostFiber, () => {
if (fiber.mode & StrictLegacyMode) {
console.error(
'%s is deprecated in StrictMode. ' +
'%s was passed an instance of %s which is inside StrictMode. ' +
'Instead, add a ref directly to the element you want to reference. ' +
'Learn more about using refs safely here: ' +
'https://react.dev/link/strict-mode-find-node',
methodName,
methodName,
componentName,
);
} else {
console.error(
'%s is deprecated in StrictMode. ' +
'%s was passed an instance of %s which renders StrictMode children. ' +
'Instead, add a ref directly to the element you want to reference. ' +
'Learn more about using refs safely here: ' +
'https://react.dev/link/strict-mode-find-node',
methodName,
methodName,
componentName,
);
}
});
}
}
return getPublicInstance(hostFiber.stateNode);
}
return findHostInstance(component);
}
export function createContainer(
containerInfo: Container,
tag: RootTag,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
isStrictMode: boolean,
concurrentUpdatesByDefaultOverride: null | boolean,
identifierPrefix: string,
onUncaughtError: (
error: mixed,
errorInfo: {+componentStack?: ?string},
) => void,
onCaughtError: (
error: mixed,
errorInfo: {
+componentStack?: ?string,
+errorBoundary?: ?React$Component<any, any>,
},
) => void,
onRecoverableError: (
error: mixed,
errorInfo: {+componentStack?: ?string},
) => void,
transitionCallbacks: null | TransitionTracingCallbacks,
): OpaqueRoot {
const hydrate = false;
const initialChildren = null;
return createFiberRoot(
containerInfo,
tag,
hydrate,
initialChildren,
hydrationCallbacks,
isStrictMode,
identifierPrefix,
onUncaughtError,
onCaughtError,
onRecoverableError,
transitionCallbacks,
null,
);
}
export function createHydrationContainer(
initialChildren: ReactNodeList,
callback: ?Function,
containerInfo: Container,
tag: RootTag,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
isStrictMode: boolean,
concurrentUpdatesByDefaultOverride: null | boolean,
identifierPrefix: string,
onUncaughtError: (
error: mixed,
errorInfo: {+componentStack?: ?string},
) => void,
onCaughtError: (
error: mixed,
errorInfo: {
+componentStack?: ?string,
+errorBoundary?: ?React$Component<any, any>,
},
) => void,
onRecoverableError: (
error: mixed,
errorInfo: {+componentStack?: ?string},
) => void,
transitionCallbacks: null | TransitionTracingCallbacks,
formState: ReactFormState<any, any> | null,
): OpaqueRoot {
const hydrate = true;
const root = createFiberRoot(
containerInfo,
tag,
hydrate,
initialChildren,
hydrationCallbacks,
isStrictMode,
identifierPrefix,
onUncaughtError,
onCaughtError,
onRecoverableError,
transitionCallbacks,
formState,
);
root.context = getContextForSubtree(null);
const current = root.current;
const lane = requestUpdateLane(current);
const update = createUpdate(lane);
update.callback =
callback !== undefined && callback !== null ? callback : null;
enqueueUpdate(current, update, lane);
scheduleInitialHydrationOnRoot(root, lane);
return root;
}
export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): Lane {
const current = container.current;
const lane = requestUpdateLane(current);
updateContainerImpl(
current,
lane,
element,
container,
parentComponent,
callback,
);
return lane;
}
export function updateContainerSync(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): Lane {
if (container.tag === LegacyRoot) {
flushPassiveEffects();
}
const current = container.current;
updateContainerImpl(
current,
SyncLane,
element,
container,
parentComponent,
callback,
);
return SyncLane;
}
function updateContainerImpl(
rootFiber: Fiber,
lane: Lane,
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): void {
if (__DEV__) {
onScheduleRoot(container, element);
}
if (enableSchedulingProfiler) {
markRenderScheduled(lane);
}
const context = getContextForSubtree(parentComponent);
if (container.context === null) {
container.context = context;
} else {
container.pendingContext = context;
}
if (__DEV__) {
if (
ReactCurrentFiberIsRendering &&
ReactCurrentFiberCurrent !== null &&
!didWarnAboutNestedUpdates
) {
didWarnAboutNestedUpdates = true;
console.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(ReactCurrentFiberCurrent) || 'Unknown',
);
}
}
const update = createUpdate(lane);
update.payload = {element};
callback = callback === undefined ? null : callback;
if (callback !== null) {
if (__DEV__) {
if (typeof callback !== 'function') {
console.error(
'Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callback,
);
}
}
update.callback = callback;
}
const root = enqueueUpdate(rootFiber, update, lane);
if (root !== null) {
startUpdateTimerByLane(lane);
scheduleUpdateOnFiber(root, rootFiber, lane);
entangleTransitions(root, rootFiber, lane);
}
}
export {
batchedUpdates,
deferredUpdates,
discreteUpdates,
flushSyncFromReconciler,
flushSyncWork,
isAlreadyRendering,
flushPassiveEffects,
};
export function getPublicRootInstance(
container: OpaqueRoot,
): React$Component<any, any> | PublicInstance | null {
const containerFiber = container.current;
if (!containerFiber.child) {
return null;
}
switch (containerFiber.child.tag) {
case HostSingleton:
case HostComponent:
return getPublicInstance(containerFiber.child.stateNode);
default:
return containerFiber.child.stateNode;
}
}
export function attemptSynchronousHydration(fiber: Fiber): void {
switch (fiber.tag) {
case HostRoot: {
const root: FiberRoot = fiber.stateNode;
if (isRootDehydrated(root)) {
const lanes = getHighestPriorityPendingLanes(root);
flushRoot(root, lanes);
}
break;
}
case SuspenseComponent: {
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
if (root !== null) {
scheduleUpdateOnFiber(root, fiber, SyncLane);
}
flushSyncWork();
const retryLane = SyncLane;
markRetryLaneIfNotHydrated(fiber, retryLane);
break;
}
}
}
function markRetryLaneImpl(fiber: Fiber, retryLane: Lane) {
const suspenseState: null | SuspenseState = fiber.memoizedState;
if (suspenseState !== null && suspenseState.dehydrated !== null) {
suspenseState.retryLane = higherPriorityLane(
suspenseState.retryLane,
retryLane,
);
}
}
function markRetryLaneIfNotHydrated(fiber: Fiber, retryLane: Lane) {
markRetryLaneImpl(fiber, retryLane);
const alternate = fiber.alternate;
if (alternate) {
markRetryLaneImpl(alternate, retryLane);
}
}
export function attemptContinuousHydration(fiber: Fiber): void {
if (fiber.tag !== SuspenseComponent) {
return;
}
const lane = SelectiveHydrationLane;
const root = enqueueConcurrentRenderForLane(fiber, lane);
if (root !== null) {
scheduleUpdateOnFiber(root, fiber, lane);
}
markRetryLaneIfNotHydrated(fiber, lane);
}
export function attemptHydrationAtCurrentPriority(fiber: Fiber): void {
if (fiber.tag !== SuspenseComponent) {
return;
}
const lane = requestUpdateLane(fiber);
const root = enqueueConcurrentRenderForLane(fiber, lane);
if (root !== null) {
scheduleUpdateOnFiber(root, fiber, lane);
}
markRetryLaneIfNotHydrated(fiber, lane);
}
export {findHostInstance};
export {findHostInstanceWithWarning};
export function findHostInstanceWithNoPortals(
fiber: Fiber,
): PublicInstance | null {
const hostFiber = findCurrentHostFiberWithNoPortals(fiber);
if (hostFiber === null) {
return null;
}
return getPublicInstance(hostFiber.stateNode);
}
let shouldErrorImpl: Fiber => ?boolean = fiber => null;
export function shouldError(fiber: Fiber): ?boolean {
return shouldErrorImpl(fiber);
}
let shouldSuspendImpl = (fiber: Fiber) => false;
export function shouldSuspend(fiber: Fiber): boolean {
return shouldSuspendImpl(fiber);
}
let overrideHookState = null;
let overrideHookStateDeletePath = null;
let overrideHookStateRenamePath = null;
let overrideProps = null;
let overridePropsDeletePath = null;
let overridePropsRenamePath = null;
let scheduleUpdate = null;
let setErrorHandler = null;
let setSuspenseHandler = null;
if (__DEV__) {
const copyWithDeleteImpl = (
obj: Object | Array<any>,
path: Array<string | number>,
index: number,
): $FlowFixMe => {
const key = path[index];
const updated = isArray(obj) ? obj.slice() : {...obj};
if (index + 1 === path.length) {
if (isArray(updated)) {
updated.splice(((key: any): number), 1);
} else {
delete updated[key];
}
return updated;
}
updated[key] = copyWithDeleteImpl(obj[key], path, index + 1);
return updated;
};
const copyWithDelete = (
obj: Object | Array<any>,
path: Array<string | number>,
): Object | Array<any> => {
return copyWithDeleteImpl(obj, path, 0);
};
const copyWithRenameImpl = (
obj: Object | Array<any>,
oldPath: Array<string | number>,
newPath: Array<string | number>,
index: number,
): $FlowFixMe => {
const oldKey = oldPath[index];
const updated = isArray(obj) ? obj.slice() : {...obj};
if (index + 1 === oldPath.length) {
const newKey = newPath[index];
updated[newKey] = updated[oldKey];
if (isArray(updated)) {
updated.splice(((oldKey: any): number), 1);
} else {
delete updated[oldKey];
}
} else {
updated[oldKey] = copyWithRenameImpl(
obj[oldKey],
oldPath,
newPath,
index + 1,
);
}
return updated;
};
const copyWithRename = (
obj: Object | Array<any>,
oldPath: Array<string | number>,
newPath: Array<string | number>,
): Object | Array<any> => {
if (oldPath.length !== newPath.length) {
console.warn('copyWithRename() expects paths of the same length');
return;
} else {
for (let i = 0; i < newPath.length - 1; i++) {
if (oldPath[i] !== newPath[i]) {
console.warn(
'copyWithRename() expects paths to be the same except for the deepest key',
);
return;
}
}
}
return copyWithRenameImpl(obj, oldPath, newPath, 0);
};
const copyWithSetImpl = (
obj: Object | Array<any>,
path: Array<string | number>,
index: number,
value: any,
): $FlowFixMe => {
if (index >= path.length) {
return value;
}
const key = path[index];
const updated = isArray(obj) ? obj.slice() : {...obj};
updated[key] = copyWithSetImpl(obj[key], path, index + 1, value);
return updated;
};
const copyWithSet = (
obj: Object | Array<any>,
path: Array<string | number>,
value: any,
): Object | Array<any> => {
return copyWithSetImpl(obj, path, 0, value);
};
const findHook = (fiber: Fiber, id: number) => {
let currentHook = fiber.memoizedState;
while (currentHook !== null && id > 0) {
currentHook = currentHook.next;
id--;
}
return currentHook;
};
overrideHookState = (
fiber: Fiber,
id: number,
path: Array<string | number>,
value: any,
) => {
const hook = findHook(fiber, id);
if (hook !== null) {
const newState = copyWithSet(hook.memoizedState, path, value);
hook.memoizedState = newState;
hook.baseState = newState;
fiber.memoizedProps = {...fiber.memoizedProps};
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
if (root !== null) {
scheduleUpdateOnFiber(root, fiber, SyncLane);
}
}
};
overrideHookStateDeletePath = (
fiber: Fiber,
id: number,
path: Array<string | number>,
) => {
const hook = findHook(fiber, id);
if (hook !== null) {
const newState = copyWithDelete(hook.memoizedState, path);
hook.memoizedState = newState;
hook.baseState = newState;
fiber.memoizedProps = {...fiber.memoizedProps};
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
if (root !== null) {
scheduleUpdateOnFiber(root, fiber, SyncLane);
}
}
};
overrideHookStateRenamePath = (
fiber: Fiber,
id: number,
oldPath: Array<string | number>,
newPath: Array<string | number>,
) => {
const hook = findHook(fiber, id);
if (hook !== null) {
const newState = copyWithRename(hook.memoizedState, oldPath, newPath);
hook.memoizedState = newState;
hook.baseState = newState;
fiber.memoizedProps = {...fiber.memoizedProps};
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
if (root !== null) {
scheduleUpdateOnFiber(root, fiber, SyncLane);
}
}
};
overrideProps = (fiber: Fiber, path: Array<string | number>, value: any) => {
fiber.pendingProps = copyWithSet(fiber.memoizedProps, path, value);
if (fiber.alternate) {
fiber.alternate.pendingProps = fiber.pendingProps;
}
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
if (root !== null) {
scheduleUpdateOnFiber(root, fiber, SyncLane);
}
};
overridePropsDeletePath = (fiber: Fiber, path: Array<string | number>) => {
fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path);
if (fiber.alternate) {
fiber.alternate.pendingProps = fiber.pendingProps;
}
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
if (root !== null) {
scheduleUpdateOnFiber(root, fiber, SyncLane);
}
};
overridePropsRenamePath = (
fiber: Fiber,
oldPath: Array<string | number>,
newPath: Array<string | number>,
) => {
fiber.pendingProps = copyWithRename(fiber.memoizedProps, oldPath, newPath);
if (fiber.alternate) {
fiber.alternate.pendingProps = fiber.pendingProps;
}
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
if (root !== null) {
scheduleUpdateOnFiber(root, fiber, SyncLane);
}
};
scheduleUpdate = (fiber: Fiber) => {
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
if (root !== null) {
scheduleUpdateOnFiber(root, fiber, SyncLane);
}
};
setErrorHandler = (newShouldErrorImpl: Fiber => ?boolean) => {
shouldErrorImpl = newShouldErrorImpl;
};
setSuspenseHandler = (newShouldSuspendImpl: Fiber => boolean) => {
shouldSuspendImpl = newShouldSuspendImpl;
};
}
function getCurrentFiberForDevTools() {
return ReactCurrentFiberCurrent;
}
function getLaneLabelMap(): Map<Lane, string> | null {
if (enableSchedulingProfiler) {
const map: Map<Lane, string> = new Map();
let lane = 1;
for (let index = 0; index < TotalLanes; index++) {
const label = ((getLabelForLane(lane): any): string);
map.set(lane, label);
lane *= 2;
}
return map;
} else {
return null;
}
}
export function injectIntoDevTools(): boolean {
const internals: Object = {
bundleType: __DEV__ ? 1 : 0,
version: rendererVersion,
rendererPackageName: rendererPackageName,
currentDispatcherRef: ReactSharedInternals,
findFiberByHostInstance: getInstanceFromNode,
reconcilerVersion: ReactVersion,
};
if (extraDevToolsConfig !== null) {
internals.rendererConfig = (extraDevToolsConfig: RendererInspectionConfig);
}
if (__DEV__) {
internals.overrideHookState = overrideHookState;
internals.overrideHookStateDeletePath = overrideHookStateDeletePath;
internals.overrideHookStateRenamePath = overrideHookStateRenamePath;
internals.overrideProps = overrideProps;
internals.overridePropsDeletePath = overridePropsDeletePath;
internals.overridePropsRenamePath = overridePropsRenamePath;
internals.scheduleUpdate = scheduleUpdate;
internals.setErrorHandler = setErrorHandler;
internals.setSuspenseHandler = setSuspenseHandler;
internals.scheduleRefresh = scheduleRefresh;
internals.scheduleRoot = scheduleRoot;
internals.setRefreshHandler = setRefreshHandler;
internals.getCurrentFiber = getCurrentFiberForDevTools;
}
if (enableSchedulingProfiler) {
internals.getLaneLabelMap = getLaneLabelMap;
internals.injectProfilingHooks = injectProfilingHooks;
}
return injectInternals(internals);
}