From a156e2f0d1e7acf95ae5a00f0c15e2dc93deaf6e Mon Sep 17 00:00:00 2001 From: Jack Pope Date: Wed, 2 Jul 2025 15:04:10 -0400 Subject: [PATCH 1/2] Start fragment ref docs --- src/content/reference/react/Fragment.md | 143 +++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/src/content/reference/react/Fragment.md b/src/content/reference/react/Fragment.md index aa72b08a0ba..2e79bc804f3 100644 --- a/src/content/reference/react/Fragment.md +++ b/src/content/reference/react/Fragment.md @@ -4,7 +4,9 @@ title: (<>...) -``, often used via `<>...` syntax, lets you group elements without a wrapper node. +``, often used via `<>...` syntax, lets you group elements without a wrapper node. + + Fragments can also accept refs, which enable interacting with underlying DOM nodes without adding wrapper elements. ```js <> @@ -25,9 +27,43 @@ title: (<>...) Wrap elements in `` to group them together in situations where you need a single element. Grouping elements in `Fragment` has no effect on the resulting DOM; it is the same as if the elements were not grouped. The empty JSX tag `<>` is shorthand for `` in most cases. + Fragments can accept refs to provide access to the underlying DOM nodes they wrap, enabling interactions like event handling, intersection observation, and focus management without requiring additional wrapper elements. + #### Props {/*props*/} - **optional** `key`: Fragments declared with the explicit `` syntax may have [keys.](/learn/rendering-lists#keeping-list-items-in-order-with-key) +- **optional** `ref`: A ref object or callback function. React will forward the ref to a `FragmentInstance` object that provides methods for interacting with the DOM nodes wrapped by the Fragment. + + +#### 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 bubble to the DOM parent. + +**Layout methods:** +- `compareDocumentPosition(otherNode)`: Compares the document position of the fragment with another node + - An element preceding a fragment is `Node.DOCUMENT_POSITION_PRECEDING` + - An element following a fragment is `Node.DOCUMENT_POSITION_FOLLOWING` + - An element containing the fragment is `Node.DOCUMENT_POSITION_PRECEDING` and `Node.DOCUMENT_POSITION_CONTAINING` + - An element within the fragment is `NODE.DOCUMENT_POSITION_CONTAINED_BY` + - 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 `ClientRect` objects representing the bounding rectangles of all DOM nodes +- `getRootNode()`: Returns the root node containing the fragment's DOM nodes + +**Focus management methods:** +- `focus(options?)`: Focuses the first focusable DOM node in the Fragment +- `focusLast(options?)`: Focuses the last focusable DOM node in the Fragment +- `blur()`: Removes focus from all DOM nodes in 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 +71,8 @@ Wrap elements in `` to group them together in situations where you nee - React does not [reset state](/learn/preserving-and-resetting-state) when you go from rendering `<>` to `[]` or back, or when you go from rendering `<>` to `` and back. This only works a single level deep: for example, going from `<><>` to `` resets the state. See the precise semantics [here.](https://gist.github.com/clemmy/b3ef00f9507909429d8aa0d3ee4f986b) +- 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 `...`. + --- ## Usage {/*usage*/} @@ -208,3 +246,106 @@ function PostBody({ body }) { ``` + + +--- + +### 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 {11-12,16-18} +import { Fragment, useRef, useEffect } from 'react'; + +function ClickableWrapper({ children, onClick }) { + const fragmentRef = useRef(null); + + useEffect(() => { + const handleClick = (event) => { + onClick(event); + }; + + fragmentRef.current.addEventListener('click', handleClick); + return () => fragmentRef.current.removeEventListener('click', handleClick); + }, [onClick]); + + return ( + + {children} + + ); +} +``` +--- + +### Tracking visibility with Fragment refs {/*tracking-visibility-with-fragment-refs*/} + +Fragment refs are particularly 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, useEffect } from 'react'; + +function VisibilityObserver({ threshold = 0.5, onVisibilityChange, children }) { + const fragmentRef = useRef(null); + + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + onVisibilityChange(entries[0].isIntersecting); + }, + { threshold } + ); + + fragmentRef.current.observeUsing(observer); + return () => fragmentRef.current.unobserveUsing(observer); + }, [threshold, onVisibilityChange]); + + return ( + + {children} + + ); +} + +function MyComponent() { + const handleVisibilityChange = (isVisible) => { + console.log('Component is', isVisible ? 'visible' : 'hidden'); + }; + + return ( + + + + + ); +} +``` + +This pattern replaces effect-based visibility logging, which can be unreliable due to component lifecycle issues and Activity unmounting/remounting. + +--- + +### 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 FocusableGroup({ children }) { + const fragmentRef = useRef(null); + + return ( + <> + + + {children} + + + ); +} +``` + +The `focus()` method focuses the first focusable element within the Fragment, while `focusLast()` focuses the last focusable element. + + From 07a93bdb0b193a30ebea899decb0a39dace01a69 Mon Sep 17 00:00:00 2001 From: Jack Pope Date: Mon, 28 Jul 2025 12:08:43 -0400 Subject: [PATCH 2/2] restructure content --- src/content/reference/react/Fragment.md | 60 ++++++++++--------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/src/content/reference/react/Fragment.md b/src/content/reference/react/Fragment.md index 2e79bc804f3..e473dcb572f 100644 --- a/src/content/reference/react/Fragment.md +++ b/src/content/reference/react/Fragment.md @@ -6,7 +6,7 @@ title: (<>...) ``, often used via `<>...` syntax, lets you group elements without a wrapper node. - Fragments can also accept refs, which enable interacting with underlying DOM nodes without adding wrapper elements. + Fragments can also accept refs, which enable interacting with underlying DOM nodes without adding wrapper elements. See reference and usage below. ```js <> @@ -27,43 +27,37 @@ title: (<>...) Wrap elements in `` to group them together in situations where you need a single element. Grouping elements in `Fragment` has no effect on the resulting DOM; it is the same as if the elements were not grouped. The empty JSX tag `<>` is shorthand for `` in most cases. - Fragments can accept refs to provide access to the underlying DOM nodes they wrap, enabling interactions like event handling, intersection observation, and focus management without requiring additional wrapper elements. - -#### Props {/*props*/} - -- **optional** `key`: Fragments declared with the explicit `` syntax may have [keys.](/learn/rendering-lists#keeping-list-items-in-order-with-key) -- **optional** `ref`: A ref object or callback function. React will forward the ref to a `FragmentInstance` object that provides methods for interacting with the DOM nodes wrapped by the Fragment. - - -#### FragmentInstance {/*fragmentinstance*/} +### 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 +- `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 bubble to the DOM parent. **Layout methods:** -- `compareDocumentPosition(otherNode)`: Compares the document position of the fragment with another node - - An element preceding a fragment is `Node.DOCUMENT_POSITION_PRECEDING` - - An element following a fragment is `Node.DOCUMENT_POSITION_FOLLOWING` - - An element containing the fragment is `Node.DOCUMENT_POSITION_PRECEDING` and `Node.DOCUMENT_POSITION_CONTAINING` - - An element within the fragment is `NODE.DOCUMENT_POSITION_CONTAINED_BY` +- `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 `ClientRect` objects representing the bounding rectangles of all DOM nodes -- `getRootNode()`: Returns the root node containing the fragment's DOM nodes +- `getRootNode()`: Returns the root node containing the Fragment's DOM nodes **Focus management methods:** - `focus(options?)`: Focuses the first focusable DOM node in the Fragment - `focusLast(options?)`: Focuses the last focusable DOM node in the Fragment -- `blur()`: Removes focus from all DOM nodes in the Fragment +- `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 - +- `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 + + +#### Props {/*props*/} + +- **optional** `key`: Fragments declared with the explicit `` syntax may have [keys.](/learn/rendering-lists#keeping-list-items-in-order-with-key) +- **optional** `ref`: A ref object or callback function. React will forward the ref to a `FragmentInstance` object that provides methods for interacting with the DOM nodes wrapped by the Fragment. #### Caveats {/*caveats*/} @@ -247,10 +241,9 @@ function PostBody({ body }) { - --- -### Using Fragment refs for DOM interaction {/*using-fragment-refs-for-dom-interaction*/} +### 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()`. @@ -278,7 +271,7 @@ function ClickableWrapper({ children, onClick }) { ``` --- -### Tracking visibility with Fragment refs {/*tracking-visibility-with-fragment-refs*/} +### Tracking visibility with Fragment refs {/*tracking-visibility-with-fragment-refs*/} Fragment refs are particularly useful for visibility tracking and intersection observation. This enables you to monitor when content becomes visible without requiring the child components to expose refs: @@ -291,7 +284,7 @@ function VisibilityObserver({ threshold = 0.5, onVisibilityChange, children }) { useEffect(() => { const observer = new IntersectionObserver( (entries) => { - onVisibilityChange(entries[0].isIntersecting); + onVisibilityChange(entries.some(entry => entry.isIntersecting)) }, { threshold } ); @@ -325,27 +318,22 @@ This pattern replaces effect-based visibility logging, which can be unreliable d --- -### Focus management with Fragment refs {/*focus-management-with-fragment-refs*/} +### 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 FocusableGroup({ children }) { +function Focus({ children }) { const fragmentRef = useRef(null); return ( - <> - - - {children} - - + fragmentInstance?.focus()}> + {children} + ); } ``` The `focus()` method focuses the first focusable element within the Fragment, while `focusLast()` focuses the last focusable element. - -