Bug: `FragmentInstance.compareDocumentPosition` alternates between `FOLLOWING` and `IMPLEMENTATION_SPECIFIC` on every other render
まだ誰も着手していません。
評価
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 初心者へのやさしさ
- 76/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- javascript, react
- 領域
- frontend
調査の方向性
提供されたJSXの再現コードをReact 19.3.0で実行し、再レンダー間で位置マスクが交互になることを確認してください。次に packages/react-reconciler/src/ReactFiberTreeReflection.js を調査し、特に validateDocumentPositionWithFiberTree、isFiberPrecedingCheck、isFiberFollowingCheck を確認してください。再レンダー間で FOLLOWING と PRECEDING の結果が安定し、文書化されている空のケースやポータルのケースを変更しなければ完了です。
索引モデルが issue の本文から書いたものです。
説明
[!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.
Steps To Reproduce
- Render a
<Fragment ref={fragmentRef}>with at least one DOM child, plus a
sibling element after the fragment under the same parent. - Call
fragmentRef.current.compareDocumentPosition(afterSibling)and then
trigger any state update (re-render). - Repeat step 2.
import { 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 current behavior
The result alternates with render parity:
4, 32, 4, 32, 4, 32
i.e. DOCUMENT_POSITION_FOLLOWING on even render generations and bare
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC on odd ones. The
PRECEDING 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.
The expected behavior
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.
Appendix
Probable cause
The fiber-tree validation added in #34069
(validateDocumentPositionWithFiberTree) returns
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC when it cannot corroborate the
DOM-derived answer. Its helpers isFiberPrecedingCheck and
isFiberFollowingCheck in
packages/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 the
CONTAINED_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.
Related work (not duplicates)
Searched existing issues/PRs mentioning compareDocumentPosition and
IMPLEMENTATION_SPECIFIC; none report this alternating behavior:
- #32722 added
compareDocumentPositionto fragment instances; #34069 added
the fiber-tree validation this report concerns. - #37142 fixes an adjacent validation bug (
CONTAINED_BYaccepted DOM that
was imperatively moved outside the fiber subtree) by switching to
doesFiberContain, which does handle alternates — the preceding/following
helpers may want the same treatment. - #37578/#37579 (
compareDocumentPosition(document)TypeError),
#37162/#37163 (empty fragments / empty portals), and #37606/#37607
(portaled fragments in ShadowRoot/DocumentFragment) are different symptoms
in the same API.
- 主要言語
- JavaScript
- スター
- 251k
- フォーク
- 51.4k
- 平均マージ
- 2日 1時間
- マージ済み PR(30日)
- 51
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
react/react のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
似ている issue
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
avniproject/avni-client#2135 ·
-
automated broken-link
難易度 1/5 1時間未満 初心者へのやさしさ 85/100
-
agent/security hive/hosted-available-lke648397-260827-5n31 security
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
-
enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
babalae/bettergi-scripts-list#3674 ·
-
A-Release-Notes C-Editing D-Modest S-Ready-For-Implementation
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
bevyengine/bevy-website#2595 ·