diff --git a/src/content/learn/editor-setup.md b/src/content/learn/editor-setup.md index 024207dd..50d6e17f 100644 --- a/src/content/learn/editor-setup.md +++ b/src/content/learn/editor-setup.md @@ -31,7 +31,11 @@ Baadhi ya vihariri huja na maumbile haya yakiwa yamejengwa ndani, lakini vingine ### Linting {/*linting*/} +<<<<<<< HEAD Code linters hupata matatizo katika kodi yako unapoandika, zikikusaidia kuyarekebisha mapema. [ESLint](https://eslint.org/) ni linter maarufu ya chanzo wazi (open source) kwa ajili ya JavaScript. +======= +Code linters find problems in your code as you write, helping you fix them early. [ESLint](https://eslint.org/) is a popular, open source linter for JavaScript. +>>>>>>> 8bb31acb86bf68fa33d97dd0f1b834dfa71e2b1a * [Sakinisha ESLint kwa mpangilio uliopendekezwa kwa React](https://www.npmjs.com/package/eslint-config-react-app) (hakikisha una [Node imesakinishwa!](https://nodejs.org/en/download/current/)) * [Unganisha ESLint katika VSCode na kiendelezi rasmi](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) diff --git a/src/content/learn/thinking-in-react.md b/src/content/learn/thinking-in-react.md index 7019c18f..ef1f2482 100644 --- a/src/content/learn/thinking-in-react.md +++ b/src/content/learn/thinking-in-react.md @@ -37,9 +37,15 @@ Anza kwa kuchora visanduku kuzunguka kila kijenzi na kijenzi kidogo kwenye kiigi Kulingana na msingi wako, unaweza kufikiria kugawanya muundo katika vijenzi kwa njia tofauti: +<<<<<<< HEAD * **Programming**--tumia mbinu zile zile za kuamua ikiwa unapaswa kuunda kitendaji kipya au object. Mbinu moja kama hiyo ni [kanuni ya uwajibikaji mmoja (single responsibility principle)](https://en.wikipedia.org/wiki/Single_responsibility_principle), yaani, kijenzi kinapaswa kufanya jambo moja tu. Ikiwa kitaishia kukua, inapaswa kigawanywe kiwe vijenzi vidogo. * **CSS**--fikiria ni nini ungetengeneza viteule vya darasa. (Hatahivyo, vijenzi ni vidogo kwa kiasi fulani.) * **Design**--fikiria jinsi unavyoweza kupanga safu za muundo. +======= +* **Programming**--use the same techniques for deciding if you should create a new function or object. One such technique is the [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns), that is, a component should ideally only be concerned with one thing. If it ends up growing, it should be decomposed into smaller subcomponents. +* **CSS**--consider what you would make class selectors for. (However, components are a bit less granular.) +* **Design**--consider how you would organize the design's layers. +>>>>>>> 8bb31acb86bf68fa33d97dd0f1b834dfa71e2b1a Ikiwa JSON yako imeundwa vizuri, mara nyingi utapata kwamba inaelekeza kwa muundo wa vijenzi vya UI yako. Hiyo ni kwa sababu UI na miundo ya data mara nyingi huwa na usanifu sawa wa habari--yaani, umbo sawa. Tenganisha UI yako katika vijenzi, ambapo kila kijenzi kinalingana na kipande kimoja cha muundo yako wa data. @@ -226,10 +232,19 @@ Kilichobaki labda ni hali. Wacha tupitie moja baada ya nyingine tena: +<<<<<<< HEAD 1. Orodha asili ya bidhaa **imepitishwa kama vifaa, kwa hivyo haijabainishwa.** 2. Maandishi ya utafutaji yanaonekana kuwa ya hali kwani yanabadilika kwa wakati na hayawezi kukokotwa kutoka kwa chochote. 3. Thamani ya kisanduku cha kuteua inaonekana kuwa hali kwani inabadilika kwa wakati na haiwezi kukokotwa kutoka kwa chochote. 4. Orodha iliyochujwa ya bidhaa **haijabainishwa kwa sababu inaweza kukokotwa** kwa kuchukua orodha asili ya bidhaa na kuichuja kulingana na maandishi ya utafutaji na thamani ya kisanduku cha kuteua. +======= +1. The original list of products is **passed in as props, so it's not state.** +2. The search text seems to be state since it changes over time and can't be computed from anything. +3. The value of the checkbox seems to be state since it changes over time and can't be computed from anything. +4. The filtered list of products **isn't state because it can be computed** by taking the original list of products and filtering it according to the search text and value of the checkbox. + +This means only the search text and the value of the checkbox are state! Nicely done! +>>>>>>> 8bb31acb86bf68fa33d97dd0f1b834dfa71e2b1a Hii ina maana kwamba ni maandishi ya utafutaji pekee na thamani ya kisanduku cha kuteua ndizo zilizotajwa! Imefanywa vizuri! @@ -262,6 +277,7 @@ In the previous step, you found two pieces of state in this application: the sea Katika hatua ya awali, ulipata vipande viwili vya hali katika programu hii: maandishi ya ingizo ya utafutaji, na thamani ya kisanduku cha kuteua. Katika mfano huu, daima huonekana pamoja, kwa hiyo ni mantiki kuviweka katika sehemu moja. +<<<<<<< HEAD Sasa wacha tupitie mkakati wetu kwao: 1. **Tambua vijenzi vinavyotumia hali:** @@ -269,6 +285,15 @@ Sasa wacha tupitie mkakati wetu kwao: * `SearchBar` inahitaji kuonyesha hali hiyo (maandishi ya utafutaji na thamani ya kisanduku cha kuteua). 1. **Tafuta mzazi wao wa kawaida:** Kijenzi mzazi cha kwanza ambacho vijenzi vyote viwili hushiriki ni `FilterableProductTable`. 2. **Amua mahali pa kuishi**: Tutaweka maandishi ya kichujio na thamani za hali zilizochaguliwa katika `FilterableProductTable`. +======= +1. **Identify components that use state:** + * `ProductTable` needs to filter the product list based on that state (search text and checkbox value). + * `SearchBar` needs to display that state (search text and checkbox value). +2. **Find their common parent:** The first parent component both components share is `FilterableProductTable`. +3. **Decide where the state lives**: We'll keep the filter text and checked state values in `FilterableProductTable`. + +So the state values will live in `FilterableProductTable`. +>>>>>>> 8bb31acb86bf68fa33d97dd0f1b834dfa71e2b1a Kwa hivyo state thamani zitaishi katika `FilterableProductTable`. @@ -456,7 +481,16 @@ function SearchBar({ filterText, inStockOnly }) { Hata hivyo, bado hujaongeza msimbo wowote ili kujibu vitendo vya mtumiaji kama vile kuandika. Hii itakuwa hatua yako ya mwisho. +<<<<<<< HEAD ## Hatua ya 5: Ongeza mtiririko wa data kinyume {/*step-5-add-inverse-data-flow*/} +======= + +## Step 5: Add inverse data flow {/*step-5-add-inverse-data-flow*/} + +Currently your app renders correctly with props and state flowing down the hierarchy. But to change the state according to user input, you will need to support data flowing the other way: the form components deep in the hierarchy need to update the state in `FilterableProductTable`. + +React makes this data flow explicit, but it requires a little more typing than two-way data binding. If you try to type or check the box in the example above, you'll see that React ignores your input. This is intentional. By writing ``, you've set the `value` prop of the `input` to always be equal to the `filterText` state passed in from `FilterableProductTable`. Since `filterText` state is never set, the input never changes. +>>>>>>> 8bb31acb86bf68fa33d97dd0f1b834dfa71e2b1a You want to make it so whenever the user changes the form inputs, the state updates to reflect those changes. The state is owned by `FilterableProductTable`, so only it can call `setFilterText` and `setInStockOnly`. To let `SearchBar` update the `FilterableProductTable`'s state, you need to pass these functions down to `SearchBar`: diff --git a/src/content/learn/tutorial-tic-tac-toe.md b/src/content/learn/tutorial-tic-tac-toe.md index 4c2404b9..ec8d0f61 100644 --- a/src/content/learn/tutorial-tic-tac-toe.md +++ b/src/content/learn/tutorial-tic-tac-toe.md @@ -337,11 +337,7 @@ import './styles.css'; import App from './App'; ``` -<<<<<<< HEAD Mstari wa 1-5 unaleta vipande vyote muhimu pamoja: -======= -Lines 1-5 bring all the necessary pieces together: ->>>>>>> 6ec61348646040795fdaa9de14a9bec603260f87 * React * Maktaba ya React ya kuwasiliana na kivinjari cha wavuti (React DOM) @@ -555,11 +551,7 @@ export default function Board() { } ``` -<<<<<<< HEAD Angalia jinsi, kinyume na `div`s za kivinjari, viungo vyako `Board` na `Square` lazima vianze na herufi kubwa. -======= -Note how unlike the browser `div`s, your own components `Board` and `Square` must start with a capital letter. ->>>>>>> 6ec61348646040795fdaa9de14a9bec603260f87 Hebu tuangalie: @@ -1102,11 +1094,7 @@ function Square({ value, onSquareClick }) { } ``` -<<<<<<< HEAD Sasa utaunganisha kipengele cha `onSquareClick` na function katika sehemu ya `Board` utakayoita `handleClick`. Ili kuunganisha `onSquareClick` na `handleClick`, utapita function kwa kipengele cha `onSquareClick` cha sehemu ya kwanza ya `Square`: -======= -Now you'll connect the `onSquareClick` prop to a function in the `Board` component that you'll name `handleClick`. To connect `onSquareClick` to `handleClick` you'll pass a function to the `onSquareClick` prop of the first `Square` component: ->>>>>>> 6ec61348646040795fdaa9de14a9bec603260f87 ```js export default function Board() { @@ -2085,22 +2073,13 @@ export default function Game() { } ``` -<<<<<<< HEAD Unaweza kuona jinsi kanuni yako inavyopaswa kuonekana hapa chini. Kumbuka kwamba unapaswa kuona hitilafu katika koni ya zana za mende inayosema: -======= -You can see what your code should look like below. Note that you should see an error in the developer tools console that says: ->>>>>>> 6ec61348646040795fdaa9de14a9bec603260f87 Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `Game`. -<<<<<<< HEAD Utarekebisha hitilafu hii katika sehemu inayofuata. -======= - -You'll fix this error in the next section. ->>>>>>> 6ec61348646040795fdaa9de14a9bec603260f87 diff --git a/src/content/reference/react/Fragment.md b/src/content/reference/react/Fragment.md index 5ffc4527..127476f5 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. See reference and usage below. +Fragments can also accept refs, which enable interacting with underlying DOM nodes without adding wrapper elements. ```js <> @@ -30,16 +30,13 @@ Wrap elements in `` to group them together in situations where you nee #### 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 (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. +- **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. -### FragmentInstance {/*fragmentinstance*/} +#### Caveats {/*caveats*/} -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: +* 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 `...`. -**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. +* 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) **Layout methods:** - `compareDocumentPosition(otherNode)`: Compares the document position of the Fragment with another node. @@ -49,22 +46,248 @@ When you pass a ref to a fragment, React provides a `FragmentInstance` object wi - `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. +### `FragmentInstance` {/*fragmentinstance*/} -#### Caveats {/*caveats*/} +When you pass a `ref` to a Fragment, React provides a `FragmentInstance` object. It implements methods for interacting with the first-level DOM children wrapped by the Fragment. + +* [`addEventListener`](#addeventlistener) and [`removeEventListener`](#removeeventlistener) manage event listeners across all first-level DOM children. +* [`dispatchEvent`](#dispatchevent) dispatches an event on the Fragment, which can bubble to the DOM parent. +* [`focus`](#focus), [`focusLast`](#focuslast), and [`blur`](#blur) manage focus across all nested children depth-first. +* [`observeUsing`](#observeusing) and [`unobserveUsing`](#unobserveusing) attach and detach `IntersectionObserver` or `ResizeObserver` instances. +* [`getClientRects`](#getclientrects) returns bounding rectangles of all first-level DOM children. +* [`getRootNode`](#getrootnode) returns the root node of the Fragment's parent. +* [`compareDocumentPosition`](#comparedocumentposition) compares the Fragment's position with another node. +* [`scrollIntoView`](#scrollintoview) scrolls the Fragment's children into view. + +--- + +#### `addEventListener(type, listener, options?)` {/*addeventlistener*/} + +Adds an event listener to all first-level DOM children of the Fragment. + +```js +fragmentRef.current.addEventListener('click', handleClick); +``` + +##### Parameters {/*addeventlistener-parameters*/} + +* `type`: A string representing the event type to listen for (e.g. `'click'`, `'focus'`). +* `listener`: The event handler function. +* **optional** `options`: An options object or boolean for capture, matching the [DOM `addEventListener` API.](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) + +##### Returns {/*addeventlistener-returns*/} + +`addEventListener` does not return anything (`undefined`). + +--- + +#### `removeEventListener(type, listener, options?)` {/*removeeventlistener*/} + +Removes an event listener from all first-level DOM children of the Fragment. + +```js +fragmentRef.current.removeEventListener('click', handleClick); +``` + +##### Parameters {/*removeeventlistener-parameters*/} + +* `type`: The event type string. +* `listener`: The event handler function to remove. +* **optional** `options`: An options object or boolean, matching the [DOM `removeEventListener` API.](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener) + +##### Returns {/*removeeventlistener-returns*/} + +`removeEventListener` does not return anything (`undefined`). + +--- + +#### `dispatchEvent(event)` {/*dispatchevent*/} + +Dispatches an event on the Fragment. Added event listeners are called, and the event can bubble to the Fragment's DOM parent. + +```js +fragmentRef.current.dispatchEvent(new Event('custom', { bubbles: true })); +``` + +##### Parameters {/*dispatchevent-parameters*/} + +* `event`: An [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) object to dispatch. If `bubbles` is `true`, the event bubbles to the Fragment's parent DOM node. + +##### Returns {/*dispatchevent-returns*/} + +`true` if the event was not cancelled, `false` if `preventDefault()` was called. + +--- + +#### `focus(options?)` {/*focus*/} + +Focuses the first focusable DOM node in the Fragment. Unlike calling `element.focus()` on a DOM element, this method searches *all* nested children depth-first until it finds a focusable element—not just the element itself or its direct children. + +```js +fragmentRef.current.focus(); +``` + +##### Parameters {/*focus-parameters*/} + +* **optional** `options`: A [`FocusOptions`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus#options) object (e.g. `{ preventScroll: true }`). + +##### Returns {/*focus-returns*/} + +`focus` does not return anything (`undefined`). + +--- + +#### `focusLast(options?)` {/*focuslast*/} + +Focuses the last focusable DOM node in the Fragment. Searches nested children depth-first, then iterates in reverse. + +```js +fragmentRef.current.focusLast(); +``` + +##### Parameters {/*focuslast-parameters*/} + +* **optional** `options`: A [`FocusOptions`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus#options) object. + +##### Returns {/*focuslast-returns*/} + +`focusLast` does not return anything (`undefined`). + +--- + +#### `blur()` {/*blur*/} + +Removes focus from the active element if it is within the Fragment. If `document.activeElement` is not within the Fragment, `blur` does nothing. + +```js +fragmentRef.current.blur(); +``` + +##### Returns {/*blur-returns*/} + +`blur` does not return anything (`undefined`). + +--- + +#### `observeUsing(observer)` {/*observeusing*/} + +Starts observing all first-level DOM children of the Fragment with the provided observer. + +```js +const observer = new IntersectionObserver(callback, options); +fragmentRef.current.observeUsing(observer); +``` + +##### Parameters {/*observeusing-parameters*/} -- 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 `...`. +* `observer`: An [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) or [`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) instance. -- 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) +##### Returns {/*observeusing-returns*/} -- 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 `...`. +`observeUsing` does not return anything (`undefined`). + +--- + +#### `unobserveUsing(observer)` {/*unobserveusing*/} + +Stops observing the Fragment's DOM children with the specified observer. + +```js +fragmentRef.current.unobserveUsing(observer); +``` + +##### Parameters {/*unobserveusing-parameters*/} + +* `observer`: The same `IntersectionObserver` or `ResizeObserver` instance previously passed to [`observeUsing`](#observeusing). + +##### Returns {/*unobserveusing-returns*/} + +`unobserveUsing` does not return anything (`undefined`). + +--- + +#### `getClientRects()` {/*getclientrects*/} + +Returns a flat array of [`DOMRect`](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect) objects representing the bounding rectangles of all first-level DOM children. + +```js +const rects = fragmentRef.current.getClientRects(); +``` + +##### Returns {/*getclientrects-returns*/} + +An `Array` containing the bounding rectangles of all children. + +--- + +#### `getRootNode(options?)` {/*getrootnode*/} + +Returns the root node containing the Fragment's parent DOM node, matching the behavior of [`Node.getRootNode()`](https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode). + +```js +const root = fragmentRef.current.getRootNode(); +``` + +##### Parameters {/*getrootnode-parameters*/} + +* **optional** `options`: An object with a `composed` boolean property, matching the [DOM `getRootNode` API.](https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode#options) + +##### Returns {/*getrootnode-returns*/} + +A `Document`, `ShadowRoot`, or the `FragmentInstance` itself if there is no parent DOM node. + +--- + +#### `compareDocumentPosition(otherNode)` {/*comparedocumentposition*/} + +Compares the document position of the Fragment with another node, returning a bitmask matching the behavior of [`Node.compareDocumentPosition()`](https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition). + +```js +const position = fragmentRef.current.compareDocumentPosition(otherElement); +``` + +##### Parameters {/*comparedocumentposition-parameters*/} + +* `otherNode`: The DOM node to compare against. + +##### Returns {/*comparedocumentposition-returns*/} + +A bitmask of [position flags](https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition#return_value). Empty Fragments and Fragments with children rendered through a [portal](/reference/react-dom/createPortal) include `Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC` in the result. + +--- + +#### `scrollIntoView(alignToTop?)` {/*scrollintoview*/} + +Scrolls the Fragment's children into view. When `alignToTop` is `true` or omitted, scrolls to align the first child with the top of the scrollable ancestor. When `alignToTop` is `false`, scrolls to align the last child with the bottom. + +```js +fragmentRef.current.scrollIntoView(); +``` + +##### Parameters {/*scrollintoview-parameters*/} + +* **optional** `alignToTop`: A boolean. If `true` (the default), scrolls the first child to the top of the scrollable area. If `false`, scrolls the last child to the bottom. Unlike [`Element.scrollIntoView()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView), this method does not accept a `ScrollIntoViewOptions` object. + +##### Returns {/*scrollintoview-returns*/} + +`scrollIntoView` does not return anything (`undefined`). + +##### Caveats {/*scrollintoview-caveats*/} + +* `scrollIntoView` does not accept an options object. Passing one throws an error. Use the `alignToTop` boolean instead. +* When the Fragment has no children, `scrollIntoView` scrolls the nearest sibling or parent into view as a fallback. + +--- + +#### `FragmentInstance` Caveats {/*fragmentinstance-caveats*/} + +* Methods that target children (such as `addEventListener`, `observeUsing`, and `getClientRects`) operate on *first-level host (DOM) children* of the Fragment. They do not directly target children nested inside another DOM element. +* `focus` and `focusLast` search nested children depth-first for focusable elements, unlike event and observer methods which only target first-level host children. +* `observeUsing` does not work on text nodes. React logs a warning in development if the Fragment contains only text children. +* React does not apply event listeners added via `addEventListener` to hidden [``](/reference/react/Activity) trees. When an `Activity` boundary switches from hidden to visible, listeners are applied automatically. +* Each first-level DOM child of a Fragment with a `ref` gets a `reactFragments` property—a `Set` containing all Fragment instances that own the element. This enables [caching a shared observer](#caching-global-intersection-observer) across multiple Fragments. --- @@ -242,42 +465,305 @@ function PostBody({ body }) { --- -### Using Fragment refs for DOM interaction {/*using-fragment-refs-for-dom-interaction*/} +### Adding event listeners without a wrapper element {/*adding-event-listeners-without-wrapper*/} -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()`. +Fragment `ref`s let you add event listeners to a group of elements without adding a wrapper DOM node. Use a [ref callback](/reference/react-dom/components/common#ref-callback) to attach and clean up listeners: + + ```js -import { Fragment } from 'react'; +import { Fragment, useState, useRef, useEffect } from 'react'; function ClickableFragment({ children, onClick }) { + const fragmentRef = useRef(null); + useEffect(() => { + const fragmentInstance = fragmentRef.current; + if (fragmentInstance === null) { + return; + } + fragmentInstance.addEventListener('click', onClick); + return () => { + fragmentInstance.removeEventListener( + 'click', + onClick + ); + }; + }, [onClick]) return ( - { - fragmentInstance.addEventListener('click', handleClick); - return () => fragmentInstance.removeEventListener('click', handleClick); - }}> + {children} ); } + +export default function App() { + const [clicks, setClicks] = useState(0); + + return ( + <> +

