Skip to content

Commit ae584af

Browse files
authored
Add experimental docs for Fragment refs (#8010)
* Add experimental docs for Fragment refs * Update wording * Address feedback
1 parent 775d895 commit ae584af

File tree

1 file changed

+121
-1
lines changed

1 file changed

+121
-1
lines changed

src/content/reference/react/Fragment.md

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ title: <Fragment> (<>...</>)
44

55
<Intro>
66

7-
`<Fragment>`, often used via `<>...</>` syntax, lets you group elements without a wrapper node.
7+
`<Fragment>`, often used via `<>...</>` syntax, lets you group elements without a wrapper node.
8+
9+
<Experimental> Fragments can also accept refs, which enable interacting with underlying DOM nodes without adding wrapper elements. See reference and usage below.</Experimental>
810

911
```js
1012
<>
@@ -28,13 +30,42 @@ Wrap elements in `<Fragment>` to group them together in situations where you nee
2830
#### Props {/*props*/}
2931

3032
- **optional** `key`: Fragments declared with the explicit `<Fragment>` syntax may have [keys.](/learn/rendering-lists#keeping-list-items-in-order-with-key)
33+
- <ExperimentalBadge /> **optional** `ref`: A ref object (e.g. from [`useRef`](/reference/react/useRef)) or [callback function](/reference/react-dom/components/common#ref-callback). React provides a `FragmentInstance` as the ref value that implements methods for interacting with the DOM nodes wrapped by the Fragment.
34+
35+
### <ExperimentalBadge /> FragmentInstance {/*fragmentinstance*/}
36+
37+
When you pass a ref to a fragment, React provides a `FragmentInstance` object with methods for interacting with the DOM nodes wrapped by the fragment:
38+
39+
**Event handling methods:**
40+
- `addEventListener(type, listener, options?)`: Adds an event listener to all first-level DOM children of the Fragment.
41+
- `removeEventListener(type, listener, options?)`: Removes an event listener from all first-level DOM children of the Fragment.
42+
- `dispatchEvent(event)`: Dispatches an event to a virtual child of the Fragment to call any added listeners and can bubble to the DOM parent.
43+
44+
**Layout methods:**
45+
- `compareDocumentPosition(otherNode)`: Compares the document position of the Fragment with another node.
46+
- If the Fragment has children, the native `compareDocumentPosition` value is returned.
47+
- Empty Fragments will attempt to compare positioning within the React tree and include `Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC`.
48+
- Elements that have a different relationship in the React tree and DOM tree due to portaling or other insertions are `Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC`.
49+
- `getClientRects()`: Returns a flat array of `DOMRect` objects representing the bounding rectangles of all children.
50+
- `getRootNode()`: Returns the root node containing the Fragment's parent DOM node.
51+
52+
**Focus management methods:**
53+
- `focus(options?)`: Focuses the first focusable DOM node in the Fragment. Focus is attempted on nested children depth-first.
54+
- `focusLast(options?)`: Focuses the last focusable DOM node in the Fragment. Focus is attempted on nested children depth-first.
55+
- `blur()`: Removes focus if `document.activeElement` is within the Fragment.
56+
57+
**Observer methods:**
58+
- `observeUsing(observer)`: Starts observing the Fragment's DOM children with an IntersectionObserver or ResizeObserver.
59+
- `unobserveUsing(observer)`: Stops observing the Fragment's DOM children with the specified observer.
3160

3261
#### Caveats {/*caveats*/}
3362

3463
- If you want to pass `key` to a Fragment, you can't use the `<>...</>` syntax. You have to explicitly import `Fragment` from `'react'` and render `<Fragment key={yourKey}>...</Fragment>`.
3564

3665
- React does not [reset state](/learn/preserving-and-resetting-state) when you go from rendering `<><Child /></>` to `[<Child />]` or back, or when you go from rendering `<><Child /></>` to `<Child />` and back. This only works a single level deep: for example, going from `<><><Child /></></>` to `<Child />` resets the state. See the precise semantics [here.](https://gist.github.com/clemmy/b3ef00f9507909429d8aa0d3ee4f986b)
3766

67+
- <ExperimentalBadge /> If you want to pass `ref` to a Fragment, you can't use the `<>...</>` syntax. You have to explicitly import `Fragment` from `'react'` and render `<Fragment ref={yourRef}>...</Fragment>`.
68+
3869
---
3970

4071
## Usage {/*usage*/}
@@ -208,3 +239,92 @@ function PostBody({ body }) {
208239
```
209240
210241
</Sandpack>
242+
243+
---
244+
245+
### <ExperimentalBadge /> Using Fragment refs for DOM interaction {/*using-fragment-refs-for-dom-interaction*/}
246+
247+
Fragment refs allow you to interact with the DOM nodes wrapped by a Fragment without adding extra wrapper elements. This is useful for event handling, visibility tracking, focus management, and replacing deprecated patterns like `ReactDOM.findDOMNode()`.
248+
249+
```js
250+
import { Fragment } from 'react';
251+
252+
function ClickableFragment({ children, onClick }) {
253+
return (
254+
<Fragment ref={fragmentInstance => {
255+
fragmentInstance.addEventListener('click', handleClick);
256+
return () => fragmentInstance.removeEventListener('click', handleClick);
257+
}}>
258+
{children}
259+
</Fragment>
260+
);
261+
}
262+
```
263+
---
264+
265+
### <ExperimentalBadge /> Tracking visibility with Fragment refs {/*tracking-visibility-with-fragment-refs*/}
266+
267+
Fragment refs are useful for visibility tracking and intersection observation. This enables you to monitor when content becomes visible without requiring the child Components to expose refs:
268+
269+
```js {19,21,31-34}
270+
import { Fragment, useRef, useLayoutEffect } from 'react';
271+
272+
function VisibilityObserverFragment({ threshold = 0.5, onVisibilityChange, children }) {
273+
const fragmentRef = useRef(null);
274+
275+
useLayoutEffect(() => {
276+
const observer = new IntersectionObserver(
277+
(entries) => {
278+
onVisibilityChange(entries.some(entry => entry.isIntersecting))
279+
},
280+
{ threshold }
281+
);
282+
283+
fragmentRef.current.observeUsing(observer);
284+
return () => fragmentRef.current.unobserveUsing(observer);
285+
}, [threshold, onVisibilityChange]);
286+
287+
return (
288+
<Fragment ref={fragmentRef}>
289+
{children}
290+
</Fragment>
291+
);
292+
}
293+
294+
function MyComponent() {
295+
const handleVisibilityChange = (isVisible) => {
296+
console.log('Component is', isVisible ? 'visible' : 'hidden');
297+
};
298+
299+
return (
300+
<VisibilityObserverFragment onVisibilityChange={handleVisibilityChange}>
301+
<SomeThirdPartyComponent />
302+
<AnotherComponent />
303+
</VisibilityObserverFragment>
304+
);
305+
}
306+
```
307+
308+
This pattern is an alternative to Effect-based visibility logging, which is an anti-pattern in most cases. Relying on Effects alone does not guarantee that the rendered Component is observable by the user.
309+
310+
---
311+
312+
### <ExperimentalBadge /> Focus management with Fragment refs {/*focus-management-with-fragment-refs*/}
313+
314+
Fragment refs provide focus management methods that work across all DOM nodes within the Fragment:
315+
316+
```js
317+
import { Fragment, useRef } from 'react';
318+
319+
function FocusFragment({ children }) {
320+
const fragmentRef = useRef(null);
321+
322+
return (
323+
<Fragment ref={(fragmentInstance) => fragmentInstance?.focus()}>
324+
{children}
325+
</Fragment>
326+
);
327+
}
328+
```
329+
330+
The `focus()` method focuses the first focusable element within the Fragment, while `focusLast()` focuses the last focusable element.

0 commit comments

Comments
 (0)