mirror of
https://github.com/reactjs/react.dev.git
synced 2026-02-22 03:42:14 +00:00
Add experimental docs for Fragment refs (#8010)
* Add experimental docs for Fragment refs * Update wording * Address feedback
This commit is contained in:
@@ -4,7 +4,9 @@ title: <Fragment> (<>...</>)
|
||||
|
||||
<Intro>
|
||||
|
||||
`<Fragment>`, often used via `<>...</>` syntax, lets you group elements without a wrapper node.
|
||||
`<Fragment>`, often used via `<>...</>` syntax, lets you group elements without a wrapper node.
|
||||
|
||||
<Experimental> Fragments can also accept refs, which enable interacting with underlying DOM nodes without adding wrapper elements. See reference and usage below.</Experimental>
|
||||
|
||||
```js
|
||||
<>
|
||||
@@ -28,6 +30,33 @@ Wrap elements in `<Fragment>` to group them together in situations where you nee
|
||||
#### Props {/*props*/}
|
||||
|
||||
- **optional** `key`: Fragments declared with the explicit `<Fragment>` syntax may have [keys.](/learn/rendering-lists#keeping-list-items-in-order-with-key)
|
||||
- <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.
|
||||
|
||||
### <ExperimentalBadge /> FragmentInstance {/*fragmentinstance*/}
|
||||
|
||||
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:
|
||||
|
||||
**Event handling methods:**
|
||||
- `addEventListener(type, listener, options?)`: Adds an event listener to all first-level DOM children of the Fragment.
|
||||
- `removeEventListener(type, listener, options?)`: Removes an event listener from all first-level DOM children of the Fragment.
|
||||
- `dispatchEvent(event)`: Dispatches an event to a virtual child of the Fragment to call any added listeners and can bubble to the DOM parent.
|
||||
|
||||
**Layout methods:**
|
||||
- `compareDocumentPosition(otherNode)`: Compares the document position of the Fragment with another node.
|
||||
- If the Fragment has children, the native `compareDocumentPosition` value is returned.
|
||||
- Empty Fragments will attempt to compare positioning within the React tree and include `Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC`.
|
||||
- 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`.
|
||||
- `getClientRects()`: Returns a flat array of `DOMRect` objects representing the bounding rectangles of all children.
|
||||
- `getRootNode()`: Returns the root node containing the Fragment's parent DOM node.
|
||||
|
||||
**Focus management methods:**
|
||||
- `focus(options?)`: Focuses the first focusable DOM node in the Fragment. Focus is attempted on nested children depth-first.
|
||||
- `focusLast(options?)`: Focuses the last focusable DOM node in the Fragment. Focus is attempted on nested children depth-first.
|
||||
- `blur()`: Removes focus if `document.activeElement` is within the Fragment.
|
||||
|
||||
**Observer methods:**
|
||||
- `observeUsing(observer)`: Starts observing the Fragment's DOM children with an IntersectionObserver or ResizeObserver.
|
||||
- `unobserveUsing(observer)`: Stops observing the Fragment's DOM children with the specified observer.
|
||||
|
||||
#### Caveats {/*caveats*/}
|
||||
|
||||
@@ -35,6 +64,8 @@ Wrap elements in `<Fragment>` to group them together in situations where you nee
|
||||
|
||||
- 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)
|
||||
|
||||
- <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>`.
|
||||
|
||||
---
|
||||
|
||||
## Usage {/*usage*/}
|
||||
@@ -208,3 +239,92 @@ function PostBody({ body }) {
|
||||
```
|
||||
|
||||
</Sandpack>
|
||||
|
||||
---
|
||||
|
||||
### <ExperimentalBadge /> Using Fragment refs for DOM interaction {/*using-fragment-refs-for-dom-interaction*/}
|
||||
|
||||
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()`.
|
||||
|
||||
```js
|
||||
import { Fragment } from 'react';
|
||||
|
||||
function ClickableFragment({ children, onClick }) {
|
||||
return (
|
||||
<Fragment ref={fragmentInstance => {
|
||||
fragmentInstance.addEventListener('click', handleClick);
|
||||
return () => fragmentInstance.removeEventListener('click', handleClick);
|
||||
}}>
|
||||
{children}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
### <ExperimentalBadge /> Tracking visibility with Fragment refs {/*tracking-visibility-with-fragment-refs*/}
|
||||
|
||||
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:
|
||||
|
||||
```js {19,21,31-34}
|
||||
import { Fragment, useRef, useLayoutEffect } from 'react';
|
||||
|
||||
function VisibilityObserverFragment({ threshold = 0.5, onVisibilityChange, children }) {
|
||||
const fragmentRef = useRef(null);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
onVisibilityChange(entries.some(entry => entry.isIntersecting))
|
||||
},
|
||||
{ threshold }
|
||||
);
|
||||
|
||||
fragmentRef.current.observeUsing(observer);
|
||||
return () => fragmentRef.current.unobserveUsing(observer);
|
||||
}, [threshold, onVisibilityChange]);
|
||||
|
||||
return (
|
||||
<Fragment ref={fragmentRef}>
|
||||
{children}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
function MyComponent() {
|
||||
const handleVisibilityChange = (isVisible) => {
|
||||
console.log('Component is', isVisible ? 'visible' : 'hidden');
|
||||
};
|
||||
|
||||
return (
|
||||
<VisibilityObserverFragment onVisibilityChange={handleVisibilityChange}>
|
||||
<SomeThirdPartyComponent />
|
||||
<AnotherComponent />
|
||||
</VisibilityObserverFragment>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
### <ExperimentalBadge /> Focus management with Fragment refs {/*focus-management-with-fragment-refs*/}
|
||||
|
||||
Fragment refs provide focus management methods that work across all DOM nodes within the Fragment:
|
||||
|
||||
```js
|
||||
import { Fragment, useRef } from 'react';
|
||||
|
||||
function FocusFragment({ children }) {
|
||||
const fragmentRef = useRef(null);
|
||||
|
||||
return (
|
||||
<Fragment ref={(fragmentInstance) => fragmentInstance?.focus()}>
|
||||
{children}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
The `focus()` method focuses the first focusable element within the Fragment, while `focusLast()` focuses the last focusable element.
|
||||
|
||||
Reference in New Issue
Block a user