Total clicks: {clicks}

+ { + setClicks(c => c + 1); + }}> + + + + + + ); +} +``` + +```json package.json hidden +{ + "dependencies": { + "react": "canary", + "react-dom": "canary", + "react-scripts": "latest" + } +} +``` + +
+ +The `addEventListener` call applies the listener to every first-level DOM child of the Fragment. When children are dynamically added or removed, the `FragmentInstance` automatically adds or removes the listener. + + + +#### Which children does a Fragment ref target? {/*which-children-does-a-fragment-ref-target*/} + +A `FragmentInstance` targets the **first-level host (DOM) children** of the Fragment. Consider this tree: + +```js + +
+ +
+
+
+ +
+ +``` + +`Wrapper` is a React component, so the `FragmentInstance` looks through it to find DOM nodes. The targeted children are `A`, `B`, and `D`. `C` is not targeted because it is nested inside the DOM element `B`. + +Methods like `addEventListener`, `observeUsing`, and `getClientRects` operate on these first-level DOM children. `focus` and `focusLast` are different—they search *all* nested children depth-first to find focusable elements. + + + +--- + +### Managing focus across a group of elements {/*managing-focus-across-elements*/} + +Fragment `ref`s provide `focus`, `focusLast`, and `blur` methods that operate across all DOM nodes within the Fragment: + + + +```js +import { Fragment, useRef } from 'react'; + +function FormFields({ children }) { + const fragmentRef = useRef(null); + + return ( + <> +
+ + + +
+ + {children} + + + ); +} + +// Even though the inputs are deeply nested, +// focus() searches depth-first to find them. +export default function App() { + return ( + +
+ Shipping + + +
+
+ ); +} +``` + +```css +.buttons { + display: flex; + gap: 8px; + margin-bottom: 10px; +} + +label { + display: inline-block; +} +``` + +```json package.json hidden +{ + "dependencies": { + "react": "canary", + "react-dom": "canary", + "react-scripts": "latest" + } +} ``` + +
+ +Calling `focus()` focuses the `street` input—even though it is nested inside a `
` and `