facebook / react · Issue No. 37610
[!NOTE]
AI Disclosure: An AI (Claude Fable 5.1) wrote the issue below. I checked it and take responsibility for it.
React version: 19.3.0 (react and react-dom). The relevant validation code
looks unchanged on current main, so I believe it is still affected.
<Fragment ref={fragmentRef}> with at least one DOM child, plus afragmentRef.current.compareDocumentPosition(afterSibling) and thenimport { Fragment, useRef, useState } from 'react';
function App() {
const fragmentRef = useRef(null);
const [results, setResults] = useState([]);
return (
<div>
<button
onClick={() => {
const mask = fragmentRef.current.compareDocumentPosition(
document.getElementById('after'),
);
// The state update re-renders, which flips the result of the
// *next* click.
setResults((prev) => [...prev, mask]);
}}
>
compareDocumentPosition(afterSibling)
</button>
<Fragment ref={fragmentRef}>
<p>fragment child</p>
</Fragment>
<p id="after">sibling after the fragment</p>
<pre>{results.join(', ')}</pre>
</div>
);
}
Link to code example: https://github.com/uhyo/react-193-playground/blob/51307a38a34202644611223a3f73e415b5ae1640/docs/repro-compare-document-position.html
The result alternates with render parity:
4, 32, 4, 32, 4, 32
i.e. DOCUMENT_POSITION_FOLLOWING on even render generations and bareDOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC on odd ones. ThePRECEDING case (a sibling before the fragment) is affected the same way on
odd generations. Queries for nodes inside the fragment (CONTAINED_BY) are
stable.
Reproduced in Chromium with react-dom 19.3.0, in both development and
production builds, with and without StrictMode.
A stable DOCUMENT_POSITION_FOLLOWING (4) / DOCUMENT_POSITION_PRECEDING (2)
regardless of how many times the tree has re-rendered. Per the
Fragment docs,DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC is expected for empty
Fragments and Fragments with children rendered through a portal — neither
applies here.
The fiber-tree validation added in #34069
(validateDocumentPositionWithFiberTree) returnsDOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC when it cannot corroborate the
DOM-derived answer. Its helpers isFiberPrecedingCheck andisFiberFollowingCheck inpackages/react-reconciler/src/ReactFiberTreeReflection.js compare fibers by
strict identity (child === boundary, child === target), while nearby
checks in the same file accept alternates (e.g.current === fragmentFiber || current.alternate === fragmentFiber in theCONTAINED_BY branch — which is presumably why that case is stable).
The traversal walks the current tree from the common ancestor, but target
comes from getClosestInstanceFromNode(otherNode) (the fiber cached on the
DOM node) and boundary from traversing the FragmentInstance's stored_fragmentFiber — after a commit these can belong to the alternate
generation, so the identity comparisons miss, the search finds nothing,
validation reports failure, and the fallback 32 is returned. The
fiber/alternate pair swaps roles on each commit, which matches the observed
every-other-render alternation exactly.
Searched existing issues/PRs mentioning compareDocumentPosition andIMPLEMENTATION_SPECIFIC; none report this alternating behavior:
compareDocumentPosition to fragment instances; #34069 addedCONTAINED_BY accepted DOM thatdoesFiberContain, which does handle alternates — the preceding/followingcompareDocumentPosition(document) TypeError),Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.