[DOM] Scroll to text siblings of empty Fragments instead of the parent (#37060)

When a Fragment has no children, React would consider scrolling to
siblings first and then to parents. However, React only considered
`HostComponent` for the siblings.

Since we already have a heuristic for scrolling to `HostText`, we can
reuse that same heuristic.

---------

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
Sebastian "Sebbie" Silbermann
2026-07-19 21:41:37 +02:00
committed by GitHub
co-authored by Hanzo Dev
parent 25a2a2926e
commit fe4075812c
5 changed files with 267 additions and 90 deletions
+124 -60
View File
@@ -62,13 +62,13 @@ import {
isOwnedInstance,
} from './ReactDOMComponentTree';
import {
traverseFragmentInstance,
getFragmentParentHostFiber,
traverseFragmentInstancesAndTextInstances,
getFragmentParentInstanceOrContainerFiber,
getInstanceFromHostFiber,
isFiberFollowing,
isFiberPreceding,
getFragmentInstanceSiblings,
traverseFragmentInstanceDeeply,
getFragmentInstanceOrTextInstanceSiblings,
traverseFragmentInstancesAndTextInstancesDeeply,
fiberIsPortaledIntoHost,
isFiberContainedByFragment,
isFragmentContainedByFiber,
@@ -223,6 +223,9 @@ export type TextInstance = Text;
type InstanceWithFragmentHandles = Instance & {
reactFragments?: Set<FragmentInstanceType>,
};
type HostNodeWithFragmentHandles = (Instance | TextInstance) & {
reactFragments?: Set<FragmentInstanceType>,
};
declare class ActivityInterface extends Comment {}
declare class SuspenseInterface extends Comment {
@@ -3036,7 +3039,7 @@ FragmentInstance.prototype.addEventListener = function (
indexOfEventListener(listeners, type, listener, optionsOrUseCapture) === -1;
if (isNewEventListener) {
listeners.push({type, listener, optionsOrUseCapture});
traverseFragmentInstance(
traverseFragmentInstancesAndTextInstances(
this._fragmentFiber,
addEventListenerToChild,
type,
@@ -3052,7 +3055,7 @@ function addEventListenerToChild(
listener: EventListener,
optionsOrUseCapture?: EventListenerOptionsOrUseCapture,
): boolean {
const instance = getInstanceFromHostFiber<Instance>(child);
const instance = getInstanceFromHostFiber<Instance | TextInstance>(child);
instance.addEventListener(type, listener, optionsOrUseCapture);
return false;
}
@@ -3068,7 +3071,7 @@ FragmentInstance.prototype.removeEventListener = function (
return;
}
if (typeof listeners !== 'undefined' && listeners.length > 0) {
traverseFragmentInstance(
traverseFragmentInstancesAndTextInstances(
this._fragmentFiber,
removeEventListenerFromChild,
type,
@@ -3092,7 +3095,7 @@ function removeEventListenerFromChild(
listener: EventListener,
optionsOrUseCapture?: EventListenerOptionsOrUseCapture,
): boolean {
const instance = getInstanceFromHostFiber<Instance>(child);
const instance = getInstanceFromHostFiber<Instance | TextInstance>(child);
instance.removeEventListener(type, listener, optionsOrUseCapture);
return false;
}
@@ -3136,12 +3139,15 @@ FragmentInstance.prototype.dispatchEvent = function (
this: FragmentInstanceType,
event: Event,
): boolean {
const parentHostFiber = getFragmentParentHostFiber(this._fragmentFiber);
const parentHostFiber = getFragmentParentInstanceOrContainerFiber(
this._fragmentFiber,
);
if (parentHostFiber === null) {
return true;
}
const parentHostInstance =
getInstanceFromHostFiber<Instance>(parentHostFiber);
const parentHostInstance = getInstanceFromHostFiber<Instance | Container>(
parentHostFiber,
);
const eventListeners = this._eventListeners;
if (
(eventListeners !== null && eventListeners.length > 0) ||
@@ -3173,7 +3179,7 @@ FragmentInstance.prototype.focus = function (
this: FragmentInstanceType,
focusOptions?: FocusOptions,
): void {
traverseFragmentInstanceDeeply(
traverseFragmentInstancesAndTextInstancesDeeply(
this._fragmentFiber,
setFocusOnFiberIfFocusable,
focusOptions,
@@ -3198,7 +3204,7 @@ FragmentInstance.prototype.focusLast = function (
focusOptions?: FocusOptions,
): void {
const children: Array<Fiber> = [];
traverseFragmentInstanceDeeply(
traverseFragmentInstancesAndTextInstancesDeeply(
this._fragmentFiber,
collectChildren,
children,
@@ -3217,18 +3223,25 @@ function collectChildren(child: Fiber, collection: Array<Fiber>): boolean {
// $FlowFixMe[prop-missing]
FragmentInstance.prototype.blur = function (this: FragmentInstanceType): void {
// Early exit if activeElement is not within the fragment's parent
const parentHostFiber = getFragmentParentHostFiber(this._fragmentFiber);
const parentHostFiber = getFragmentParentInstanceOrContainerFiber(
this._fragmentFiber,
);
if (parentHostFiber === null) {
return;
}
const parentHostInstance =
getInstanceFromHostFiber<Instance>(parentHostFiber);
const activeElement = parentHostInstance.ownerDocument.activeElement;
if (activeElement === null || !parentHostInstance.contains(activeElement)) {
const parentInstanceOrContainer = getInstanceFromHostFiber<
Instance | Container,
>(parentHostFiber);
// TODO: Handle parentInstanceOrContainer being a document
const activeElement = parentInstanceOrContainer.ownerDocument.activeElement;
if (
activeElement === null ||
!parentInstanceOrContainer.contains(activeElement)
) {
return;
}
traverseFragmentInstance(
traverseFragmentInstancesAndTextInstances(
this._fragmentFiber,
blurActiveElementWithinFragment,
activeElement,
@@ -3259,16 +3272,19 @@ FragmentInstance.prototype.observeUsing = function (
if (enableFragmentRefsTextNodes) {
let hasText = false;
let hasElement = false;
traverseFragmentInstance(this._fragmentFiber, (child: Fiber) => {
if (child.tag === HostText) {
hasText = true;
} else {
// Stop traversal, found element
hasElement = true;
return true;
}
return false;
});
traverseFragmentInstancesAndTextInstances(
this._fragmentFiber,
(child: Fiber) => {
if (child.tag === HostText) {
hasText = true;
} else {
// Stop traversal, found element
hasElement = true;
return true;
}
return false;
},
);
if (hasText && !hasElement) {
console.error(
'observeUsing() was called on a FragmentInstance with only text children. ' +
@@ -3281,7 +3297,11 @@ FragmentInstance.prototype.observeUsing = function (
this._observers = new Set();
}
this._observers.add(observer);
traverseFragmentInstance(this._fragmentFiber, observeChild, observer);
traverseFragmentInstancesAndTextInstances(
this._fragmentFiber,
observeChild,
observer,
);
};
function observeChild(
child: Fiber,
@@ -3312,7 +3332,11 @@ FragmentInstance.prototype.unobserveUsing = function (
}
} else {
observers.delete(observer);
traverseFragmentInstance(this._fragmentFiber, unobserveChild, observer);
traverseFragmentInstancesAndTextInstances(
this._fragmentFiber,
unobserveChild,
observer,
);
}
};
function unobserveChild(
@@ -3334,7 +3358,11 @@ FragmentInstance.prototype.getClientRects = function (
this: FragmentInstanceType,
): Array<DOMRect> {
const rects: Array<DOMRect> = [];
traverseFragmentInstance(this._fragmentFiber, collectClientRects, rects);
traverseFragmentInstancesAndTextInstances(
this._fragmentFiber,
collectClientRects,
rects,
);
return rects;
};
function collectClientRects(child: Fiber, rects: Array<DOMRect>): boolean {
@@ -3356,12 +3384,15 @@ FragmentInstance.prototype.getRootNode = function (
this: FragmentInstanceType,
getRootNodeOptions?: {composed: boolean},
): Document | ShadowRoot | FragmentInstanceType {
const parentHostFiber = getFragmentParentHostFiber(this._fragmentFiber);
const parentHostFiber = getFragmentParentInstanceOrContainerFiber(
this._fragmentFiber,
);
if (parentHostFiber === null) {
return this;
}
const parentHostInstance =
getInstanceFromHostFiber<Instance>(parentHostFiber);
const parentHostInstance = getInstanceFromHostFiber<Instance | Container>(
parentHostFiber,
);
const rootNode =
// $FlowFixMe[incompatible-type] Flow expects Node
parentHostInstance.getRootNode(getRootNodeOptions) as Document | ShadowRoot;
@@ -3372,14 +3403,21 @@ FragmentInstance.prototype.compareDocumentPosition = function (
this: FragmentInstanceType,
otherNode: Instance,
): number {
const parentHostFiber = getFragmentParentHostFiber(this._fragmentFiber);
const parentHostFiber = getFragmentParentInstanceOrContainerFiber(
this._fragmentFiber,
);
if (parentHostFiber === null) {
return Node.DOCUMENT_POSITION_DISCONNECTED;
}
const children: Array<Fiber> = [];
traverseFragmentInstance(this._fragmentFiber, collectChildren, children);
const parentHostInstance =
getInstanceFromHostFiber<Instance>(parentHostFiber);
traverseFragmentInstancesAndTextInstances(
this._fragmentFiber,
collectChildren,
children,
);
const parentHostInstance = getInstanceFromHostFiber<Instance | Container>(
parentHostFiber,
);
if (children.length === 0) {
return compareDocumentPositionForEmptyFragment(
@@ -3390,8 +3428,10 @@ FragmentInstance.prototype.compareDocumentPosition = function (
);
}
const firstNode = getInstanceFromHostFiber<Instance>(children[0]);
const lastNode = getInstanceFromHostFiber<Instance>(
const firstNode = getInstanceFromHostFiber<Instance | TextInstance>(
children[0],
);
const lastNode = getInstanceFromHostFiber<Instance | TextInstance>(
children[children.length - 1],
);
@@ -3510,6 +3550,19 @@ function validateDocumentPositionWithFiberTree(
return false;
}
function scrollTextNodeIntoView(
textNode: TextInstance,
resolvedAlignToTop: boolean,
): void {
const range = textNode.ownerDocument.createRange();
range.selectNodeContents(textNode);
const rect = range.getBoundingClientRect();
const scrollY = resolvedAlignToTop
? window.scrollY + rect.top
: window.scrollY + rect.bottom - window.innerHeight;
window.scrollTo(window.scrollX + rect.left, scrollY);
}
if (enableFragmentRefsScrollIntoView) {
// $FlowFixMe[prop-missing]
FragmentInstance.prototype.scrollIntoView = function (
@@ -3524,17 +3577,23 @@ if (enableFragmentRefsScrollIntoView) {
}
// First, get the children nodes
const children: Array<Fiber> = [];
traverseFragmentInstance(this._fragmentFiber, collectChildren, children);
traverseFragmentInstancesAndTextInstances(
this._fragmentFiber,
collectChildren,
children,
);
const resolvedAlignToTop = alignToTop !== false;
// If there are no children, we can use the parent and siblings to determine a position
if (children.length === 0) {
const hostSiblings = getFragmentInstanceSiblings(this._fragmentFiber);
const hostSiblings = getFragmentInstanceOrTextInstanceSiblings(
this._fragmentFiber,
);
const targetFiber = resolvedAlignToTop
? hostSiblings[1] ||
hostSiblings[0] ||
getFragmentParentHostFiber(this._fragmentFiber)
getFragmentParentInstanceOrContainerFiber(this._fragmentFiber)
: hostSiblings[0] || hostSiblings[1];
if (targetFiber === null) {
@@ -3546,7 +3605,19 @@ if (enableFragmentRefsScrollIntoView) {
}
return;
}
const target = getInstanceFromHostFiber<Instance>(targetFiber);
// For text node siblings, use Range API to scroll to their position
if (enableFragmentRefsTextNodes && targetFiber.tag === HostText) {
const textNode = getInstanceFromHostFiber<TextInstance>(targetFiber);
scrollTextNodeIntoView(textNode, resolvedAlignToTop);
return;
}
const target = getInstanceFromHostFiber<Instance | Container>(
targetFiber,
);
// TODO: If the parent host fiber is a HostRoot, the target is a
// Container which can be a Document or DocumentFragment. Those have no
// scrollIntoView method, so this crashes at runtime.
// $FlowFixMe[prop-missing]
target.scrollIntoView(alignToTop);
return;
}
@@ -3556,14 +3627,8 @@ if (enableFragmentRefsScrollIntoView) {
const child = children[i];
// For text nodes, use Range API to scroll to their position
if (enableFragmentRefsTextNodes && child.tag === HostText) {
const textNode: Text = child.stateNode;
const range = textNode.ownerDocument.createRange();
range.selectNodeContents(textNode);
const rect = range.getBoundingClientRect();
const scrollY = resolvedAlignToTop
? window.scrollY + rect.top
: window.scrollY + rect.bottom - window.innerHeight;
window.scrollTo(window.scrollX + rect.left, scrollY);
const textNode = getInstanceFromHostFiber<TextInstance>(child);
scrollTextNodeIntoView(textNode, resolvedAlignToTop);
i += resolvedAlignToTop ? -1 : 1;
continue;
}
@@ -3579,17 +3644,16 @@ function addFragmentHandleToFiber(
fragmentInstance: FragmentInstanceType,
): boolean {
if (enableFragmentRefsInstanceHandles) {
const instance =
getInstanceFromHostFiber<InstanceWithFragmentHandles>(child);
if (instance != null) {
addFragmentHandleToInstance(instance, fragmentInstance);
}
const instance = getInstanceFromHostFiber<Instance | TextInstance>(
child,
) as any as HostNodeWithFragmentHandles;
addFragmentHandleToInstance(instance, fragmentInstance);
}
return false;
}
function addFragmentHandleToInstance(
instance: InstanceWithFragmentHandles,
instance: HostNodeWithFragmentHandles,
fragmentInstance: FragmentInstanceType,
): void {
if (enableFragmentRefsInstanceHandles) {
@@ -3605,7 +3669,7 @@ export function createFragmentInstance(
): FragmentInstanceType {
const fragmentInstance = new (FragmentInstance as any)(fragmentFiber);
if (enableFragmentRefsInstanceHandles) {
traverseFragmentInstance(
traverseFragmentInstancesAndTextInstances(
fragmentFiber,
addFragmentHandleToFiber,
fragmentInstance,
@@ -2755,6 +2755,48 @@ describe('FragmentRefs', () => {
restoreRange();
});
// @gate enableFragmentRefs && enableFragmentRefsTextNodes && enableFragmentRefsScrollIntoView
it('scrollIntoView scrolls to text siblings of an empty fragment using the Range API', async () => {
const restoreRange = mockRangeClientRects([
{x: 100, y: 200, width: 80, height: 16},
]);
const fragmentRef = React.createRef();
const parentRef = React.createRef();
const root = ReactDOMClient.createRoot(container);
await act(() =>
root.render(
<div ref={parentRef}>
Text before
<Fragment ref={fragmentRef} />
Text after
</div>,
),
);
const parentScrollMock = jest.fn();
parentRef.current.scrollIntoView = parentScrollMock;
// Mock window.scrollTo to verify Range-based text scrolling
const originalScrollTo = window.scrollTo;
const scrollToMock = jest.fn();
window.scrollTo = scrollToMock;
// Default call scrolls to the following text sibling
fragmentRef.current.scrollIntoView();
expect(scrollToMock).toHaveBeenCalledTimes(1);
expect(parentScrollMock).toHaveBeenCalledTimes(0);
scrollToMock.mockClear();
// alignToTop=false scrolls to the preceding text sibling
fragmentRef.current.scrollIntoView(false);
expect(scrollToMock).toHaveBeenCalledTimes(1);
expect(parentScrollMock).toHaveBeenCalledTimes(0);
window.scrollTo = originalScrollTo;
restoreRange();
});
// @gate enableFragmentRefs
it('treats passive:true and passive:false as same listener per DOM spec', async () => {
const fragmentRef = React.createRef();
@@ -24,8 +24,8 @@ import {
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import {HostText} from 'react-reconciler/src/ReactWorkTags';
import {
getFragmentParentHostFiber,
traverseFragmentInstance,
getFragmentParentInstanceOrContainerFiber,
traverseFragmentInstancesAndTextInstances,
} from 'react-reconciler/src/ReactFiberTreeReflection';
// Modules provided by RN:
@@ -704,7 +704,11 @@ FragmentInstance.prototype.observeUsing = function (
this._observers = new Set();
}
this._observers.add(observer);
traverseFragmentInstance(this._fragmentFiber, observeChild, observer);
traverseFragmentInstancesAndTextInstances(
this._fragmentFiber,
observeChild,
observer,
);
};
function observeChild(child: Fiber, observer: IntersectionObserver) {
// $FlowFixMe[incompatible-type]
@@ -728,7 +732,11 @@ FragmentInstance.prototype.unobserveUsing = function (
}
} else {
observers.delete(observer);
traverseFragmentInstance(this._fragmentFiber, unobserveChild, observer);
traverseFragmentInstancesAndTextInstances(
this._fragmentFiber,
unobserveChild,
observer,
);
}
};
function unobserveChild(child: Fiber, observer: IntersectionObserver) {
@@ -744,12 +752,18 @@ FragmentInstance.prototype.compareDocumentPosition = function (
this: FragmentInstanceType,
otherNode: PublicInstance,
): number {
const parentHostFiber = getFragmentParentHostFiber(this._fragmentFiber);
const parentHostFiber = getFragmentParentInstanceOrContainerFiber(
this._fragmentFiber,
);
if (parentHostFiber === null) {
return Node.DOCUMENT_POSITION_DISCONNECTED;
}
const children: Array<Fiber> = [];
traverseFragmentInstance(this._fragmentFiber, collectChildren, children);
traverseFragmentInstancesAndTextInstances(
this._fragmentFiber,
collectChildren,
children,
);
if (children.length === 0) {
const parentHostInstance = getPublicInstanceFromHostFiber(parentHostFiber);
return compareDocumentPositionForEmptyFragment<PublicInstance>(
@@ -804,7 +818,9 @@ FragmentInstance.prototype.getRootNode = function (
this: FragmentInstanceType,
getRootNodeOptions?: {composed: boolean},
): Node | FragmentInstanceType {
const parentHostFiber = getFragmentParentHostFiber(this._fragmentFiber);
const parentHostFiber = getFragmentParentInstanceOrContainerFiber(
this._fragmentFiber,
);
if (parentHostFiber === null) {
return this;
}
@@ -819,7 +835,11 @@ FragmentInstance.prototype.getClientRects = function (
this: FragmentInstanceType,
): Array<DOMRect> {
const rects: Array<DOMRect> = [];
traverseFragmentInstance(this._fragmentFiber, collectClientRects, rects);
traverseFragmentInstancesAndTextInstances(
this._fragmentFiber,
collectClientRects,
rects,
);
return rects;
};
function collectClientRects(child: Fiber, rects: Array<DOMRect>): boolean {
@@ -867,7 +887,7 @@ export function createFragmentInstance(
): FragmentInstanceType {
const fragmentInstance = new (FragmentInstance as any)(fragmentFiber);
if (enableFragmentRefsInstanceHandles) {
traverseFragmentInstance(
traverseFragmentInstancesAndTextInstances(
fragmentFiber,
addFragmentHandleToFiber,
fragmentInstance,
+69 -19
View File
@@ -10,6 +10,8 @@
import type {Fiber} from './ReactInternalTypes';
import type {
Container,
Instance,
TextInstance,
ActivityInstance,
SuspenseInstance,
} from './ReactFiberConfig';
@@ -345,27 +347,41 @@ export function doesFiberContain(
return false;
}
export function traverseFragmentInstance<A, B, C>(
export function traverseFragmentInstancesAndTextInstances<A, B, C>(
fragmentFiber: Fiber,
fn: (Fiber, A, B, C) => boolean,
a: A,
b: B,
c: C,
): void {
traverseVisibleHostChildren(fragmentFiber.child, false, fn, a, b, c);
traverseVisibleInstancesAndTextInstances(
fragmentFiber.child,
false,
fn,
a,
b,
c,
);
}
export function traverseFragmentInstanceDeeply<A, B, C>(
export function traverseFragmentInstancesAndTextInstancesDeeply<A, B, C>(
fragmentFiber: Fiber,
fn: (Fiber, A, B, C) => boolean,
a: A,
b: B,
c: C,
): void {
traverseVisibleHostChildren(fragmentFiber.child, true, fn, a, b, c);
traverseVisibleInstancesAndTextInstances(
fragmentFiber.child,
true,
fn,
a,
b,
c,
);
}
function traverseVisibleHostChildren<A, B, C>(
function traverseVisibleInstancesAndTextInstances<A, B, C>(
child: Fiber | null,
searchWithinHosts: boolean,
fn: (Fiber, A, B, C) => boolean,
@@ -387,7 +403,14 @@ function traverseVisibleHostChildren<A, B, C>(
} else {
if (
(searchWithinHosts || child.tag !== HostComponent) &&
traverseVisibleHostChildren(child.child, searchWithinHosts, fn, a, b, c)
traverseVisibleInstancesAndTextInstances(
child.child,
searchWithinHosts,
fn,
a,
b,
c,
)
) {
return true;
}
@@ -397,7 +420,9 @@ function traverseVisibleHostChildren<A, B, C>(
return false;
}
export function getFragmentParentHostFiber(fiber: Fiber): null | Fiber {
export function getFragmentParentInstanceOrContainerFiber(
fiber: Fiber,
): null | Fiber {
let parent = fiber.return;
while (parent !== null) {
if (parent.tag === HostRoot || parent.tag === HostComponent) {
@@ -424,20 +449,27 @@ export function fiberIsPortaledIntoHost(fiber: Fiber): boolean {
return foundPortalParent;
}
export function getFragmentInstanceSiblings(
export function getFragmentInstanceOrTextInstanceSiblings(
fiber: Fiber,
): [Fiber | null, Fiber | null] {
const result: [Fiber | null, Fiber | null] = [null, null];
const parentHostFiber = getFragmentParentHostFiber(fiber);
const parentHostFiber = getFragmentParentInstanceOrContainerFiber(fiber);
if (parentHostFiber === null) {
return result;
}
findFragmentInstanceSiblings(result, fiber, parentHostFiber.child);
findFragmentInstanceOrTextInstanceSiblings(
result,
fiber,
parentHostFiber.child,
);
return result;
}
function findFragmentInstanceSiblings(
/**
* Only collects HostText with enableFragmentRefsTextNodes enabled. Otherwise, only collects HostComponent.
*/
function findFragmentInstanceOrTextInstanceSiblings(
result: [Fiber | null, Fiber | null],
self: Fiber,
child: null | Fiber,
@@ -452,7 +484,10 @@ function findFragmentInstanceSiblings(
return true;
}
}
if (child.tag === HostComponent) {
if (
child.tag === HostComponent ||
(enableFragmentRefsTextNodes && child.tag === HostText)
) {
if (foundSelf) {
result[1] = child;
return true;
@@ -465,7 +500,14 @@ function findFragmentInstanceSiblings(
) {
// Skip hidden subtrees
} else {
if (findFragmentInstanceSiblings(result, self, child.child, foundSelf)) {
if (
findFragmentInstanceOrTextInstanceSiblings(
result,
self,
child.child,
foundSelf,
)
) {
return true;
}
}
@@ -474,7 +516,9 @@ function findFragmentInstanceSiblings(
return false;
}
export function getInstanceFromHostFiber<I>(fiber: Fiber): I {
export function getInstanceFromHostFiber<
I: Instance | TextInstance | Container,
>(fiber: Fiber): I {
switch (fiber.tag) {
case HostComponent:
case HostText:
@@ -501,8 +545,14 @@ function popSearchBoundary(): null | Fiber {
return searchBoundary;
}
export function getNextSiblingHostFiber(fiber: Fiber): null | Fiber {
traverseVisibleHostChildren(fiber.sibling, false, findNextSibling);
export function getNextSiblingInstanceOrTextInstanceFiber(
fiber: Fiber,
): null | Fiber {
traverseVisibleInstancesAndTextInstances(
fiber.sibling,
false,
findNextSibling,
);
const sibling = popSearchTarget();
pushSearchTarget(null);
return sibling;
@@ -536,7 +586,7 @@ export function isFragmentContainedByFiber(
): boolean {
let current: Fiber | null = fragmentFiber;
const fiberHostParent: Fiber | null =
getFragmentParentHostFiber(fragmentFiber);
getFragmentParentInstanceOrContainerFiber(fragmentFiber);
while (current !== null) {
if (
(current.tag === HostComponent || current.tag === HostRoot) &&
@@ -558,7 +608,7 @@ export function isFiberPreceding(fiber: Fiber, otherFiber: Fiber): boolean {
if (commonAncestor === null) {
return false;
}
traverseVisibleHostChildren(
traverseVisibleInstancesAndTextInstances(
commonAncestor,
true,
isFiberPrecedingCheck,
@@ -594,7 +644,7 @@ export function isFiberFollowing(fiber: Fiber, otherFiber: Fiber): boolean {
if (commonAncestor === null) {
return false;
}
traverseVisibleHostChildren(
traverseVisibleInstancesAndTextInstances(
commonAncestor,
true,
isFiberFollowingCheck,
+3 -2
View File
@@ -11,7 +11,7 @@
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import {getNextSiblingHostFiber} from 'react-reconciler/src/ReactFiberTreeReflection';
import {getNextSiblingInstanceOrTextInstanceFiber} from 'react-reconciler/src/ReactFiberTreeReflection';
export function compareDocumentPositionForEmptyFragment<TPublicInstance>(
fragmentFiber: Fiber,
@@ -32,7 +32,8 @@ export function compareDocumentPositionForEmptyFragment<TPublicInstance>(
if (parentResult & Node.DOCUMENT_POSITION_CONTAINED_BY) {
// otherNode is one of the fragment's siblings. Use the next
// sibling to determine if its preceding or following.
const nextSiblingFiber = getNextSiblingHostFiber(fragmentFiber);
const nextSiblingFiber =
getNextSiblingInstanceOrTextInstanceFiber(fragmentFiber);
if (nextSiblingFiber === null) {
result = Node.DOCUMENT_POSITION_PRECEDING;
} else {