diff --git a/src/content/community/conferences.md b/src/content/community/conferences.md index 57b1432f3..2b9b045a0 100644 --- a/src/content/community/conferences.md +++ b/src/content/community/conferences.md @@ -24,6 +24,11 @@ September 10-11, 2026. In-person in Zurich, Switzerland [Website](https://conf.zurichjs.com?utm_campaign=ZurichJS_Conf&utm_source=referral&utm_content=reactjs_community_conferences) - [Twitter](https://x.com/zurichjs) - [LinkedIn](https://www.linkedin.com/company/zurichjs/) +### React Conf Japan 2027 {/*react-conf-japan-2027*/} +April 24, 2027. In-person in Tokyo, Japan + +[Website](https://reactconf.jp/) - [Twitter](https://x.com/reactconfjp) + ## Past Conferences {/*past-conferences*/} ### CityJS New Delhi 2026 {/*cityjs-newdelhi-2026*/} diff --git a/src/content/learn/react-compiler/installation.md b/src/content/learn/react-compiler/installation.md index 8f77af32b..77d108d84 100644 --- a/src/content/learn/react-compiler/installation.md +++ b/src/content/learn/react-compiler/installation.md @@ -64,9 +64,36 @@ module.exports = { ### Vite {/*vite*/} +<<<<<<< HEAD Vite를 사용하는 경우 `vite-plugin-react`에 플러그인을 추가할 수 있습니다. +======= +If you use Vite with version 6.0.0 or later of `@vitejs/plugin-react`, you can use the `reactCompilerPreset`: +>>>>>>> 8bb31acb86bf68fa33d97dd0f1b834dfa71e2b1a -```js {3,9} + +npm install -D @rolldown/plugin-babel + + +```js {3-4,9-11} +// vite.config.js +import { defineConfig } from 'vite'; +import react, { reactCompilerPreset } from '@vitejs/plugin-react'; +import babel from '@rolldown/plugin-babel'; + +export default defineConfig({ + plugins: [ + react(), + babel({ + presets: [reactCompilerPreset()] + }), + ], +}); +``` + + +In `@vitejs/plugin-react@6.0.0`, the inline Babel option was removed. If you're using an older version, you can use: + +```js // vite.config.js import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; @@ -81,26 +108,25 @@ export default defineConfig({ ], }); ``` + +<<<<<<< HEAD 또는 Vite용 별도의 Babel 플러그인을 선호하는 경우 +======= +Alternatively, you can use the Babel plugin directly with `@rolldown/plugin-babel`: +>>>>>>> 8bb31acb86bf68fa33d97dd0f1b834dfa71e2b1a - -npm install -D vite-plugin-babel - - -```js {2,11} +```js {3,9} // vite.config.js -import babel from 'vite-plugin-babel'; import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; +import babel from '@rolldown/plugin-babel'; export default defineConfig({ plugins: [ react(), babel({ - babelConfig: { - plugins: ['babel-plugin-react-compiler'], - }, + plugins: ['babel-plugin-react-compiler'], }), ], }); diff --git a/src/content/reference/react/Fragment.md b/src/content/reference/react/Fragment.md index 88aa3e9a1..cea9cb723 100644 --- a/src/content/reference/react/Fragment.md +++ b/src/content/reference/react/Fragment.md @@ -6,7 +6,11 @@ title: (<>...) ``는 `<>...` 문법으로 자주 사용되며, 래퍼 노드 없이 엘리먼트를 그룹화할 수 있게 해줍니다. +<<<<<<< HEAD Fragment는 `ref`를 받을 수도 있으며, 래퍼 엘리먼트를 추가하지 않고도 기본 DOM 노드와 상호작용할 수 있습니다. 아래 레퍼런스와 사용법을 참고하세요. +======= +Fragments can also accept refs, which enable interacting with underlying DOM nodes without adding wrapper elements. +>>>>>>> 8bb31acb86bf68fa33d97dd0f1b834dfa71e2b1a ```js <> @@ -30,6 +34,7 @@ title: (<>...) #### Props {/*props*/} - `key`**(선택사항)**: 명시적 ``로 선언된 `Fragment`에는 [`key`](/learn/rendering-lists#keeping-list-items-in-order-with-key)를 사용할 수 있습니다. +<<<<<<< HEAD - `ref`**(선택사항)**: ref 객체(예: [`useRef`](/reference/react/useRef)에서 반환된 것) 또는 [콜백 함수](/reference/react-dom/components/common#ref-callback)입니다. React는 `Fragment`로 감싼 DOM 노드와 상호작용하기 위한 메서드를 구현한 `FragmentInstance`를 ref 값으로 제공합니다. ### FragmentInstance {/*fragmentinstance*/} @@ -57,14 +62,269 @@ title: (<>...) **옵저버 메서드** - `observeUsing(observer)`: IntersectionObserver 또는 ResizeObserver로 Fragment의 DOM 자식을 관찰하기 시작합니다. - `unobserveUsing(observer)`: 지정된 옵저버로 Fragment의 DOM 자식 관찰을 중지합니다. +======= +- **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. +>>>>>>> 8bb31acb86bf68fa33d97dd0f1b834dfa71e2b1a #### 주의 사항 {/*caveats*/} +<<<<<<< HEAD - Fragment에 `key`를 사용하려면 `<>...` 구문을 사용할 수 없습니다. 명시적으로 `react`에서 `Fragment`를 불러오고Import `...`를 렌더링해야 합니다. - React는 `<>`에서 `[]`로 렌더링하거나 (또는 반대의 경우), 혹은 `<>` 에서 `` 렌더링하거나 (또는 반대의 경우) [State를 초기화](/learn/preserving-and-resetting-state)하지 않습니다. 이는 오직 한 단계 깊이Single Level Deep까지만 적용됩니다. 예를 들어 `<><>` 에서 ``로 렌더링하는 것은 State가 초기화됩니다. 정확한 의미는 [여기](https://gist.github.com/clemmy/b3ef00f9507909429d8aa0d3ee4f986b)서 확인할 수 있습니다. - Fragment에 `ref`를 전달하려면 `<>...` 문법을 사용할 수 없습니다. 명시적으로 `'react'`에서 `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 `...`. + +* 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 `...`. + +--- + +### `FragmentInstance` {/*fragmentinstance*/} + +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*/} + +* `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. + +##### Returns {/*observeusing-returns*/} + +`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. +>>>>>>> 8bb31acb86bf68fa33d97dd0f1b834dfa71e2b1a --- @@ -242,47 +502,324 @@ function PostBody({ body }) { --- +<<<<<<< HEAD ### Fragment ref를 사용한 DOM 상호작용 {/*using-fragment-refs-for-dom-interaction*/} Fragment ref를 사용하면 래퍼 엘리먼트를 추가하지 않고도 Fragment로 감싼 DOM 노드와 상호작용할 수 있습니다. 이벤트 처리, 가시성 추적, 포커스 관리, 그리고 `ReactDOM.findDOMNode()`와 같이 더 이상 사용되지 않는 패턴을 대체하는 데 유용합니다. +======= +### Adding event listeners without a wrapper element {/*adding-event-listeners-without-wrapper*/} + +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: + + +>>>>>>> 8bb31acb86bf68fa33d97dd0f1b834dfa71e2b1a ```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. + + + --- +<<<<<<< HEAD ### Fragment ref로 가시성 추적하기 {/*tracking-visibility-with-fragment-refs*/} Fragment ref는 가시성 추적과 교차 관찰에 유용합니다. 자식 컴포넌트가 ref를 노출하지 않아도 콘텐츠가 화면에 보이는 시점을 모니터링할 수 있습니다. +======= +### Managing focus across a group of elements {/*managing-focus-across-elements*/} -```js {19,21,31-34} -import { Fragment, useRef, useLayoutEffect } from 'react'; +Fragment `ref`s provide `focus`, `focusLast`, and `blur` methods that operate across all DOM nodes within the Fragment: +>>>>>>> 8bb31acb86bf68fa33d97dd0f1b834dfa71e2b1a + + + +```js +import { Fragment, useRef } from 'react'; -function VisibilityObserverFragment({ threshold = 0.5, onVisibilityChange, children }) { +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 `