[
  {
    "path": ".claude/agents/docs-reviewer.md",
    "content": "---\nname: docs-reviewer\ndescription: \"Lean docs reviewer that dispatches reviews docs for a particular skill.\"\nmodel: opus\ncolor: cyan\n---\n\nYou are a direct, critical, expert reviewer for React documentation. \n\nYour role is to use given skills to validate given doc pages for consistency, correctness, and adherence to established patterns.\n\nComplete this process:\n\n## Phase 1: Task Creation\n1. CRITICAL: Read the skill requested.\n2. Understand the skill's requirements.\n3. Create a task list to validate skills requirements.\n\n## Phase 2: Validate\n\n1. Read the docs files given.\n2. Review each file with the task list to verify.\n\n## Phase 3: Respond\n\nYou must respond with a checklist of the issues you identified, and line number.\n\nDO NOT respond with passed validations, ONLY respond with the problems. \n"
  },
  {
    "path": ".claude/settings.json",
    "content": "{\n  \"skills\": {\n    \"suggest\": [\n      {\n        \"pattern\": \"src/content/learn/**/*.md\",\n        \"skill\": \"docs-writer-learn\"\n      },\n      {\n        \"pattern\": \"src/content/reference/**/*.md\",\n        \"skill\": \"docs-writer-reference\"\n      }\n    ]\n  },\n  \"permissions\": {\n    \"allow\": [\n      \"Skill(docs-voice)\",\n      \"Skill(docs-components)\",\n      \"Skill(docs-sandpack)\",\n      \"Skill(docs-writer-learn)\",\n      \"Skill(docs-writer-reference)\",\n      \"Bash(yarn lint:*)\",\n      \"Bash(yarn lint-heading-ids:*)\",\n      \"Bash(yarn lint:fix:*)\",\n      \"Bash(yarn tsc:*)\",\n      \"Bash(yarn check-all:*)\",\n      \"Bash(yarn fix-headings:*)\",\n      \"Bash(yarn deadlinks:*)\",\n      \"Bash(yarn prettier:diff:*)\"\n    ]\n  }\n}\n"
  },
  {
    "path": ".claude/skills/docs-components/SKILL.md",
    "content": "---\nname: docs-components\ndescription: Comprehensive MDX component patterns (Note, Pitfall, DeepDive, Recipes, etc.) for all documentation types. Authoritative source for component usage, examples, and heading conventions.\n---\n\n# MDX Component Patterns\n\n## Quick Reference\n\n### Component Decision Tree\n\n| Need | Component |\n|------|-----------|\n| Helpful tip or terminology | `<Note>` |\n| Common mistake warning | `<Pitfall>` |\n| Advanced technical explanation | `<DeepDive>` |\n| Canary-only feature | `<Canary>` or `<CanaryBadge />` |\n| Server Components only | `<RSC>` |\n| Deprecated API | `<Deprecated>` |\n| Experimental/WIP | `<Wip>` |\n| Visual diagram | `<Diagram>` |\n| Multiple related examples | `<Recipes>` |\n| Interactive code | `<Sandpack>` (see `/docs-sandpack`) |\n| Console error display | `<ConsoleBlock>` |\n| End-of-page exercises | `<Challenges>` (Learn pages only) |\n\n### Heading Level Conventions\n\n| Component | Heading Level |\n|-----------|---------------|\n| DeepDive title | `####` (h4) |\n| Titled Pitfall | `#####` (h5) |\n| Titled Note | `####` (h4) |\n| Recipe items | `####` (h4) |\n| Challenge items | `####` (h4) |\n\n### Callout Spacing Rules\n\nCallout components (Note, Pitfall, DeepDive) require a **blank line after the opening tag** before content begins.\n\n**Never place consecutively:**\n- `<Pitfall>` followed by `<Pitfall>` - Combine into one with titled subsections, or separate with prose\n- `<Note>` followed by `<Note>` - Combine into one, or separate with prose\n\n**Allowed consecutive patterns:**\n- `<DeepDive>` followed by `<DeepDive>` - OK for multi-part explorations (see useMemo.md)\n- `<Pitfall>` followed by `<DeepDive>` - OK when DeepDive explains \"why\" behind the Pitfall\n\n**Separation content:** Prose paragraphs, code examples (Sandpack), or section headers.\n\n**Why:** Consecutive warnings create a \"wall of cautions\" that overwhelms readers and causes important warnings to be skimmed.\n\n**Incorrect:**\n```mdx\n<Pitfall>\nDon't do X.\n</Pitfall>\n\n<Pitfall>\nDon't do Y.\n</Pitfall>\n```\n\n**Correct - combined:**\n```mdx\n<Pitfall>\n\n##### Don't do X {/*pitfall-x*/}\nExplanation.\n\n##### Don't do Y {/*pitfall-y*/}\nExplanation.\n\n</Pitfall>\n```\n\n**Correct - separated:**\n```mdx\n<Pitfall>\nDon't do X.\n</Pitfall>\n\nThis leads to another common mistake:\n\n<Pitfall>\nDon't do Y.\n</Pitfall>\n```\n\n---\n\n## `<Note>`\n\nImportant clarifications, conventions, or tips. Less severe than Pitfall.\n\n### Simple Note\n\n```mdx\n<Note>\n\nThe optimization of caching return values is known as [_memoization_](https://en.wikipedia.org/wiki/Memoization).\n\n</Note>\n```\n\n### Note with Title\n\nUse `####` (h4) heading with an ID.\n\n```mdx\n<Note>\n\n#### There is no directive for Server Components. {/*no-directive*/}\n\nA common misunderstanding is that Server Components are denoted by `\"use server\"`, but there is no directive for Server Components. The `\"use server\"` directive is for Server Functions.\n\n</Note>\n```\n\n### Version-Specific Note\n\n```mdx\n<Note>\n\nStarting in React 19, you can render `<SomeContext>` as a provider.\n\nIn older versions of React, use `<SomeContext.Provider>`.\n\n</Note>\n```\n\n---\n\n## `<Pitfall>`\n\nCommon mistakes that cause bugs. Use for errors readers will likely make.\n\n### Simple Pitfall\n\n```mdx\n<Pitfall>\n\nWe recommend defining components as functions instead of classes. [See how to migrate.](#alternatives)\n\n</Pitfall>\n```\n\n### Titled Pitfall\n\nUse `#####` (h5) heading with an ID.\n\n```mdx\n<Pitfall>\n\n##### Calling different memoized functions will read from different caches. {/*pitfall-different-caches*/}\n\nTo access the same cache, components must call the same memoized function.\n\n</Pitfall>\n```\n\n### Pitfall with Wrong/Right Code\n\n```mdx\n<Pitfall>\n\n##### `useFormStatus` will not return status information for a `<form>` rendered in the same component. {/*pitfall-same-component*/}\n\n```js\nfunction Form() {\n  // 🔴 `pending` will never be true\n  const { pending } = useFormStatus();\n  return <form action={submit}></form>;\n}\n```\n\nInstead call `useFormStatus` from inside a component located inside `<form>`.\n\n</Pitfall>\n```\n\n---\n\n## `<DeepDive>`\n\nOptional deep technical content. **First child must be `####` heading with ID.**\n\n### Standard DeepDive\n\n```mdx\n<DeepDive>\n\n#### Is using an updater always preferred? {/*is-updater-preferred*/}\n\nYou might hear a recommendation to always write code like `setAge(a => a + 1)` if the state you're setting is calculated from the previous state. There's no harm in it, but it's also not always necessary.\n\nIn most cases, there is no difference between these two approaches. React always makes sure that for intentional user actions, like clicks, the `age` state variable would be updated before the next click.\n\n</DeepDive>\n```\n\n### Comparison DeepDive\n\nFor comparing related concepts:\n\n```mdx\n<DeepDive>\n\n#### When should I use `cache`, `memo`, or `useMemo`? {/*cache-memo-usememo*/}\n\nAll mentioned APIs offer memoization but differ in what they memoize, who can access the cache, and when their cache is invalidated.\n\n#### `useMemo` {/*deep-dive-usememo*/}\n\nIn general, you should use `useMemo` for caching expensive computations in Client Components across renders.\n\n#### `cache` {/*deep-dive-cache*/}\n\nIn general, you should use `cache` in Server Components to memoize work that can be shared across components.\n\n</DeepDive>\n```\n\n---\n\n## `<Recipes>`\n\nMultiple related examples showing variations. Each recipe needs `<Solution />`.\n\n```mdx\n<Recipes titleText=\"Basic useState examples\" titleId=\"examples-basic\">\n\n#### Counter (number) {/*counter-number*/}\n\nIn this example, the `count` state variable holds a number.\n\n<Sandpack>\n{/* code */}\n</Sandpack>\n\n<Solution />\n\n#### Text field (string) {/*text-field-string*/}\n\nIn this example, the `text` state variable holds a string.\n\n<Sandpack>\n{/* code */}\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n```\n\n**Common titleText/titleId combinations:**\n- \"Basic [hookName] examples\" / `examples-basic`\n- \"Examples of [concept]\" / `examples-[concept]`\n- \"The difference between [A] and [B]\" / `examples-[topic]`\n\n---\n\n## `<Challenges>`\n\nEnd-of-page exercises. **Learn pages only.** Each challenge needs problem + solution Sandpack.\n\n```mdx\n<Challenges>\n\n#### Fix the bug {/*fix-the-bug*/}\n\nProblem description...\n\n<Hint>\nOptional hint text.\n</Hint>\n\n<Sandpack>\n{/* problem code */}\n</Sandpack>\n\n<Solution>\n\nExplanation...\n\n<Sandpack>\n{/* solution code */}\n</Sandpack>\n\n</Solution>\n\n</Challenges>\n```\n\n**Guidelines:**\n- Only at end of standard Learn pages\n- No Challenges in chapter intros or tutorials\n- Each challenge has `####` heading with ID\n\n---\n\n## `<Deprecated>`\n\nFor deprecated APIs. Content should explain what to use instead.\n\n### Page-Level Deprecation\n\n```mdx\n<Deprecated>\n\nIn React 19, `forwardRef` is no longer necessary. Pass `ref` as a prop instead.\n\n`forwardRef` will be deprecated in a future release. Learn more [here](/blog/2024/04/25/react-19#ref-as-a-prop).\n\n</Deprecated>\n```\n\n### Method-Level Deprecation\n\n```mdx\n### `componentWillMount()` {/*componentwillmount*/}\n\n<Deprecated>\n\nThis API has been renamed from `componentWillMount` to [`UNSAFE_componentWillMount`.](#unsafe_componentwillmount)\n\nRun the [`rename-unsafe-lifecycles` codemod](codemod-link) to automatically update.\n\n</Deprecated>\n```\n\n---\n\n## `<RSC>`\n\nFor APIs that only work with React Server Components.\n\n### Basic RSC\n\n```mdx\n<RSC>\n\n`cache` is only for use with [React Server Components](/reference/rsc/server-components).\n\n</RSC>\n```\n\n### Extended RSC (for Server Functions)\n\n```mdx\n<RSC>\n\nServer Functions are for use in [React Server Components](/reference/rsc/server-components).\n\n**Note:** Until September 2024, we referred to all Server Functions as \"Server Actions\".\n\n</RSC>\n```\n\n---\n\n## `<Canary>` and `<CanaryBadge />`\n\nFor features only available in Canary releases.\n\n### Canary Wrapper (inline in Intro)\n\n```mdx\n<Intro>\n\n`<Fragment>` lets you group elements without a wrapper node.\n\n<Canary>Fragments can also accept refs, enabling interaction with underlying DOM nodes.</Canary>\n\n</Intro>\n```\n\n### CanaryBadge in Section Headings\n\n```mdx\n### <CanaryBadge /> FragmentInstance {/*fragmentinstance*/}\n```\n\n### CanaryBadge in Props Lists\n\n```mdx\n* <CanaryBadge /> **optional** `ref`: A ref object from `useRef` or callback function.\n```\n\n### CanaryBadge in Caveats\n\n```mdx\n* <CanaryBadge /> If you want to pass `ref` to a Fragment, you can't use the `<>...</>` syntax.\n```\n\n---\n\n## `<Diagram>`\n\nVisual explanations of module dependencies, render trees, or data flow.\n\n```mdx\n<Diagram name=\"use_client_module_dependency\" height={250} width={545} alt=\"A tree graph with the top node representing the module 'App.js'. 'App.js' has three children...\">\n`'use client'` segments the module dependency tree, marking `InspirationGenerator.js` and all dependencies as client-rendered.\n</Diagram>\n```\n\n**Attributes:**\n- `name`: Diagram identifier (used for image file)\n- `height`: Height in pixels\n- `width`: Width in pixels\n- `alt`: Accessible description of the diagram\n\n---\n\n## `<CodeStep>` (Use Sparingly)\n\nNumbered callouts in prose. Pairs with code block annotations.\n\n### Syntax\n\nIn code blocks:\n```mdx\n```js [[1, 4, \"age\"], [2, 4, \"setAge\"], [3, 4, \"42\"]]\nimport { useState } from 'react';\n\nfunction MyComponent() {\n  const [age, setAge] = useState(42);\n}\n```\n```\n\nFormat: `[[step_number, line_number, \"text_to_highlight\"], ...]`\n\nIn prose:\n```mdx\n1. The <CodeStep step={1}>current state</CodeStep> initially set to the <CodeStep step={3}>initial value</CodeStep>.\n2. The <CodeStep step={2}>`set` function</CodeStep> that lets you change it.\n```\n\n### Guidelines\n\n- Maximum 2-3 different colors per explanation\n- Don't highlight every keyword - only key concepts\n- Use for terms in prose, not entire code blocks\n- Maintain consistent usage within a section\n\n✅ **Good use** - highlighting key concepts:\n```mdx\nReact will compare the <CodeStep step={2}>dependencies</CodeStep> with the dependencies you passed...\n```\n\n🚫 **Avoid** - excessive highlighting:\n```mdx\nWhen an <CodeStep step={1}>Activity</CodeStep> boundary is <CodeStep step={2}>hidden</CodeStep> during its <CodeStep step={3}>initial</CodeStep> render...\n```\n\n---\n\n## `<ConsoleBlock>`\n\nDisplay console output (errors, warnings, logs).\n\n```mdx\n<ConsoleBlock level=\"error\">\nUncaught Error: Too many re-renders.\n</ConsoleBlock>\n```\n\n**Levels:** `error`, `warning`, `info`\n\n---\n\n## Component Usage by Page Type\n\n### Reference Pages\n\nFor component placement rules specific to Reference pages, invoke `/docs-writer-reference`.\n\nKey placement patterns:\n- `<RSC>` goes before `<Intro>` at top of page\n- `<Deprecated>` goes after `<Intro>` for page-level deprecation\n- `<Deprecated>` goes after method heading for method-level deprecation\n- `<Canary>` wrapper goes inline within `<Intro>`\n- `<CanaryBadge />` appears in headings, props lists, and caveats\n\n### Learn Pages\n\nFor Learn page structure and patterns, invoke `/docs-writer-learn`.\n\nKey usage patterns:\n- Challenges only at end of standard Learn pages\n- No Challenges in chapter intros or tutorials\n- DeepDive for optional advanced content\n- CodeStep should be used sparingly\n\n### Blog Pages\n\nFor Blog page structure and patterns, invoke `/docs-writer-blog`.\n\nKey usage patterns:\n- Generally avoid deep technical components\n- Note and Pitfall OK for clarifications\n- Prefer inline explanations over DeepDive\n\n---\n\n## Other Available Components\n\n**Version/Status:** `<Experimental>`, `<ExperimentalBadge />`, `<RSCBadge />`, `<NextMajor>`, `<Wip>`\n\n**Visuals:** `<DiagramGroup>`, `<Illustration>`, `<IllustrationBlock>`, `<CodeDiagram>`, `<FullWidth>`\n\n**Console:** `<ConsoleBlockMulti>`, `<ConsoleLogLine>`\n\n**Specialized:** `<TerminalBlock>`, `<BlogCard>`, `<TeamMember>`, `<YouTubeIframe>`, `<ErrorDecoder />`, `<LearnMore>`, `<Math>`, `<MathI>`, `<LanguageList>`\n\nSee existing docs for usage examples of these components.\n"
  },
  {
    "path": ".claude/skills/docs-sandpack/SKILL.md",
    "content": "---\nname: docs-sandpack\ndescription: Use when adding interactive code examples to React docs.\n---\n\n# Sandpack Patterns\n\n## Quick Start Template\n\nMost examples are single-file. Copy this and modify:\n\n```mdx\n<Sandpack>\n\n` ` `js\nimport { useState } from 'react';\n\nexport default function Example() {\n  const [value, setValue] = useState(0);\n\n  return (\n    <button onClick={() => setValue(value + 1)}>\n      Clicked {value} times\n    </button>\n  );\n}\n` ` `\n\n</Sandpack>\n```\n\n---\n\n## File Naming\n\n| Pattern | Usage |\n|---------|-------|\n| ` ```js ` | Main file (no prefix) |\n| ` ```js src/FileName.js ` | Supporting files |\n| ` ```js src/File.js active ` | Active file (reference pages) |\n| ` ```js src/data.js hidden ` | Hidden files |\n| ` ```css ` | CSS styles |\n| ` ```json package.json ` | External dependencies |\n\n**Critical:** Main file must have `export default`.\n\n## Line Highlighting\n\n```mdx\n```js {2-4}\nfunction Example() {\n  // Lines 2-4\n  // will be\n  // highlighted\n  return null;\n}\n```\n\n## Code References (numbered callouts)\n\n```mdx\n```js [[1, 4, \"age\"], [2, 4, \"setAge\"]]\n// Creates numbered markers pointing to \"age\" and \"setAge\" on line 4\n```\n\n## Expected Errors (intentionally broken examples)\n\n```mdx\n```js {expectedErrors: {'react-compiler': [7]}}\n// Line 7 shows as expected error\n```\n\n## Multi-File Example\n\n```mdx\n<Sandpack>\n\n```js src/App.js\nimport Gallery from './Gallery.js';\n\nexport default function App() {\n  return <Gallery />;\n}\n```\n\n```js src/Gallery.js\nexport default function Gallery() {\n  return <h1>Gallery</h1>;\n}\n```\n\n```css\nh1 { color: purple; }\n```\n\n</Sandpack>\n```\n\n## External Dependencies\n\n```mdx\n<Sandpack>\n\n```js\nimport { useImmer } from 'use-immer';\n// ...\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"use-immer\": \"0.5.1\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\"\n  }\n}\n```\n\n</Sandpack>\n```\n\n## Code Style in Sandpack (Required)\n\nSandpack examples are held to strict code style standards:\n\n1. **Function declarations** for components (not arrows)\n2. **`e`** for event parameters\n3. **Single quotes** in JSX\n4. **`const`** unless reassignment needed\n5. **Spaces in destructuring**: `({ props })` not `({props})`\n6. **Two-line createRoot**: separate declaration and render call\n7. **Multiline if statements**: always use braces\n\n### Don't Create Hydration Mismatches\n\nSandpack examples must produce the same output on server and client:\n\n```js\n// 🚫 This will cause hydration warnings\nexport default function App() {\n  const isClient = typeof window !== 'undefined';\n  return <div>{isClient ? 'Client' : 'Server'}</div>;\n}\n```\n\n### Use Ref for Non-Rendered State\n\n```js\n// 🚫 Don't trigger re-renders for non-visual state\nconst [mounted, setMounted] = useState(false);\nuseEffect(() => { setMounted(true); }, []);\n\n// ✅ Use ref instead\nconst mounted = useRef(false);\nuseEffect(() => { mounted.current = true; }, []);\n```\n\n## forwardRef and memo Patterns\n\n### forwardRef - Use Named Function\n```js\n// ✅ Named function for DevTools display name\nconst MyInput = forwardRef(function MyInput(props, ref) {\n  return <input {...props} ref={ref} />;\n});\n\n// 🚫 Anonymous loses name\nconst MyInput = forwardRef((props, ref) => { ... });\n```\n\n### memo - Use Named Function\n```js\n// ✅ Preserves component name\nconst Greeting = memo(function Greeting({ name }) {\n  return <h1>Hello, {name}</h1>;\n});\n```\n\n## Line Length\n\n- Prose: ~80 characters\n- Code: ~60-70 characters\n- Break long lines to avoid horizontal scrolling\n\n## Anti-Patterns\n\n| Pattern | Problem | Fix |\n|---------|---------|-----|\n| `const Comp = () => {}` | Not standard | `function Comp() {}` |\n| `onClick={(event) => ...}` | Conflicts with global | `onClick={(e) => ...}` |\n| `useState` for non-rendered values | Re-renders | Use `useRef` |\n| Reading `window` during render | Hydration mismatch | Check in useEffect |\n| Single-line if without braces | Harder to debug | Use multiline with braces |\n| Chained `createRoot().render()` | Less clear | Two statements |\n| `//...` without space | Inconsistent | `// ...` with space |\n| Tabs | Inconsistent | 2 spaces |\n| `ReactDOM.render` | Deprecated | Use `createRoot` |\n| Fake package names | Confusing | Use `'./your-storage-layer'` |\n| `PropsWithChildren` | Outdated | `children?: ReactNode` |\n| Missing `key` in lists | Warnings | Always include key |\n\n## Additional Code Quality Rules\n\n### Always Include Keys in Lists\n```js\n// ✅ Correct\n{items.map(item => <li key={item.id}>{item.name}</li>)}\n\n// 🚫 Wrong - missing key\n{items.map(item => <li>{item.name}</li>)}\n```\n\n### Use Realistic Import Paths\n```js\n// ✅ Correct - descriptive path\nimport { fetchData } from './your-data-layer';\n\n// 🚫 Wrong - looks like a real npm package\nimport { fetchData } from 'cool-data-lib';\n```\n\n### Console.log Labels\n```js\n// ✅ Correct - labeled for clarity\nconsole.log('User:', user);\nconsole.log('Component Stack:', errorInfo.componentStack);\n\n// 🚫 Wrong - unlabeled\nconsole.log(user);\n```\n\n### Keep Delays Reasonable\n```js\n// ✅ Correct - 1-1.5 seconds\nsetTimeout(() => setLoading(false), 1000);\n\n// 🚫 Wrong - too long, feels sluggish\nsetTimeout(() => setLoading(false), 3000);\n```\n\n## Updating Line Highlights\n\nWhen modifying code in examples with line highlights (`{2-4}`), **always update the highlight line numbers** to match the new code. Incorrect line numbers cause rendering crashes.\n\n## File Name Conventions\n\n- Capitalize file names for component files: `Gallery.js` not `gallery.js`\n- After initially explaining files are in `src/`, refer to files by name only: `Gallery.js` not `src/Gallery.js`\n\n## Naming Conventions in Code\n\n**Components:** PascalCase\n- `Profile`, `Avatar`, `TodoList`, `PackingList`\n\n**State variables:** Destructured pattern\n- `const [count, setCount] = useState(0)`\n- Booleans: `[isOnline, setIsOnline]`, `[isPacked, setIsPacked]`\n- Status strings: `'typing'`, `'submitting'`, `'success'`, `'error'`\n\n**Event handlers:**\n- `handleClick`, `handleSubmit`, `handleAddTask`\n\n**Props for callbacks:**\n- `onClick`, `onChange`, `onAddTask`, `onSelect`\n\n**Custom Hooks:**\n- `useOnlineStatus`, `useChatRoom`, `useFormInput`\n\n**Reducer actions:**\n- Past tense: `'added'`, `'changed'`, `'deleted'`\n- Snake_case compounds: `'changed_selection'`, `'sent_message'`\n\n**Updater functions:** Single letter\n- `setCount(n => n + 1)`\n\n### Pedagogical Code Markers\n\n**Wrong vs right code:**\n```js\n// 🔴 Avoid: redundant state and unnecessary Effect\n// ✅ Good: calculated during rendering\n```\n\n**Console.log for lifecycle teaching:**\n```js\nconsole.log('✅ Connecting...');\nconsole.log('❌ Disconnected.');\n```\n\n### Server/Client Labeling\n\n```js\n// Server Component\nasync function Notes() {\n  const notes = await db.notes.getAll();\n}\n\n// Client Component\n\"use client\"\nexport default function Expandable({children}) {\n  const [expanded, setExpanded] = useState(false);\n}\n```\n\n### Bundle Size Annotations\n\n```js\nimport marked from 'marked'; // 35.9K (11.2K gzipped)\nimport sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)\n```\n\n---\n\n## Sandpack Example Guidelines\n\n### Package.json Rules\n\n**Include package.json when:**\n- Using external npm packages (immer, remarkable, leaflet, toastify-js, etc.)\n- Demonstrating experimental/canary React features\n- Requiring specific React versions (`react: beta`, `react: 19.0.0-rc-*`)\n\n**Omit package.json when:**\n- Example uses only built-in React features\n- No external dependencies needed\n- Teaching basic hooks, state, or components\n\n**Always mark package.json as hidden:**\n```mdx\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"immer\": \"1.7.3\"\n  }\n}\n```\n```\n\n**Version conventions:**\n- Use `\"latest\"` for stable features\n- Use exact versions only when compatibility requires it\n- Include minimal dependencies (just what the example needs)\n\n### Hidden File Patterns\n\n**Always hide these file types:**\n\n| File Type | Reason |\n|-----------|--------|\n| `package.json` | Configuration not the teaching point |\n| `sandbox.config.json` | Sandbox setup is boilerplate |\n| `public/index.html` | HTML structure not the focus |\n| `src/data.js` | When it contains sample/mock data |\n| `src/api.js` | When showing API usage, not implementation |\n| `src/styles.css` | When styling is not the lesson |\n| `src/router.js` | Supporting infrastructure |\n| `src/actions.js` | Server action implementation details |\n\n**Rationale:**\n- Reduces cognitive load\n- Keeps focus on the primary concept\n- Creates cleaner, more focused examples\n\n**Example:**\n```mdx\n```js src/data.js hidden\nexport const items = [\n  { id: 1, name: 'Item 1' },\n  { id: 2, name: 'Item 2' },\n];\n```\n```\n\n### Active File Patterns\n\n**Mark as active when:**\n- File contains the primary teaching concept\n- Learner should focus on this code first\n- Component demonstrates the hook/pattern being taught\n\n**Effect of the `active` marker:**\n- Sets initial editor tab focus when Sandpack loads\n- Signals \"this is what you should study\"\n- Works with hidden files to create focused examples\n\n**Most common active file:** `src/index.js` or `src/App.js`\n\n**Example:**\n```mdx\n```js src/App.js active\n// This file will be focused when example loads\nexport default function App() {\n  // ...\n}\n```\n```\n\n### File Structure Guidelines\n\n| Scenario | Structure | Reason |\n|----------|-----------|--------|\n| Basic hook usage | Single file | Simple, focused |\n| Teaching imports | 2-3 files | Shows modularity |\n| Context patterns | 4-5 files | Realistic structure |\n| Complex state | 3+ files | Separation of concerns |\n\n**Single File Examples (70% of cases):**\n- Use for simple concepts\n- 50-200 lines typical\n- Best for: Counter, text inputs, basic hooks\n\n**Multi-File Examples (30% of cases):**\n- Use when teaching modularity/imports\n- Use for context patterns (4-5 files)\n- Use when component is reused\n\n**File Naming:**\n- Main component: `App.js` (capitalized)\n- Component files: `Gallery.js`, `Button.js` (capitalized)\n- Data files: `data.js` (lowercase)\n- Utility files: `utils.js` (lowercase)\n- Context files: `TasksContext.js` (named after what they provide)\n\n### Code Size Limits\n\n- Single file: **<200 lines**\n- Multi-file total: **150-300 lines**\n- Main component: **100-150 lines**\n- Supporting files: **20-40 lines each**\n\n### CSS Guidelines\n\n**Always:**\n- Include minimal CSS for demo interactivity\n- Use semantic class names (`.panel`, `.button-primary`, `.panel-dark`)\n- Support light/dark themes when showing UI concepts\n- Keep CSS visible (never hidden)\n\n**Size Guidelines:**\n- Minimal (5-10 lines): Basic button styling, spacing\n- Medium (15-30 lines): Panel styling, form layouts\n- Complex (40+ lines): Only for layout-focused examples\n"
  },
  {
    "path": ".claude/skills/docs-voice/SKILL.md",
    "content": "---\nname: docs-voice\ndescription: Use when writing any React documentation. Provides voice, tone, and style rules for all doc types.\n---\n\n# React Docs Voice & Style\n\n## Universal Rules\n\n- **Capitalize React terms** when referring to the React concept in headings or as standalone concepts:\n  - Core: Hook, Effect, State, Context, Ref, Component, Fragment\n  - Concurrent: Transition, Action, Suspense\n  - Server: Server Component, Client Component, Server Function, Server Action\n  - Patterns: Error Boundary\n  - Canary: Activity, View Transition, Transition Type\n  - **In prose:** Use lowercase when paired with descriptors: \"state variable\", \"state updates\", \"event handler\". Capitalize when the concept stands alone or in headings: \"State is isolated and private\"\n  - General usage stays lowercase: \"the page transitions\", \"takes an action\"\n- **Product names:** ESLint, TypeScript, JavaScript, Next.js (not lowercase)\n- **Bold** for key concepts: **state variable**, **event handler**\n- **Italics** for new terms being defined: *event handlers*\n- **Inline code** for APIs: `useState`, `startTransition`, `<Suspense>`\n- **Avoid:** \"simple\", \"easy\", \"just\", time estimates\n- Frame differences as \"capabilities\" not \"advantages/disadvantages\"\n- Avoid passive voice and jargon\n\n## Tone by Page Type\n\n| Type | Tone | Example |\n|------|------|---------|\n| Learn | Conversational | \"Here's what that looks like...\", \"You might be wondering...\" |\n| Reference | Technical | \"Call `useState` at the top level...\", \"This Hook returns...\" |\n| Blog | Accurate | Focus on facts, not marketing |\n\n**Note:** Pitfall and DeepDive components can use slightly more conversational phrasing (\"You might wonder...\", \"It might be tempting...\") even in Reference pages, since they're explanatory asides.\n\n## Avoiding Jargon\n\n**Pattern:** Explain behavior first, then name it.\n\n✅ \"React waits until all code in event handlers runs before processing state updates. This is called *batching*.\"\n\n❌ \"React uses batching to process state updates atomically.\"\n\n**Terms to avoid or explain:**\n| Jargon | Plain Language |\n|--------|----------------|\n| atomic | all-or-nothing, batched together |\n| idempotent | same inputs, same output |\n| deterministic | predictable, same result every time |\n| memoize | remember the result, skip recalculating |\n| referentially transparent | (avoid - describe the behavior) |\n| invariant | rule that must always be true |\n| reify | (avoid - describe what's being created) |\n\n**Allowed technical terms in Reference pages:**\n- \"stale closures\" - standard JS/React term, can be used in Caveats\n- \"stable identity\" - React term for consistent object references across renders\n- \"reactive\" - React term for values that trigger re-renders when changed\n- These don't need explanation in Reference pages (readers are expected to know them)\n\n**Use established analogies sparingly—once when introducing a concept, not repeatedly:**\n\n| Concept | Analogy |\n|---------|---------|\n| Components/React | Kitchen (components as cooks, React as waiter) |\n| Render phases | Restaurant ordering (trigger/render/commit) |\n| State batching | Waiter collecting full order before going to kitchen |\n| State behavior | Snapshot/photograph in time |\n| State storage | React storing state \"on a shelf\" |\n| State purpose | Component's memory |\n| Pure functions | Recipes (same ingredients → same dish) |\n| Pure functions | Math formulas (y = 2x) |\n| Props | Adjustable \"knobs\" |\n| Children prop | \"Hole\" to be filled by parent |\n| Keys | File names in a folder |\n| Curly braces in JSX | \"Window into JavaScript\" |\n| Declarative UI | Taxi driver (destination, not turn-by-turn) |\n| Imperative UI | Turn-by-turn navigation |\n| State structure | Database normalization |\n| Refs | \"Secret pocket\" React doesn't track |\n| Effects/Refs | \"Escape hatch\" from React |\n| Context | CSS inheritance / \"Teleportation\" |\n| Custom Hooks | Design system |\n\n## Common Prose Patterns\n\n**Wrong vs Right code:**\n```mdx\n\\`\\`\\`js\n// 🚩 Don't mutate state:\nobj.x = 10;\n\\`\\`\\`\n\n\\`\\`\\`js\n// ✅ Replace with new object:\nsetObj({ ...obj, x: 10 });\n\\`\\`\\`\n```\n\n**Table comparisons:**\n```mdx\n| passing a function | calling a function |\n| `onClick={handleClick}` | `onClick={handleClick()}` |\n```\n\n**Linking:**\n```mdx\n[Read about state](/learn/state-a-components-memory)\n[See `useState` reference](/reference/react/useState)\n```\n\n## Code Style\n\n- Prefer JSX over createElement\n- Use const/let, never var\n- Prefer named function declarations for top-level functions\n- Arrow functions for callbacks that need `this` preservation\n\n## Version Documentation\n\nWhen APIs change between versions:\n\n```mdx\nStarting in React 19, render `<Context>` as a provider:\n\\`\\`\\`js\n<SomeContext value={value}>{children}</SomeContext>\n\\`\\`\\`\n\nIn older versions:\n\\`\\`\\`js\n<SomeContext.Provider value={value}>{children}</SomeContext.Provider>\n\\`\\`\\`\n```\n\nPatterns:\n- \"Starting in React 19...\" for new APIs\n- \"In older versions of React...\" for legacy patterns\n"
  },
  {
    "path": ".claude/skills/docs-writer-blog/SKILL.md",
    "content": "---\nname: docs-writer-blog\ndescription: Use when writing or editing files in src/content/blog/. Provides blog post structure and conventions.\n---\n\n# Blog Post Writer\n\n## Persona\n\n**Voice:** Official React team voice\n**Tone:** Accurate, professional, forward-looking\n\n## Voice & Style\n\nFor tone, capitalization, jargon, and prose patterns, invoke `/docs-voice`.\n\n---\n\n## Frontmatter Schema\n\nAll blog posts use this YAML frontmatter structure:\n\n```yaml\n---\ntitle: \"Title in Quotes\"\nauthor: Author Name(s)\ndate: YYYY/MM/DD\ndescription: One or two sentence summary.\n---\n```\n\n### Field Details\n\n| Field | Format | Example |\n|-------|--------|---------|\n| `title` | Quoted string | `\"React v19\"`, `\"React Conf 2024 Recap\"` |\n| `author` | Unquoted, comma + \"and\" for multiple | `The React Team`, `Dan Abramov and Lauren Tan` |\n| `date` | `YYYY/MM/DD` with forward slashes | `2024/12/05` |\n| `description` | 1-2 sentences, often mirrors intro | Summarizes announcement or content |\n\n### Title Patterns by Post Type\n\n| Type | Pattern | Example |\n|------|---------|---------|\n| Release | `\"React vX.Y\"` or `\"React X.Y\"` | `\"React v19\"` |\n| Upgrade | `\"React [VERSION] Upgrade Guide\"` | `\"How to Upgrade to React 18\"` |\n| Labs | `\"React Labs: [Topic] – [Month Year]\"` | `\"React Labs: What We've Been Working On – February 2024\"` |\n| Conf | `\"React Conf [YEAR] Recap\"` | `\"React Conf 2024 Recap\"` |\n| Feature | `\"Introducing [Feature]\"` or descriptive | `\"Introducing react.dev\"` |\n| Security | `\"[Severity] Security Vulnerability in [Component]\"` | `\"Critical Security Vulnerability in React Server Components\"` |\n\n---\n\n## Author Byline\n\nImmediately after frontmatter, add a byline:\n\n```markdown\n---\n\nMonth DD, YYYY by [Author Name](social-link)\n\n---\n```\n\n### Conventions\n\n- Full date spelled out: `December 05, 2024`\n- Team posts link to `/community/team`: `[The React Team](/community/team)`\n- Individual authors link to Twitter/X or Bluesky\n- Multiple authors: Oxford comma before \"and\"\n- Followed by horizontal rule `---`\n\n**Examples:**\n\n```markdown\nDecember 05, 2024 by [The React Team](/community/team)\n\n---\n```\n\n```markdown\nMay 3, 2023 by [Dan Abramov](https://bsky.app/profile/danabra.mov), [Sophie Alpert](https://twitter.com/sophiebits), and [Andrew Clark](https://twitter.com/acdlite)\n\n---\n```\n\n---\n\n## Universal Post Structure\n\nAll blog posts follow this structure:\n\n1. **Frontmatter** (YAML)\n2. **Author byline** with date\n3. **Horizontal rule** (`---`)\n4. **`<Intro>` component** (1-3 sentences)\n5. **Horizontal rule** (`---`) (optional)\n6. **Main content sections** (H2 with IDs)\n7. **Closing section** (Changelog, Thanks, etc.)\n\n---\n\n## Post Type Templates\n\n### Major Release Announcement\n\n```markdown\n---\ntitle: \"React vX.Y\"\nauthor: The React Team\ndate: YYYY/MM/DD\ndescription: React X.Y is now available on npm! In this post, we'll give an overview of the new features.\n---\n\nMonth DD, YYYY by [The React Team](/community/team)\n\n---\n\n<Intro>\n\nReact vX.Y is now available on npm!\n\n</Intro>\n\nIn our [Upgrade Guide](/blog/YYYY/MM/DD/react-xy-upgrade-guide), we shared step-by-step instructions for upgrading. In this post, we'll give an overview of what's new.\n\n- [What's new in React X.Y](#whats-new)\n- [Improvements](#improvements)\n- [How to upgrade](#how-to-upgrade)\n\n---\n\n## What's new in React X.Y {/*whats-new*/}\n\n### Feature Name {/*feature-name*/}\n\n[Problem this solves. Before/after code examples.]\n\nFor more information, see the docs for [`Feature`](/reference/react/Feature).\n\n---\n\n## Improvements in React X.Y {/*improvements*/}\n\n### Improvement Name {/*improvement-name*/}\n\n[Description of improvement.]\n\n---\n\n## How to upgrade {/*how-to-upgrade*/}\n\nSee [How to Upgrade to React X.Y](/blog/YYYY/MM/DD/react-xy-upgrade-guide) for step-by-step instructions.\n\n---\n\n## Changelog {/*changelog*/}\n\n### React {/*react*/}\n\n* Add `useNewHook` for [purpose]. ([#12345](https://github.com/facebook/react/pull/12345) by [@contributor](https://github.com/contributor))\n\n---\n\n_Thanks to [Name](url) for reviewing this post._\n```\n\n### Upgrade Guide\n\n```markdown\n---\ntitle: \"React [VERSION] Upgrade Guide\"\nauthor: Author Name\ndate: YYYY/MM/DD\ndescription: Step-by-step instructions for upgrading to React [VERSION].\n---\n\nMonth DD, YYYY by [Author Name](social-url)\n\n---\n\n<Intro>\n\n[Summary of upgrade and what this guide covers.]\n\n</Intro>\n\n<Note>\n\n#### Stepping stone version {/*stepping-stone*/}\n\n[If applicable, describe intermediate upgrade steps.]\n\n</Note>\n\nIn this post, we will guide you through the steps for upgrading:\n\n- [Installing](#installing)\n- [Codemods](#codemods)\n- [Breaking changes](#breaking-changes)\n- [New deprecations](#new-deprecations)\n\n---\n\n## Installing {/*installing*/}\n\n```bash\nnpm install --save-exact react@^X.Y.Z react-dom@^X.Y.Z\n```\n\n## Codemods {/*codemods*/}\n\n<Note>\n\n#### Run all React [VERSION] codemods {/*run-all-codemods*/}\n\n```bash\nnpx codemod@latest react/[VERSION]/migration-recipe\n```\n\n</Note>\n\n## Breaking changes {/*breaking-changes*/}\n\n### Removed: `apiName` {/*removed-api-name*/}\n\n`apiName` was deprecated in [Month YYYY (vX.X.X)](link).\n\n```js\n// Before\n[old code]\n\n// After\n[new code]\n```\n\n<Note>\n\nCodemod [description]:\n\n```bash\nnpx codemod@latest react/[VERSION]/codemod-name\n```\n\n</Note>\n\n## New deprecations {/*new-deprecations*/}\n\n### Deprecated: `apiName` {/*deprecated-api-name*/}\n\n[Explanation and migration path.]\n\n---\n\nThanks to [Contributor](link) for reviewing this post.\n```\n\n### React Labs Research Update\n\n```markdown\n---\ntitle: \"React Labs: What We've Been Working On – [Month Year]\"\nauthor: Author1, Author2, and Author3\ndate: YYYY/MM/DD\ndescription: In React Labs posts, we write about projects in active research and development.\n---\n\nMonth DD, YYYY by [Author1](url), [Author2](url), and [Author3](url)\n\n---\n\n<Intro>\n\nIn React Labs posts, we write about projects in active research and development. We've made significant progress since our [last update](/blog/previous-labs-post), and we'd like to share our progress.\n\n</Intro>\n\n[Optional: Roadmap disclaimer about timelines]\n\n---\n\n## Feature Name {/*feature-name*/}\n\n<Note>\n\n`<FeatureName />` is now available in React's Canary channel.\n\n</Note>\n\n[Description of feature, motivation, current status.]\n\n### Subsection {/*subsection*/}\n\n[Details, examples, use cases.]\n\n---\n\n## Research Area {/*research-area*/}\n\n[Problem space description. Status communication.]\n\nThis research is still early. We'll share more when we're further along.\n\n---\n\n_Thanks to [Reviewer](url) for reviewing this post._\n\nThanks for reading, and see you in the next update!\n```\n\n### React Conf Recap\n\n```markdown\n---\ntitle: \"React Conf [YEAR] Recap\"\nauthor: Author1 and Author2\ndate: YYYY/MM/DD\ndescription: Last week we hosted React Conf [YEAR]. In this post, we'll summarize the talks and announcements.\n---\n\nMonth DD, YYYY by [Author1](url) and [Author2](url)\n\n---\n\n<Intro>\n\nLast week we hosted React Conf [YEAR] [where we announced [key announcements]].\n\n</Intro>\n\n---\n\nThe entire [day 1](youtube-url) and [day 2](youtube-url) streams are available online.\n\n## Day 1 {/*day-1*/}\n\n_[Watch the full day 1 stream here.](youtube-url)_\n\n[Description of day 1 opening and keynote highlights.]\n\nWatch the full day 1 keynote here:\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/VIDEO_ID\" />\n\n## Day 2 {/*day-2*/}\n\n_[Watch the full day 2 stream here.](youtube-url)_\n\n[Day 2 summary.]\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/VIDEO_ID\" />\n\n## Q&A {/*q-and-a*/}\n\n* [Q&A Title](youtube-url) hosted by [Host](url)\n\n## And more... {/*and-more*/}\n\nWe also heard talks including:\n* [Talk Title](youtube-url) by [Speaker](url)\n\n## Thank you {/*thank-you*/}\n\nThank you to all the staff, speakers, and participants who made React Conf [YEAR] possible.\n\nSee you next time!\n```\n\n### Feature/Tool Announcement\n\n```markdown\n---\ntitle: \"Introducing [Feature Name]\"\nauthor: Author Name\ndate: YYYY/MM/DD\ndescription: Today we are announcing [feature]. In this post, we'll explain [what this post covers].\n---\n\nMonth DD, YYYY by [Author Name](url)\n\n---\n\n<Intro>\n\nToday we are [excited/thrilled] to announce [feature]. [What this means for users.]\n\n</Intro>\n\n---\n\n## tl;dr {/*tldr*/}\n\n* Key announcement point with [relevant link](/path).\n* What users can do now.\n* Availability or adoption information.\n\n## What is [Feature]? {/*what-is-feature*/}\n\n[Explanation of the feature/tool.]\n\n## Why we built this {/*why-we-built-this*/}\n\n[Motivation, history, problem being solved.]\n\n## Getting started {/*getting-started*/}\n\nTo install [feature]:\n\n<TerminalBlock>\nnpm install package-name\n</TerminalBlock>\n\n[You can find more documentation here.](/path/to/docs)\n\n## What's next {/*whats-next*/}\n\n[Future plans and next steps.]\n\n## Thank you {/*thank-you*/}\n\n[Acknowledgments to contributors.]\n\n---\n\nThanks to [Reviewer](url) for reviewing this post.\n```\n\n### Security Announcement\n\n```markdown\n---\ntitle: \"[Severity] Security Vulnerability in [Component]\"\nauthor: The React Team\ndate: YYYY/MM/DD\ndescription: Brief summary of the vulnerability. A fix has been published. We recommend upgrading immediately.\n\n---\n\nMonth DD, YYYY by [The React Team](/community/team)\n\n---\n\n<Intro>\n\n[One or two sentences summarizing the vulnerability.]\n\nWe recommend upgrading immediately.\n\n</Intro>\n\n---\n\nOn [date], [researcher] reported a security vulnerability that allows [description].\n\nThis vulnerability was disclosed as [CVE-YYYY-NNNNN](https://www.cve.org/CVERecord?id=CVE-YYYY-NNNNN) and is rated CVSS [score].\n\nThe vulnerability is present in versions [list] of:\n\n* [package-name](https://www.npmjs.com/package/package-name)\n\n## Immediate Action Required {/*immediate-action-required*/}\n\nA fix was introduced in versions [linked versions]. Upgrade immediately.\n\n### Affected frameworks {/*affected-frameworks*/}\n\n[List of affected frameworks with npm links.]\n\n### Vulnerability overview {/*vulnerability-overview*/}\n\n[Technical explanation of the vulnerability.]\n\n## Update Instructions {/*update-instructions*/}\n\n### Framework Name {/*update-framework-name*/}\n\n```bash\nnpm install package@version\n```\n\n## Timeline {/*timeline*/}\n\n* **November 29th**: [Researcher] reported the vulnerability.\n* **December 1st**: Fix was created and validated.\n* **December 3rd**: Fix published and CVE disclosed.\n\n## Attribution {/*attribution*/}\n\nThank you to [Researcher Name](url) for discovering and reporting this vulnerability.\n```\n\n---\n\n## Heading Conventions\n\n### ID Syntax\n\nAll headings require IDs using CSS comment syntax:\n\n```markdown\n## Heading Text {/*heading-id*/}\n```\n\n### ID Rules\n\n- Lowercase\n- Kebab-case (hyphens for spaces)\n- Remove special characters (apostrophes, colons, backticks)\n- Concise but descriptive\n\n### Heading Patterns\n\n| Context | Example |\n|---------|---------|\n| Feature section | `## New Feature: Automatic Batching {/*new-feature-automatic-batching*/}` |\n| New hook | `### New hook: \\`useActionState\\` {/*new-hook-useactionstate*/}` |\n| API in backticks | `### \\`<Activity />\\` {/*activity*/}` |\n| Removed API | `#### Removed: \\`propTypes\\` {/*removed-proptypes*/}` |\n| tl;dr section | `## tl;dr {/*tldr*/}` |\n\n---\n\n## Component Usage Guide\n\n### Blog-Appropriate Components\n\n| Component | Usage in Blog |\n|-----------|---------------|\n| `<Intro>` | **Required** - Opening summary after byline |\n| `<Note>` | Callouts, caveats, important clarifications |\n| `<Pitfall>` | Warnings about common mistakes |\n| `<DeepDive>` | Optional technical deep dives (use sparingly) |\n| `<TerminalBlock>` | CLI/installation commands |\n| `<ConsoleBlock>` | Console error/warning output |\n| `<ConsoleBlockMulti>` | Multi-line console output |\n| `<YouTubeIframe>` | Conference video embeds |\n| `<Diagram>` | Visual explanations |\n| `<InlineToc />` | Auto-generated table of contents |\n\n### `<Intro>` Pattern\n\nAlways wrap opening paragraph:\n\n```markdown\n<Intro>\n\nReact 19 is now available on npm!\n\n</Intro>\n```\n\n### `<Note>` Patterns\n\n**Simple note:**\n```markdown\n<Note>\n\nFor React Native users, React 18 ships with the New Architecture.\n\n</Note>\n```\n\n**Titled note (H4 inside):**\n```markdown\n<Note>\n\n#### React 18.3 has also been published {/*react-18-3*/}\n\nTo help with the upgrade, we've published `react@18.3`...\n\n</Note>\n```\n\n### `<TerminalBlock>` Pattern\n\n```markdown\n<TerminalBlock>\nnpm install react@latest react-dom@latest\n</TerminalBlock>\n```\n\n### `<YouTubeIframe>` Pattern\n\n```markdown\n<YouTubeIframe src=\"https://www.youtube.com/embed/VIDEO_ID\" />\n```\n\n---\n\n## Link Patterns\n\n### Internal Links\n\n| Type | Pattern | Example |\n|------|---------|---------|\n| Blog post | `/blog/YYYY/MM/DD/slug` | `/blog/2024/12/05/react-19` |\n| API reference | `/reference/react/HookName` | `/reference/react/useState` |\n| Learn section | `/learn/topic-name` | `/learn/react-compiler` |\n| Community | `/community/team` | `/community/team` |\n\n### External Links\n\n| Type | Pattern |\n|------|---------|\n| GitHub PR | `[#12345](https://github.com/facebook/react/pull/12345)` |\n| GitHub user | `[@username](https://github.com/username)` |\n| Twitter/X | `[@username](https://x.com/username)` |\n| Bluesky | `[Name](https://bsky.app/profile/handle)` |\n| CVE | `[CVE-YYYY-NNNNN](https://www.cve.org/CVERecord?id=CVE-YYYY-NNNNN)` |\n| npm package | `[package](https://www.npmjs.com/package/package)` |\n\n### \"See docs\" Pattern\n\n```markdown\nFor more information, see the docs for [`useActionState`](/reference/react/useActionState).\n```\n\n---\n\n## Changelog Format\n\n### Bullet Pattern\n\n```markdown\n* Add `useTransition` for concurrent rendering. ([#10426](https://github.com/facebook/react/pull/10426) by [@acdlite](https://github.com/acdlite))\n* Fix `useReducer` observing incorrect props. ([#22445](https://github.com/facebook/react/pull/22445) by [@josephsavona](https://github.com/josephsavona))\n```\n\n**Structure:** `Verb` + backticked API + description + `([#PR](url) by [@user](url))`\n\n**Verbs:** Add, Fix, Remove, Make, Improve, Allow, Deprecate\n\n### Section Organization\n\n```markdown\n## Changelog {/*changelog*/}\n\n### React {/*react*/}\n\n* [changes]\n\n### React DOM {/*react-dom*/}\n\n* [changes]\n```\n\n---\n\n## Acknowledgments Format\n\n### Post-closing Thanks\n\n```markdown\n---\n\nThanks to [Name](url), [Name](url), and [Name](url) for reviewing this post.\n```\n\nOr italicized:\n\n```markdown\n_Thanks to [Name](url) for reviewing this post._\n```\n\n### Update Notes\n\nFor post-publication updates:\n\n```markdown\n<Note>\n\n[Updated content]\n\n-----\n\n_Updated January 26, 2026._\n\n</Note>\n```\n\n---\n\n## Tone & Length by Post Type\n\n| Type | Tone | Length | Key Elements |\n|------|------|--------|--------------|\n| Release | Celebratory, informative | Medium-long | Feature overview, upgrade link, changelog |\n| Upgrade | Instructional, precise | Long | Step-by-step, codemods, breaking changes |\n| Labs | Transparent, exploratory | Medium | Status updates, roadmap disclaimers |\n| Conf | Enthusiastic, community-focused | Medium | YouTube embeds, speaker credits |\n| Feature | Excited, explanatory | Medium | tl;dr, \"why\", getting started |\n| Security | Urgent, factual | Short-medium | Immediate action, timeline, CVE |\n\n---\n\n## Do's and Don'ts\n\n**Do:**\n- Focus on facts over marketing\n- Say \"upcoming\" explicitly for unreleased features\n- Include FAQ sections for major announcements\n- Credit contributors and link to GitHub\n- Use \"we\" voice for team posts\n- Link to upgrade guides from release posts\n- Include table of contents for long posts\n- End with acknowledgments\n\n**Don't:**\n- Promise features not yet available\n- Rewrite history (add update notes instead)\n- Break existing URLs\n- Use hyperbolic language (\"revolutionary\", \"game-changing\")\n- Skip the `<Intro>` component\n- Forget heading IDs\n- Use heavy component nesting in blogs\n- Make time estimates or predictions\n\n---\n\n## Updating Old Posts\n\n- Never break existing URLs; add redirects when URLs change\n- Don't rewrite history; add update notes instead:\n  ```markdown\n  <Note>\n\n  [Updated information]\n\n  -----\n\n  _Updated Month Year._\n\n  </Note>\n  ```\n\n---\n\n## Critical Rules\n\n1. **Heading IDs required:** `## Title {/*title-id*/}`\n2. **`<Intro>` required:** Every post starts with `<Intro>` component\n3. **Byline required:** Date + linked author(s) after frontmatter\n4. **Date format:** Frontmatter uses `YYYY/MM/DD`, byline uses `Month DD, YYYY`\n5. **Link to docs:** New APIs must link to reference documentation\n6. **Security posts:** Always include \"We recommend upgrading immediately\"\n\n---\n\n## Components Reference\n\nFor complete MDX component patterns, invoke `/docs-components`.\n\nBlog posts commonly use: `<Intro>`, `<Note>`, `<Pitfall>`, `<TerminalBlock>`, `<ConsoleBlock>`, `<YouTubeIframe>`, `<DeepDive>`, `<Diagram>`.\n\nPrefer inline explanations over heavy component usage.\n"
  },
  {
    "path": ".claude/skills/docs-writer-learn/SKILL.md",
    "content": "---\nname: docs-writer-learn\ndescription: Use when writing or editing files in src/content/learn/. Provides Learn page structure and tone.\n---\n\n# Learn Page Writer\n\n## Persona\n\n**Voice:** Patient teacher guiding a friend through concepts\n**Tone:** Conversational, warm, encouraging\n\n## Voice & Style\n\nFor tone, capitalization, jargon, and prose patterns, invoke `/docs-voice`.\n\n## Page Structure Variants\n\n### 1. Standard Learn Page (Most Common)\n\n```mdx\n---\ntitle: Page Title\n---\n\n<Intro>\n1-3 sentences introducing the concept. Use *italics* for new terms.\n</Intro>\n\n<YouWillLearn>\n\n* Learning outcome 1\n* Learning outcome 2\n* Learning outcome 3-5\n\n</YouWillLearn>\n\n## Section Name {/*section-id*/}\n\nContent with Sandpack examples, Pitfalls, Notes, DeepDives...\n\n## Another Section {/*another-section*/}\n\nMore content...\n\n<Recap>\n\n* Summary point 1\n* Summary point 2\n* Summary points 3-9\n\n</Recap>\n\n<Challenges>\n\n#### Challenge title {/*challenge-id*/}\n\nDescription...\n\n<Hint>\nOptional guidance (single paragraph)\n</Hint>\n\n<Sandpack>\n{/* Starting code */}\n</Sandpack>\n\n<Solution>\nExplanation...\n\n<Sandpack>\n{/* Fixed code */}\n</Sandpack>\n</Solution>\n\n</Challenges>\n```\n\n### 2. Chapter Introduction Page\n\nFor pages that introduce a chapter (like describing-the-ui.md, managing-state.md):\n\n```mdx\n<YouWillLearn isChapter={true}>\n\n* [Sub-page title](/learn/sub-page-name) to learn...\n* [Another page](/learn/another-page) to learn...\n\n</YouWillLearn>\n\n## Preview Section {/*section-id*/}\n\nPreview description with mini Sandpack example\n\n<LearnMore path=\"/learn/sub-page-name\">\n\nRead **[Page Title](/learn/sub-page-name)** to learn how to...\n\n</LearnMore>\n\n## What's next? {/*whats-next*/}\n\nHead over to [First Page](/learn/first-page) to start reading this chapter page by page!\n```\n\n**Important:** Chapter intro pages do NOT include `<Recap>` or `<Challenges>` sections.\n\n### 3. Tutorial Page\n\nFor step-by-step tutorials (like tutorial-tic-tac-toe.md):\n\n```mdx\n<Intro>\nBrief statement of what will be built\n</Intro>\n\n<Note>\nAlternative learning path offered\n</Note>\n\nTable of contents (prose listing of major sections)\n\n## Setup {/*setup*/}\n...\n\n## Main Content {/*main-content*/}\nProgressive code building with ### subsections\n\nNo YouWillLearn, Recap, or Challenges\n\nEnds with ordered list of \"extra credit\" improvements\n```\n\n### 4. Reference-Style Learn Page\n\nFor pages with heavy API documentation (like typescript.md):\n\n```mdx\n<YouWillLearn>\n\n* [Link to section](#section-anchor)\n* [Link to another section](#another-section)\n\n</YouWillLearn>\n\n## Sections with ### subsections\n\n## Further learning {/*further-learning*/}\n\nNo Recap or Challenges\n```\n\n## Heading ID Conventions\n\nAll headings require IDs in `{/*kebab-case*/}` format:\n\n```markdown\n## Section Title {/*section-title*/}\n### Subsection Title {/*subsection-title*/}\n#### DeepDive Title {/*deepdive-title*/}\n```\n\n**ID Generation Rules:**\n- Lowercase everything\n- Replace spaces with hyphens\n- Remove apostrophes, quotes\n- Remove or convert special chars (`:`, `?`, `!`, `.`, parentheses)\n\n**Examples:**\n- \"What's React?\" → `{/*whats-react*/}`\n- \"Step 1: Create the context\" → `{/*step-1-create-the-context*/}`\n- \"Conditional (ternary) operator (? :)\" → `{/*conditional-ternary-operator--*/}`\n\n## Teaching Patterns\n\n### Problem-First Teaching\n\nShow broken/problematic code BEFORE the solution:\n\n1. Present problematic approach with `// 🔴 Avoid:` comment\n2. Explain WHY it's wrong (don't just say it is)\n3. Show the solution with `// ✅ Good:` comment\n4. Invite experimentation\n\n### Progressive Complexity\n\nBuild understanding in layers:\n1. Show simplest working version\n2. Identify limitation or repetition\n3. Introduce solution incrementally\n4. Show complete solution\n5. Invite experimentation: \"Try changing...\"\n\n### Numbered Step Patterns\n\nFor multi-step processes:\n\n**As section headings:**\n```markdown\n### Step 1: Action to take {/*step-1-action*/}\n### Step 2: Next action {/*step-2-next-action*/}\n```\n\n**As inline lists:**\n```markdown\nTo implement this:\n1. **Declare** `inputRef` with the `useRef` Hook.\n2. **Pass it** as `<input ref={inputRef}>`.\n3. **Read** the input DOM node from `inputRef.current`.\n```\n\n### Interactive Invitations\n\nAfter Sandpack examples, encourage experimentation:\n- \"Try changing X to Y. See how...?\"\n- \"Try it in the sandbox above!\"\n- \"Click each button separately:\"\n- \"Have a guess!\"\n- \"Verify that...\"\n\n### Decision Questions\n\nHelp readers build intuition:\n> \"When you're not sure whether some code should be in an Effect or in an event handler, ask yourself *why* this code needs to run.\"\n\n## Component Placement Order\n\n1. `<Intro>` - First after frontmatter\n2. `<YouWillLearn>` - After Intro (standard/chapter pages)\n3. Body content with `<Note>`, `<Pitfall>`, `<DeepDive>` placed contextually\n4. `<Recap>` - Before Challenges (standard pages only)\n5. `<Challenges>` - End of page (standard pages only)\n\nFor component structure and syntax, invoke `/docs-components`.\n\n## Code Examples\n\nFor Sandpack file structure, naming conventions, code style, and pedagogical markers, invoke `/docs-sandpack`.\n\n## Cross-Referencing\n\n### When to Link\n\n**Link to /learn:**\n- Explaining concepts or mental models\n- Teaching how things work together\n- Tutorials and guides\n- \"Why\" questions\n\n**Link to /reference:**\n- API details, Hook signatures\n- Parameter lists and return values\n- Rules and restrictions\n- \"What exactly\" questions\n\n### Link Formats\n\n```markdown\n[concept name](/learn/page-name)\n[`useState`](/reference/react/useState)\n[section link](/learn/page-name#section-id)\n[MDN](https://developer.mozilla.org/...)\n```\n\n## Section Dividers\n\n**Important:** Learn pages typically do NOT use `---` dividers. The heading hierarchy provides sufficient structure. Only consider dividers in exceptional cases like separating main content from meta/contribution sections.\n\n## Do's and Don'ts\n\n**Do:**\n- Use \"you\" to address the reader\n- Show broken code before fixes\n- Explain behavior before naming concepts\n- Build concepts progressively\n- Include interactive Sandpack examples\n- Use established analogies consistently\n- Place Pitfalls AFTER explaining concepts\n- Invite experimentation with \"Try...\" phrases\n\n**Don't:**\n- Use \"simple\", \"easy\", \"just\", or time estimates\n- Reference concepts not yet introduced\n- Skip required components for page type\n- Use passive voice without reason\n- Place Pitfalls before teaching the concept\n- Use `---` dividers between sections\n- Create unnecessary abstraction in examples\n- Place consecutive Pitfalls or Notes without separating prose (combine or separate)\n\n## Critical Rules\n\n1. **All headings require IDs:** `## Title {/*title-id*/}`\n2. **Chapter intros use `isChapter={true}` and `<LearnMore>`**\n3. **Tutorial pages omit YouWillLearn/Recap/Challenges**\n4. **Problem-first teaching:** Show broken → explain → fix\n5. **No consecutive Pitfalls/Notes:** See `/docs-components` Callout Spacing Rules\n\nFor component patterns, invoke `/docs-components`. For Sandpack patterns, invoke `/docs-sandpack`.\n"
  },
  {
    "path": ".claude/skills/docs-writer-reference/SKILL.md",
    "content": "---\nname: docs-writer-reference\ndescription: Reference page structure, templates, and writing patterns for src/content/reference/. For components, see /docs-components. For code examples, see /docs-sandpack.\n---\n\n# Reference Page Writer\n\n## Quick Reference\n\n### Page Type Decision Tree\n\n1. Is it a Hook? Use **Type A (Hook/Function)**\n2. Is it a React component (`<Something>`)? Use **Type B (Component)**\n3. Is it a compiler configuration option? Use **Type C (Configuration)**\n4. Is it a directive (`'use something'`)? Use **Type D (Directive)**\n5. Is it an ESLint rule? Use **Type E (ESLint Rule)**\n6. Is it listing multiple APIs? Use **Type F (Index/Category)**\n\n### Component Selection\n\nFor component selection and patterns, invoke `/docs-components`.\n\n---\n\n## Voice & Style\n\n**Voice:** Authoritative technical reference writer\n**Tone:** Precise, comprehensive, neutral\n\nFor tone, capitalization, jargon, and prose patterns, invoke `/docs-voice`.\n\n**Do:**\n- Start with single-line description: \"`useState` is a React Hook that lets you...\"\n- Include Parameters, Returns, Caveats sections for every API\n- Document edge cases most developers will encounter\n- Use section dividers between major sections\n- Include \"See more examples below\" links\n- Be assertive, not hedging - \"This is designed for...\" not \"This helps avoid issues with...\"\n- State facts, not benefits - \"The callback always accesses the latest values\" not \"This helps avoid stale closures\"\n- Use minimal but meaningful names - `onEvent` or `onTick` over `onSomething`\n\n**Don't:**\n- Skip the InlineToc component\n- Omit error cases or caveats\n- Use conversational language\n- Mix teaching with reference (that's Learn's job)\n- Document past bugs or fixed issues\n- Include niche edge cases (e.g., `this` binding, rare class patterns)\n- Add phrases explaining \"why you'd want this\" - the Usage section examples do that\n- Exception: Pitfall and DeepDive asides can use slightly conversational phrasing\n\n---\n\n## Page Templates\n\n### Type A: Hook/Function\n\n**When to use:** Documenting React hooks and standalone functions (useState, useEffect, memo, lazy, etc.)\n\n```mdx\n---\ntitle: hookName\n---\n\n<Intro>\n\n`hookName` is a React Hook that lets you [brief description].\n\n```js\nconst result = hookName(arg)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## Reference {/*reference*/}\n\n### `hookName(arg)` {/*hookname*/}\n\nCall `hookName` at the top level of your component to...\n\n```js\n[signature example with annotations]\n```\n\n[See more examples below.](#usage)\n\n#### Parameters {/*parameters*/}\n* `arg`: Description of the parameter.\n\n#### Returns {/*returns*/}\nDescription of return value.\n\n#### Caveats {/*caveats*/}\n* Important caveat about usage.\n\n---\n\n## Usage {/*usage*/}\n\n### Common Use Case {/*common-use-case*/}\nExplanation with Sandpack examples...\n\n---\n\n## Troubleshooting {/*troubleshooting*/}\n\n### Common Problem {/*common-problem*/}\nHow to solve it...\n```\n\n---\n\n### Type B: Component\n\n**When to use:** Documenting React components (Suspense, Fragment, Activity, StrictMode)\n\n```mdx\n---\ntitle: <ComponentName>\n---\n\n<Intro>\n\n`<ComponentName>` lets you [primary action].\n\n```js\n<ComponentName prop={value}>\n  <Children />\n</ComponentName>\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## Reference {/*reference*/}\n\n### `<ComponentName>` {/*componentname*/}\n\n[Component purpose and behavior]\n\n#### Props {/*props*/}\n\n* `propName`: Description of the prop...\n* **optional** `optionalProp`: Description...\n\n#### Caveats {/*caveats*/}\n\n* [Caveats specific to this component]\n```\n\n**Key differences from Hook pages:**\n- Title uses JSX syntax: `<ComponentName>`\n- Uses `#### Props` instead of `#### Parameters`\n- Reference heading uses JSX: `` ### `<ComponentName>` ``\n\n---\n\n### Type C: Configuration\n\n**When to use:** Documenting React Compiler configuration options\n\n```mdx\n---\ntitle: optionName\n---\n\n<Intro>\n\nThe `optionName` option [controls/specifies/determines] [what it does].\n\n</Intro>\n\n```js\n{\n  optionName: 'value' // Quick example\n}\n```\n\n<InlineToc />\n\n---\n\n## Reference {/*reference*/}\n\n### `optionName` {/*optionname*/}\n\n[Description of the option's purpose]\n\n#### Type {/*type*/}\n\n```\n'value1' | 'value2' | 'value3'\n```\n\n#### Default value {/*default-value*/}\n\n`'value1'`\n\n#### Options {/*options*/}\n\n- **`'value1'`** (default): Description\n- **`'value2'`**: Description\n- **`'value3'`**: Description\n\n#### Caveats {/*caveats*/}\n\n* [Usage caveats]\n```\n\n---\n\n### Type D: Directive\n\n**When to use:** Documenting directives like 'use server', 'use client', 'use memo'\n\n```mdx\n---\ntitle: \"'use directive'\"\ntitleForTitleTag: \"'use directive' directive\"\n---\n\n<RSC>\n\n`'use directive'` is for use with [React Server Components](/reference/rsc/server-components).\n\n</RSC>\n\n<Intro>\n\n`'use directive'` marks [what it marks] for [purpose].\n\n```js {1}\nfunction MyComponent() {\n  'use directive';\n  // ...\n}\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## Reference {/*reference*/}\n\n### `'use directive'` {/*use-directive*/}\n\nAdd `'use directive'` at the beginning of [location] to [action].\n\n#### Caveats {/*caveats*/}\n\n* `'use directive'` must be at the very beginning...\n* The directive must be written with single or double quotes, not backticks.\n* [Other placement/syntax caveats]\n```\n\n**Key characteristics:**\n- Title includes quotes: `title: \"'use server'\"`\n- Uses `titleForTitleTag` for browser tab title\n- `<RSC>` block appears before `<Intro>`\n- Caveats focus on placement and syntax requirements\n\n---\n\n### Type E: ESLint Rule\n\n**When to use:** Documenting ESLint plugin rules\n\n```mdx\n---\ntitle: rule-name\n---\n\n<Intro>\nValidates that [what the rule checks].\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\n[Explanation of why this rule exists and React's underlying assumptions]\n\n## Common Violations {/*common-violations*/}\n\n[Description of violation patterns]\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js\n// X Missing dependency\nuseEffect(() => {\n  console.log(count);\n}, []); // Missing 'count'\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// checkmark All dependencies included\nuseEffect(() => {\n  console.log(count);\n}, [count]);\n```\n\n## Troubleshooting {/*troubleshooting*/}\n\n### [Problem description] {/*problem-slug*/}\n\n[Solution]\n\n## Options {/*options*/}\n\n[Configuration options if applicable]\n```\n\n**Key characteristics:**\n- Intro is a single \"Validates that...\" sentence\n- Uses \"Invalid\"/\"Valid\" sections with emoji-prefixed code comments\n- Rule Details explains \"why\" not just \"what\"\n\n---\n\n### Type F: Index/Category\n\n**When to use:** Overview pages listing multiple APIs in a category\n\n```mdx\n---\ntitle: \"Built-in React [Type]\"\n---\n\n<Intro>\n\n*Concept* let you [purpose]. Brief scope statement.\n\n</Intro>\n\n---\n\n## Category Name {/*category-name*/}\n\n*Concept* explanation with [Learn section link](/learn/topic).\n\nTo [action], use one of these [Type]:\n\n* [`apiName`](/reference/react/apiName) lets you [action].\n* [`apiName`](/reference/react/apiName) declares [thing].\n\n```js\nfunction Example() {\n  const value = useHookName(args);\n}\n```\n\n---\n\n## Your own [Type] {/*your-own-type*/}\n\nYou can also [define your own](/learn/topic) as JavaScript functions.\n```\n\n**Key characteristics:**\n- Title format: \"Built-in React [Type]\"\n- Italicized concept definitions\n- Horizontal rules between sections\n- Closes with \"Your own [Type]\" section\n\n---\n\n## Advanced Patterns\n\n### Multi-Function Documentation\n\n**When to use:** When a hook returns a function that needs its own documentation (useState's setter, useReducer's dispatch)\n\n```md\n### `hookName(args)` {/*hookname*/}\n\n[Main hook documentation]\n\n#### Parameters {/*parameters*/}\n#### Returns {/*returns*/}\n#### Caveats {/*caveats*/}\n\n---\n\n### `set` functions, like `setSomething(nextState)` {/*setstate*/}\n\nThe `set` function returned by `hookName` lets you [action].\n\n#### Parameters {/*setstate-parameters*/}\n#### Returns {/*setstate-returns*/}\n#### Caveats {/*setstate-caveats*/}\n```\n\n**Key conventions:**\n- Horizontal rule (`---`) separates main hook from returned function\n- Heading IDs include prefix: `{/*setstate-parameters*/}` vs `{/*parameters*/}`\n- Use generic names: \"set functions\" not \"setCount\"\n\n---\n\n### Compound Return Objects\n\n**When to use:** When a function returns an object with multiple properties/methods (createContext)\n\n```md\n### `createContext(defaultValue)` {/*createcontext*/}\n\n[Main function documentation]\n\n#### Returns {/*returns*/}\n\n`createContext` returns a context object.\n\n**The context object itself does not hold any information.** It represents...\n\n* `SomeContext` lets you provide the context value.\n* `SomeContext.Consumer` is an alternative way to read context.\n\n---\n\n### `SomeContext` Provider {/*provider*/}\n\n[Documentation for Provider]\n\n#### Props {/*provider-props*/}\n\n---\n\n### `SomeContext.Consumer` {/*consumer*/}\n\n[Documentation for Consumer]\n\n#### Props {/*consumer-props*/}\n```\n\n---\n\n## Writing Patterns\n\n### Opening Lines by Page Type\n\n| Page Type | Pattern | Example |\n|-----------|---------|---------|\n| Hook | `` `hookName` is a React Hook that lets you [action]. `` | \"`useState` is a React Hook that lets you add a state variable to your component.\" |\n| Component | `` `<ComponentName>` lets you [action]. `` | \"`<Suspense>` lets you display a fallback until its children have finished loading.\" |\n| API | `` `apiName` lets you [action]. `` | \"`memo` lets you skip re-rendering a component when its props are unchanged.\" |\n| Configuration | `` The `optionName` option [controls/specifies/determines] [what]. `` | \"The `target` option specifies which React version the compiler generates code for.\" |\n| Directive | `` `'directive'` [marks/opts/prevents] [what] for [purpose]. `` | \"`'use server'` marks a function as callable from the client.\" |\n| ESLint Rule | `` Validates that [condition]. `` | \"Validates that dependency arrays for React hooks contain all necessary dependencies.\" |\n\n---\n\n### Parameter Patterns\n\n**Simple parameter:**\n```md\n* `paramName`: Description of what it does.\n```\n\n**Optional parameter:**\n```md\n* **optional** `paramName`: Description of what it does.\n```\n\n**Parameter with special function behavior:**\n```md\n* `initialState`: The value you want the state to be initially. It can be a value of any type, but there is a special behavior for functions. This argument is ignored after the initial render.\n  * If you pass a function as `initialState`, it will be treated as an _initializer function_. It should be pure, should take no arguments, and should return a value of any type.\n```\n\n**Callback parameter with sub-parameters:**\n```md\n* `subscribe`: A function that takes a single `callback` argument and subscribes it to the store. When the store changes, it should invoke the provided `callback`. The `subscribe` function should return a function that cleans up the subscription.\n```\n\n**Nested options object:**\n```md\n* **optional** `options`: An object with options for this React root.\n  * **optional** `onCaughtError`: Callback called when React catches an error in an Error Boundary.\n  * **optional** `onUncaughtError`: Callback called when an error is thrown and not caught.\n  * **optional** `identifierPrefix`: A string prefix React uses for IDs generated by `useId`.\n```\n\n---\n\n### Return Value Patterns\n\n**Single value return:**\n```md\n`hookName` returns the current value. The value will be the same as `initialValue` during the first render.\n```\n\n**Array return (numbered list):**\n```md\n`useState` returns an array with exactly two values:\n\n1. The current state. During the first render, it will match the `initialState` you have passed.\n2. The [`set` function](#setstate) that lets you update the state to a different value and trigger a re-render.\n```\n\n**Object return (bulleted list):**\n```md\n`createElement` returns a React element object with a few properties:\n\n* `type`: The `type` you have passed.\n* `props`: The `props` you have passed except for `ref` and `key`.\n* `ref`: The `ref` you have passed. If missing, `null`.\n* `key`: The `key` you have passed, coerced to a string. If missing, `null`.\n```\n\n**Promise return:**\n```md\n`prerender` returns a Promise:\n- If rendering is successful, the Promise will resolve to an object containing:\n  - `prelude`: a [Web Stream](MDN-link) of HTML.\n  - `postponed`: a JSON-serializable object for resumption.\n- If rendering fails, the Promise will be rejected.\n```\n\n**Wrapped function return:**\n```md\n`cache` returns a cached version of `fn` with the same type signature. It does not call `fn` in the process.\n\nWhen calling `cachedFn` with given arguments, it first checks if a cached result exists. If cached, it returns the result. If not, it calls `fn`, stores the result, and returns it.\n```\n\n---\n\n### Caveats Patterns\n\n**Standard Hook caveat (almost always first for Hooks):**\n```md\n* `useXxx` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it.\n```\n\n**Stable identity caveat (for returned functions):**\n```md\n* The `set` function has a stable identity, so you will often see it omitted from Effect dependencies, but including it will not cause the Effect to fire.\n```\n\n**Strict Mode caveat:**\n```md\n* In Strict Mode, React will **call your render function twice** in order to help you find accidental impurities. This is development-only behavior and does not affect production.\n```\n\n**Caveat with code example:**\n```md\n* It's not recommended to _suspend_ a render based on a store value returned by `useSyncExternalStore`. For example, the following is discouraged:\n\n  ```js\n  const selectedProductId = useSyncExternalStore(...);\n  const data = use(fetchItem(selectedProductId)) // X Don't suspend based on store value\n  ```\n```\n\n**Canary caveat:**\n```md\n* <CanaryBadge /> If you want to pass `ref` to a Fragment, you can't use the `<>...</>` syntax.\n```\n\n---\n\n### Troubleshooting Patterns\n\n**Heading format (first person problem statements):**\n```md\n### I've updated the state, but logging gives me the old value {/*old-value*/}\n\n### My initializer or updater function runs twice {/*runs-twice*/}\n\n### I want to read the latest state from a callback {/*read-latest-state*/}\n```\n\n**Error message format:**\n```md\n### I'm getting an error: \"Too many re-renders\" {/*too-many-rerenders*/}\n\n### I'm getting an error: \"Rendered more hooks than during the previous render\" {/*more-hooks*/}\n```\n\n**Lint error format:**\n```md\n### I'm getting a lint error: \"[exact error message]\" {/*lint-error-slug*/}\n```\n\n**Problem-solution structure:**\n1. State the problem with code showing the issue\n2. Explain why it happens\n3. Provide the solution with corrected code\n4. Link to Learn section for deeper understanding\n\n---\n\n### Code Comment Conventions\n\nFor code comment conventions (wrong/right, legacy/recommended, server/client labeling, bundle size annotations), invoke `/docs-sandpack`.\n\n---\n\n### Link Description Patterns\n\n| Pattern | Example |\n|---------|---------|\n| \"lets you\" + action | \"`memo` lets you skip re-rendering when props are unchanged.\" |\n| \"declares\" + thing | \"`useState` declares a state variable that you can update directly.\" |\n| \"reads\" + thing | \"`useContext` reads and subscribes to a context.\" |\n| \"connects\" + thing | \"`useEffect` connects a component to an external system.\" |\n| \"Used with\" | \"Used with [`useContext`.](/reference/react/useContext)\" |\n| \"Similar to\" | \"Similar to [`useTransition`.](/reference/react/useTransition)\" |\n\n---\n\n## Component Patterns\n\nFor comprehensive MDX component patterns (Note, Pitfall, DeepDive, Recipes, Deprecated, RSC, Canary, Diagram, Code Steps), invoke `/docs-components`.\n\nFor Sandpack-specific patterns and code style, invoke `/docs-sandpack`.\n\n### Reference-Specific Component Rules\n\n**Component placement in Reference pages:**\n- `<RSC>` goes before `<Intro>` at top of page\n- `<Deprecated>` goes after `<Intro>` for page-level deprecation\n- `<Deprecated>` goes after method heading for method-level deprecation\n- `<Canary>` wrapper goes inline within `<Intro>`\n- `<CanaryBadge />` appears in headings, props lists, and caveats\n\n**Troubleshooting-specific components:**\n- Use first-person problem headings\n- Cross-reference Pitfall IDs when relevant\n\n**Callout spacing:**\n- Never place consecutive Pitfalls or consecutive Notes\n- Combine related warnings into one with titled subsections, or separate with prose/code\n- Consecutive DeepDives OK for multi-part explorations\n- See `/docs-components` Callout Spacing Rules\n\n---\n\n## Content Principles\n\n### Intro Section\n- **One sentence, ~15 words max** - State what the Hook does, not how it works\n- ✅ \"`useEffectEvent` is a React Hook that lets you separate events from Effects.\"\n- ❌ \"`useEffectEvent` is a React Hook that lets you extract non-reactive logic from your Effects into a reusable function called an Effect Event.\"\n\n### Reference Code Example\n- Show just the API call (5-10 lines), not a full component\n- Move full component examples to Usage section\n\n### Usage Section Structure\n1. **First example: Core mental model** - Show the canonical use case with simplest concrete example\n2. **Subsequent examples: Canonical use cases** - Name the *why* (e.g., \"Avoid reconnecting to external systems\"), show a concrete *how*\n   - Prefer broad canonical use cases over multiple narrow concrete examples\n   - The section title IS the teaching - \"When would I use this?\" should be answered by the heading\n\n### What to Include vs. Exclude\n- **Never** document past bugs or fixed issues\n- **Include** edge cases most developers will encounter\n- **Exclude** niche edge cases (e.g., `this` binding, rare class patterns)\n\n### Caveats Section\n- Include rules the linter enforces or that cause immediate errors\n- Include fundamental usage restrictions\n- Exclude implementation details unless they affect usage\n- Exclude repetition of things explained elsewhere\n- Keep each caveat to one sentence when possible\n\n### Troubleshooting Section\n- Error headings only: \"I'm getting an error: '[message]'\" format\n- Never document past bugs - if it's fixed, it doesn't belong here\n- Focus on errors developers will actually encounter today\n\n### DeepDive Content\n- **Goldilocks principle** - Deep enough for curious developers, short enough to not overwhelm\n- Answer \"why is it designed this way?\" - not exhaustive technical details\n- Readers who skip it should miss nothing essential for using the API\n- If the explanation is getting long, you're probably explaining too much\n\n---\n\n## Domain-Specific Guidance\n\n### Hooks\n\n**Returned function documentation:**\n- Document setter/dispatch functions as separate `###` sections\n- Use generic names: \"set functions\" not \"setCount\"\n- Include stable identity caveat for returned functions\n\n**Dependency array documentation:**\n- List what counts as reactive values\n- Explain when dependencies are ignored\n- Link to removing effect dependencies guide\n\n**Recipes usage:**\n- Group related examples with meaningful titleText\n- Each recipe has brief intro, Sandpack, and `<Solution />`\n\n---\n\n### Components\n\n**Props documentation:**\n- Use `#### Props` instead of `#### Parameters`\n- Mark optional props with `**optional**` prefix\n- Use `<CanaryBadge />` inline for canary-only props\n\n**JSX syntax in titles/headings:**\n- Frontmatter title: `title: <Suspense>`\n- Reference heading: `` ### `<Suspense>` {/*suspense*/} ``\n\n---\n\n### React-DOM\n\n**Common props linking:**\n```md\n`<input>` supports all [common element props.](/reference/react-dom/components/common#common-props)\n```\n\n**Props categorization:**\n- Controlled vs uncontrolled props grouped separately\n- Form-specific props documented with action patterns\n- MDN links for standard HTML attributes\n\n**Environment-specific notes:**\n```mdx\n<Note>\n\nThis API is specific to Node.js. Environments with [Web Streams](MDN-link), like Deno and modern edge runtimes, should use [`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream) instead.\n\n</Note>\n```\n\n**Progressive enhancement:**\n- Document benefits for users without JavaScript\n- Explain Server Function + form action integration\n- Show hidden form field and `.bind()` patterns\n\n---\n\n### RSC\n\n**RSC banner (before Intro):**\nAlways place `<RSC>` component before `<Intro>` for Server Component-only APIs.\n\n**Serialization type lists:**\nWhen documenting Server Function arguments, list supported types:\n```md\nSupported types for Server Function arguments:\n\n* Primitives\n\t* [string](MDN-link)\n\t* [number](MDN-link)\n* Iterables containing serializable values\n\t* [Array](MDN-link)\n\t* [Map](MDN-link)\n\nNotably, these are not supported:\n* React elements, or [JSX](/learn/writing-markup-with-jsx)\n* Functions (other than Server Functions)\n```\n\n**Bundle size comparisons:**\n- Show \"Not included in bundle\" for server-only imports\n- Annotate client bundle sizes with gzip: `// 35.9K (11.2K gzipped)`\n\n---\n\n### Compiler\n\n**Configuration page structure:**\n- Type (union type or interface)\n- Default value\n- Options/Valid values with descriptions\n\n**Directive documentation:**\n- Placement requirements are critical\n- Mode interaction tables showing combinations\n- \"Use sparingly\" + \"Plan for removal\" patterns for escape hatches\n\n**Library author guides:**\n- Audience-first intro\n- Benefits/Why section\n- Numbered step-by-step setup\n\n---\n\n### ESLint\n\n**Rule Details section:**\n- Explain \"why\" not just \"what\"\n- Focus on React's underlying assumptions\n- Describe consequences of violations\n\n**Invalid/Valid sections:**\n- Standard intro: \"Examples of [in]correct code for this rule:\"\n- Use X emoji for invalid, checkmark for valid\n- Show inline comments explaining the violation\n\n**Configuration options:**\n- Show shared settings (preferred)\n- Show rule-level options (backward compatibility)\n- Note precedence when both exist\n\n---\n\n## Edge Cases\n\nFor deprecated, canary, and version-specific component patterns (placement, syntax, examples), invoke `/docs-components`.\n\n**Quick placement rules:**\n- `<Deprecated>` after `<Intro>` for page-level, after heading for method-level\n- `<Canary>` wrapper inline in Intro, `<CanaryBadge />` in headings/props/caveats\n- Version notes use `<Note>` with \"Starting in React 19...\" pattern\n\n**Removed APIs on index pages:**\n```md\n## Removed APIs {/*removed-apis*/}\n\nThese APIs were removed in React 19:\n\n* [`render`](https://18.react.dev/reference/react-dom/render): use [`createRoot`](/reference/react-dom/client/createRoot) instead.\n```\n\nLink to previous version docs (18.react.dev) for removed API documentation.\n\n---\n\n## Critical Rules\n\n1. **Heading IDs required:** `## Title {/*title-id*/}` (lowercase, hyphens)\n2. **Sandpack main file needs `export default`**\n3. **Active file syntax:** ` ```js src/File.js active `\n4. **Error headings in Troubleshooting:** Use `### I'm getting an error: \"[message]\" {/*id*/}`\n5. **Section dividers (`---`)** required between headings (see Section Dividers below)\n6. **InlineToc required:** Always include `<InlineToc />` after Intro\n7. **Consistent parameter format:** Use `* \\`paramName\\`: description` with `**optional**` prefix for optional params\n8. **Numbered lists for array returns:** When hooks return arrays, use numbered lists in Returns section\n9. **Generic names for returned functions:** Use \"set functions\" not \"setCount\"\n10. **Props vs Parameters:** Use `#### Props` for Components (Type B), `#### Parameters` for Hooks/APIs (Type A)\n11. **RSC placement:** `<RSC>` component goes before `<Intro>`, not after\n12. **Canary markers:** Use `<Canary>` wrapper inline in Intro, `<CanaryBadge />` in headings/props\n13. **Deprecated placement:** `<Deprecated>` goes after `<Intro>` for page-level, after heading for method-level\n14. **Code comment emojis:** Use X for wrong, checkmark for correct in code examples\n15. **No consecutive Pitfalls/Notes:** Combine into one component with titled subsections, or separate with prose/code (see `/docs-components`)\n\nFor component heading level conventions (DeepDive, Pitfall, Note, Recipe headings), see `/docs-components`.\n\n### Section Dividers\n\nUse `---` horizontal rules to visually separate major sections:\n\n- **After `<InlineToc />`** - Before `## Reference` heading\n- **Between API subsections** - Between different function/hook definitions (e.g., between `useState()` and `set functions`)\n- **Before `## Usage`** - Separates API reference from examples\n- **Before `## Troubleshooting`** - Separates content from troubleshooting\n- **Between EVERY Usage subsections** - When switching to a new major use case\n\nAlways have a blank line before and after `---`.\n\n### Section ID Conventions\n\n| Section | ID Format |\n|---------|-----------|\n| Main function | `{/*functionname*/}` |\n| Returned function | `{/*setstate*/}`, `{/*dispatch*/}` |\n| Sub-section of returned function | `{/*setstate-parameters*/}` |\n| Troubleshooting item | `{/*problem-description-slug*/}` |\n| Pitfall | `{/*pitfall-description*/}` |\n| Deep dive | `{/*deep-dive-topic*/}` |\n"
  },
  {
    "path": ".claude/skills/react-expert/SKILL.md",
    "content": "---\nname: react-expert\ndescription: Use when researching React APIs or concepts for documentation. Use when you need authoritative usage examples, caveats, warnings, or errors for a React feature.\n---\n\n# React Expert Research Skill\n\n## Overview\n\nThis skill produces exhaustive documentation research on any React API or concept by searching authoritative sources (tests, source code, PRs, issues) rather than relying on LLM training knowledge.\n\n<CRITICAL>\n**Skepticism Mandate:** You must be skeptical of your own knowledge. Claude is often trained on outdated or incorrect React patterns. Treat source material as the sole authority. If findings contradict your prior understanding, explicitly flag this discrepancy.\n\n**Red Flags - STOP if you catch yourself thinking:**\n- \"I know this API does X\" → Find source evidence first\n- \"Common pattern is Y\" → Verify in test files\n- Generating example code → Must have source file reference\n</CRITICAL>\n\n## Invocation\n\n```\n/react-expert useTransition\n/react-expert suspense boundaries\n/react-expert startTransition\n```\n\n## Sources (Priority Order)\n\n1. **React Repo Tests** - Most authoritative for actual behavior\n2. **React Source Code** - Warnings, errors, implementation details\n3. **Git History** - Commit messages with context\n4. **GitHub PRs & Comments** - Design rationale (via `gh` CLI)\n5. **GitHub Issues** - Confusion/questions (facebook/react + reactjs/react.dev)\n6. **React Working Group** - Design discussions for newer APIs\n7. **Flow Types** - Source of truth for type signatures\n8. **TypeScript Types** - Note discrepancies with Flow\n9. **Current react.dev docs** - Baseline (not trusted as complete)\n\n**No web search** - No Stack Overflow, blog posts, or web searches. GitHub API via `gh` CLI is allowed.\n\n## Workflow\n\n### Step 1: Setup React Repo\n\nFirst, ensure the React repo is available locally:\n\n```bash\n# Check if React repo exists, clone or update\nif [ -d \".claude/react\" ]; then\n  cd .claude/react && git pull origin main\nelse\n  git clone --depth=100 https://github.com/facebook/react.git .claude/react\nfi\n```\n\nGet the current commit hash for the research document:\n```bash\ncd .claude/react && git rev-parse --short HEAD\n```\n\n### Step 2: Dispatch 6 Parallel Research Agents\n\nSpawn these agents IN PARALLEL using the Task tool. Each agent receives the skepticism preamble:\n\n> \"You are researching React's `<TOPIC>`. CRITICAL: Do NOT rely on your prior knowledge about this API. Your training may contain outdated or incorrect patterns. Only report what you find in the source files. If your findings contradict common understanding, explicitly highlight this discrepancy.\"\n\n| Agent | subagent_type | Focus | Instructions |\n|-------|---------------|-------|--------------|\n| test-explorer | Explore | Test files for usage patterns | Search `.claude/react/packages/*/src/__tests__/` for test files mentioning the topic. Extract actual usage examples WITH file paths and line numbers. |\n| source-explorer | Explore | Warnings/errors in source | Search `.claude/react/packages/*/src/` for console.error, console.warn, and error messages mentioning the topic. Document trigger conditions. |\n| git-historian | Explore | Commit messages | Run `git log --all --grep=\"<topic>\" --oneline -50` in `.claude/react`. Read full commit messages for context. |\n| pr-researcher | Explore | PRs introducing/modifying API | Run `gh pr list -R facebook/react --search \"<topic>\" --state all --limit 20`. Read key PR descriptions and comments. |\n| issue-hunter | Explore | Issues showing confusion | Search issues in both `facebook/react` and `reactjs/react.dev` repos. Look for common questions and misunderstandings. |\n| types-inspector | Explore | Flow + TypeScript signatures | Find Flow types in `.claude/react/packages/*/src/*.js` (look for `@flow` annotations). Find TS types in `.claude/react/packages/*/index.d.ts`. Note discrepancies. |\n\n### Step 3: Agent Prompts\n\nUse these exact prompts when spawning agents:\n\n#### test-explorer\n```\nYou are researching React's <TOPIC>.\n\nCRITICAL: Do NOT rely on your prior knowledge about this API. Your training may contain outdated or incorrect patterns. Only report what you find in the source files.\n\nYour task: Find test files in .claude/react that demonstrate <TOPIC> usage.\n\n1. Search for test files: Glob for `**/__tests__/**/*<topic>*` and `**/__tests__/**/*.js` then grep for <topic>\n2. For each relevant test file, extract:\n   - The test description (describe/it blocks)\n   - The actual usage code\n   - Any assertions about behavior\n   - Edge cases being tested\n3. Report findings with exact file paths and line numbers\n\nFormat your output as:\n## Test File: <path>\n### Test: \"<test description>\"\n```javascript\n<exact code from test>\n```\n**Behavior:** <what the test asserts>\n```\n\n#### source-explorer\n```\nYou are researching React's <TOPIC>.\n\nCRITICAL: Do NOT rely on your prior knowledge about this API. Only report what you find in the source files.\n\nYour task: Find warnings, errors, and implementation details for <TOPIC>.\n\n1. Search .claude/react/packages/*/src/ for:\n   - console.error mentions of <topic>\n   - console.warn mentions of <topic>\n   - Error messages mentioning <topic>\n   - The main implementation file\n2. For each warning/error, document:\n   - The exact message text\n   - The condition that triggers it\n   - The source file and line number\n\nFormat your output as:\n## Warnings & Errors\n| Message | Trigger Condition | Source |\n|---------|------------------|--------|\n| \"<exact message>\" | <condition> | <file:line> |\n\n## Implementation Notes\n<key details from source code>\n```\n\n#### git-historian\n```\nYou are researching React's <TOPIC>.\n\nCRITICAL: Do NOT rely on your prior knowledge. Only report what you find in git history.\n\nYour task: Find commit messages that explain <TOPIC> design decisions.\n\n1. Run: cd .claude/react && git log --all --grep=\"<topic>\" --oneline -50\n2. For significant commits, read full message: git show <hash> --stat\n3. Look for:\n   - Initial introduction of the API\n   - Bug fixes (reveal edge cases)\n   - Behavior changes\n   - Deprecation notices\n\nFormat your output as:\n## Key Commits\n### <short hash> - <subject>\n**Date:** <date>\n**Context:** <why this change was made>\n**Impact:** <what behavior changed>\n```\n\n#### pr-researcher\n```\nYou are researching React's <TOPIC>.\n\nCRITICAL: Do NOT rely on your prior knowledge. Only report what you find in PRs.\n\nYour task: Find PRs that introduced or modified <TOPIC>.\n\n1. Run: gh pr list -R facebook/react --search \"<topic>\" --state all --limit 20 --json number,title,url\n2. For promising PRs, read details: gh pr view <number> -R facebook/react\n3. Look for:\n   - The original RFC/motivation\n   - Design discussions in comments\n   - Alternative approaches considered\n   - Breaking changes\n\nFormat your output as:\n## Key PRs\n### PR #<number>: <title>\n**URL:** <url>\n**Summary:** <what it introduced/changed>\n**Design Rationale:** <why this approach>\n**Discussion Highlights:** <key points from comments>\n```\n\n#### issue-hunter\n```\nYou are researching React's <TOPIC>.\n\nCRITICAL: Do NOT rely on your prior knowledge. Only report what you find in issues.\n\nYour task: Find issues that reveal common confusion about <TOPIC>.\n\n1. Search facebook/react: gh issue list -R facebook/react --search \"<topic>\" --state all --limit 20 --json number,title,url\n2. Search reactjs/react.dev: gh issue list -R reactjs/react.dev --search \"<topic>\" --state all --limit 20 --json number,title,url\n3. For each issue, identify:\n   - What the user was confused about\n   - What the resolution was\n   - Any gotchas revealed\n\nFormat your output as:\n## Common Confusion\n### Issue #<number>: <title>\n**Repo:** <facebook/react or reactjs/react.dev>\n**Confusion:** <what they misunderstood>\n**Resolution:** <correct understanding>\n**Gotcha:** <if applicable>\n```\n\n#### types-inspector\n```\nYou are researching React's <TOPIC>.\n\nCRITICAL: Do NOT rely on your prior knowledge. Only report what you find in type definitions.\n\nYour task: Find and compare Flow and TypeScript type signatures for <TOPIC>.\n\n1. Flow types (source of truth): Search .claude/react/packages/*/src/*.js for @flow annotations related to <topic>\n2. TypeScript types: Search .claude/react/packages/*/index.d.ts and @types/react\n3. Compare and note any discrepancies\n\nFormat your output as:\n## Flow Types (Source of Truth)\n**File:** <path>\n```flow\n<exact type definition>\n```\n\n## TypeScript Types\n**File:** <path>\n```typescript\n<exact type definition>\n```\n\n## Discrepancies\n<any differences between Flow and TS definitions>\n```\n\n### Step 4: Synthesize Results\n\nAfter all agents complete, combine their findings into a single research document.\n\n**DO NOT add information from your own knowledge.** Only include what agents found in sources.\n\n### Step 5: Save Output\n\nWrite the final document to `.claude/research/<topic>.md`\n\nReplace spaces in topic with hyphens (e.g., \"suspense boundaries\" → \"suspense-boundaries.md\")\n\n## Output Document Template\n\n```markdown\n# React Research: <topic>\n\n> Generated by /react-expert on YYYY-MM-DD\n> Sources: React repo (commit <hash>), N PRs, M issues\n\n## Summary\n\n[Brief summary based SOLELY on source findings, not prior knowledge]\n\n## API Signature\n\n### Flow Types (Source of Truth)\n\n[From types-inspector agent]\n\n### TypeScript Types\n\n[From types-inspector agent]\n\n### Discrepancies\n\n[Any differences between Flow and TS]\n\n## Usage Examples\n\n### From Tests\n\n[From test-explorer agent - with file:line references]\n\n### From PRs/Issues\n\n[Real-world patterns from discussions]\n\n## Caveats & Gotchas\n\n[Each with source link]\n\n- **<gotcha>** - Source: <link>\n\n## Warnings & Errors\n\n| Message | Trigger Condition | Source File |\n|---------|------------------|-------------|\n[From source-explorer agent]\n\n## Common Confusion\n\n[From issue-hunter agent]\n\n## Design Decisions\n\n[From git-historian and pr-researcher agents]\n\n## Source Links\n\n### Commits\n- <hash>: <description>\n\n### Pull Requests\n- PR #<number>: <title> - <url>\n\n### Issues\n- Issue #<number>: <title> - <url>\n```\n\n## Common Mistakes to Avoid\n\n1. **Trusting prior knowledge** - If you \"know\" something about the API, find the source evidence anyway\n2. **Generating example code** - Every code example must come from an actual source file\n3. **Skipping agents** - All 6 agents must run; each provides unique perspective\n4. **Summarizing without sources** - Every claim needs a file:line or PR/issue reference\n5. **Using web search** - No Stack Overflow, no blog posts, no social media\n\n## Verification Checklist\n\nBefore finalizing the research document:\n\n- [ ] React repo is at `.claude/react` with known commit hash\n- [ ] All 6 agents were spawned in parallel\n- [ ] Every code example has a source file reference\n- [ ] Warnings/errors table has source locations\n- [ ] No claims made without source evidence\n- [ ] Discrepancies between Flow/TS types documented\n- [ ] Source links section is complete\n"
  },
  {
    "path": ".claude/skills/review-docs/SKILL.md",
    "content": "---\nname: review-docs\ndescription: Use when reviewing React documentation for structure, components, and style compliance\n---\nCRITICAL: do not load these skills yourself.\n\nRun these tasks in parallel for the given file(s). Each agent checks different aspects—not all apply to every file:\n\n- [ ] Ask docs-reviewer agent to review {files} with docs-writer-learn (only for files in src/content/learn/).\n- [ ] Ask docs-reviewer agent to review {files} with docs-writer-reference (only for files in src/content/reference/).\n- [ ] Ask docs-reviewer agent to review {files} with docs-writer-blog (only for files in src/content/blog/).\n- [ ] Ask docs-reviewer agent to review {files} with docs-voice (all documentation files).\n- [ ] Ask docs-reviewer agent to review {files} with docs-components (all documentation files).\n- [ ] Ask docs-reviewer agent to review {files} with docs-sandpack (files containing Sandpack examples).\n\nIf no file is specified, check git status for modified MDX files in `src/content/`.\n\nThe docs-reviewer will return a checklist of the issues it found. Respond with the full checklist and line numbers from all agents, and prompt the user to create a plan to fix these issues.\n\n\n"
  },
  {
    "path": ".eslintignore",
    "content": "scripts\nplugins\nnext.config.js\n.claude/\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"root\": true,\n  \"extends\": \"next/core-web-vitals\",\n  \"parser\": \"@typescript-eslint/parser\",\n  \"plugins\": [\"@typescript-eslint\", \"eslint-plugin-react-compiler\", \"local-rules\"],\n  \"rules\": {\n    \"no-unused-vars\": \"off\",\n    \"@typescript-eslint/no-unused-vars\": [\"error\", {\"varsIgnorePattern\": \"^_\"}],\n    \"react-hooks/exhaustive-deps\": \"error\",\n    \"react/no-unknown-property\": [\"error\", {\"ignore\": [\"meta\"]}],\n    \"react-compiler/react-compiler\": \"error\",\n    \"local-rules/lint-markdown-code-blocks\": \"error\"\n  },\n  \"env\": {\n    \"node\": true,\n    \"commonjs\": true,\n    \"browser\": true,\n    \"es6\": true\n  },\n  \"overrides\": [\n    {\n      \"files\": [\"src/content/**/*.md\"],\n      \"parser\": \"./eslint-local-rules/parser\",\n      \"parserOptions\": {\n        \"sourceType\": \"module\"\n      },\n      \"rules\": {\n        \"no-unused-vars\": \"off\",\n        \"@typescript-eslint/no-unused-vars\": \"off\",\n        \"react-hooks/exhaustive-deps\": \"off\",\n        \"react/no-unknown-property\": \"off\",\n        \"react-compiler/react-compiler\": \"off\",\n        \"local-rules/lint-markdown-code-blocks\": \"error\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/0-bug.yml",
    "content": "name: \"🐛 Report a bug\"\ndescription: \"Report a problem on the website.\"\ntitle: \"[Bug]: \"\nlabels: [\"bug: unconfirmed\"]\nbody:\n  - type: textarea\n    attributes:\n      label: Summary\n      description: |\n        A clear and concise summary of what the bug is.\n      placeholder: |\n        Example bug report:\n        When I click the \"Submit\" button on \"Feedback\", nothing happens.\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Page\n      description: |\n        What page(s) did you encounter this bug on?\n      placeholder: |\n        https://react.dev/\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Details\n      description: |\n        Please provide any additional details about the bug.\n      placeholder: |\n        Example details:\n        The \"Submit\" button is unresponsive. I've tried refreshing the page and using a different browser, but the issue persists.\n    validations:\n      required: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1-typo.yml",
    "content": "name: \"🤦 Typo or mistake\"\ndescription: \"Report a typo or mistake in the docs.\"\ntitle: \"[Typo]: \"\nlabels: [\"type: typos\"]\nbody:\n  - type: textarea\n    attributes:\n      label: Summary\n      description: |\n        A clear and concise summary of what the mistake is.\n      placeholder: |\n        Example:\n        The code example on the \"useReducer\" page includes an unused variable `nextId`.\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Page\n      description: |\n        What page is the typo on?\n      placeholder: |\n        https://react.dev/\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Details\n      description: |\n        Please provide a explanation for why this is a mistake.\n      placeholder: |\n        Example mistake:\n        In the \"useReducer\" section of the \"API Reference\" page, the code example under \"Writing a reducer function\" includes an unused variable `nextId` that should be removed.\n    validations:\n      required: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/2-suggestion.yml",
    "content": "name: \"💡 Suggestions\"\ndescription: \"Suggest a new page, section, or edit for an existing page.\"\ntitle: \"[Suggestion]: \"\nlabels: [\"type: documentation\"]\nbody:\n  - type: textarea\n    attributes:\n      label: Summary\n      description: |\n        A clear and concise summary of what we should add.\n      placeholder: |\n        Example:\n        Add a new page for how to use React with TypeScript.\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Page\n      description: |\n        What page is this about?\n      placeholder: |\n        https://react.dev/\n    validations:\n      required: false\n  - type: textarea\n    attributes:\n      label: Details\n      description: |\n        Please provide a explanation for what you're suggesting.\n      placeholder: |\n        Example:\n        I think it would be helpful to have a page that explains how to use React with TypeScript. This could include a basic example of a component written in TypeScript, and a link to the TypeScript documentation.\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/3-framework.yml",
    "content": "name: \"📄 Suggest new framework\"\ndescription: \"I am a framework author applying to be included as a recommended framework.\"\ntitle: \"[Framework]: \"\nlabels: [\"type: framework\"]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        ## Apply to be included as a recommended React framework\n\n        _This form is for framework authors to apply to be included as a recommended [React framework](https://react.dev/learn/creating-a-react-app). If you are not a framework author, please contact the authors before submitting._\n        \n        Our goal when recommending a framework is to start developers with a React project that solves common problems like code splitting, data fetching, routing, and HTML generation without any extra work later. We believe this will allow users to get started quickly with React, and scale their app to production.\n        \n        While we understand that many frameworks may want to be featured, this page is not a place to advertise every possible React framework or all frameworks that you can add React to. There are many great frameworks that offer support for React that are not listed in our guides. The frameworks we recommend have invested significantly in the React ecosystem, and collaborated with the React team to be compatible with our [full-stack React architecture vision](https://react.dev/learn/creating-a-react-app#which-features-make-up-the-react-teams-full-stack-architecture-vision).\n        \n        To be included, frameworks must meet the following criteria:\n        \n        - **Free & open-source**: must be open source and free to use.\n        - **Well maintained**. must be actively maintained, providing bug fixes and improvements.\n        - **Active community**: must have a sufficiently large and active community to support users.\n        - **Clear onboarding**: must have clear install steps to install the React version of the framework.\n        - **Ecosystem compatibility**: must support using the full range of libraries and tools in the React ecosystem.\n        - **Self-hosting option**: must support an option to self-host applications without losing access to features.\n        - **Developer experience**. must allow developers to be productive by supporting features like Fast Refresh.\n        - **User experience**. must provide built-in support for common problems like routing and data-fetching.\n        - **Compatible with our future vision for React**. React evolves over time, and frameworks that do not align with React’s direction risk isolating their users from the main React ecosystem over time. To be included on this page we must feel confident that the framework is setting its users up for success with React over time.\n        \n        Please note, we have reviewed most of the popular frameworks available today, so it is unlikely we have not considered your framework already. But if you think we missed something, please complete the application below.\n  - type: input\n    attributes:\n      label: Name\n      description: |\n        What is the name of your framework?\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Homepage\n      description: |\n        What is the URL of your homepage?\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Install instructions\n      description: |\n        What is the URL of your getting started guide?\n    validations:\n      required: true\n  - type: dropdown\n    attributes:\n      label: Is your framework open source?\n      description: |\n        We only recommend free and open source frameworks.\n      options:\n        - 'No'\n        - 'Yes'\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Well maintained\n      description: |\n        Please describe how your framework is actively maintained. Include recent releases, bug fixes, and improvements as examples.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Active community\n      description: |\n        Please describe your community. Include the size of your community, and links to community resources.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Clear onboarding\n      description: |\n        Please describe how a user can install your framework with React. Include links to any relevant documentation.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Ecosystem compatibility\n      description: |\n        Please describe any limitations your framework has with the React ecosystem. Include any libraries or tools that are not compatible with your framework.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Self-hosting option\n      description: |\n        Please describe how your framework supports self-hosting. Include any limitations to features when self-hosting. Also include whether you require a server to deploy your framework.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Developer Experience\n      description: |\n        Please describe how your framework provides a great developer experience. Include any limitations to React features like React DevTools, Chrome DevTools, and Fast Refresh.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: User Experience\n      description: |\n        Please describe how your framework helps developers create high quality user experiences by solving common use-cases. Include specifics for how your framework offers built-in support for code-splitting, routing, HTML generation, and data-fetching in a way that avoids client/server waterfalls by default. Include details on how you offer features such as SSG and SSR.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Compatible with our future vision for React\n      description: |\n        Please describe how your framework aligns with our future vision for React. Include how your framework will evolve with React over time, and your plans to support future React features like React Server Components.\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "contact_links:\n  - name: 📃 Bugs in React\n    url: https://github.com/facebook/react/issues/new/choose\n    about: This issue tracker is not for bugs in React. Please file React issues here.\n  - name: 🤔 Questions and Help\n    url: https://reactjs.org/community/support.html\n    about: This issue tracker is not for support questions. Please refer to the React community's help and discussion forums.\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\n\n日本語版 (ja.react.dev) リポジトリでのPR/Issueは、日本語版固有の問題\n（翻訳や日本語版独自機能に関係するもの）のみ受け付けます。\n\n全言語に関わる問題や改善（英語部分のスペルミス、コードサンプルの修正、ビルドシステム改善等）\nについては、英語版リポジトリ (https://github.com/reactjs/react.dev)\nでPR/Issueを作成してください。\n\n日本語版の作業フローについては以下を参照してください。\nhttps://github.com/reactjs/ja.react.dev/wiki\n\n-->\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"npm\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    # Disable Dependabot. Doing it here so it propagates to translation forks.\n    open-pull-requests-limit: 0\n"
  },
  {
    "path": ".github/workflows/analyze.yml",
    "content": "name: Analyze Bundle\n\non:\n  pull_request:\n  push:\n    branches:\n      - main # change this if your default branch is named differently\n  workflow_dispatch:\n\npermissions: {}\n\njobs:\n  analyze:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Set up node\n        uses: actions/setup-node@v4\n        with:\n          node-version: '20.x'\n          cache: yarn\n          cache-dependency-path: yarn.lock\n\n      - name: Restore cached node_modules\n        uses: actions/cache@v4\n        with:\n          path: '**/node_modules'\n          key: node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}\n\n      - name: Install deps\n        run: yarn install --frozen-lockfile\n\n      - name: Restore next build\n        uses: actions/cache@v4\n        id: restore-build-cache\n        env:\n          cache-name: cache-next-build\n        with:\n          path: .next/cache\n          # change this if you prefer a more strict cache\n          key: ${{ runner.os }}-build-${{ env.cache-name }}\n\n      - name: Build next.js app\n        # change this if your site requires a custom build command\n        run: ./node_modules/.bin/next build\n\n      # Here's the first place where next-bundle-analysis' own script is used\n      # This step pulls the raw bundle stats for the current bundle\n      - name: Analyze bundle\n        run: npx -p nextjs-bundle-analysis@0.5.0 report\n\n      - name: Upload bundle\n        uses: actions/upload-artifact@v4\n        with:\n          path: .next/analyze/__bundle_analysis.json\n          name: bundle_analysis.json\n\n      - name: Download base branch bundle stats\n        uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e\n        if: success() && github.event.number\n        with:\n          workflow: analyze.yml\n          branch: ${{ github.event.pull_request.base.ref }}\n          name: bundle_analysis.json\n          path: .next/analyze/base/bundle\n\n      # And here's the second place - this runs after we have both the current and\n      # base branch bundle stats, and will compare them to determine what changed.\n      # There are two configurable arguments that come from package.json:\n      #\n      # - budget: optional, set a budget (bytes) against which size changes are measured\n      #           it's set to 350kb here by default, as informed by the following piece:\n      #           https://infrequently.org/2021/03/the-performance-inequality-gap/\n      #\n      # - red-status-percentage: sets the percent size increase where you get a red\n      #                          status indicator, defaults to 20%\n      #\n      # Either of these arguments can be changed or removed by editing the `nextBundleAnalysis`\n      # entry in your package.json file.\n      - name: Compare with base branch bundle\n        if: success() && github.event.number\n        run: ls -laR .next/analyze/base && npx -p nextjs-bundle-analysis compare\n\n      - name: Upload analysis comment\n        uses: actions/upload-artifact@v4\n        with:\n          name: analysis_comment.txt\n          path: .next/analyze/__bundle_analysis_comment.txt\n\n      - name: Save PR number\n        run: echo ${{ github.event.number }} > ./pr_number\n\n      - name: Upload PR number\n        uses: actions/upload-artifact@v4\n        with:\n          name: pr_number\n          path: ./pr_number\n\n      # The actual commenting happens in the other action, matching the guidance in\n      # https://securitylab.github.com/research/github-actions-preventing-pwn-requests/\n"
  },
  {
    "path": ".github/workflows/analyze_comment.yml",
    "content": "name: Analyze Bundle (Comment)\n\non:\n  workflow_run:\n    workflows: ['Analyze Bundle']\n    types:\n      - completed\n\npermissions:\n  contents: read\n  issues: write\n  pull-requests: write\n  \njobs:\n  comment:\n    runs-on: ubuntu-latest\n    if: >\n      ${{ github.event.workflow_run.event == 'pull_request' &&\n      github.event.workflow_run.conclusion == 'success' }}\n    steps:\n      - name: Download base branch bundle stats\n        uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e\n        with:\n          workflow: analyze.yml\n          run_id: ${{ github.event.workflow_run.id }}\n          name: analysis_comment.txt\n          path: analysis_comment.txt\n\n      - name: Download PR number\n        uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e\n        with:\n          workflow: analyze.yml\n          run_id: ${{ github.event.workflow_run.id }}\n          name: pr_number\n          path: pr_number\n\n      - name: Get comment body\n        id: get-comment-body\n        if: success()\n        run: |\n          echo 'body<<EOF' >> $GITHUB_OUTPUT\n          echo '' >>  $GITHUB_OUTPUT\n          echo '## Size changes' >>  $GITHUB_OUTPUT\n          echo '' >>  $GITHUB_OUTPUT\n          echo '<details>' >>  $GITHUB_OUTPUT\n          echo '' >>  $GITHUB_OUTPUT\n          cat analysis_comment.txt/__bundle_analysis_comment.txt >> $GITHUB_OUTPUT\n          echo '' >>  $GITHUB_OUTPUT\n          echo '</details>' >>  $GITHUB_OUTPUT\n          echo '' >>  $GITHUB_OUTPUT\n          echo 'EOF' >> $GITHUB_OUTPUT\n          pr_number=$(cat pr_number/pr_number)\n          echo \"pr-number=$pr_number\" >> $GITHUB_OUTPUT\n\n      - name: Comment\n        uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728\n        with:\n          header: next-bundle-analysis\n          number: ${{ steps.get-comment-body.outputs.pr-number }}\n          message: ${{ steps.get-comment-body.outputs.body }}\n"
  },
  {
    "path": ".github/workflows/discord_notify.yml",
    "content": "name: Discord Notify\n\non:\n  pull_request_target:\n    types: [opened, ready_for_review]\n\npermissions: {}\n\njobs:\n  check_maintainer:\n    uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main\n    permissions:\n      # Used by check_maintainer\n      contents: read\n    with:\n      actor: ${{ github.event.pull_request.user.login }}\n\n  notify:\n    if: ${{ needs.check_maintainer.outputs.is_core_team == 'true' }}\n    needs: check_maintainer\n    runs-on: ubuntu-latest\n    steps:\n      - name: Discord Webhook Action\n        uses: tsickert/discord-webhook@v6.0.0\n        with:\n          webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}\n          embed-author-name: ${{ github.event.pull_request.user.login }}\n          embed-author-url: ${{ github.event.pull_request.user.html_url }}\n          embed-author-icon-url: ${{ github.event.pull_request.user.avatar_url }}\n          embed-title: '#${{ github.event.number }} (+${{github.event.pull_request.additions}} -${{github.event.pull_request.deletions}}): ${{ github.event.pull_request.title }}'\n          embed-description: ${{ github.event.pull_request.body }}\n          embed-url: ${{ github.event.pull_request.html_url }}\n"
  },
  {
    "path": ".github/workflows/label_core_team_prs.yml",
    "content": "name: Label Core Team PRs\n\non:\n  pull_request_target:\n\npermissions: {}\n\nenv:\n  TZ: /usr/share/zoneinfo/America/Los_Angeles\n  # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout\n  SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1\n\njobs:\n  check_maintainer:\n    uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main\n    permissions:\n      # Used by check_maintainer\n      contents: read\n    with:\n      actor: ${{ github.event.pull_request.user.login }}\n\n  label:\n    if: ${{ needs.check_maintainer.outputs.is_core_team == 'true' }}\n    runs-on: ubuntu-latest\n    needs: check_maintainer\n    permissions:\n      # Used to add labels on issues\n      issues: write\n      # Used to add labels on PRs\n      pull-requests: write\n    steps:\n      - name: Label PR as React Core Team\n        uses: actions/github-script@v7\n        with:\n          script: |\n            github.rest.issues.addLabels({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              issue_number: ${{ github.event.number }},\n              labels: ['React Core Team']\n            });\n"
  },
  {
    "path": ".github/workflows/site_lint.yml",
    "content": "name: Site Lint / Heading ID check\n\non:\n  push:\n    branches:\n      - main # change this if your default branch is named differently\n  pull_request:\n    types: [opened, synchronize, reopened]\n\npermissions: {}\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n\n    name: Lint on node 20.x and ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v4\n      - name: Use Node.js 20.x\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20.x\n          cache: yarn\n          cache-dependency-path: yarn.lock\n\n      - name: Restore cached node_modules\n        uses: actions/cache@v4\n        with:\n          path: '**/node_modules'\n          key: node_modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}\n\n      - name: Install deps\n        run: yarn install --frozen-lockfile\n\n      - name: Lint codebase\n        run: yarn ci-check\n\n      - name: Textlint (Japanese)\n        run: yarn textlint"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\nnode_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\ntsconfig.tsbuildinfo\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# vercel\n.vercel\n\n# external fonts\npublic/fonts/**/Optimistic_*.woff2\n\n# rss\npublic/rss.xml\n\n# claude local settings\n.claude/*.local.*\n.claude/react/\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nyarn lint-staged"
  },
  {
    "path": ".prettierignore",
    "content": "src/content/**/*.md\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"bracketSpacing\": false,\n  \"singleQuote\": true,\n  \"bracketSameLine\": true,\n  \"trailingComma\": \"es5\",\n  \"printWidth\": 80,\n  \"overrides\": [\n    {\n      \"files\": \"*.css\",\n      \"options\": {\n        \"parser\": \"css\"\n      }\n    },\n    {\n      \"files\": \"*.md\",\n      \"options\": {\n        \"parser\": \"mdx\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code when working with this repository.\n\n## Project Overview\n\nThis is the React documentation website (react.dev), built with Next.js 15.1.11 and React 19. Documentation is written in MDX format.\n\n## Development Commands\n\n```bash\nyarn build         # Production build\nyarn lint          # Run ESLint\nyarn lint:fix      # Auto-fix lint issues\nyarn tsc           # TypeScript type checking\nyarn check-all     # Run prettier, lint:fix, tsc, and rss together\n```\n\n## Project Structure\n\n```\nsrc/\n├── content/           # Documentation content (MDX files)\n│   ├── learn/         # Tutorial/learning content\n│   ├── reference/     # API reference docs\n│   ├── blog/          # Blog posts\n│   └── community/     # Community pages\n├── components/        # React components\n├── pages/             # Next.js pages\n├── hooks/             # Custom React hooks\n├── utils/             # Utility functions\n└── styles/            # CSS/Tailwind styles\n```\n\n## Code Conventions\n\n### TypeScript/React\n- Functional components only\n- Tailwind CSS for styling\n\n### Documentation Style\n\nWhen editing files in `src/content/`, the appropriate skill will be auto-suggested:\n- `src/content/learn/` - Learn page structure and tone\n- `src/content/reference/` - Reference page structure and tone\n\nFor MDX components (DeepDive, Pitfall, Note, etc.), invoke `/docs-components`.\nFor Sandpack code examples, invoke `/docs-sandpack`.\n\nSee `.claude/docs/react-docs-patterns.md` for comprehensive style guidelines.\n\nPrettier is used for formatting (config in `.prettierrc`).\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to make participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies within all project spaces, and it also applies when\nan individual is representing the project or its community in public spaces.\nExamples of representing a project or community include using an official\nproject e-mail address, posting via an official social media account, or acting\nas an appointed representative at an online or offline event. Representation of\na project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at <opensource-conduct@fb.com>. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nThank you for your interest in contributing to the React Docs!\n\n## Code of Conduct\n\nFacebook has adopted a Code of Conduct that we expect project\nparticipants to adhere to. Please [read the full text](https://code.facebook.com/codeofconduct)\nso that you can understand what actions will and will not be tolerated.\n\n## Technical Writing Tips\n\nThis is a [good summary](https://medium.com/@kvosswinkel/coding-like-a-journalist-ee52360a16bc) for things to keep in mind when writing technical docs.\n\n## Guidelines for Text\n\n**Different sections intentionally have different styles.**\n\nThe documentation is divided into sections to cater to different learning styles and use cases. When editing an article, try to match the surrounding text in tone and style. When creating a new article, try to match the tone of the other articles in the same section. Learn about the motivation behind each section below.\n\n**[Learn React](https://react.dev/learn)** is designed to introduce fundamental concepts in a step-by-step way. Each individual article in Learn React builds on the knowledge from the previous ones, so make sure not to add any \"cyclical dependencies\" between them. It is important that the reader can start with the first article and work their way to the last Learn React article without ever having to \"look ahead\" for a definition. This explains some ordering choices (e.g. that state is explained before events, or that \"thinking in React\" doesn't use refs). Learn React also serves as a reference manual for React concepts, so it is important to be very strict about their definitions and relationships between them.\n\n**[API Reference](https://react.dev/reference/react)** is organized by APIs rather than concepts. It is intended to be exhaustive. Any corner cases or recommendations that were skipped for brevity in Learn React should be mentioned in the reference documentation for the corresponding APIs.\n\n**Try to follow your own instructions.**\n\nWhen writing step-by-step instructions (e.g. how to install something), try to forget everything you know about the topic, and actually follow the instructions you wrote, a single step at time. Often you will discover that there is implicit knowledge that you forgot to mention, or that there are missing or out-of-order steps in the instructions. Bonus points for getting *somebody else* to follow the steps and watching what they struggle with. Often it would be something very simple that you have not anticipated.\n\n## Guidelines for Code Examples\n\n### Syntax\n\n#### Prefer JSX to `createElement`.\n\nIgnore this if you're specifically describing `createElement`.\n\n#### Use `const` where possible, otherwise `let`. Don't use `var`.\n\nIgnore this if you're specifically writing about ES5.\n\n#### Don't use ES6 features when equivalent ES5 features have no downsides.\n\nRemember that ES6 is still new to a lot of people. While we use it in many places (`const` / `let`, classes, arrow functions), if the equivalent ES5 code is just as straightforward and readable, consider using it.\n\nIn particular, you should prefer named `function` declarations over `const myFunction = () => ...` arrows for top-level functions. However, you *should* use arrow functions where they provide a tangible improvement (such as preserving `this` context inside a component). Consider both sides of the tradeoff when deciding whether to use a new feature.\n\n#### Don't use features that aren't standardized yet.\n\nFor example, **don't** write this:\n\n```js\nclass MyComponent extends React.Component {\n  state = {value: ''};\n  handleChange = (e) => {\n    this.setState({value: e.target.value});\n  };\n}\n```\n\nInstead, **do** write this:\n\n```js\nclass MyComponent extends React.Component {\n  constructor(props) {\n    super(props);\n    this.handleChange = this.handleChange.bind(this);\n    this.state = {value: ''};\n  }\n  handleChange(e) {\n    this.setState({value: e.target.value});\n  }\n}\n```\n\nIgnore this rule if you're specifically describing an experimental proposal. Make sure to mention its experimental nature in the code and in the surrounding text.\n\n### Style\n\n- Use semicolons.\n- No space between function names and parens (`method() {}` not `method () {}`).\n- When in doubt, use the default style favored by [Prettier](https://prettier.io/playground/).\n- Always capitalize React concepts such as Hooks, Effects, and Transitions.\n\n### Highlighting\n\nUse `js` as the highlighting language in Markdown code blocks:\n\n````\n```js\n// code\n```\n````\n\nSometimes you'll see blocks with numbers.  \nThey tell the website to highlight specific lines.\n\nYou can highlight a single line:\n\n````\n```js {2}\nfunction hello() {\n  // this line will get highlighted\n}\n```\n````\n\nA range of lines:\n\n````\n```js {2-4}\nfunction hello() {\n  // these lines\n  // will get\n  // highlighted\n}\n```\n````\n\nOr even multiple ranges:\n\n````\n```js {2-4,6}\nfunction hello() {\n  // these lines\n  // will get\n  // highlighted\n  console.log('hello');\n  // also this one\n  console.log('there');\n}\n```\n````\n\nBe mindful that if you move some code in an example with highlighting, you also need to update the highlighting.\n\nDon't be afraid to often use highlighting! It is very valuable when you need to focus the reader's attention on a particular detail that's easy to miss.\n"
  },
  {
    "path": "LICENSE-DOCS.md",
    "content": "Attribution 4.0 International\n\n=======================================================================\n\nCreative Commons Corporation (\"Creative Commons\") is not a law firm and\ndoes not provide legal services or legal advice. Distribution of\nCreative Commons public licenses does not create a lawyer-client or\nother relationship. Creative Commons makes its licenses and related\ninformation available on an \"as-is\" basis. Creative Commons gives no\nwarranties regarding its licenses, any material licensed under their\nterms and conditions, or any related information. Creative Commons\ndisclaims all liability for damages resulting from their use to the\nfullest extent possible.\n\nUsing Creative Commons Public Licenses\n\nCreative Commons public licenses provide a standard set of terms and\nconditions that creators and other rights holders may use to share\noriginal works of authorship and other material subject to copyright\nand certain other rights specified in the public license below. The\nfollowing considerations are for informational purposes only, are not\nexhaustive, and do not form part of our licenses.\n\n     Considerations for licensors: Our public licenses are\n     intended for use by those authorized to give the public\n     permission to use material in ways otherwise restricted by\n     copyright and certain other rights. Our licenses are\n     irrevocable. Licensors should read and understand the terms\n     and conditions of the license they choose before applying it.\n     Licensors should also secure all rights necessary before\n     applying our licenses so that the public can reuse the\n     material as expected. Licensors should clearly mark any\n     material not subject to the license. This includes other CC-\n     licensed material, or material used under an exception or\n     limitation to copyright. More considerations for licensors:\n  wiki.creativecommons.org/Considerations_for_licensors\n\n     Considerations for the public: By using one of our public\n     licenses, a licensor grants the public permission to use the\n     licensed material under specified terms and conditions. If\n     the licensor's permission is not necessary for any reason--for\n     example, because of any applicable exception or limitation to\n     copyright--then that use is not regulated by the license. Our\n     licenses grant only permissions under copyright and certain\n     other rights that a licensor has authority to grant. Use of\n     the licensed material may still be restricted for other\n     reasons, including because others have copyright or other\n     rights in the material. A licensor may make special requests,\n     such as asking that all changes be marked or described.\n     Although not required by our licenses, you are encouraged to\n     respect those requests where reasonable. More_considerations\n     for the public:\n  wiki.creativecommons.org/Considerations_for_licensees\n\n=======================================================================\n\nCreative Commons Attribution 4.0 International Public License\n\nBy exercising the Licensed Rights (defined below), You accept and agree\nto be bound by the terms and conditions of this Creative Commons\nAttribution 4.0 International Public License (\"Public License\"). To the\nextent this Public License may be interpreted as a contract, You are\ngranted the Licensed Rights in consideration of Your acceptance of\nthese terms and conditions, and the Licensor grants You such rights in\nconsideration of benefits the Licensor receives from making the\nLicensed Material available under these terms and conditions.\n\n\nSection 1 -- Definitions.\n\n  a. Adapted Material means material subject to Copyright and Similar\n     Rights that is derived from or based upon the Licensed Material\n     and in which the Licensed Material is translated, altered,\n     arranged, transformed, or otherwise modified in a manner requiring\n     permission under the Copyright and Similar Rights held by the\n     Licensor. For purposes of this Public License, where the Licensed\n     Material is a musical work, performance, or sound recording,\n     Adapted Material is always produced where the Licensed Material is\n     synched in timed relation with a moving image.\n\n  b. Adapter's License means the license You apply to Your Copyright\n     and Similar Rights in Your contributions to Adapted Material in\n     accordance with the terms and conditions of this Public License.\n\n  c. Copyright and Similar Rights means copyright and/or similar rights\n     closely related to copyright including, without limitation,\n     performance, broadcast, sound recording, and Sui Generis Database\n     Rights, without regard to how the rights are labeled or\n     categorized. For purposes of this Public License, the rights\n     specified in Section 2(b)(1)-(2) are not Copyright and Similar\n     Rights.\n\n  d. Effective Technological Measures means those measures that, in the\n     absence of proper authority, may not be circumvented under laws\n     fulfilling obligations under Article 11 of the WIPO Copyright\n     Treaty adopted on December 20, 1996, and/or similar international\n     agreements.\n\n  e. Exceptions and Limitations means fair use, fair dealing, and/or\n     any other exception or limitation to Copyright and Similar Rights\n     that applies to Your use of the Licensed Material.\n\n  f. Licensed Material means the artistic or literary work, database,\n     or other material to which the Licensor applied this Public\n     License.\n\n  g. Licensed Rights means the rights granted to You subject to the\n     terms and conditions of this Public License, which are limited to\n     all Copyright and Similar Rights that apply to Your use of the\n     Licensed Material and that the Licensor has authority to license.\n\n  h. Licensor means the individual(s) or entity(ies) granting rights\n     under this Public License.\n\n  i. Share means to provide material to the public by any means or\n     process that requires permission under the Licensed Rights, such\n     as reproduction, public display, public performance, distribution,\n     dissemination, communication, or importation, and to make material\n     available to the public including in ways that members of the\n     public may access the material from a place and at a time\n     individually chosen by them.\n\n  j. Sui Generis Database Rights means rights other than copyright\n     resulting from Directive 96/9/EC of the European Parliament and of\n     the Council of 11 March 1996 on the legal protection of databases,\n     as amended and/or succeeded, as well as other essentially\n     equivalent rights anywhere in the world.\n\n  k. You means the individual or entity exercising the Licensed Rights\n     under this Public License. Your has a corresponding meaning.\n\n\nSection 2 -- Scope.\n\n  a. License grant.\n\n       1. Subject to the terms and conditions of this Public License,\n          the Licensor hereby grants You a worldwide, royalty-free,\n          non-sublicensable, non-exclusive, irrevocable license to\n          exercise the Licensed Rights in the Licensed Material to:\n\n            a. reproduce and Share the Licensed Material, in whole or\n               in part; and\n\n            b. produce, reproduce, and Share Adapted Material.\n\n       2. Exceptions and Limitations. For the avoidance of doubt, where\n          Exceptions and Limitations apply to Your use, this Public\n          License does not apply, and You do not need to comply with\n          its terms and conditions.\n\n       3. Term. The term of this Public License is specified in Section\n          6(a).\n\n       4. Media and formats; technical modifications allowed. The\n          Licensor authorizes You to exercise the Licensed Rights in\n          all media and formats whether now known or hereafter created,\n          and to make technical modifications necessary to do so. The\n          Licensor waives and/or agrees not to assert any right or\n          authority to forbid You from making technical modifications\n          necessary to exercise the Licensed Rights, including\n          technical modifications necessary to circumvent Effective\n          Technological Measures. For purposes of this Public License,\n          simply making modifications authorized by this Section 2(a)\n          (4) never produces Adapted Material.\n\n       5. Downstream recipients.\n\n            a. Offer from the Licensor -- Licensed Material. Every\n               recipient of the Licensed Material automatically\n               receives an offer from the Licensor to exercise the\n               Licensed Rights under the terms and conditions of this\n               Public License.\n\n            b. No downstream restrictions. You may not offer or impose\n               any additional or different terms or conditions on, or\n               apply any Effective Technological Measures to, the\n               Licensed Material if doing so restricts exercise of the\n               Licensed Rights by any recipient of the Licensed\n               Material.\n\n       6. No endorsement. Nothing in this Public License constitutes or\n          may be construed as permission to assert or imply that You\n          are, or that Your use of the Licensed Material is, connected\n          with, or sponsored, endorsed, or granted official status by,\n          the Licensor or others designated to receive attribution as\n          provided in Section 3(a)(1)(A)(i).\n\n  b. Other rights.\n\n       1. Moral rights, such as the right of integrity, are not\n          licensed under this Public License, nor are publicity,\n          privacy, and/or other similar personality rights; however, to\n          the extent possible, the Licensor waives and/or agrees not to\n          assert any such rights held by the Licensor to the limited\n          extent necessary to allow You to exercise the Licensed\n          Rights, but not otherwise.\n\n       2. Patent and trademark rights are not licensed under this\n          Public License.\n\n       3. To the extent possible, the Licensor waives any right to\n          collect royalties from You for the exercise of the Licensed\n          Rights, whether directly or through a collecting society\n          under any voluntary or waivable statutory or compulsory\n          licensing scheme. In all other cases the Licensor expressly\n          reserves any right to collect such royalties.\n\n\nSection 3 -- License Conditions.\n\nYour exercise of the Licensed Rights is expressly made subject to the\nfollowing conditions.\n\n  a. Attribution.\n\n       1. If You Share the Licensed Material (including in modified\n          form), You must:\n\n            a. retain the following if it is supplied by the Licensor\n               with the Licensed Material:\n\n                 i. identification of the creator(s) of the Licensed\n                    Material and any others designated to receive\n                    attribution, in any reasonable manner requested by\n                    the Licensor (including by pseudonym if\n                    designated);\n\n                ii. a copyright notice;\n\n               iii. a notice that refers to this Public License;\n\n                iv. a notice that refers to the disclaimer of\n                    warranties;\n\n                 v. a URI or hyperlink to the Licensed Material to the\n                    extent reasonably practicable;\n\n            b. indicate if You modified the Licensed Material and\n               retain an indication of any previous modifications; and\n\n            c. indicate the Licensed Material is licensed under this\n               Public License, and include the text of, or the URI or\n               hyperlink to, this Public License.\n\n       2. You may satisfy the conditions in Section 3(a)(1) in any\n          reasonable manner based on the medium, means, and context in\n          which You Share the Licensed Material. For example, it may be\n          reasonable to satisfy the conditions by providing a URI or\n          hyperlink to a resource that includes the required\n          information.\n\n       3. If requested by the Licensor, You must remove any of the\n          information required by Section 3(a)(1)(A) to the extent\n          reasonably practicable.\n\n       4. If You Share Adapted Material You produce, the Adapter's\n          License You apply must not prevent recipients of the Adapted\n          Material from complying with this Public License.\n\n\nSection 4 -- Sui Generis Database Rights.\n\nWhere the Licensed Rights include Sui Generis Database Rights that\napply to Your use of the Licensed Material:\n\n  a. for the avoidance of doubt, Section 2(a)(1) grants You the right\n     to extract, reuse, reproduce, and Share all or a substantial\n     portion of the contents of the database;\n\n  b. if You include all or a substantial portion of the database\n     contents in a database in which You have Sui Generis Database\n     Rights, then the database in which You have Sui Generis Database\n     Rights (but not its individual contents) is Adapted Material; and\n\n  c. You must comply with the conditions in Section 3(a) if You Share\n     all or a substantial portion of the contents of the database.\n\nFor the avoidance of doubt, this Section 4 supplements and does not\nreplace Your obligations under this Public License where the Licensed\nRights include other Copyright and Similar Rights.\n\n\nSection 5 -- Disclaimer of Warranties and Limitation of Liability.\n\n  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE\n     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS\n     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF\n     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,\n     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,\n     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR\n     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,\n     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT\n     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT\n     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.\n\n  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE\n     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,\n     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,\n     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,\n     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR\n     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN\n     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR\n     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR\n     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.\n\n  c. The disclaimer of warranties and limitation of liability provided\n     above shall be interpreted in a manner that, to the extent\n     possible, most closely approximates an absolute disclaimer and\n     waiver of all liability.\n\n\nSection 6 -- Term and Termination.\n\n  a. This Public License applies for the term of the Copyright and\n     Similar Rights licensed here. However, if You fail to comply with\n     this Public License, then Your rights under this Public License\n     terminate automatically.\n\n  b. Where Your right to use the Licensed Material has terminated under\n     Section 6(a), it reinstates:\n\n       1. automatically as of the date the violation is cured, provided\n          it is cured within 30 days of Your discovery of the\n          violation; or\n\n       2. upon express reinstatement by the Licensor.\n\n     For the avoidance of doubt, this Section 6(b) does not affect any\n     right the Licensor may have to seek remedies for Your violations\n     of this Public License.\n\n  c. For the avoidance of doubt, the Licensor may also offer the\n     Licensed Material under separate terms or conditions or stop\n     distributing the Licensed Material at any time; however, doing so\n     will not terminate this Public License.\n\n  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public\n     License.\n\n\nSection 7 -- Other Terms and Conditions.\n\n  a. The Licensor shall not be bound by any additional or different\n     terms or conditions communicated by You unless expressly agreed.\n\n  b. Any arrangements, understandings, or agreements regarding the\n     Licensed Material not stated herein are separate from and\n     independent of the terms and conditions of this Public License.\n\n\nSection 8 -- Interpretation.\n\n  a. For the avoidance of doubt, this Public License does not, and\n     shall not be interpreted to, reduce, limit, restrict, or impose\n     conditions on any use of the Licensed Material that could lawfully\n     be made without permission under this Public License.\n\n  b. To the extent possible, if any provision of this Public License is\n     deemed unenforceable, it shall be automatically reformed to the\n     minimum extent necessary to make it enforceable. If the provision\n     cannot be reformed, it shall be severed from this Public License\n     without affecting the enforceability of the remaining terms and\n     conditions.\n\n  c. No term or condition of this Public License will be waived and no\n     failure to comply consented to unless expressly agreed to by the\n     Licensor.\n\n  d. Nothing in this Public License constitutes or may be interpreted\n     as a limitation upon, or waiver of, any privileges and immunities\n     that apply to the Licensor or You, including from the legal\n     processes of any jurisdiction or authority.\n\n\n=======================================================================\n\nCreative Commons is not a party to its public licenses.\nNotwithstanding, Creative Commons may elect to apply one of its public\nlicenses to material it publishes and in those instances will be\nconsidered the \"Licensor.\" Except for the limited purpose of indicating\nthat material is shared under a Creative Commons public license or as\notherwise permitted by the Creative Commons policies published at\ncreativecommons.org/policies, Creative Commons does not authorize the\nuse of the trademark \"Creative Commons\" or any other trademark or logo\nof Creative Commons without its prior written consent including,\nwithout limitation, in connection with any unauthorized modifications\nto any of its public licenses or any other arrangements,\nunderstandings, or agreements concerning use of licensed material. For\nthe avoidance of doubt, this paragraph does not form part of the public\nlicenses.\n\nCreative Commons may be contacted at creativecommons.org.\n"
  },
  {
    "path": "README.md",
    "content": "# react.dev\n\nThis repo contains the source code and documentation powering [react.dev](https://react.dev/).\n\n> 以下はオリジナル（英語版）リポジトリの README です。日本語版 React ドキュメントへの貢献（翻訳・修正など）に興味がある方は[こちらの Wiki ページ](https://github.com/reactjs/ja.react.dev/wiki)を参照してください。\n\n## Getting started\n\n### Prerequisites\n\n1. Git\n1. Node: any version starting with v16.8.0 or greater\n1. Yarn: See [Yarn website for installation instructions](https://yarnpkg.com/lang/en/docs/install/)\n1. A fork of the repo (for any contributions)\n1. A clone of the [react.dev repo](https://github.com/reactjs/react.dev) on your local machine\n\n### Installation\n\n1. `cd react.dev` to go into the project root\n3. `yarn` to install the website's npm dependencies\n\n### Running locally\n\n1. `yarn dev` to start the development server (powered by [Next.js](https://nextjs.org/))\n1. `open http://localhost:3000` to open the site in your favorite browser\n\n## Contributing\n\n### Guidelines\n\nThe documentation is divided into several sections with a different tone and purpose. If you plan to write more than a few sentences, you might find it helpful to get familiar with the [contributing guidelines](https://github.com/reactjs/react.dev/blob/main/CONTRIBUTING.md#guidelines-for-text) for the appropriate sections.\n\n### Create a branch\n\n1. `git checkout main` from any folder in your local `react.dev` repository\n1. `git pull origin main` to ensure you have the latest main code\n1. `git checkout -b the-name-of-my-branch` (replacing `the-name-of-my-branch` with a suitable name) to create a branch\n\n### Make the change\n\n1. Follow the [\"Running locally\"](#running-locally) instructions\n1. Save the files and check in the browser\n  1. Changes to React components in `src` will hot-reload\n  1. Changes to markdown files in `content` will hot-reload\n  1. If working with plugins, you may need to remove the `.cache` directory and restart the server\n\n### Test the change\n\n1. If possible, test any visual changes in all latest versions of common browsers, on both desktop and mobile.\n2. Run `yarn check-all`. (This will run Prettier, ESLint and validate types.)\n\n### Push it\n\n1. `git add -A && git commit -m \"My message\"` (replacing `My message` with a commit message, such as `Fix header logo on Android`) to stage and commit your changes\n1. `git push my-fork-name the-name-of-my-branch`\n1. Go to the [react.dev repo](https://github.com/reactjs/react.dev) and you should see recently pushed branches.\n1. Follow GitHub's instructions.\n1. If possible, include screenshots of visual changes. A preview build is triggered after your changes are pushed to GitHub.\n\n## Translation\n\nIf you are interested in translating `react.dev`, please see the current translation efforts [here](https://github.com/reactjs/react.dev/issues/4135).\n\n## License\nContent submitted to [react.dev](https://react.dev/) is CC-BY-4.0 licensed, as found in the [LICENSE-DOCS.md](https://github.com/reactjs/react.dev/blob/main/LICENSE-DOCS.md) file."
  },
  {
    "path": "colors.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nmodule.exports = {\n  // Text colors\n  primary: '#23272F', // gray-90\n  'primary-dark': '#F6F7F9', // gray-5\n  secondary: '#404756', // gray-70\n  'secondary-dark': '#EBECF0', // gray-10\n  tertiary: '#5E687E', // gray-50\n  'tertiary-dark': '#99A1B3', // gray-30\n  link: '#087EA4', // blue-50\n  'link-dark': '#58C4DC', // blue-40\n  syntax: '#EBECF0', // gray-10\n  wash: '#FFFFFF',\n  'wash-dark': '#23272F', // gray-90\n  card: '#F6F7F9', // gray-05\n  'card-dark': '#343A46', // gray-80\n  highlight: '#E6F7FF', // blue-10\n  'highlight-dark': 'rgba(88,175,223,.1)',\n  border: '#EBECF0', // gray-10\n  'border-dark': '#343A46', // gray-80\n  'secondary-button': '#EBECF0', // gray-10\n  'secondary-button-dark': '#404756', // gray-70\n  brand: '#087EA4', // blue-40\n  'brand-dark': '#58C4DC', // blue-40\n\n  // Gray\n  'gray-95': '#16181D',\n  'gray-90': '#23272F',\n  'gray-80': '#343A46',\n  'gray-70': '#404756',\n  'gray-60': '#4E5769',\n  'gray-50': '#5E687E',\n  'gray-40': '#78839B',\n  'gray-30': '#99A1B3',\n  'gray-20': '#BCC1CD',\n  'gray-15': '#D0D3DC',\n  'gray-10': '#EBECF0',\n  'gray-5': '#F6F7F9',\n\n  // Blue\n  'blue-80': '#043849',\n  'blue-60': '#045975',\n  'blue-50': '#087EA4',\n  'blue-40': '#149ECA', // Brand Blue\n  'blue-30': '#58C4DC', // unused\n  'blue-20': '#ABE2ED',\n  'blue-10': '#E6F7FF', // todo: doesn't match illustrations\n  'blue-5': '#E6F6FA',\n\n  // Yellow\n  'yellow-60': '#B65700',\n  'yellow-50': '#C76A15',\n  'yellow-40': '#DB7D27', // unused\n  'yellow-30': '#FABD62', // unused\n  'yellow-20': '#FCDEB0', // unused\n  'yellow-10': '#FDE7C7',\n  'yellow-5': '#FEF5E7',\n\n  // Purple\n  'purple-60': '#2B3491', // unused\n  'purple-50': '#575FB7',\n  'purple-40': '#6B75DB',\n  'purple-30': '#8891EC',\n  'purple-20': '#C3C8F5', // unused\n  'purple-10': '#E7E9FB',\n  'purple-5': '#F3F4FD',\n\n  // Green\n  'green-60': '#2B6E62',\n  'green-50': '#388F7F',\n  'green-40': '#44AC99',\n  'green-30': '#7FCCBF',\n  'green-20': '#ABDED5',\n  'green-10': '#E5F5F2',\n  'green-5': '#F4FBF9',\n\n  // RED\n  'red-60': '#712D28',\n  'red-50': '#A6423A', // unused\n  'red-40': '#C1554D',\n  'red-30': '#D07D77',\n  'red-20': '#E5B7B3', // unused\n  'red-10': '#F2DBD9', // unused\n  'red-5': '#FAF1F0',\n\n  // MISC\n  'code-block': '#99a1b30f', // gray-30 @ 6%\n  'gradient-blue': '#58C4DC', // Only used for the landing gradient for now.\n  github: {\n    highlight: '#fffbdd',\n  },\n};\n"
  },
  {
    "path": "eslint-local-rules/__tests__/fixtures/src/content/basic-error.md",
    "content": "```jsx\nimport {useState} from 'react';\nfunction Counter() {\n  const [count, setCount] = useState(0);\n  setCount(count + 1);\n  return <div>{count}</div>;\n}\n```\n"
  },
  {
    "path": "eslint-local-rules/__tests__/fixtures/src/content/duplicate-metadata.md",
    "content": "```jsx title=\"Counter\" {expectedErrors: {'react-compiler': [99]}} {expectedErrors: {'react-compiler': [2]}}\nimport {useState} from 'react';\nfunction Counter() {\n  const [count, setCount] = useState(0);\n  setCount(count + 1);\n  return <div>{count}</div>;\n}\n```\n"
  },
  {
    "path": "eslint-local-rules/__tests__/fixtures/src/content/malformed-metadata.md",
    "content": "```jsx {expectedErrors: {'react-compiler': 'invalid'}}\nimport {useState} from 'react';\nfunction Counter() {\n  const [count, setCount] = useState(0);\n  setCount(count + 1);\n  return <div>{count}</div>;\n}\n```\n"
  },
  {
    "path": "eslint-local-rules/__tests__/fixtures/src/content/mixed-language.md",
    "content": "```bash\nsetCount()\n```\n\n```txt\nimport {useState} from 'react';\n```\n"
  },
  {
    "path": "eslint-local-rules/__tests__/fixtures/src/content/stale-expected-error.md",
    "content": "```jsx {expectedErrors: {'react-compiler': [3]}}\nfunction Hello() {\n  return <h1>Hello</h1>;\n}\n```\n"
  },
  {
    "path": "eslint-local-rules/__tests__/fixtures/src/content/suppressed-error.md",
    "content": "```jsx {expectedErrors: {'react-compiler': [4]}}\nimport {useState} from 'react';\nfunction Counter() {\n  const [count, setCount] = useState(0);\n  setCount(count + 1);\n  return <div>{count}</div>;\n}\n```\n"
  },
  {
    "path": "eslint-local-rules/__tests__/lint-markdown-code-blocks.test.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nconst assert = require('assert');\nconst fs = require('fs');\nconst path = require('path');\nconst {ESLint} = require('eslint');\nconst plugin = require('..');\n\nconst FIXTURES_DIR = path.join(\n  __dirname,\n  'fixtures',\n  'src',\n  'content'\n);\nconst PARSER_PATH = path.join(__dirname, '..', 'parser.js');\n\nfunction createESLint({fix = false} = {}) {\n  return new ESLint({\n    useEslintrc: false,\n    fix,\n    plugins: {\n      'local-rules': plugin,\n    },\n    overrideConfig: {\n      parser: PARSER_PATH,\n      plugins: ['local-rules'],\n      rules: {\n        'local-rules/lint-markdown-code-blocks': 'error',\n      },\n      parserOptions: {\n        sourceType: 'module',\n      },\n    },\n  });\n}\n\nfunction readFixture(name) {\n  return fs.readFileSync(path.join(FIXTURES_DIR, name), 'utf8');\n}\n\nasync function lintFixture(name, {fix = false} = {}) {\n  const eslint = createESLint({fix});\n  const filePath = path.join(FIXTURES_DIR, name);\n  const markdown = readFixture(name);\n  const [result] = await eslint.lintText(markdown, {filePath});\n  return result;\n}\n\nasync function run() {\n  const basicResult = await lintFixture('basic-error.md');\n  assert.strictEqual(\n    basicResult.messages.length,\n    1,\n    'expected one diagnostic'\n  );\n  assert(\n    basicResult.messages[0].message.includes('Calling setState during render'),\n    'expected message to mention setState during render'\n  );\n\n  const suppressedResult = await lintFixture('suppressed-error.md');\n  assert.strictEqual(\n    suppressedResult.messages.length,\n    0,\n    'expected suppression metadata to silence diagnostic'\n  );\n\n  const staleResult = await lintFixture('stale-expected-error.md');\n  assert.strictEqual(\n    staleResult.messages.length,\n    1,\n    'expected stale metadata error'\n  );\n  assert.strictEqual(\n    staleResult.messages[0].message,\n    'React Compiler expected error on line 3 was not triggered'\n  );\n\n  const duplicateResult = await lintFixture('duplicate-metadata.md');\n  assert.strictEqual(\n    duplicateResult.messages.length,\n    2,\n    'expected duplicate metadata to surface compiler diagnostic and stale metadata notice'\n  );\n  const duplicateFixed = await lintFixture('duplicate-metadata.md', {\n    fix: true,\n  });\n  assert(\n    duplicateFixed.output.includes(\n      \"{expectedErrors: {'react-compiler': [4]}}\"\n    ),\n    'expected duplicates to be rewritten to a single canonical block'\n  );\n  assert(\n    !duplicateFixed.output.includes('[99]'),\n    'expected stale line numbers to be removed from metadata'\n  );\n\n  const mixedLanguageResult = await lintFixture('mixed-language.md');\n  assert.strictEqual(\n    mixedLanguageResult.messages.length,\n    0,\n    'expected non-js code fences to be ignored'\n  );\n\n  const malformedResult = await lintFixture('malformed-metadata.md');\n  assert.strictEqual(\n    malformedResult.messages.length,\n    1,\n    'expected malformed metadata to fall back to compiler diagnostics'\n  );\n  const malformedFixed = await lintFixture('malformed-metadata.md', {\n    fix: true,\n  });\n  assert(\n    malformedFixed.output.includes(\n      \"{expectedErrors: {'react-compiler': [4]}}\"\n    ),\n    'expected malformed metadata to be replaced with canonical form'\n  );\n}\n\nrun().catch(error => {\n  console.error(error);\n  process.exitCode = 1;\n});\n"
  },
  {
    "path": "eslint-local-rules/index.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nconst lintMarkdownCodeBlocks = require('./rules/lint-markdown-code-blocks');\n\nmodule.exports = {\n  rules: {\n    'lint-markdown-code-blocks': lintMarkdownCodeBlocks,\n  },\n};\n"
  },
  {
    "path": "eslint-local-rules/package.json",
    "content": "{\n  \"name\": \"eslint-plugin-local-rules\",\n  \"version\": \"0.0.0\",\n  \"main\": \"index.js\",\n  \"private\": \"true\",\n  \"scripts\": {\n    \"test\": \"node __tests__/lint-markdown-code-blocks.test.js\"\n  },\n  \"devDependencies\": {\n    \"eslint-mdx\": \"^2\"\n  }\n}\n"
  },
  {
    "path": "eslint-local-rules/parser.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nmodule.exports = require('eslint-mdx');\n"
  },
  {
    "path": "eslint-local-rules/rules/diagnostics.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nfunction getRelativeLine(loc) {\n  return loc?.start?.line ?? loc?.line ?? 1;\n}\n\nfunction getRelativeColumn(loc) {\n  return loc?.start?.column ?? loc?.column ?? 0;\n}\n\nfunction getRelativeEndLine(loc, fallbackLine) {\n  if (loc?.end?.line != null) {\n    return loc.end.line;\n  }\n  if (loc?.line != null) {\n    return loc.line;\n  }\n  return fallbackLine;\n}\n\nfunction getRelativeEndColumn(loc, fallbackColumn) {\n  if (loc?.end?.column != null) {\n    return loc.end.column;\n  }\n  if (loc?.column != null) {\n    return loc.column;\n  }\n  return fallbackColumn;\n}\n\n/**\n * @param {import('./markdown').MarkdownCodeBlock} block\n * @param {Array<{detail: any, loc: any, message: string}>} diagnostics\n * @returns {Array<{detail: any, message: string, relativeStartLine: number, markdownLoc: {start: {line: number, column: number}, end: {line: number, column: number}}}>}\n */\nfunction normalizeDiagnostics(block, diagnostics) {\n  return diagnostics.map(({detail, loc, message}) => {\n    const relativeStartLine = Math.max(getRelativeLine(loc), 1);\n    const relativeStartColumn = Math.max(getRelativeColumn(loc), 0);\n    const relativeEndLine = Math.max(\n      getRelativeEndLine(loc, relativeStartLine),\n      relativeStartLine\n    );\n    const relativeEndColumn = Math.max(\n      getRelativeEndColumn(loc, relativeStartColumn),\n      relativeStartColumn\n    );\n\n    const markdownStartLine = block.codeStartLine + relativeStartLine - 1;\n    const markdownEndLine = block.codeStartLine + relativeEndLine - 1;\n\n    return {\n      detail,\n      message,\n      relativeStartLine,\n      markdownLoc: {\n        start: {\n          line: markdownStartLine,\n          column: relativeStartColumn,\n        },\n        end: {\n          line: markdownEndLine,\n          column: relativeEndColumn,\n        },\n      },\n    };\n  });\n}\n\nmodule.exports = {\n  normalizeDiagnostics,\n};\n"
  },
  {
    "path": "eslint-local-rules/rules/lint-markdown-code-blocks.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nconst {\n  buildFenceLine,\n  getCompilerExpectedLines,\n  getSortedUniqueNumbers,\n  hasCompilerEntry,\n  metadataEquals,\n  metadataHasExpectedErrorsToken,\n  removeCompilerExpectedLines,\n  setCompilerExpectedLines,\n} = require('./metadata');\nconst {normalizeDiagnostics} = require('./diagnostics');\nconst {parseMarkdownFile} = require('./markdown');\nconst {runReactCompiler} = require('./react-compiler');\n\nmodule.exports = {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'Run React Compiler on markdown code blocks',\n      category: 'Possible Errors',\n    },\n    fixable: 'code',\n    hasSuggestions: true,\n    schema: [],\n  },\n\n  create(context) {\n    return {\n      Program(node) {\n        const filename = context.getFilename();\n        if (!filename.endsWith('.md') || !filename.includes('src/content')) {\n          return;\n        }\n\n        const sourceCode = context.getSourceCode();\n        const {blocks} = parseMarkdownFile(sourceCode.text, filename);\n        // For each supported code block, run the compiler and reconcile metadata.\n        for (const block of blocks) {\n          const compilerResult = runReactCompiler(\n            block.code,\n            `${filename}#codeblock`\n          );\n\n          const expectedLines = getCompilerExpectedLines(block.metadata);\n          const expectedLineSet = new Set(expectedLines);\n          const diagnostics = normalizeDiagnostics(\n            block,\n            compilerResult.diagnostics\n          );\n\n          const errorLines = new Set();\n          const unexpectedDiagnostics = [];\n\n          for (const diagnostic of diagnostics) {\n            const line = diagnostic.relativeStartLine;\n            errorLines.add(line);\n            if (!expectedLineSet.has(line)) {\n              unexpectedDiagnostics.push(diagnostic);\n            }\n          }\n\n          const normalizedErrorLines = getSortedUniqueNumbers(\n            Array.from(errorLines)\n          );\n          const missingExpectedLines = expectedLines.filter(\n            (line) => !errorLines.has(line)\n          );\n\n          const desiredMetadata = normalizedErrorLines.length\n            ? setCompilerExpectedLines(block.metadata, normalizedErrorLines)\n            : removeCompilerExpectedLines(block.metadata);\n\n          // Compute canonical metadata and attach an autofix when it differs.\n          const metadataChanged = !metadataEquals(\n            block.metadata,\n            desiredMetadata\n          );\n          const replacementLine = buildFenceLine(block.lang, desiredMetadata);\n          const replacementDiffers = block.fence.rawText !== replacementLine;\n          const applyReplacementFix = replacementDiffers\n            ? (fixer) =>\n                fixer.replaceTextRange(block.fence.range, replacementLine)\n            : null;\n\n          const hasDuplicateMetadata =\n            block.metadata.hadDuplicateExpectedErrors;\n          const hasExpectedErrorsMetadata = metadataHasExpectedErrorsToken(\n            block.metadata\n          );\n\n          const shouldFixUnexpected =\n            Boolean(applyReplacementFix) &&\n            normalizedErrorLines.length > 0 &&\n            (metadataChanged ||\n              hasDuplicateMetadata ||\n              !hasExpectedErrorsMetadata);\n\n          let fixAlreadyAttached = false;\n\n          for (const diagnostic of unexpectedDiagnostics) {\n            const reportData = {\n              node,\n              loc: diagnostic.markdownLoc,\n              message: diagnostic.message,\n            };\n\n            if (\n              shouldFixUnexpected &&\n              applyReplacementFix &&\n              !fixAlreadyAttached\n            ) {\n              reportData.fix = applyReplacementFix;\n              reportData.suggest = [\n                {\n                  desc: 'Add expectedErrors metadata to suppress these errors',\n                  fix: applyReplacementFix,\n                },\n              ];\n              fixAlreadyAttached = true;\n            }\n\n            context.report(reportData);\n          }\n\n          // Assert that expectedErrors is actually needed\n          if (\n            Boolean(applyReplacementFix) &&\n            missingExpectedLines.length > 0 &&\n            hasCompilerEntry(block.metadata)\n          ) {\n            const plural = missingExpectedLines.length > 1;\n            const message = plural\n              ? `React Compiler expected errors on lines ${missingExpectedLines.join(\n                  ', '\n                )} were not triggered`\n              : `React Compiler expected error on line ${missingExpectedLines[0]} was not triggered`;\n\n            const reportData = {\n              node,\n              loc: {\n                start: {\n                  line: block.position.start.line,\n                  column: 0,\n                },\n                end: {\n                  line: block.position.start.line,\n                  column: block.fence.rawText.length,\n                },\n              },\n              message,\n            };\n\n            if (!fixAlreadyAttached && applyReplacementFix) {\n              reportData.fix = applyReplacementFix;\n              fixAlreadyAttached = true;\n            } else if (applyReplacementFix) {\n              reportData.suggest = [\n                {\n                  desc: 'Remove stale expectedErrors metadata',\n                  fix: applyReplacementFix,\n                },\n              ];\n            }\n\n            context.report(reportData);\n          }\n        }\n      },\n    };\n  },\n};\n"
  },
  {
    "path": "eslint-local-rules/rules/markdown.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nconst remark = require('remark');\nconst {parseFenceMetadata} = require('./metadata');\n\n/**\n * @typedef {Object} MarkdownCodeBlock\n * @property {string} code\n * @property {number} codeStartLine\n * @property {{start: {line: number, column: number}, end: {line: number, column: number}}} position\n * @property {{lineIndex: number, rawText: string, metaText: string, range: [number, number]}} fence\n * @property {string} filePath\n * @property {string} lang\n * @property {import('./metadata').FenceMetadata} metadata\n */\n\nconst SUPPORTED_LANGUAGES = new Set([\n  'js',\n  'jsx',\n  'javascript',\n  'ts',\n  'tsx',\n  'typescript',\n]);\n\nfunction computeLineOffsets(lines) {\n  const offsets = [];\n  let currentOffset = 0;\n\n  for (const line of lines) {\n    offsets.push(currentOffset);\n    currentOffset += line.length + 1;\n  }\n\n  return offsets;\n}\n\nfunction parseMarkdownFile(content, filePath) {\n  const tree = remark().parse(content);\n  const lines = content.split('\\n');\n  const lineOffsets = computeLineOffsets(lines);\n  const blocks = [];\n\n  function traverse(node) {\n    if (!node || typeof node !== 'object') {\n      return;\n    }\n\n    if (node.type === 'code') {\n      const rawLang = node.lang || '';\n      const normalizedLang = rawLang.toLowerCase();\n      if (!normalizedLang || !SUPPORTED_LANGUAGES.has(normalizedLang)) {\n        return;\n      }\n\n      const fenceLineIndex = (node.position?.start?.line ?? 1) - 1;\n      const fenceStartOffset = node.position?.start?.offset ?? 0;\n      const fenceLine = lines[fenceLineIndex] ?? '';\n      const fenceEndOffset = fenceStartOffset + fenceLine.length;\n\n      let metaText = '';\n      if (fenceLine) {\n        const prefixMatch = fenceLine.match(/^`{3,}\\s*/);\n        const prefixLength = prefixMatch ? prefixMatch[0].length : 3;\n        metaText = fenceLine.slice(prefixLength + rawLang.length);\n      } else if (node.meta) {\n        metaText = ` ${node.meta}`;\n      }\n\n      const metadata = parseFenceMetadata(metaText);\n\n      blocks.push({\n        lang: rawLang || normalizedLang,\n        metadata,\n        filePath,\n        code: node.value || '',\n        codeStartLine: (node.position?.start?.line ?? 1) + 1,\n        position: {\n          start: {\n            line: fenceLineIndex + 1,\n            column: (node.position?.start?.column ?? 1) - 1,\n          },\n          end: {\n            line: fenceLineIndex + 1,\n            column: (node.position?.start?.column ?? 1) - 1 + fenceLine.length,\n          },\n        },\n        fence: {\n          lineIndex: fenceLineIndex,\n          rawText: fenceLine,\n          metaText,\n          range: [fenceStartOffset, fenceEndOffset],\n        },\n      });\n      return;\n    }\n\n    if ('children' in node && Array.isArray(node.children)) {\n      for (const child of node.children) {\n        traverse(child);\n      }\n    }\n  }\n\n  traverse(tree);\n\n  return {\n    content,\n    blocks,\n    lines,\n    lineOffsets,\n  };\n}\n\nmodule.exports = {\n  SUPPORTED_LANGUAGES,\n  computeLineOffsets,\n  parseMarkdownFile,\n};\n"
  },
  {
    "path": "eslint-local-rules/rules/metadata.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/**\n * @typedef {{type: 'text', raw: string}} TextToken\n * @typedef {{\n *   type: 'expectedErrors',\n *   entries: Record<string, Array<number>>,\n *   raw?: string,\n * }} ExpectedErrorsToken\n * @typedef {TextToken | ExpectedErrorsToken} MetadataToken\n *\n * @typedef {{\n *   leading: string,\n *   trailing: string,\n *   tokens: Array<MetadataToken>,\n *   parseError: boolean,\n *   hadDuplicateExpectedErrors: boolean,\n * }} FenceMetadata\n */\n\nconst EXPECTED_ERRORS_BLOCK_REGEX = /\\{\\s*expectedErrors\\s*:/;\nconst REACT_COMPILER_KEY = 'react-compiler';\n\nfunction getSortedUniqueNumbers(values) {\n  return Array.from(new Set(values))\n    .filter((value) => typeof value === 'number' && !Number.isNaN(value))\n    .sort((a, b) => a - b);\n}\n\nfunction tokenizeMeta(body) {\n  if (!body) {\n    return [];\n  }\n\n  const tokens = [];\n  let current = '';\n  let depth = 0;\n\n  for (let i = 0; i < body.length; i++) {\n    const char = body[i];\n    if (char === '{') {\n      depth++;\n    } else if (char === '}') {\n      depth = Math.max(depth - 1, 0);\n    }\n\n    if (char === ' ' && depth === 0) {\n      if (current) {\n        tokens.push(current);\n        current = '';\n      }\n      continue;\n    }\n\n    current += char;\n  }\n\n  if (current) {\n    tokens.push(current);\n  }\n\n  return tokens;\n}\n\nfunction normalizeEntryValues(values) {\n  if (!Array.isArray(values)) {\n    return [];\n  }\n  return getSortedUniqueNumbers(values);\n}\n\nfunction parseExpectedErrorsEntries(rawEntries) {\n  const normalized = rawEntries\n    .replace(/([{,]\\s*)([a-zA-Z_$][\\w$]*)\\s*:/g, '$1\"$2\":')\n    .replace(/'([^']*)'/g, '\"$1\"');\n\n  const parsed = JSON.parse(normalized);\n  const entries = {};\n\n  if (parsed && typeof parsed === 'object') {\n    for (const [key, value] of Object.entries(parsed)) {\n      entries[key] = normalizeEntryValues(Array.isArray(value) ? value.flat() : value);\n    }\n  }\n\n  return entries;\n}\n\nfunction parseExpectedErrorsToken(tokenText) {\n  const match = tokenText.match(/^\\{\\s*expectedErrors\\s*:\\s*(\\{[\\s\\S]*\\})\\s*\\}$/);\n  if (!match) {\n    return null;\n  }\n\n  const entriesSource = match[1];\n  let parseError = false;\n  let entries;\n\n  try {\n    entries = parseExpectedErrorsEntries(entriesSource);\n  } catch (error) {\n    parseError = true;\n    entries = {};\n  }\n\n  return {\n    token: {\n      type: 'expectedErrors',\n      entries,\n      raw: tokenText,\n    },\n    parseError,\n  };\n}\n\nfunction parseFenceMetadata(metaText) {\n  if (!metaText) {\n    return {\n      leading: '',\n      trailing: '',\n      tokens: [],\n      parseError: false,\n      hadDuplicateExpectedErrors: false,\n    };\n  }\n\n  const leading = metaText.match(/^\\s*/)?.[0] ?? '';\n  const trailing = metaText.match(/\\s*$/)?.[0] ?? '';\n  const bodyStart = leading.length;\n  const bodyEnd = metaText.length - trailing.length;\n  const body = metaText.slice(bodyStart, bodyEnd).trim();\n\n  if (!body) {\n    return {\n      leading,\n      trailing,\n      tokens: [],\n      parseError: false,\n      hadDuplicateExpectedErrors: false,\n    };\n  }\n\n  const tokens = [];\n  let parseError = false;\n  let sawExpectedErrors = false;\n  let hadDuplicateExpectedErrors = false;\n\n  for (const rawToken of tokenizeMeta(body)) {\n    const normalizedToken = rawToken.trim();\n    if (!normalizedToken) {\n      continue;\n    }\n\n    if (EXPECTED_ERRORS_BLOCK_REGEX.test(normalizedToken)) {\n      const parsed = parseExpectedErrorsToken(normalizedToken);\n      if (parsed) {\n        if (sawExpectedErrors) {\n          hadDuplicateExpectedErrors = true;\n          // Drop duplicates. We'll rebuild the canonical block on write.\n          continue;\n        }\n        tokens.push(parsed.token);\n        parseError = parseError || parsed.parseError;\n        sawExpectedErrors = true;\n        continue;\n      }\n    }\n\n    tokens.push({type: 'text', raw: normalizedToken});\n  }\n\n  return {\n    leading,\n    trailing,\n    tokens,\n    parseError,\n    hadDuplicateExpectedErrors,\n  };\n}\n\nfunction cloneMetadata(metadata) {\n  return {\n    leading: metadata.leading,\n    trailing: metadata.trailing,\n    parseError: metadata.parseError,\n    hadDuplicateExpectedErrors: metadata.hadDuplicateExpectedErrors,\n    tokens: metadata.tokens.map((token) => {\n      if (token.type === 'expectedErrors') {\n        const clonedEntries = {};\n        for (const [key, value] of Object.entries(token.entries)) {\n          clonedEntries[key] = [...value];\n        }\n        return {type: 'expectedErrors', entries: clonedEntries};\n      }\n      return {type: 'text', raw: token.raw};\n    }),\n  };\n}\n\nfunction findExpectedErrorsToken(metadata) {\n  return metadata.tokens.find((token) => token.type === 'expectedErrors') || null;\n}\n\nfunction getCompilerExpectedLines(metadata) {\n  const token = findExpectedErrorsToken(metadata);\n  if (!token) {\n    return [];\n  }\n  return getSortedUniqueNumbers(token.entries[REACT_COMPILER_KEY] || []);\n}\n\nfunction hasCompilerEntry(metadata) {\n  const token = findExpectedErrorsToken(metadata);\n  return Boolean(token && token.entries[REACT_COMPILER_KEY]?.length);\n}\n\nfunction metadataHasExpectedErrorsToken(metadata) {\n  return Boolean(findExpectedErrorsToken(metadata));\n}\n\nfunction stringifyExpectedErrorsToken(token) {\n  const entries = token.entries || {};\n  const keys = Object.keys(entries).filter((key) => entries[key].length > 0);\n  if (keys.length === 0) {\n    return '';\n  }\n\n  keys.sort();\n\n  const segments = keys.map((key) => {\n    const values = entries[key];\n    return `'${key}': [${values.join(', ')}]`;\n  });\n\n  return `{expectedErrors: {${segments.join(', ')}}}`;\n}\n\nfunction stringifyFenceMetadata(metadata) {\n  if (!metadata.tokens.length) {\n    return '';\n  }\n\n  const parts = metadata.tokens\n    .map((token) => {\n      if (token.type === 'expectedErrors') {\n        return stringifyExpectedErrorsToken(token);\n      }\n      return token.raw;\n    })\n    .filter(Boolean);\n\n  if (!parts.length) {\n    return '';\n  }\n\n  const leading = metadata.leading || ' ';\n  const trailing = metadata.trailing ? metadata.trailing.trimEnd() : '';\n  const body = parts.join(' ');\n  return `${leading}${body}${trailing}`;\n}\n\nfunction buildFenceLine(lang, metadata) {\n  const meta = stringifyFenceMetadata(metadata);\n  return meta ? `\\`\\`\\`${lang}${meta}` : `\\`\\`\\`${lang}`;\n}\n\nfunction metadataEquals(a, b) {\n  if (a.leading !== b.leading || a.trailing !== b.trailing) {\n    return false;\n  }\n\n  if (a.tokens.length !== b.tokens.length) {\n    return false;\n  }\n\n  for (let i = 0; i < a.tokens.length; i++) {\n    const left = a.tokens[i];\n    const right = b.tokens[i];\n    if (left.type !== right.type) {\n      return false;\n    }\n    if (left.type === 'text') {\n      if (left.raw !== right.raw) {\n        return false;\n      }\n    } else {\n      const leftKeys = Object.keys(left.entries).sort();\n      const rightKeys = Object.keys(right.entries).sort();\n      if (leftKeys.length !== rightKeys.length) {\n        return false;\n      }\n      for (let j = 0; j < leftKeys.length; j++) {\n        if (leftKeys[j] !== rightKeys[j]) {\n          return false;\n        }\n        const lValues = getSortedUniqueNumbers(left.entries[leftKeys[j]]);\n        const rValues = getSortedUniqueNumbers(right.entries[rightKeys[j]]);\n        if (lValues.length !== rValues.length) {\n          return false;\n        }\n        for (let k = 0; k < lValues.length; k++) {\n          if (lValues[k] !== rValues[k]) {\n            return false;\n          }\n        }\n      }\n    }\n  }\n\n  return true;\n}\n\nfunction normalizeMetadata(metadata) {\n  const normalized = cloneMetadata(metadata);\n  normalized.hadDuplicateExpectedErrors = false;\n  normalized.parseError = false;\n  if (!normalized.tokens.length) {\n    normalized.leading = '';\n    normalized.trailing = '';\n  }\n  return normalized;\n}\n\nfunction setCompilerExpectedLines(metadata, lines) {\n  const normalizedLines = getSortedUniqueNumbers(lines);\n  if (normalizedLines.length === 0) {\n    return removeCompilerExpectedLines(metadata);\n  }\n\n  const next = cloneMetadata(metadata);\n  let token = findExpectedErrorsToken(next);\n  if (!token) {\n    token = {type: 'expectedErrors', entries: {}};\n    next.tokens = [token, ...next.tokens];\n  }\n\n  token.entries[REACT_COMPILER_KEY] = normalizedLines;\n  return normalizeMetadata(next);\n}\n\nfunction removeCompilerExpectedLines(metadata) {\n  const next = cloneMetadata(metadata);\n  const token = findExpectedErrorsToken(next);\n  if (!token) {\n    return normalizeMetadata(next);\n  }\n\n  delete token.entries[REACT_COMPILER_KEY];\n\n  const hasEntries = Object.values(token.entries).some(\n    (value) => Array.isArray(value) && value.length > 0\n  );\n\n  if (!hasEntries) {\n    next.tokens = next.tokens.filter((item) => item !== token);\n  }\n\n  return normalizeMetadata(next);\n}\n\nmodule.exports = {\n  buildFenceLine,\n  getCompilerExpectedLines,\n  getSortedUniqueNumbers,\n  hasCompilerEntry,\n  metadataEquals,\n  metadataHasExpectedErrorsToken,\n  parseFenceMetadata,\n  removeCompilerExpectedLines,\n  setCompilerExpectedLines,\n  stringifyFenceMetadata,\n};\n"
  },
  {
    "path": "eslint-local-rules/rules/react-compiler.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nconst {transformFromAstSync} = require('@babel/core');\nconst {parse: babelParse} = require('@babel/parser');\nconst BabelPluginReactCompiler = require('babel-plugin-react-compiler').default;\nconst {\n  parsePluginOptions,\n  validateEnvironmentConfig,\n} = require('babel-plugin-react-compiler');\n\nconst COMPILER_OPTIONS = {\n  noEmit: true,\n  panicThreshold: 'none',\n  environment: validateEnvironmentConfig({\n    validateRefAccessDuringRender: true,\n    validateNoSetStateInRender: true,\n    validateNoSetStateInEffects: true,\n    validateNoJSXInTryStatements: true,\n    validateNoImpureFunctionsInRender: true,\n    validateStaticComponents: true,\n    validateNoFreezingKnownMutableFunctions: true,\n    validateNoVoidUseMemo: true,\n    validateNoCapitalizedCalls: [],\n    validateHooksUsage: true,\n    validateNoDerivedComputationsInEffects: true,\n  }),\n};\n\nfunction hasRelevantCode(code) {\n  const functionPattern = /^(export\\s+)?(default\\s+)?function\\s+\\w+/m;\n  const arrowPattern =\n    /^(export\\s+)?(const|let|var)\\s+\\w+\\s*=\\s*(\\([^)]*\\)|\\w+)\\s*=>/m;\n  const hasImports = /^import\\s+/m.test(code);\n\n  return functionPattern.test(code) || arrowPattern.test(code) || hasImports;\n}\n\nfunction runReactCompiler(code, filename) {\n  const result = {\n    sourceCode: code,\n    events: [],\n  };\n\n  if (!hasRelevantCode(code)) {\n    return {...result, diagnostics: []};\n  }\n\n  const options = parsePluginOptions({\n    ...COMPILER_OPTIONS,\n  });\n\n  options.logger = {\n    logEvent: (_, event) => {\n      if (event.kind === 'CompileError') {\n        const category = event.detail?.category;\n        if (category === 'Todo' || category === 'Invariant') {\n          return;\n        }\n        result.events.push(event);\n      }\n    },\n  };\n\n  try {\n    const ast = babelParse(code, {\n      sourceFilename: filename,\n      sourceType: 'module',\n      plugins: ['jsx', 'typescript'],\n    });\n\n    transformFromAstSync(ast, code, {\n      filename,\n      highlightCode: false,\n      retainLines: true,\n      plugins: [[BabelPluginReactCompiler, options]],\n      sourceType: 'module',\n      configFile: false,\n      babelrc: false,\n    });\n  } catch (error) {\n    return {...result, diagnostics: []};\n  }\n\n  const diagnostics = [];\n\n  for (const event of result.events) {\n    if (event.kind !== 'CompileError') {\n      continue;\n    }\n\n    const detail = event.detail;\n    if (!detail) {\n      continue;\n    }\n\n    const loc = typeof detail.primaryLocation === 'function'\n      ? detail.primaryLocation()\n      : null;\n\n    if (loc == null || typeof loc === 'symbol') {\n      continue;\n    }\n\n    const message = typeof detail.printErrorMessage === 'function'\n      ? detail.printErrorMessage(result.sourceCode, {eslint: true})\n      : detail.description || 'Unknown React Compiler error';\n\n    diagnostics.push({detail, loc, message});\n  }\n\n  return {...result, diagnostics};\n}\n\nmodule.exports = {\n  hasRelevantCode,\n  runReactCompiler,\n};\n"
  },
  {
    "path": "next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.\n"
  },
  {
    "path": "next.config.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\n/**\n * @type {import('next').NextConfig}\n **/\nconst nextConfig = {\n  pageExtensions: ['jsx', 'js', 'ts', 'tsx', 'mdx', 'md'],\n  reactStrictMode: true,\n  experimental: {\n    scrollRestoration: true,\n    reactCompiler: true,\n  },\n  async rewrites() {\n    return {\n      beforeFiles: [\n        // Serve markdown when Accept header prefers text/markdown\n        // Useful for LLM agents - https://www.skeptrune.com/posts/use-the-accept-header-to-serve-markdown-instead-of-html-to-llms/\n        {\n          source: '/:path((?!llms.txt).*)',\n          has: [\n            {\n              type: 'header',\n              key: 'accept',\n              value: '(.*text/markdown.*)',\n            },\n          ],\n          destination: '/api/md/:path*',\n        },\n        // Explicit .md extension also serves markdown\n        {\n          source: '/:path*.md',\n          destination: '/api/md/:path*',\n        },\n      ],\n    };\n  },\n  env: {},\n  webpack: (config, {dev, isServer, ...options}) => {\n    if (process.env.ANALYZE) {\n      const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');\n      config.plugins.push(\n        new BundleAnalyzerPlugin({\n          analyzerMode: 'static',\n          reportFilename: options.isServer\n            ? '../analyze/server.html'\n            : './analyze/client.html',\n        })\n      );\n    }\n\n    // Don't bundle the shim unnecessarily.\n    config.resolve.alias['use-sync-external-store/shim'] = 'react';\n\n    // ESLint depends on the CommonJS version of esquery,\n    // but Webpack loads the ESM version by default. This\n    // alias ensures the correct version is used.\n    //\n    // More info:\n    // https://github.com/reactjs/react.dev/pull/8115\n    config.resolve.alias['esquery'] = 'esquery/dist/esquery.min.js';\n\n    const {IgnorePlugin, NormalModuleReplacementPlugin} = require('webpack');\n    config.plugins.push(\n      new NormalModuleReplacementPlugin(\n        /^raf$/,\n        require.resolve('./src/utils/rafShim.js')\n      ),\n      new NormalModuleReplacementPlugin(\n        /^process$/,\n        require.resolve('./src/utils/processShim.js')\n      ),\n      new IgnorePlugin({\n        checkResource(resource, context) {\n          if (\n            /\\/eslint\\/lib\\/rules$/.test(context) &&\n            /\\.\\/[\\w-]+(\\.js)?$/.test(resource)\n          ) {\n            // Skips imports of built-in rules that ESLint\n            // tries to carry into the bundle by default.\n            // We only want the engine and the React rules.\n            return true;\n          }\n          return false;\n        },\n      })\n    );\n\n    return config;\n  },\n};\n\nmodule.exports = nextConfig;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-dev\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"license\": \"CC\",\n  \"scripts\": {\n    \"analyze\": \"ANALYZE=true next build\",\n    \"dev\": \"next-remote-watch ./src/content\",\n    \"build\": \"next build && node --experimental-modules ./scripts/downloadFonts.mjs\",\n    \"lint\": \"next lint && eslint \\\"src/content/**/*.md\\\"\",\n    \"lint:fix\": \"next lint --fix && eslint \\\"src/content/**/*.md\\\" --fix\",\n    \"format:source\": \"prettier --config .prettierrc --write \\\"{plugins,src}/**/*.{js,ts,jsx,tsx,css}\\\"\",\n    \"nit:source\": \"prettier --config .prettierrc --list-different \\\"{plugins,src}/**/*.{js,ts,jsx,tsx,css}\\\"\",\n    \"prettier\": \"yarn format:source\",\n    \"prettier:diff\": \"yarn nit:source\",\n    \"lint-heading-ids\": \"node scripts/headingIdLinter.js\",\n    \"fix-headings\": \"node scripts/headingIdLinter.js --fix\",\n    \"ci-check\": \"npm-run-all prettier:diff --parallel lint tsc lint-heading-ids rss deadlinks\",\n    \"tsc\": \"tsc --noEmit\",\n    \"start\": \"next start\",\n    \"postinstall\": \"yarn --cwd eslint-local-rules install && is-ci || husky install .husky\",\n    \"check-all\": \"npm-run-all prettier lint:fix tsc rss\",\n    \"rss\": \"node scripts/generateRss.js\",\n    \"deadlinks\": \"node scripts/deadLinkChecker.js\",\n    \"copyright\": \"node scripts/copyright.js\",\n    \"test:eslint-local-rules\": \"yarn --cwd eslint-local-rules test\",\n    \"textlint\": \"cd textlint && yarn --frozen-lockfile && yarn textlint\",\n    \"textlint-staged\": \"cd textlint && yarn --frozen-lockfile && yarn textlint-staged --\"\n  },\n  \"dependencies\": {\n    \"@codesandbox/sandpack-react\": \"2.13.5\",\n    \"@docsearch/css\": \"^3.8.3\",\n    \"@docsearch/react\": \"^3.8.3\",\n    \"@headlessui/react\": \"^1.7.0\",\n    \"@radix-ui/react-context-menu\": \"^2.1.5\",\n    \"body-scroll-lock\": \"^3.1.3\",\n    \"classnames\": \"^2.2.6\",\n    \"debounce\": \"^1.2.1\",\n    \"github-slugger\": \"^1.3.0\",\n    \"next\": \"15.1.12\",\n    \"next-remote-watch\": \"^1.0.0\",\n    \"parse-numeric-range\": \"^1.2.0\",\n    \"react\": \"^19.0.0\",\n    \"react-collapsed\": \"4.0.4\",\n    \"react-dom\": \"^19.0.0\",\n    \"remark-frontmatter\": \"^4.0.1\",\n    \"remark-gfm\": \"^3.0.1\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.12.9\",\n    \"@babel/plugin-transform-modules-commonjs\": \"^7.18.6\",\n    \"@babel/preset-react\": \"^7.18.6\",\n    \"@mdx-js/mdx\": \"^2.1.3\",\n    \"@types/body-scroll-lock\": \"^2.6.1\",\n    \"@types/classnames\": \"^2.2.10\",\n    \"@types/debounce\": \"^1.2.1\",\n    \"@types/github-slugger\": \"^1.3.0\",\n    \"@types/mdx-js__react\": \"^1.5.2\",\n    \"@types/node\": \"^14.6.4\",\n    \"@types/parse-numeric-range\": \"^0.0.1\",\n    \"@types/react\": \"^19.0.0\",\n    \"@types/react-dom\": \"^19.0.0\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.36.2\",\n    \"@typescript-eslint/parser\": \"^5.36.2\",\n    \"asyncro\": \"^3.0.0\",\n    \"autoprefixer\": \"^10.4.2\",\n    \"babel-eslint\": \"10.x\",\n    \"babel-plugin-react-compiler\": \"^1.0.0\",\n    \"chalk\": \"4.1.2\",\n    \"eslint\": \"7.x\",\n    \"eslint-config-next\": \"12.0.3\",\n    \"eslint-config-react-app\": \"^5.2.1\",\n    \"eslint-plugin-flowtype\": \"4.x\",\n    \"eslint-plugin-import\": \"2.x\",\n    \"eslint-plugin-jsx-a11y\": \"6.x\",\n    \"eslint-plugin-local-rules\": \"link:eslint-local-rules\",\n    \"eslint-plugin-react\": \"7.x\",\n    \"eslint-plugin-react-compiler\": \"^19.0.0-beta-e552027-20250112\",\n    \"eslint-plugin-react-hooks\": \"^0.0.0-experimental-fabef7a6b-20221215\",\n    \"fs-extra\": \"^9.0.1\",\n    \"globby\": \"^11.0.1\",\n    \"gray-matter\": \"^4.0.2\",\n    \"husky\": \"^7.0.4\",\n    \"is-ci\": \"^3.0.1\",\n    \"lint-staged\": \">=10\",\n    \"mdast-util-to-string\": \"^1.1.0\",\n    \"metro-cache\": \"0.72.2\",\n    \"npm-run-all\": \"^4.1.5\",\n    \"postcss\": \"^8.4.5\",\n    \"postcss-flexbugs-fixes\": \"4.2.1\",\n    \"postcss-preset-env\": \"^6.7.0\",\n    \"prettier\": \"^2.5.1\",\n    \"reading-time\": \"^1.2.0\",\n    \"remark\": \"^12.0.1\",\n    \"remark-external-links\": \"^7.0.0\",\n    \"remark-html\": \"^12.0.0\",\n    \"remark-images\": \"^2.0.0\",\n    \"remark-slug\": \"^7.0.0\",\n    \"remark-unwrap-images\": \"^2.0.0\",\n    \"retext\": \"^7.0.1\",\n    \"retext-smartypants\": \"^4.0.0\",\n    \"rss\": \"^1.2.2\",\n    \"tailwindcss\": \"^3.4.1\",\n    \"typescript\": \"^5.7.2\",\n    \"unist-util-visit\": \"^2.0.3\",\n    \"webpack-bundle-analyzer\": \"^4.5.0\"\n  },\n  \"engines\": {\n    \"node\": \">=16.8.0\"\n  },\n  \"nextBundleAnalysis\": {\n    \"budget\": null,\n    \"budgetPercentIncreaseRed\": 10,\n    \"showDetails\": true\n  },\n  \"lint-staged\": {\n    \"*.{js,ts,jsx,tsx,css}\": \"yarn prettier\",\n    \"src/**/*.md\": [\"yarn fix-headings\", \"yarn textlint-staged --\"]\n  },\n  \"packageManager\": \"yarn@1.22.22\"\n}\n"
  },
  {
    "path": "plugins/gatsby-remark-japanese-fix/index.js",
    "content": "const toString = require('mdast-util-to-string');\nconst visit = require('unist-util-visit');\n\nconst hasJapaneseCharacter = (str) => {\n  // Detects katakana, hiragana, iteration marks, and CJK unified ideographs\n  return /[\\u30a0-\\u30ff\\u3040-\\u309f\\u3005-\\u3006\\u4e00-\\u9fea。、]/.test(str);\n};\n\n/**\n * Iterates over emphases (<em>'s) within the AST created by remark.\n * Applies 'em-ja' class only when it contains a Japanese character,\n * so that it can be displayed with a different style.\n */\n\nmodule.exports = ({markdownAST}, options) => {\n  visit(markdownAST, 'emphasis', (node) => {\n    const nodeStr = toString(node);\n    if (hasJapaneseCharacter(nodeStr)) {\n      // Patch AST\n      node.data = node.data || {};\n      node.data.hProperties = node.data.hProperties || {};\n      node.data.hProperties.class = 'em-ja';\n    }\n  });\n  return markdownAST;\n};\n"
  },
  {
    "path": "plugins/gatsby-remark-japanese-fix/package.json",
    "content": "{\n  \"name\": \"gatsby-remark-japanese-fix\",\n  \"version\": \"0.0.1\"\n}\n"
  },
  {
    "path": "plugins/markdownToHtml.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nconst remark = require('remark');\nconst externalLinks = require('remark-external-links'); // Add _target and rel to external links\nconst customHeaders = require('./remark-header-custom-ids'); // Custom header id's for i18n\nconst images = require('remark-images'); // Improved image syntax\nconst unrwapImages = require('remark-unwrap-images'); // Removes <p> wrapper around images\nconst smartyPants = require('./remark-smartypants'); // Cleans up typography\nconst html = require('remark-html');\n\nmodule.exports = {\n  remarkPlugins: [\n    externalLinks,\n    customHeaders,\n    images,\n    unrwapImages,\n    smartyPants,\n  ],\n  markdownToHtml,\n};\n\nasync function markdownToHtml(markdown) {\n  const result = await remark()\n    .use(externalLinks)\n    .use(customHeaders)\n    .use(images)\n    .use(unrwapImages)\n    .use(smartyPants)\n    .use(html)\n    .process(markdown);\n  return result.toString();\n}\n"
  },
  {
    "path": "plugins/remark-header-custom-ids.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*!\n * Based on 'gatsby-remark-autolink-headers'\n * Original Author: Kyle Mathews <mathews.kyle@gmail.com>\n * Updated by Jared Palmer;\n * Copyright (c) 2015 Gatsbyjs\n */\n\nconst toString = require('mdast-util-to-string');\nconst visit = require('unist-util-visit');\nconst toSlug = require('github-slugger').slug;\n\nfunction patch(context, key, value) {\n  if (!context[key]) {\n    context[key] = value;\n  }\n  return context[key];\n}\n\nconst svgIcon = `<svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg>`;\n\nmodule.exports = ({icon = svgIcon, className = `anchor`} = {}) => {\n  return function transformer(tree) {\n    const ids = new Set();\n    visit(tree, 'heading', (node) => {\n      let children = [...node.children];\n      let id;\n      if (children[children.length - 1].type === 'mdxTextExpression') {\n        // # My header {/*my-header*/}\n        id = children.pop().value;\n        const isValidCustomId = id.startsWith('/*') && id.endsWith('*/');\n        if (!isValidCustomId) {\n          throw Error(\n            'Expected header ID to be like: {/*some-header*/}. ' +\n              'Instead, received: ' +\n              id\n          );\n        }\n        id = id.slice(2, id.length - 2);\n        if (id !== toSlug(id)) {\n          throw Error(\n            'Expected header ID to be a valid slug. You specified: {/*' +\n              id +\n              '*/}. Replace it with: {/*' +\n              toSlug(id) +\n              '*/}'\n          );\n        }\n      } else {\n        // # My header\n        id = toSlug(toString(node));\n      }\n\n      if (ids.has(id)) {\n        throw Error(\n          'Cannot have a duplicate header with id \"' +\n            id +\n            '\" on the page. ' +\n            'Rename the section or give it an explicit unique ID. ' +\n            'For example: #### Arguments {/*setstate-arguments*/}'\n        );\n      }\n      ids.add(id);\n\n      const data = patch(node, 'data', {});\n      patch(data, 'id', id);\n      patch(data, 'htmlAttributes', {});\n      patch(data, 'hProperties', {});\n      patch(data.htmlAttributes, 'id', id);\n      patch(data.hProperties, 'id', id);\n    });\n  };\n};\n"
  },
  {
    "path": "plugins/remark-smartypants.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*!\n * Based on 'silvenon/remark-smartypants'\n * https://github.com/silvenon/remark-smartypants/pull/80\n */\n\nconst visit = require('unist-util-visit');\nconst retext = require('retext');\nconst smartypants = require('retext-smartypants');\n\nfunction check(node, parent) {\n  if (node.data?.skipSmartyPants) return false;\n  if (parent.tagName === 'script') return false;\n  if (parent.tagName === 'style') return false;\n  return true;\n}\n\nfunction markSkip(node) {\n  if (!node) return;\n  node.data ??= {};\n  node.data.skipSmartyPants = true;\n  if (Array.isArray(node.children)) {\n    for (const child of node.children) {\n      markSkip(child);\n    }\n  }\n}\n\nmodule.exports = function (options) {\n  const processor = retext().use(smartypants, {\n    ...options,\n    // Do not replace ellipses, dashes, backticks because they change string\n    // length, and we couldn't guarantee right splice of text in second visit of\n    // tree\n    ellipses: false,\n    dashes: false,\n    backticks: false,\n  });\n\n  const processor2 = retext().use(smartypants, {\n    ...options,\n    // Do not replace quotes because they are already replaced in the first\n    // processor\n    quotes: false,\n  });\n\n  function transformer(tree) {\n    let allText = '';\n    let startIndex = 0;\n    const textOrInlineCodeNodes = [];\n\n    visit(tree, 'mdxJsxFlowElement', (node) => {\n      if (['TerminalBlock'].includes(node.name)) {\n        markSkip(node); // Mark all children to skip smarty pants\n      }\n    });\n\n    visit(tree, ['text', 'inlineCode'], (node, _, parent) => {\n      if (check(node, parent)) {\n        if (node.type === 'text') allText += node.value;\n        // for the case when inlineCode contains just one part of quote: `foo'bar`\n        else allText += 'A'.repeat(node.value.length);\n        textOrInlineCodeNodes.push(node);\n      }\n    });\n\n    // Concat all text into one string, to properly replace quotes around non-\"text\" nodes\n    allText = String(processor.processSync(allText));\n\n    for (const node of textOrInlineCodeNodes) {\n      const endIndex = startIndex + node.value.length;\n      if (node.type === 'text') {\n        const processedText = allText.slice(startIndex, endIndex);\n        node.value = String(processor2.processSync(processedText));\n      }\n      startIndex = endIndex;\n    }\n  }\n\n  return transformer;\n};\n"
  },
  {
    "path": "postcss.config.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nmodule.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n    'postcss-flexbugs-fixes': {},\n    'postcss-preset-env': {\n      autoprefixer: {\n        flexbox: 'no-2009',\n      },\n      stage: 3,\n      features: {\n        'custom-properties': false,\n      },\n    },\n  },\n};\n"
  },
  {
    "path": "public/.well-known/atproto-did",
    "content": "did:plc:uorpbnp2q32vuvyeruwauyhe"
  },
  {
    "path": "public/browserconfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig>\n    <msapplication>\n        <tile>\n            <square150x150logo src=\"/mstile-150x150.png\"/>\n            <TileColor>#2b5797</TileColor>\n        </tile>\n    </msapplication>\n</browserconfig>\n"
  },
  {
    "path": "public/html/single-file-example.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"UTF-8\" />\n    <title>Hello World</title>\n    <script src=\"https://unpkg.com/react@18/umd/react.development.js\"></script>\n    <script src=\"https://unpkg.com/react-dom@18/umd/react-dom.development.js\"></script>\n\n    <!-- Don't use this in production—do this: https://reactjs.org/docs/add-react-to-a-website#add-jsx-to-a-project -->\n    <script src=\"https://unpkg.com/babel-standalone@6/babel.min.js\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"text/babel\">\n\n      ReactDOM.render(\n        <h1>Hello, world!</h1>,\n        document.getElementById('root')\n      );\n\n    </script>\n    <!--\n      Note: this page is a great way to try React but it's not suitable for production.\n      It slowly compiles JSX with Babel in the browser and uses a large development build of React.\n\n      Read this section for a production-ready setup with JSX:\n      https://reactjs.org/docs/docs/add-react-to-a-website#try-react-with-jsx\n\n      In a larger project, you can use an integrated toolchain that includes JSX instead:\n      https://reactjs.org/docs/start-a-new-react-project\n\n      You can also use React without JSX, in which case you can remove Babel:\n      https://reactjs.org/docs/add-react-to-a-website#add-react-in-one-minute\n    -->\n  </body>\n</html>\n"
  },
  {
    "path": "public/js/jsfiddle-integration-babel.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n// Do not delete or move this file.\n// Many fiddles reference it so we have to keep it here.\n(function () {\n  var tag = document.querySelector(\n    'script[type=\"application/javascript;version=1.7\"]'\n  );\n  if (!tag || tag.textContent.indexOf('window.onload=function(){') !== -1) {\n    alert(\n      'Bad JSFiddle configuration, please fork the original React JSFiddle'\n    );\n  }\n  tag.setAttribute('type', 'text/babel');\n  tag.textContent = tag.textContent.replace(/^\\/\\/<!\\[CDATA\\[/, '');\n})();\n"
  },
  {
    "path": "public/js/jsfiddle-integration.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n// Do not delete or move this file.\n// Many fiddles reference it so we have to keep it here.\n(function () {\n  var tag = document.querySelector(\n    'script[type=\"application/javascript;version=1.7\"]'\n  );\n  if (!tag || tag.textContent.indexOf('window.onload=function(){') !== -1) {\n    alert(\n      'Bad JSFiddle configuration, please fork the original React JSFiddle'\n    );\n  }\n  tag.setAttribute('type', 'text/jsx;harmony=true');\n  tag.textContent = tag.textContent.replace(/^\\/\\/<!\\[CDATA\\[/, '');\n})();\n"
  },
  {
    "path": "public/robots.txt",
    "content": "User-agent: *\nDisallow:\n"
  },
  {
    "path": "public/site.webmanifest",
    "content": "{\n    \"name\": \"React\",\n    \"short_name\": \"React\",\n    \"icons\": [\n        {\n            \"src\": \"/android-chrome-192x192.png\",\n            \"sizes\": \"192x192\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"/android-chrome-384x384.png\",\n            \"sizes\": \"384x384\",\n            \"type\": \"image/png\"\n        }\n    ],\n    \"theme_color\": \"#23272f\",\n    \"background_color\": \"#23272f\",\n    \"display\": \"standalone\"\n}\n"
  },
  {
    "path": "scripts/copyright.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n'use strict';\n\nconst fs = require('fs');\nconst glob = require('glob');\n\nconst META_COPYRIGHT_COMMENT_BLOCK =\n  `/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */`.trim() + '\\n\\n';\n\nconst files = glob.sync('**/*.{js,ts,tsx,jsx,rs}', {\n  ignore: [\n    '**/dist/**',\n    '**/node_modules/**',\n    '**/tests/fixtures/**',\n    '**/__tests__/fixtures/**',\n  ],\n});\n\nconst updatedFiles = new Map();\nlet hasErrors = false;\nfiles.forEach((file) => {\n  try {\n    const result = processFile(file);\n    if (result != null) {\n      updatedFiles.set(file, result);\n    }\n  } catch (e) {\n    console.error(e);\n    hasErrors = true;\n  }\n});\nif (hasErrors) {\n  console.error('Update failed');\n  process.exit(1);\n} else {\n  for (const [file, source] of updatedFiles) {\n    fs.writeFileSync(file, source, 'utf8');\n  }\n  console.log('Update complete');\n}\n\nfunction processFile(file) {\n  if (fs.lstatSync(file).isDirectory()) {\n    return;\n  }\n  let source = fs.readFileSync(file, 'utf8');\n  let shebang = '';\n\n  if (source.startsWith('#!')) {\n    const newlineIndex = source.indexOf('\\n');\n    if (newlineIndex === -1) {\n      shebang = `${source}\\n`;\n      source = '';\n    } else {\n      shebang = source.slice(0, newlineIndex + 1);\n      source = source.slice(newlineIndex + 1);\n    }\n  }\n\n  if (source.indexOf(META_COPYRIGHT_COMMENT_BLOCK) === 0) {\n    return null;\n  }\n  if (/^\\/\\*\\*/.test(source)) {\n    source = source.replace(/\\/\\*\\*[^\\/]+\\/\\s+/, META_COPYRIGHT_COMMENT_BLOCK);\n  } else {\n    source = `${META_COPYRIGHT_COMMENT_BLOCK}${source}`;\n  }\n\n  if (shebang) {\n    return `${shebang}${source}`;\n  }\n  return source;\n}\n"
  },
  {
    "path": "scripts/deadLinkChecker.js",
    "content": "#!/usr/bin/env node\n/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nconst fs = require('fs');\nconst path = require('path');\nconst globby = require('globby');\nconst chalk = require('chalk');\n\nconst CONTENT_DIR = path.join(__dirname, '../src/content');\nconst PUBLIC_DIR = path.join(__dirname, '../public');\nconst fileCache = new Map();\nconst anchorMap = new Map(); // Map<filepath, Set<anchorId>>\nconst contributorMap = new Map(); // Map<anchorId, URL>\nconst redirectMap = new Map(); // Map<source, destination>\nlet errorCodes = new Set();\n\nasync function readFileWithCache(filePath) {\n  if (!fileCache.has(filePath)) {\n    try {\n      const content = await fs.promises.readFile(filePath, 'utf8');\n      fileCache.set(filePath, content);\n    } catch (error) {\n      throw new Error(`Failed to read file ${filePath}: ${error.message}`);\n    }\n  }\n  return fileCache.get(filePath);\n}\n\nasync function fileExists(filePath) {\n  try {\n    await fs.promises.access(filePath, fs.constants.R_OK);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\nfunction getMarkdownFiles() {\n  // Convert Windows paths to POSIX for globby compatibility\n  const baseDir = CONTENT_DIR.replace(/\\\\/g, '/');\n  const patterns = [\n    path.posix.join(baseDir, '**/*.md'),\n    path.posix.join(baseDir, '**/*.mdx'),\n  ];\n  return globby.sync(patterns);\n}\n\nfunction extractAnchorsFromContent(content) {\n  const anchors = new Set();\n\n  // MDX-style heading IDs: {/*anchor-id*/}\n  const mdxPattern = /\\{\\/\\*([a-zA-Z0-9-_]+)\\*\\/\\}/g;\n  let match;\n  while ((match = mdxPattern.exec(content)) !== null) {\n    anchors.add(match[1].toLowerCase());\n  }\n\n  // HTML id attributes\n  const htmlIdPattern = /\\sid=[\"']([a-zA-Z0-9-_]+)[\"']/g;\n  while ((match = htmlIdPattern.exec(content)) !== null) {\n    anchors.add(match[1].toLowerCase());\n  }\n\n  // Markdown heading with explicit ID: ## Heading {#anchor-id}\n  const markdownHeadingPattern = /^#+\\s+.*\\{#([a-zA-Z0-9-_]+)\\}/gm;\n  while ((match = markdownHeadingPattern.exec(content)) !== null) {\n    anchors.add(match[1].toLowerCase());\n  }\n\n  return anchors;\n}\n\nasync function buildAnchorMap(files) {\n  for (const filePath of files) {\n    const content = await readFileWithCache(filePath);\n    const anchors = extractAnchorsFromContent(content);\n    if (anchors.size > 0) {\n      anchorMap.set(filePath, anchors);\n    }\n  }\n}\n\nfunction extractLinksFromContent(content) {\n  const linkPattern = /\\[([^\\]]*)\\]\\(([^)]+)\\)/g;\n  const links = [];\n  let match;\n\n  while ((match = linkPattern.exec(content)) !== null) {\n    const [, linkText, linkUrl] = match;\n    if (linkUrl.startsWith('/') && !linkUrl.startsWith('//')) {\n      const lines = content.substring(0, match.index).split('\\n');\n      const line = lines.length;\n      const lastLineStart =\n        lines.length > 1 ? content.lastIndexOf('\\n', match.index - 1) + 1 : 0;\n      const column = match.index - lastLineStart + 1;\n\n      links.push({\n        text: linkText,\n        url: linkUrl,\n        line,\n        column,\n      });\n    }\n  }\n\n  return links;\n}\n\nasync function findTargetFile(urlPath) {\n  // Check if it's an image or static asset that might be in the public directory\n  const imageExtensions = [\n    '.png',\n    '.jpg',\n    '.jpeg',\n    '.gif',\n    '.svg',\n    '.ico',\n    '.webp',\n  ];\n  const hasImageExtension = imageExtensions.some((ext) =>\n    urlPath.toLowerCase().endsWith(ext)\n  );\n\n  if (hasImageExtension || urlPath.includes('.')) {\n    // Check in public directory (with and without leading slash)\n    const publicPaths = [\n      path.join(PUBLIC_DIR, urlPath),\n      path.join(PUBLIC_DIR, urlPath.substring(1)),\n    ];\n\n    for (const p of publicPaths) {\n      if (await fileExists(p)) {\n        return p;\n      }\n    }\n  }\n\n  const possiblePaths = [\n    path.join(CONTENT_DIR, urlPath + '.md'),\n    path.join(CONTENT_DIR, urlPath + '.mdx'),\n    path.join(CONTENT_DIR, urlPath, 'index.md'),\n    path.join(CONTENT_DIR, urlPath, 'index.mdx'),\n    // Without leading slash\n    path.join(CONTENT_DIR, urlPath.substring(1) + '.md'),\n    path.join(CONTENT_DIR, urlPath.substring(1) + '.mdx'),\n    path.join(CONTENT_DIR, urlPath.substring(1), 'index.md'),\n    path.join(CONTENT_DIR, urlPath.substring(1), 'index.mdx'),\n  ];\n\n  for (const p of possiblePaths) {\n    if (await fileExists(p)) {\n      return p;\n    }\n  }\n  return null;\n}\n\nasync function validateLink(link) {\n  const urlAnchorPattern = /#([a-zA-Z0-9-_]+)$/;\n  const anchorMatch = link.url.match(urlAnchorPattern);\n  const urlWithoutAnchor = link.url.replace(urlAnchorPattern, '');\n\n  if (urlWithoutAnchor === '/') {\n    return {valid: true};\n  }\n\n  // Check for redirects\n  if (redirectMap.has(urlWithoutAnchor)) {\n    const redirectDestination = redirectMap.get(urlWithoutAnchor);\n    if (\n      redirectDestination.startsWith('http://') ||\n      redirectDestination.startsWith('https://')\n    ) {\n      return {valid: true};\n    }\n    const redirectedLink = {\n      ...link,\n      url: redirectDestination + (anchorMatch ? anchorMatch[0] : ''),\n    };\n    return validateLink(redirectedLink);\n  }\n\n  // Check if it's an error code link\n  const errorCodeMatch = urlWithoutAnchor.match(/^\\/errors\\/(\\d+)$/);\n  if (errorCodeMatch) {\n    const code = errorCodeMatch[1];\n    if (!errorCodes.has(code)) {\n      return {\n        valid: false,\n        reason: `Error code ${code} not found in React error codes`,\n      };\n    }\n    return {valid: true};\n  }\n\n  // Check if it's a contributor link on the team or acknowledgements page\n  if (\n    anchorMatch &&\n    (urlWithoutAnchor === '/community/team' ||\n      urlWithoutAnchor === '/community/acknowledgements')\n  ) {\n    const anchorId = anchorMatch[1].toLowerCase();\n    if (contributorMap.has(anchorId)) {\n      const correctUrl = contributorMap.get(anchorId);\n      if (correctUrl !== link.url) {\n        return {\n          valid: false,\n          reason: `Contributor link should be updated to: ${correctUrl}`,\n        };\n      }\n      return {valid: true};\n    } else {\n      return {\n        valid: false,\n        reason: `Contributor link not found`,\n      };\n    }\n  }\n\n  const targetFile = await findTargetFile(urlWithoutAnchor);\n\n  if (!targetFile) {\n    return {\n      valid: false,\n      reason: `Target file not found for: ${urlWithoutAnchor}`,\n    };\n  }\n\n  // Only check anchors for content files, not static assets\n  if (anchorMatch && targetFile.startsWith(CONTENT_DIR)) {\n    const anchorId = anchorMatch[1].toLowerCase();\n\n    // TODO handle more special cases. These are usually from custom MDX components that include\n    // a Heading from src/components/MDX/Heading.tsx which automatically injects an anchor tag.\n    switch (anchorId) {\n      case 'challenges':\n      case 'recap': {\n        return {valid: true};\n      }\n    }\n\n    const fileAnchors = anchorMap.get(targetFile);\n\n    if (!fileAnchors || !fileAnchors.has(anchorId)) {\n      return {\n        valid: false,\n        reason: `Anchor #${anchorMatch[1]} not found in ${path.relative(\n          CONTENT_DIR,\n          targetFile\n        )}`,\n      };\n    }\n  }\n\n  return {valid: true};\n}\n\nasync function processFile(filePath) {\n  const content = await readFileWithCache(filePath);\n  const links = extractLinksFromContent(content);\n  const deadLinks = [];\n\n  for (const link of links) {\n    const result = await validateLink(link);\n    if (!result.valid) {\n      deadLinks.push({\n        file: path.relative(process.cwd(), filePath),\n        line: link.line,\n        column: link.column,\n        text: link.text,\n        url: link.url,\n        reason: result.reason,\n      });\n    }\n  }\n\n  return {deadLinks, totalLinks: links.length};\n}\n\nasync function buildContributorMap() {\n  const teamFile = path.join(CONTENT_DIR, 'community/team.md');\n  const teamContent = await readFileWithCache(teamFile);\n\n  const teamMemberPattern = /<TeamMember[^>]*permalink=[\"']([^\"']+)[\"']/g;\n  let match;\n\n  while ((match = teamMemberPattern.exec(teamContent)) !== null) {\n    const permalink = match[1];\n    contributorMap.set(permalink, `/community/team#${permalink}`);\n  }\n\n  const ackFile = path.join(CONTENT_DIR, 'community/acknowledgements.md');\n  const ackContent = await readFileWithCache(ackFile);\n  const contributorPattern = /\\*\\s*\\[([^\\]]+)\\]\\(([^)]+)\\)/g;\n\n  while ((match = contributorPattern.exec(ackContent)) !== null) {\n    const name = match[1];\n    const url = match[2];\n    const hyphenatedName = name.toLowerCase().replace(/\\s+/g, '-');\n    if (!contributorMap.has(hyphenatedName)) {\n      contributorMap.set(hyphenatedName, url);\n    }\n  }\n}\n\nasync function fetchErrorCodes() {\n  try {\n    const response = await fetch(\n      'https://raw.githubusercontent.com/facebook/react/main/scripts/error-codes/codes.json'\n    );\n    if (!response.ok) {\n      throw new Error(`Failed to fetch error codes: ${response.status}`);\n    }\n    const codes = await response.json();\n    errorCodes = new Set(Object.keys(codes));\n    console.log(chalk.gray(`Fetched ${errorCodes.size} React error codes`));\n  } catch (error) {\n    throw new Error(`Failed to fetch error codes: ${error.message}`);\n  }\n}\n\nasync function buildRedirectsMap() {\n  try {\n    const vercelConfigPath = path.join(__dirname, '../vercel.json');\n    const vercelConfig = JSON.parse(\n      await fs.promises.readFile(vercelConfigPath, 'utf8')\n    );\n\n    if (vercelConfig.redirects) {\n      for (const redirect of vercelConfig.redirects) {\n        redirectMap.set(redirect.source, redirect.destination);\n      }\n      console.log(\n        chalk.gray(`Loaded ${redirectMap.size} redirects from vercel.json`)\n      );\n    }\n  } catch (error) {\n    console.log(\n      chalk.yellow(\n        `Warning: Could not load redirects from vercel.json: ${error.message}\\n`\n      )\n    );\n  }\n}\n\nasync function main() {\n  const files = getMarkdownFiles();\n  console.log(chalk.gray(`Checking ${files.length} markdown files...`));\n\n  await fetchErrorCodes();\n  await buildRedirectsMap();\n  await buildContributorMap();\n  await buildAnchorMap(files);\n\n  const filePromises = files.map((filePath) => processFile(filePath));\n  const results = await Promise.all(filePromises);\n  const deadLinks = results.flatMap((r) => r.deadLinks);\n  const totalLinks = results.reduce((sum, r) => sum + r.totalLinks, 0);\n\n  if (deadLinks.length > 0) {\n    console.log('\\n');\n    for (const link of deadLinks) {\n      console.log(chalk.yellow(`${link.file}:${link.line}:${link.column}`));\n      console.log(chalk.reset(`  Link text: ${link.text}`));\n      console.log(chalk.reset(`  URL: ${link.url}`));\n      console.log(`  ${chalk.red('✗')} ${chalk.red(link.reason)}\\n`);\n    }\n\n    console.log(\n      chalk.red(\n        `\\nFound ${deadLinks.length} dead link${\n          deadLinks.length > 1 ? 's' : ''\n        } out of ${totalLinks} total links\\n`\n      )\n    );\n    process.exit(1);\n  }\n\n  console.log(chalk.green(`\\n✓ All ${totalLinks} links are valid!\\n`));\n  process.exit(0);\n}\n\nmain().catch((error) => {\n  console.log(chalk.red(`Error: ${error.message}`));\n  process.exit(1);\n});\n"
  },
  {
    "path": "scripts/downloadFonts.mjs",
    "content": "/**\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport { exec } from 'child_process';\nimport { mkdir, promises as fsPromises } from 'fs';\nimport { dirname } from 'path';\nimport { promisify } from 'util';\n\nconst execAsync = promisify(exec);\n\n// Taken from Downloads on https://www.facebook.com/brand/meta/typography/.\n// To refresh the list, go to the Conf website's public/fonts/ folder and run this:\n// printf \"\\n[\\n%s\\n]\\n\" \"$(find . -type f ! -path \"*/.*\" -name \"*.woff2\" | sed 's|^./||' | sort | awk '{printf \"  \\\"%s\\\",\\n\", $0}' | sed '$s/,$//')\"\nconst paths = [\n  \"Optimistic_Display_Arbc_W_Bd.woff2\",\n  \"Optimistic_Display_Arbc_W_Md.woff2\",\n  \"Optimistic_Display_Arbc_W_SBd.woff2\",\n  \"Optimistic_Display_Cyrl_W_Bd.woff2\",\n  \"Optimistic_Display_Cyrl_W_Md.woff2\",\n  \"Optimistic_Display_Cyrl_W_SBd.woff2\",\n  \"Optimistic_Display_Deva_W_Bd.woff2\",\n  \"Optimistic_Display_Deva_W_Md.woff2\",\n  \"Optimistic_Display_Deva_W_SBd.woff2\",\n  \"Optimistic_Display_Viet_W_Bd.woff2\",\n  \"Optimistic_Display_Viet_W_Md.woff2\",\n  \"Optimistic_Display_Viet_W_SBd.woff2\",\n  \"Optimistic_Display_W_Bd.woff2\",\n  \"Optimistic_Display_W_BdIt.woff2\",\n  \"Optimistic_Display_W_Lt.woff2\",\n  \"Optimistic_Display_W_Md.woff2\",\n  \"Optimistic_Display_W_MdIt.woff2\",\n  \"Optimistic_Display_W_SBd.woff2\",\n  \"Optimistic_Display_W_SBdIt.woff2\",\n  \"Optimistic_Display_W_XBd.woff2\",\n  \"Optimistic_Display_W_XLt.woff2\",\n  \"Optimistic_Text_Arbc_W_Bd.woff2\",\n  \"Optimistic_Text_Arbc_W_Md.woff2\",\n  \"Optimistic_Text_Arbc_W_Rg.woff2\",\n  \"Optimistic_Text_Arbc_W_XBd.woff2\",\n  \"Optimistic_Text_Cyrl_W_Bd.woff2\",\n  \"Optimistic_Text_Cyrl_W_Md.woff2\",\n  \"Optimistic_Text_Cyrl_W_Rg.woff2\",\n  \"Optimistic_Text_Cyrl_W_XBd.woff2\",\n  \"Optimistic_Text_Deva_W_Bd.woff2\",\n  \"Optimistic_Text_Deva_W_Md.woff2\",\n  \"Optimistic_Text_Deva_W_Rg.woff2\",\n  \"Optimistic_Text_Deva_W_XBd.woff2\",\n  \"Optimistic_Text_Viet_W_Bd.woff2\",\n  \"Optimistic_Text_Viet_W_Md.woff2\",\n  \"Optimistic_Text_Viet_W_Rg.woff2\",\n  \"Optimistic_Text_Viet_W_XBd.woff2\",\n  \"Optimistic_Text_W_Bd.woff2\",\n  \"Optimistic_Text_W_BdIt.woff2\",\n  \"Optimistic_Text_W_It.woff2\",\n  \"Optimistic_Text_W_Md.woff2\",\n  \"Optimistic_Text_W_MdIt.woff2\",\n  \"Optimistic_Text_W_Rg.woff2\",\n  \"Optimistic_Text_W_XBd.woff2\",\n  \"Optimistic_Text_W_XBdIt.woff2\"\n];\n\nconst baseURL = \"https://conf.reactjs.org/fonts/\";\nconst outputDir = \"public/fonts/\";\n\nawait Promise.all(\n  paths.map(async (path) => {\n    const localPath = `${outputDir}${path}`;\n    const localDir = dirname(localPath);\n    await fsPromises.mkdir(localDir, { recursive: true });\n\n    const command = `curl ${baseURL}${path} --output ${localPath}`;\n    await execAsync(command);\n    console.log(`Downloaded ${path}`);\n  })\n);\n\nconsole.log(\"All fonts downloaded.\");\n"
  },
  {
    "path": "scripts/generateRss.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\nconst {generateRssFeed} = require('../src/utils/rss');\n\ngenerateRssFeed();\n"
  },
  {
    "path": "scripts/headingIDHelpers/generateHeadingIDs.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n// To do: Make this ESM.\n// To do: properly check heading numbers (headings with the same text get\n// numbered, this script doesn’t check that).\n\nconst assert = require('assert');\nconst fs = require('fs');\nconst GithubSlugger = require('github-slugger');\nconst walk = require('./walk');\n\nlet modules;\n\nfunction stripLinks(line) {\n  return line.replace(/\\[([^\\]]+)\\]\\([^)]+\\)/, (match, p1) => p1);\n}\n\nfunction addHeaderID(line, slugger) {\n  // check if we're a header at all\n  if (!line.startsWith('#')) {\n    return line;\n  }\n\n  const match =\n    /^(#+\\s+)(.+?)(\\s*\\{(?:\\/\\*|#)([^\\}\\*\\/]+)(?:\\*\\/)?\\}\\s*)?$/.exec(line);\n  const before = match[1] + match[2];\n  const proc = modules\n    .unified()\n    .use(modules.remarkParse)\n    .use(modules.remarkSlug);\n  const tree = proc.runSync(proc.parse(before));\n  const head = tree.children[0];\n  assert(\n    head && head.type === 'heading',\n    'expected `' +\n      before +\n      '` to be a heading, is it using a normal space after `#`?'\n  );\n  const autoId = head.data.id;\n  const existingId = match[4];\n  const id = existingId || autoId;\n  // Ignore numbers:\n  const cleanExisting = existingId\n    ? existingId.replace(/-\\d+$/, '')\n    : undefined;\n  const cleanAuto = autoId.replace(/-\\d+$/, '');\n\n  if (cleanExisting && cleanExisting !== cleanAuto) {\n    console.log(\n      'Note: heading `%s` has a different ID (`%s`) than what GH generates for it: `%s`:',\n      before,\n      existingId,\n      autoId\n    );\n  }\n\n  return match[1] + match[2] + ' {/*' + id + '*/}';\n}\n\nfunction addHeaderIDs(lines) {\n  // Sluggers should be per file\n  const slugger = new GithubSlugger();\n  let inCode = false;\n  const results = [];\n  lines.forEach((line) => {\n    // Ignore code blocks\n    if (line.startsWith('```')) {\n      inCode = !inCode;\n      results.push(line);\n      return;\n    }\n    if (inCode) {\n      results.push(line);\n      return;\n    }\n\n    results.push(addHeaderID(line, slugger));\n  });\n  return results;\n}\n\nasync function main(paths) {\n  paths = paths.length === 0 ? ['src/content'] : paths;\n\n  const [unifiedMod, remarkParseMod, remarkSlugMod] = await Promise.all([\n    import('unified'),\n    import('remark-parse'),\n    import('remark-slug'),\n  ]);\n  const unified = unifiedMod.unified;\n  const remarkParse = remarkParseMod.default;\n  const remarkSlug = remarkSlugMod.default;\n  modules = {unified, remarkParse, remarkSlug};\n  const files = paths.map((path) => [...walk(path)]).flat();\n\n  files.forEach((file) => {\n    if (!(file.endsWith('.md') || file.endsWith('.mdx'))) {\n      return;\n    }\n\n    const content = fs.readFileSync(file, 'utf8');\n    const lines = content.split('\\n');\n    const updatedLines = addHeaderIDs(lines);\n    fs.writeFileSync(file, updatedLines.join('\\n'));\n  });\n}\n\nmodule.exports = main;\n"
  },
  {
    "path": "scripts/headingIDHelpers/validateHeadingIDs.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nconst fs = require('fs');\nconst walk = require('./walk');\n\n/**\n * Validate if there is a custom heading id and exit if there isn't a heading\n * @param {string} line\n * @returns\n */\nfunction validateHeaderId(line) {\n  if (!line.startsWith('#')) {\n    return;\n  }\n\n  const match = /\\{\\/\\*(.*?)\\*\\/}/.exec(line);\n  const id = match;\n  if (!id) {\n    console.error('Run yarn fix-headings to generate headings.');\n    process.exit(1);\n  }\n}\n\n/**\n * Loops through the lines to skip code blocks\n * @param {Array<string>} lines\n */\nfunction validateHeaderIds(lines) {\n  let inCode = false;\n  const results = [];\n  lines.forEach((line) => {\n    // Ignore code blocks\n    if (line.startsWith('```')) {\n      inCode = !inCode;\n\n      results.push(line);\n      return;\n    }\n    if (inCode) {\n      results.push(line);\n      return;\n    }\n    validateHeaderId(line);\n  });\n}\n/**\n * paths are basically array of path for which we have to validate heading IDs\n * @param {Array<string>} paths\n */\nasync function main(paths) {\n  paths = paths.length === 0 ? ['src/content'] : paths;\n  const files = paths.map((path) => [...walk(path)]).flat();\n\n  files.forEach((file) => {\n    if (!(file.endsWith('.md') || file.endsWith('.mdx'))) {\n      return;\n    }\n\n    const content = fs.readFileSync(file, 'utf8');\n    const lines = content.split('\\n');\n    validateHeaderIds(lines);\n  });\n}\n\nmodule.exports = main;\n"
  },
  {
    "path": "scripts/headingIDHelpers/walk.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nconst fs = require('fs');\n\nmodule.exports = function walk(dir) {\n  let results = [];\n  /**\n   * If the param is a directory we can return the file\n   */\n  if (dir.includes('md')) {\n    return [dir];\n  }\n  const list = fs.readdirSync(dir);\n  list.forEach(function (file) {\n    file = dir + '/' + file;\n    const stat = fs.statSync(file);\n    if (stat && stat.isDirectory()) {\n      /* Recurse into a subdirectory */\n      results = results.concat(walk(file));\n    } else {\n      /* Is a file */\n      results.push(file);\n    }\n  });\n  return results;\n};\n"
  },
  {
    "path": "scripts/headingIdLinter.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nconst validateHeaderIds = require('./headingIDHelpers/validateHeadingIDs');\nconst generateHeadingIds = require('./headingIDHelpers/generateHeadingIDs');\n\n/**\n * yarn lint-heading-ids --> Checks all files and causes an error if heading ID is missing\n * yarn lint-heading-ids --fix --> Fixes all markdown file's heading IDs\n * yarn lint-heading-ids path/to/markdown.md --> Checks that particular file for missing heading ID (path can denote a directory or particular file)\n * yarn lint-heading-ids --fix path/to/markdown.md --> Fixes that particular file's markdown IDs (path can denote a directory or particular file)\n */\n\nconst markdownPaths = process.argv.slice(2);\nif (markdownPaths.includes('--fix')) {\n  generateHeadingIds(markdownPaths.filter((path) => path !== '--fix'));\n} else {\n  validateHeaderIds(markdownPaths);\n}\n"
  },
  {
    "path": "src/components/Breadcrumbs.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {Fragment} from 'react';\nimport Link from 'next/link';\nimport type {RouteItem} from 'components/Layout/getRouteMeta';\n\nfunction Breadcrumbs({breadcrumbs}: {breadcrumbs: RouteItem[]}) {\n  return (\n    <div className=\"flex flex-wrap\">\n      {breadcrumbs.map(\n        (crumb, i) =>\n          crumb.path &&\n          !crumb.skipBreadcrumb && (\n            <div className=\"flex mb-3 mt-0.5 items-center\" key={i}>\n              <Fragment key={crumb.path}>\n                <Link\n                  href={crumb.path}\n                  className=\"text-link dark:text-link-dark text-sm tracking-wide font-bold uppercase me-1 hover:underline\">\n                  {crumb.title}\n                </Link>\n                <span className=\"inline-block me-1 text-link dark:text-link-dark text-lg rtl:rotate-180\">\n                  <svg\n                    width=\"20\"\n                    height=\"20\"\n                    viewBox=\"0 0 20 20\"\n                    fill=\"none\"\n                    xmlns=\"http://www.w3.org/2000/svg\">\n                    <path\n                      d=\"M6.86612 13.6161C6.37796 14.1043 6.37796 14.8957 6.86612 15.3839C7.35427 15.872 8.14572 15.872 8.63388 15.3839L13.1339 10.8839C13.622 10.3957 13.622 9.60428 13.1339 9.11612L8.63388 4.61612C8.14572 4.12797 7.35427 4.12797 6.86612 4.61612C6.37796 5.10428 6.37796 5.89573 6.86612 6.38388L10.4822 10L6.86612 13.6161Z\"\n                      fill=\"currentColor\"\n                    />\n                  </svg>\n                </span>\n              </Fragment>\n            </div>\n          )\n      )}\n    </div>\n  );\n}\n\nexport default Breadcrumbs;\n"
  },
  {
    "path": "src/components/Button.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nimport cn from 'classnames';\n\ninterface ButtonProps {\n  children: React.ReactNode;\n  onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;\n  active?: boolean;\n  className?: string;\n  style?: Record<string, string>;\n}\n\nexport function Button({\n  children,\n  onClick,\n  active = false,\n  className,\n  style,\n}: ButtonProps) {\n  return (\n    <button\n      style={style}\n      onMouseDown={(evt) => {\n        evt.preventDefault();\n        evt.stopPropagation();\n      }}\n      onClick={onClick}\n      className={cn(\n        className,\n        'text-base leading-tight font-bold rounded-full py-2 px-4 focus:outline focus:outline-offset-2 focus:outline-link dark:focus:outline-link-dark inline-flex items-center my-1',\n        {\n          'bg-link border-link text-white hover:bg-link focus:bg-link active:bg-link':\n            active,\n          'bg-transparent text-primary dark:text-primary-dark active:text-primary shadow-secondary-button-stroke dark:shadow-secondary-button-stroke-dark hover:bg-gray-40/5 active:bg-gray-40/10  hover:dark:bg-gray-60/5 active:dark:bg-gray-60/10':\n            !active,\n        }\n      )}>\n      {children}\n    </button>\n  );\n}\n\nexport default Button;\n"
  },
  {
    "path": "src/components/ButtonLink.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport cn from 'classnames';\nimport NextLink from 'next/link';\n\ninterface ButtonLinkProps {\n  size?: 'md' | 'lg';\n  type?: 'primary' | 'secondary';\n  label?: string;\n  target?: '_self' | '_blank';\n}\n\nfunction ButtonLink({\n  href,\n  className,\n  children,\n  type = 'primary',\n  size = 'md',\n  label,\n  target = '_self',\n  ...props\n}: React.AnchorHTMLAttributes<HTMLAnchorElement> & ButtonLinkProps) {\n  const classes = cn(\n    className,\n    'active:scale-[.98] transition-transform inline-flex font-bold items-center outline-none focus:outline-none focus-visible:outline focus-visible:outline-link focus:outline-offset-2 focus-visible:dark:focus:outline-link-dark leading-snug',\n    {\n      'bg-link text-white dark:bg-brand-dark dark:text-secondary hover:bg-opacity-80':\n        type === 'primary',\n      'text-primary dark:text-primary-dark shadow-secondary-button-stroke dark:shadow-secondary-button-stroke-dark hover:bg-gray-40/5 active:bg-gray-40/10 hover:dark:bg-gray-60/5 active:dark:bg-gray-60/10':\n        type === 'secondary',\n      'text-lg py-3 rounded-full px-4 sm:px-6': size === 'lg',\n      'text-base rounded-full px-4 py-2': size === 'md',\n    }\n  );\n  return (\n    <NextLink\n      href={href as string}\n      className={classes}\n      {...props}\n      aria-label={label}\n      target={target}>\n      {children}\n    </NextLink>\n  );\n}\n\nexport default ButtonLink;\n"
  },
  {
    "path": "src/components/DocsFooter.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport NextLink from 'next/link';\nimport {memo} from 'react';\nimport cn from 'classnames';\nimport {IconNavArrow} from './Icon/IconNavArrow';\nimport type {RouteMeta} from './Layout/getRouteMeta';\n\nexport type DocsPageFooterProps = Pick<\n  RouteMeta,\n  'route' | 'nextRoute' | 'prevRoute'\n>;\n\nfunction areEqual(prevProps: DocsPageFooterProps, props: DocsPageFooterProps) {\n  return prevProps.route?.path === props.route?.path;\n}\n\nexport const DocsPageFooter = memo<DocsPageFooterProps>(\n  function DocsPageFooter({nextRoute, prevRoute, route}) {\n    if (!route || route?.heading) {\n      return null;\n    }\n\n    return (\n      <>\n        {prevRoute?.path || nextRoute?.path ? (\n          <>\n            <div className=\"grid grid-cols-1 gap-4 py-4 mx-auto max-w-7xl md:grid-cols-2 md:py-12\">\n              {prevRoute?.path ? (\n                <FooterLink\n                  type=\"Previous\"\n                  title={prevRoute.title}\n                  href={prevRoute.path}\n                />\n              ) : (\n                <div />\n              )}\n\n              {nextRoute?.path ? (\n                <FooterLink\n                  type=\"Next\"\n                  title={nextRoute.title}\n                  href={nextRoute.path}\n                />\n              ) : (\n                <div />\n              )}\n            </div>\n          </>\n        ) : null}\n      </>\n    );\n  },\n  areEqual\n);\n\nfunction FooterLink({\n  href,\n  title,\n  type,\n}: {\n  href: string;\n  title: string;\n  type: 'Previous' | 'Next';\n}) {\n  return (\n    <NextLink\n      href={href}\n      className={cn(\n        'flex gap-x-4 md:gap-x-6 items-center w-full md:min-w-80 md:w-fit md:max-w-md px-4 md:px-5 py-6 border-2 border-transparent text-base leading-base text-link dark:text-link-dark rounded-lg group focus:text-link dark:focus:text-link-dark focus:bg-highlight focus:border-link dark:focus:bg-highlight-dark dark:focus:border-link-dark focus:border-opacity-100 focus:border-2 focus:ring-1 focus:ring-offset-4 focus:ring-blue-40 active:ring-0 active:ring-offset-0 hover:bg-gray-5 dark:hover:bg-gray-80',\n        {\n          'flex-row-reverse justify-self-end text-end': type === 'Next',\n        }\n      )}>\n      <IconNavArrow\n        className=\"inline text-tertiary dark:text-tertiary-dark group-focus:text-link dark:group-focus:text-link-dark\"\n        displayDirection={type === 'Previous' ? 'start' : 'end'}\n      />\n      <div className=\"flex flex-col overflow-hidden\">\n        <span className=\"text-sm font-bold tracking-wide no-underline uppercase text-secondary dark:text-secondary-dark group-focus:text-link dark:group-focus:text-link-dark group-focus:text-opacity-100\">\n          {type === 'Previous' ? 'Previous' : 'Next'}\n        </span>\n        <span className=\"text-lg break-words group-hover:underline\">\n          {title}\n        </span>\n      </div>\n    </NextLink>\n  );\n}\n"
  },
  {
    "path": "src/components/ErrorDecoderContext.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n// Error Decoder requires reading pregenerated error message from getStaticProps,\n// but MDX component doesn't support props. So we use React Context to populate\n// the value without prop-drilling.\n// TODO: Replace with React.cache + React.use when migrating to Next.js App Router\n\nimport {createContext, useContext} from 'react';\n\nconst notInErrorDecoderContext = Symbol('not in error decoder context');\n\nexport const ErrorDecoderContext = createContext<\n  | {errorMessage: string | null; errorCode: string | null}\n  | typeof notInErrorDecoderContext\n>(notInErrorDecoderContext);\n\nexport const useErrorDecoderParams = () => {\n  const params = useContext(ErrorDecoderContext);\n\n  if (params === notInErrorDecoderContext) {\n    throw new Error('useErrorDecoder must be used in error decoder pages only');\n  }\n\n  return params;\n};\n"
  },
  {
    "path": "src/components/ExternalLink.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\nimport type {DetailedHTMLProps, AnchorHTMLAttributes} from 'react';\n\nexport function ExternalLink({\n  href,\n  target,\n  children,\n  ...props\n}: DetailedHTMLProps<\n  AnchorHTMLAttributes<HTMLAnchorElement>,\n  HTMLAnchorElement\n>) {\n  return (\n    <a href={href} target={target ?? '_blank'} rel=\"noopener\" {...props}>\n      {children}\n    </a>\n  );\n}\n"
  },
  {
    "path": "src/components/Icon/IconArrow.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport cn from 'classnames';\nimport type {SVGProps} from 'react';\n\nexport const IconArrow = memo<\n  SVGProps<SVGSVGElement> & {\n    /**\n     * The direction the arrow should point.\n     * `start` and `end` are relative to the current locale.\n     * for example, in LTR, `start` is left and `end` is right.\n     */\n    displayDirection: 'start' | 'end' | 'right' | 'left' | 'up' | 'down';\n  }\n>(function IconArrow({displayDirection, className, ...rest}) {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      width=\"1.33em\"\n      height=\"1.33em\"\n      fill=\"currentColor\"\n      {...rest}\n      className={cn(className, {\n        'rotate-180': displayDirection === 'right',\n        'rotate-180 rtl:rotate-0': displayDirection === 'end',\n      })}>\n      <path fill=\"none\" d=\"M0 0h24v24H0z\" />\n      <path d=\"M7.828 11H20v2H7.828l5.364 5.364-1.414 1.414L4 12l7.778-7.778 1.414 1.414z\" />\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconArrowSmall.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport cn from 'classnames';\nimport type {SVGProps} from 'react';\n\nexport const IconArrowSmall = memo<\n  SVGProps<SVGSVGElement> & {\n    /**\n     * The direction the arrow should point.\n     * `start` and `end` are relative to the current locale.\n     * for example, in LTR, `start` is left and `end` is right.\n     */\n    displayDirection: 'start' | 'end' | 'right' | 'left' | 'up' | 'down';\n  }\n>(function IconArrowSmall({displayDirection, className, ...rest}) {\n  const classes = cn(className, {\n    'rotate-180': displayDirection === 'left',\n    'rotate-180 rtl:rotate-0': displayDirection === 'start',\n    'rtl:rotate-180': displayDirection === 'end',\n    'rotate-90': displayDirection === 'down',\n  });\n  return (\n    <svg\n      width=\"1em\"\n      height=\"1em\"\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={classes}\n      {...rest}>\n      <path\n        d=\"M6.86612 13.6161C6.37796 14.1043 6.37796 14.8957 6.86612 15.3839C7.35427 15.872 8.14572 15.872 8.63388 15.3839L13.1339 10.8839C13.622 10.3957 13.622 9.60428 13.1339 9.11612L8.63388 4.61612C8.14572 4.12797 7.35427 4.12797 6.86612 4.61612C6.37796 5.10428 6.37796 5.89573 6.86612 6.38388L10.4822 10L6.86612 13.6161Z\"\n        fill=\"currentColor\"></path>\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconBsky.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport type {SVGProps} from 'react';\n\nexport const IconBsky = memo<SVGProps<SVGSVGElement>>(function IconBsky(props) {\n  return (\n    <svg\n      aria-label=\"Bluesky\"\n      viewBox=\"0 0 16 16\"\n      height=\"1.25em\"\n      width=\"1.25em\"\n      fill=\"currentColor\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      {...props}>\n      <path\n        className=\"x19hqcy\"\n        d=\"M3.468 1.948C5.303 3.325 7.276 6.118 8 7.616c.725-1.498 2.697-4.29 4.532-5.668C13.855.955 16 .186 16 2.632c0 .489-.28 4.105-.444 4.692-.572 2.04-2.653 2.561-4.504 2.246 3.236.551 4.06 2.375 2.281 4.2-3.376 3.464-4.852-.87-5.23-1.98-.07-.204-.103-.3-.103-.218 0-.081-.033.014-.102.218-.379 1.11-1.855 5.444-5.231 1.98-1.778-1.825-.955-3.65 2.28-4.2-1.85.315-3.932-.205-4.503-2.246C.28 6.737 0 3.12 0 2.632 0 .186 2.145.955 3.468 1.948Z\"></path>\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconCanary.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\n\nexport const IconCanary = memo<\n  JSX.IntrinsicElements['svg'] & {title?: string; size?: 's' | 'md'}\n>(function IconCanary(\n  {className, title, size} = {\n    className: undefined,\n    title: undefined,\n    size: 'md',\n  }\n) {\n  return (\n    <svg\n      className={className}\n      width={size === 's' ? '12px' : '20px'}\n      height={size === 's' ? '12px' : '20px'}\n      viewBox=\"0 0 20 20\"\n      version=\"1.1\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      {title && <title>{title}</title>}\n      <g stroke=\"none\" strokeWidth=\"1\" fill=\"none\" fillRule=\"evenodd\">\n        <g\n          id=\"noun-labs-1201738-(2)\"\n          transform=\"translate(2, 0)\"\n          fill=\"currentColor\"\n          fillRule=\"nonzero\">\n          <path\n            d=\"M10.2865804,5.55665262 L10.2865804,2.22331605 L10.8591544,2.22331605 C11.0103911,2.22244799 11.1551447,2.16342155 11.2617505,2.05914367 C11.3684534,1.95486857 11.4282767,1.81370176 11.4282767,1.66667106 L11.4282767,0.556642208 C11.4282767,0.40907262 11.3678934,0.26747526 11.2605218,0.16308627 C11.1531503,0.0587028348 11.0074938,0 10.8556998,0 L5.14338868,0 C4.9915947,0 4.84594391,0.0587028348 4.73856664,0.16308627 C4.63119507,0.267469704 4.57081178,0.40907262 4.57081178,0.556642208 L4.57081178,1.66667106 C4.57081178,1.81434899 4.63119507,1.95594912 4.73856664,2.06033811 C4.8459382,2.16472155 4.9915947,2.22331605 5.14338868,2.22331605 L5.71596273,2.22331605 L5.71596273,5.55665262 C5.71596273,8.38665538 2.97295619,9.88999017 0.651686904,15.5566623 C-0.0957823782,17.360053 -2.00560068,20 7.99951567,20 C18.004632,20 16.0948137,17.3600252 15.3507732,15.5566623 C13.0124432,9.88999017 10.2865804,8.38665538 10.2865804,5.55665262 Z M9.89570197,10.709991 C10.0921412,10.709991 10.2805515,10.7858383 10.4193876,10.9209301 C10.5583466,11.0559135 10.6363652,11.2390693 10.6363652,11.4300417 C10.6363652,11.6210141 10.5583466,11.8040698 10.4193876,11.9391533 C10.2805401,12.0741367 10.0921412,12.1499813 9.89570197,12.1499813 C9.6992627,12.1499813 9.51096673,12.074134 9.37201631,11.9391533 C9.23316875,11.8040615 9.15515307,11.6210141 9.15515307,11.4300417 C9.15515307,11.2390693 9.2331716,11.0559024 9.37201631,10.9209301 C9.57264221,10.7258996 9.61239426,10.709991 9.89570197,10.709991 Z M8.98919546,9.04212824 C9.09790709,9.14792278 9.15884755,9.29158681 9.1585213,9.44110085 C9.15829001,9.59073155 9.09678989,9.73407335 8.98763252,9.83954568 C8.87847514,9.945018 8.73069852,10.0039347 8.57678157,10.0033977 C8.42286747,10.0027392 8.27565088,9.94273467 8.16727355,9.83639845 C8.05900765,9.73006224 7.99873866,9.58628988 7.99963013,9.43664806 C8.00052304,9.28788403 8.0620221,9.14542556 8.17051087,9.04048101 C8.27911107,8.93555591 8.42599335,8.87663641 8.57913312,8.87663641 C8.73291864,8.87665585 8.88047525,8.93622535 8.98919546,9.04212824 Z M7.99965585,17.9999981 C4.91377349,17.9999981 3.29882839,17.7332867 2.51364277,17.4999976 C2.37780966,17.4476975 2.26954376,17.3439641 2.21396931,17.2125528 C2.15838628,17.0811499 2.16006066,16.9334692 2.21876871,16.8033858 C2.6144474,15.5921346 3.14916224,14.4280501 3.81316983,13.3333824 C5.980145,9.82337899 8.22941036,13.8867718 10.0980836,13.8867718 C11.9666996,13.8867718 11.4695868,12.1534924 12.1827971,13.3333824 C12.8511505,14.4269112 13.3916656,15.5896902 13.794259,16.8000524 C13.8533022,16.9322137 13.8537479,17.0822749 13.7952635,17.2147751 C13.7368889,17.3472613 13.6248314,17.4504531 13.4856467,17.5000531 C12.6833967,17.7332867 11.0855382,17.9999981 7.99965585,17.9999981 Z\"\n            id=\"Shape\"></path>\n        </g>\n      </g>\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconChevron.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport cn from 'classnames';\n\nexport const IconChevron = memo<\n  JSX.IntrinsicElements['svg'] & {\n    /**\n     * The direction the arrow should point.\n     * `start` and `end` are relative to the current locale.\n     * for example, in LTR, `start` is left and `end` is right.\n     */\n    displayDirection: 'start' | 'end' | 'right' | 'left' | 'up' | 'down';\n  }\n>(function IconChevron({className, displayDirection}) {\n  const classes = cn(\n    {\n      'rotate-0': displayDirection === 'down',\n      'rotate-90': displayDirection === 'left',\n      'rotate-180': displayDirection === 'up',\n      '-rotate-90': displayDirection === 'right',\n      'rotate-90 rtl:-rotate-90': displayDirection === 'start',\n      '-rotate-90 rtl:rotate-90': displayDirection === 'end',\n    },\n    className\n  );\n  return (\n    <svg\n      className={classes}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\">\n      <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-446 -398)\">\n        <path\n          fill=\"currentColor\"\n          fillRule=\"nonzero\"\n          d=\"M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z\"\n          transform=\"translate(356.5 164.5)\"\n        />\n        <polygon points=\"446 418 466 418 466 398 446 398\" />\n      </g>\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconClose.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport type {SVGProps} from 'react';\n\nexport const IconClose = memo<SVGProps<SVGSVGElement>>(function IconClose(\n  props\n) {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"1.33em\"\n      height=\"1.33em\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={2}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      {...props}>\n      <line x1={18} y1={6} x2={6} y2={18} />\n      <line x1={6} y1={6} x2={18} y2={18} />\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconCodeBlock.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\n\nexport const IconCodeBlock = memo<JSX.IntrinsicElements['svg']>(\n  function IconCodeBlock({className}) {\n    return (\n      <svg\n        className={className}\n        width=\"1.33em\"\n        height=\"1em\"\n        viewBox=\"0 0 24 18\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <path\n          d=\"M24 9L18.343 14.657L16.929 13.243L21.172 9L16.929 4.757L18.343 3.343L24 9ZM2.828 9L7.071 13.243L5.657 14.657L0 9L5.657 3.343L7.07 4.757L2.828 9ZM9.788 18H7.66L14.212 0H16.34L9.788 18Z\"\n          fill=\"currentColor\"\n        />\n      </svg>\n    );\n  }\n);\n"
  },
  {
    "path": "src/components/Icon/IconCopy.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\n\nexport const IconCopy = memo<JSX.IntrinsicElements['svg']>(function IconCopy({\n  className,\n}) {\n  return (\n    <svg\n      className={className}\n      width=\"1em\"\n      height=\"1em\"\n      viewBox=\"0 0 18 18\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M5.40382 15.3671C5.03332 15.1901 4.70081 14.9381 4.42481 14.6286C4.34831 14.5431 4.23931 14.5001 4.12981 14.5206L3.66181 14.6081C3.33531 14.6691 3.02032 14.4361 2.96232 14.0876L1.30981 4.12512C1.28181 3.95662 1.31731 3.7861 1.40981 3.6456C1.50231 3.5051 1.64082 3.41162 1.79932 3.38162L3.22131 3.00012C3.37681 2.97062 3.48981 2.82761 3.48981 2.65961V1.9101C3.48981 1.8276 3.49381 1.74561 3.49931 1.66461C3.50931 1.53461 3.35181 1.57211 3.35181 1.57211L1.64381 2.0076C1.18481 2.0936 0.751316 2.32662 0.451316 2.70612C0.0808162 3.17362 -0.0686885 3.77259 0.0293115 4.36459L1.68231 14.3281C1.84531 15.3081 2.65031 16.0001 3.55631 16.0001C3.66531 16.0001 3.77631 15.9896 3.88731 15.9691L5.36632 15.6916C5.52332 15.6626 5.54982 15.4366 5.40382 15.3671ZM14.9331 4.55801H12.9116C12.1351 4.55801 11.5001 3.91502 11.5001 3.12952V1.06802C11.5001 0.480524 11.0196 0 10.4321 0H7.44161C6.36911 0 5.50011 0.879508 5.50011 1.96451V12.1665C5.50011 13.179 6.33412 14 7.36262 14H14.1371C15.1661 14 16.0001 13.179 16.0001 12.1665V5.625C16.0001 5.038 15.5201 4.55801 14.9331 4.55801Z\"\n        fill=\"currentColor\"\n      />{' '}\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M12.5888 0.0914385C12.4493 0.00843847 12.5158 0.252449 12.5158 0.252449C12.5653 0.428449 12.5918 0.613451 12.5918 0.804451V2.90296C12.5918 3.17646 12.8158 3.40046 13.0903 3.40046H15.1718C15.3883 3.40046 15.5968 3.43495 15.7918 3.49845C15.7918 3.49845 15.9373 3.50844 15.9008 3.43494C15.8383 3.33744 15.7673 3.24494 15.6833 3.16044L12.8303 0.289467C12.7558 0.214467 12.6743 0.149438 12.5888 0.0914385Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconDeepDive.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\n\nexport const IconDeepDive = memo<JSX.IntrinsicElements['svg']>(\n  function IconDeepDive({className}) {\n    return (\n      <svg\n        className={className}\n        width=\"1.5em\"\n        height=\"1.5em\"\n        viewBox=\"0 0 72 72\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <path\n          fillRule=\"evenodd\"\n          clipRule=\"evenodd\"\n          d=\"M34.7409 59.7228L32.9567 58.9094C27.2672 56.3157 20.7328 56.3157 15.0433 58.9094C12.6018 60.0224 9.39163 59.0275 8.44602 56.0621C7.45647 52.9589 5.99975 46.5898 6 35.9997C6.00029 23.5648 8.00803 18.3599 9.11099 16.4196C9.67795 15.4222 10.5255 14.8455 11.2254 14.5264L12.0179 14.1651C19.6351 10.6926 28.4011 10.6738 36 14.1733C43.5989 10.6738 52.3649 10.6926 59.9821 14.1651L60.7746 14.5264C61.4745 14.8455 62.3221 15.4222 62.889 16.4196C63.992 18.3599 65.9997 23.5648 66 35.9997C66.0002 46.5898 64.5435 52.9589 63.554 56.0621C62.6084 59.0275 59.3982 60.0224 56.9567 58.9094C51.2672 56.3157 44.7328 56.3157 39.0433 58.9094L37.2591 59.7228C37.1986 59.7508 37.1373 59.7767 37.0753 59.8004C36.4484 60.0411 35.7556 60.0653 35.1102 59.8648C34.9847 59.8258 34.8613 59.7784 34.7409 59.7228ZM14.5068 19.6246C20.3733 16.9501 27.0874 16.8775 33 19.4067V52.473C26.7613 50.32 19.9378 50.471 13.7811 52.9261C13.0005 49.9843 11.9998 44.547 12 35.9998C12.0002 25.5786 13.4879 21.1893 14.1179 19.8018L14.5068 19.6246ZM39 52.473C45.2387 50.32 52.0622 50.471 58.2189 52.9261C58.9995 49.9843 60.0002 44.547 60 35.9998C59.9998 25.5786 58.5121 21.1893 57.8821 19.8018L57.4932 19.6246C51.6267 16.9501 44.9126 16.8775 39 19.4067V52.473Z\"\n          fill=\"currentColor\"\n        />\n      </svg>\n    );\n  }\n);\n"
  },
  {
    "path": "src/components/Icon/IconDownload.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\n\nexport const IconDownload = memo<JSX.IntrinsicElements['svg']>(\n  function IconDownload({className}) {\n    return (\n      <svg\n        width=\"1em\"\n        height=\"1em\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        className={className}>\n        <path\n          d=\"M20.5 22H3.5C3.10218 22 2.72064 21.842 2.43934 21.5607C2.15804 21.2794 2 20.8978 2 20.5V15.5C2 15.3674 2.05268 15.2402 2.14645 15.1464C2.24021 15.0527 2.36739 15 2.5 15H3.5C3.63261 15 3.75979 15.0527 3.85355 15.1464C3.94732 15.2402 4 15.3674 4 15.5V20H20V15.5C20 15.3674 20.0527 15.2402 20.1464 15.1464C20.2402 15.0527 20.3674 15 20.5 15H21.5C21.6326 15 21.7598 15.0527 21.8536 15.1464C21.9473 15.2402 22 15.3674 22 15.5V20.5C22 20.8978 21.842 21.2794 21.5607 21.5607C21.2794 21.842 20.8978 22 20.5 22Z\"\n          fill=\"currentColor\"\n        />\n        <path\n          d=\"M10.9999 2.5V13.79L8.81994 11.61C8.72479 11.5178 8.59747 11.4662 8.46494 11.4662C8.33241 11.4662 8.20509 11.5178 8.10994 11.61L7.39994 12.32C7.30769 12.4151 7.2561 12.5425 7.2561 12.675C7.2561 12.8075 7.30769 12.9348 7.39994 13.03L10.9399 16.56C11.0785 16.7003 11.2436 16.8117 11.4255 16.8877C11.6075 16.9637 11.8027 17.0029 11.9999 17.0029C12.1971 17.0029 12.3924 16.9637 12.5743 16.8877C12.7563 16.8117 12.9214 16.7003 13.0599 16.56L16.5999 13C16.6922 12.9048 16.7438 12.7775 16.7438 12.645C16.7438 12.5125 16.6922 12.3851 16.5999 12.29L15.8899 11.58C15.7948 11.4878 15.6675 11.4362 15.5349 11.4362C15.4024 11.4362 15.2751 11.4878 15.1799 11.58L12.9999 13.79V2.5C12.9999 2.36739 12.9473 2.24021 12.8535 2.14645C12.7597 2.05268 12.6325 2 12.4999 2H11.4999C11.3673 2 11.2402 2.05268 11.1464 2.14645C11.0526 2.24021 10.9999 2.36739 10.9999 2.5Z\"\n          fill=\"currentColor\"\n        />\n      </svg>\n    );\n  }\n);\n"
  },
  {
    "path": "src/components/Icon/IconError.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\n\nexport const IconError = memo<JSX.IntrinsicElements['svg']>(function IconError({\n  className,\n}) {\n  return (\n    <svg\n      className={className}\n      width=\"1.33em\"\n      height=\"1.33em\"\n      viewBox=\"0 0 24 24\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <circle cx=\"10.1626\" cy=\"9.99951\" r=\"9.47021\" fill=\"currentColor\" />\n      <path d=\"M6.22705 5.95996L14.2798 14.0127\" stroke=\"white\" />\n      <path d=\"M14.2798 5.95996L6.22705 14.0127\" stroke=\"white\" />\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconExperimental.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\n\nexport const IconExperimental = memo<\n  JSX.IntrinsicElements['svg'] & {title?: string; size?: 's' | 'md'}\n>(function IconExperimental(\n  {className, title, size} = {\n    className: undefined,\n    title: undefined,\n    size: 'md',\n  }\n) {\n  return (\n    <svg\n      className={className}\n      width={size === 's' ? '12px' : '20px'}\n      height={size === 's' ? '12px' : '20px'}\n      viewBox=\"0 0 20 20\"\n      version=\"1.1\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      {title && <title>{title}</title>}\n      <g stroke=\"none\" strokeWidth=\"1\" fill=\"none\" fillRule=\"evenodd\">\n        <g\n          id=\"noun-labs-1201738-(2)\"\n          transform=\"translate(2, 0)\"\n          fill=\"currentColor\"\n          fillRule=\"nonzero\">\n          <path\n            d=\"M10.2865804,5.55665262 L10.2865804,2.22331605 L10.8591544,2.22331605 C11.0103911,2.22244799 11.1551447,2.16342155 11.2617505,2.05914367 C11.3684534,1.95486857 11.4282767,1.81370176 11.4282767,1.66667106 L11.4282767,0.556642208 C11.4282767,0.40907262 11.3678934,0.26747526 11.2605218,0.16308627 C11.1531503,0.0587028348 11.0074938,0 10.8556998,0 L5.14338868,0 C4.9915947,0 4.84594391,0.0587028348 4.73856664,0.16308627 C4.63119507,0.267469704 4.57081178,0.40907262 4.57081178,0.556642208 L4.57081178,1.66667106 C4.57081178,1.81434899 4.63119507,1.95594912 4.73856664,2.06033811 C4.8459382,2.16472155 4.9915947,2.22331605 5.14338868,2.22331605 L5.71596273,2.22331605 L5.71596273,5.55665262 C5.71596273,8.38665538 2.97295619,9.88999017 0.651686904,15.5566623 C-0.0957823782,17.360053 -2.00560068,20 7.99951567,20 C18.004632,20 16.0948137,17.3600252 15.3507732,15.5566623 C13.0124432,9.88999017 10.2865804,8.38665538 10.2865804,5.55665262 Z M9.89570197,10.709991 C10.0921412,10.709991 10.2805515,10.7858383 10.4193876,10.9209301 C10.5583466,11.0559135 10.6363652,11.2390693 10.6363652,11.4300417 C10.6363652,11.6210141 10.5583466,11.8040698 10.4193876,11.9391533 C10.2805401,12.0741367 10.0921412,12.1499813 9.89570197,12.1499813 C9.6992627,12.1499813 9.51096673,12.074134 9.37201631,11.9391533 C9.23316875,11.8040615 9.15515307,11.6210141 9.15515307,11.4300417 C9.15515307,11.2390693 9.2331716,11.0559024 9.37201631,10.9209301 C9.57264221,10.7258996 9.61239426,10.709991 9.89570197,10.709991 Z M8.98919546,9.04212824 C9.09790709,9.14792278 9.15884755,9.29158681 9.1585213,9.44110085 C9.15829001,9.59073155 9.09678989,9.73407335 8.98763252,9.83954568 C8.87847514,9.945018 8.73069852,10.0039347 8.57678157,10.0033977 C8.42286747,10.0027392 8.27565088,9.94273467 8.16727355,9.83639845 C8.05900765,9.73006224 7.99873866,9.58628988 7.99963013,9.43664806 C8.00052304,9.28788403 8.0620221,9.14542556 8.17051087,9.04048101 C8.27911107,8.93555591 8.42599335,8.87663641 8.57913312,8.87663641 C8.73291864,8.87665585 8.88047525,8.93622535 8.98919546,9.04212824 Z M7.99965585,17.9999981 C4.91377349,17.9999981 3.29882839,17.7332867 2.51364277,17.4999976 C2.37780966,17.4476975 2.26954376,17.3439641 2.21396931,17.2125528 C2.15838628,17.0811499 2.16006066,16.9334692 2.21876871,16.8033858 C2.6144474,15.5921346 3.14916224,14.4280501 3.81316983,13.3333824 C5.980145,9.82337899 8.22941036,13.8867718 10.0980836,13.8867718 C11.9666996,13.8867718 11.4695868,12.1534924 12.1827971,13.3333824 C12.8511505,14.4269112 13.3916656,15.5896902 13.794259,16.8000524 C13.8533022,16.9322137 13.8537479,17.0822749 13.7952635,17.2147751 C13.7368889,17.3472613 13.6248314,17.4504531 13.4856467,17.5000531 C12.6833967,17.7332867 11.0855382,17.9999981 7.99965585,17.9999981 Z\"\n            id=\"Shape\"></path>\n        </g>\n      </g>\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconFacebookCircle.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport type {SVGProps} from 'react';\n\nexport const IconFacebookCircle = memo<SVGProps<SVGSVGElement>>(\n  function IconFacebookCircle(props) {\n    return (\n      <svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        viewBox=\"0 0 24 24\"\n        width=\"1.33em\"\n        height=\"1.33em\"\n        fill=\"currentColor\"\n        {...props}>\n        <path fill=\"none\" d=\"M0 0h24v24H0z\" />\n        <path d=\"M12 2C6.477 2 2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.879V14.89h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.989C18.343 21.129 22 16.99 22 12c0-5.523-4.477-10-10-10z\" />\n      </svg>\n    );\n  }\n);\n"
  },
  {
    "path": "src/components/Icon/IconGitHub.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport type {SVGProps} from 'react';\n\nexport const IconGitHub = memo<SVGProps<SVGSVGElement>>(function IconGitHub(\n  props\n) {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"1.5em\"\n      height=\"1.5em\"\n      viewBox=\"0 -2 24 24\"\n      fill=\"currentColor\"\n      {...props}>\n      <path d=\"M10 0a10 10 0 0 0-3.16 19.49c.5.1.68-.22.68-.48l-.01-1.7c-2.78.6-3.37-1.34-3.37-1.34-.46-1.16-1.11-1.47-1.11-1.47-.9-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.9 1.52 2.34 1.08 2.91.83.1-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.94 0-1.1.39-1.99 1.03-2.69a3.6 3.6 0 0 1 .1-2.64s.84-.27 2.75 1.02a9.58 9.58 0 0 1 5 0c1.91-1.3 2.75-1.02 2.75-1.02.55 1.37.2 2.4.1 2.64.64.7 1.03 1.6 1.03 2.69 0 3.84-2.34 4.68-4.57 4.93.36.31.68.92.68 1.85l-.01 2.75c0 .26.18.58.69.48A10 10 0 0 0 10 0\"></path>\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconHamburger.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport type {SVGProps} from 'react';\n\nexport const IconHamburger = memo<SVGProps<SVGSVGElement>>(\n  function IconHamburger(props) {\n    return (\n      <svg\n        width=\"1.33em\"\n        height=\"1.33em\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeWidth=\"2\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        {...props}>\n        <line x1=\"3\" y1=\"12\" x2=\"21\" y2=\"12\"></line>\n        <line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"></line>\n        <line x1=\"3\" y1=\"18\" x2=\"21\" y2=\"18\"></line>\n      </svg>\n    );\n  }\n);\n"
  },
  {
    "path": "src/components/Icon/IconHint.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport cn from 'classnames';\n\nexport const IconHint = memo<JSX.IntrinsicElements['svg']>(function IconHint({\n  className,\n}) {\n  return (\n    <svg\n      className={cn('inline -mt-0.5', className)}\n      width=\"12\"\n      height=\"14\"\n      viewBox=\"0 0 12 15\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        d=\"M4.53487 11H5.21954V7.66665H6.55287V11H7.23754C7.32554 10.1986 7.7342 9.53732 8.39754 8.81532C8.47287 8.73398 8.9522 8.23732 9.00887 8.16665C9.47973 7.5784 9.77486 6.86913 9.86028 6.1205C9.9457 5.37187 9.81794 4.61434 9.4917 3.93514C9.16547 3.25594 8.65402 2.6827 8.01628 2.28143C7.37853 1.88016 6.64041 1.66719 5.88692 1.66703C5.13344 1.66686 4.39523 1.87953 3.75731 2.28052C3.11939 2.68152 2.60771 3.25454 2.28118 3.9336C1.95465 4.61266 1.82656 5.37014 1.91167 6.1188C1.99677 6.86747 2.2916 7.57687 2.7622 8.16532C2.81954 8.23665 3.3002 8.73398 3.3742 8.81465C4.0382 9.53732 4.44687 10.1986 4.53487 11ZM4.55287 12.3333V13H7.21954V12.3333H4.55287ZM1.7222 8.99998C1.09433 8.21551 0.700836 7.26963 0.587047 6.2713C0.473258 5.27296 0.643804 4.26279 1.07904 3.35715C1.51428 2.4515 2.19649 1.68723 3.04711 1.15237C3.89772 0.617512 4.88213 0.333824 5.88692 0.333984C6.89172 0.334145 7.87604 0.61815 8.72648 1.15328C9.57692 1.68841 10.2589 2.4529 10.6938 3.35869C11.1288 4.26447 11.299 5.27469 11.1849 6.27299C11.0708 7.27129 10.677 8.21705 10.0489 9.00132C9.63554 9.51598 8.55287 10.3333 8.55287 11.3333V13C8.55287 13.3536 8.41239 13.6927 8.16235 13.9428C7.9123 14.1928 7.57316 14.3333 7.21954 14.3333H4.55287C4.19925 14.3333 3.86011 14.1928 3.61006 13.9428C3.36001 13.6927 3.21954 13.3536 3.21954 13V11.3333C3.21954 10.3333 2.1362 9.51598 1.7222 8.99998Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconInstagram.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport type {SVGProps} from 'react';\n\nexport const IconInstagram = memo<SVGProps<SVGSVGElement>>(\n  function IconInstagram(props) {\n    return (\n      <svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        viewBox=\"0 0 24 24\"\n        width=\"1.33em\"\n        height=\"1.33em\"\n        fill=\"currentColor\"\n        {...props}>\n        <path fill=\"none\" d=\"M0 0h24v24H0z\" />\n        <path d=\"M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6zm0-2a5 5 0 1 1 0 10 5 5 0 0 1 0-10zm6.5-.25a1.25 1.25 0 0 1-2.5 0 1.25 1.25 0 0 1 2.5 0zM12 4c-2.474 0-2.878.007-4.029.058-.784.037-1.31.142-1.798.332-.434.168-.747.369-1.08.703a2.89 2.89 0 0 0-.704 1.08c-.19.49-.295 1.015-.331 1.798C4.006 9.075 4 9.461 4 12c0 2.474.007 2.878.058 4.029.037.783.142 1.31.331 1.797.17.435.37.748.702 1.08.337.336.65.537 1.08.703.494.191 1.02.297 1.8.333C9.075 19.994 9.461 20 12 20c2.474 0 2.878-.007 4.029-.058.782-.037 1.309-.142 1.797-.331.433-.169.748-.37 1.08-.702.337-.337.538-.65.704-1.08.19-.493.296-1.02.332-1.8.052-1.104.058-1.49.058-4.029 0-2.474-.007-2.878-.058-4.029-.037-.782-.142-1.31-.332-1.798a2.911 2.911 0 0 0-.703-1.08 2.884 2.884 0 0 0-1.08-.704c-.49-.19-1.016-.295-1.798-.331C14.925 4.006 14.539 4 12 4zm0-2c2.717 0 3.056.01 4.122.06 1.065.05 1.79.217 2.428.465.66.254 1.216.598 1.772 1.153a4.908 4.908 0 0 1 1.153 1.772c.247.637.415 1.363.465 2.428.047 1.066.06 1.405.06 4.122 0 2.717-.01 3.056-.06 4.122-.05 1.065-.218 1.79-.465 2.428a4.883 4.883 0 0 1-1.153 1.772 4.915 4.915 0 0 1-1.772 1.153c-.637.247-1.363.415-2.428.465-1.066.047-1.405.06-4.122.06-2.717 0-3.056-.01-4.122-.06-1.065-.05-1.79-.218-2.428-.465a4.89 4.89 0 0 1-1.772-1.153 4.904 4.904 0 0 1-1.153-1.772c-.248-.637-.415-1.363-.465-2.428C2.013 15.056 2 14.717 2 12c0-2.717.01-3.056.06-4.122.05-1.066.217-1.79.465-2.428a4.88 4.88 0 0 1 1.153-1.772A4.897 4.897 0 0 1 5.45 2.525c.638-.248 1.362-.415 2.428-.465C8.944 2.013 9.283 2 12 2z\" />\n      </svg>\n    );\n  }\n);\n"
  },
  {
    "path": "src/components/Icon/IconLink.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport type {SVGProps} from 'react';\n\nexport const IconLink = memo<SVGProps<SVGSVGElement>>(function IconLink(props) {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"1.33em\"\n      height=\"1.33em\"\n      viewBox=\"0 -2 24 24\"\n      fill=\"currentColor\"\n      {...props}>\n      <path\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        d=\"M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244\"\n      />\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconNavArrow.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport cn from 'classnames';\n\nexport const IconNavArrow = memo<\n  JSX.IntrinsicElements['svg'] & {\n    /**\n     * The direction the arrow should point.\n     * `start` and `end` are relative to the current locale.\n     * for example, in LTR, `start` is left and `end` is right.\n     */\n    displayDirection: 'start' | 'end' | 'right' | 'left' | 'down';\n  }\n>(function IconNavArrow({displayDirection = 'start', className}) {\n  const classes = cn(\n    'duration-100 ease-in transition',\n    {\n      'rotate-0': displayDirection === 'down',\n      'rotate-90': displayDirection === 'left',\n      '-rotate-90': displayDirection === 'right',\n      'rotate-90 rtl:-rotate-90': displayDirection === 'start',\n      '-rotate-90 rtl:rotate-90': displayDirection === 'end',\n    },\n    className\n  );\n\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n      className={classes}\n      style={{minWidth: 20, minHeight: 20}}>\n      <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-446 -398)\">\n        <path\n          fill=\"currentColor\"\n          fillRule=\"nonzero\"\n          d=\"M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z\"\n          transform=\"translate(356.5 164.5)\"\n        />\n        <polygon points=\"446 418 466 418 466 398 446 398\" />\n      </g>\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconNewPage.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport type {SVGProps} from 'react';\n\nexport const IconNewPage = memo<SVGProps<SVGSVGElement>>(function IconNewPage(\n  props\n) {\n  return (\n    <svg\n      width=\"1em\"\n      height=\"1em\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      {...props}>\n      <path\n        d=\"M20.5001 2H15.5001C15.3675 2 15.2403 2.05268 15.1465 2.14645C15.0528 2.24021 15.0001 2.36739 15.0001 2.5V3.5C15.0001 3.63261 15.0528 3.75979 15.1465 3.85355C15.2403 3.94732 15.3675 4 15.5001 4H18.5901L7.6501 14.94C7.60323 14.9865 7.56604 15.0418 7.54065 15.1027C7.51527 15.1636 7.5022 15.229 7.5022 15.295C7.5022 15.361 7.51527 15.4264 7.54065 15.4873C7.56604 15.5482 7.60323 15.6035 7.6501 15.65L8.3501 16.35C8.39658 16.3969 8.45188 16.4341 8.51281 16.4594C8.57374 16.4848 8.63909 16.4979 8.7051 16.4979C8.7711 16.4979 8.83646 16.4848 8.89738 16.4594C8.95831 16.4341 9.01362 16.3969 9.0601 16.35L20.0001 5.41V8.5C20.0001 8.63261 20.0528 8.75979 20.1465 8.85355C20.2403 8.94732 20.3675 9 20.5001 9H21.5001C21.6327 9 21.7599 8.94732 21.8537 8.85355C21.9474 8.75979 22.0001 8.63261 22.0001 8.5V3.5C22.0001 3.10218 21.8421 2.72064 21.5608 2.43934C21.2795 2.15804 20.8979 2 20.5001 2V2Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        d=\"M21.5 13H20.5C20.3674 13 20.2402 13.0527 20.1464 13.1464C20.0527 13.2402 20 13.3674 20 13.5V20H4V4H10.5C10.6326 4 10.7598 3.94732 10.8536 3.85355C10.9473 3.75979 11 3.63261 11 3.5V2.5C11 2.36739 10.9473 2.24021 10.8536 2.14645C10.7598 2.05268 10.6326 2 10.5 2H3.5C3.10218 2 2.72064 2.15804 2.43934 2.43934C2.15804 2.72064 2 3.10218 2 3.5V20.5C2 20.8978 2.15804 21.2794 2.43934 21.5607C2.72064 21.842 3.10218 22 3.5 22H20.5C20.8978 22 21.2794 21.842 21.5607 21.5607C21.842 21.2794 22 20.8978 22 20.5V13.5C22 13.3674 21.9473 13.2402 21.8536 13.1464C21.7598 13.0527 21.6326 13 21.5 13Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconNote.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\n\nexport const IconNote = memo<JSX.IntrinsicElements['svg']>(function IconNote({\n  className,\n}) {\n  return (\n    <svg\n      className={className}\n      width=\"2em\"\n      height=\"2em\"\n      viewBox=\"0 0 72 72\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <g clipPath=\"url(#clip0_40_48064)\">\n        <path\n          d=\"M24 27C24 25.3431 25.3431 24 27 24H45C46.6569 24 48 25.3431 48 27C48 28.6569 46.6569 30 45 30H27C25.3431 30 24 28.6569 24 27Z\"\n          fill=\"currentColor\"\n        />\n        <path\n          d=\"M24 39C24 37.3431 25.3431 36 27 36H39C40.6569 36 42 37.3431 42 39C42 40.6569 40.6569 42 39 42H27C25.3431 42 24 40.6569 24 39Z\"\n          fill=\"currentColor\"\n        />\n        <path\n          fillRule=\"evenodd\"\n          clipRule=\"evenodd\"\n          d=\"M12 18C12 13.0294 16.0294 9 21 9H51C55.9706 9 60 13.0294 60 18V54C60 58.9706 55.9706 63 51 63H21C16.0294 63 12 58.9706 12 54V18ZM21 15H51C52.6569 15 54 16.3431 54 18V54C54 55.6569 52.6569 57 51 57H21C19.3431 57 18 55.6569 18 54V18C18 16.3431 19.3431 15 21 15Z\"\n          fill=\"currentColor\"\n        />\n      </g>\n      <defs>\n        <clipPath id=\"clip0_40_48064\">\n          <rect width=\"72\" height=\"72\" fill=\"white\" />\n        </clipPath>\n      </defs>\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconPitfall.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\n\nexport const IconPitfall = memo<JSX.IntrinsicElements['svg']>(\n  function IconPitfall({className}) {\n    return (\n      <svg\n        className={className}\n        width=\"2em\"\n        height=\"2em\"\n        viewBox=\"0 0 72 72\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <g clipPath=\"url(#clip0_738_836)\">\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"M27 48L27 57.3409L40.0772 48L55.6975 48C57.1595 48 58.1986 47.0112 58.3851 45.8604C59.1824 40.9398 60 34.619 60 29.625C60 24.7282 59.2125 18.7546 58.4302 14.0813C58.2445 12.9721 57.2326 12 55.7805 12L16.2195 12C14.7674 12 13.7555 12.9721 13.5698 14.0813C12.7875 18.7546 12 24.7282 12 29.625C12 34.619 12.8176 40.9398 13.6149 45.8604C13.8014 47.0112 14.8404 48 16.3025 48H27ZM42 54H55.6975C59.9534 54 63.6271 51.0213 64.3078 46.8201C65.1161 41.8322 66 35.1209 66 29.625C66 24.2196 65.1449 17.8522 64.3478 13.0906C63.6513 8.93026 59.9987 6 55.7805 6H16.2195C12.0013 6 8.34867 8.93026 7.65218 13.0906C6.85505 17.8522 6 24.2196 6 29.625C6 35.1209 6.88391 41.8322 7.69215 46.8201C8.37291 51.0213 12.0466 54 16.3025 54H21L21 63.1704C21 65.6106 23.7581 67.0299 25.7437 65.6116L42 54ZM39 39.3686C39 40.9422 38 41.9912 36 41.9912C34 41.9912 33 40.9422 33 39.3686C33 37.7951 34 36.746 36 36.746C38 36.746 39 37.7951 39 39.3686ZM38.1771 20.2412C38.1771 18.9986 37.1697 17.9912 35.9271 17.9912C34.6845 17.9912 33.6771 18.9986 33.6771 20.2412V31.5956C33.6771 32.8382 34.6845 33.8456 35.9271 33.8456C37.1697 33.8456 38.1771 32.8382 38.1771 31.5956V20.2412Z\"\n            fill=\"currentColor\"\n          />\n        </g>\n        <defs>\n          <clipPath id=\"clip0_738_836\">\n            <rect width=\"72\" height=\"72\" fill=\"white\" />\n          </clipPath>\n        </defs>\n      </svg>\n    );\n  }\n);\n"
  },
  {
    "path": "src/components/Icon/IconRestart.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\n\nexport const IconRestart = memo<JSX.IntrinsicElements['svg']>(\n  function IconRestart({className}) {\n    return (\n      <svg\n        width=\"1em\"\n        height=\"1em\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        className={className}>\n        <path\n          d=\"M13.8982 5.20844C12.4626 4.88688 10.9686 4.93769 9.55821 5.35604L11.8524 3.06184C11.8989 3.0154 11.9357 2.96028 11.9608 2.89961C11.986 2.83894 11.9989 2.77391 11.9989 2.70824C11.9989 2.64256 11.986 2.57754 11.9608 2.51686C11.9357 2.45619 11.8989 2.40107 11.8524 2.35464L11.1456 1.64784C11.0992 1.60139 11.0441 1.56455 10.9834 1.53942C10.9227 1.51428 10.8577 1.50134 10.792 1.50134C10.7263 1.50134 10.6613 1.51428 10.6006 1.53942C10.54 1.56455 10.4848 1.60139 10.4384 1.64784L6.14571 5.94054C6.00654 6.07969 5.89615 6.2449 5.82083 6.42673C5.74551 6.60855 5.70675 6.80343 5.70675 7.00024C5.70675 7.19704 5.74551 7.39192 5.82083 7.57374C5.89615 7.75557 6.00654 7.92078 6.14571 8.05994L10.4387 12.3529C10.5325 12.4465 10.6595 12.4991 10.792 12.4991C10.9245 12.4991 11.0516 12.4465 11.1453 12.3529L11.8527 11.6455C11.9463 11.5518 11.9989 11.4247 11.9989 11.2922C11.9989 11.1598 11.9463 11.0327 11.8527 10.9389L8.77481 7.86104C9.99795 7.16236 11.415 6.8801 12.8125 7.05678C14.21 7.23347 15.5122 7.85953 16.523 8.84064C17.5338 9.82176 18.1983 11.1048 18.4165 12.4964C18.6347 13.888 18.3947 15.3129 17.7328 16.5562C17.0708 17.7996 16.0227 18.7942 14.7463 19.3902C13.47 19.9861 12.0345 20.1511 10.6563 19.8603C9.27798 19.5695 8.03152 18.8387 7.10469 17.778C6.17786 16.7172 5.62086 15.384 5.51761 13.9791C5.51156 13.8512 5.45689 13.7303 5.36477 13.6413C5.27265 13.5522 5.15001 13.5017 5.02191 13.5H4.02081C3.95297 13.4996 3.88574 13.5129 3.8232 13.5392C3.76065 13.5655 3.70408 13.6042 3.6569 13.6529C3.60972 13.7017 3.57291 13.7595 3.54869 13.8228C3.52448 13.8862 3.51336 13.9538 3.51601 14.0216C3.61349 15.5965 4.1473 17.1132 5.0577 18.4019C5.9681 19.6906 7.21917 20.7006 8.6709 21.3188C10.1226 21.937 11.7178 22.139 13.2778 21.9022C14.8378 21.6654 16.3011 20.9992 17.504 19.978C18.7069 18.9569 19.6019 17.6212 20.0889 16.1203C20.5759 14.6195 20.6356 13.0128 20.2614 11.4799C19.8872 9.94705 19.0938 8.54858 17.97 7.44098C16.8462 6.33339 15.4363 5.56037 13.8982 5.20844V5.20844Z\"\n          fill=\"currentColor\"\n        />\n      </svg>\n    );\n  }\n);\n"
  },
  {
    "path": "src/components/Icon/IconRocket.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\n\nexport const IconRocket = memo<\n  JSX.IntrinsicElements['svg'] & {title?: string; size?: 's' | 'md'}\n>(function IconRocket({className, size = 'md'}) {\n  return (\n    <svg\n      className={className}\n      aria-hidden=\"true\"\n      width={size === 's' ? '1.2em' : '1.5em'}\n      height={size === 's' ? '1.2em' : '1.5em'}\n      fill=\"currentColor\"\n      version=\"1.1\"\n      viewBox=\"0 0 1200 1200\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <g fillRule=\"evenodd\">\n        <path d=\"m911.8 288.2c65.051 65.051 65.051 170.6 0 235.65-65.051 65.051-170.6 65.051-235.65 0-65.051-65.051-65.051-170.6 0-235.65 65.051-65.051 170.6-65.051 235.65 0zm-53.051 53.051c-35.75-35.801-93.801-35.801-129.55 0-35.801 35.75-35.801 93.801 0 129.55 35.75 35.801 93.801 35.801 129.55 0 35.801-35.75 35.801-93.801 0-129.55z\" />\n        <path d=\"m1122.2 103.4s96.648 328.1-194.4 619.1c-130.75 130.75-303.25 226.75-440.75 250.5-12.102 2.0508-24.449-1.8984-33.102-10.648l-231.55-234.8c-8.6484-8.8008-12.449-21.301-10.102-33.398 26.102-135.4 135.45-292.2 265.2-421.95 291-291.05 619.1-194.4 619.1-194.4 12.352 3.6016 22 13.25 25.602 25.602zm-67.5 41.898c-70.898-12.898-308.6-35.602-524.15 179.9-112.35 112.35-210.4 245.4-240.4 364.25 0 0 203.05 205.9 203.1 205.9 121.75-26.852 268.4-112.75 381.55-225.9 215.5-215.55 192.8-453.25 179.9-524.15z\" />\n        <path d=\"m151.55 543.85 124 20.648c20.398 3.3984 34.25 22.75 30.801 43.148-3.3984 20.449-22.699 34.25-43.148 30.852l-144.35-24.051c-22.148-3.6992-40.699-18.949-48.602-40-7.9492-21.051-4.0508-44.699 10.199-62.148l122.85-150.15c15.051-18.398 36.898-30 60.551-32.148l179.55-16.301c20.602-1.8984 38.852 13.352 40.75 33.949 1.8516 20.602-13.352 38.852-33.949 40.75l-179.55 16.301c-3.6484 0.35156-7 2.1016-9.3008 4.9492z\" />\n        <path d=\"m656.15 1048.4 134.2-109.8c2.8516-2.3008 4.6016-5.6484 4.9492-9.3008l16.301-179.55c1.8984-20.602 20.148-35.801 40.75-33.949 20.602 1.8984 35.852 20.148 33.949 40.75l-16.301 179.55c-2.1484 23.648-13.75 45.5-32.148 60.551l-150.15 122.85c-17.449 14.25-41.102 18.148-62.148 10.199-21.051-7.8984-36.301-26.449-40-48.602l-29.25-175.7c-3.3984-20.398 10.398-39.75 30.801-43.148 20.449-3.3984 39.75 10.449 43.148 30.852l25.898 155.3z\" />\n        <path d=\"m310.9 560.4c-14.648-14.648-14.648-38.398 0-53.051 14.648-14.648 38.398-14.648 53.051 0l328.7 328.7c14.648 14.648 14.648 38.398 0 53.051-14.648 14.648-38.398 14.648-53.051 0z\" />\n        <path d=\"m383.95 982.15c14.648-14.602 38.398-14.602 53.051 0 14.602 14.648 14.602 38.398 0 53.051l-91.352 91.301c-14.602 14.648-38.398 14.648-53 0-14.648-14.602-14.648-38.398 0-53z\" />\n        <path d=\"m237.85 909.1c14.648-14.602 38.398-14.602 53.051 0 14.602 14.648 14.602 38.398 0 53.051l-127.85 127.85c-14.648 14.648-38.398 14.648-53.051 0-14.648-14.648-14.648-38.398 0-53.051z\" />\n        <path d=\"m164.8 763c14.648-14.602 38.398-14.602 53.051 0 14.602 14.648 14.602 38.398 0 53.051l-91.352 91.301c-14.602 14.648-38.398 14.648-53 0-14.648-14.602-14.648-38.398 0-53z\" />\n      </g>\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconRss.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport type {SVGProps} from 'react';\n\nexport const IconRss = memo<SVGProps<SVGSVGElement>>(function IconRss(props) {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"1em\"\n      height=\"1em\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={2}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      {...props}>\n      <path d=\"M4 11a9 9 0 0 1 9 9\" />\n      <path d=\"M4 4a16 16 0 0 1 16 16\" />\n      <circle cx={5} cy={19} r={1} />\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconSearch.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport type {SVGProps} from 'react';\n\nexport const IconSearch = memo<SVGProps<SVGSVGElement>>(function IconSearch(\n  props\n) {\n  return (\n    <svg width=\"1em\" height=\"1em\" viewBox=\"0 0 20 20\" {...props}>\n      <path\n        d=\"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z\"\n        stroke=\"currentColor\"\n        fill=\"none\"\n        strokeWidth=\"2\"\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"></path>\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconSolution.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport cn from 'classnames';\n\nexport const IconSolution = memo<JSX.IntrinsicElements['svg']>(\n  function IconSolution({className}) {\n    return (\n      <svg\n        className={cn('inline', className)}\n        width=\"0.75em\"\n        height=\"0.75em\"\n        viewBox=\"0 0 13 13\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <path\n          d=\"M2.21908 8.74479V12.7448H0.885742V0.078125H7.14041C7.26418 0.0781911 7.3855 0.112714 7.49076 0.177827C7.59602 0.242939 7.68108 0.336071 7.73641 0.446792L8.21908 1.41146H12.2191C12.3959 1.41146 12.5655 1.4817 12.6905 1.60672C12.8155 1.73174 12.8857 1.90131 12.8857 2.07812V9.41146C12.8857 9.58827 12.8155 9.75784 12.6905 9.88286C12.5655 10.0079 12.3959 10.0781 12.2191 10.0781H7.96441C7.84063 10.0781 7.71932 10.0435 7.61406 9.97842C7.50879 9.91331 7.42374 9.82018 7.36841 9.70946L6.88574 8.74479H2.21908ZM2.21908 1.41146V7.41146H7.70974L8.37641 8.74479H11.5524V2.74479H7.39508L6.72841 1.41146H2.21908Z\"\n          fill=\"currentColor\"\n        />\n      </svg>\n    );\n  }\n);\n"
  },
  {
    "path": "src/components/Icon/IconTerminal.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\n\nexport const IconTerminal = memo<JSX.IntrinsicElements['svg']>(\n  function IconTerminal({className}) {\n    return (\n      <svg\n        className={className}\n        width=\"1em\"\n        height=\"1em\"\n        viewBox=\"0 0 18 18\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <path\n          d=\"M2.40299 2.61279H14.403C14.5798 2.61279 14.7494 2.68303 14.8744 2.80806C14.9994 2.93308 15.0697 3.10265 15.0697 3.27946V13.9461C15.0697 14.1229 14.9994 14.2925 14.8744 14.4175C14.7494 14.5426 14.5798 14.6128 14.403 14.6128H2.40299C2.22618 14.6128 2.05661 14.5426 1.93159 14.4175C1.80657 14.2925 1.73633 14.1229 1.73633 13.9461V3.27946C1.73633 3.10265 1.80657 2.93308 1.93159 2.80806C2.05661 2.68303 2.22618 2.61279 2.40299 2.61279ZM8.403 10.6128V11.9461H12.403V10.6128H8.403ZM6.01233 8.61279L4.12699 10.4981L5.06966 11.4415L7.89833 8.61279L5.06966 5.78413L4.12699 6.72746L6.01233 8.61279Z\"\n          fill=\"currentColor\"\n        />\n      </svg>\n    );\n  }\n);\n"
  },
  {
    "path": "src/components/Icon/IconThreads.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport type {SVGProps} from 'react';\n\nexport const IconThreads = memo<SVGProps<SVGSVGElement>>(function IconThreads(\n  props\n) {\n  return (\n    <svg\n      aria-label=\"Threads\"\n      viewBox=\"0 0 192 192\"\n      height=\"1.40em\"\n      width=\"1.40em\"\n      fill=\"currentColor\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      {...props}>\n      <path\n        className=\"x19hqcy\"\n        d=\"M141.537 88.9883C140.71 88.5919 139.87 88.2104 139.019 87.8451C137.537 60.5382 122.616 44.905 97.5619 44.745C97.4484 44.7443 97.3355 44.7443 97.222 44.7443C82.2364 44.7443 69.7731 51.1409 62.102 62.7807L75.881 72.2328C81.6116 63.5383 90.6052 61.6848 97.2286 61.6848C97.3051 61.6848 97.3819 61.6848 97.4576 61.6855C105.707 61.7381 111.932 64.1366 115.961 68.814C118.893 72.2193 120.854 76.925 121.825 82.8638C114.511 81.6207 106.601 81.2385 98.145 81.7233C74.3247 83.0954 59.0111 96.9879 60.0396 116.292C60.5615 126.084 65.4397 134.508 73.775 140.011C80.8224 144.663 89.899 146.938 99.3323 146.423C111.79 145.74 121.563 140.987 128.381 132.296C133.559 125.696 136.834 117.143 138.28 106.366C144.217 109.949 148.617 114.664 151.047 120.332C155.179 129.967 155.42 145.8 142.501 158.708C131.182 170.016 117.576 174.908 97.0135 175.059C74.2042 174.89 56.9538 167.575 45.7381 153.317C35.2355 139.966 29.8077 120.682 29.6052 96C29.8077 71.3178 35.2355 52.0336 45.7381 38.6827C56.9538 24.4249 74.2039 17.11 97.0132 16.9405C119.988 17.1113 137.539 24.4614 149.184 38.788C154.894 45.8136 159.199 54.6488 162.037 64.9503L178.184 60.6422C174.744 47.9622 169.331 37.0357 161.965 27.974C147.036 9.60668 125.202 0.195148 97.0695 0H96.9569C68.8816 0.19447 47.2921 9.6418 32.7883 28.0793C19.8819 44.4864 13.2244 67.3157 13.0007 95.9325L13 96L13.0007 96.0675C13.2244 124.684 19.8819 147.514 32.7883 163.921C47.2921 182.358 68.8816 191.806 96.9569 192H97.0695C122.03 191.827 139.624 185.292 154.118 170.811C173.081 151.866 172.51 128.119 166.26 113.541C161.776 103.087 153.227 94.5962 141.537 88.9883ZM98.4405 129.507C88.0005 130.095 77.1544 125.409 76.6196 115.372C76.2232 107.93 81.9158 99.626 99.0812 98.6368C101.047 98.5234 102.976 98.468 104.871 98.468C111.106 98.468 116.939 99.0737 122.242 100.233C120.264 124.935 108.662 128.946 98.4405 129.507Z\"></path>\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconTwitter.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\nimport type {SVGProps} from 'react';\n\nexport const IconTwitter = memo<SVGProps<SVGSVGElement>>(function IconTwitter(\n  props\n) {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 512 512\"\n      height=\"1.30em\"\n      width=\"1.30em\"\n      fill=\"currentColor\"\n      {...props}>\n      <path fill=\"none\" d=\"M0 0h24v24H0z\" />\n      <path d=\"M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z\" />\n    </svg>\n  );\n});\n"
  },
  {
    "path": "src/components/Icon/IconWarning.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {memo} from 'react';\n\nexport const IconWarning = memo<JSX.IntrinsicElements['svg']>(\n  function IconWarning({className}) {\n    return (\n      <svg\n        className={className}\n        width=\"2em\"\n        height=\"2em\"\n        viewBox=\"0 0 72 72\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <g>\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"M36 63C50.9117 63 63 50.9117 63 36C63 21.0883 50.9117 9 36 9C21.0883 9 9 21.0883 9 36C9 50.9117 21.0883 63 36 63ZM36 69C54.2254 69 69 54.2254 69 36C69 17.7746 54.2254 3 36 3C17.7746 3 3 17.7746 3 36C3 54.2254 17.7746 69 36 69ZM39.7515 47.9926C39.7515 49.7926 38.5015 50.9926 36.0015 50.9926C33.5015 50.9926 32.2515 49.7926 32.2515 47.9926C32.2515 46.1926 33.5015 44.9926 36.0015 44.9926C38.5015 44.9926 39.7515 46.1926 39.7515 47.9926ZM38.6265 23.6199C38.6265 22.1701 37.4512 20.9949 36.0015 20.9949C34.5517 20.9949 33.3765 22.1701 33.3765 23.6199V38.5443C33.3765 39.9941 34.5517 41.1693 36.0015 41.1693C37.4512 41.1693 38.6265 39.9941 38.6265 38.5443V23.6199Z\"\n            fill=\"currentColor\"\n          />\n        </g>\n      </svg>\n    );\n  }\n);\n"
  },
  {
    "path": "src/components/Layout/Footer.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nimport NextLink from 'next/link';\nimport cn from 'classnames';\nimport {ExternalLink} from 'components/ExternalLink';\nimport {IconFacebookCircle} from 'components/Icon/IconFacebookCircle';\nimport {IconTwitter} from 'components/Icon/IconTwitter';\nimport {IconBsky} from 'components/Icon/IconBsky';\nimport {IconGitHub} from 'components/Icon/IconGitHub';\n\nexport function Footer() {\n  const socialLinkClasses = 'hover:text-primary dark:text-primary-dark';\n  return (\n    <footer className={cn('text-secondary dark:text-secondary-dark')}>\n      <div className=\"grid grid-cols-2 md:grid-cols-3 xl:grid-cols-5 gap-x-12 gap-y-8 max-w-7xl mx-auto\">\n        <div className=\"col-span-2 md:col-span-1 justify-items-start mt-3.5\">\n          <ExternalLink\n            href=\"https://opensource.fb.com/\"\n            aria-label=\"Meta Open Source\">\n            <div>\n              <svg\n                width=\"160\"\n                height=\"19\"\n                viewBox=\"0 0 160 19\"\n                fill=\"none\"\n                xmlns=\"http://www.w3.org/2000/svg\"\n                className=\"text-primary dark:text-primary-dark\">\n                <path\n                  d=\"M22.0605 3.62598H24.3349L28.202 10.6212L32.0691 3.62598H34.2942V15.1206H32.4387V6.31077L29.0476 12.4111H27.307L23.9162 6.31077V15.1206H22.0605V3.62598Z\"\n                  fill=\"currentColor\"\n                />\n                <path\n                  d=\"M40.2785 15.3259C39.4191 15.3259 38.6638 15.1357 38.0124 14.7554C37.367 14.3812 36.8394 13.8336 36.4895 13.1747C36.1253 12.5015 35.9433 11.7297 35.9434 10.8594C35.9434 9.97825 36.1213 9.19824 36.4771 8.5194C36.8329 7.84077 37.3269 7.30982 37.9592 6.92653C38.5913 6.54347 39.3179 6.3519 40.139 6.35181C40.9546 6.35181 41.6566 6.54477 42.2449 6.9307C42.8334 7.31658 43.2863 7.85713 43.6038 8.55232C43.9212 9.24748 44.08 10.063 44.0801 10.9989V11.5081H37.7826C37.8975 12.2088 38.1808 12.7602 38.6323 13.1625C39.0839 13.5648 39.6546 13.7659 40.3443 13.766C40.8971 13.766 41.3733 13.6839 41.7729 13.5196C42.1723 13.3554 42.5473 13.1063 42.8977 12.7724L43.8831 13.9794C42.9031 14.8771 41.7016 15.326 40.2785 15.3259ZM41.6334 8.50718C41.2447 8.11027 40.7356 7.91184 40.1062 7.91189C39.4931 7.91189 38.9799 8.11439 38.5667 8.51941C38.1533 8.92464 37.8919 9.46931 37.7826 10.1534H42.2984C42.2436 9.45273 42.0219 8.90398 41.6334 8.50716V8.50718Z\"\n                  fill=\"currentColor\"\n                />\n                <path\n                  d=\"M46.3308 8.07609H44.623V6.55715H46.3308V4.04468H48.1209V6.55715H50.7153V8.07609H48.1209V11.9267C48.1209 12.5672 48.2303 13.0243 48.4492 13.298C48.6682 13.5717 49.0431 13.7086 49.5741 13.7084C49.7742 13.7102 49.9743 13.7006 50.1734 13.6797C50.3376 13.6606 50.5183 13.6346 50.7153 13.6017V15.1043C50.4905 15.1692 50.2614 15.2186 50.0297 15.252C49.7647 15.2911 49.4971 15.3103 49.2292 15.3095C47.2969 15.3095 46.3308 14.2531 46.3308 12.1403L46.3308 8.07609Z\"\n                  fill=\"currentColor\"\n                />\n                <path\n                  d=\"M60.0415 15.1207H58.2844V13.9219C57.9815 14.3629 57.572 14.7202 57.094 14.9606C56.6123 15.204 56.0649 15.3258 55.4519 15.3259C54.6966 15.3259 54.0274 15.133 53.4444 14.7472C52.8614 14.3611 52.4029 13.8302 52.0692 13.1543C51.7353 12.4784 51.5684 11.7052 51.5684 10.8348C51.5684 9.95904 51.738 9.1845 52.0774 8.5112C52.4167 7.83795 52.8861 7.30972 53.4855 6.92653C54.0847 6.54347 54.773 6.3519 55.5503 6.35181C56.1361 6.35181 56.6616 6.46538 57.1269 6.69253C57.5858 6.91465 57.9833 7.24591 58.2844 7.65731V6.55718H60.0415V15.1207ZM58.2516 9.55395C58.06 9.06686 57.7576 8.68232 57.3444 8.40033C56.9311 8.11861 56.4535 7.97771 55.9116 7.97762C55.1452 7.97762 54.5349 8.23487 54.0807 8.74939C53.6264 9.2639 53.3993 9.95905 53.3993 10.8349C53.3993 11.7162 53.6182 12.4141 54.0561 12.9285C54.4939 13.443 55.0877 13.7003 55.8377 13.7003C56.3906 13.7003 56.8833 13.5579 57.3156 13.2733C57.7405 12.9979 58.068 12.5957 58.2516 12.1238V9.55395Z\"\n                  fill=\"currentColor\"\n                />\n                <path\n                  d=\"M64.4113 11.7585C64.1266 11.0332 63.9843 10.2382 63.9844 9.3733C63.9844 8.50853 64.1267 7.71351 64.4113 6.98824C64.6823 6.28392 65.0929 5.6416 65.6182 5.09981C66.1399 4.56496 66.7659 4.14298 67.4573 3.86003C68.1634 3.56716 68.9379 3.4207 69.7808 3.42065C70.6238 3.42065 71.3984 3.56711 72.1045 3.86003C72.796 4.14302 73.422 4.56499 73.9437 5.09981C74.4689 5.64171 74.8795 6.284 75.1507 6.98824C75.4351 7.71351 75.5774 8.50853 75.5776 9.3733C75.5776 10.2382 75.4353 11.0333 75.1507 11.7585C74.8795 12.4627 74.4689 13.105 73.9437 13.6469C73.422 14.1818 72.796 14.6038 72.1045 14.8867C71.3984 15.1794 70.6239 15.3259 69.7808 15.3259C68.938 15.3259 68.1635 15.1795 67.4573 14.8867C66.7658 14.6038 66.1399 14.1818 65.6182 13.6469C65.0929 13.1051 64.6823 12.4628 64.4113 11.7585ZM73.6152 9.3733C73.6152 8.54697 73.451 7.81763 73.1226 7.18529C72.7942 6.55303 72.3413 6.05904 71.7637 5.70331C71.1862 5.34753 70.5252 5.16962 69.7808 5.16958C69.0365 5.16958 68.3756 5.34749 67.7981 5.70331C67.2205 6.05909 66.7676 6.55308 66.4392 7.18529C66.1108 7.81741 65.9466 8.54674 65.9466 9.3733C65.9466 10.1999 66.1108 10.9293 66.4392 11.5615C66.7677 12.1937 67.2206 12.6877 67.7981 13.0434C68.3755 13.3993 69.0364 13.5773 69.7808 13.5772C70.5252 13.5772 71.1862 13.3993 71.7637 13.0434C72.3413 12.6877 72.7942 12.1937 73.1226 11.5615C73.451 10.9292 73.6152 10.1998 73.6152 9.3733V9.3733Z\"\n                  fill=\"currentColor\"\n                />\n                <path\n                  d=\"M77.2188 6.55718H78.9839V7.74763C79.2856 7.30796 79.6938 6.95201 80.1705 6.71309C80.6492 6.47237 81.1952 6.35194 81.8084 6.35181C82.5637 6.35181 83.2342 6.54477 83.8199 6.9307C84.4056 7.31658 84.8641 7.84615 85.1952 8.5194C85.5263 9.19264 85.6919 9.96719 85.6919 10.843C85.6919 11.7133 85.5223 12.4865 85.1829 13.1625C84.8436 13.8386 84.3742 14.3681 83.7747 14.7512C83.1755 15.1343 82.4873 15.3258 81.71 15.3259C81.1353 15.3259 80.618 15.2165 80.1581 14.9976C79.7045 14.7837 79.31 14.4624 79.0087 14.0616V18.569H77.2188V6.55718ZM79.9159 13.2774C80.3291 13.5594 80.8067 13.7004 81.3487 13.7003C82.1148 13.7003 82.7251 13.443 83.1796 12.9285C83.6339 12.414 83.861 11.7188 83.861 10.843C83.861 9.96172 83.6421 9.26383 83.2042 8.74937C82.7662 8.23486 82.1723 7.9776 81.4226 7.9776C80.8697 7.9776 80.377 8.11989 79.9447 8.40448C79.5197 8.67987 79.1923 9.08202 79.0087 9.55393V12.1238C79.2002 12.611 79.5026 12.9956 79.9159 13.2774Z\"\n                  fill=\"currentColor\"\n                />\n                <path\n                  d=\"M91.177 15.3259C90.3176 15.3259 89.5622 15.1357 88.9109 14.7554C88.2654 14.3812 87.7377 13.8336 87.3878 13.1747C87.0236 12.5015 86.8417 11.7297 86.8418 10.8594C86.8418 9.97825 87.0197 9.19824 87.3754 8.5194C87.7312 7.84077 88.2252 7.30982 88.8574 6.92653C89.4896 6.54347 90.2163 6.3519 91.0373 6.35181C91.8528 6.35181 92.5548 6.54477 93.1434 6.9307C93.7317 7.31658 94.1846 7.85713 94.5022 8.55232C94.8196 9.24748 94.9782 10.063 94.9783 10.9989V11.5081H88.6809C88.7958 12.2088 89.0791 12.7602 89.5308 13.1625C89.9824 13.5648 90.553 13.7659 91.2426 13.766C91.7954 13.766 92.2716 13.6839 92.6712 13.5196C93.0708 13.3554 93.4457 13.1063 93.796 12.7724L94.7812 13.9794C93.8014 14.8771 92.6 15.326 91.177 15.3259ZM92.5315 8.50718C92.1428 8.11027 91.6338 7.91184 91.0044 7.91189C90.3914 7.91189 89.8782 8.11439 89.465 8.51941C89.0517 8.92464 88.7903 9.46931 88.6809 10.1534H93.1966C93.1419 9.45273 92.9202 8.90398 92.5315 8.50716V8.50718Z\"\n                  fill=\"currentColor\"\n                />\n                <path\n                  d=\"M96.4883 6.55718H98.2536V7.80515C98.9158 6.83621 99.8381 6.35176 101.021 6.35181C102.039 6.35181 102.821 6.66928 103.369 7.30422C103.916 7.93929 104.19 8.84793 104.19 10.0301V15.1207H102.4V10.2436C102.4 9.44454 102.258 8.85615 101.973 8.47842C101.688 8.10074 101.242 7.9119 100.635 7.9119C100.104 7.9119 99.6356 8.04872 99.2307 8.32238C98.8255 8.59617 98.508 8.97932 98.2783 9.47183V15.1207H96.4883L96.4883 6.55718Z\"\n                  fill=\"currentColor\"\n                />\n                <path\n                  d=\"M116.875 11.8694C116.875 12.9586 116.499 13.8071 115.746 14.4147C114.994 15.0222 113.914 15.3259 112.507 15.3259C111.451 15.3259 110.511 15.1262 109.687 14.7266C108.863 14.3272 108.221 13.7469 107.762 12.9859L109.157 11.8858C109.54 12.466 110.019 12.8971 110.594 13.1789C111.169 13.4609 111.829 13.6018 112.573 13.6018C113.323 13.6018 113.906 13.4594 114.322 13.1747C114.738 12.8902 114.946 12.5043 114.946 12.0171C114.946 11.5957 114.801 11.2468 114.511 10.9703C114.221 10.694 113.728 10.4846 113.033 10.3422L111.309 9.98094C109.196 9.54304 108.139 8.47567 108.139 6.77883C108.139 6.10558 108.315 5.51714 108.665 5.01352C109.015 4.51002 109.512 4.11867 110.155 3.83947C110.798 3.56031 111.552 3.4207 112.417 3.42065C114.338 3.42065 115.775 4.16509 116.727 5.65397L115.315 6.66391C114.976 6.14939 114.572 5.76759 114.104 5.51849C113.636 5.26943 113.068 5.14492 112.401 5.14497C111.662 5.14497 111.088 5.28041 110.681 5.55128C110.273 5.82225 110.069 6.20952 110.069 6.7131C110.069 7.09629 110.202 7.40557 110.467 7.64091C110.732 7.87626 111.196 8.0651 111.859 8.20744L113.583 8.56874C115.778 9.02855 116.875 10.1288 116.875 11.8694Z\"\n                  fill=\"currentColor\"\n                />\n                <path\n                  d=\"M118.677 13.1831C118.308 12.5097 118.123 11.7297 118.123 10.843C118.123 9.95083 118.308 9.16809 118.677 8.4948C119.034 7.83483 119.569 7.28851 120.221 6.91833C120.88 6.54065 121.645 6.3518 122.516 6.35181C123.386 6.35181 124.151 6.54065 124.81 6.91833C125.463 7.28862 125.998 7.83491 126.354 8.4948C126.724 9.16805 126.908 9.95079 126.908 10.843C126.908 11.7297 126.724 12.5097 126.354 13.1831C125.998 13.8429 125.463 14.3892 124.81 14.7594C124.151 15.1371 123.386 15.3259 122.516 15.3259C121.651 15.3259 120.887 15.1371 120.225 14.7594C119.571 14.3902 119.034 13.8438 118.677 13.1831ZM125.077 10.843C125.077 9.98377 124.843 9.29408 124.375 8.77396C123.907 8.25393 123.288 7.99394 122.516 7.994C121.744 7.994 121.124 8.25398 120.656 8.77396C120.188 9.29399 119.954 9.98368 119.954 10.843C119.954 11.6969 120.188 12.3839 120.656 12.9039C121.124 13.4239 121.744 13.6839 122.516 13.6839C123.288 13.6839 123.907 13.4239 124.375 12.9039C124.843 12.3839 125.077 11.6969 125.077 10.843Z\"\n                  fill=\"currentColor\"\n                />\n                <path\n                  d=\"M135.907 15.1206H134.141V13.8891C133.484 14.847 132.576 15.3259 131.415 15.3259C130.408 15.3259 129.635 15.0084 129.096 14.3735C128.557 13.7385 128.287 12.8299 128.287 11.6475V6.55713H130.077V11.4341C130.077 12.2278 130.217 12.8148 130.496 13.1953C130.775 13.5758 131.213 13.766 131.809 13.7659C132.324 13.7659 132.781 13.6305 133.18 13.3595C133.58 13.0885 133.892 12.7095 134.116 12.2223V6.55713H135.907L135.907 15.1206Z\"\n                  fill=\"currentColor\"\n                />\n                <path\n                  d=\"M137.877 6.55733H139.642V7.8709C140.195 6.91311 140.95 6.43417 141.908 6.43408C142.22 6.43408 142.475 6.46146 142.672 6.5162V8.16659C142.403 8.12858 142.131 8.10936 141.859 8.10907C140.786 8.10907 140.055 8.59074 139.667 9.55408V15.1208H137.877L137.877 6.55733Z\"\n                  fill=\"currentColor\"\n                />\n                <path\n                  d=\"M143.737 8.49063C144.079 7.83229 144.601 7.28535 145.244 6.9143C145.889 6.53944 146.645 6.35194 147.51 6.35181C149.119 6.35181 150.318 6.98952 151.106 8.26496L149.71 9.20918C149.431 8.78768 149.119 8.47975 148.774 8.2854C148.429 8.09118 148.013 7.99405 147.526 7.994C146.782 7.994 146.18 8.25534 145.72 8.778C145.26 9.30085 145.03 9.98647 145.03 10.8348C145.03 11.727 145.255 12.4249 145.703 12.9285C146.152 13.4321 146.785 13.6839 147.6 13.6839C148.034 13.6874 148.463 13.5845 148.848 13.3841C149.218 13.1962 149.536 12.9215 149.776 12.5836L151.024 13.6839C150.186 14.7786 149.031 15.326 147.559 15.3259C146.678 15.3259 145.91 15.1412 145.256 14.7718C144.609 14.4099 144.081 13.8679 143.737 13.2117C143.378 12.5413 143.199 11.7544 143.199 10.8512C143.199 9.95352 143.378 9.16666 143.737 8.49063Z\"\n                  fill=\"currentColor\"\n                />\n                <path\n                  d=\"M156.195 15.3259C155.335 15.3259 154.58 15.1357 153.928 14.7554C153.283 14.3812 152.755 13.8336 152.405 13.1747C152.041 12.5015 151.859 11.7297 151.859 10.8594C151.859 9.97825 152.037 9.19824 152.393 8.5194C152.749 7.84077 153.243 7.30982 153.875 6.92653C154.507 6.54347 155.234 6.3519 156.055 6.35181C156.87 6.35181 157.572 6.54477 158.161 6.9307C158.749 7.31658 159.202 7.85713 159.52 8.55232C159.837 9.24748 159.996 10.063 159.996 10.9989V11.5081H153.698C153.814 12.2088 154.097 12.7602 154.548 13.1625C155 13.5648 155.571 13.7659 156.26 13.766C156.813 13.766 157.289 13.6839 157.689 13.5196C158.088 13.3554 158.463 13.1063 158.814 12.7724L159.799 13.9794C158.819 14.8771 157.618 15.326 156.195 15.3259ZM157.549 8.50718C157.161 8.11027 156.651 7.91184 156.022 7.91189C155.409 7.91189 154.896 8.11439 154.483 8.51941C154.069 8.92464 153.808 9.46931 153.698 10.1534H158.214C158.159 9.45273 157.938 8.90398 157.549 8.50716V8.50718Z\"\n                  fill=\"currentColor\"\n                />\n                <path\n                  d=\"M5.26022 3.23511C5.25436 3.23511 5.24854 3.23513 5.24268 3.23516L5.21875 5.21191C5.22423 5.21185 5.22969 5.2118 5.23518 5.2118C6.53629 5.2118 7.54551 6.23768 9.73906 9.93252L9.87278 10.1575L9.88153 10.1722L11.1094 8.32979L11.1009 8.31556C10.812 7.84556 10.5344 7.41312 10.2681 7.01826C9.95934 6.56075 9.66404 6.15204 9.37746 5.78713C7.92635 3.93952 6.71249 3.23511 5.26022 3.23511Z\"\n                  fill=\"url(#paint0_linear_627_396207)\"\n                />\n                <path\n                  d=\"M5.24198 3.23516C3.78266 3.24267 2.49251 4.18633 1.56092 5.63032C1.55819 5.63455 1.55546 5.63879 1.55273 5.64302L3.26279 6.57377C3.26556 6.56957 3.26836 6.56535 3.27114 6.56117C3.81514 5.7421 4.49212 5.21969 5.21805 5.21191C5.22353 5.21185 5.229 5.21181 5.23448 5.21181L5.2595 3.23511C5.25364 3.23511 5.24783 3.23513 5.24198 3.23516Z\"\n                  fill=\"url(#paint1_linear_627_396207)\"\n                />\n                <path\n                  d=\"M1.56088 5.63037C1.55816 5.6346 1.55543 5.63884 1.5527 5.64307C0.94054 6.596 0.484192 7.76537 0.237567 9.02689C0.236499 9.03235 0.235435 9.03781 0.234375 9.04329L2.15555 9.49659C2.15655 9.49111 2.15756 9.48562 2.15857 9.48015C2.36393 8.37149 2.75488 7.34323 3.26274 6.57382C3.26552 6.56962 3.26831 6.5654 3.2711 6.56122L1.56088 5.63037Z\"\n                  fill=\"url(#paint2_linear_627_396207)\"\n                />\n                <path\n                  d=\"M2.15979 9.48011L0.238778 9.02686C0.23771 9.03231 0.236646 9.03778 0.235585 9.04325C0.101104 9.73704 0.0326863 10.442 0.03125 11.1487C0.03125 11.1544 0.03125 11.1601 0.03125 11.1658L2.00149 11.3421C2.00133 11.3364 2.00117 11.3307 2.00103 11.3249C2.00007 11.284 1.99958 11.2424 1.99956 11.2003C2.00054 10.6288 2.05316 10.0586 2.15678 9.49655C2.15776 9.49107 2.15878 9.48558 2.15979 9.48011Z\"\n                  fill=\"url(#paint3_linear_627_396207)\"\n                />\n                <path\n                  d=\"M2.06148 11.9568C2.02614 11.7537 2.00611 11.5482 2.00156 11.3421C2.0014 11.3363 2.00124 11.3307 2.0011 11.3249L0.031335 11.1487C0.031335 11.1544 0.031335 11.1601 0.031335 11.1658V11.1669C0.0292535 11.5801 0.0653944 11.9925 0.139296 12.399C0.140327 12.4045 0.14134 12.4099 0.142386 12.4154L2.06448 11.9732C2.06345 11.9678 2.06247 11.9623 2.06148 11.9568Z\"\n                  fill=\"url(#paint4_linear_627_396207)\"\n                />\n                <path\n                  d=\"M2.50976 12.9765C2.29536 12.7425 2.14362 12.405 2.06386 11.9732C2.06285 11.9678 2.06187 11.9623 2.06088 11.9568L0.138672 12.399C0.139703 12.4045 0.140716 12.4099 0.141762 12.4154C0.28705 13.1782 0.571996 13.8139 0.980035 14.2949C0.983663 14.2991 0.987305 14.3034 0.990959 14.3077L2.52121 12.9888C2.51738 12.9848 2.51355 12.9807 2.50976 12.9765Z\"\n                  fill=\"url(#paint5_linear_627_396207)\"\n                />\n                <path\n                  d=\"M8.20487 7.50854C7.04655 9.28523 6.34486 10.3996 6.34486 10.3996C4.80187 12.8183 4.26806 13.3604 3.409 13.3604C3.05054 13.3604 2.75107 13.2328 2.52164 12.9888C2.51782 12.9848 2.51398 12.9807 2.51019 12.9765L0.980469 14.2949C0.984097 14.2991 0.987738 14.3034 0.991392 14.3077C1.5548 14.9644 2.35009 15.3288 3.33393 15.3288C4.82242 15.3288 5.89296 14.6271 7.79608 11.3004C7.79608 11.3004 8.58943 9.8994 9.1352 8.93436C8.79713 8.38854 8.48948 7.91597 8.20487 7.50854Z\"\n                  fill=\"#0082FB\"\n                />\n                <path\n                  d=\"M10.2688 4.7041C10.2649 4.70825 10.261 4.71248 10.2571 4.71664C9.94322 5.05596 9.64935 5.41323 9.37695 5.78663C9.66354 6.15154 9.95939 6.56105 10.2682 7.01855C10.6321 6.45684 10.9718 6.00189 11.3048 5.6532C11.3087 5.64907 11.3126 5.64504 11.3166 5.64094L10.2688 4.7041Z\"\n                  fill=\"url(#paint6_linear_627_396207)\"\n                />\n                <path\n                  d=\"M15.8912 4.53007C15.0834 3.71396 14.1202 3.23511 13.0905 3.23511C12.0047 3.23511 11.0914 3.83012 10.2677 4.70423C10.2637 4.70837 10.2598 4.71261 10.2559 4.71677L11.3036 5.65333C11.3075 5.6492 11.3114 5.64517 11.3154 5.64107C11.858 5.0766 12.3832 4.79478 12.9654 4.79478H12.9654C13.592 4.79478 14.1786 5.08975 14.6867 5.60687C14.6906 5.61092 14.6946 5.61494 14.6986 5.61902L15.9032 4.54221C15.8992 4.53815 15.8952 4.53412 15.8912 4.53007Z\"\n                  fill=\"#0082FB\"\n                />\n                <path\n                  d=\"M18.2273 10.8885C18.1821 8.26813 17.2651 5.92556 15.904 4.54218C15.9 4.53811 15.896 4.53408 15.892 4.53003L14.6875 5.60684C14.6915 5.61089 14.6954 5.61491 14.6994 5.61899C15.7233 6.67077 16.4256 8.6271 16.4895 10.8879C16.4897 10.8936 16.4898 10.8993 16.49 10.905L18.2276 10.9056C18.2275 10.8999 18.2274 10.8942 18.2273 10.8885Z\"\n                  fill=\"url(#paint7_linear_627_396207)\"\n                />\n                <path\n                  d=\"M18.2262 10.9056C18.2261 10.8999 18.226 10.8942 18.2259 10.8885L16.4881 10.8879C16.4883 10.8936 16.4884 10.8993 16.4886 10.905C16.4914 11.0111 16.4928 11.1179 16.4928 11.2253C16.4928 11.8417 16.4007 12.34 16.2135 12.6997C16.2107 12.705 16.2079 12.7104 16.2051 12.7157L17.5007 14.0632C17.504 14.0583 17.5071 14.0535 17.5103 14.0486C17.9807 13.3228 18.2276 12.3145 18.2276 11.0918C18.2276 11.0296 18.2272 10.9675 18.2262 10.9056Z\"\n                  fill=\"url(#paint8_linear_627_396207)\"\n                />\n                <path\n                  d=\"M16.2158 12.6997C16.213 12.705 16.2102 12.7104 16.2074 12.7157C16.0453 13.0189 15.814 13.2212 15.5117 13.3096L16.1024 15.1711C16.1806 15.1445 16.2567 15.1147 16.3308 15.0816C16.353 15.0718 16.3749 15.0616 16.3967 15.0512C16.4092 15.0452 16.4217 15.0391 16.4341 15.0329C16.8281 14.8341 17.1672 14.5417 17.4217 14.1812C17.438 14.1587 17.4541 14.1359 17.47 14.1127C17.4811 14.0963 17.4921 14.0798 17.5031 14.0632C17.5063 14.0583 17.5094 14.0534 17.5126 14.0485L16.2158 12.6997Z\"\n                  fill=\"url(#paint9_linear_627_396207)\"\n                />\n                <path\n                  d=\"M15.1349 13.3603C14.9481 13.3648 14.7626 13.3286 14.5911 13.2544L13.9863 15.1602C14.3262 15.2763 14.6889 15.3287 15.0932 15.3287C15.4415 15.3319 15.7878 15.2768 16.1179 15.1654L15.5273 13.3046C15.4001 13.3427 15.2678 13.3615 15.1349 13.3603Z\"\n                  fill=\"url(#paint10_linear_627_396207)\"\n                />\n                <path\n                  d=\"M13.9243 12.7085C13.9206 12.7042 13.9168 12.6999 13.9131 12.6956L12.5215 14.1429C12.5254 14.147 12.5293 14.1512 12.5332 14.1553C13.0167 14.6706 13.4784 14.9903 14.0021 15.1657L14.6064 13.2613C14.3857 13.1665 14.1723 12.9947 13.9243 12.7085Z\"\n                  fill=\"url(#paint11_linear_627_396207)\"\n                />\n                <path\n                  d=\"M13.9142 12.6956C13.4968 12.2101 12.9804 11.4019 12.1682 10.095L11.1097 8.32966L11.1012 8.31543L9.87305 10.1573L9.8818 10.172L10.6318 11.4337C11.3588 12.6503 11.9511 13.5304 12.5226 14.1428C12.5265 14.147 12.5304 14.1512 12.5343 14.1553L13.9254 12.7085C13.9217 12.7042 13.918 12.6999 13.9142 12.6956Z\"\n                  fill=\"url(#paint12_linear_627_396207)\"\n                />\n                <defs>\n                  <linearGradient\n                    id=\"paint0_linear_627_396207\"\n                    x1=\"10.2933\"\n                    y1=\"9.42293\"\n                    x2=\"6.21654\"\n                    y2=\"4.08099\"\n                    gradientUnits=\"userSpaceOnUse\">\n                    <stop offset=\"0.0006\" stopColor=\"#0867DF\" />\n                    <stop offset=\"0.4539\" stopColor=\"#0668E1\" />\n                    <stop offset=\"0.8591\" stopColor=\"#0064E0\" />\n                  </linearGradient>\n                  <linearGradient\n                    id=\"paint1_linear_627_396207\"\n                    x1=\"2.35598\"\n                    y1=\"5.96246\"\n                    x2=\"5.15084\"\n                    y2=\"3.84063\"\n                    gradientUnits=\"userSpaceOnUse\">\n                    <stop offset=\"0.1323\" stopColor=\"#0064DF\" />\n                    <stop offset=\"0.9988\" stopColor=\"#0064E0\" />\n                  </linearGradient>\n                  <linearGradient\n                    id=\"paint2_linear_627_396207\"\n                    x1=\"1.17132\"\n                    y1=\"9.07623\"\n                    x2=\"2.29244\"\n                    y2=\"6.25404\"\n                    gradientUnits=\"userSpaceOnUse\">\n                    <stop offset=\"0.0147\" stopColor=\"#0072EC\" />\n                    <stop offset=\"0.6881\" stopColor=\"#0064DF\" />\n                  </linearGradient>\n                  <linearGradient\n                    id=\"paint3_linear_627_396207\"\n                    x1=\"1.02028\"\n                    y1=\"11.115\"\n                    x2=\"1.15\"\n                    y2=\"9.39138\"\n                    gradientUnits=\"userSpaceOnUse\">\n                    <stop offset=\"0.0731\" stopColor=\"#007CF6\" />\n                    <stop offset=\"0.9943\" stopColor=\"#0072EC\" />\n                  </linearGradient>\n                  <linearGradient\n                    id=\"paint4_linear_627_396207\"\n                    x1=\"1.0917\"\n                    y1=\"12.0512\"\n                    x2=\"0.998912\"\n                    y2=\"11.3606\"\n                    gradientUnits=\"userSpaceOnUse\">\n                    <stop offset=\"0.0731\" stopColor=\"#007FF9\" />\n                    <stop offset=\"1\" stopColor=\"#007CF6\" />\n                  </linearGradient>\n                  <linearGradient\n                    id=\"paint5_linear_627_396207\"\n                    x1=\"1.03663\"\n                    y1=\"12.2326\"\n                    x2=\"1.61491\"\n                    y2=\"13.4591\"\n                    gradientUnits=\"userSpaceOnUse\">\n                    <stop offset=\"0.0731\" stopColor=\"#007FF9\" />\n                    <stop offset=\"1\" stopColor=\"#0082FB\" />\n                  </linearGradient>\n                  <linearGradient\n                    id=\"paint6_linear_627_396207\"\n                    x1=\"9.92449\"\n                    y1=\"6.29781\"\n                    x2=\"10.689\"\n                    y2=\"5.24046\"\n                    gradientUnits=\"userSpaceOnUse\">\n                    <stop offset=\"0.2799\" stopColor=\"#007FF8\" />\n                    <stop offset=\"0.9141\" stopColor=\"#0082FB\" />\n                  </linearGradient>\n                  <linearGradient\n                    id=\"paint7_linear_627_396207\"\n                    x1=\"15.7367\"\n                    y1=\"4.92752\"\n                    x2=\"17.3361\"\n                    y2=\"10.8108\"\n                    gradientUnits=\"userSpaceOnUse\">\n                    <stop stopColor=\"#0082FB\" />\n                    <stop offset=\"0.9995\" stopColor=\"#0081FA\" />\n                  </linearGradient>\n                  <linearGradient\n                    id=\"paint8_linear_627_396207\"\n                    x1=\"17.7208\"\n                    y1=\"11.0359\"\n                    x2=\"16.7086\"\n                    y2=\"13.0813\"\n                    gradientUnits=\"userSpaceOnUse\">\n                    <stop offset=\"0.0619\" stopColor=\"#0081FA\" />\n                    <stop offset=\"1\" stopColor=\"#0080F9\" />\n                  </linearGradient>\n                  <linearGradient\n                    id=\"paint9_linear_627_396207\"\n                    x1=\"15.9065\"\n                    y1=\"14.1657\"\n                    x2=\"16.8526\"\n                    y2=\"13.5213\"\n                    gradientUnits=\"userSpaceOnUse\">\n                    <stop stopColor=\"#027AF3\" />\n                    <stop offset=\"1\" stopColor=\"#0080F9\" />\n                  </linearGradient>\n                  <linearGradient\n                    id=\"paint10_linear_627_396207\"\n                    x1=\"14.4218\"\n                    y1=\"14.2915\"\n                    x2=\"15.7366\"\n                    y2=\"14.2915\"\n                    gradientUnits=\"userSpaceOnUse\">\n                    <stop stopColor=\"#0377EF\" />\n                    <stop offset=\"0.9994\" stopColor=\"#0279F1\" />\n                  </linearGradient>\n                  <linearGradient\n                    id=\"paint11_linear_627_396207\"\n                    x1=\"13.2783\"\n                    y1=\"13.5675\"\n                    x2=\"14.2235\"\n                    y2=\"14.1236\"\n                    gradientUnits=\"userSpaceOnUse\">\n                    <stop offset=\"0.0019\" stopColor=\"#0471E9\" />\n                    <stop offset=\"1\" stopColor=\"#0377EF\" />\n                  </linearGradient>\n                  <linearGradient\n                    id=\"paint12_linear_627_396207\"\n                    x1=\"10.3961\"\n                    y1=\"9.46696\"\n                    x2=\"13.424\"\n                    y2=\"13.274\"\n                    gradientUnits=\"userSpaceOnUse\">\n                    <stop offset=\"0.2765\" stopColor=\"#0867DF\" />\n                    <stop offset=\"1\" stopColor=\"#0471E9\" />\n                  </linearGradient>\n                </defs>\n              </svg>\n            </div>\n          </ExternalLink>\n\n          <div\n            className=\"text-xs text-left rtl:text-right mt-2 pe-0.5\"\n            dir=\"ltr\">\n            Copyright &copy; Meta Platforms, Inc\n          </div>\n          <div\n            className=\"uwu-visible text-xs cursor-pointer hover:text-link hover:dark:text-link-dark hover:underline\"\n            onClick={() => {\n              // @ts-ignore\n              window.__setUwu(false);\n            }}>\n            no uwu plz\n          </div>\n          <div\n            className=\"uwu-hidden text-xs cursor-pointer hover:text-link hover:dark:text-link-dark hover:underline\"\n            onClick={() => {\n              // @ts-ignore\n              window.__setUwu(true);\n            }}>\n            uwu?\n          </div>\n          <div className=\"uwu-visible text-xs\">\n            Logo by\n            <ExternalLink\n              className=\"ms-1\"\n              href=\"https://twitter.com/sawaratsuki1004\">\n              @sawaratsuki1004\n            </ExternalLink>\n          </div>\n        </div>\n        <div className=\"flex flex-col\">\n          <FooterLink href=\"/learn\" isHeader={true}>\n            React を学ぶ\n          </FooterLink>\n          <FooterLink href=\"/learn/\">クイックスタート</FooterLink>\n          <FooterLink href=\"/learn/installation\">インストール</FooterLink>\n          <FooterLink href=\"/learn/describing-the-ui\">UI の記述</FooterLink>\n          <FooterLink href=\"/learn/adding-interactivity\">\n            インタラクティビティの追加\n          </FooterLink>\n          <FooterLink href=\"/learn/managing-state\">state の管理</FooterLink>\n          <FooterLink href=\"/learn/escape-hatches\">避難ハッチ</FooterLink>\n        </div>\n        <div className=\"flex flex-col\">\n          <FooterLink href=\"/reference/react\" isHeader={true}>\n            API リファレンス\n          </FooterLink>\n          <FooterLink href=\"/reference/react\">React APIs</FooterLink>\n          <FooterLink href=\"/reference/react-dom\">React DOM APIs</FooterLink>\n        </div>\n        <div className=\"md:col-start-2 xl:col-start-4 flex flex-col\">\n          <FooterLink href=\"/community\" isHeader={true}>\n            コミュニティ\n          </FooterLink>\n          <FooterLink href=\"https://github.com/facebook/react/blob/main/CODE_OF_CONDUCT.md\">\n            行動規範\n          </FooterLink>\n          <FooterLink href=\"/community/team\">チーム紹介</FooterLink>\n          <FooterLink href=\"/community/docs-contributors\">\n            ドキュメント貢献者\n          </FooterLink>\n          <FooterLink href=\"/community/acknowledgements\">謝辞</FooterLink>\n        </div>\n        <div className=\"flex flex-col\">\n          <FooterLink isHeader={true}>More</FooterLink>\n          <FooterLink href=\"/blog\">ブログ</FooterLink>\n          <FooterLink href=\"https://reactnative.dev/\">React Native</FooterLink>\n          <FooterLink href=\"https://opensource.facebook.com/legal/privacy\">\n            プライバシー\n          </FooterLink>\n          <FooterLink href=\"https://opensource.fb.com/legal/terms/\">\n            利用規約\n          </FooterLink>\n          <div className=\"flex flex-row items-center mt-8 gap-x-2\">\n            <ExternalLink\n              aria-label=\"React on Facebook\"\n              href=\"https://www.facebook.com/react\"\n              className={socialLinkClasses}>\n              <IconFacebookCircle />\n            </ExternalLink>\n            <ExternalLink\n              aria-label=\"React on Twitter\"\n              href=\"https://twitter.com/reactjs\"\n              className={socialLinkClasses}>\n              <IconTwitter />\n            </ExternalLink>\n            <ExternalLink\n              aria-label=\"React on Bluesky\"\n              href=\"https://bsky.app/profile/react.dev\"\n              className={socialLinkClasses}>\n              <IconBsky />\n            </ExternalLink>\n            <ExternalLink\n              aria-label=\"React on Github\"\n              href=\"https://github.com/facebook/react\"\n              className={socialLinkClasses}>\n              <IconGitHub />\n            </ExternalLink>\n          </div>\n        </div>\n      </div>\n    </footer>\n  );\n}\n\nfunction FooterLink({\n  href,\n  children,\n  isHeader = false,\n}: {\n  href?: string;\n  children: React.ReactNode;\n  isHeader?: boolean;\n}) {\n  const classes = cn('border-b inline-block border-transparent', {\n    'text-sm text-primary dark:text-primary-dark': !isHeader,\n    'text-md text-secondary dark:text-secondary-dark my-2 font-bold': isHeader,\n    'hover:border-gray-10': href,\n  });\n\n  if (!href) {\n    return <div className={classes}>{children}</div>;\n  }\n\n  if (href.startsWith('https://')) {\n    return (\n      <div>\n        <ExternalLink href={href} className={classes}>\n          {children}\n        </ExternalLink>\n      </div>\n    );\n  }\n\n  return (\n    <div>\n      <NextLink href={href} className={classes}>\n        {children}\n      </NextLink>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/Layout/HomeContent.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {\n  createContext,\n  memo,\n  useState,\n  useContext,\n  useId,\n  Suspense,\n  useEffect,\n  useRef,\n  useTransition,\n} from 'react';\nimport cn from 'classnames';\nimport NextLink from 'next/link';\n\nimport ButtonLink from '../ButtonLink';\nimport {IconRestart} from '../Icon/IconRestart';\nimport BlogCard from 'components/MDX/BlogCard';\nimport {IconChevron} from 'components/Icon/IconChevron';\nimport {IconSearch} from 'components/Icon/IconSearch';\nimport {Logo} from 'components/Logo';\nimport Link from 'components/MDX/Link';\nimport CodeBlock from 'components/MDX/CodeBlock';\nimport {ExternalLink} from 'components/ExternalLink';\nimport sidebarBlog from '../../sidebarBlog.json';\nimport * as React from 'react';\nimport Image from 'next/image';\n\nfunction Section({children, background = null}) {\n  return (\n    <div\n      className={cn(\n        'mx-auto flex flex-col w-full',\n        background === null && 'max-w-7xl',\n        background === 'left-card' &&\n          'bg-gradient-left dark:bg-gradient-left-dark border-t border-primary/10 dark:border-primary-dark/10 ',\n        background === 'right-card' &&\n          'bg-gradient-right dark:bg-gradient-right-dark border-t border-primary/5 dark:border-primary-dark/5'\n      )}\n      style={{\n        contain: 'content',\n      }}>\n      <div className=\"flex-col gap-2 flex grow w-full my-20 lg:my-32 mx-auto items-center\">\n        {children}\n      </div>\n    </div>\n  );\n}\n\nfunction Header({children}) {\n  // \"lg:max-w-xl\" has been removed in Japanese version\n  return (\n    <h2 className=\"leading-xl font-display text-primary dark:text-primary-dark font-semibold text-5xl lg:text-6xl -mt-4 mb-7 w-full max-w-3xl\">\n      {children}\n    </h2>\n  );\n}\n\nfunction Para({children}) {\n  return (\n    <p className=\"max-w-3xl mx-auto text-lg lg:text-xl text-secondary dark:text-secondary-dark leading-normal\">\n      {children}\n    </p>\n  );\n}\n\nfunction Center({children}) {\n  return (\n    <div className=\"px-5 lg:px-0 max-w-4xl lg:text-center text-white text-opacity-80 flex flex-col items-center justify-center\">\n      {children}\n    </div>\n  );\n}\n\nfunction FullBleed({children}) {\n  return (\n    <div className=\"max-w-7xl mx-auto flex flex-col w-full\">{children}</div>\n  );\n}\n\nfunction CurrentTime() {\n  const [date, setDate] = useState(new Date());\n  const currentTime = date.toLocaleTimeString([], {\n    hour: 'numeric',\n    minute: 'numeric',\n  });\n  useEffect(() => {\n    const msPerMinute = 60 * 1000;\n    let nextMinute = Math.floor(+date / msPerMinute + 1) * msPerMinute;\n\n    const timeout = setTimeout(() => {\n      if (Date.now() > nextMinute) {\n        setDate(new Date());\n      }\n    }, nextMinute - Date.now());\n    return () => clearTimeout(timeout);\n  }, [date]);\n\n  return <span suppressHydrationWarning>{currentTime}</span>;\n}\n\nconst blogSidebar = sidebarBlog.routes[1];\nif (blogSidebar.path !== '/blog') {\n  throw Error('Could not find the blog route in sidebarBlog.json');\n}\nconst recentPosts = blogSidebar.routes.slice(0, 4).map((entry) => ({\n  title: entry.titleForHomepage,\n  icon: entry.icon,\n  date: entry.date,\n  url: entry.path,\n}));\n\nexport function HomeContent() {\n  return (\n    <>\n      <div className=\"ps-0\">\n        <div className=\"mx-5 mt-12 lg:mt-24 mb-20 lg:mb-32 flex flex-col justify-center\">\n          <div className=\"uwu-visible flex justify-center\">\n            <Image\n              alt=\"logo by @sawaratsuki1004\"\n              title=\"logo by @sawaratsuki1004\"\n              loading=\"eager\"\n              width={313}\n              height={160}\n              src=\"/images/uwu.png\"\n            />\n          </div>\n          <Logo\n            className={cn(\n              'uwu-hidden mt-4 mb-3 text-brand dark:text-brand-dark w-24 lg:w-28 self-center text-sm me-0 flex origin-center transition-all ease-in-out'\n            )}\n          />\n          <h1 className=\"uwu-hidden text-5xl font-display lg:text-6xl self-center flex font-semibold leading-snug text-primary dark:text-primary-dark\">\n            React\n          </h1>\n          <p className=\"text-4xl font-display max-w-lg md:max-w-full py-1 text-center text-secondary dark:text-primary-dark leading-snug self-center\">\n            Web とネイティブユーザインターフェースのためのライブラリ\n          </p>\n          <div className=\"mt-5 self-center flex gap-2 w-full sm:w-auto flex-col sm:flex-row\">\n            <ButtonLink\n              href={'/learn'}\n              type=\"primary\"\n              size=\"lg\"\n              className=\"w-full sm:w-auto justify-center\"\n              label=\"React を学ぶ\">\n              React を学ぶ\n            </ButtonLink>\n            <ButtonLink\n              href={'/reference/react'}\n              type=\"secondary\"\n              size=\"lg\"\n              className=\"w-full sm:w-auto justify-center\"\n              label=\"API リファレンス\">\n              API リファレンス\n            </ButtonLink>\n          </div>\n        </div>\n\n        <Section background=\"left-card\">\n          <Center>\n            <Header>\n              コンポーネントから\n              <br className=\"hidden lg:inline\" />\n              ユーザインターフェースを作成\n            </Header>\n            <Para>\n              React ではユーザインターフェースを、{''}\n              コンポーネントと呼ばれる部品を使って構築できます。\n              <Code>Thumbnail</Code>、<Code>LikeButton</Code>、\n              <Code>Video</Code>\n              といった React コンポーネントを書き、{''}\n              それらを組み合わせて画面やページやアプリの全体を組み立てましょう。\n            </Para>\n          </Center>\n          <FullBleed>\n            <Example1 />\n          </FullBleed>\n          <Center>\n            <Para>\n              独りで開発していても、数千の開発者と共同開発していても、{''}\n              React の開発体験は同じです。個人、チーム、大規模な組織によって{''}\n              書かれたさまざまなコンポーネントを、シームレスに組み合わせながら\n              {''}\n              開発できる。それが React の設計理念です。\n            </Para>\n          </Center>\n        </Section>\n\n        <Section background=\"right-card\">\n          <Center>\n            <Header>\n              マークアップとコードから\n              <br className=\"hidden lg:inline\" />\n              コンポーネントを作成\n            </Header>\n            <Para>\n              React コンポーネントは単なる JavaScript の関数です。{''}\n              条件によってコンテンツの表示を変えたければ <Code>if</Code>{' '}\n              文を使いましょう！ リストを表示したいなら配列の <Code>map()</Code>{' '}\n              を使いましょう！ React\n              を学ぶということは、プログラミングを学ぶということなのです。\n            </Para>\n          </Center>\n          <FullBleed>\n            <Example2 />\n          </FullBleed>\n          <Center>\n            <Para>\n              このマークアップ構文は JSX と呼ばれます。React が普及させた\n              JavaScript の構文拡張です。JSX マークアップは関連する{''}\n              レンダリングロジックのすぐそばに配置できるので、React\n              コンポーネントは簡単に作成、保守、削除ができます。\n            </Para>\n          </Center>\n        </Section>\n\n        <Section background=\"left-card\">\n          <Center>\n            <Header>\n              インタラクティブ機能を\n              <br className=\"hidden lg:inline\" />\n              どこでも必要な場所に\n            </Header>\n            <Para>\n              React\n              コンポーネントはデータを受け取り、画面に表示するものを返します。\n              {''}\n              入力フィールドへのタイピングなどのユーザ操作によって{''}\n              新しいデータができたら、コンポーネントにそれを渡します。{''}\n              React が新しいデータに基づいて画面を更新します。\n            </Para>\n          </Center>\n          <FullBleed>\n            <Example3 />\n          </FullBleed>\n          <Center>\n            <Para>\n              ページ全体を React で構築する必要はありません。既存の HTML\n              ページに React を追加すれば、どんな場所にでもインタラクティブな\n              React コンポーネントを表示できます。\n            </Para>\n            <div className=\"flex justify-start w-full lg:justify-center\">\n              <CTA\n                color=\"gray\"\n                icon=\"code\"\n                href=\"/learn/add-react-to-an-existing-project\">\n                既存のページに React を追加する\n              </CTA>\n            </div>\n          </Center>\n        </Section>\n\n        <Section background=\"right-card\">\n          <Center>\n            <Header>\n              フレームワークで\n              <br className=\"hidden lg:inline\" />\n              フルスタックな開発を\n            </Header>\n            <Para>\n              React はライブラリです。{''}\n              コンポーネントを組み合わせることはできますが、{''}\n              ルーティングやデータフェッチの方法までは指定しません。{''}\n              React でアプリ全体を構築する場合は、{''}\n              <Link href=\"https://nextjs.org\">Next.js</Link> や{' '}\n              <Link href=\"https://reactrouter.com\">React Router</Link>{' '}\n              のようなフルスタックのフレームワークをお勧めします。\n            </Para>\n          </Center>\n          <FullBleed>\n            <Example4 />\n          </FullBleed>\n          <Center>\n            <Para>\n              React とはアーキテクチャでもあります。{''}\n              フレームワークでは、サーバやビルド時に動作する{''}\n              非同期コンポーネントを使ってデータの取得が可能です。{''}\n              ファイルやデータベースからデータを読み込んで、{''}\n              インタラクティブなコンポーネントに渡しましょう。\n            </Para>\n            <div className=\"flex justify-start w-full lg:justify-center\">\n              <CTA\n                color=\"gray\"\n                icon=\"framework\"\n                href=\"/learn/creating-a-react-app\">\n                フレームワークで始める\n              </CTA>\n            </div>\n          </Center>\n        </Section>\n        <Section background=\"left-card\">\n          <div className=\"mx-auto flex flex-col w-full\">\n            <div className=\"mx-auto max-w-4xl lg:text-center items-center px-5 flex flex-col\">\n              <Header>\n                あらゆるプラットフォームの\n                <br />\n                能力を最大限に活用\n              </Header>\n              <Para>\n                人々はウェブを愛し、そしてネイティブアプリを愛しています。{''}\n                その理由は様々です。{''}\n                React\n                を使えば、同じスキルを使ってウェブアプリとネイティブアプリの{''}\n                両方を構築できます。{''}\n                各プラットフォームが持つ独自の強みを活かし、{''}\n                どんなプラットフォームにおいても自然なインターフェースを実現します。\n              </Para>\n            </div>\n            <div className=\"max-w-7xl mx-auto flex flex-col lg:flex-row mt-16 mb-20 lg:mb-28 px-5 gap-20 lg:gap-5\">\n              <div className=\"relative lg:w-6/12 flex\">\n                <div className=\"absolute -bottom-8 lg:-bottom-10 z-10 w-full\">\n                  <WebIcons />\n                </div>\n                <BrowserChrome hasRefresh={false} domain=\"example.com\">\n                  <div className=\"relative overflow-hidden\">\n                    <div className=\"absolute inset-0 bg-gradient-right\" />\n                    <div className=\"bg-wash relative h-14 w-full\" />\n                    <div className=\"relative flex items-start justify-center flex-col flex-1 pb-16 pt-5 gap-3 px-5 lg:px-10 lg:pt-8\">\n                      <h4 className=\"leading-tight text-primary font-semibold text-3xl lg:text-4xl\">\n                        ウェブの本質に忠実に\n                      </h4>\n                      <p className=\"lg:text-xl leading-normal text-secondary\">\n                        人々はウェブアプリが素早く読み込まれることを期待します。\n                        {''}\n                        React を使用すれば、サーバ上でデータが取得中でも HTML\n                        のストリーミングを開始でき、JavaScript\n                        コードが読み込まれる前に{''}\n                        コンテンツを段階的にロードすることができます。{''}\n                        クライアント側では、React は標準的な Web API\n                        を使用して、レンダーの最中でも UI の応答性を保ちます。\n                      </p>\n                    </div>\n                  </div>\n                </BrowserChrome>\n              </div>\n              <div className=\"relative lg:w-6/12 flex\">\n                <div className=\"absolute -bottom-8 lg:-bottom-10 z-10 w-full\">\n                  <NativeIcons />\n                </div>\n                <figure className=\"mx-auto max-w-3xl h-auto\">\n                  <div className=\"p-2.5 bg-gray-95 dark:bg-black rounded-2xl shadow-nav dark:shadow-nav-dark\">\n                    <div className=\"bg-gradient-right dark:bg-gradient-right-dark px-3 sm:px-3 pb-12 lg:pb-20 rounded-lg overflow-hidden\">\n                      <div className=\"select-none w-full h-14 flex flex-row items-start pt-3 -mb-2.5 justify-between text-tertiary dark:text-tertiary-dark\">\n                        <span className=\"uppercase tracking-wide leading-none font-bold text-sm text-tertiary dark:text-tertiary-dark\">\n                          <CurrentTime />\n                        </span>\n                        <div className=\"gap-2 flex -mt-0.5\">\n                          <svg\n                            width=\"16\"\n                            height=\"20\"\n                            viewBox=\"0 0 72 72\"\n                            fill=\"none\"\n                            xmlns=\"http://www.w3.org/2000/svg\">\n                            <path\n                              fillRule=\"evenodd\"\n                              clipRule=\"evenodd\"\n                              d=\"M34.852 6.22836C35.973 5.76401 37.2634 6.02068 38.1214 6.87868L53.1214 21.8787C53.7485 22.5058 54.066 23.3782 53.9886 24.2617C53.9113 25.1451 53.447 25.9491 52.7205 26.4577L39.0886 36.0003L52.7204 45.5423C53.447 46.0508 53.9113 46.8548 53.9886 47.7383C54.066 48.6218 53.7485 49.4942 53.1214 50.1213L38.1214 65.1213C37.2634 65.9793 35.973 66.236 34.852 65.7716C33.731 65.3073 33.0001 64.2134 33.0001 63V40.2624L22.7205 47.4583C21.3632 48.4085 19.4926 48.0784 18.5424 46.721C17.5922 45.3637 17.9223 43.4931 19.2797 42.543L28.6258 36.0004L19.2797 29.4583C17.9224 28.5082 17.5922 26.6376 18.5424 25.2803C19.4925 23.9229 21.3631 23.5928 22.7204 24.5429L33.0001 31.7384V9C33.0001 7.78661 33.731 6.6927 34.852 6.22836ZM39.0001 43.2622L46.3503 48.4072L39.0001 55.7574V43.2622ZM39.0001 28.7382V16.2426L46.3503 23.5929L39.0001 28.7382Z\"\n                              fill=\"currentColor\"\n                            />\n                          </svg>\n\n                          <svg\n                            width=\"16\"\n                            height=\"20\"\n                            viewBox=\"0 0 72 72\"\n                            fill=\"none\"\n                            xmlns=\"http://www.w3.org/2000/svg\">\n                            <path\n                              d=\"M9 27C9.82864 27 10.5788 26.664 11.1217 26.1209C11.2116 26.0355 11.3037 25.9526 11.397 25.871C11.625 25.6714 11.9885 25.3677 12.4871 24.9938C13.4847 24.2455 15.0197 23.219 17.0912 22.1833C21.2243 20.1167 27.5179 18 35.9996 18C44.4813 18 50.7748 20.1167 54.9079 22.1833C56.9794 23.219 58.5144 24.2455 59.5121 24.9938C59.6056 25.0639 60.8802 26.1233 60.8802 26.1233C61.423 26.6652 62.1724 27 63 27C64.6569 27 66 25.6569 66 24C66 22.8871 65.3475 22.0506 64.5532 21.3556C64.2188 21.0629 63.7385 20.6635 63.1121 20.1938C61.8597 19.2545 60.0197 18.031 57.5912 16.8167C52.7243 14.3833 45.5179 12 35.9996 12C26.4813 12 19.2748 14.3833 14.4079 16.8167C11.9794 18.031 10.1394 19.2545 8.88706 20.1938C8.26066 20.6635 7.78035 21.0629 7.44593 21.3556C7.2605 21.5178 7.07794 21.6834 6.9016 21.8555C6.33334 22.417 6 23.1999 6 24C6 25.6569 7.34315 27 9 27Z\"\n                              fill=\"currentColor\"\n                            />\n                            <path\n                              fillRule=\"evenodd\"\n                              clipRule=\"evenodd\"\n                              d=\"M26.1116 48.631C24.2868 50.4378 21 49.0661 21 46.5C21 45.6707 21.3365 44.92 21.8804 44.3769C21.9856 44.2702 22.0973 44.1695 22.209 44.0697C22.3915 43.9065 22.6466 43.6885 22.9713 43.4344C23.6195 42.9271 24.5536 42.2694 25.7509 41.6163C28.1445 40.3107 31.6365 39 35.9999 39C40.3634 39 43.8554 40.3107 46.249 41.6163C47.4463 42.2694 48.3804 42.9271 49.0286 43.4344C50.0234 44.213 51 45.134 51 46.5C51 48.1569 49.6569 49.5 48 49.5C47.1724 49.5 46.4231 49.1649 45.8803 48.623C45.7028 48.4617 45.5197 48.3073 45.3307 48.1594C44.9007 47.8229 44.2411 47.3556 43.3759 46.8837C41.6445 45.9393 39.1365 45 35.9999 45C32.8634 45 30.3554 45.9393 28.624 46.8837C27.7588 47.3556 27.0992 47.8229 26.6692 48.1594C26.3479 48.4109 26.155 48.5899 26.1116 48.631Z\"\n                              fill=\"currentColor\"\n                            />\n                            <path\n                              d=\"M36 63C39.3137 63 42 60.3137 42 57C42 53.6863 39.3137 51 36 51C32.6863 51 30 53.6863 30 57C30 60.3137 32.6863 63 36 63Z\"\n                              fill=\"currentColor\"\n                            />\n                            <path\n                              d=\"M15 39C13.3431 39 12 37.6569 12 36C12 34.3892 13.3933 33.3427 14.5534 32.4503C15.5841 31.6574 17.0871 30.6231 19.04 29.5952C22.9506 27.537 28.6773 25.5 35.9997 25.5C43.3222 25.5 49.0488 27.537 52.9595 29.5952C54.9123 30.6231 56.4154 31.6574 57.4461 32.4503C57.9619 32.847 58.361 33.1846 58.6407 33.4324C59.4024 34.1073 60 34.9345 60 36C60 37.6569 58.6569 39 57 39C56.1737 39 55.4255 38.6662 54.8829 38.1258C54.5371 37.7978 54.1653 37.4964 53.7878 37.206C52.9903 36.5926 51.7746 35.7519 50.165 34.9048C46.9506 33.213 42.1773 31.5 35.9997 31.5C29.8222 31.5 25.0488 33.213 21.8345 34.9048C20.2248 35.7519 19.0091 36.5926 18.2117 37.206C17.6144 37.6654 17.2549 37.9951 17.1459 38.098C16.5581 38.6591 15.8222 39 15 39Z\"\n                              fill=\"currentColor\"\n                            />\n                          </svg>\n                          <svg\n                            width=\"20\"\n                            height=\"20\"\n                            viewBox=\"0 0 72 72\"\n                            fill=\"none\"\n                            xmlns=\"http://www.w3.org/2000/svg\">\n                            <path\n                              d=\"M12.9533 26.0038C13.224 24.7829 14.3285 24 15.579 24H50.421C51.6715 24 52.776 24.7829 53.0467 26.0038C53.4754 27.937 54 31.2691 54 36C54 40.7309 53.4754 44.063 53.0467 45.9962C52.776 47.2171 51.6715 48 50.421 48H15.579C14.3285 48 13.224 47.2171 12.9533 45.9962C12.5246 44.063 12 40.7309 12 36C12 31.2691 12.5246 27.937 12.9533 26.0038Z\"\n                              fill=\"currentColor\"\n                            />\n                            <path\n                              fillRule=\"evenodd\"\n                              clipRule=\"evenodd\"\n                              d=\"M12.7887 15C8.77039 15 5.23956 17.668 4.48986 21.6158C3.74326 25.5473 3 30.7737 3 36C3 41.2263 3.74326 46.4527 4.48986 50.3842C5.23956 54.332 8.77039 57 12.7887 57H53.2113C57.2296 57 60.7604 54.332 61.5101 50.3842C61.8155 48.7765 62.1202 46.9522 62.3738 45H63.7918C64.5731 45 65.3283 44.8443 66 44.5491C67.2821 43.9857 68.2596 42.9142 68.5322 41.448C68.7927 40.0466 69 38.2306 69 36C69 33.7694 68.7927 31.9534 68.5322 30.552C68.2596 29.0858 67.2821 28.0143 66 27.4509C65.3283 27.1557 64.5731 27 63.7918 27H62.3738C62.1202 25.0478 61.8155 23.2235 61.5101 21.6158C60.7604 17.668 57.2296 15 53.2113 15H12.7887ZM53.2113 21H12.7887C11.3764 21 10.5466 21.8816 10.3845 22.7352C9.67563 26.4681 9 31.29 9 36C9 40.71 9.67563 45.5319 10.3845 49.2648C10.5466 50.1184 11.3764 51 12.7887 51H53.2113C54.6236 51 55.4534 50.1184 55.6155 49.2648C56.3244 45.5319 57 40.71 57 36C57 31.29 56.3244 26.4681 55.6155 22.7352C55.4534 21.8816 54.6236 21 53.2113 21Z\"\n                              fill=\"currentColor\"\n                            />\n                          </svg>\n                        </div>\n                      </div>\n                      <div className=\"flex flex-col items-start justify-center pt-0 gap-3 px-2.5 lg:pt-8 lg:px-8\">\n                        <h4 className=\"leading-tight text-primary dark:text-primary-dark font-semibold text-3xl lg:text-4xl\">\n                          真にネイティブな体験を実現\n                        </h4>\n                        <p className=\"h-full lg:text-xl text-secondary dark:text-secondary-dark leading-normal\">\n                          人々はネイティブアプリがそのプラットフォームに見合った\n                          {''}\n                          ルック＆フィールを持つことを期待します。\n                          <Link href=\"https://reactnative.dev\">\n                            React Native\n                          </Link>{' '}\n                          や{' '}\n                          <Link href=\"https://github.com/expo/expo\">Expo</Link>{' '}\n                          を使えば、React で Android、iOS\n                          などのアプリを構築できます。{''}\n                          ネイティブアプリのように感じるのは、{''}\n                          ウェブビューではなく真のネイティブ UI だからです。{''}\n                          React コンポーネントは、プラットフォーム固有の、{''}\n                          本物の Android や iOS のビューを表示できます。\n                        </p>\n                      </div>\n                    </div>\n                  </div>\n                </figure>\n              </div>\n            </div>\n            <div className=\"px-5 lg:px-0 max-w-4xl mx-auto lg:text-center text-secondary dark:text-secondary-dark\">\n              <Para>\n                React を使えば、{''}\n                ウェブ開発者にもネイティブアプリ開発者にもなれるのです。{''}\n                ユーザー体験を犠牲にすることなく、{''}\n                多くのプラットフォームでリリースを行えます。{''}\n                ひとつのプラットフォームに縛られることなく、{''}\n                すべての機能をエンドツーエンドで担当するチームを作れます。\n              </Para>\n              <div className=\"flex justify-start w-full lg:justify-center\">\n                <CTA color=\"gray\" icon=\"native\" href=\"https://reactnative.dev/\">\n                  ネイティブプラットフォーム向けに開発する\n                </CTA>\n              </div>\n            </div>\n          </div>\n        </Section>\n\n        <Section background=\"right-card\">\n          <div className=\"max-w-7xl mx-auto flex flex-col lg:flex-row px-5\">\n            <div className=\"max-w-3xl lg:max-w-7xl gap-5 flex flex-col lg:flex-row lg:px-5\">\n              <div className=\"w-full lg:w-6/12 max-w-3xl flex flex-col items-start justify-start lg:ps-5 lg:pe-10\">\n                <Header>\n                  完成した機能だけが\n                  <br className=\"hidden lg:inline\" />\n                  リリースされる\n                </Header>\n                <Para>\n                  React は開発アプローチの変更に慎重に取り組みます。{''}\n                  すべてのコミットは 10 億人以上のユーザによる{''}\n                  ビジネスクリティカルな環境においてテストされます。{''}\n                  Meta にある 10 万以上の React コンポーネントが、{''}\n                  すべての移行戦略の検証を支援します。\n                </Para>\n                <div className=\"order-last pt-5\">\n                  <Para>\n                    React チームは、常に React\n                    を改善する方法を模索していますが、{''}\n                    研究によっては成果が出るまでに何年もかかることもあります。\n                    {''}\n                    研究のアイデアをリリースするまでの高いハードルを越えた、{''}\n                    実証済みのアプローチだけが React の一部となるのです。\n                  </Para>\n                  <div className=\"hidden lg:flex justify-start w-full\">\n                    <CTA color=\"gray\" icon=\"news\" href=\"/blog\">\n                      React のニュースを読む\n                    </CTA>\n                  </div>\n                </div>\n              </div>\n              <div className=\"w-full lg:w-6/12\">\n                <p className=\"uppercase tracking-wide font-bold text-sm text-tertiary dark:text-tertiary-dark flex flex-row gap-2 items-center mt-5 lg:-mt-2 w-full\">\n                  <IconChevron />\n                  最新の React ニュース\n                </p>\n                <div className=\"flex-col sm:flex-row flex-wrap flex gap-5 text-start my-5\">\n                  <div className=\"flex-1 min-w-[40%] text-start\">\n                    <BlogCard {...recentPosts[0]} />\n                  </div>\n                  <div className=\"flex-1 min-w-[40%] text-start\">\n                    <BlogCard {...recentPosts[1]} />\n                  </div>\n                  <div className=\"flex-1 min-w-[40%] text-start\">\n                    <BlogCard {...recentPosts[2]} />\n                  </div>\n                  <div className=\"hidden sm:flex-1 sm:inline\">\n                    <BlogCard {...recentPosts[3]} />\n                  </div>\n                </div>\n                <div className=\"flex lg:hidden justify-start w-full\">\n                  <CTA color=\"gray\" icon=\"news\" href=\"/blog\">\n                    Read more React news\n                  </CTA>\n                </div>\n              </div>\n            </div>\n          </div>\n        </Section>\n\n        <Section background=\"left-card\">\n          <div className=\"w-full\">\n            <div className=\"mx-auto flex flex-col max-w-4xl\">\n              <Center>\n                <Header>\n                  数百万人の\n                  <br className=\"hidden lg:inline\" />\n                  コミュニティに参加しよう\n                </Header>\n                <Para>\n                  あなたは 1 人ではありません。世界中から毎月 200 万人の開発者が\n                  React ドキュメントに訪れています。{''}\n                  人々とチームが共感できる技術、それが React なのです。\n                </Para>\n              </Center>\n            </div>\n            <CommunityGallery />\n            <div className=\"mx-auto flex flex-col max-w-4xl\">\n              <Center>\n                <Para>\n                  React は単なるライブラリやアーキテクチャ、{''}\n                  あるいはエコシステム以上の存在です。{''}\n                  React とはコミュニティです。{''}\n                  ヘルプを求め、チャンスを見つけ、新しい友人に会える場所です。\n                  {''}\n                  開発者やデザイナ、初心者やエキスパート、{''}\n                  研究者やアーティスト、教師や学生と出会える場所です。{''}\n                  私たちのバックグラウンドはさまざまですが、React を通じて皆で\n                  {''}\n                  ユーザーインターフェースの創造に取り組んでいるのです。\n                </Para>\n              </Center>\n            </div>\n          </div>\n\n          <div className=\"mt-20 px-5 lg:px-0 mb-6 max-w-4xl text-center text-opacity-80\">\n            <div className=\"uwu-visible flex justify-center\">\n              <img\n                alt=\"logo by @sawaratsuki1004\"\n                title=\"logo by @sawaratsuki1004\"\n                className=\"uwu-visible mb-10 lg:mb-8 h-24 lg:h-32\"\n                src=\"/images/uwu.png\"\n              />\n            </div>\n            <Logo className=\"uwu-hidden text-brand dark:text-brand-dark w-24 lg:w-28 mb-10 lg:mb-8 mt-12 h-auto mx-auto self-start\" />\n            <Header>\n              React コミュニティに\n              <br className=\"hidden lg:inline\" />\n              ようこそ！\n            </Header>\n            <ButtonLink\n              href={'/learn'}\n              type=\"primary\"\n              size=\"lg\"\n              label=\"Take the Tutorial\">\n              はじめる\n            </ButtonLink>\n          </div>\n        </Section>\n      </div>\n    </>\n  );\n}\n\nfunction CTA({children, icon, href}) {\n  let Tag;\n  let extraProps;\n  if (href.startsWith('https://')) {\n    Tag = ExternalLink;\n  } else {\n    Tag = NextLink;\n    extraProps = {legacyBehavior: false};\n  }\n  return (\n    <Tag\n      {...extraProps}\n      href={href}\n      className=\"focus:outline-none focus-visible:outline focus-visible:outline-link focus:outline-offset-2 focus-visible:dark:focus:outline-link-dark group cursor-pointer w-auto justify-center inline-flex font-bold items-center mt-10 outline-none hover:bg-gray-40/5 active:bg-gray-40/10 hover:dark:bg-gray-60/5 active:dark:bg-gray-60/10 leading-tight hover:bg-opacity-80 text-lg py-2.5 rounded-full px-4 sm:px-6 ease-in-out shadow-secondary-button-stroke dark:shadow-secondary-button-stroke-dark text-primary dark:text-primary-dark\">\n      {icon === 'native' && (\n        <svg\n          className=\"me-2.5 text-primary dark:text-primary-dark\"\n          fill=\"none\"\n          width=\"24\"\n          height=\"24\"\n          viewBox=\"0 0 72 72\"\n          aria-hidden=\"true\">\n          <g clipPath=\"url(#clip0_8_10998)\">\n            <path\n              d=\"M54.0001 15H18.0001C16.3432 15 15.0001 16.3431 15.0001 18V42H33V48H12.9567L9.10021 57L24.0006 57C24.0006 55.3431 25.3437 54 27.0006 54H33V57.473C33 59.3786 33.3699 61.2582 34.0652 63H9.10021C4.79287 63 1.88869 58.596 3.5852 54.6368L9.0001 42V18C9.0001 13.0294 13.0295 9 18.0001 9H54.0001C58.9707 9 63.0001 13.0294 63.0001 18V25.4411C62.0602 25.0753 61.0589 24.8052 60.0021 24.6458C59.0567 24.5032 58.0429 24.3681 57.0001 24.2587V18C57.0001 16.3431 55.6569 15 54.0001 15Z\"\n              fill=\"currentColor\"\n            />\n            <path\n              d=\"M48 42C48 40.3431 49.3431 39 51 39H54C55.6569 39 57 40.3431 57 42C57 43.6569 55.6569 45 54 45H51C49.3431 45 48 43.6569 48 42Z\"\n              fill=\"currentColor\"\n            />\n            <path\n              fillRule=\"evenodd\"\n              clipRule=\"evenodd\"\n              d=\"M45.8929 30.5787C41.8093 31.1947 39 34.8257 39 38.9556V57.473C39 61.6028 41.8093 65.2339 45.8929 65.8499C48.0416 66.174 50.3981 66.4286 52.5 66.4286C54.6019 66.4286 56.9584 66.174 59.1071 65.8499C63.1907 65.2339 66 61.6028 66 57.473V38.9556C66 34.8258 63.1907 31.1947 59.1071 30.5787C56.9584 30.2545 54.6019 30 52.5 30C50.3981 30 48.0416 30.2545 45.8929 30.5787ZM60 57.473V38.9556C60 37.4615 59.0438 36.637 58.2121 36.5116C56.2014 36.2082 54.1763 36 52.5 36C50.8237 36 48.7986 36.2082 46.7879 36.5116C45.9562 36.637 45 37.4615 45 38.9556V57.473C45 58.9671 45.9562 59.7916 46.7879 59.917C48.7986 60.2203 50.8237 60.4286 52.5 60.4286C54.1763 60.4286 56.2014 60.2203 58.2121 59.917C59.0438 59.7916 60 58.9671 60 57.473Z\"\n              fill=\"currentColor\"\n            />\n          </g>\n          <defs>\n            <clipPath id=\"clip0_8_10998\">\n              <rect width=\"72\" height=\"72\" fill=\"white\" />\n            </clipPath>\n          </defs>\n        </svg>\n      )}\n      {icon === 'framework' && (\n        <svg\n          className=\"me-2.5 text-primary dark:text-primary-dark\"\n          fill=\"none\"\n          width=\"24\"\n          height=\"24\"\n          viewBox=\"0 0 72 72\"\n          aria-hidden=\"true\">\n          <g clipPath=\"url(#clip0_10_21081)\">\n            <path\n              fillRule=\"evenodd\"\n              clipRule=\"evenodd\"\n              d=\"M44.9136 29.0343C46.8321 26.9072 48 24.09 48 21C48 14.3726 42.6274 9 36 9C29.3726 9 24 14.3726 24 21C24 24.0904 25.1682 26.9079 27.0871 29.0351L21.0026 39.3787C20.0429 39.1315 19.0368 39 18 39C11.3726 39 6 44.3726 6 51C6 57.6274 11.3726 63 18 63C23.5915 63 28.2898 59.1757 29.6219 54H42.3781C43.7102 59.1757 48.4085 63 54 63C60.6274 63 66 57.6274 66 51C66 44.3726 60.6274 39 54 39C52.9614 39 51.9537 39.1319 50.9926 39.38L44.9136 29.0343ZM42 21C42 24.3137 39.3137 27 36 27C32.6863 27 30 24.3137 30 21C30 17.6863 32.6863 15 36 15C39.3137 15 42 17.6863 42 21ZM39.9033 32.3509C38.6796 32.7716 37.3665 33 36 33C34.6338 33 33.321 32.7717 32.0975 32.3512L26.2523 42.288C27.8635 43.8146 29.0514 45.7834 29.6219 48H42.3781C42.9482 45.785 44.1348 43.8175 45.7441 42.2913L39.9033 32.3509ZM54 57C50.6863 57 48 54.3137 48 51C48 47.6863 50.6863 45 54 45C57.3137 45 60 47.6863 60 51C60 54.3137 57.3137 57 54 57ZM24 51C24 47.6863 21.3137 45 18 45C14.6863 45 12 47.6863 12 51C12 54.3137 14.6863 57 18 57C21.3137 57 24 54.3137 24 51Z\"\n              fill=\"currentColor\"\n            />\n          </g>\n          <defs>\n            <clipPath id=\"clip0_10_21081\">\n              <rect width=\"72\" height=\"72\" fill=\"white\" />\n            </clipPath>\n          </defs>\n        </svg>\n      )}\n      {icon === 'code' && (\n        <svg\n          className=\"me-2.5 text-primary dark:text-primary-dark\"\n          fill=\"none\"\n          width=\"24\"\n          height=\"24\"\n          viewBox=\"0 0 72 72\"\n          aria-hidden=\"true\">\n          <g clipPath=\"url(#clip0_8_9064)\">\n            <path\n              d=\"M44.7854 22.1142C45.4008 20.5759 44.6525 18.83 43.1142 18.2146C41.5758 17.5993 39.8299 18.3475 39.2146 19.8859L27.2146 49.8859C26.5992 51.4242 27.3475 53.1702 28.8858 53.7855C30.4242 54.4008 32.1701 53.6526 32.7854 52.1142L44.7854 22.1142Z\"\n              fill=\"currentColor\"\n            />\n            <path\n              d=\"M9.87868 38.1214C8.70711 36.9498 8.70711 35.0503 9.87868 33.8787L18.8787 24.8787C20.0503 23.7072 21.9497 23.7072 23.1213 24.8787C24.2929 26.0503 24.2929 27.9498 23.1213 29.1214L16.2426 36.0001L23.1213 42.8787C24.2929 44.0503 24.2929 45.9498 23.1213 47.1214C21.9497 48.293 20.0503 48.293 18.8787 47.1214L9.87868 38.1214Z\"\n              fill=\"currentColor\"\n            />\n            <path\n              d=\"M62.1213 33.8787L53.1213 24.8787C51.9497 23.7072 50.0503 23.7072 48.8787 24.8787C47.7071 26.0503 47.7071 27.9498 48.8787 29.1214L55.7574 36.0001L48.8787 42.8787C47.7071 44.0503 47.7071 45.9498 48.8787 47.1214C50.0503 48.293 51.9497 48.293 53.1213 47.1214L62.1213 38.1214C63.2929 36.9498 63.2929 35.0503 62.1213 33.8787Z\"\n              fill=\"currentColor\"\n            />\n          </g>\n          <defs>\n            <clipPath id=\"clip0_8_9064\">\n              <rect width=\"72\" height=\"72\" fill=\"white\" />\n            </clipPath>\n          </defs>\n        </svg>\n      )}\n      {icon === 'news' && (\n        <svg\n          className=\"me-2.5 text-primary dark:text-primary-dark\"\n          fill=\"none\"\n          width=\"24\"\n          height=\"24\"\n          viewBox=\"0 0 72 72\"\n          aria-hidden=\"true\">\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"M12.7101 56.3758C13.0724 56.7251 13.6324 57 14.3887 57H57.6113C58.3676 57 58.9276 56.7251 59.2899 56.3758C59.6438 56.0346 59.8987 55.5407 59.9086 54.864C59.9354 53.022 59.9591 50.7633 59.9756 48H12.0244C12.0409 50.7633 12.0645 53.022 12.0914 54.864C12.1013 55.5407 12.3562 56.0346 12.7101 56.3758ZM12.0024 42H59.9976C59.9992 41.0437 60 40.0444 60 39C60 29.5762 59.9327 22.5857 59.8589 17.7547C59.8359 16.2516 58.6168 15 56.9938 15L15.0062 15C13.3832 15 12.1641 16.2516 12.1411 17.7547C12.0673 22.5857 12 29.5762 12 39C12 40.0444 12.0008 41.0437 12.0024 42ZM65.8582 17.6631C65.7843 12.8227 61.8348 9 56.9938 9H15.0062C10.1652 9 6.21572 12.8227 6.1418 17.6631C6.06753 22.5266 6 29.5477 6 39C6 46.2639 6.03988 51.3741 6.09205 54.9515C6.15893 59.537 9.80278 63 14.3887 63H57.6113C62.1972 63 65.8411 59.537 65.9079 54.9515C65.9601 51.3741 66 46.2639 66 39C66 29.5477 65.9325 22.5266 65.8582 17.6631ZM39 21C37.3431 21 36 22.3431 36 24C36 25.6569 37.3431 27 39 27H51C52.6569 27 54 25.6569 54 24C54 22.3431 52.6569 21 51 21H39ZM36 33C36 31.3431 37.3431 30 39 30H51C52.6569 30 54 31.3431 54 33C54 34.6569 52.6569 36 51 36H39C37.3431 36 36 34.6569 36 33ZM24 33C27.3137 33 30 30.3137 30 27C30 23.6863 27.3137 21 24 21C20.6863 21 18 23.6863 18 27C18 30.3137 20.6863 33 24 33Z\"\n            fill=\"currentColor\"\n          />\n        </svg>\n      )}\n      {children}\n      <svg\n        className=\"text-primary dark:text-primary-dark rtl:rotate-180\"\n        fill=\"none\"\n        width=\"24\"\n        height=\"24\"\n        viewBox=\"0 0 72 72\"\n        aria-hidden=\"true\">\n        <path\n          className=\"transition-transform ease-in-out translate-x-[-8px] group-hover:translate-x-[8px]\"\n          fillRule=\"evenodd\"\n          clipRule=\"evenodd\"\n          d=\"M40.0001 19.0245C41.0912 17.7776 42.9864 17.6513 44.2334 18.7423L58.9758 33.768C59.6268 34.3377 60.0002 35.1607 60.0002 36.0257C60.0002 36.8908 59.6268 37.7138 58.9758 38.2835L44.2335 53.3078C42.9865 54.3988 41.0913 54.2725 40.0002 53.0256C38.9092 51.7786 39.0355 49.8835 40.2824 48.7924L52.4445 36.0257L40.2823 23.2578C39.0354 22.1667 38.9091 20.2714 40.0001 19.0245Z\"\n          fill=\"currentColor\"\n        />\n        <path\n          className=\"opacity-0 ease-in-out transition-opacity group-hover:opacity-100\"\n          d=\"M60 36.0273C60 37.6842 58.6569 39.0273 57 39.0273H15C13.3431 39.0273 12 37.6842 12 36.0273C12 34.3704 13.3431 33.0273 15 33.0273H57C58.6569 33.0273 60 34.3704 60 36.0273Z\"\n          fill=\"currentColor\"\n        />\n      </svg>\n    </Tag>\n  );\n}\n\nconst reactConf2021Cover = '/images/home/conf2021/cover.svg';\nconst reactConf2019Cover = '/images/home/conf2019/cover.svg';\nconst communityImages = [\n  {\n    src: '/images/home/community/react_conf_fun.webp',\n    alt: 'People singing karaoke at React Conf',\n  },\n  {\n    src: '/images/home/community/react_india_sunil.webp',\n    alt: 'Sunil Pai speaking at React India',\n  },\n  {\n    src: '/images/home/community/react_conf_hallway.webp',\n    alt: 'A hallway conversation between two people at React Conf',\n  },\n  {\n    src: '/images/home/community/react_india_hallway.webp',\n    alt: 'A hallway conversation at React India',\n  },\n  {\n    src: '/images/home/community/react_conf_elizabet.webp',\n    alt: 'Elizabet Oliveira speaking at React Conf',\n  },\n  {\n    src: '/images/home/community/react_india_selfie.webp',\n    alt: 'People taking a group selfie at React India',\n  },\n  {\n    src: '/images/home/community/react_conf_nat.webp',\n    alt: 'Nat Alison speaking at React Conf',\n  },\n  {\n    src: '/images/home/community/react_india_team.webp',\n    alt: 'Organizers greeting attendees at React India',\n  },\n];\n\nfunction CommunityGallery() {\n  const ref = useRef();\n\n  const [shouldPlay, setShouldPlay] = useState(false);\n  useEffect(() => {\n    const observer = new IntersectionObserver(\n      (entries) => {\n        entries.forEach((entry) => {\n          setShouldPlay(entry.isIntersecting);\n        });\n      },\n      {\n        root: null,\n        rootMargin: `${window.innerHeight}px 0px`,\n      }\n    );\n    observer.observe(ref.current);\n    return () => observer.disconnect();\n  }, []);\n\n  const [isLazy, setIsLazy] = useState(true);\n  // Either wait until we're scrolling close...\n  useEffect(() => {\n    if (!isLazy) {\n      return;\n    }\n    const rootVertical = parseInt(window.innerHeight * 2.5);\n    const observer = new IntersectionObserver(\n      (entries) => {\n        entries.forEach((entry) => {\n          if (entry.isIntersecting) {\n            setIsLazy(false);\n          }\n        });\n      },\n      {\n        root: null,\n        rootMargin: `${rootVertical}px 0px`,\n      }\n    );\n    observer.observe(ref.current);\n    return () => observer.disconnect();\n  }, [isLazy]);\n  // ... or until it's been a while after hydration.\n  useEffect(() => {\n    const timeout = setTimeout(() => {\n      setIsLazy(false);\n    }, 20 * 1000);\n    return () => clearTimeout(timeout);\n  }, []);\n\n  return (\n    <div ref={ref} className=\"relative flex overflow-x-clip w-auto\">\n      <div\n        className=\"w-full py-12 lg:py-20 whitespace-nowrap flex flex-row animate-marquee lg:animate-large-marquee\"\n        style={{\n          animationPlayState: shouldPlay ? 'running' : 'paused',\n        }}>\n        <CommunityImages isLazy={isLazy} />\n      </div>\n      <div\n        aria-hidden=\"true\"\n        className=\"w-full absolute top-0 py-12 lg:py-20 whitespace-nowrap flex flex-row animate-marquee2 lg:animate-large-marquee2\"\n        style={{\n          animationPlayState: shouldPlay ? 'running' : 'paused',\n        }}>\n        <CommunityImages isLazy={isLazy} />\n      </div>\n    </div>\n  );\n}\n\nconst CommunityImages = memo(function CommunityImages({isLazy}) {\n  return (\n    <>\n      {communityImages.map(({src, alt}, i) => (\n        <div\n          key={i}\n          className={cn(\n            `group flex justify-center px-5 min-w-[50%] lg:min-w-[25%] rounded-2xl`\n          )}>\n          <div\n            className={cn(\n              'h-auto rounded-2xl before:rounded-2xl before:absolute before:pointer-events-none before:inset-0 before:transition-opacity before:-z-1 before:shadow-lg lg:before:shadow-2xl before:opacity-0 before:group-hover:opacity-100  transition-transform ease-in-out duration-300',\n              i % 2 === 0\n                ? 'rotate-2 group-hover:rotate-[-1deg] group-hover:scale-110'\n                : 'group-hover:rotate-1 group-hover:scale-110 rotate-[-2deg]'\n            )}>\n            <div\n              className={cn(\n                'overflow-clip relative before:absolute before:inset-0 before:pointer-events-none before:-translate-x-full group-hover:before:animate-[shimmer_1s_forwards] before:bg-gradient-to-r before:from-transparent before:via-white/10 before:to-transparent transition-transform ease-in-out duration-300'\n              )}>\n              <img\n                loading={isLazy ? 'lazy' : 'eager'}\n                src={src}\n                alt={alt}\n                className=\"aspect-[4/3] h-full w-full flex object-cover rounded-2xl bg-gray-10 dark:bg-gray-80\"\n              />\n            </div>\n          </div>\n        </div>\n      ))}\n    </>\n  );\n});\n\nfunction ExampleLayout({\n  filename,\n  left,\n  right,\n  activeArea,\n  hoverTopOffset = 0,\n}) {\n  const contentRef = useRef(null);\n  useNestedScrollLock(contentRef);\n\n  const [overlayStyles, setOverlayStyles] = useState([]);\n  useEffect(() => {\n    if (activeArea) {\n      const nodes = contentRef.current.querySelectorAll(\n        '[data-hover=\"' + activeArea.name + '\"]'\n      );\n      const nextOverlayStyles = Array.from(nodes)\n        .map((node) => {\n          const parentRect = contentRef.current.getBoundingClientRect();\n          const nodeRect = node.getBoundingClientRect();\n          let top = Math.round(nodeRect.top - parentRect.top) - 8;\n          let bottom = Math.round(nodeRect.bottom - parentRect.top) + 8;\n          let left = Math.round(nodeRect.left - parentRect.left) - 8;\n          let right = Math.round(nodeRect.right - parentRect.left) + 8;\n          top = Math.max(top, hoverTopOffset);\n          bottom = Math.min(bottom, parentRect.height - 12);\n          if (top >= bottom) {\n            return null;\n          }\n          return {\n            width: right - left + 'px',\n            height: bottom - top + 'px',\n            transform: `translate(${left}px, ${top}px)`,\n          };\n        })\n        .filter((s) => s !== null);\n      setOverlayStyles(nextOverlayStyles);\n    }\n  }, [activeArea, hoverTopOffset]);\n  return (\n    <div className=\"lg:ps-10 lg:pe-5 w-full\">\n      <div className=\"mt-12 mb-2 lg:my-16 max-w-7xl mx-auto flex flex-col w-full lg:rounded-2xl lg:bg-card lg:dark:bg-card-dark\">\n        <div className=\"flex-col gap-0 lg:gap-5 lg:rounded-2xl lg:bg-gray-10 lg:dark:bg-gray-70 shadow-inner-border dark:shadow-inner-border-dark lg:flex-row flex grow w-full mx-auto items-center bg-cover bg-center lg:bg-right ltr:lg:bg-[length:60%_100%] bg-no-repeat bg-meta-gradient dark:bg-meta-gradient-dark\">\n          <div className=\"lg:-m-5 h-full shadow-nav dark:shadow-nav-dark lg:rounded-2xl bg-wash dark:bg-gray-95 w-full flex grow flex-col\">\n            <div className=\"w-full bg-card dark:bg-wash-dark lg:rounded-t-2xl border-b border-black/5 dark:border-white/5\">\n              <h3 className=\"text-sm my-1 mx-5 text-tertiary dark:text-tertiary-dark select-none text-start\">\n                {filename}\n              </h3>\n            </div>\n            {left}\n          </div>\n          <div\n            ref={contentRef}\n            className=\"relative mt-0 lg:-my-20 w-full p-2.5 xs:p-5 lg:p-10 flex grow justify-center\"\n            dir=\"ltr\">\n            {right}\n            <div\n              className={cn(\n                'absolute z-10 inset-0 pointer-events-none transition-opacity transform-gpu',\n                activeArea ? 'opacity-100' : 'opacity-0'\n              )}>\n              {overlayStyles.map((styles, i) => (\n                <div\n                  key={i}\n                  className=\"top-0 start-0 bg-blue-30/5 border-2 border-link dark:border-link-dark absolute rounded-lg\"\n                  style={styles}\n                />\n              ))}\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nfunction useCodeHover(areas) {\n  const [hoverLine, setHoverLine] = useState(null);\n  const area = areas.get(hoverLine);\n  let meta;\n  if (area) {\n    const highlightLines = area.lines ?? [hoverLine];\n    meta = '```js {' + highlightLines.map((l) => l + 1).join(',') + '}';\n  }\n  return [area, meta, setHoverLine];\n}\n\nconst example1Areas = new Map([\n  [2, {name: 'Video'}],\n  [3, {name: 'Thumbnail'}],\n  [4, {name: 'a'}],\n  [5, {name: 'h3'}],\n  [6, {name: 'p'}],\n  [7, {name: 'a'}],\n  [8, {name: 'LikeButton'}],\n  [9, {name: 'Video'}],\n]);\n\nfunction Example1() {\n  const [area, meta, onLineHover] = useCodeHover(example1Areas);\n  return (\n    <ExampleLayout\n      filename=\"Video.js\"\n      activeArea={area}\n      left={\n        <CodeBlock\n          onLineHover={onLineHover}\n          isFromPackageImport={false}\n          noShadow={true}\n          noMargin={true}>\n          <div meta={meta}>{`function Video({ video }) {\n  return (\n    <div>\n      <Thumbnail video={video} />\n      <a href={video.url}>\n        <h3>{video.title}</h3>\n        <p>{video.description}</p>\n      </a>\n      <LikeButton video={video} />\n    </div>\n  );\n}\n          `}</div>\n        </CodeBlock>\n      }\n      right={\n        <ExamplePanel height=\"113px\">\n          <Video\n            video={{\n              id: 'ex1-0',\n              title: 'My video',\n              description: 'Video description',\n              image: 'blue',\n              url: null,\n            }}\n          />\n        </ExamplePanel>\n      }\n    />\n  );\n}\n\nconst example2Areas = new Map([\n  [8, {name: 'VideoList'}],\n  [9, {name: 'h2'}],\n  [11, {name: 'Video', lines: [11]}],\n  [13, {name: 'VideoList'}],\n]);\n\nfunction Example2() {\n  const [area, meta, onLineHover] = useCodeHover(example2Areas);\n  const videos = [\n    {\n      id: 'ex2-0',\n      title: 'First video',\n      description: 'Video description',\n      image: 'blue',\n    },\n    {\n      id: 'ex2-1',\n      title: 'Second video',\n      description: 'Video description',\n      image: 'red',\n    },\n    {\n      id: 'ex2-2',\n      title: 'Third video',\n      description: 'Video description',\n      image: 'green',\n    },\n  ];\n\n  return (\n    <ExampleLayout\n      filename=\"VideoList.js\"\n      activeArea={area}\n      left={\n        <CodeBlock\n          onLineHover={onLineHover}\n          isFromPackageImport={false}\n          noShadow={true}\n          noMargin={true}>\n          <div meta={meta}>{`function VideoList({ videos, emptyHeading }) {\n  const count = videos.length;\n  let heading = emptyHeading;\n  if (count > 0) {\n    const noun = count > 1 ? 'Videos' : 'Video';\n    heading = count + ' ' + noun;\n  }\n  return (\n    <section>\n      <h2>{heading}</h2>\n      {videos.map(video =>\n        <Video key={video.id} video={video} />\n      )}\n    </section>\n  );\n}`}</div>\n        </CodeBlock>\n      }\n      right={\n        <ExamplePanel height=\"22rem\" noShadow={false} noPadding={true}>\n          <div className=\"m-4\">\n            <VideoList videos={videos} />\n          </div>\n        </ExamplePanel>\n      }\n    />\n  );\n}\n\nconst example3Areas = new Map([\n  [6, {name: 'SearchableVideoList'}],\n  [7, {name: 'SearchInput', lines: [7, 8, 9]}],\n  [8, {name: 'SearchInput', lines: [7, 8, 9]}],\n  [9, {name: 'SearchInput', lines: [7, 8, 9]}],\n  [10, {name: 'VideoList', lines: [10, 11, 12]}],\n  [11, {name: 'VideoList', lines: [10, 11, 12]}],\n  [12, {name: 'VideoList', lines: [10, 11, 12]}],\n  [13, {name: 'SearchableVideoList'}],\n]);\n\nfunction Example3() {\n  const [area, meta, onLineHover] = useCodeHover(example3Areas);\n  const videos = [\n    {\n      id: 'vids-0',\n      title: 'React: The Documentary',\n      description: 'The origin story of React',\n      image: '/images/home/videos/documentary.webp',\n      url: 'https://www.youtube.com/watch?v=8pDqJVdNa44',\n    },\n    {\n      id: 'vids-1',\n      title: 'Rethinking Best Practices',\n      description: 'Pete Hunt (2013)',\n      image: '/images/home/videos/rethinking.jpg',\n      url: 'https://www.youtube.com/watch?v=x7cQ3mrcKaY',\n    },\n    {\n      id: 'vids-2',\n      title: 'Introducing React Native',\n      description: 'Tom Occhino (2015)',\n      image: '/images/home/videos/rn.jpg',\n      url: 'https://www.youtube.com/watch?v=KVZ-P-ZI6W4',\n    },\n    {\n      id: 'vids-3',\n      title: 'Introducing React Hooks',\n      description: 'Sophie Alpert and Dan Abramov (2018)',\n      image: '/images/home/videos/hooks.jpg',\n      url: 'https://www.youtube.com/watch?v=V-QO-KO90iQ',\n    },\n    {\n      id: 'vids-4',\n      title: 'Introducing Server Components',\n      description: 'Dan Abramov and Lauren Tan (2020)',\n      image: '/images/home/videos/rsc.jpg',\n      url: 'https://www.youtube.com/watch?v=TQQPAU21ZUw',\n    },\n  ];\n\n  return (\n    <ExampleLayout\n      filename=\"SearchableVideoList.js\"\n      activeArea={area}\n      hoverTopOffset={60}\n      left={\n        <CodeBlock\n          onLineHover={onLineHover}\n          isFromPackageImport={false}\n          noShadow={true}\n          noMargin={true}>\n          <div meta={meta}>{`import { useState } from 'react';\n\nfunction SearchableVideoList({ videos }) {\n  const [searchText, setSearchText] = useState('');\n  const foundVideos = filterVideos(videos, searchText);\n  return (\n    <>\n      <SearchInput\n        value={searchText}\n        onChange={newText => setSearchText(newText)} />\n      <VideoList\n        videos={foundVideos}\n        emptyHeading={\\`No matches for “\\${searchText}”\\`} />\n    </>\n  );\n}`}</div>\n        </CodeBlock>\n      }\n      right={\n        <BrowserChrome domain=\"example.com\" path={'videos.html'}>\n          <ExamplePanel\n            noShadow={false}\n            noPadding={true}\n            contentMarginTop=\"72px\"\n            height=\"30rem\">\n            <h1 className=\"mx-4 mb-1 font-bold text-3xl text-primary\">\n              React Videos\n            </h1>\n            <p className=\"mx-4 mb-0 leading-snug text-secondary text-xl\">\n              A brief history of React\n            </p>\n            <div className=\"px-4 pb-4\">\n              <SearchableVideoList videos={videos} />\n            </div>\n          </ExamplePanel>\n        </BrowserChrome>\n      }\n    />\n  );\n}\n\nconst example4Areas = new Map([\n  [6, {name: 'ConferenceLayout'}],\n  [7, {name: 'Suspense'}],\n  [8, {name: 'SearchableVideoList'}],\n  [9, {name: 'Suspense'}],\n  [10, {name: 'ConferenceLayout'}],\n  [17, {name: 'SearchableVideoList'}],\n]);\n\nfunction Example4() {\n  const [area, meta, onLineHover] = useCodeHover(example4Areas);\n  const [slug, setSlug] = useState('react-conf-2021');\n  const [animate, setAnimate] = useState(false);\n\n  function navigate(newSlug) {\n    setSlug(newSlug);\n    setAnimate(true);\n  }\n\n  return (\n    <ExampleLayout\n      filename=\"confs/[slug].js\"\n      activeArea={area}\n      hoverTopOffset={60}\n      left={\n        <CodeBlock\n          onLineHover={onLineHover}\n          isFromPackageImport={false}\n          noShadow={true}\n          noMargin={true}>\n          <div meta={meta}>{`import { db } from './database.js';\nimport { Suspense } from 'react';\n\nasync function ConferencePage({ slug }) {\n  const conf = await db.Confs.find({ slug });\n  return (\n    <ConferenceLayout conf={conf}>\n      <Suspense fallback={<TalksLoading />}>\n        <Talks confId={conf.id} />\n      </Suspense>\n    </ConferenceLayout>\n  );\n}\n\nasync function Talks({ confId }) {\n  const talks = await db.Talks.findAll({ confId });\n  const videos = talks.map(talk => talk.video);\n  return <SearchableVideoList videos={videos} />;\n}`}</div>\n        </CodeBlock>\n      }\n      right={\n        <NavContext value={{slug, navigate}}>\n          <BrowserChrome\n            domain=\"example.com\"\n            path={'confs/' + slug}\n            hasRefresh={true}\n            hasPulse={true}>\n            <ExamplePanel\n              noPadding={true}\n              noShadow={true}\n              contentMarginTop=\"56px\"\n              height=\"35rem\">\n              <Suspense fallback={null}>\n                <div style={{animation: animate ? 'fadein 200ms' : null}}>\n                  <link rel=\"preload\" href={reactConf2019Cover} as=\"image\" />\n                  <link rel=\"preload\" href={reactConf2021Cover} as=\"image\" />\n                  <ConferencePage slug={slug} />\n                </div>\n              </Suspense>\n            </ExamplePanel>\n          </BrowserChrome>\n        </NavContext>\n      }\n    />\n  );\n}\n\nfunction useNestedScrollLock(ref) {\n  useEffect(() => {\n    let node = ref.current;\n    let isLocked = false;\n    let lastScroll = performance.now();\n\n    function handleScroll() {\n      if (!isLocked) {\n        isLocked = true;\n        node.style.pointerEvents = 'none';\n      }\n      lastScroll = performance.now();\n    }\n\n    function updateLock() {\n      if (isLocked && performance.now() - lastScroll > 150) {\n        isLocked = false;\n        node.style.pointerEvents = '';\n      }\n    }\n\n    window.addEventListener('scroll', handleScroll);\n    const interval = setInterval(updateLock, 60);\n    return () => {\n      window.removeEventListener('scroll', handleScroll);\n      clearInterval(interval);\n    };\n  }, [ref]);\n}\n\nfunction ExamplePanel({\n  children,\n  noPadding,\n  noShadow,\n  height,\n  contentMarginTop,\n}) {\n  return (\n    <div\n      className={cn(\n        'max-w-3xl rounded-2xl mx-auto text-secondary leading-normal bg-white overflow-hidden w-full overflow-y-auto',\n        noShadow ? 'shadow-none' : 'shadow-nav dark:shadow-nav-dark'\n      )}\n      style={{height}}>\n      <div\n        className={noPadding ? 'p-0' : 'p-4'}\n        style={{contentVisibility: 'auto', marginTop: contentMarginTop}}>\n        {children}\n      </div>\n    </div>\n  );\n}\n\nconst NavContext = createContext(null);\n\nfunction BrowserChrome({children, hasPulse, hasRefresh, domain, path}) {\n  const [restartId, setRestartId] = useState(0);\n  const isPulsing = hasPulse && restartId === 0;\n  const [shouldAnimatePulse, setShouldAnimatePulse] = useState(false);\n  const refreshRef = useRef(null);\n\n  useEffect(() => {\n    if (!isPulsing) {\n      return;\n    }\n    const observer = new IntersectionObserver(\n      (entries) => {\n        entries.forEach((entry) => {\n          setShouldAnimatePulse(entry.isIntersecting);\n        });\n      },\n      {\n        root: null,\n        rootMargin: `0px 0px`,\n      }\n    );\n    observer.observe(refreshRef.current);\n    return () => observer.disconnect();\n  }, [isPulsing]);\n\n  function handleRestart() {\n    confCache = new Map();\n    talksCache = new Map();\n    setRestartId((i) => i + 1);\n  }\n\n  return (\n    <div className=\"mx-auto max-w-3xl shadow-nav dark:shadow-nav-dark relative overflow-hidden w-full dark:border-opacity-10 rounded-2xl\">\n      <div className=\"w-full h-14 rounded-t-2xl shadow-outer-border backdrop-filter overflow-hidden backdrop-blur-lg backdrop-saturate-200 bg-white bg-opacity-90 z-10 absolute top-0 px-3 gap-2 flex flex-row items-center\">\n        <div className=\"select-none h-8 relative bg-gray-30/20 text-sm text-tertiary text-center rounded-full w-full flex-row flex space-between items-center\">\n          {hasRefresh && <div className=\"h-4 w-6\" />}\n          <div className=\"w-full leading-snug flex flex-row items-center justify-center\">\n            <svg\n              className=\"text-tertiary me-1 opacity-60\"\n              width=\"12\"\n              height=\"12\"\n              viewBox=\"0 0 44 44\"\n              fill=\"none\"\n              xmlns=\"http://www.w3.org/2000/svg\">\n              <path\n                fillRule=\"evenodd\"\n                clipRule=\"evenodd\"\n                d=\"M22 4C17.0294 4 13 8.0294 13 13V16H12.3103C10.5296 16 8.8601 16.8343 8.2855 18.5198C7.6489 20.387 7 23.4148 7 28C7 32.5852 7.6489 35.613 8.2855 37.4802C8.8601 39.1657 10.5296 40 12.3102 40H31.6897C33.4704 40 35.1399 39.1657 35.7145 37.4802C36.3511 35.613 37 32.5852 37 28C37 23.4148 36.3511 20.387 35.7145 18.5198C35.1399 16.8343 33.4704 16 31.6897 16H31V13C31 8.0294 26.9706 4 22 4ZM25 16V13C25 11.3431 23.6569 10 22 10C20.3431 10 19 11.3431 19 13V16H25Z\"\n                fill=\"currentColor\"\n              />\n            </svg>\n\n            <span className=\"text-gray-30\">\n              {domain}\n              {path != null && '/'}\n            </span>\n            {path}\n          </div>\n          {hasRefresh && (\n            <div\n              ref={refreshRef}\n              className={cn(\n                'relative rounded-full flex justify-center items-center ',\n                isPulsing && shouldAnimatePulse && 'animation-pulse-button'\n              )}>\n              {isPulsing && shouldAnimatePulse && (\n                <div className=\"z-0 absolute shadow-[0_0_0_8px_rgba(0,0,0,0.5)] inset-0 rounded-full animation-pulse-shadow\" />\n              )}\n              <button\n                aria-label=\"Reload\"\n                onClick={handleRestart}\n                className={\n                  'z-10 flex items-center p-1.5 rounded-full cursor-pointer justify-center' +\n                  // bg-transparent hover:bg-gray-20/50,\n                  // but opaque to obscure the pulsing wave.\n                  ' bg-[#ebecef] hover:bg-[#d3d7de]'\n                }>\n                <IconRestart className=\"text-tertiary text-lg\" />\n              </button>\n            </div>\n          )}\n        </div>\n        {restartId > 0 && (\n          <div\n            key={restartId}\n            className=\"z-10 loading h-0.5 bg-link transition-all duration-200 absolute bottom-0 start-0\"\n            style={{\n              animation: `progressbar ${loadTalksDelay + 100}ms ease-in-out`,\n            }}\n          />\n        )}\n      </div>\n      <div className=\"h-full flex flex-1\" key={restartId}>\n        {children}\n      </div>\n    </div>\n  );\n}\n\nfunction ConferencePage({slug}) {\n  const conf = use(fetchConf(slug));\n  return (\n    <ConferenceLayout conf={conf}>\n      <div data-hover=\"Suspense\">\n        <Suspense fallback={<TalksLoading />}>\n          <Talks confId={conf.id} />\n        </Suspense>\n      </div>\n    </ConferenceLayout>\n  );\n}\n\nfunction TalksLoading() {\n  return (\n    <div className=\"flex flex-col items-center h-[25rem] overflow-hidden\">\n      <div className=\"w-full\">\n        <div className=\"relative overflow-hidden before:-skew-x-12 before:absolute before:inset-0 before:-translate-x-full before:animate-[shimmer_2.5s_infinite] before:bg-gradient-to-r before:from-transparent before:via-white/50 before:to-transparent\">\n          <div className=\"space-y-4\">\n            <div className=\"pt-4 pb-1\">\n              <div className=\"h-10 w-full rounded-full bg-gray-10\"></div>\n            </div>\n            <div className=\"pb-1\">\n              <div className=\"h-5 w-20 rounded-lg bg-gray-10\"></div>\n            </div>\n            <div className=\"flex flex-row items-center gap-3\">\n              <div className=\"aspect-video w-32 xs:w-36 rounded-lg bg-gray-10\"></div>\n              <div className=\"flex flex-col gap-2\">\n                <div className=\"h-3 w-40 rounded-lg bg-gray-10\"></div>\n                <div className=\"h-3 w-32 rounded-lg bg-gray-10\"></div>\n                <div className=\"h-3 w-24 rounded-lg bg-gray-10\"></div>\n              </div>\n            </div>\n            <div className=\"flex flex-row items-center gap-3\">\n              <div className=\"aspect-video w-32 xs:w-36 rounded-lg bg-gray-10\"></div>\n              <div className=\"flex flex-col gap-2\">\n                <div className=\"h-3 w-40 rounded-lg bg-gray-10\"></div>\n                <div className=\"h-3 w-32 rounded-lg bg-gray-10\"></div>\n                <div className=\"h-3 w-24 rounded-lg bg-gray-10\"></div>\n              </div>\n            </div>\n            <div className=\"flex flex-row items-center gap-3\">\n              <div className=\"aspect-video w-32 xs:w-36 rounded-lg bg-gray-10\"></div>\n              <div className=\"flex flex-col gap-2\">\n                <div className=\"h-3 w-40 rounded-lg bg-gray-10\"></div>\n                <div className=\"h-3 w-32 rounded-lg bg-gray-10\"></div>\n                <div className=\"h-3 w-24 rounded-lg bg-gray-10\"></div>\n              </div>\n            </div>\n            <div className=\"flex flex-row items-center gap-3\">\n              <div className=\"aspect-video w-32 xs:w-36 rounded-lg bg-gray-10\"></div>\n              <div className=\"flex flex-col gap-2\">\n                <div className=\"h-3 w-40 rounded-lg bg-gray-10\"></div>\n                <div className=\"h-3 w-32 rounded-lg bg-gray-10\"></div>\n                <div className=\"h-3 w-24 rounded-lg bg-gray-10\"></div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nfunction Talks({confId}) {\n  const videos = use(fetchTalks(confId));\n  return <SearchableVideoList videos={videos} />;\n}\n\nfunction SearchableVideoList({videos}) {\n  const [searchText, setSearchText] = useState('');\n  const foundVideos = filterVideos(videos, searchText);\n  return (\n    <div className=\"mt-3\" data-hover=\"SearchableVideoList\">\n      <SearchInput value={searchText} onChange={setSearchText} />\n      <VideoList\n        videos={foundVideos}\n        emptyHeading={`No matches for “${searchText}”`}\n      />\n    </div>\n  );\n}\n\nfunction filterVideos(videos, query) {\n  const keywords = query\n    .toLowerCase()\n    .split(' ')\n    .filter((s) => s !== '');\n  if (keywords.length === 0) {\n    return videos;\n  }\n  return videos.filter((video) => {\n    const words = (video.title + ' ' + video.description)\n      .toLowerCase()\n      .split(' ');\n    return keywords.every((kw) => words.some((w) => w.startsWith(kw)));\n  });\n}\n\nfunction VideoList({videos, emptyHeading}) {\n  let heading = emptyHeading;\n  const count = videos.length;\n  if (count > 0) {\n    const noun = count > 1 ? 'Videos' : 'Video';\n    heading = count + ' ' + noun;\n  }\n  return (\n    <section className=\"relative\" data-hover=\"VideoList\">\n      <h2\n        className=\"font-bold text-xl text-primary mb-4 leading-snug\"\n        data-hover=\"h2\">\n        {heading}\n      </h2>\n      <div className=\"flex flex-col gap-4\">\n        {videos.map((video) => (\n          <Video key={video.id} video={video} />\n        ))}\n      </div>\n    </section>\n  );\n}\n\nfunction SearchInput({value, onChange}) {\n  const id = useId();\n  return (\n    <form\n      className=\"mb-3 py-1\"\n      data-hover=\"SearchInput\"\n      onSubmit={(e) => e.preventDefault()}>\n      <label htmlFor={id} className=\"sr-only\">\n        Search\n      </label>\n      <div className=\"relative w-full\">\n        <div className=\"absolute inset-y-0 start-0 flex items-center ps-4 pointer-events-none\">\n          <IconSearch className=\"text-gray-30 w-4\" />\n        </div>\n        <input\n          type=\"text\"\n          id={id}\n          className=\"flex ps-11 py-4 h-10 w-full text-start bg-secondary-button outline-none betterhover:hover:bg-opacity-80 pointer items-center text-primary rounded-full align-middle text-base\"\n          placeholder=\"Search\"\n          value={value}\n          onChange={(e) => onChange(e.target.value)}\n        />\n      </div>\n    </form>\n  );\n}\n\nfunction ConferenceLayout({conf, children}) {\n  const {slug, navigate} = useContext(NavContext);\n  const [isPending, startTransition] = useTransition();\n  return (\n    <div\n      className={cn(\n        'transition-opacity delay-100',\n        isPending ? 'opacity-90' : 'opacity-100'\n      )}\n      data-hover=\"ConferenceLayout\">\n      <Cover background={conf.cover}>\n        <select\n          aria-label=\"Event\"\n          defaultValue={slug}\n          onChange={(e) => {\n            startTransition(() => {\n              navigate(e.target.value);\n            });\n          }}\n          className=\"appearance-none pe-8 ps-2 bg-transparent text-primary-dark text-2xl font-bold mb-0.5\"\n          style={{\n            backgroundSize: '4px 4px, 4px 4px',\n            backgroundRepeat: 'no-repeat',\n            backgroundPosition:\n              'calc(100% - 20px) calc(1px + 50%),calc(100% - 16px) calc(1px + 50%)',\n            backgroundImage:\n              'linear-gradient(45deg,transparent 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,transparent 50%)',\n          }}>\n          <option\n            className=\"bg-wash dark:bg-wash-dark text-primary dark:text-primary-dark\"\n            value=\"react-conf-2021\">\n            React Conf 2021\n          </option>\n          <option\n            className=\"bg-wash dark:bg-wash-dark text-primary dark:text-primary-dark\"\n            value=\"react-conf-2019\">\n            React Conf 2019\n          </option>\n        </select>\n      </Cover>\n      <div className=\"px-4 pb-4\" key={conf.id}>\n        {children}\n      </div>\n    </div>\n  );\n}\n\nfunction Cover({background, children}) {\n  return (\n    <div className=\"h-40 overflow-hidden relative items-center flex\">\n      <div className=\"absolute inset-0 px-4 py-2 flex items-end bg-gradient-to-t from-black/40 via-black/0\">\n        {children}\n      </div>\n      <img\n        src={background}\n        width={500}\n        height={263}\n        alt=\"\"\n        className=\"w-full object-cover\"\n      />\n    </div>\n  );\n}\n\nfunction Video({video}) {\n  return (\n    <div className=\"flex flex-row items-center gap-3\" data-hover=\"Video\">\n      <Thumbnail video={video} />\n      <a\n        href={video.url}\n        target=\"_blank\"\n        rel=\"noreferrer\"\n        className=\"outline-link dark:outline-link outline-offset-4 group flex flex-col flex-1 gap-0.5\"\n        data-hover=\"a\">\n        <h3\n          className={cn(\n            'text-base leading-tight text-primary font-bold',\n            video.url && 'group-hover:underline'\n          )}\n          data-hover=\"h3\">\n          {video.title}\n        </h3>\n        <p className=\"text-tertiary text-sm leading-snug\" data-hover=\"p\">\n          {video.description}\n        </p>\n      </a>\n      <LikeButton video={video} />\n    </div>\n  );\n}\n\nfunction Code({children}) {\n  return (\n    <code\n      dir=\"ltr\"\n      className=\"font-mono inline rounded-lg bg-gray-15/40 dark:bg-secondary-button-dark py-0.5 px-1 text-left\">\n      {children}\n    </code>\n  );\n}\n\nfunction Thumbnail({video}) {\n  const {image} = video;\n  return (\n    <a\n      data-hover=\"Thumbnail\"\n      href={video.url}\n      target=\"_blank\"\n      rel=\"noreferrer\"\n      aria-hidden=\"true\"\n      tabIndex={-1}\n      className={cn(\n        'outline-link dark:outline-link outline-offset-2 aspect-video w-32 xs:w-36 select-none flex-col shadow-inner-border rounded-lg flex items-center overflow-hidden justify-center align-middle text-white/50 bg-cover bg-white bg-[conic-gradient(at_top_right,_var(--tw-gradient-stops))]',\n        image === 'blue' && 'from-yellow-50 via-blue-50 to-purple-60',\n        image === 'red' && 'from-yellow-50 via-red-50 to-purple-60',\n        image === 'green' && 'from-yellow-50 via-green-50 to-purple-60',\n        image === 'purple' && 'from-yellow-50 via-purple-50 to-purple-60',\n        typeof image === 'object' && 'from-gray-80 via-gray-95 to-gray-70',\n        video.url && 'hover:opacity-95 transition-opacity'\n      )}\n      style={{\n        backgroundImage:\n          typeof image === 'string' && image.startsWith('/')\n            ? 'url(' + image + ')'\n            : null,\n      }}>\n      {typeof image !== 'string' ? (\n        <>\n          <div className=\"transition-opacity mt-2.5 -space-x-2 flex flex-row w-full justify-center\">\n            {image.speakers.map((src, i) => (\n              <img\n                key={i}\n                className=\"h-8 w-8 border-2 shadow-md border-gray-70 object-cover rounded-full\"\n                src={src}\n                alt=\"\"\n              />\n            ))}\n          </div>\n          <div className=\"mt-1\">\n            <span className=\"inline-flex text-xs font-normal items-center text-primary-dark py-1 whitespace-nowrap outline-link px-1.5 rounded-lg\">\n              <Logo className=\"text-xs me-1 w-4 h-4 text-brand text-brand-dark\" />\n              React Conf\n            </span>\n          </div>\n        </>\n      ) : image.startsWith('/') ? null : (\n        <ThumbnailPlaceholder />\n      )}\n    </a>\n  );\n}\n\nfunction ThumbnailPlaceholder() {\n  return (\n    <svg\n      className=\"drop-shadow-xl\"\n      width=\"36\"\n      height=\"36\"\n      viewBox=\"0 0 72 72\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M36 69C54.2254 69 69 54.2254 69 36C69 17.7746 54.2254 3 36 3C17.7746 3 3 17.7746 3 36C3 54.2254 17.7746 69 36 69ZM52.1716 38.6337L28.4366 51.5801C26.4374 52.6705 24 51.2235 24 48.9464V23.0536C24 20.7764 26.4374 19.3295 28.4366 20.4199L52.1716 33.3663C54.2562 34.5034 54.2562 37.4966 52.1716 38.6337Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n\n// A hack since we don't actually have a backend.\n// Unlike local state, this survives videos being filtered.\nconst likedVideos = new Set();\n\nfunction LikeButton({video}) {\n  const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));\n  const [animate, setAnimate] = useState(false);\n  return (\n    <button\n      data-hover=\"LikeButton\"\n      className={cn(\n        'outline-none focus:bg-red-50/5 focus:text-red-50 relative flex items-center justify-center w-10 h-10 cursor-pointer rounded-full hover:bg-card active:scale-95 active:bg-red-50/5 active:text-red-50',\n        isLiked ? 'text-red-50' : 'text-tertiary'\n      )}\n      aria-label={isLiked ? 'Unsave' : 'Save'}\n      onClick={() => {\n        const nextIsLiked = !isLiked;\n        if (nextIsLiked) {\n          likedVideos.add(video.id);\n        } else {\n          likedVideos.delete(video.id);\n        }\n        setAnimate(true);\n        setIsLiked(nextIsLiked);\n      }}>\n      <svg\n        className=\"absolute overflow-visible\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <circle\n          className={cn(\n            'text-red-50/50 origin-center transition-all ease-in-out',\n            isLiked && animate && 'animate-[circle_.3s_forwards]'\n          )}\n          cx=\"12\"\n          cy=\"12\"\n          r=\"11.5\"\n          fill=\"transparent\"\n          strokeWidth=\"0\"\n          stroke=\"currentColor\"\n        />\n      </svg>\n      {isLiked ? (\n        <svg\n          className={cn(\n            'w-6 h-6 origin-center transition-all ease-in-out',\n            isLiked && animate && 'animate-[scale_.35s_ease-in-out_forwards]'\n          )}\n          viewBox=\"0 0 24 24\"\n          fill=\"none\"\n          xmlns=\"http://www.w3.org/2000/svg\">\n          <path\n            d=\"M12 23a.496.496 0 0 1-.26-.074C7.023 19.973 0 13.743 0 8.68c0-4.12 2.322-6.677 6.058-6.677 2.572 0 5.108 2.387 5.134 2.41l.808.771.808-.771C12.834 4.387 15.367 2 17.935 2 21.678 2 24 4.558 24 8.677c0 5.06-7.022 11.293-11.74 14.246a.496.496 0 0 1-.26.074V23z\"\n            fill=\"currentColor\"\n          />\n        </svg>\n      ) : (\n        <svg\n          className=\"w-6 h-6\"\n          viewBox=\"0 0 24 24\"\n          fill=\"none\"\n          xmlns=\"http://www.w3.org/2000/svg\">\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"m12 5.184-.808-.771-.004-.004C11.065 4.299 8.522 2.003 6 2.003c-3.736 0-6 2.558-6 6.677 0 4.47 5.471 9.848 10 13.079.602.43 1.187.82 1.74 1.167A.497.497 0 0 0 12 23v-.003c.09 0 .182-.026.26-.074C16.977 19.97 24 13.737 24 8.677 24 4.557 21.743 2 18 2c-2.569 0-5.166 2.387-5.192 2.413L12 5.184zm-.002 15.525c2.071-1.388 4.477-3.342 6.427-5.47C20.72 12.733 22 10.401 22 8.677c0-1.708-.466-2.855-1.087-3.55C20.316 4.459 19.392 4 18 4c-.726 0-1.63.364-2.5.9-.67.412-1.148.82-1.266.92-.03.025-.037.031-.019.014l-.013.013L12 7.949 9.832 5.88a10.08 10.08 0 0 0-1.33-.977C7.633 4.367 6.728 4.003 6 4.003c-1.388 0-2.312.459-2.91 1.128C2.466 5.826 2 6.974 2 8.68c0 1.726 1.28 4.058 3.575 6.563 1.948 2.127 4.352 4.078 6.423 5.466z\"\n            fill=\"currentColor\"\n          />\n        </svg>\n      )}\n    </button>\n  );\n}\nfunction SvgContainer({children}) {\n  return (\n    <svg\n      className=\"w-16 h-16 lg:w-20 lg:h-20 rounded-2xl lg:rounded-3xl shadow-nav bg-wash\"\n      viewBox=\"0 0 120 120\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      {children}\n    </svg>\n  );\n}\n\nfunction NativeIcons() {\n  return (\n    <div className=\"flex items-center justify-center gap-5\">\n      <SvgContainer>\n        <path\n          d=\"M89.9356 44.0658C89.4752 44.4231 81.3451 49.0042 81.3451 59.1906C81.3451 70.9729 91.6903 75.1411 91.9999 75.2443C91.9523 75.4984 90.3564 80.9529 86.5455 86.5105C83.1474 91.4013 79.5984 96.2841 74.1995 96.2841C68.8006 96.2841 67.4112 93.148 61.1787 93.148C55.105 93.148 52.9454 96.3873 48.007 96.3873C43.0686 96.3873 39.6229 91.8618 35.6611 86.3041C31.072 79.7778 27.3643 69.639 27.3643 60.0163C27.3643 44.5819 37.3998 36.3963 47.2766 36.3963C52.5246 36.3963 56.8993 39.842 60.1942 39.842C63.3303 39.842 68.221 36.1898 74.1916 36.1898C76.4543 36.1898 84.5844 36.3963 89.9356 44.0658ZM71.3572 29.6556C73.8264 26.7259 75.573 22.6609 75.573 18.5958C75.573 18.0321 75.5254 17.4605 75.4222 17C71.4048 17.1509 66.6252 19.6756 63.7432 23.0182C61.4804 25.5906 59.3685 29.6556 59.3685 33.7762C59.3685 34.3955 59.4717 35.0148 59.5193 35.2133C59.7734 35.2609 60.1863 35.3165 60.5991 35.3165C64.2036 35.3165 68.7371 32.9029 71.3572 29.6556Z\"\n          fill=\"black\"\n        />\n      </SvgContainer>\n      <SvgContainer>\n        <path\n          d=\"M22.1378 84.6843C19.1119 84.6843 16 87.1151 16 91.363C16 95.259 18.7358 98 22.1378 98C24.9457 98 26.1963 96.1105 26.1963 96.1105V96.9286C26.2071 97.1436 26.2971 97.3469 26.4489 97.4991C26.6008 97.6513 26.8036 97.7416 27.018 97.7523H29.0473V84.9626H26.1963V86.5864C26.1963 86.5864 24.9346 84.6843 22.1378 84.6843ZM22.6458 87.3002C25.1359 87.3002 26.4434 89.4958 26.4434 91.3686C26.4434 93.4557 24.8916 95.4371 22.65 95.4371C20.7775 95.4371 18.9023 93.9163 18.9023 91.3422C18.9009 89.0102 20.5152 87.2932 22.6458 87.2932V87.3002Z\"\n          fill=\"black\"\n        />\n        <path\n          d=\"M33.01 97.7636C32.9013 97.7667 32.793 97.7475 32.692 97.7072C32.5909 97.6669 32.4991 97.6063 32.4222 97.5292C32.3452 97.4521 32.2848 97.3601 32.2446 97.2587C32.2044 97.1574 32.1852 97.0489 32.1883 96.9399V84.9628H35.0407V86.5462C35.6861 85.5722 36.9478 84.6692 38.8855 84.6692C42.0529 84.6692 43.7435 87.1988 43.7435 89.5655V97.7636H41.7573C41.5268 97.7625 41.3061 97.6703 41.1431 97.5069C40.9801 97.3435 40.8881 97.1223 40.887 96.8912V90.2C40.887 88.8893 40.0861 87.2962 38.2317 87.2962C36.2316 87.2962 35.0393 89.1912 35.0393 90.975V97.7636H33.01Z\"\n          fill=\"black\"\n        />\n        <path\n          d=\"M52.0506 84.6843C49.0248 84.6843 45.9128 87.1151 45.9128 91.363C45.9128 95.259 48.6486 98 52.0506 98C54.8641 98 56.1133 96.1105 56.1133 96.1105V96.9286C56.1241 97.1436 56.2141 97.3469 56.3659 97.4991C56.5178 97.6513 56.7206 97.7416 56.9351 97.7523H58.9643V78.5747H56.1133V86.5919C56.1133 86.5919 54.8475 84.6843 52.0506 84.6843ZM52.5586 87.3002C55.0487 87.3002 56.3562 89.4958 56.3562 91.3686C56.3562 93.4557 54.8045 95.4371 52.5628 95.4371C50.6904 95.4371 48.8152 93.9163 48.8152 91.3422C48.8138 89.0102 50.4225 87.2932 52.5586 87.2932V87.3002Z\"\n          fill=\"black\"\n        />\n        <path\n          d=\"M62.9232 97.7634C62.8145 97.7665 62.7064 97.7473 62.6054 97.707C62.5044 97.6667 62.4126 97.6061 62.3358 97.5289C62.259 97.4518 62.1987 97.3598 62.1587 97.2584C62.1186 97.1571 62.0996 97.0487 62.1029 96.9397V84.9626H64.9539V87.0942C65.4438 85.9004 66.5029 84.8179 68.385 84.8179C68.7253 84.8211 69.0648 84.8532 69.3997 84.9139V87.8692C68.9654 87.7128 68.5078 87.631 68.0464 87.6271C66.0462 87.6271 64.9539 89.5222 64.9539 91.3073V97.7634H62.9232Z\"\n          fill=\"black\"\n        />\n        <path\n          d=\"M86.6997 97.7635C86.591 97.7668 86.4826 97.7477 86.3815 97.7075C86.2803 97.6673 86.1884 97.6067 86.1114 97.5295C86.0345 97.4524 85.9741 97.3603 85.9339 97.2589C85.8938 97.1574 85.8748 97.0489 85.878 96.9398V84.9626H88.7318V97.7635H86.6997Z\"\n          fill=\"black\"\n        />\n        <path\n          d=\"M97.089 84.6843C94.0632 84.6843 90.9526 87.1151 90.9526 91.363C90.9526 95.259 93.6884 98 97.089 98C99.897 98 101.149 96.1105 101.149 96.1105V96.9286C101.16 97.1436 101.25 97.3469 101.402 97.4991C101.553 97.6513 101.756 97.7416 101.971 97.7523H104V78.5747H101.149V86.5919C101.149 86.5919 99.8873 84.6843 97.089 84.6843ZM97.5971 87.3002C100.095 87.3002 101.395 89.4958 101.395 91.3686C101.395 93.4557 99.8429 95.4371 97.6026 95.4371C95.7288 95.4371 93.855 93.9163 93.855 91.3422C93.8536 89.0102 95.4678 87.2932 97.5971 87.2932V87.3002Z\"\n          fill=\"black\"\n        />\n        <path\n          d=\"M87.2813 82.2103C88.3231 82.2103 89.1676 81.3637 89.1676 80.3194C89.1676 79.2751 88.3231 78.4285 87.2813 78.4285C86.2395 78.4285 85.395 79.2751 85.395 80.3194C85.395 81.3637 86.2395 82.2103 87.2813 82.2103Z\"\n          fill=\"black\"\n        />\n        <path\n          d=\"M76.9184 84.6731C73.7496 84.6731 70.2712 87.0496 70.2712 91.3407C70.2712 95.2547 73.236 97.9999 76.9143 97.9999C81.4475 97.9999 83.66 94.3475 83.66 91.3643C83.66 87.705 80.8104 84.6731 76.9212 84.6731H76.9184ZM76.9282 87.3432C79.1198 87.3432 80.7549 89.1131 80.7549 91.3476C80.7549 93.6226 79.0199 95.3827 76.9351 95.3827C75.0002 95.3827 73.1194 93.8035 73.1194 91.3922C73.1194 88.9405 74.9086 87.3488 76.9282 87.3488V87.3432Z\"\n          fill=\"black\"\n        />\n        <path\n          d=\"M81.4769 35.895L88.7723 23.2263C88.9677 22.8863 89.021 22.4826 88.9207 22.1034C88.8203 21.7241 88.5743 21.4 88.2365 21.2018C88.0696 21.1035 87.8848 21.0394 87.693 21.0133C87.5011 20.9871 87.306 20.9993 87.119 21.0493C86.9319 21.0992 86.7566 21.1859 86.6031 21.3043C86.4497 21.4227 86.3213 21.5704 86.2253 21.7389L78.8355 34.5704C73.196 31.988 66.85 30.5493 60.0237 30.5493C53.1975 30.5493 46.8501 31.988 41.212 34.5704L33.8208 21.7389C33.7265 21.565 33.5983 21.4118 33.4439 21.2884C33.2895 21.165 33.1119 21.0739 32.9217 21.0205C32.7316 20.9671 32.5326 20.9524 32.3367 20.9774C32.1408 21.0025 31.9519 21.0666 31.7811 21.1661C31.6104 21.2656 31.4613 21.3984 31.3427 21.5567C31.224 21.715 31.1383 21.8955 31.0904 22.0876C31.0426 22.2797 31.0337 22.4794 31.0643 22.675C31.0948 22.8706 31.1642 23.0581 31.2683 23.2263L38.5623 35.895C25.9827 42.7268 17.4645 55.4915 16.0557 70.4337H104C102.587 55.4915 94.0661 42.7268 81.4769 35.895ZM39.8365 58.053C39.1072 58.0533 38.3943 57.8368 37.7878 57.4308C37.1814 57.0248 36.7086 56.4477 36.4294 55.7723C36.1502 55.097 36.0771 54.3538 36.2193 53.6368C36.3615 52.9199 36.7126 52.2612 37.2283 51.7443C37.744 51.2274 38.401 50.8754 39.1162 50.7329C39.8315 50.5903 40.5728 50.6636 41.2465 50.9435C41.9202 51.2234 42.496 51.6973 42.9009 52.3052C43.3059 52.9131 43.5219 53.6278 43.5217 54.3589C43.5209 55.3389 43.132 56.2785 42.4405 56.9712C41.749 57.6639 40.8113 58.053 39.8337 58.053H39.8365ZM80.2068 58.053C79.4776 58.053 78.7648 57.8363 78.1585 57.4301C77.5523 57.024 77.0798 56.4467 76.8008 55.7714C76.5218 55.096 76.4488 54.3529 76.5912 53.636C76.7336 52.9191 77.0848 52.2606 77.6005 51.7438C78.1162 51.2271 78.7733 50.8752 79.4885 50.7328C80.2037 50.5903 80.945 50.6637 81.6186 50.9436C82.2922 51.2235 82.8679 51.6974 83.2728 52.3053C83.6777 52.9133 83.8937 53.6279 83.8934 54.3589C83.8932 54.8443 83.7976 55.3249 83.6121 55.7733C83.4266 56.2217 83.1548 56.629 82.8122 56.9721C82.4695 57.3152 82.0629 57.5872 81.6154 57.7727C81.1679 57.9581 80.6883 58.0534 80.2041 58.053H80.2068Z\"\n          fill=\"#32DE84\"\n        />\n      </SvgContainer>\n    </div>\n  );\n}\n\nfunction WebIcons() {\n  return (\n    <div className=\"flex items-center justify-center gap-3\">\n      <SvgContainer>\n        <g clipPath=\"url(#ee)\">\n          <path\n            d=\"m60 81.99c12.15 0 22-9.8497 22-22 0-12.15-9.8497-22-22-22s-22 9.8498-22 22c0 12.15 9.8497 22 22 22z\"\n            fill=\"#fff\"\n          />\n          <path\n            d=\"m60 38h38.099c-3.8606-6.6892-9.4144-12.244-16.103-16.106-6.6884-3.862-14.276-5.8948-21.999-5.8943-7.7232 5e-4 -15.31 2.0345-21.998 5.8973-6.6879 3.8629-12.241 9.4184-16.101 16.108l19.05 32.995 0.017-0.0044c-1.9378-3.3417-2.9604-7.1352-2.9648-10.998-0.0043-3.8629 1.0098-7.6586 2.9401-11.005 1.9303-3.346 4.7086-6.124 8.0548-8.0539 3.3463-1.93 7.1422-2.9437 11.005-2.9389z\"\n            fill=\"url(#cc)\"\n          />\n          <path\n            d=\"m60 77.417c9.619 0 17.417-7.7977 17.417-17.417 0-9.6189-7.7977-17.417-17.417-17.417-9.6189 0-17.417 7.7977-17.417 17.417 0 9.619 7.7977 17.417 17.417 17.417z\"\n            fill=\"#1A73E8\"\n          />\n          <path\n            d=\"m79.05 71.006-19.05 32.994c7.7233 1e-3 15.311-2.031 21.999-5.8925 6.6886-3.8614 12.243-9.4158 16.104-16.105 3.8607-6.6888 5.8927-14.276 5.8917-22-1e-3 -7.7232-2.036-15.31-5.8996-21.997h-38.099l-0.0045 0.017c3.8629-0.0074 7.6595 1.0036 11.007 2.9313 3.3476 1.9277 6.1278 4.7038 8.0604 8.0485s2.9492 7.1398 2.9474 11.003c-0.0017 3.8629-1.0219 7.657-2.9575 11z\"\n            fill=\"url(#bb)\"\n          />\n          <path\n            d=\"m40.949 71.006-19.049-32.995c-3.8626 6.688-5.8963 14.275-5.8966 21.998s2.0328 15.31 5.8949 21.999 9.4171 12.242 16.106 16.102c6.6893 3.8603 14.277 5.8913 22 5.8893l19.049-32.994-0.0123-0.0124c-1.925 3.349-4.699 6.1314-8.0422 8.0666s-7.1375 2.9549-11 2.9562-7.6579-1.0158-11.002-2.9488c-3.3445-1.9329-6.1203-4.7134-8.0476-8.0612z\"\n            fill=\"url(#aa)\"\n          />\n        </g>\n        <defs>\n          <linearGradient\n            id=\"cc\"\n            x1=\"21.898\"\n            x2=\"98.099\"\n            y1=\"43.5\"\n            y2=\"43.5\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#D93025\" offset=\"0\" />\n            <stop stopColor=\"#EA4335\" offset=\"1\" />\n          </linearGradient>\n          <linearGradient\n            id=\"bb\"\n            x1=\"53.99\"\n            x2=\"92.09\"\n            y1=\"103.41\"\n            y2=\"37.42\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#FCC934\" offset=\"0\" />\n            <stop stopColor=\"#FBBC04\" offset=\"1\" />\n          </linearGradient>\n          <linearGradient\n            id=\"aa\"\n            x1=\"64.763\"\n            x2=\"26.663\"\n            y1=\"101.25\"\n            y2=\"35.261\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#1E8E3E\" offset=\"0\" />\n            <stop stopColor=\"#34A853\" offset=\"1\" />\n          </linearGradient>\n          <clipPath id=\"ee\">\n            <rect\n              transform=\"translate(16 16)\"\n              width=\"88\"\n              height=\"88\"\n              fill=\"#fff\"\n            />\n          </clipPath>\n        </defs>\n      </SvgContainer>\n\n      <SvgContainer>\n        <path\n          d=\"m101.3 42.856c-1.9371-4.6598-5.8655-9.691-8.9417-11.282 2.1941 4.2494 3.7168 8.8131 4.5137 13.529l0.0081 0.0748c-5.0393-12.564-13.585-17.63-20.564-28.66-0.3605-0.5624-0.7106-1.1313-1.05-1.7066-0.175-0.3005-0.3388-0.6074-0.491-0.92-0.2895-0.5606-0.5126-1.153-0.6647-1.7653 2e-4 -0.0282-0.01-0.0556-0.0287-0.0768s-0.0445-0.0348-0.0725-0.0382c-0.0275-0.0075-0.0565-0.0075-0.084 0-0.0057 0-0.0149 0.0104-0.0218 0.0127s-0.0219 0.0126-0.0322 0.0172l0.0172-0.0299c-11.195 6.555-14.994 18.69-15.343 24.76-4.4708 0.3074-8.7453 1.9549-12.266 4.7277-0.3673-0.3111-0.7512-0.6021-1.15-0.8717-1.0156-3.5547-1.0588-7.3168-0.1253-10.894-4.1114 1.9917-7.7645 4.8151-10.728 8.2915h-0.0207c-1.7664-2.239-1.6422-9.622-1.541-11.164-0.5225 0.21-1.0214 0.4749-1.4881 0.7901-1.5593 1.1129-3.0171 2.3617-4.3562 3.7317-1.5259 1.5472-2.9196 3.2193-4.1664 4.9991v0.0069-0.0081c-2.8656 4.0625-4.898 8.6523-5.98 13.504l-0.0598 0.2944c-0.1644 0.9247-0.3105 1.8525-0.4382 2.783 0 0.0333-0.0069 0.0644-0.0103 0.0977-0.3902 2.0279-0.6319 4.0815-0.7234 6.1445v0.23c0.0098 11.16 4.2056 21.91 11.758 30.126 7.5526 8.216 17.912 13.3 29.032 14.247s22.19-2.312 31.023-9.132c8.8332-6.8204 14.786-16.706 16.683-27.704 0.075-0.575 0.136-1.1443 0.203-1.725 0.918-7.5875-0.076-15.284-2.891-22.389zm-51.371 34.889c0.2082 0.1001 0.4037 0.2082 0.6176 0.3036l0.031 0.0196c-0.2162-0.1035-0.4324-0.2112-0.6486-0.3232zm46.954-32.556v-0.0425l0.0081 0.0471-0.0081-0.0046z\"\n          fill=\"url(#l)\"\n        />\n        <path\n          d=\"m101.3 42.856c-1.9371-4.6598-5.8655-9.691-8.9417-11.282 2.1941 4.2494 3.7168 8.8131 4.5137 13.529v0.0426l0.0081 0.0471c3.4349 9.8307 2.9384 20.609-1.3869 30.082-5.1083 10.961-17.473 22.195-36.828 21.649-20.913-0.5923-39.33-16.11-42.773-36.436-0.6268-3.205 0-4.83 0.3151-7.4347-0.4299 2.0233-0.6697 4.0823-0.7165 6.1502v0.23c0.0098 11.16 4.2056 21.91 11.758 30.126 7.5526 8.216 17.912 13.3 29.032 14.247s22.19-2.312 31.023-9.1321c8.8332-6.8204 14.786-16.706 16.683-27.704 0.075-0.575 0.136-1.1443 0.203-1.725 0.918-7.5875-0.076-15.284-2.891-22.389z\"\n          fill=\"url(#i)\"\n        />\n        <path\n          d=\"m101.3 42.856c-1.9371-4.6598-5.8655-9.691-8.9417-11.282 2.1941 4.2494 3.7168 8.8131 4.5137 13.529v0.0426l0.0081 0.0471c3.4349 9.8307 2.9384 20.609-1.3869 30.082-5.1083 10.961-17.473 22.195-36.828 21.649-20.913-0.5923-39.33-16.11-42.773-36.436-0.6268-3.205 0-4.83 0.3151-7.4347-0.4299 2.0233-0.6697 4.0823-0.7165 6.1502v0.23c0.0098 11.16 4.2056 21.91 11.758 30.126 7.5526 8.216 17.912 13.3 29.032 14.247s22.19-2.312 31.023-9.1321c8.8332-6.8204 14.786-16.706 16.683-27.704 0.075-0.575 0.136-1.1443 0.203-1.725 0.918-7.5875-0.076-15.284-2.891-22.389z\"\n          fill=\"url(#h)\"\n        />\n        <path\n          d=\"m79.644 48.095c0.0966 0.0678 0.1863 0.1357 0.2772 0.2035-1.1196-1.9852-2.5133-3.8028-4.14-5.3992-13.853-13.855-3.6306-30.042-1.9067-30.864l0.0172-0.0253c-11.195 6.555-14.994 18.69-15.343 24.76 0.5198-0.0357 1.035-0.0794 1.5663-0.0794 3.9723 0.0073 7.8719 1.0664 11.302 3.0696 3.4303 2.0031 6.2689 4.879 8.2272 8.335z\"\n          fill=\"url(#g)\"\n        />\n        <path\n          d=\"m60.144 50.862c-0.0736 1.1086-3.9905 4.9323-5.3602 4.9323-12.674 0-14.732 7.6671-14.732 7.6671 0.5612 6.4561 5.06 11.774 10.498 14.587 0.2484 0.1288 0.5002 0.2449 0.7521 0.3588 0.4362 0.1932 0.8724 0.3718 1.3087 0.5359 1.8664 0.6605 3.8212 1.0377 5.7994 1.1189 22.215 1.0419 26.518-26.565 10.487-34.576 3.7818-0.492 7.6116 0.4378 10.747 2.6094-1.9583-3.4561-4.7969-6.3319-8.2272-8.3351-3.4302-2.0031-7.3298-3.0622-11.302-3.0695-0.529 0-1.0465 0.0437-1.5663 0.0794-4.4708 0.3073-8.7453 1.9548-12.266 4.7276 0.6797 0.575 1.4467 1.3432 3.0625 2.936 3.0245 2.9796 10.781 6.0662 10.798 6.4285z\"\n          fill=\"url(#f)\"\n        />\n        <path\n          d=\"m60.144 50.862c-0.0736 1.1086-3.9905 4.9323-5.3602 4.9323-12.674 0-14.732 7.6671-14.732 7.6671 0.5612 6.4561 5.06 11.774 10.498 14.587 0.2484 0.1288 0.5002 0.2449 0.7521 0.3588 0.4362 0.1932 0.8724 0.3718 1.3087 0.5359 1.8664 0.6605 3.8212 1.0377 5.7994 1.1189 22.215 1.0419 26.518-26.565 10.487-34.576 3.7818-0.492 7.6116 0.4378 10.747 2.6094-1.9583-3.4561-4.7969-6.3319-8.2272-8.3351-3.4302-2.0031-7.3298-3.0622-11.302-3.0695-0.529 0-1.0465 0.0437-1.5663 0.0794-4.4708 0.3073-8.7453 1.9548-12.266 4.7276 0.6797 0.575 1.4467 1.3432 3.0625 2.936 3.0245 2.9796 10.781 6.0662 10.798 6.4285z\"\n          fill=\"url(#e)\"\n        />\n        <path\n          d=\"m44.205 40.015c0.3611 0.23 0.6589 0.4301 0.92 0.6107-1.0156-3.5547-1.0589-7.3168-0.1254-10.894-4.1113 1.9917-7.7644 4.8151-10.728 8.2915 0.2173-0.0057 6.6826-0.1219 9.9337 1.9918z\"\n          fill=\"url(#d)\"\n        />\n        <path\n          d=\"m15.902 60.487c3.4397 20.325 21.86 35.843 42.773 36.436 19.354 0.5474 31.719-10.688 36.828-21.649 4.3254-9.4729 4.8223-20.251 1.3869-30.082v-0.0425c0-0.0334-0.0069-0.0529 0-0.0426l0.0081 0.0748c1.5812 10.324-3.6697 20.325-11.878 27.088l-0.0253 0.0575c-15.994 13.026-31.301 7.8591-34.399 5.75-0.2162-0.1035-0.4324-0.2112-0.6486-0.3231-9.3253-4.4574-13.178-12.954-12.352-20.24-2.2137 0.0326-4.3893-0.5774-6.2632-1.7561-1.874-1.1788-3.3659-2.8757-4.295-4.8852 2.4479-1.4997 5.2391-2.3475 8.1075-2.4626 2.8685-0.1152 5.7186 0.5062 8.2789 1.8048 5.2783 2.3962 11.285 2.6323 16.735 0.6578-0.0173-0.3622-7.774-3.45-10.798-6.4285-1.6158-1.5927-2.3828-2.3598-3.0625-2.9359-0.3673-0.3112-0.7512-0.6022-1.15-0.8717-0.2645-0.1806-0.5623-0.3761-0.92-0.6107-3.251-2.1137-9.7163-1.9975-9.9302-1.9918h-0.0207c-1.7664-2.239-1.6422-9.622-1.541-11.164-0.5226 0.21-1.0214 0.4749-1.4881 0.7901-1.5594 1.1129-3.0171 2.3617-4.3562 3.7317-1.5314 1.5428-2.9309 3.2111-4.1837 4.9876v0.0069-0.0081c-2.8656 4.0624-4.898 8.6523-5.98 13.504-0.0219 0.0908-1.6054 7.0138-0.8246 10.604z\"\n          fill=\"url(#c)\"\n        />\n        <path\n          d=\"m75.784 42.9c1.6271 1.5982 3.0208 3.4178 4.14 5.405 0.2449 0.1852 0.4738 0.3692 0.6681 0.5474 10.105 9.315 4.8105 22.482 4.416 23.42 8.2087-6.7632 13.455-16.765 11.878-27.088-5.0416-12.57-13.587-17.635-20.567-28.666-0.3605-0.5624-0.7106-1.1313-1.05-1.7066-0.175-0.3005-0.3388-0.6074-0.491-0.92-0.2895-0.5606-0.5126-1.153-0.6647-1.7653 2e-4 -0.0282-0.01-0.0556-0.0287-0.0768s-0.0445-0.0348-0.0725-0.0382c-0.0275-0.0075-0.0565-0.0075-0.084 0-0.0057 0-0.0149 0.0104-0.0218 0.0127s-0.0219 0.0126-0.0322 0.0172c-1.7239 0.8177-11.946 17.004 1.909 30.859z\"\n          fill=\"url(#b)\"\n        />\n        <path\n          d=\"m80.585 48.846c-0.2142-0.1927-0.4371-0.3754-0.6682-0.5474-0.0908-0.0679-0.1805-0.1357-0.2771-0.2036-3.1351-2.1715-6.965-3.1014-10.747-2.6093 16.031 8.0155 11.73 35.618-10.487 34.576-1.9781-0.0812-3.933-0.4584-5.7994-1.119-0.4362-0.1633-0.8725-0.3419-1.3087-0.5359-0.2519-0.115-0.5037-0.23-0.7521-0.3588l0.031 0.0196c3.0981 2.1148 18.4 7.2818 34.399-5.75l0.0253-0.0575c0.399-0.9315 5.6936-14.102-4.416-23.414z\"\n          fill=\"url(#a)\"\n        />\n        <path\n          d=\"m40.052 63.462s2.0573-7.667 14.732-7.667c1.3696 0 5.29-3.8238 5.3601-4.9324-5.4501 1.9745-11.456 1.7384-16.735-0.6578-2.5602-1.2986-5.4104-1.9199-8.2788-1.8048-2.8684 0.1152-5.6596 0.963-8.1075 2.4626 0.9291 2.0095 2.421 3.7064 4.2949 4.8852 1.874 1.1788 4.0496 1.7888 6.2632 1.7561-0.8257 7.2875 3.0268 15.784 12.352 20.24 0.2081 0.1 0.4036 0.2081 0.6175 0.3036-5.4429-2.8118-9.9371-8.1294-10.498-14.586z\"\n          fill=\"url(#k)\"\n        />\n        <path\n          d=\"m101.3 42.856c-1.9371-4.6598-5.8655-9.691-8.9417-11.282 2.1941 4.2494 3.7168 8.8131 4.5137 13.529l0.0081 0.0748c-5.0393-12.564-13.585-17.63-20.564-28.66-0.3605-0.5624-0.7106-1.1313-1.05-1.7066-0.175-0.3005-0.3388-0.6074-0.491-0.92-0.2895-0.5606-0.5126-1.153-0.6647-1.7653 2e-4 -0.0282-0.01-0.0556-0.0287-0.0768s-0.0445-0.0348-0.0725-0.0382c-0.0275-0.0075-0.0565-0.0075-0.084 0-0.0057 0-0.0149 0.0104-0.0218 0.0127s-0.0219 0.0126-0.0322 0.0172l0.0172-0.0299c-11.195 6.555-14.994 18.69-15.343 24.76 0.5198-0.0356 1.035-0.0793 1.5663-0.0793 3.9723 0.0073 7.8719 1.0663 11.302 3.0695 3.4303 2.0032 6.2689 4.879 8.2272 8.335-3.1351-2.1715-6.9649-3.1014-10.747-2.6093 16.031 8.0155 11.73 35.618-10.487 34.576-1.9782-0.0813-3.933-0.4584-5.7994-1.119-0.4363-0.1633-0.8725-0.3419-1.3087-0.5359-0.2519-0.115-0.5037-0.23-0.7521-0.3588l0.031 0.0196c-0.2162-0.1035-0.4324-0.2112-0.6486-0.3232 0.2082 0.1001 0.4037 0.2082 0.6176 0.3036-5.443-2.8129-9.9372-8.1305-10.498-14.587 0 0 2.0574-7.667 14.732-7.667 1.3697 0 5.29-3.8238 5.3602-4.9324-0.0173-0.3622-7.774-3.45-10.798-6.4285-1.6158-1.5927-2.3828-2.3598-3.0625-2.9359-0.3673-0.3111-0.7512-0.6021-1.15-0.8717-1.0156-3.5547-1.0588-7.3168-0.1253-10.894-4.1114 1.9917-7.7645 4.8151-10.728 8.2915h-0.0207c-1.7664-2.239-1.6422-9.622-1.541-11.164-0.5225 0.21-1.0214 0.4749-1.4881 0.7901-1.5593 1.1129-3.0171 2.3617-4.3562 3.7317-1.5259 1.5472-2.9196 3.2193-4.1664 4.9991v0.0069-0.0081c-2.8656 4.0625-4.898 8.6523-5.98 13.504l-0.0598 0.2944c-0.084 0.3921-0.46 2.3839-0.5141 2.8117v0c-0.3439 2.0561-0.5636 4.131-0.6578 6.2135v0.23c0.0098 11.16 4.2056 21.91 11.758 30.126 7.5526 8.216 17.912 13.3 29.032 14.247s22.19-2.312 31.023-9.132c8.8332-6.8204 14.786-16.706 16.683-27.704 0.075-0.575 0.136-1.1443 0.203-1.725 0.918-7.5875-0.076-15.284-2.891-22.389z\"\n          fill=\"url(#j)\"\n        />\n        <defs>\n          <linearGradient\n            id=\"l\"\n            x1=\"95.404\"\n            x2=\"21.414\"\n            y1=\"26.252\"\n            y2=\"97.638\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#FFF44F\" offset=\".048\" />\n            <stop stopColor=\"#FFE847\" offset=\".111\" />\n            <stop stopColor=\"#FFC830\" offset=\".225\" />\n            <stop stopColor=\"#FF980E\" offset=\".368\" />\n            <stop stopColor=\"#FF8B16\" offset=\".401\" />\n            <stop stopColor=\"#FF672A\" offset=\".462\" />\n            <stop stopColor=\"#FF3647\" offset=\".534\" />\n            <stop stopColor=\"#E31587\" offset=\".705\" />\n          </linearGradient>\n          <radialGradient\n            id=\"i\"\n            cx=\"0\"\n            cy=\"0\"\n            r=\"1\"\n            gradientTransform=\"translate(91.985 22.211) scale(92.916 92.917)\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#FFBD4F\" offset=\".129\" />\n            <stop stopColor=\"#FFAC31\" offset=\".186\" />\n            <stop stopColor=\"#FF9D17\" offset=\".247\" />\n            <stop stopColor=\"#FF980E\" offset=\".283\" />\n            <stop stopColor=\"#FF563B\" offset=\".403\" />\n            <stop stopColor=\"#FF3750\" offset=\".467\" />\n            <stop stopColor=\"#F5156C\" offset=\".71\" />\n            <stop stopColor=\"#EB0878\" offset=\".782\" />\n            <stop stopColor=\"#E50080\" offset=\".86\" />\n          </radialGradient>\n          <radialGradient\n            id=\"h\"\n            cx=\"0\"\n            cy=\"0\"\n            r=\"1\"\n            gradientTransform=\"translate(58.032 60.198) scale(92.916 92.917)\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#960E18\" offset=\".3\" />\n            <stop stopColor=\"#B11927\" stopOpacity=\".74\" offset=\".351\" />\n            <stop stopColor=\"#DB293D\" stopOpacity=\".343\" offset=\".435\" />\n            <stop stopColor=\"#F5334B\" stopOpacity=\".094\" offset=\".497\" />\n            <stop stopColor=\"#FF3750\" stopOpacity=\"0\" offset=\".53\" />\n          </radialGradient>\n          <radialGradient\n            id=\"g\"\n            cx=\"0\"\n            cy=\"0\"\n            r=\"1\"\n            gradientTransform=\"translate(69.234 1.1246) scale(67.314)\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#FFF44F\" offset=\".132\" />\n            <stop stopColor=\"#FFDC3E\" offset=\".252\" />\n            <stop stopColor=\"#FF9D12\" offset=\".506\" />\n            <stop stopColor=\"#FF980E\" offset=\".526\" />\n          </radialGradient>\n          <radialGradient\n            id=\"f\"\n            cx=\"0\"\n            cy=\"0\"\n            r=\"1\"\n            gradientTransform=\"translate(47.755 84.468) scale(44.242 44.242)\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#3A8EE6\" offset=\".353\" />\n            <stop stopColor=\"#5C79F0\" offset=\".472\" />\n            <stop stopColor=\"#9059FF\" offset=\".669\" />\n            <stop stopColor=\"#C139E6\" offset=\"1\" />\n          </radialGradient>\n          <radialGradient\n            id=\"e\"\n            cx=\"0\"\n            cy=\"0\"\n            r=\"1\"\n            gradientTransform=\"translate(63.11 52.583) rotate(-13.592) scale(23.457 27.462)\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#9059FF\" stopOpacity=\"0\" offset=\".206\" />\n            <stop stopColor=\"#8C4FF3\" stopOpacity=\".064\" offset=\".278\" />\n            <stop stopColor=\"#7716A8\" stopOpacity=\".45\" offset=\".747\" />\n            <stop stopColor=\"#6E008B\" stopOpacity=\".6\" offset=\".975\" />\n          </radialGradient>\n          <radialGradient\n            id=\"d\"\n            cx=\"0\"\n            cy=\"0\"\n            r=\"1\"\n            gradientTransform=\"translate(56.859 18.409) scale(31.827)\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#FFE226\" offset=\"0\" />\n            <stop stopColor=\"#FFDB27\" offset=\".121\" />\n            <stop stopColor=\"#FFC82A\" offset=\".295\" />\n            <stop stopColor=\"#FFA930\" offset=\".502\" />\n            <stop stopColor=\"#FF7E37\" offset=\".732\" />\n            <stop stopColor=\"#FF7139\" offset=\".792\" />\n          </radialGradient>\n          <radialGradient\n            id=\"c\"\n            cx=\"0\"\n            cy=\"0\"\n            r=\"1\"\n            gradientTransform=\"translate(81.876 -1.7782) scale(135.79)\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#FFF44F\" offset=\".113\" />\n            <stop stopColor=\"#FF980E\" offset=\".456\" />\n            <stop stopColor=\"#FF5634\" offset=\".622\" />\n            <stop stopColor=\"#FF3647\" offset=\".716\" />\n            <stop stopColor=\"#E31587\" offset=\".904\" />\n          </radialGradient>\n          <radialGradient\n            id=\"b\"\n            cx=\"0\"\n            cy=\"0\"\n            r=\"1\"\n            gradientTransform=\"translate(70.431 5.7724) rotate(83.976) scale(99.526 65.318)\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#FFF44F\" offset=\"0\" />\n            <stop stopColor=\"#FFE847\" offset=\".06\" />\n            <stop stopColor=\"#FFC830\" offset=\".168\" />\n            <stop stopColor=\"#FF980E\" offset=\".304\" />\n            <stop stopColor=\"#FF8B16\" offset=\".356\" />\n            <stop stopColor=\"#FF672A\" offset=\".455\" />\n            <stop stopColor=\"#FF3647\" offset=\".57\" />\n            <stop stopColor=\"#E31587\" offset=\".737\" />\n          </radialGradient>\n          <radialGradient\n            id=\"a\"\n            cx=\"0\"\n            cy=\"0\"\n            r=\"1\"\n            gradientTransform=\"translate(56.11 30.198) scale(84.778)\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#FFF44F\" offset=\".137\" />\n            <stop stopColor=\"#FF980E\" offset=\".48\" />\n            <stop stopColor=\"#FF5634\" offset=\".592\" />\n            <stop stopColor=\"#FF3647\" offset=\".655\" />\n            <stop stopColor=\"#E31587\" offset=\".904\" />\n          </radialGradient>\n          <radialGradient\n            id=\"k\"\n            cx=\"0\"\n            cy=\"0\"\n            r=\"1\"\n            gradientTransform=\"translate(78.488 35.16) scale(92.789)\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#FFF44F\" offset=\".094\" />\n            <stop stopColor=\"#FFE141\" offset=\".231\" />\n            <stop stopColor=\"#FFAF1E\" offset=\".509\" />\n            <stop stopColor=\"#FF980E\" offset=\".626\" />\n          </radialGradient>\n          <linearGradient\n            id=\"j\"\n            x1=\"94.515\"\n            x2=\"31.557\"\n            y1=\"25.87\"\n            y2=\"88.827\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#FFF44F\" stopOpacity=\".8\" offset=\".167\" />\n            <stop stopColor=\"#FFF44F\" stopOpacity=\".634\" offset=\".266\" />\n            <stop stopColor=\"#FFF44F\" stopOpacity=\".217\" offset=\".489\" />\n            <stop stopColor=\"#FFF44F\" stopOpacity=\"0\" offset=\".6\" />\n          </linearGradient>\n        </defs>\n      </SvgContainer>\n      <SvgContainer>\n        <path\n          d=\"m60 104c24.3 0 44-19.7 44-44s-19.7-44-44-44-44 19.7-44 44 19.7 44 44 44z\"\n          fill=\"url(#aaa)\"\n        />\n        <path\n          d=\"m60.002 100.1v-6.756\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m60.002 26.624v-6.7563\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m53.018 99.512 1.1732-6.6536m11.585-65.704 1.1732-6.6536\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m46.278 97.689 2.3108-6.3488m22.819-62.694 2.3108-6.3488\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m39.91 94.765 3.3781-5.851m33.359-57.779 3.3782-5.8511\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m34.252 90.737 4.3428-5.1756m42.885-51.109 4.3429-5.1756\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m29.227 85.774 5.1756-4.3428m51.109-42.885 5.1756-4.3428\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m25.244 80.033 5.8511-3.3781m57.779-33.359 5.8511-3.3781\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m22.238 73.725 6.3488-2.3108m62.694-22.819 6.3488-2.3108\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m20.469 67.009 6.6535-1.1733m65.704-11.585 6.6536-1.1732\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m19.9 60.002h6.7563m66.718 0h6.7558\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m20.488 53.062 6.6536 1.1732m65.704 11.585 6.6536 1.1732\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m22.309 46.295 6.3488 2.3108m62.694 22.819 6.3488 2.3108\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m25.229 39.99 5.8511 3.3781m57.779 33.359 5.851 3.3781\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m29.24 34.19 5.1756 4.3428m51.109 42.885 5.1756 4.3429\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m34.166 29.281 4.3428 5.1756m42.885 51.109 4.3428 5.1755\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m39.918 25.256 3.3781 5.8511m33.359 57.779 3.3781 5.8511\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m46.28 22.3 2.3108 6.3487m22.819 62.694 2.3107 6.3488\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m52.992 20.554 1.1733 6.6536m11.585 65.704 1.1732 6.6536\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m56.484 99.953 0.2945-3.3653m6.4037-73.194 0.2944-3.3653\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m49.629 98.788 0.8743-3.263m19.016-70.97 0.8743-3.263\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m43.024 96.33 1.4276-3.0616m31.052-66.59 1.4277-3.0616\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m36.978 92.875 1.9376-2.7672m42.143-60.186 1.9376-2.7672\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m31.658 88.369 2.3887-2.3886m51.954-51.954 2.3887-2.3886\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m27.141 83.051 2.7672-1.9376m60.186-42.143 2.7672-1.9376\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m23.653 76.966 3.0616-1.4277m66.59-31.052 3.0617-1.4276\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m21.24 70.41 3.263-0.8744m70.97-19.016 3.263-0.8743\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m20 63.507 3.3653-0.2944m73.194-6.4037 3.3652-0.2944\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m20.03 56.514 3.3653 0.2944m73.194 6.4037 3.3653 0.2944\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m21.19 49.657 3.2631 0.8743m70.97 19.016 3.263 0.8744\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m23.624 43.042 3.0616 1.4276m66.59 31.052 3.0616 1.4277\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m27.127 37.029 2.7671 1.9376m60.186 42.143 2.7672 1.9376\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m31.654 31.661 2.3887 2.3887m51.954 51.954 2.3887 2.3887\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m36.935 27.134 1.9376 2.7672m42.143 60.186 1.9376 2.7672\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m43.044 23.676 1.4276 3.0616m31.052 66.59 1.4277 3.0616\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m49.59 21.297 0.8743 3.263m19.016 70.97 0.8743 3.263\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m56.449 20.063 0.2944 3.3653m6.4037 73.194 0.2944 3.3652\"\n          stroke=\"#fff\"\n          strokeLinecap=\"square\"\n          strokeWidth=\".84706\"\n        />\n        <path\n          d=\"m91.222 33.87-34.71 22.211-27.785 30.15 34.879-21.704 27.616-30.656z\"\n          clipRule=\"evenodd\"\n          fill=\"#fff\"\n          fillRule=\"evenodd\"\n        />\n        <path\n          d=\"m91.222 33.87-34.71 22.211 7.094 8.4453 27.616-30.656z\"\n          clipRule=\"evenodd\"\n          fill=\"#FF3B30\"\n          fillRule=\"evenodd\"\n        />\n        <defs>\n          <linearGradient\n            id=\"aaa\"\n            x1=\"59.999\"\n            x2=\"59.999\"\n            y1=\"104.01\"\n            y2=\"15.992\"\n            gradientUnits=\"userSpaceOnUse\">\n            <stop stopColor=\"#1E6FF1\" offset=\"0\" />\n            <stop stopColor=\"#28CEFB\" offset=\"1\" />\n          </linearGradient>\n        </defs>\n      </SvgContainer>\n    </div>\n  );\n}\n\n// TODO: upgrade React and use the built-in version.\nfunction use(promise) {\n  if (promise.status === 'fulfilled') {\n    return promise.value;\n  } else if (promise.status === 'rejected') {\n    throw promise.reason;\n  } else if (promise.status === 'pending') {\n    throw promise;\n  } else {\n    promise.status = 'pending';\n    promise.then(\n      (result) => {\n        promise.status = 'fulfilled';\n        promise.value = result;\n      },\n      (reason) => {\n        promise.status = 'rejected';\n        promise.reason = reason;\n      }\n    );\n    throw promise;\n  }\n}\n\nlet confCache = new Map();\nlet talksCache = new Map();\nconst loadConfDelay = 250;\nconst loadTalksDelay = 1000;\n\nfunction fetchConf(slug) {\n  if (confCache.has(slug)) {\n    return confCache.get(slug);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      if (slug === 'react-conf-2021') {\n        resolve({\n          id: 0,\n          cover: reactConf2021Cover,\n          name: 'React Conf 2021',\n        });\n      } else if (slug === 'react-conf-2019') {\n        resolve({\n          id: 1,\n          cover: reactConf2019Cover,\n          name: 'React Conf 2019',\n        });\n      }\n    }, loadConfDelay);\n  });\n  confCache.set(slug, promise);\n  return promise;\n}\n\nfunction fetchTalks(confId) {\n  if (talksCache.has(confId)) {\n    return talksCache.get(confId);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      if (confId === 0) {\n        resolve([\n          {\n            id: 'conf-2021-0',\n            title: 'React 18 Keynote',\n            description: 'The React Team',\n            url: 'https://www.youtube.com/watch?v=FZ0cG47msEk&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=1',\n            image: {\n              speakers: [\n                '/images/home/conf2021/andrew.jpg',\n                '/images/home/conf2021/lauren.jpg',\n                '/images/home/conf2021/juan.jpg',\n                '/images/home/conf2021/rick.jpg',\n              ],\n            },\n          },\n          {\n            id: 'conf-2021-1',\n            title: 'React 18 for App Developers',\n            description: 'Shruti Kapoor',\n            url: 'https://www.youtube.com/watch?v=ytudH8je5ko&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=2',\n            image: {\n              speakers: ['/images/home/conf2021/shruti.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-2',\n            title: 'Streaming Server Rendering with Suspense',\n            description: 'Shaundai Person',\n            url: 'https://www.youtube.com/watch?v=pj5N-Khihgc&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=3',\n            image: {\n              speakers: ['/images/home/conf2021/shaundai.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-3',\n            title: 'The First React Working Group',\n            description: 'Aakansha Doshi',\n            url: 'https://www.youtube.com/watch?v=qn7gRClrC9U&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=4',\n            image: {\n              speakers: ['/images/home/conf2021/aakansha.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-4',\n            title: 'React Developer Tooling',\n            description: 'Brian Vaughn',\n            url: 'https://www.youtube.com/watch?v=oxDfrke8rZg&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=5',\n            image: {\n              speakers: ['/images/home/conf2021/brian.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-5',\n            title: 'React without memo',\n            description: 'Xuan Huang (黄玄)',\n            url: 'https://www.youtube.com/watch?v=lGEMwh32soc&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=6',\n            image: {\n              speakers: ['/images/home/conf2021/xuan.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-6',\n            title: 'React Docs Keynote',\n            description: 'Rachel Nabors',\n            url: 'https://www.youtube.com/watch?v=mneDaMYOKP8&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=7',\n            image: {\n              speakers: ['/images/home/conf2021/rachel.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-7',\n            title: 'Things I Learnt from the New React Docs',\n            description: \"Debbie O'Brien\",\n            url: 'https://www.youtube.com/watch?v=-7odLW_hG7s&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=8',\n            image: {\n              speakers: ['/images/home/conf2021/debbie.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-8',\n            title: 'Learning in the Browser',\n            description: 'Sarah Rainsberger',\n            url: 'https://www.youtube.com/watch?v=5X-WEQflCL0&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=9',\n            image: {\n              speakers: ['/images/home/conf2021/sarah.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-9',\n            title: 'The ROI of Designing with React',\n            description: 'Linton Ye',\n            url: 'https://www.youtube.com/watch?v=7cPWmID5XAk&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=10',\n            image: {\n              speakers: ['/images/home/conf2021/linton.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-10',\n            title: 'Interactive Playgrounds with React',\n            description: 'Delba de Oliveira',\n            url: 'https://www.youtube.com/watch?v=zL8cz2W0z34&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=11',\n            image: {\n              speakers: ['/images/home/conf2021/delba.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-11',\n            title: 'Re-introducing Relay',\n            description: 'Robert Balicki',\n            url: 'https://www.youtube.com/watch?v=lhVGdErZuN4&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=12',\n            image: {\n              speakers: ['/images/home/conf2021/robert.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-12',\n            title: 'React Native Desktop',\n            description: 'Eric Rozell and Steven Moyes',\n            url: 'https://www.youtube.com/watch?v=9L4FFrvwJwY&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=13',\n            image: {\n              speakers: [\n                '/images/home/conf2021/eric.jpg',\n                '/images/home/conf2021/steven.jpg',\n              ],\n            },\n          },\n          {\n            id: 'conf-2021-13',\n            title: 'On-device Machine Learning for React Native',\n            description: 'Roman Rädle',\n            url: 'https://www.youtube.com/watch?v=NLj73vrc2I8&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=14',\n            image: {\n              speakers: ['/images/home/conf2021/roman.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-14',\n            title: 'React 18 for External Store Libraries',\n            description: 'Daishi Kato',\n            url: 'https://www.youtube.com/watch?v=oPfSC5bQPR8&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=15',\n            image: {\n              speakers: ['/images/home/conf2021/daishi.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-15',\n            title: 'Building Accessible Components with React 18',\n            description: 'Diego Haz',\n            url: 'https://www.youtube.com/watch?v=dcm8fjBfro8&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=16',\n            image: {\n              speakers: ['/images/home/conf2021/diego.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-16',\n            title: 'Accessible Japanese Form Components with React',\n            description: 'Tafu Nakazaki',\n            url: 'https://www.youtube.com/watch?v=S4a0QlsH0pU&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=17',\n            image: {\n              speakers: ['/images/home/conf2021/tafu.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-17',\n            title: 'UI Tools for Artists',\n            description: 'Lyle Troxell',\n            url: 'https://www.youtube.com/watch?v=b3l4WxipFsE&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=18',\n            image: {\n              speakers: ['/images/home/conf2021/lyle.jpg'],\n            },\n          },\n          {\n            id: 'conf-2021-18',\n            title: 'Hydrogen + React 18',\n            description: 'Helen Lin',\n            url: 'https://www.youtube.com/watch?v=HS6vIYkSNks&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=19',\n            image: {\n              speakers: ['/images/home/conf2021/helen.jpg'],\n            },\n          },\n        ]);\n      } else if (confId === 1) {\n        resolve([\n          {\n            id: 'conf-2019-0',\n            title: 'Keynote (Part 1)',\n            description: 'Tom Occhino',\n            url: 'https://www.youtube.com/watch?v=QnZHO7QvjaM&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh',\n            image: {\n              speakers: ['/images/home/conf2019/tom.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-1',\n            title: 'Keynote (Part 2)',\n            description: 'Yuzhi Zheng',\n            url: 'https://www.youtube.com/watch?v=uXEEL9mrkAQ&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=2',\n            image: {\n              speakers: ['https://conf2019.reactjs.org/img/speakers/yuzhi.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-2',\n            title: 'Building The New Facebook With React and Relay (Part 1)',\n            description: 'Frank Yan',\n            url: 'https://www.youtube.com/watch?v=9JZHodNR184&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=3',\n            image: {\n              speakers: ['/images/home/conf2019/frank.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-3',\n            title: 'Building The New Facebook With React and Relay (Part 2)',\n            description: 'Ashley Watkins',\n            url: 'https://www.youtube.com/watch?v=KT3XKDBZW7M&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=4',\n            image: {\n              speakers: ['/images/home/conf2019/ashley.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-4',\n            title: 'How Our Team Is Using React Native to Save The World',\n            description: 'Tania Papazafeiropoulou',\n            url: 'https://www.youtube.com/watch?v=zVHWugBPGBE&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=5',\n            image: {\n              speakers: ['/images/home/conf2019/tania.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-5',\n            title:\n              'Using Hooks and Codegen to Bring the Benefits of GraphQL to REST APIs',\n            description: 'Tejas Kumar',\n            url: 'https://www.youtube.com/watch?v=cdsnzfJUqm0&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=6',\n            image: {\n              speakers: ['/images/home/conf2019/tejas.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-6',\n            title: 'Building a Custom React Renderer',\n            description: 'Sophie Alpert',\n            url: 'https://www.youtube.com/watch?v=CGpMlWVcHok&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=7',\n            image: {\n              speakers: ['/images/home/conf2019/sophie.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-7',\n            title: 'Is React Translated Yet?',\n            description: 'Nat Alison',\n            url: 'https://www.youtube.com/watch?v=lLE4Jqaek5k&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=12',\n            image: {\n              speakers: ['/images/home/conf2019/nat.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-8',\n            title: 'Building (And Re-Building) the Airbnb Design System',\n            description: 'Maja Wichrowska and Tae Kim',\n            url: 'https://www.youtube.com/watch?v=fHQ1WSx41CA&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=13',\n            image: {\n              speakers: [\n                '/images/home/conf2019/maja.jpg',\n                '/images/home/conf2019/tae.jpg',\n              ],\n            },\n          },\n          {\n            id: 'conf-2019-9',\n            title: 'Accessibility Is a Marathon, Not a Sprint',\n            description: 'Brittany Feenstra',\n            url: 'https://www.youtube.com/watch?v=ONSD-t4gBb8&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=14',\n            image: {\n              speakers: ['/images/home/conf2019/brittany.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-10',\n            title: 'The State of React State in 2019',\n            description: 'Becca Bailey',\n            url: 'https://www.youtube.com/watch?v=wUMMUyQtMSg&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=15',\n            image: {\n              speakers: ['/images/home/conf2019/becca.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-11',\n            title: 'Let’s Program Like It’s 1999',\n            description: 'Lee Byron',\n            url: 'https://www.youtube.com/watch?v=vG8WpLr6y_U&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=16',\n            image: {\n              speakers: ['/images/home/conf2019/lee.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-12',\n            title: 'React Developer Tooling',\n            description: 'Brian Vaughn',\n            url: 'https://www.youtube.com/watch?v=Mjrfb1r3XEM&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=17',\n            image: {\n              speakers: ['/images/home/conf2019/brian.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-13',\n            title: 'Data Fetching With Suspense In Relay',\n            description: 'Joe Savona',\n            url: 'https://www.youtube.com/watch?v=Tl0S7QkxFE4&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=18',\n            image: {\n              speakers: ['/images/home/conf2019/joe.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-14',\n            title: 'Automatic Visualizations of the Frontend',\n            description: 'Cameron Yick',\n            url: 'https://www.youtube.com/watch?v=SbreAPNmZOk&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=19',\n            image: {\n              speakers: ['/images/home/conf2019/cameron.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-15',\n            title: 'React Is Fiction',\n            description: 'Jenn Creighton',\n            url: 'https://www.youtube.com/watch?v=kqh4lz2Lkzs&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=20',\n            image: {\n              speakers: ['/images/home/conf2019/jenn.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-16',\n            title: 'Progressive Web Animations',\n            description: 'Alexandra Holachek',\n            url: 'https://www.youtube.com/watch?v=laPsceJ4tTY&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=21',\n            image: {\n              speakers: ['/images/home/conf2019/alexandra.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-17',\n            title:\n              'Creating Games, Animations and Interactions with the Wick Editor',\n            description: 'Luca Damasco',\n            url: 'https://www.youtube.com/watch?v=laPsceJ4tTY&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=21',\n            image: {\n              speakers: ['/images/home/conf2019/luca.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-18',\n            title: 'Building React-Select',\n            description: 'Jed Watson',\n            url: 'https://www.youtube.com/watch?v=yS0jUnmBujE&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=25',\n            image: {\n              speakers: ['/images/home/conf2019/jed.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-19',\n            title: 'Promoting Transparency in Government Spending with React',\n            description: 'Lizzie Salita',\n            url: 'https://www.youtube.com/watch?v=CVfXICcNfHE&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=26',\n            image: {\n              speakers: ['/images/home/conf2019/lizzie.jpg'],\n            },\n          },\n          {\n            id: 'conf-2019-20',\n            title: 'Wonder-driven Development: Using React to Make a Spaceship',\n            description: 'Alex Anderson',\n            url: 'https://www.youtube.com/watch?v=aV0uOPWHKt4&list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh&index=27',\n            image: {\n              speakers: ['/images/home/conf2019/alex.jpg'],\n            },\n          },\n        ]);\n      }\n    }, loadTalksDelay);\n  });\n  talksCache.set(confId, promise);\n  return promise;\n}\n"
  },
  {
    "path": "src/components/Layout/Page.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {Suspense} from 'react';\nimport * as React from 'react';\nimport {useRouter} from 'next/router';\nimport {SidebarNav} from './SidebarNav';\nimport {Footer} from './Footer';\nimport {Toc} from './Toc';\n// import SocialBanner from '../SocialBanner';\nimport {DocsPageFooter} from 'components/DocsFooter';\nimport {Seo} from 'components/Seo';\nimport PageHeading from 'components/PageHeading';\nimport {getRouteMeta} from './getRouteMeta';\nimport {TocContext} from '../MDX/TocContext';\nimport {Languages, LanguagesContext} from '../MDX/LanguagesContext';\nimport type {TocItem} from 'components/MDX/TocContext';\nimport type {RouteItem} from 'components/Layout/getRouteMeta';\nimport {HomeContent} from './HomeContent';\nimport {TopNav} from './TopNav';\nimport cn from 'classnames';\nimport Head from 'next/head';\n\nimport(/* webpackPrefetch: true */ '../MDX/CodeBlock/CodeBlock');\n\ninterface PageProps {\n  children: React.ReactNode;\n  toc: Array<TocItem>;\n  routeTree: RouteItem;\n  meta: {\n    title?: string;\n    titleForTitleTag?: string;\n    version?: 'experimental' | 'canary';\n    description?: string;\n  };\n  section: 'learn' | 'reference' | 'community' | 'blog' | 'home' | 'unknown';\n  languages?: Languages | null;\n}\n\nexport function Page({\n  children,\n  toc,\n  routeTree,\n  meta,\n  section,\n  languages = null,\n}: PageProps) {\n  const {asPath} = useRouter();\n  const cleanedPath = asPath.split(/[\\?\\#]/)[0];\n  const {route, nextRoute, prevRoute, breadcrumbs, order} = getRouteMeta(\n    cleanedPath,\n    routeTree\n  );\n  const title = meta.title || route?.title || '';\n  const version = meta.version;\n  const description = meta.description || route?.description || '';\n  const isHomePage = cleanedPath === '/';\n  const isBlogIndex = cleanedPath === '/blog';\n\n  let content;\n  if (isHomePage) {\n    content = <HomeContent />;\n  } else {\n    content = (\n      <div className=\"ps-0\">\n        <div\n          className={cn(\n            section === 'blog' && 'mx-auto px-0 lg:px-4 max-w-5xl'\n          )}>\n          <PageHeading\n            title={title}\n            version={version}\n            description={description}\n            tags={route?.tags}\n            breadcrumbs={breadcrumbs}\n          />\n        </div>\n        <div className=\"px-5 sm:px-12\">\n          <div\n            className={cn(\n              'max-w-7xl mx-auto',\n              section === 'blog' && 'lg:flex lg:flex-col lg:items-center'\n            )}>\n            <TocContext value={toc}>\n              <LanguagesContext value={languages}>{children}</LanguagesContext>\n            </TocContext>\n          </div>\n          {!isBlogIndex && (\n            <DocsPageFooter\n              route={route}\n              nextRoute={nextRoute}\n              prevRoute={prevRoute}\n            />\n          )}\n        </div>\n      </div>\n    );\n  }\n\n  let hasColumns = true;\n  let showSidebar = true;\n  let showToc = true;\n  if (isHomePage || isBlogIndex) {\n    hasColumns = false;\n    showSidebar = false;\n    showToc = false;\n  } else if (section === 'blog') {\n    showToc = false;\n    hasColumns = false;\n    showSidebar = false;\n  }\n\n  let searchOrder;\n  if (section === 'learn' || (section === 'blog' && !isBlogIndex)) {\n    searchOrder = order;\n  }\n\n  return (\n    <>\n      <Seo\n        title={title}\n        titleForTitleTag={meta.titleForTitleTag}\n        isHomePage={isHomePage}\n        image={`/images/og-` + section + '.png'}\n        searchOrder={searchOrder}\n      />\n      {(isHomePage || isBlogIndex) && (\n        <Head>\n          <link\n            rel=\"alternate\"\n            type=\"application/rss+xml\"\n            title=\"React Blog RSS Feed\"\n            href=\"/rss.xml\"\n          />\n        </Head>\n      )}\n      {/* <SocialBanner /> */}\n      <TopNav\n        section={section}\n        routeTree={routeTree}\n        breadcrumbs={breadcrumbs}\n      />\n      <div\n        className={cn(\n          hasColumns &&\n            'grid grid-cols-only-content lg:grid-cols-sidebar-content 2xl:grid-cols-sidebar-content-toc'\n        )}>\n        {showSidebar && (\n          <div className=\"lg:-mt-16 z-10\">\n            <div className=\"fixed top-0 py-0 shadow lg:pt-16 lg:sticky start-0 end-0 lg:shadow-none\">\n              <SidebarNav\n                key={section}\n                routeTree={routeTree}\n                breadcrumbs={breadcrumbs}\n              />\n            </div>\n          </div>\n        )}\n        {/* No fallback UI so need to be careful not to suspend directly inside. */}\n        <Suspense fallback={null}>\n          <main className=\"min-w-0 isolate\">\n            <article\n              className=\"font-normal break-words text-primary dark:text-primary-dark\"\n              key={asPath}>\n              {content}\n            </article>\n            <div\n              className={cn(\n                'self-stretch w-full',\n                isHomePage && 'bg-wash dark:bg-gray-95 mt-[-1px]'\n              )}>\n              {!isHomePage && (\n                <div className=\"w-full px-5 pt-10 mx-auto sm:px-12 md:px-12 md:pt-12 lg:pt-10\">\n                  <hr className=\"mx-auto max-w-7xl border-border dark:border-border-dark\" />\n                </div>\n              )}\n              <div\n                className={cn(\n                  'py-12 px-5 sm:px-12 md:px-12 sm:py-12 md:py-16 lg:py-14',\n                  isHomePage && 'lg:pt-0'\n                )}>\n                <Footer />\n              </div>\n            </div>\n          </main>\n        </Suspense>\n        <div className=\"hidden -mt-16 lg:max-w-custom-xs 2xl:block\">\n          {showToc && toc.length > 0 && <Toc headings={toc} key={asPath} />}\n        </div>\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/components/Layout/Sidebar/SidebarButton.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nimport cn from 'classnames';\nimport {IconNavArrow} from 'components/Icon/IconNavArrow';\n\ninterface SidebarButtonProps {\n  title: string;\n  heading: boolean;\n  level: number;\n  onClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;\n  isExpanded?: boolean;\n  isBreadcrumb?: boolean;\n}\n\nexport function SidebarButton({\n  title,\n  heading,\n  level,\n  onClick,\n  isExpanded,\n  isBreadcrumb,\n}: SidebarButtonProps) {\n  return (\n    <div\n      className={cn({\n        'my-1': heading || level === 1,\n        'my-3': level > 1,\n      })}>\n      <button\n        className={cn(\n          'p-2 pe-2 ps-5 w-full rounded-e-lg text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between',\n          {\n            'p-2 text-base': level > 1,\n            'text-link bg-highlight dark:bg-highlight-dark text-base font-bold hover:bg-highlight dark:hover:bg-highlight-dark hover:text-link dark:hover:text-link-dark':\n              !heading && isBreadcrumb && !isExpanded,\n            'p-4 my-6 text-2xl lg:my-auto lg:text-sm font-bold': heading,\n            'p-2 hover:text-gray-70 text-base font-bold text-primary dark:text-primary-dark':\n              !heading && !isBreadcrumb,\n            'text-primary dark:text-primary-dark': heading && !isBreadcrumb,\n            'text-primary dark:text-primary-dark text-base font-bold bg-card dark:bg-card-dark':\n              !heading && isExpanded,\n          }\n        )}\n        onClick={onClick}>\n        {title}\n        {typeof isExpanded && !heading && (\n          <span className=\"pe-2 text-gray-30\">\n            <IconNavArrow displayDirection={isExpanded ? 'down' : 'end'} />\n          </span>\n        )}\n      </button>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/Layout/Sidebar/SidebarLink.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\n/* eslint-disable jsx-a11y/no-static-element-interactions */\n/* eslint-disable jsx-a11y/click-events-have-key-events */\nimport {useRef, useEffect} from 'react';\nimport * as React from 'react';\nimport cn from 'classnames';\nimport {IconNavArrow} from 'components/Icon/IconNavArrow';\nimport {IconCanary} from 'components/Icon/IconCanary';\nimport {IconExperimental} from 'components/Icon/IconExperimental';\nimport Link from 'next/link';\n\ninterface SidebarLinkProps {\n  href: string;\n  selected?: boolean;\n  title: string;\n  level: number;\n  version?: 'canary' | 'major' | 'experimental' | 'rc';\n  icon?: React.ReactNode;\n  isExpanded?: boolean;\n  hideArrow?: boolean;\n  isPending: boolean;\n}\n\nexport function SidebarLink({\n  href,\n  selected = false,\n  title,\n  version,\n  level,\n  isExpanded,\n  hideArrow,\n  isPending,\n}: SidebarLinkProps) {\n  const ref = useRef<HTMLAnchorElement>(null);\n\n  useEffect(() => {\n    if (selected && ref && ref.current) {\n      // @ts-ignore\n      if (typeof ref.current.scrollIntoViewIfNeeded === 'function') {\n        // @ts-ignore\n        ref.current.scrollIntoViewIfNeeded();\n      }\n    }\n  }, [ref, selected]);\n\n  let target = '';\n  if (href.startsWith('https://')) {\n    target = '_blank';\n  }\n  return (\n    <Link\n      href={href}\n      ref={ref}\n      title={title}\n      target={target}\n      passHref\n      aria-current={selected ? 'page' : undefined}\n      className={cn(\n        'p-2 pe-2 w-full rounded-none lg:rounded-e-2xl text-start hover:bg-gray-5 dark:hover:bg-gray-80 relative flex items-center justify-between',\n        {\n          'text-sm ps-6': level > 0,\n          'ps-5': level < 2,\n          'text-base font-bold': level === 0,\n          'text-primary dark:text-primary-dark': level === 0 && !selected,\n          'text-base text-secondary dark:text-secondary-dark':\n            level > 0 && !selected,\n          'text-base text-link dark:text-link-dark bg-highlight dark:bg-highlight-dark border-blue-40 hover:bg-highlight hover:text-link dark:hover:bg-highlight-dark dark:hover:text-link-dark':\n            selected,\n          'dark:bg-gray-70 bg-gray-3 dark:hover:bg-gray-70 hover:bg-gray-3':\n            isPending,\n        }\n      )}>\n      {/* This here needs to be refactored ofc */}\n      <div>\n        {title}{' '}\n        {version === 'major' && (\n          <span\n            title=\"- This feature is available in React 19 beta and the React canary channel\"\n            className={`text-xs px-1 ms-1 rounded bg-gray-10 dark:bg-gray-40 dark:bg-opacity-20 text-gray-40 dark:text-gray-40`}>\n            React 19\n          </span>\n        )}\n        {version === 'canary' && (\n          <IconCanary\n            title=\" - This feature is available in the latest Canary version of React\"\n            className=\"ms-1 text-gray-30 dark:text-gray-60 inline-block w-3.5 h-3.5 align-[-3px]\"\n          />\n        )}\n        {version === 'experimental' && (\n          <IconExperimental\n            title=\" - This feature is available in the latest Experimental version of React\"\n            className=\"ms-1 text-gray-30 dark:text-gray-60 inline-block w-3.5 h-3.5 align-[-3px]\"\n          />\n        )}\n        {version === 'rc' && (\n          <IconCanary\n            title=\" - This feature is available in the latest RC version\"\n            className=\"ms-1 text-gray-30 dark:text-gray-60 inline-block w-3.5 h-3.5 align-[-3px]\"\n          />\n        )}\n      </div>\n\n      {isExpanded != null && !hideArrow && (\n        <span\n          className={cn('pe-1', {\n            'text-link dark:text-link-dark': isExpanded,\n            'text-tertiary dark:text-tertiary-dark': !isExpanded,\n          })}>\n          <IconNavArrow displayDirection={isExpanded ? 'down' : 'end'} />\n        </span>\n      )}\n    </Link>\n  );\n}\n"
  },
  {
    "path": "src/components/Layout/Sidebar/SidebarRouteTree.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {useRef, useLayoutEffect, Fragment} from 'react';\n\nimport cn from 'classnames';\nimport {useRouter} from 'next/router';\nimport {SidebarLink} from './SidebarLink';\nimport {useCollapse} from 'react-collapsed';\nimport usePendingRoute from 'hooks/usePendingRoute';\nimport type {RouteItem} from 'components/Layout/getRouteMeta';\nimport {siteConfig} from 'siteConfig';\n\ninterface SidebarRouteTreeProps {\n  isForceExpanded: boolean;\n  breadcrumbs: RouteItem[];\n  routeTree: RouteItem;\n  level?: number;\n}\n\nfunction CollapseWrapper({\n  isExpanded,\n  duration,\n  children,\n}: {\n  isExpanded: boolean;\n  duration: number;\n  children: any;\n}) {\n  const ref = useRef<HTMLDivElement | null>(null);\n  const timeoutRef = useRef<number | null>(null);\n  const {getCollapseProps} = useCollapse({\n    isExpanded,\n    duration,\n  });\n\n  // Disable pointer events while animating.\n  const isExpandedRef = useRef(isExpanded);\n  if (typeof window !== 'undefined') {\n    // eslint-disable-next-line react-compiler/react-compiler\n    // eslint-disable-next-line react-hooks/rules-of-hooks\n    useLayoutEffect(() => {\n      const wasExpanded = isExpandedRef.current;\n      if (wasExpanded === isExpanded) {\n        return;\n      }\n      isExpandedRef.current = isExpanded;\n      if (ref.current !== null) {\n        const node: HTMLDivElement = ref.current;\n        node.style.pointerEvents = 'none';\n        if (timeoutRef.current !== null) {\n          window.clearTimeout(timeoutRef.current);\n        }\n        timeoutRef.current = window.setTimeout(() => {\n          node.style.pointerEvents = '';\n        }, duration + 100);\n      }\n    });\n  }\n\n  return (\n    <div\n      ref={ref}\n      className={cn(isExpanded ? 'opacity-100' : 'opacity-50')}\n      style={{\n        transition: `opacity ${duration}ms ease-in-out`,\n      }}>\n      <div {...getCollapseProps()}>{children}</div>\n    </div>\n  );\n}\n\nexport function SidebarRouteTree({\n  isForceExpanded,\n  breadcrumbs,\n  routeTree,\n  level = 0,\n}: SidebarRouteTreeProps) {\n  const slug = useRouter().asPath.split(/[\\?\\#]/)[0];\n  const pendingRoute = usePendingRoute();\n  const currentRoutes = routeTree.routes as RouteItem[];\n  return (\n    <ul>\n      {currentRoutes.map(\n        (\n          {\n            path,\n            title,\n            routes,\n            version,\n            heading,\n            hasSectionHeader,\n            sectionHeader,\n          },\n          index\n        ) => {\n          const selected = slug === path;\n          let listItem = null;\n          if (!path || heading) {\n            // if current route item has no path and children treat it as an API sidebar heading\n            listItem = (\n              <SidebarRouteTree\n                level={level + 1}\n                isForceExpanded={isForceExpanded}\n                routeTree={{title, routes}}\n                breadcrumbs={[]}\n              />\n            );\n          } else if (routes) {\n            // if route has a path and child routes, treat it as an expandable sidebar item\n            const isBreadcrumb =\n              breadcrumbs.length > 1 &&\n              breadcrumbs[breadcrumbs.length - 1].path === path;\n            const isExpanded = isForceExpanded || isBreadcrumb || selected;\n            listItem = (\n              <li key={`${title}-${path}-${level}-heading`}>\n                <SidebarLink\n                  key={`${title}-${path}-${level}-link`}\n                  href={path}\n                  isPending={pendingRoute === path}\n                  selected={selected}\n                  level={level}\n                  title={title}\n                  version={version}\n                  isExpanded={isExpanded}\n                  hideArrow={isForceExpanded}\n                />\n                <CollapseWrapper duration={250} isExpanded={isExpanded}>\n                  <SidebarRouteTree\n                    isForceExpanded={isForceExpanded}\n                    routeTree={{title, routes}}\n                    breadcrumbs={breadcrumbs}\n                    level={level + 1}\n                  />\n                </CollapseWrapper>\n              </li>\n            );\n          } else {\n            // if route has a path and no child routes, treat it as a sidebar link\n            listItem = (\n              <li key={`${title}-${path}-${level}-link`}>\n                <SidebarLink\n                  isPending={pendingRoute === path}\n                  href={path}\n                  selected={selected}\n                  level={level}\n                  title={title}\n                  version={version}\n                />\n              </li>\n            );\n          }\n          if (hasSectionHeader) {\n            let sectionHeaderText =\n              sectionHeader != null\n                ? sectionHeader.replace('{{version}}', siteConfig.version)\n                : '';\n            return (\n              <Fragment key={`${sectionHeaderText}-${level}-separator`}>\n                {index !== 0 && (\n                  <li\n                    role=\"separator\"\n                    className=\"mt-4 mb-2 ms-5 border-b border-border dark:border-border-dark\"\n                  />\n                )}\n                <h3\n                  className={cn(\n                    'mb-1 text-sm font-bold ms-5 text-tertiary dark:text-tertiary-dark',\n                    index !== 0 && 'mt-2'\n                  )}>\n                  {sectionHeaderText}\n                </h3>\n              </Fragment>\n            );\n          } else {\n            return listItem;\n          }\n        }\n      )}\n    </ul>\n  );\n}\n"
  },
  {
    "path": "src/components/Layout/Sidebar/index.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nexport {SidebarButton} from './SidebarButton';\nexport {SidebarLink} from './SidebarLink';\nexport {SidebarRouteTree} from './SidebarRouteTree';\n"
  },
  {
    "path": "src/components/Layout/SidebarNav/SidebarNav.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {Suspense} from 'react';\nimport * as React from 'react';\nimport cn from 'classnames';\nimport {SidebarRouteTree} from '../Sidebar/SidebarRouteTree';\nimport type {RouteItem} from '../getRouteMeta';\n\ndeclare global {\n  interface Window {\n    __theme: string;\n    __setPreferredTheme: (theme: string) => void;\n  }\n}\n\nexport default function SidebarNav({\n  routeTree,\n  breadcrumbs,\n}: {\n  routeTree: RouteItem;\n  breadcrumbs: RouteItem[];\n}) {\n  // HACK. Fix up the data structures instead.\n  if ((routeTree as any).routes.length === 1) {\n    routeTree = (routeTree as any).routes[0];\n  }\n\n  return (\n    <div\n      className={cn(\n        'sticky top-0 lg:bottom-0 lg:h-[calc(100vh-4rem)] flex flex-col'\n      )}>\n      <div\n        className=\"overflow-y-scroll no-bg-scrollbar lg:w-[342px] grow bg-wash dark:bg-wash-dark\"\n        style={{\n          overscrollBehavior: 'contain',\n        }}>\n        <aside\n          className={cn(\n            `lg:grow flex-col w-full pb-8 lg:pb-0 lg:max-w-custom-xs z-10 hidden lg:block`\n          )}>\n          <nav\n            role=\"navigation\"\n            style={{'--bg-opacity': '.2'} as React.CSSProperties} // Need to cast here because CSS vars aren't considered valid in TS types (cuz they could be anything)\n            className=\"w-full pt-6 scrolling-touch lg:h-auto grow pe-0 lg:pe-5 lg:pb-16 md:pt-4 lg:pt-4 scrolling-gpu\">\n            {/* No fallback UI so need to be careful not to suspend directly inside. */}\n            <Suspense fallback={null}>\n              <SidebarRouteTree\n                routeTree={routeTree}\n                breadcrumbs={breadcrumbs}\n                isForceExpanded={false}\n              />\n            </Suspense>\n            <div className=\"h-20\" />\n          </nav>\n        </aside>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/Layout/SidebarNav/index.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nexport {default as SidebarNav} from './SidebarNav';\n"
  },
  {
    "path": "src/components/Layout/Toc.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport cx from 'classnames';\nimport {useTocHighlight} from './useTocHighlight';\nimport type {Toc} from '../MDX/TocContext';\n\nexport function Toc({headings}: {headings: Toc}) {\n  const {currentIndex} = useTocHighlight();\n  // TODO: We currently have a mismatch between the headings in the document\n  // and the headings we find in MarkdownPage (i.e. we don't find Recap or Challenges).\n  // Select the max TOC item we have here for now, but remove this after the fix.\n  const selectedIndex = Math.min(currentIndex, headings.length - 1);\n  return (\n    <nav role=\"navigation\" className=\"pt-20 sticky top-0 end-0\">\n      {headings.length > 0 && (\n        <h2 className=\"mb-3 lg:mb-3 uppercase tracking-wide font-bold text-sm text-secondary dark:text-secondary-dark px-4 w-full\">\n          このページの内容\n        </h2>\n      )}\n      <div\n        className=\"h-full overflow-y-auto ps-4 max-h-[calc(100vh-7.5rem)]\"\n        style={{\n          overscrollBehavior: 'contain',\n        }}>\n        <ul className=\"space-y-2 pb-16\">\n          {headings.length > 0 &&\n            headings.map((h, i) => {\n              if (!h.url && process.env.NODE_ENV === 'development') {\n                console.error('Heading does not have URL');\n              }\n              return (\n                <li\n                  key={`heading-${h.url}-${i}`}\n                  className={cx(\n                    'text-sm px-2 rounded-s-xl',\n                    selectedIndex === i\n                      ? 'bg-highlight dark:bg-highlight-dark'\n                      : null,\n                    {\n                      'ps-4': h?.depth === 3,\n                      hidden: h.depth && h.depth > 3,\n                    }\n                  )}>\n                  <a\n                    className={cx(\n                      selectedIndex === i\n                        ? 'text-link dark:text-link-dark font-bold'\n                        : 'text-secondary dark:text-secondary-dark',\n                      'block hover:text-link dark:hover:text-link-dark leading-normal py-2'\n                    )}\n                    href={h.url}>\n                    {h.text}\n                  </a>\n                </li>\n              );\n            })}\n        </ul>\n      </div>\n    </nav>\n  );\n}\n"
  },
  {
    "path": "src/components/Layout/TopNav/BrandMenu.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport * as ContextMenu from '@radix-ui/react-context-menu';\nimport {IconCopy} from 'components/Icon/IconCopy';\nimport {IconDownload} from 'components/Icon/IconDownload';\nimport {IconNewPage} from 'components/Icon/IconNewPage';\nimport {ExternalLink} from 'components/ExternalLink';\nimport {IconClose} from '../../Icon/IconClose';\n\nfunction MenuItem({\n  children,\n  onSelect,\n}: {\n  children: React.ReactNode;\n  onSelect?: () => void;\n}) {\n  return (\n    <ContextMenu.Item\n      className=\"flex items-center hover:bg-border dark:hover:bg-border-dark ps-6 pe-4 py-2 w-full text-base cursor-pointer\"\n      onSelect={onSelect}>\n      {children}\n    </ContextMenu.Item>\n  );\n}\n\nfunction DownloadMenuItem({\n  fileName,\n  href,\n  children,\n}: {\n  fileName: string;\n  href: string;\n  children: React.ReactNode;\n}) {\n  return (\n    <a download={fileName} href={href} className=\"flex items-center w-full\">\n      <MenuItem>{children}</MenuItem>\n    </a>\n  );\n}\n\nexport default function BrandMenu({children}: {children: React.ReactNode}) {\n  return (\n    <ContextMenu.Root>\n      <ContextMenu.Trigger className=\"flex items-center\">\n        {children}\n      </ContextMenu.Trigger>\n      <ContextMenu.Portal>\n        <ContextMenu.Content\n          className=\"hidden lg:block z-50 mt-6 bg-wash border border-border dark:border-border-dark dark:bg-wash-dark rounded min-w-56 overflow-hidden shadow\"\n          // @ts-ignore\n          sideOffset={0}\n          align=\"end\">\n          <ContextMenu.Label className=\"ps-4 pt-2 text-base text-tertiary dark:text-tertiary-dark\">\n            Dark Mode\n          </ContextMenu.Label>\n          <DownloadMenuItem\n            fileName=\"react_logo_dark.svg\"\n            href=\"/images/brand/logo_dark.svg\">\n            <span className=\"w-8\">\n              <IconDownload />\n            </span>\n            <span>Logo SVG</span>\n          </DownloadMenuItem>\n          <DownloadMenuItem\n            fileName=\"react_wordmark_dark.svg\"\n            href=\"/images/brand/wordmark_dark.svg\">\n            <span className=\"w-8\">\n              <IconDownload />\n            </span>\n            <span>Wordmark SVG</span>\n          </DownloadMenuItem>\n          <MenuItem\n            onSelect={async () => {\n              await navigator.clipboard.writeText('#58C4DC');\n            }}>\n            <span className=\"w-8\">\n              <IconCopy />\n            </span>\n            <span>Copy dark mode color</span>\n          </MenuItem>\n          <ContextMenu.Label className=\"ps-4 text-base text-tertiary dark:text-tertiary-dark\">\n            Light Mode\n          </ContextMenu.Label>\n          <DownloadMenuItem\n            fileName=\"react_logo_light.svg\"\n            href=\"/images/brand/logo_light.svg\">\n            <span className=\"w-8\">\n              <IconDownload />\n            </span>\n            <span>Logo SVG</span>\n          </DownloadMenuItem>\n          <DownloadMenuItem\n            fileName=\"react_wordmark_light.svg\"\n            href=\"/images/brand/wordmark_light.svg\">\n            <span className=\"w-8\">\n              <IconDownload />\n            </span>\n            <span>Wordmark SVG</span>\n          </DownloadMenuItem>\n          <MenuItem\n            onSelect={async () => {\n              await navigator.clipboard.writeText('#087EA4');\n            }}>\n            <span className=\"w-8\">\n              <IconCopy />\n            </span>\n            <span>Copy light mode color</span>\n          </MenuItem>\n          <div className=\"uwu-visible flex flex-col\">\n            <ContextMenu.Separator className=\"\" />\n            <ContextMenu.Label className=\"ps-4 text-base text-tertiary dark:text-tertiary-dark\">\n              uwu\n            </ContextMenu.Label>\n            <MenuItem\n              onSelect={() => {\n                // @ts-ignore\n                window.__setUwu(false);\n              }}>\n              <span className=\"w-8\">\n                <IconClose />\n              </span>\n              <span>Turn off</span>\n            </MenuItem>\n            <DownloadMenuItem fileName=\"react_uwu_png\" href=\"/images/uwu.png\">\n              <span className=\"w-8\">\n                <IconDownload />\n              </span>\n              <span>Logo PNG</span>\n            </DownloadMenuItem>\n\n            <ExternalLink\n              className=\"flex items-center\"\n              href=\"https://github.com/SAWARATSUKI/KawaiiLogos\">\n              <MenuItem>\n                <span className=\"w-8\">\n                  <IconNewPage />\n                </span>\n                <span>Logo by @sawaratsuki1004</span>\n              </MenuItem>\n            </ExternalLink>\n          </div>\n        </ContextMenu.Content>\n      </ContextMenu.Portal>\n    </ContextMenu.Root>\n  );\n}\n"
  },
  {
    "path": "src/components/Layout/TopNav/TopNav.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {\n  useState,\n  useRef,\n  useCallback,\n  useEffect,\n  startTransition,\n  Suspense,\n} from 'react';\nimport Image from 'next/image';\nimport * as React from 'react';\nimport cn from 'classnames';\nimport NextLink from 'next/link';\nimport {useRouter} from 'next/router';\nimport {disableBodyScroll, enableBodyScroll} from 'body-scroll-lock';\n\nimport {IconClose} from 'components/Icon/IconClose';\nimport {IconHamburger} from 'components/Icon/IconHamburger';\nimport {IconSearch} from 'components/Icon/IconSearch';\nimport {Search} from 'components/Search';\nimport {Logo} from '../../Logo';\nimport {SidebarRouteTree} from '../Sidebar';\nimport type {RouteItem} from '../getRouteMeta';\nimport {siteConfig} from 'siteConfig';\nimport BrandMenu from './BrandMenu';\n\ndeclare global {\n  interface Window {\n    __theme: string;\n    __setPreferredTheme: (theme: string) => void;\n  }\n}\n\nconst darkIcon = (\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    width=\"28\"\n    height=\"28\"\n    viewBox=\"0 0 32 32\">\n    <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-440 -200)\">\n      <path\n        fill=\"currentColor\"\n        fillRule=\"nonzero\"\n        stroke=\"currentColor\"\n        strokeWidth={0.5}\n        d=\"M102,21 C102,18.1017141 103.307179,15.4198295 105.51735,13.6246624 C106.001939,13.2310647 105.821611,12.4522936 105.21334,12.3117518 C104.322006,12.1058078 103.414758,12 102.5,12 C95.8722864,12 90.5,17.3722864 90.5,24 C90.5,30.6277136 95.8722864,36 102.5,36 C106.090868,36 109.423902,34.4109093 111.690274,31.7128995 C112.091837,31.2348572 111.767653,30.5041211 111.143759,30.4810139 C106.047479,30.2922628 102,26.1097349 102,21 Z M102.5,34.5 C96.7007136,34.5 92,29.7992864 92,24 C92,18.2007136 96.7007136,13.5 102.5,13.5 C102.807386,13.5 103.113925,13.5136793 103.419249,13.5407785 C101.566047,15.5446378 100.5,18.185162 100.5,21 C100.5,26.3198526 104.287549,30.7714322 109.339814,31.7756638 L109.516565,31.8092927 C107.615276,33.5209452 105.138081,34.5 102.5,34.5 Z\"\n        transform=\"translate(354.5 192)\"\n      />\n      <polygon points=\"444 228 468 228 468 204 444 204\" />\n    </g>\n  </svg>\n);\n\nconst lightIcon = (\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    width=\"32\"\n    height=\"32\"\n    viewBox=\"0 0 32 32\">\n    <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-442 -200)\">\n      <g fill=\"currentColor\" transform=\"translate(356 144)\">\n        <path\n          fillRule=\"nonzero\"\n          d=\"M108.5 24C108.5 27.5902136 105.590214 30.5 102 30.5 98.4097864 30.5 95.5 27.5902136 95.5 24 95.5 20.4097864 98.4097864 17.5 102 17.5 105.590214 17.5 108.5 20.4097864 108.5 24zM107 24C107 21.2382136 104.761786 19 102 19 99.2382136 19 97 21.2382136 97 24 97 26.7617864 99.2382136 29 102 29 104.761786 29 107 26.7617864 107 24zM101 12.75L101 14.75C101 15.1642136 101.335786 15.5 101.75 15.5 102.164214 15.5 102.5 15.1642136 102.5 14.75L102.5 12.75C102.5 12.3357864 102.164214 12 101.75 12 101.335786 12 101 12.3357864 101 12.75zM95.7255165 14.6323616L96.7485165 16.4038616C96.9556573 16.7625614 97.4143618 16.8854243 97.7730616 16.6782835 98.1317614 16.4711427 98.2546243 16.0124382 98.0474835 15.6537384L97.0244835 13.8822384C96.8173427 13.5235386 96.3586382 13.4006757 95.9999384 13.6078165 95.6412386 13.8149573 95.5183757 14.2736618 95.7255165 14.6323616zM91.8822384 19.0244835L93.6537384 20.0474835C94.0124382 20.2546243 94.4711427 20.1317614 94.6782835 19.7730616 94.8854243 19.4143618 94.7625614 18.9556573 94.4038616 18.7485165L92.6323616 17.7255165C92.2736618 17.5183757 91.8149573 17.6412386 91.6078165 17.9999384 91.4006757 18.3586382 91.5235386 18.8173427 91.8822384 19.0244835zM90.75 25L92.75 25C93.1642136 25 93.5 24.6642136 93.5 24.25 93.5 23.8357864 93.1642136 23.5 92.75 23.5L90.75 23.5C90.3357864 23.5 90 23.8357864 90 24.25 90 24.6642136 90.3357864 25 90.75 25zM92.6323616 30.2744835L94.4038616 29.2514835C94.7625614 29.0443427 94.8854243 28.5856382 94.6782835 28.2269384 94.4711427 27.8682386 94.0124382 27.7453757 93.6537384 27.9525165L91.8822384 28.9755165C91.5235386 29.1826573 91.4006757 29.6413618 91.6078165 30.0000616 91.8149573 30.3587614 92.2736618 30.4816243 92.6323616 30.2744835zM97.0244835 34.1177616L98.0474835 32.3462616C98.2546243 31.9875618 98.1317614 31.5288573 97.7730616 31.3217165 97.4143618 31.1145757 96.9556573 31.2374386 96.7485165 31.5961384L95.7255165 33.3676384C95.5183757 33.7263382 95.6412386 34.1850427 95.9999384 34.3921835 96.3586382 34.5993243 96.8173427 34.4764614 97.0244835 34.1177616zM103 35.25L103 33.25C103 32.8357864 102.664214 32.5 102.25 32.5 101.835786 32.5 101.5 32.8357864 101.5 33.25L101.5 35.25C101.5 35.6642136 101.835786 36 102.25 36 102.664214 36 103 35.6642136 103 35.25zM108.274483 33.3676384L107.251483 31.5961384C107.044343 31.2374386 106.585638 31.1145757 106.226938 31.3217165 105.868239 31.5288573 105.745376 31.9875618 105.952517 32.3462616L106.975517 34.1177616C107.182657 34.4764614 107.641362 34.5993243 108.000062 34.3921835 108.358761 34.1850427 108.481624 33.7263382 108.274483 33.3676384zM112.117762 28.9755165L110.346262 27.9525165C109.987562 27.7453757 109.528857 27.8682386 109.321717 28.2269384 109.114576 28.5856382 109.237439 29.0443427 109.596138 29.2514835L111.367638 30.2744835C111.726338 30.4816243 112.185043 30.3587614 112.392183 30.0000616 112.599324 29.6413618 112.476461 29.1826573 112.117762 28.9755165zM113.25 23L111.25 23C110.835786 23 110.5 23.3357864 110.5 23.75 110.5 24.1642136 110.835786 24.5 111.25 24.5L113.25 24.5C113.664214 24.5 114 24.1642136 114 23.75 114 23.3357864 113.664214 23 113.25 23zM111.367638 17.7255165L109.596138 18.7485165C109.237439 18.9556573 109.114576 19.4143618 109.321717 19.7730616 109.528857 20.1317614 109.987562 20.2546243 110.346262 20.0474835L112.117762 19.0244835C112.476461 18.8173427 112.599324 18.3586382 112.392183 17.9999384 112.185043 17.6412386 111.726338 17.5183757 111.367638 17.7255165zM106.975517 13.8822384L105.952517 15.6537384C105.745376 16.0124382 105.868239 16.4711427 106.226938 16.6782835 106.585638 16.8854243 107.044343 16.7625614 107.251483 16.4038616L108.274483 14.6323616C108.481624 14.2736618 108.358761 13.8149573 108.000062 13.6078165 107.641362 13.4006757 107.182657 13.5235386 106.975517 13.8822384z\"\n          transform=\"translate(0 48)\"\n          stroke=\"currentColor\"\n          strokeWidth={0.25}\n        />\n        <path\n          d=\"M98.6123,60.1372 C98.6123,59.3552 98.8753,58.6427 99.3368,58.0942 C99.5293,57.8657 99.3933,57.5092 99.0943,57.5017 C99.0793,57.5012 99.0633,57.5007 99.0483,57.5007 C97.1578,57.4747 95.5418,59.0312 95.5008,60.9217 C95.4578,62.8907 97.0408,64.5002 98.9998,64.5002 C99.7793,64.5002 100.4983,64.2452 101.0798,63.8142 C101.3183,63.6372 101.2358,63.2627 100.9478,63.1897 C99.5923,62.8457 98.6123,61.6072 98.6123,60.1372\"\n          transform=\"translate(3 11)\"\n        />\n      </g>\n      <polygon points=\"444 228 468 228 468 204 444 204\" />\n    </g>\n  </svg>\n);\n\nconst languageIcon = (\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    width=\"24\"\n    height=\"24\"\n    viewBox=\"0 0 24 24\">\n    <path\n      fill=\"currentColor\"\n      d=\" M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z \"\n    />\n  </svg>\n);\n\nconst githubIcon = (\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    width=\"20\"\n    height=\"20\"\n    viewBox=\"0 0 24 24\">\n    <g fill=\"currentColor\">\n      <path d=\"M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z\" />\n    </g>\n  </svg>\n);\n\nfunction Link({\n  href,\n  children,\n  ...props\n}: React.AnchorHTMLAttributes<HTMLAnchorElement>) {\n  return (\n    <NextLink\n      href={`${href}`}\n      className=\"inline leading-normal transition duration-100 ease-in border-b border-opacity-0 text-primary dark:text-primary-dark hover:text-link hover:dark:text-link-dark border-link hover:border-opacity-100\"\n      {...props}>\n      {children}\n    </NextLink>\n  );\n}\n\nfunction NavItem({url, isActive, children}: any) {\n  return (\n    <div className=\"flex flex-auto sm:flex-1\">\n      <Link\n        href={url}\n        className={cn(\n          'active:scale-95 transition-transform w-full text-center outline-link py-1.5 px-1.5 xs:px-3 sm:px-4 rounded-full capitalize whitespace-nowrap',\n          !isActive && 'hover:bg-primary/5 hover:dark:bg-primary-dark/5',\n          isActive &&\n            'bg-highlight dark:bg-highlight-dark text-link dark:text-link-dark'\n        )}>\n        {children}\n      </Link>\n    </div>\n  );\n}\n\nfunction Kbd(props: {children?: React.ReactNode; wide?: boolean}) {\n  const {wide, ...rest} = props;\n  const width = wide ? 'w-10' : 'w-5';\n\n  return (\n    <kbd\n      className={`${width} h-5 border border-transparent me-1 bg-wash dark:bg-wash-dark text-gray-30 align-middle p-0 inline-flex justify-center items-center text-xs text-center rounded-md`}\n      {...rest}\n    />\n  );\n}\n\nexport default function TopNav({\n  routeTree,\n  breadcrumbs,\n  section,\n}: {\n  routeTree: RouteItem;\n  breadcrumbs: RouteItem[];\n  section: 'learn' | 'reference' | 'community' | 'blog' | 'home' | 'unknown';\n}) {\n  const [isMenuOpen, setIsMenuOpen] = useState(false);\n  const [showSearch, setShowSearch] = useState(false);\n  const [isScrolled, setIsScrolled] = useState(false);\n  const scrollParentRef = useRef<HTMLDivElement>(null);\n  const {asPath} = useRouter();\n\n  // HACK. Fix up the data structures instead.\n  if ((routeTree as any).routes.length === 1) {\n    routeTree = (routeTree as any).routes[0];\n  }\n\n  // While the overlay is open, disable body scroll.\n  useEffect(() => {\n    if (isMenuOpen) {\n      const preferredScrollParent = scrollParentRef.current!;\n      disableBodyScroll(preferredScrollParent);\n      return () => enableBodyScroll(preferredScrollParent);\n    } else {\n      return undefined;\n    }\n  }, [isMenuOpen]);\n\n  // Close the overlay on any navigation.\n  useEffect(() => {\n    setIsMenuOpen(false);\n  }, [asPath]);\n\n  // Also close the overlay if the window gets resized past mobile layout.\n  // (This is also important because we don't want to keep the body locked!)\n  useEffect(() => {\n    const media = window.matchMedia(`(max-width: 1023px)`);\n\n    function closeIfNeeded() {\n      if (!media.matches) {\n        setIsMenuOpen(false);\n      }\n    }\n\n    closeIfNeeded();\n    media.addEventListener('change', closeIfNeeded);\n    return () => {\n      media.removeEventListener('change', closeIfNeeded);\n    };\n  }, []);\n\n  const scrollDetectorRef = useRef(null);\n  useEffect(() => {\n    const observer = new IntersectionObserver(\n      (entries) => {\n        entries.forEach((entry) => {\n          setIsScrolled(!entry.isIntersecting);\n        });\n      },\n      {\n        root: null,\n        rootMargin: `0px 0px`,\n        threshold: 0,\n      }\n    );\n    observer.observe(scrollDetectorRef.current!);\n    return () => observer.disconnect();\n  }, []);\n\n  const onOpenSearch = useCallback(() => {\n    startTransition(() => {\n      setShowSearch(true);\n    });\n  }, []);\n  const onCloseSearch = useCallback(() => {\n    setShowSearch(false);\n  }, []);\n\n  return (\n    <>\n      <Search\n        isOpen={showSearch}\n        onOpen={onOpenSearch}\n        onClose={onCloseSearch}\n      />\n      <div ref={scrollDetectorRef} />\n      <div\n        className={cn(\n          isMenuOpen\n            ? 'h-screen sticky top-0 lg:bottom-0 lg:h-screen flex flex-col shadow-nav dark:shadow-nav-dark z-20'\n            : 'z-40 sticky top-0'\n        )}>\n        <nav\n          className={cn(\n            'duration-300 backdrop-filter backdrop-blur-lg backdrop-saturate-200 transition-shadow bg-opacity-90 items-center w-full flex justify-between bg-wash dark:bg-wash-dark dark:bg-opacity-95 px-1.5 lg:pe-5 lg:ps-4 z-40',\n            {'dark:shadow-nav-dark shadow-nav': isScrolled || isMenuOpen}\n          )}>\n          <div className=\"flex items-center justify-between w-full h-16 gap-0 sm:gap-3\">\n            <div className=\"flex flex-row 3xl:flex-1 items-centers\">\n              <button\n                type=\"button\"\n                aria-label=\"Menu\"\n                onClick={() => setIsMenuOpen(!isMenuOpen)}\n                className={cn(\n                  'active:scale-95 transition-transform flex lg:hidden w-12 h-12 rounded-full items-center justify-center hover:bg-primary/5 hover:dark:bg-primary-dark/5 outline-link',\n                  {\n                    'text-link dark:text-link-dark': isMenuOpen,\n                  }\n                )}>\n                {isMenuOpen ? <IconClose /> : <IconHamburger />}\n              </button>\n              <BrandMenu>\n                <div className=\"flex items-center\">\n                  <div className=\"uwu-visible flex items-center justify-center h-full\">\n                    <NextLink\n                      href=\"/\"\n                      className=\"active:scale-95 transition-transform\">\n                      <Image\n                        alt=\"logo by @sawaratsuki1004\"\n                        title=\"logo by @sawaratsuki1004\"\n                        className=\"h-8\"\n                        priority\n                        width={63}\n                        height={32}\n                        src=\"/images/uwu.png\"\n                      />\n                    </NextLink>\n                  </div>\n                  <div className=\"uwu-hidden\">\n                    <NextLink\n                      href=\"/\"\n                      className={`active:scale-95 overflow-hidden transition-transform relative items-center text-primary dark:text-primary-dark p-1 whitespace-nowrap outline-link rounded-full 3xl:rounded-xl inline-flex text-lg font-normal gap-2`}>\n                      <Logo\n                        className={cn(\n                          'text-sm me-0 w-10 h-10 text-brand dark:text-brand-dark flex origin-center transition-all ease-in-out'\n                        )}\n                      />\n                      <span className=\"sr-only 3xl:not-sr-only\">React</span>\n                    </NextLink>\n                  </div>\n                </div>\n              </BrandMenu>\n              <div className=\"flex flex-column justify-center items-center\">\n                <NextLink\n                  href=\"/versions\"\n                  className=\" flex py-2 flex-column justify-center items-center text-gray-50 dark:text-gray-30 hover:text-link hover:dark:text-link-dark hover:underline text-sm ms-1 cursor-pointer\">\n                  v{siteConfig.version}\n                </NextLink>\n              </div>\n            </div>\n            <div className=\"items-center justify-center flex-1 hidden w-full md:flex 3xl:w-auto 3xl:shrink-0 3xl:justify-center\">\n              <button\n                type=\"button\"\n                className={cn(\n                  'flex 3xl:w-[56rem] 3xl:mx-0 relative ps-4 pe-1 py-1 h-10 bg-gray-30/20 dark:bg-gray-40/20 outline-none focus:outline-link betterhover:hover:bg-opacity-80 pointer items-center text-start w-full text-gray-30 rounded-full align-middle text-base'\n                )}\n                onClick={onOpenSearch}>\n                <IconSearch className=\"align-middle me-3 text-gray-30 shrink-0 group-betterhover:hover:text-gray-70\" />\n                Search\n                <span className=\"hidden ms-auto sm:flex item-center me-1\">\n                  <Kbd data-platform=\"mac\">⌘</Kbd>\n                  <Kbd data-platform=\"win\" wide>\n                    Ctrl\n                  </Kbd>\n                  <Kbd>K</Kbd>\n                </span>\n              </button>\n            </div>\n            <div className=\"text-base justify-center items-center gap-1.5 flex 3xl:flex-1 flex-row 3xl:justify-end\">\n              <div className=\"mx-2.5 gap-1.5 hidden lg:flex\">\n                <NavItem isActive={section === 'learn'} url=\"/learn\">\n                  Learn\n                </NavItem>\n                <NavItem\n                  isActive={section === 'reference'}\n                  url=\"/reference/react\">\n                  Reference\n                </NavItem>\n                <NavItem isActive={section === 'community'} url=\"/community\">\n                  Community\n                </NavItem>\n                <NavItem isActive={section === 'blog'} url=\"/blog\">\n                  Blog\n                </NavItem>\n              </div>\n              <div className=\"flex w-full md:hidden\"></div>\n              <div className=\"flex items-center -space-x-2.5 xs:space-x-0 \">\n                <div className=\"flex md:hidden\">\n                  <button\n                    aria-label=\"Search\"\n                    type=\"button\"\n                    className=\"flex items-center justify-center w-12 h-12 transition-transform rounded-full active:scale-95 md:hidden hover:bg-secondary-button hover:dark:bg-secondary-button-dark outline-link\"\n                    onClick={onOpenSearch}>\n                    <IconSearch className=\"w-5 h-5 align-middle\" />\n                  </button>\n                </div>\n                <div className=\"flex dark:hidden\">\n                  <button\n                    type=\"button\"\n                    aria-label=\"Use Dark Mode\"\n                    onClick={() => {\n                      window.__setPreferredTheme('dark');\n                    }}\n                    className=\"flex items-center justify-center w-12 h-12 transition-transform rounded-full active:scale-95 hover:bg-primary/5 hover:dark:bg-primary-dark/5 outline-link\">\n                    {darkIcon}\n                  </button>\n                </div>\n                <div className=\"hidden dark:flex\">\n                  <button\n                    type=\"button\"\n                    aria-label=\"Use Light Mode\"\n                    onClick={() => {\n                      window.__setPreferredTheme('light');\n                    }}\n                    className=\"flex items-center justify-center w-12 h-12 transition-transform rounded-full active:scale-95 hover:bg-primary/5 hover:dark:bg-primary-dark/5 outline-link\">\n                    {lightIcon}\n                  </button>\n                </div>\n                <div className=\"flex\">\n                  <Link\n                    href=\"/community/translations\"\n                    aria-label=\"Translations\"\n                    className=\"active:scale-95 transition-transform flex w-12 h-12 rounded-full items-center justify-center hover:bg-primary/5 hover:dark:bg-primary-dark/5 outline-link\">\n                    {languageIcon}\n                  </Link>\n                </div>\n                <div className=\"flex\">\n                  <Link\n                    href=\"https://github.com/facebook/react/releases\"\n                    target=\"_blank\"\n                    rel=\"noreferrer noopener\"\n                    aria-label=\"Open on GitHub\"\n                    className=\"flex items-center justify-center w-12 h-12 transition-transform rounded-full active:scale-95 hover:bg-primary/5 hover:dark:bg-primary-dark/5 outline-link\">\n                    {githubIcon}\n                  </Link>\n                </div>\n              </div>\n            </div>\n          </div>\n        </nav>\n\n        {isMenuOpen && (\n          <div\n            ref={scrollParentRef}\n            className=\"overflow-y-scroll isolate no-bg-scrollbar lg:w-[342px] grow bg-wash dark:bg-wash-dark\">\n            <aside\n              className={cn(\n                `lg:grow lg:flex flex-col w-full pb-8 lg:pb-0 lg:max-w-custom-xs z-40`,\n                isMenuOpen ? 'block z-30' : 'hidden lg:block'\n              )}>\n              <nav\n                role=\"navigation\"\n                style={{'--bg-opacity': '.2'} as React.CSSProperties} // Need to cast here because CSS vars aren't considered valid in TS types (cuz they could be anything)\n                className=\"w-full pt-4 scrolling-touch lg:h-auto grow pe-0 lg:pe-5 lg:py-6 md:pt-4 lg:pt-4 scrolling-gpu\">\n                {/* No fallback UI so need to be careful not to suspend directly inside. */}\n                <Suspense fallback={null}>\n                  <div className=\"ps-3 xs:ps-5 xs:gap-0.5 xs:text-base overflow-x-auto flex flex-row lg:hidden text-base font-bold text-secondary dark:text-secondary-dark\">\n                    <NavItem isActive={section === 'learn'} url=\"/learn\">\n                      Learn\n                    </NavItem>\n                    <NavItem\n                      isActive={section === 'reference'}\n                      url=\"/reference/react\">\n                      Reference\n                    </NavItem>\n                    <NavItem\n                      isActive={section === 'community'}\n                      url=\"/community\">\n                      Community\n                    </NavItem>\n                    <NavItem isActive={section === 'blog'} url=\"/blog\">\n                      Blog\n                    </NavItem>\n                  </div>\n                  <div\n                    role=\"separator\"\n                    className=\"mt-4 mb-2 border-b ms-5 border-border dark:border-border-dark\"\n                  />\n                  <SidebarRouteTree\n                    // Don't share state between the desktop and mobile versions.\n                    // This avoids unnecessary animations and visual flicker.\n                    key={isMenuOpen ? 'mobile-overlay' : 'desktop-or-hidden'}\n                    routeTree={routeTree}\n                    breadcrumbs={breadcrumbs}\n                    isForceExpanded={isMenuOpen}\n                  />\n                </Suspense>\n                <div className=\"h-16\" />\n              </nav>\n            </aside>\n          </div>\n        )}\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/components/Layout/TopNav/index.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nexport {default as TopNav} from './TopNav';\n"
  },
  {
    "path": "src/components/Layout/getRouteMeta.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\n/**\n * While Next.js provides file-based routing, we still need to construct\n * a sidebar for navigation and provide each markdown page\n * previous/next links and titles. To do this, we construct a nested\n * route object that is infinitely nestable.\n */\n\nexport type RouteTag =\n  | 'foundation'\n  | 'intermediate'\n  | 'advanced'\n  | 'experimental'\n  | 'deprecated';\n\nexport interface RouteItem {\n  /** Page title (for the sidebar) */\n  title: string;\n  /** Optional version flag for heading */\n  version?: 'canary' | 'major';\n  /** Optional page description for heading */\n  description?: string;\n  /* Additional meta info for page tagging */\n  tags?: RouteTag[];\n  /** Path to page */\n  path?: string;\n  /** Whether the entry is a heading */\n  heading?: boolean;\n  /** List of sub-routes */\n  routes?: RouteItem[];\n  /** Adds a section header above the route item */\n  hasSectionHeader?: boolean;\n  /** Title of section header */\n  sectionHeader?: string;\n  /** Whether it should be omitted in breadcrumbs */\n  skipBreadcrumb?: boolean;\n}\n\nexport interface Routes {\n  /** List of routes */\n  routes: RouteItem[];\n}\n\n/** Routing metadata about a given route and it's siblings and parent */\nexport interface RouteMeta {\n  /** The previous route */\n  prevRoute?: RouteItem;\n  /** The next route */\n  nextRoute?: RouteItem;\n  /** The current route */\n  route?: RouteItem;\n  /** Trail of parent routes */\n  breadcrumbs?: RouteItem[];\n  /** Order in the section */\n  order?: number;\n}\n\ntype TraversalContext = RouteMeta & {\n  currentIndex: number;\n};\n\nexport function getRouteMeta(cleanedPath: string, routeTree: RouteItem) {\n  const breadcrumbs = getBreadcrumbs(cleanedPath, routeTree);\n  const ctx: TraversalContext = {\n    currentIndex: 0,\n  };\n  buildRouteMeta(cleanedPath, routeTree, ctx);\n  const {currentIndex: _, ...meta} = ctx;\n  return {\n    ...meta,\n    breadcrumbs: breadcrumbs.length > 0 ? breadcrumbs : [routeTree],\n  };\n}\n\n// Performs a depth-first search to find the current route and its previous/next route\nfunction buildRouteMeta(\n  searchPath: string,\n  currentRoute: RouteItem,\n  ctx: TraversalContext\n) {\n  ctx.currentIndex++;\n\n  const {routes} = currentRoute;\n\n  if (ctx.route && !ctx.nextRoute) {\n    ctx.nextRoute = currentRoute;\n  }\n\n  if (currentRoute.path === searchPath) {\n    ctx.route = currentRoute;\n    ctx.order = ctx.currentIndex;\n    // If we've found a deeper match, reset the previously stored next route.\n    // TODO: this only works reliably if deeper matches are first in the tree.\n    // We should revamp all of this to be more explicit.\n    ctx.nextRoute = undefined;\n  }\n\n  if (!ctx.route) {\n    ctx.prevRoute = currentRoute;\n  }\n\n  if (!routes) {\n    return;\n  }\n\n  for (const route of routes) {\n    buildRouteMeta(searchPath, route, ctx);\n  }\n}\n\n// iterates the route tree from the current route to find its ancestors for breadcrumbs\nfunction getBreadcrumbs(\n  path: string,\n  currentRoute: RouteItem,\n  breadcrumbs: RouteItem[] = []\n): RouteItem[] {\n  if (currentRoute.path === path) {\n    return breadcrumbs;\n  }\n\n  if (!currentRoute.routes) {\n    return [];\n  }\n\n  for (const route of currentRoute.routes) {\n    const childRoute = getBreadcrumbs(path, route, [\n      ...breadcrumbs,\n      currentRoute,\n    ]);\n    if (childRoute?.length) {\n      return childRoute;\n    }\n  }\n\n  return [];\n}\n"
  },
  {
    "path": "src/components/Layout/useTocHighlight.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {useState, useRef, useEffect} from 'react';\n\nconst TOP_OFFSET = 85;\n\nexport function getHeaderAnchors(): HTMLAnchorElement[] {\n  return Array.prototype.filter.call(\n    document.getElementsByClassName('mdx-header-anchor'),\n    function (testElement) {\n      return (\n        testElement.parentNode.nodeName === 'H1' ||\n        testElement.parentNode.nodeName === 'H2' ||\n        testElement.parentNode.nodeName === 'H3'\n      );\n    }\n  );\n}\n\n/**\n * Sets up Table of Contents highlighting.\n */\nexport function useTocHighlight() {\n  const [currentIndex, setCurrentIndex] = useState<number>(0);\n  const timeoutRef = useRef<number | null>(null);\n\n  useEffect(() => {\n    function updateActiveLink() {\n      const pageHeight = document.body.scrollHeight;\n      const scrollPosition = window.scrollY + window.innerHeight;\n      const headersAnchors = getHeaderAnchors();\n\n      if (scrollPosition >= 0 && pageHeight - scrollPosition <= 0) {\n        // Scrolled to bottom of page.\n        setCurrentIndex(headersAnchors.length - 1);\n        return;\n      }\n\n      let index = -1;\n      while (index < headersAnchors.length - 1) {\n        const headerAnchor = headersAnchors[index + 1];\n        const {top} = headerAnchor.getBoundingClientRect();\n\n        if (top >= TOP_OFFSET) {\n          break;\n        }\n        index += 1;\n      }\n\n      setCurrentIndex(Math.max(index, 0));\n    }\n\n    function throttledUpdateActiveLink() {\n      if (timeoutRef.current === null) {\n        timeoutRef.current = window.setTimeout(() => {\n          timeoutRef.current = null;\n          updateActiveLink();\n        }, 100);\n      }\n    }\n\n    document.addEventListener('scroll', throttledUpdateActiveLink);\n    document.addEventListener('resize', throttledUpdateActiveLink);\n\n    updateActiveLink();\n\n    return () => {\n      if (timeoutRef.current != null) {\n        clearTimeout(timeoutRef.current);\n        timeoutRef.current = null;\n      }\n      document.removeEventListener('scroll', throttledUpdateActiveLink);\n      document.removeEventListener('resize', throttledUpdateActiveLink);\n    };\n  }, []);\n\n  return {\n    currentIndex,\n  };\n}\n"
  },
  {
    "path": "src/components/Logo.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\nimport type {SVGProps} from 'react';\n\nexport function Logo(props: SVGProps<SVGSVGElement>) {\n  return (\n    <svg\n      width=\"100%\"\n      height=\"100%\"\n      viewBox=\"-10.5 -9.45 21 18.9\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      {...props}>\n      <circle cx=\"0\" cy=\"0\" r=\"2\" fill=\"currentColor\" />\n      <g stroke=\"currentColor\" strokeWidth=\"1\" fill=\"none\">\n        <ellipse rx=\"10\" ry=\"4.5\" />\n        <ellipse rx=\"10\" ry=\"4.5\" transform=\"rotate(60)\" />\n        <ellipse rx=\"10\" ry=\"4.5\" transform=\"rotate(120)\" />\n      </g>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/BlogCard.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nimport Link from 'next/link';\n\nexport interface BlogCardProps {\n  title?: string;\n  badge?: boolean;\n  icon?: string;\n  date?: string;\n  url?: string;\n  children?: React.ReactNode;\n}\n\nfunction BlogCard({title, badge, date, icon, url, children}: BlogCardProps) {\n  return (\n    <Link\n      href={url as string}\n      passHref\n      className=\"block h-full w-full rounded-2xl outline-none focus:outline-none focus-visible:outline focus-visible:outline-link focus:outline-offset-2 focus-visible:dark:focus:outline-link-dark\">\n      <div className=\"justify-between p-5 sm:p-5 cursor-pointer w-full h-full flex flex-col flex-1 shadow-secondary-button-stroke dark:shadow-secondary-button-stroke-dark hover:bg-gray-40/5 active:bg-gray-40/10  hover:dark:bg-gray-60/5 active:dark:bg-gray-60/10 rounded-2xl text-xl text-primary dark:text-primary-dark leading-relaxed\">\n        <div className=\"flex flex-row gap-3 w-full\">\n          <h2 className=\"font-semibold flex-1 text-2xl lg:text-3xl hover:underline leading-snug mb-4\">\n            {title}\n          </h2>\n        </div>\n        <div>\n          <div className=\"flex flex-row justify-start gap-2 items-center text-base text-tertiary dark:text-tertiary-dark\">\n            {icon === 'labs' && (\n              <svg\n                className=\"w-6 h-6 text-tertiary dark:text-tertiary-dark\"\n                viewBox=\"0 0 72 72\"\n                fill=\"none\"\n                xmlns=\"http://www.w3.org/2000/svg\">\n                <path\n                  fillRule=\"evenodd\"\n                  clipRule=\"evenodd\"\n                  d=\"M27.4865 9C25.8297 9 24.4865 10.3431 24.4865 12C24.4865 13.6569 25.8297 15 27.4865 15V31.1087C27.4865 32.3397 27.1078 33.5409 26.4019 34.5494L13.095 53.5592C10.3114 57.5359 13.1563 63 18.0104 63H54.9626C59.8167 63 62.6616 57.5359 59.878 53.5592L46.5711 34.5494C45.8652 33.5409 45.4865 32.3397 45.4865 31.1087V15C47.1434 15 48.4865 13.6569 48.4865 12C48.4865 10.3431 47.1434 9 45.4865 9H27.4865ZM39.4865 31.1087V15H33.4865V31.1087C33.4865 33.5707 32.7292 35.9732 31.3173 37.9902L28.5104 42H44.4626L41.6557 37.9902C40.2438 35.9732 39.4865 33.5707 39.4865 31.1087ZM18.0104 57L24.3104 48H48.6626L54.9626 57H18.0104Z\"\n                  fill=\"currentColor\"\n                />\n              </svg>\n            )}\n            {icon === 'blog' && (\n              <svg\n                className=\"w-6 h-6 text-tertiary dark:text-tertiary-dark\"\n                viewBox=\"0 0 72 72\"\n                fill=\"none\"\n                xmlns=\"http://www.w3.org/2000/svg\">\n                <path\n                  fillRule=\"evenodd\"\n                  clipRule=\"evenodd\"\n                  d=\"M12.7101 56.3758C13.0724 56.7251 13.6324 57 14.3887 57H57.6113C58.3676 57 58.9276 56.7251 59.2899 56.3758C59.6438 56.0346 59.8987 55.5407 59.9086 54.864C59.9354 53.022 59.9591 50.7633 59.9756 48H12.0244C12.0409 50.7633 12.0645 53.022 12.0914 54.864C12.1013 55.5407 12.3562 56.0346 12.7101 56.3758ZM12.0024 42H59.9976C59.9992 41.0437 60 40.0444 60 39C60 29.5762 59.9327 22.5857 59.8589 17.7547C59.8359 16.2516 58.6168 15 56.9938 15L15.0062 15C13.3832 15 12.1641 16.2516 12.1411 17.7547C12.0673 22.5857 12 29.5762 12 39C12 40.0444 12.0008 41.0437 12.0024 42ZM65.8582 17.6631C65.7843 12.8227 61.8348 9 56.9938 9H15.0062C10.1652 9 6.21572 12.8227 6.1418 17.6631C6.06753 22.5266 6 29.5477 6 39C6 46.2639 6.03988 51.3741 6.09205 54.9515C6.15893 59.537 9.80278 63 14.3887 63H57.6113C62.1972 63 65.8411 59.537 65.9079 54.9515C65.9601 51.3741 66 46.2639 66 39C66 29.5477 65.9325 22.5266 65.8582 17.6631ZM39 21C37.3431 21 36 22.3431 36 24C36 25.6569 37.3431 27 39 27H51C52.6569 27 54 25.6569 54 24C54 22.3431 52.6569 21 51 21H39ZM36 33C36 31.3431 37.3431 30 39 30H51C52.6569 30 54 31.3431 54 33C54 34.6569 52.6569 36 51 36H39C37.3431 36 36 34.6569 36 33ZM24 33C27.3137 33 30 30.3137 30 27C30 23.6863 27.3137 21 24 21C20.6863 21 18 23.6863 18 27C18 30.3137 20.6863 33 24 33Z\"\n                  fill=\"currentColor\"\n                />\n              </svg>\n            )}\n            {date}\n            {badge ? (\n              <div className=\"h-fit px-1 bg-highlight dark:bg-highlight-dark rounded uppercase text-link dark:text-link-dark font-bold tracking-wide text-xs whitespace-nowrap\">\n                New\n              </div>\n            ) : null}\n          </div>\n          <span className=\"text-base text-secondary dark:text-secondary-dark\">\n            {children}\n          </span>\n          {children != null && (\n            <div className=\"text-link text-base dark:text-link-dark hover:underline mt-4\">\n              Read more\n            </div>\n          )}\n        </div>\n      </div>\n    </Link>\n  );\n}\n\nexport default BlogCard;\n"
  },
  {
    "path": "src/components/MDX/Challenges/Challenge.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {useState} from 'react';\nimport cn from 'classnames';\nimport {Button} from 'components/Button';\nimport {ChallengeContents} from './Challenges';\nimport {IconHint} from '../../Icon/IconHint';\nimport {IconSolution} from '../../Icon/IconSolution';\nimport {IconArrowSmall} from '../../Icon/IconArrowSmall';\nimport {H4} from '../Heading';\n\ninterface ChallengeProps {\n  isRecipes?: boolean;\n  totalChallenges: number;\n  currentChallenge: ChallengeContents;\n  hasNextChallenge: boolean;\n  handleClickNextChallenge: () => void;\n}\n\nexport function Challenge({\n  isRecipes,\n  totalChallenges,\n  currentChallenge,\n  hasNextChallenge,\n  handleClickNextChallenge,\n}: ChallengeProps) {\n  const [showHint, setShowHint] = useState(false);\n  const [showSolution, setShowSolution] = useState(false);\n\n  const toggleHint = () => {\n    if (showSolution && !showHint) {\n      setShowSolution(false);\n    }\n    setShowHint((hint) => !hint);\n  };\n\n  const toggleSolution = () => {\n    if (showHint && !showSolution) {\n      setShowHint(false);\n    }\n    setShowSolution((solution) => !solution);\n  };\n\n  return (\n    <div className=\"p-5 sm:py-8 sm:px-8\">\n      <div>\n        <H4\n          className=\"text-xl text-primary dark:text-primary-dark mb-2 mt-0 font-medium\"\n          id={currentChallenge.id}>\n          <div className=\"font-bold block md:inline\">\n            {isRecipes ? '例' : 'チャレンジ'} {currentChallenge.order}/\n            {totalChallenges}\n            <span className=\"text-primary dark:text-primary-dark\">: </span>\n          </div>\n          {currentChallenge.name}\n        </H4>\n        {currentChallenge.content}\n      </div>\n      <div className=\"flex justify-between items-center mt-4\">\n        {currentChallenge.hint ? (\n          <div>\n            <Button className=\"me-2\" onClick={toggleHint} active={showHint}>\n              <IconHint className=\"me-1.5\" />{' '}\n              {showHint ? 'ヒントを隠す' : 'ヒントを見る'}\n            </Button>\n            <Button\n              className=\"me-2\"\n              onClick={toggleSolution}\n              active={showSolution}>\n              <IconSolution className=\"me-1.5\" />{' '}\n              {showSolution ? '答えを隠す' : '答えを見る'}\n            </Button>\n          </div>\n        ) : (\n          !isRecipes && (\n            <Button\n              className=\"me-2\"\n              onClick={toggleSolution}\n              active={showSolution}>\n              <IconSolution className=\"me-1.5\" />{' '}\n              {showSolution ? '答えを隠す' : '答えを見る'}\n            </Button>\n          )\n        )}\n\n        {hasNextChallenge && (\n          <Button\n            className={cn(\n              isRecipes\n                ? 'bg-purple-50 border-purple-50 hover:bg-purple-50 focus:bg-purple-50 active:bg-purple-50'\n                : 'bg-link dark:bg-link-dark'\n            )}\n            onClick={handleClickNextChallenge}\n            active>\n            Next {isRecipes ? '例' : 'チャレンジ'}\n            <IconArrowSmall displayDirection=\"end\" className=\"block ms-1.5\" />\n          </Button>\n        )}\n      </div>\n      {showHint && currentChallenge.hint}\n\n      {showSolution && (\n        <div className=\"mt-6\">\n          <h3 className=\"text-2xl font-bold text-primary dark:text-primary-dark\">\n            答え\n          </h3>\n          {currentChallenge.solution}\n          <div className=\"flex justify-between items-center mt-4\">\n            <Button onClick={() => setShowSolution(false)}>答えを閉じる</Button>\n            {hasNextChallenge && (\n              <Button\n                className={cn(\n                  isRecipes ? 'bg-purple-50' : 'bg-link dark:bg-link-dark'\n                )}\n                onClick={handleClickNextChallenge}\n                active>\n                次の問題\n                <IconArrowSmall\n                  displayDirection=\"end\"\n                  className=\"block ms-1.5\"\n                />\n              </Button>\n            )}\n          </div>\n        </div>\n      )}\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/Challenges/Challenges.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {Children, useRef, useEffect, useState} from 'react';\nimport * as React from 'react';\nimport cn from 'classnames';\nimport {H2} from 'components/MDX/Heading';\nimport {H4} from 'components/MDX/Heading';\nimport {Challenge} from './Challenge';\nimport {Navigation} from './Navigation';\nimport {useRouter} from 'next/router';\n\ninterface ChallengesProps {\n  children: React.ReactElement[];\n  isRecipes?: boolean;\n  titleText?: string;\n  titleId?: string;\n  noTitle?: boolean;\n}\n\nexport interface ChallengeContents {\n  id: string;\n  name: string;\n  order: number;\n  content: React.ReactNode;\n  solution: React.ReactNode;\n  hint?: React.ReactNode;\n}\n\nconst parseChallengeContents = (\n  children: React.ReactElement[]\n): ChallengeContents[] => {\n  const contents: ChallengeContents[] = [];\n\n  if (!children) {\n    return contents;\n  }\n\n  let challenge: Partial<ChallengeContents> = {};\n  let content: React.ReactElement[] = [];\n  Children.forEach(children, (child) => {\n    const {props, type} = child as React.ReactElement<{\n      children?: string;\n      id?: string;\n    }>;\n    switch ((type as any).mdxName) {\n      case 'Solution': {\n        challenge.solution = child;\n        challenge.content = content;\n        contents.push(challenge as ChallengeContents);\n        challenge = {};\n        content = [];\n        break;\n      }\n      case 'Hint': {\n        challenge.hint = child;\n        break;\n      }\n      case 'h4': {\n        challenge.order = contents.length + 1;\n        challenge.name = props.children;\n        challenge.id = props.id;\n        break;\n      }\n      default: {\n        content.push(child);\n      }\n    }\n  });\n\n  return contents;\n};\n\nenum QueuedScroll {\n  INIT = 'init',\n  NEXT = 'next',\n}\n\nexport function Challenges({\n  children,\n  isRecipes,\n  noTitle,\n  titleText = isRecipes ? '例を試す' : 'チャレンジ問題にトライ',\n  titleId = isRecipes ? 'examples' : 'challenges',\n}: ChallengesProps) {\n  const challenges = parseChallengeContents(children);\n  const totalChallenges = challenges.length;\n  const scrollAnchorRef = useRef<HTMLDivElement>(null);\n  const queuedScrollRef = useRef<undefined | QueuedScroll>(QueuedScroll.INIT);\n  const [activeIndex, setActiveIndex] = useState(0);\n  const currentChallenge = challenges[activeIndex];\n  const {asPath} = useRouter();\n\n  useEffect(() => {\n    if (queuedScrollRef.current === QueuedScroll.INIT) {\n      const initIndex = challenges.findIndex(\n        (challenge) => challenge.id === asPath.split('#')[1]\n      );\n      if (initIndex === -1) {\n        queuedScrollRef.current = undefined;\n      } else if (initIndex !== activeIndex) {\n        setActiveIndex(initIndex);\n      }\n    }\n    if (queuedScrollRef.current) {\n      scrollAnchorRef.current!.scrollIntoView({\n        block: 'start',\n        ...(queuedScrollRef.current === QueuedScroll.NEXT && {\n          behavior: 'smooth',\n        }),\n      });\n      queuedScrollRef.current = undefined;\n    }\n  }, [activeIndex, asPath, challenges]);\n\n  const handleChallengeChange = (index: number) => {\n    setActiveIndex(index);\n  };\n\n  const Heading = isRecipes ? H4 : H2;\n  return (\n    <div className=\"max-w-7xl mx-auto py-4 w-full\">\n      <div\n        className={cn(\n          'border-gray-10 bg-card dark:bg-card-dark shadow-inner rounded-none -mx-5 sm:mx-auto sm:rounded-2xl'\n        )}>\n        <div ref={scrollAnchorRef} className=\"py-2 px-5 sm:px-8 pb-0 md:pb-0\">\n          {!noTitle && (\n            <Heading\n              id={titleId}\n              className={cn(\n                'mb-2 leading-10 relative',\n                isRecipes\n                  ? 'text-xl text-purple-50 dark:text-purple-30'\n                  : 'text-3xl text-link'\n              )}>\n              {titleText}\n            </Heading>\n          )}\n          {totalChallenges > 1 && (\n            <Navigation\n              currentChallenge={currentChallenge}\n              challenges={challenges}\n              handleChange={handleChallengeChange}\n              isRecipes={isRecipes}\n            />\n          )}\n        </div>\n        <Challenge\n          key={currentChallenge.id}\n          isRecipes={isRecipes}\n          currentChallenge={currentChallenge}\n          totalChallenges={totalChallenges}\n          hasNextChallenge={activeIndex < totalChallenges - 1}\n          handleClickNextChallenge={() => {\n            setActiveIndex((i) => i + 1);\n            queuedScrollRef.current = QueuedScroll.NEXT;\n          }}\n        />\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/Challenges/Navigation.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {useRef, useCallback, useEffect, createRef} from 'react';\nimport cn from 'classnames';\nimport {IconChevron} from 'components/Icon/IconChevron';\nimport {ChallengeContents} from './Challenges';\nimport {debounce} from 'debounce';\n\nexport function Navigation({\n  challenges,\n  handleChange,\n  currentChallenge,\n  isRecipes,\n}: {\n  challenges: ChallengeContents[];\n  handleChange: (index: number) => void;\n  currentChallenge: ChallengeContents;\n  isRecipes?: boolean;\n}) {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const challengesNavRef = useRef(\n    challenges.map(() => createRef<HTMLButtonElement>())\n  );\n  const scrollPos = currentChallenge.order - 1;\n  const canScrollLeft = scrollPos > 0;\n  const canScrollRight = scrollPos < challenges.length - 1;\n\n  const handleScrollRight = () => {\n    if (scrollPos < challenges.length - 1) {\n      const currentNavRef = challengesNavRef.current[scrollPos + 1].current;\n      if (!currentNavRef) {\n        return;\n      }\n      if (containerRef.current) {\n        containerRef.current.scrollLeft = currentNavRef.offsetLeft;\n      }\n      handleChange(scrollPos + 1);\n    }\n  };\n\n  const handleScrollLeft = () => {\n    if (scrollPos > 0) {\n      const currentNavRef = challengesNavRef.current[scrollPos - 1].current;\n      if (!currentNavRef) {\n        return;\n      }\n      if (containerRef.current) {\n        containerRef.current.scrollLeft = currentNavRef.offsetLeft;\n      }\n      handleChange(scrollPos - 1);\n    }\n  };\n\n  const handleSelectNav = (index: number) => {\n    const currentNavRef = challengesNavRef.current[index].current;\n    if (containerRef.current) {\n      containerRef.current.scrollLeft = currentNavRef?.offsetLeft || 0;\n    }\n    handleChange(index);\n  };\n\n  const handleResize = useCallback(() => {\n    if (containerRef.current) {\n      const el = containerRef.current;\n      el.scrollLeft =\n        challengesNavRef.current[scrollPos].current?.offsetLeft || 0;\n    }\n  }, [containerRef, challengesNavRef, scrollPos]);\n\n  useEffect(() => {\n    handleResize();\n    const debouncedHandleResize = debounce(handleResize, 200);\n    window.addEventListener('resize', debouncedHandleResize);\n    return () => {\n      window.removeEventListener('resize', debouncedHandleResize);\n    };\n  }, [handleResize]);\n\n  return (\n    <div className=\"flex items-center justify-between\">\n      <div className=\"overflow-hidden\">\n        <div\n          ref={containerRef}\n          className=\"flex relative transition-transform content-box overflow-x-auto\">\n          {challenges.map(({name, id, order}, index) => (\n            <button\n              className={cn(\n                'py-2 me-4 text-base border-b-4 duration-100 ease-in transition whitespace-nowrap text-ellipsis',\n                isRecipes &&\n                  currentChallenge.id === id &&\n                  'text-purple-50 border-purple-50 hover:text-purple-50 dark:text-purple-30 dark:border-purple-30 dark:hover:text-purple-30',\n                !isRecipes &&\n                  currentChallenge.id === id &&\n                  'text-link border-link hover:text-link dark:text-link-dark dark:border-link-dark dark:hover:text-link-dark'\n              )}\n              onClick={() => handleSelectNav(index)}\n              key={`button-${id}`}\n              ref={challengesNavRef.current[index]}>\n              {order}. {name}\n            </button>\n          ))}\n        </div>\n      </div>\n      <div className=\"flex z-10 pb-2 ps-2\">\n        <button\n          onClick={handleScrollLeft}\n          aria-label=\"Scroll left\"\n          className={cn(\n            'bg-secondary-button dark:bg-secondary-button-dark h-8 px-2 rounded-l rtl:rounded-r rtl:rounded-l-none border-gray-20 border-r rtl:border-l rtl:border-r-0',\n            {\n              'text-primary dark:text-primary-dark': canScrollLeft,\n              'text-gray-30': !canScrollLeft,\n            }\n          )}>\n          <IconChevron displayDirection=\"start\" />\n        </button>\n        <button\n          onClick={handleScrollRight}\n          aria-label=\"Scroll right\"\n          className={cn(\n            'bg-secondary-button dark:bg-secondary-button-dark h-8 px-2 rounded-e',\n            {\n              'text-primary dark:text-primary-dark': canScrollRight,\n              'text-gray-30': !canScrollRight,\n            }\n          )}>\n          <IconChevron displayDirection=\"end\" />\n        </button>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/Challenges/index.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nexport {Challenges} from './Challenges';\n\nexport function Hint({children}: {children: React.ReactNode}) {\n  return <div>{children}</div>;\n}\n\nexport function Solution({children}: {children: React.ReactNode}) {\n  return <div>{children}</div>;\n}\n"
  },
  {
    "path": "src/components/MDX/CodeBlock/CodeBlock.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport cn from 'classnames';\nimport {HighlightStyle} from '@codemirror/language';\nimport {highlightTree} from '@lezer/highlight';\nimport {javascript} from '@codemirror/lang-javascript';\nimport {html} from '@codemirror/lang-html';\nimport {css} from '@codemirror/lang-css';\nimport rangeParser from 'parse-numeric-range';\nimport {tags} from '@lezer/highlight';\n\nimport {CustomTheme} from '../Sandpack/Themes';\n\ninterface InlineHighlight {\n  step: number;\n  line: number;\n  startColumn: number;\n  endColumn: number;\n}\n\nconst jsxLang = javascript({jsx: true, typescript: false});\nconst cssLang = css();\nconst htmlLang = html();\n\nconst CodeBlock = function CodeBlock({\n  children: {\n    props: {className = 'language-js', children: code = '', meta},\n  },\n  noMargin,\n  noShadow,\n  onLineHover,\n}: {\n  children: React.ReactNode & {\n    props: {\n      className: string;\n      children?: string;\n      meta?: string;\n    };\n  };\n  className?: string;\n  noMargin?: boolean;\n  noShadow?: boolean;\n  onLineHover?: (lineNumber: number | null) => void;\n}) {\n  code = code.trimEnd();\n  let lang = jsxLang;\n  if (className === 'language-css') {\n    lang = cssLang;\n  } else if (className === 'language-html') {\n    lang = htmlLang;\n  }\n  const tree = lang.language.parser.parse(code);\n  let tokenStarts = new Map();\n  let tokenEnds = new Map();\n  const highlightTheme = getSyntaxHighlight(CustomTheme);\n  highlightTree(tree, highlightTheme, (from, to, className) => {\n    tokenStarts.set(from, className);\n    tokenEnds.set(to, className);\n  });\n\n  const highlightedLines = new Map();\n  const lines = code.split('\\n');\n  const lineDecorators = getLineDecorators(code, meta);\n  for (let decorator of lineDecorators) {\n    highlightedLines.set(decorator.line - 1, decorator.className);\n  }\n\n  const inlineDecorators = getInlineDecorators(code, meta);\n  const decoratorStarts = new Map();\n  const decoratorEnds = new Map();\n  for (let decorator of inlineDecorators) {\n    // Find where inline highlight starts and ends.\n    let decoratorStart = 0;\n    for (let i = 0; i < decorator.line - 1; i++) {\n      decoratorStart += lines[i].length + 1;\n    }\n    decoratorStart += decorator.startColumn;\n    const decoratorEnd =\n      decoratorStart + (decorator.endColumn - decorator.startColumn);\n    if (decoratorStarts.has(decoratorStart)) {\n      throw Error('Already opened decorator at ' + decoratorStart);\n    }\n    decoratorStarts.set(decoratorStart, decorator.className);\n    if (decoratorEnds.has(decoratorEnd)) {\n      throw Error('Already closed decorator at ' + decoratorEnd);\n    }\n    decoratorEnds.set(decoratorEnd, decorator.className);\n  }\n\n  // Produce output based on tokens and decorators.\n  // We assume tokens never overlap other tokens, and\n  // decorators never overlap with other decorators.\n  // However, tokens and decorators may mutually overlap.\n  // In that case, decorators always take precedence.\n  let currentDecorator = null;\n  let currentToken = null;\n  let buffer = '';\n  let lineIndex = 0;\n  let lineOutput = [];\n  let finalOutput = [];\n  for (let i = 0; i < code.length; i++) {\n    if (tokenEnds.has(i)) {\n      if (!currentToken) {\n        throw Error('Cannot close token at ' + i + ' because it was not open.');\n      }\n      if (!currentDecorator) {\n        lineOutput.push(\n          <span key={i + '/t'} className={currentToken}>\n            {buffer}\n          </span>\n        );\n        buffer = '';\n      }\n      currentToken = null;\n    }\n    if (decoratorEnds.has(i)) {\n      if (!currentDecorator) {\n        throw Error(\n          'Cannot close decorator at ' + i + ' because it was not open.'\n        );\n      }\n      lineOutput.push(\n        <span key={i + '/d'} className={currentDecorator}>\n          {buffer}\n        </span>\n      );\n      buffer = '';\n      currentDecorator = null;\n    }\n    if (decoratorStarts.has(i)) {\n      if (currentDecorator) {\n        throw Error(\n          'Cannot open decorator at ' + i + ' before closing last one.'\n        );\n      }\n      if (currentToken) {\n        lineOutput.push(\n          <span key={i + 'd'} className={currentToken}>\n            {buffer}\n          </span>\n        );\n        buffer = '';\n      } else {\n        lineOutput.push(buffer);\n        buffer = '';\n      }\n      currentDecorator = decoratorStarts.get(i);\n    }\n    if (tokenStarts.has(i)) {\n      if (currentToken) {\n        throw Error('Cannot open token at ' + i + ' before closing last one.');\n      }\n      currentToken = tokenStarts.get(i);\n      if (!currentDecorator) {\n        lineOutput.push(buffer);\n        buffer = '';\n      }\n    }\n    if (code[i] === '\\n') {\n      lineOutput.push(buffer);\n      buffer = '';\n      const currentLineIndex = lineIndex;\n      finalOutput.push(\n        <div\n          key={lineIndex}\n          className={'cm-line ' + (highlightedLines.get(lineIndex) ?? '')}\n          onMouseEnter={\n            onLineHover ? () => onLineHover(currentLineIndex) : undefined\n          }>\n          {lineOutput}\n          <br />\n        </div>\n      );\n      lineOutput = [];\n      lineIndex++;\n    } else {\n      buffer += code[i];\n    }\n  }\n  if (currentDecorator) {\n    lineOutput.push(\n      <span key={'end/d'} className={currentDecorator}>\n        {buffer}\n      </span>\n    );\n  } else if (currentToken) {\n    lineOutput.push(\n      <span key={'end/t'} className={currentToken}>\n        {buffer}\n      </span>\n    );\n  } else {\n    lineOutput.push(buffer);\n  }\n  finalOutput.push(\n    <div\n      key={lineIndex}\n      className={'cm-line ' + (highlightedLines.get(lineIndex) ?? '')}\n      onMouseEnter={onLineHover ? () => onLineHover(lineIndex) : undefined}>\n      {lineOutput}\n    </div>\n  );\n\n  return (\n    <div\n      dir=\"ltr\"\n      className={cn(\n        'sandpack sandpack--codeblock',\n        'rounded-2xl h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg',\n        !noMargin && 'my-8',\n        noShadow &&\n          'shadow-none rounded-2xl overflow-hidden w-full flex bg-transparent'\n      )}\n      style={{contain: 'content'}}>\n      <div className=\"sp-wrapper\">\n        <div className=\"sp-stack\">\n          <div className=\"sp-code-editor\">\n            <pre className=\"sp-cm sp-pristine sp-javascript flex align-start\">\n              <code\n                className=\"sp-pre-placeholder grow-[2]\"\n                onMouseLeave={\n                  onLineHover ? () => onLineHover(null) : undefined\n                }>\n                {finalOutput}\n              </code>\n            </pre>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default CodeBlock;\n\nfunction classNameToken(name: string): string {\n  return `sp-syntax-${name}`;\n}\n\nfunction getSyntaxHighlight(theme: any): HighlightStyle {\n  return HighlightStyle.define([\n    {tag: tags.link, textdecorator: 'underline'},\n    {tag: tags.emphasis, fontStyle: 'italic'},\n    {tag: tags.strong, fontWeight: 'bold'},\n\n    {\n      tag: tags.keyword,\n      class: classNameToken('keyword'),\n    },\n    {\n      tag: [tags.atom, tags.number, tags.bool],\n      class: classNameToken('static'),\n    },\n    {\n      tag: tags.standard(tags.tagName),\n      class: classNameToken('tag'),\n    },\n    {tag: tags.variableName, class: classNameToken('plain')},\n    {\n      // Highlight function call\n      tag: tags.function(tags.variableName),\n      class: classNameToken('definition'),\n    },\n    {\n      // Highlight function definition differently (eg: functional component def in React)\n      tag: [tags.definition(tags.function(tags.variableName)), tags.tagName],\n      class: classNameToken('definition'),\n    },\n    {\n      tag: tags.propertyName,\n      class: classNameToken('property'),\n    },\n    {\n      tag: [tags.literal, tags.inserted],\n      class: classNameToken(theme.syntax.string ? 'string' : 'static'),\n    },\n    {\n      tag: tags.punctuation,\n      class: classNameToken('punctuation'),\n    },\n    {\n      tag: [tags.comment, tags.quote],\n      class: classNameToken('comment'),\n    },\n  ]);\n}\n\nfunction getLineDecorators(\n  code: string,\n  meta?: string\n): Array<{\n  line: number;\n  className: string;\n}> {\n  if (!meta) {\n    return [];\n  }\n  const linesToHighlight = getHighlightLines(meta);\n  const highlightedLineConfig = linesToHighlight.map((line) => {\n    return {\n      className: 'bg-github-highlight dark:bg-opacity-10',\n      line,\n    };\n  });\n  return highlightedLineConfig;\n}\n\nfunction getInlineDecorators(\n  code: string,\n  meta?: string\n): Array<{\n  step: number;\n  line: number;\n  startColumn: number;\n  endColumn: number;\n  className: string;\n}> {\n  if (!meta) {\n    return [];\n  }\n  const inlineHighlightLines = getInlineHighlights(meta, code);\n  const inlineHighlightConfig = inlineHighlightLines.map(\n    (line: InlineHighlight) => ({\n      ...line,\n      elementAttributes: {'data-step': `${line.step}`},\n      className: cn(\n        'code-step bg-opacity-10 dark:bg-opacity-20 relative rounded px-1 py-[1.5px] border-b-[2px] border-opacity-60',\n        {\n          'bg-blue-40 border-blue-40 text-blue-60 dark:text-blue-30':\n            line.step === 1,\n          'bg-yellow-40 border-yellow-40 text-yellow-60 dark:text-yellow-30':\n            line.step === 2,\n          'bg-purple-40 border-purple-40 text-purple-60 dark:text-purple-30':\n            line.step === 3,\n          'bg-green-40 border-green-40 text-green-60 dark:text-green-30':\n            line.step === 4,\n          // TODO: Some codeblocks use up to 6 steps.\n        }\n      ),\n    })\n  );\n  return inlineHighlightConfig;\n}\n\n/**\n *\n * @param meta string provided after the language in a markdown block\n * @returns array of lines to highlight\n * @example\n * ```js {1-3,7} [[1, 1, 20, 33], [2, 4, 4, 8]] App.js active\n * ...\n * ```\n *\n * -> The meta is `{1-3,7} [[1, 1, 20, 33], [2, 4, 4, 8]] App.js active`\n */\nfunction getHighlightLines(meta: string): number[] {\n  const HIGHLIGHT_REGEX = /{([\\d,-]+)}/;\n  const parsedMeta = HIGHLIGHT_REGEX.exec(meta);\n  if (!parsedMeta) {\n    return [];\n  }\n  return rangeParser(parsedMeta[1]);\n}\n\n/**\n *\n * @param meta string provided after the language in a markdown block\n * @returns InlineHighlight[]\n * @example\n * ```js {1-3,7} [[1, 1, 'count'], [2, 4, 'setCount']] App.js active\n * ...\n * ```\n *\n * -> The meta is `{1-3,7} [[1, 1, 'count', [2, 4, 'setCount']] App.js active`\n */\nfunction getInlineHighlights(meta: string, code: string) {\n  const INLINE_HEIGHT_REGEX = /(\\[\\[.*\\]\\])/;\n  const parsedMeta = INLINE_HEIGHT_REGEX.exec(meta);\n  if (!parsedMeta) {\n    return [];\n  }\n\n  const lines = code.split('\\n');\n  const encodedHighlights = JSON.parse(parsedMeta[1]);\n  return encodedHighlights.map(([step, lineNo, substr, fromIndex]: any[]) => {\n    const line = lines[lineNo - 1];\n    let index = line.indexOf(substr);\n    const lastIndex = line.lastIndexOf(substr);\n    if (index !== lastIndex) {\n      if (fromIndex === undefined) {\n        throw Error(\n          \"Found '\" +\n            substr +\n            \"' twice. Specify fromIndex as the fourth value in the tuple.\"\n        );\n      }\n      index = line.indexOf(substr, fromIndex);\n    }\n    if (index === -1) {\n      throw Error(\"Could not find: '\" + substr + \"'\");\n    }\n    return {\n      step,\n      line: lineNo,\n      startColumn: index,\n      endColumn: index + substr.length,\n    };\n  });\n}\n"
  },
  {
    "path": "src/components/MDX/CodeBlock/index.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nimport cn from 'classnames';\nimport {lazy, memo, Suspense} from 'react';\nconst CodeBlock = lazy(() => import('./CodeBlock'));\n\nexport default memo(function CodeBlockWrapper(props: {\n  children: React.ReactNode & {\n    props: {\n      className: string;\n      children: string;\n      meta?: string;\n    };\n  };\n  isFromPackageImport: boolean;\n  noMargin?: boolean;\n  noMarkers?: boolean;\n}): any {\n  const {children, isFromPackageImport} = props;\n  return (\n    <Suspense\n      fallback={\n        <pre\n          translate=\"no\"\n          dir=\"ltr\"\n          className={cn(\n            'rounded-lg leading-6 h-full w-full overflow-x-auto flex items-center bg-wash dark:bg-gray-95 shadow-lg text-[13.6px] overflow-hidden',\n            !isFromPackageImport && 'my-8'\n          )}>\n          <div className=\"py-[18px] ps-5 font-normal \">\n            <p className=\"sp-pre-placeholder overflow-hidden\">{children}</p>\n          </div>\n        </pre>\n      }>\n      <CodeBlock {...props} />\n    </Suspense>\n  );\n});\n"
  },
  {
    "path": "src/components/MDX/CodeDiagram.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {Children} from 'react';\nimport * as React from 'react';\nimport CodeBlock from './CodeBlock';\n\ninterface CodeDiagramProps {\n  children: React.ReactNode;\n  flip?: boolean;\n}\n\nexport function CodeDiagram({children, flip = false}: CodeDiagramProps) {\n  const illustration = Children.toArray(children).filter((child: any) => {\n    return child.type === 'img';\n  });\n  const content = Children.toArray(children).map((child: any) => {\n    if (child.type?.mdxName === 'pre') {\n      return (\n        <CodeBlock\n          key={child.key}\n          {...child.props}\n          noMargin={true}\n          noMarkers={true}\n        />\n      );\n    } else if (child.type === 'img') {\n      return null;\n    } else {\n      return child;\n    }\n  });\n  if (flip) {\n    return (\n      <section className=\"my-8 grid grid-cols-1 lg:grid-cols-2 gap-x-8 gap-y-4\">\n        {illustration}\n        <div className=\"flex flex-col justify-center\">{content}</div>\n      </section>\n    );\n  }\n  return (\n    <section className=\"my-8 grid grid-cols-1 lg:grid-cols-2 gap-x-8 gap-y-4\">\n      <div className=\"flex flex-col justify-center\">{content}</div>\n      <div className=\"py-4\">{illustration}</div>\n    </section>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/ConsoleBlock.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {isValidElement} from 'react';\nimport * as React from 'react';\nimport cn from 'classnames';\nimport {IconWarning} from '../Icon/IconWarning';\nimport {IconError} from '../Icon/IconError';\n\ntype LogLevel = 'warning' | 'error' | 'info';\n\ninterface ConsoleBlockProps {\n  level?: LogLevel;\n  children: React.ReactNode;\n}\n\ninterface ConsoleBlockMultiProps {\n  children: React.ReactNode;\n}\n\nconst Box = ({\n  width = '60px',\n  height = '17px',\n  className,\n  customStyles,\n}: {\n  width?: string;\n  height?: string;\n  className?: string;\n  customStyles?: Record<string, string>;\n}) => (\n  <div className={className} style={{width, height, ...customStyles}}></div>\n);\n\nexport function ConsoleBlock({level = 'error', children}: ConsoleBlockProps) {\n  let message: React.ReactNode | null;\n  if (typeof children === 'string') {\n    message = children;\n  } else if (isValidElement(children)) {\n    message = (children as React.ReactElement<{children?: React.ReactNode}>)\n      .props.children;\n  }\n\n  return (\n    <div\n      className=\"console-block mb-4 text-secondary bg-wash dark:bg-wash-dark rounded-lg\"\n      translate=\"no\"\n      dir=\"ltr\">\n      <div className=\"flex w-full rounded-t-lg bg-gray-200 dark:bg-gray-80\">\n        <div className=\"px-4 py-2 border-gray-300 dark:border-gray-90 border-r\">\n          <Box className=\"bg-gray-300 dark:bg-gray-70\" width=\"15px\" />\n        </div>\n        <div className=\"flex text-sm px-4\">\n          <div className=\"border-b-2 border-gray-300 dark:border-gray-90 text-tertiary dark:text-tertiary-dark\">\n            Console\n          </div>\n          <div className=\"px-4 py-2 flex\">\n            <Box className=\"me-2 bg-gray-300 dark:bg-gray-70\" />\n            <Box className=\"me-2 hidden md:block bg-gray-300 dark:bg-gray-70\" />\n            <Box className=\"hidden md:block bg-gray-300 dark:bg-gray-70\" />\n          </div>\n        </div>\n      </div>\n      <div\n        className={cn(\n          'flex px-4 pt-4 pb-6 items-center content-center font-mono text-code rounded-b-md',\n          {\n            'bg-red-30 text-red-50 dark:text-red-30 bg-opacity-5':\n              level === 'error',\n            'bg-yellow-5 text-yellow-50': level === 'warning',\n            'bg-gray-5 text-secondary dark:text-secondary-dark':\n              level === 'info',\n          }\n        )}>\n        {level === 'error' && <IconError className=\"self-start mt-1.5\" />}\n        {level === 'warning' && <IconWarning className=\"self-start mt-1\" />}\n        <div className=\"px-3\">{message}</div>\n      </div>\n    </div>\n  );\n}\n\nexport function ConsoleBlockMulti({children}: ConsoleBlockMultiProps) {\n  return (\n    <div\n      className=\"console-block mb-4 text-secondary bg-wash dark:bg-wash-dark rounded-lg\"\n      translate=\"no\"\n      dir=\"ltr\">\n      <div className=\"flex w-full rounded-t-lg bg-gray-200 dark:bg-gray-80\">\n        <div className=\"px-4 py-2 border-gray-300 dark:border-gray-90 border-r\">\n          <Box className=\"bg-gray-300 dark:bg-gray-70\" width=\"15px\" />\n        </div>\n        <div className=\"flex text-sm px-4\">\n          <div className=\"border-b-2 border-gray-300 dark:border-gray-90 text-tertiary dark:text-tertiary-dark\">\n            Console\n          </div>\n          <div className=\"px-4 py-2 flex\">\n            <Box className=\"me-2 bg-gray-300 dark:bg-gray-70\" />\n            <Box className=\"me-2 hidden md:block bg-gray-300 dark:bg-gray-70\" />\n            <Box className=\"hidden md:block bg-gray-300 dark:bg-gray-70\" />\n          </div>\n        </div>\n      </div>\n      <div className=\"grid grid-cols-1 divide-y divide-gray-300 dark:divide-gray-70 text-base\">\n        {children}\n      </div>\n    </div>\n  );\n}\n\nexport function ConsoleLogLine({children, level}: ConsoleBlockProps) {\n  let message: React.ReactNode | null;\n  if (typeof children === 'string') {\n    message = children;\n  } else if (isValidElement(children)) {\n    message = (children as React.ReactElement<{children?: React.ReactNode}>)\n      .props.children;\n  } else if (Array.isArray(children)) {\n    message = children.reduce((result, child) => {\n      if (typeof child === 'string') {\n        result += child;\n      } else if (isValidElement(child)) {\n        // @ts-ignore\n        result += child.props.children;\n      }\n      return result;\n    }, '');\n  }\n\n  return (\n    <div\n      className={cn(\n        'ps-4 pe-2 pt-1 pb-2 grid grid-cols-[18px_auto] font-mono rounded-b-md',\n        {\n          'bg-red-30 text-red-50 dark:text-red-30 bg-opacity-5':\n            level === 'error',\n          'bg-yellow-5 text-yellow-50': level === 'warning',\n          'bg-gray-5 text-secondary dark:text-secondary-dark': level === 'info',\n        }\n      )}>\n      {level === 'error' && (\n        <IconError className=\"self-start mt-1.5 text-[.7rem] w-6\" />\n      )}\n      {level === 'warning' && (\n        <IconWarning className=\"self-start mt-1 text-[.65rem] w-6\" />\n      )}\n      <div className=\"px-2 pt-1 whitespace-break-spaces text-code leading-tight\">\n        {message}\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/Diagram.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport Image from 'next/image';\n\ninterface DiagramProps {\n  name: string;\n  alt: string;\n  height: number;\n  width: number;\n  children: string;\n  captionPosition: 'top' | 'bottom' | null;\n}\n\nfunction Caption({text}: {text: string}) {\n  return (\n    <div className=\"w-full flex justify-center\">\n      <figcaption className=\"p-1 sm:p-2 mt-0 sm:mt-0 text-gray-40 text-base lg:text-lg text-center leading-tight table-caption max-w-lg\">\n        {text}\n      </figcaption>\n    </div>\n  );\n}\n\nexport function Diagram({\n  name,\n  alt,\n  height,\n  width,\n  children,\n  captionPosition,\n}: DiagramProps) {\n  return (\n    <figure className=\"flex flex-col px-0 p-0 sm:p-10 first:mt-0 mt-10 sm:mt-0 justify-center items-center\">\n      {captionPosition === 'top' && <Caption text={children} />}\n      <div className=\"dark-image\">\n        <Image\n          src={`/images/docs/diagrams/${name}.dark.png`}\n          alt={alt}\n          height={height}\n          width={width}\n        />\n      </div>\n      <div className=\"light-image\">\n        <Image\n          src={`/images/docs/diagrams/${name}.png`}\n          alt={alt}\n          height={height}\n          width={width}\n        />\n      </div>\n      {(!captionPosition || captionPosition === 'bottom') && (\n        <Caption text={children} />\n      )}\n    </figure>\n  );\n}\n\nexport default Diagram;\n"
  },
  {
    "path": "src/components/MDX/DiagramGroup.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {ReactNode} from 'react';\n\ninterface DiagramGroupProps {\n  children: ReactNode;\n}\n\nexport function DiagramGroup({children}: DiagramGroupProps) {\n  return (\n    <div className=\"flex flex-col sm:flex-row py-2 sm:p-0 sm:space-y-0 justify-center items-start sm:items-center w-full\">\n      {children}\n    </div>\n  );\n}\n\nexport default DiagramGroup;\n"
  },
  {
    "path": "src/components/MDX/ErrorDecoder.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport {useEffect, useState} from 'react';\nimport {useErrorDecoderParams} from '../ErrorDecoderContext';\nimport cn from 'classnames';\n\nfunction replaceArgs(\n  msg: string,\n  argList: Array<string | undefined>,\n  replacer = '[missing argument]'\n): string {\n  let argIdx = 0;\n  return msg.replace(/%s/g, function () {\n    const arg = argList[argIdx++];\n    // arg can be an empty string: ?args[0]=&args[1]=count\n    return arg === undefined ? replacer : arg;\n  });\n}\n\n/**\n * Sindre Sorhus <https://sindresorhus.com>\n * Released under MIT license\n * https://github.com/sindresorhus/linkify-urls/blob/b2397096df152e2f799011f7a48e5f73b4bf1c7e/index.js#L5C1-L7C1\n *\n * The regex is used to extract URL from the string for linkify.\n */\nconst urlRegex = () =>\n  /((?:https?(?::\\/\\/))(?:www\\.)?(?:[a-zA-Z\\d-_.]+(?:(?:\\.|@)[a-zA-Z\\d]{2,})|localhost)(?:(?:[-a-zA-Z\\d:%_+.~#!?&//=@]*)(?:[,](?![\\s]))*)*)/g;\n\n// When the message contains a URL (like https://fb.me/react-refs-must-have-owner),\n// make it a clickable link.\nfunction urlify(str: string): React.ReactNode[] {\n  const segments = str.split(urlRegex());\n\n  return segments.map((message, i) => {\n    if (i % 2 === 1) {\n      return (\n        <a\n          key={i}\n          target=\"_blank\"\n          className=\"underline\"\n          rel=\"noopener noreferrer\"\n          href={message}>\n          {message}\n        </a>\n      );\n    }\n    return message;\n  });\n}\n\n// `?args[]=foo&args[]=bar`\n// or `// ?args[0]=foo&args[1]=bar`\nfunction parseQueryString(search: string): Array<string | undefined> {\n  const rawQueryString = search.substring(1);\n  if (!rawQueryString) {\n    return [];\n  }\n\n  const args: Array<string | undefined> = [];\n\n  const queries = rawQueryString.split('&');\n  for (let i = 0; i < queries.length; i++) {\n    const query = decodeURIComponent(queries[i]);\n    if (query.startsWith('args[')) {\n      args.push(query.slice(query.indexOf(']=') + 2));\n    }\n  }\n\n  return args;\n}\n\nexport default function ErrorDecoder() {\n  const {errorMessage, errorCode} = useErrorDecoderParams();\n  /** error messages that contain %s require reading location.search */\n  const hasParams = errorMessage?.includes('%s');\n  const [message, setMessage] = useState<React.ReactNode | null>(() =>\n    errorMessage ? urlify(errorMessage) : null\n  );\n\n  const [isReady, setIsReady] = useState(errorMessage == null || !hasParams);\n\n  useEffect(() => {\n    if (errorMessage == null || !hasParams) {\n      return;\n    }\n    const args = parseQueryString(window.location.search);\n    let message = errorMessage;\n    if (errorCode === '418') {\n      // Hydration errors have a %s for the diff, but we don't add that to the args for security reasons.\n      message = message.replace(/%s$/, '');\n\n      // Before React 19.1, the error message didn't have an arg, and was always HTML.\n      if (args.length === 0) {\n        args.push('HTML');\n      } else if (args.length === 1 && args[0] === '') {\n        args[0] = 'HTML';\n      }\n    }\n\n    setMessage(urlify(replaceArgs(message, args, '[missing argument]')));\n    setIsReady(true);\n  }, [errorCode, hasParams, errorMessage]);\n\n  return (\n    <code\n      className={cn(\n        'whitespace-pre-line block bg-red-100 text-red-600 py-4 px-6 mt-5 rounded-lg',\n        isReady ? 'opacity-100' : 'opacity-0'\n      )}>\n      <b>{message}</b>\n    </code>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/ExpandableCallout.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nimport cn from 'classnames';\nimport {IconNote} from '../Icon/IconNote';\nimport {IconWarning} from '../Icon/IconWarning';\nimport {IconPitfall} from '../Icon/IconPitfall';\nimport {IconCanary} from '../Icon/IconCanary';\nimport {IconRocket} from '../Icon/IconRocket';\n\ntype CalloutVariants =\n  | 'deprecated'\n  | 'pitfall'\n  | 'note'\n  | 'wip'\n  | 'canary'\n  | 'experimental'\n  | 'rc'\n  | 'major'\n  | 'rsc';\n\ninterface ExpandableCalloutProps {\n  children: React.ReactNode;\n  type: CalloutVariants;\n}\n\nconst variantMap = {\n  deprecated: {\n    title: 'Deprecated',\n    Icon: IconWarning,\n    containerClasses: 'bg-red-5 dark:bg-red-60 dark:bg-opacity-20',\n    textColor: 'text-red-50 dark:text-red-40',\n    overlayGradient:\n      'linear-gradient(rgba(249, 247, 243, 0), rgba(249, 247, 243, 1)',\n  },\n  note: {\n    title: '補足',\n    Icon: IconNote,\n    containerClasses:\n      'bg-green-5 dark:bg-green-60 dark:bg-opacity-20 text-primary dark:text-primary-dark text-lg',\n    textColor: 'text-green-60 dark:text-green-40',\n    overlayGradient:\n      'linear-gradient(rgba(245, 249, 248, 0), rgba(245, 249, 248, 1)',\n  },\n  rc: {\n    title: 'RC',\n    Icon: IconCanary,\n    containerClasses:\n      'bg-gray-5 dark:bg-gray-60 dark:bg-opacity-20 text-primary dark:text-primary-dark text-lg',\n    textColor: 'text-gray-60 dark:text-gray-30',\n    overlayGradient:\n      'linear-gradient(rgba(245, 249, 248, 0), rgba(245, 249, 248, 1)',\n  },\n  canary: {\n    title: 'Canary',\n    Icon: IconCanary,\n    containerClasses:\n      'bg-gray-5 dark:bg-gray-60 dark:bg-opacity-20 text-primary dark:text-primary-dark text-lg',\n    textColor: 'text-gray-60 dark:text-gray-30',\n    overlayGradient:\n      'linear-gradient(rgba(245, 249, 248, 0), rgba(245, 249, 248, 1)',\n  },\n  experimental: {\n    title: '実験的機能',\n    Icon: IconCanary,\n    containerClasses:\n      'bg-green-5 dark:bg-green-60 dark:bg-opacity-20 text-primary dark:text-primary-dark text-lg',\n    textColor: 'text-green-60 dark:text-green-40',\n    overlayGradient:\n      'linear-gradient(rgba(245, 249, 248, 0), rgba(245, 249, 248, 1)',\n  },\n  pitfall: {\n    title: '落とし穴',\n    Icon: IconPitfall,\n    containerClasses: 'bg-yellow-5 dark:bg-yellow-60 dark:bg-opacity-20',\n    textColor: 'text-yellow-50 dark:text-yellow-40',\n    overlayGradient:\n      'linear-gradient(rgba(249, 247, 243, 0), rgba(249, 247, 243, 1)',\n  },\n  wip: {\n    title: '準備中',\n    Icon: IconNote,\n    containerClasses: 'bg-yellow-5 dark:bg-yellow-60 dark:bg-opacity-20',\n    textColor: 'text-yellow-50 dark:text-yellow-40',\n    overlayGradient:\n      'linear-gradient(rgba(249, 247, 243, 0), rgba(249, 247, 243, 1)',\n  },\n  major: {\n    title: 'React 19',\n    Icon: IconRocket,\n    containerClasses: 'bg-blue-10 dark:bg-blue-60 dark:bg-opacity-20',\n    textColor: 'text-blue-50 dark:text-blue-40',\n    overlayGradient:\n      'linear-gradient(rgba(249, 247, 243, 0), rgba(249, 247, 243, 1)',\n  },\n  rsc: {\n    title: 'React Server Components',\n    Icon: null,\n    containerClasses: 'bg-blue-10 dark:bg-blue-60 dark:bg-opacity-20',\n    textColor: 'text-blue-50 dark:text-blue-40',\n    overlayGradient:\n      'linear-gradient(rgba(249, 247, 243, 0), rgba(249, 247, 243, 1)',\n  },\n};\n\nfunction ExpandableCallout({children, type = 'note'}: ExpandableCalloutProps) {\n  const variant = variantMap[type];\n\n  return (\n    <div\n      className={cn(\n        'expandable-callout',\n        'pt-8 pb-4 px-5 sm:px-8 my-8 relative rounded-none shadow-inner-border -mx-5 sm:mx-auto sm:rounded-2xl',\n        variant.containerClasses\n      )}>\n      <h3 className={cn('text-2xl font-display font-bold', variant.textColor)}>\n        {variant.Icon && (\n          <variant.Icon\n            className={cn('inline me-2 mb-1 text-lg', variant.textColor)}\n          />\n        )}\n        {variant.title}\n      </h3>\n      <div className=\"relative\">\n        <div className=\"py-2\">{children}</div>\n      </div>\n    </div>\n  );\n}\n\nexport default ExpandableCallout;\n"
  },
  {
    "path": "src/components/MDX/ExpandableExample.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nimport cn from 'classnames';\nimport {IconChevron} from '../Icon/IconChevron';\nimport {IconDeepDive} from '../Icon/IconDeepDive';\nimport {IconCodeBlock} from '../Icon/IconCodeBlock';\nimport {Button} from '../Button';\nimport {H4} from './Heading';\nimport {useRouter} from 'next/router';\nimport {useEffect, useRef, useState} from 'react';\n\ninterface ExpandableExampleProps {\n  children: React.ReactNode;\n  excerpt?: string;\n  type: 'DeepDive' | 'Example';\n}\n\nfunction ExpandableExample({children, excerpt, type}: ExpandableExampleProps) {\n  if (!Array.isArray(children) || children[0].type.mdxName !== 'h4') {\n    throw Error(\n      `Expandable content ${type} is missing a corresponding title at the beginning`\n    );\n  }\n  const isDeepDive = type === 'DeepDive';\n  const isExample = type === 'Example';\n  const id = children[0].props.id;\n\n  const {asPath} = useRouter();\n  const shouldAutoExpand = id === asPath.split('#')[1];\n  const queuedExpandRef = useRef<boolean>(shouldAutoExpand);\n  const [isExpanded, setIsExpanded] = useState(false);\n\n  useEffect(() => {\n    if (queuedExpandRef.current) {\n      queuedExpandRef.current = false;\n      setIsExpanded(true);\n    }\n  }, []);\n\n  return (\n    <details\n      open={isExpanded}\n      onToggle={(e: any) => {\n        setIsExpanded(e.currentTarget!.open);\n      }}\n      className={cn(\n        'my-12 rounded-2xl shadow-inner-border dark:shadow-inner-border-dark relative',\n        {\n          'dark:bg-opacity-20 dark:bg-purple-60 bg-purple-5': isDeepDive,\n          'dark:bg-opacity-20 dark:bg-yellow-60 bg-yellow-5': isExample,\n        }\n      )}>\n      <summary\n        className=\"list-none p-8\"\n        tabIndex={-1 /* there's a button instead */}\n        onClick={(e) => {\n          // We toggle using a button instead of this whole area,\n          // with an escape case for the header anchor link\n          if (!(e.target instanceof SVGElement)) {\n            e.preventDefault();\n          }\n        }}>\n        <h5\n          className={cn('mb-4 uppercase font-bold flex items-center text-sm', {\n            'dark:text-purple-30 text-purple-50': isDeepDive,\n            'dark:text-yellow-30 text-yellow-60': isExample,\n          })}>\n          {isDeepDive && (\n            <>\n              <IconDeepDive className=\"inline me-2 dark:text-purple-30 text-purple-40\" />\n              さらに深く知る\n            </>\n          )}\n          {isExample && (\n            <>\n              <IconCodeBlock className=\"inline me-2 dark:text-yellow-30 text-yellow-50\" />\n              例\n            </>\n          )}\n        </h5>\n        <div className=\"mb-4\">\n          <H4\n            id={id}\n            className=\"text-xl font-bold text-primary dark:text-primary-dark\">\n            {children[0].props.children}\n          </H4>\n          {excerpt && <div>{excerpt}</div>}\n        </div>\n        <Button\n          active={true}\n          className={cn({\n            'bg-purple-50 border-purple-50 hover:bg-purple-40 focus:bg-purple-50 active:bg-purple-50':\n              isDeepDive,\n            'bg-yellow-50 border-yellow-50 hover:bg-yellow-40 focus:bg-yellow-50 active:bg-yellow-50':\n              isExample,\n          })}\n          onClick={() => setIsExpanded((current) => !current)}>\n          <span className=\"me-1\">\n            <IconChevron displayDirection={isExpanded ? 'up' : 'down'} />\n          </span>\n          {isExpanded ? '詳細を隠す' : '詳細を開く'}\n        </Button>\n      </summary>\n      <div\n        className={cn('p-8 border-t', {\n          'dark:border-purple-60 border-purple-10 ': isDeepDive,\n          'dark:border-yellow-60 border-yellow-50': isExample,\n        })}>\n        {children.slice(1)}\n      </div>\n    </details>\n  );\n}\n\nexport default ExpandableExample;\n"
  },
  {
    "path": "src/components/MDX/Heading.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport cn from 'classnames';\nimport * as React from 'react';\nimport {forwardRefWithAs} from 'utils/forwardRefWithAs';\nexport interface HeadingProps {\n  className?: string;\n  isPageAnchor?: boolean;\n  children: React.ReactNode;\n  id?: string;\n  as?: any;\n}\n\nconst Heading = forwardRefWithAs<HeadingProps, 'div'>(function Heading(\n  {as: Comp = 'div', className, children, id, isPageAnchor = true, ...props},\n  ref\n) {\n  let label = 'Link for this heading';\n  if (typeof children === 'string') {\n    label = 'Link for ' + children;\n  }\n\n  return (\n    <Comp id={id} {...props} ref={ref} className={cn('mdx-heading', className)}>\n      {children}\n      {isPageAnchor && (\n        <a\n          href={`#${id}`}\n          aria-label={label}\n          title={label}\n          className={cn(\n            'mdx-header-anchor',\n            Comp === 'h1' ? 'hidden' : 'inline-block'\n          )}>\n          <svg\n            width=\"1em\"\n            height=\"1em\"\n            viewBox=\"0 0 13 13\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n            className=\"text-gray-70 ms-2 h-5 w-5\">\n            <g fill=\"currentColor\" fillRule=\"evenodd\">\n              <path d=\"M7.778 7.975a2.5 2.5 0 0 0 .347-3.837L6.017 2.03a2.498 2.498 0 0 0-3.542-.007 2.5 2.5 0 0 0 .006 3.543l1.153 1.15c.07-.29.154-.563.25-.773.036-.077.084-.16.14-.25L3.18 4.85a1.496 1.496 0 0 1 .002-2.12 1.496 1.496 0 0 1 2.12 0l2.124 2.123a1.496 1.496 0 0 1-.333 2.37c.16.246.42.504.685.752z\" />\n              <path d=\"M5.657 4.557a2.5 2.5 0 0 0-.347 3.837l2.108 2.108a2.498 2.498 0 0 0 3.542.007 2.5 2.5 0 0 0-.006-3.543L9.802 5.815c-.07.29-.154.565-.25.774-.036.076-.084.16-.14.25l.842.84c.585.587.59 1.532 0 2.122-.587.585-1.532.59-2.12 0L6.008 7.68a1.496 1.496 0 0 1 .332-2.372c-.16-.245-.42-.503-.685-.75z\" />\n            </g>\n          </svg>\n        </a>\n      )}\n    </Comp>\n  );\n});\n\nexport const H1 = ({className, ...props}: HeadingProps) => (\n  <Heading\n    as=\"h1\"\n    className={cn(className, 'text-5xl font-display font-bold leading-tight')}\n    {...props}\n  />\n);\n\nexport const H2 = ({className, ...props}: HeadingProps) => (\n  <Heading\n    as=\"h2\"\n    className={cn(\n      'text-3xl font-display leading-10 text-primary dark:text-primary-dark font-bold my-6',\n      className\n    )}\n    {...props}\n  />\n);\n\nexport const H3 = ({className, ...props}: HeadingProps) => (\n  <Heading\n    as=\"h3\"\n    className={cn(\n      className,\n      'text-2xl font-display leading-9 text-primary dark:text-primary-dark font-bold my-6'\n    )}\n    {...props}\n  />\n);\n\nexport const H4 = ({className, ...props}: HeadingProps) => (\n  <Heading\n    as=\"h4\"\n    className={cn(className, 'text-xl font-display font-bold leading-9 my-4')}\n    {...props}\n  />\n);\n\nexport const H5 = ({className, ...props}: HeadingProps) => (\n  <Heading\n    as=\"h5\"\n    className={cn(className, 'text-lg font-display font-bold leading-9 my-2')}\n    {...props}\n  />\n);\n"
  },
  {
    "path": "src/components/MDX/InlineCode.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport cn from 'classnames';\nimport type {HTMLAttributes} from 'react';\n\ninterface InlineCodeProps {\n  isLink?: boolean;\n  meta?: string;\n}\nfunction InlineCode({\n  isLink,\n  ...props\n}: HTMLAttributes<HTMLElement> & InlineCodeProps) {\n  return (\n    <code\n      dir=\"ltr\" // This is needed to prevent the code from inheriting the RTL direction of <html> in case of RTL languages to avoid like `()console.log` to be rendered as `console.log()`\n      className={cn(\n        'inline text-code text-secondary dark:text-secondary-dark px-1 rounded-md no-underline',\n        {\n          'bg-gray-30 bg-opacity-10 py-px': !isLink,\n          'bg-highlight dark:bg-highlight-dark py-0': isLink,\n        }\n      )}\n      {...props}\n    />\n  );\n}\n\nexport default InlineCode;\n"
  },
  {
    "path": "src/components/MDX/Intro.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\n\nexport interface IntroProps {\n  children?: React.ReactNode;\n}\n\nfunction Intro({children}: IntroProps) {\n  return (\n    <div className=\"font-display text-xl text-primary dark:text-primary-dark leading-relaxed\">\n      {children}\n    </div>\n  );\n}\n\nexport default Intro;\n"
  },
  {
    "path": "src/components/MDX/LanguagesContext.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {createContext} from 'react';\n\nexport type LanguageItem = {\n  code: string;\n  name: string;\n  enName: string;\n};\nexport type Languages = Array<LanguageItem>;\n\nexport const LanguagesContext = createContext<Languages | null>(null);\n"
  },
  {
    "path": "src/components/MDX/Link.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {Children, cloneElement} from 'react';\nimport NextLink from 'next/link';\nimport cn from 'classnames';\n\nimport {ExternalLink} from 'components/ExternalLink';\n\nfunction Link({\n  href,\n  className,\n  children,\n  ...props\n}: React.AnchorHTMLAttributes<HTMLAnchorElement>) {\n  const classes =\n    'inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal';\n  const modifiedChildren = Children.toArray(children).map((child: any) => {\n    if (child.type?.mdxName && child.type?.mdxName === 'inlineCode') {\n      return cloneElement(child, {\n        isLink: true,\n      });\n    }\n    return child;\n  });\n\n  if (!href) {\n    // eslint-disable-next-line jsx-a11y/anchor-has-content\n    return <a href={href} className={className} {...props} />;\n  }\n\n  // Tweak external links (added by ja.react.dev team)\n  href = href\n    .replace(\n      'https://developer.mozilla.org/en-US/',\n      'https://developer.mozilla.org/'\n    )\n    .replace('https://reactjs.org/', 'https://ja.reactjs.org/')\n    .replace('https://legacy.reactjs.org/', 'https://ja.legacy.reactjs.org/');\n\n  return (\n    <>\n      {href.startsWith('https://') ? (\n        <ExternalLink href={href} className={cn(classes, className)} {...props}>\n          {modifiedChildren}\n        </ExternalLink>\n      ) : href.startsWith('#') ? (\n        // eslint-disable-next-line jsx-a11y/anchor-has-content\n        <a className={cn(classes, className)} href={href} {...props}>\n          {modifiedChildren}\n        </a>\n      ) : (\n        <NextLink href={href} className={cn(classes, className)} {...props}>\n          {modifiedChildren}\n        </NextLink>\n      )}\n    </>\n  );\n}\n\nexport default Link;\n"
  },
  {
    "path": "src/components/MDX/MDXComponents.module.css",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\n/* Stop purging. */\n.markdown p {\n  @apply mb-4 leading-7 whitespace-pre-wrap text-gray-70;\n}\n\n.markdown ol {\n  @apply mb-4 ms-8 list-decimal;\n}\n\n.markdown ul {\n  @apply mb-4 ms-8 list-disc;\n}\n\n.markdown h1 {\n  @apply mb-6 text-4xl font-extrabold tracking-tight;\n}\n\n.markdown h2 {\n  @apply mt-12 mb-4 text-3xl font-extrabold tracking-tight;\n}\n.markdown h3 {\n  @apply mt-8 mb-3 text-2xl font-extrabold tracking-tight;\n}\n.markdown h4 {\n  @apply mt-8 mb-3 text-xl font-extrabold tracking-tight;\n}\n\n.markdown code {\n  @apply text-gray-70 bg-card dark:bg-card-dark p-1 rounded-lg no-underline;\n  font-size: 90%;\n}\n\n.markdown li {\n  @apply mb-2;\n}\n\n.markdown a {\n  @apply inline text-link dark:text-link-dark break-normal border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal;\n}\n"
  },
  {
    "path": "src/components/MDX/MDXComponents.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {Children, useContext, useMemo} from 'react';\nimport * as React from 'react';\nimport cn from 'classnames';\nimport type {HTMLAttributes} from 'react';\n\nimport CodeBlock from './CodeBlock';\nimport {CodeDiagram} from './CodeDiagram';\nimport {ConsoleBlock, ConsoleLogLine, ConsoleBlockMulti} from './ConsoleBlock';\nimport ExpandableCallout from './ExpandableCallout';\nimport ExpandableExample from './ExpandableExample';\nimport {H1, H2, H3, H4, H5} from './Heading';\nimport InlineCode from './InlineCode';\nimport Intro from './Intro';\nimport BlogCard from './BlogCard';\nimport Link from './Link';\nimport {PackageImport} from './PackageImport';\nimport Recap from './Recap';\nimport Sandpack from './Sandpack';\nimport SandpackWithHTMLOutput from './SandpackWithHTMLOutput';\nimport Diagram from './Diagram';\nimport DiagramGroup from './DiagramGroup';\nimport SimpleCallout from './SimpleCallout';\nimport TerminalBlock from './TerminalBlock';\nimport YouWillLearnCard from './YouWillLearnCard';\nimport {Challenges, Hint, Solution} from './Challenges';\nimport {IconNavArrow} from '../Icon/IconNavArrow';\nimport ButtonLink from 'components/ButtonLink';\nimport {TocContext} from './TocContext';\nimport type {Toc, TocItem} from './TocContext';\nimport {TeamMember} from './TeamMember';\nimport {LanguagesContext} from './LanguagesContext';\nimport {finishedTranslations} from 'utils/finishedTranslations';\n\nimport ErrorDecoder from './ErrorDecoder';\nimport {IconCanary} from '../Icon/IconCanary';\nimport {IconExperimental} from 'components/Icon/IconExperimental';\n\nfunction CodeStep({children, step}: {children: any; step: number}) {\n  return (\n    <span\n      data-step={step}\n      className={cn(\n        'code-step bg-opacity-10 dark:bg-opacity-20 relative rounded px-[6px] py-[1.5px] border-b-[2px] border-opacity-60',\n        {\n          'bg-blue-40 border-blue-40 text-blue-60 dark:text-blue-30':\n            step === 1,\n          'bg-yellow-40 border-yellow-40 text-yellow-60 dark:text-yellow-30':\n            step === 2,\n          'bg-purple-40 border-purple-40 text-purple-60 dark:text-purple-30':\n            step === 3,\n          'bg-green-40 border-green-40 text-green-60 dark:text-green-30':\n            step === 4,\n        }\n      )}>\n      {children}\n    </span>\n  );\n}\n\nconst P = (p: HTMLAttributes<HTMLParagraphElement>) => (\n  <p className=\"whitespace-pre-wrap my-4\" {...p} />\n);\n\nconst Strong = (strong: HTMLAttributes<HTMLElement>) => (\n  <strong className=\"font-bold\" {...strong} />\n);\n\nconst OL = (p: HTMLAttributes<HTMLOListElement>) => (\n  <ol className=\"ms-6 my-3 list-decimal\" {...p} />\n);\nconst LI = (p: HTMLAttributes<HTMLLIElement>) => (\n  <li className=\"leading-relaxed mb-1\" {...p} />\n);\nconst UL = (p: HTMLAttributes<HTMLUListElement>) => (\n  <ul className=\"ms-6 my-3 list-disc\" {...p} />\n);\n\nconst Divider = () => (\n  <hr className=\"my-6 block border-b border-t-0 border-border dark:border-border-dark\" />\n);\nconst Wip = ({children}: {children: React.ReactNode}) => (\n  <ExpandableCallout type=\"wip\">{children}</ExpandableCallout>\n);\nconst Pitfall = ({children}: {children: React.ReactNode}) => (\n  <ExpandableCallout type=\"pitfall\">{children}</ExpandableCallout>\n);\nconst Deprecated = ({children}: {children: React.ReactNode}) => (\n  <ExpandableCallout type=\"deprecated\">{children}</ExpandableCallout>\n);\nconst Note = ({children}: {children: React.ReactNode}) => (\n  <ExpandableCallout type=\"note\">{children}</ExpandableCallout>\n);\n\nconst Canary = ({children}: {children: React.ReactNode}) => (\n  <ExpandableCallout type=\"canary\">{children}</ExpandableCallout>\n);\n\nconst RC = ({children}: {children: React.ReactNode}) => (\n  <ExpandableCallout type=\"rc\">{children}</ExpandableCallout>\n);\n\nconst Experimental = ({children}: {children: React.ReactNode}) => (\n  <ExpandableCallout type=\"experimental\">{children}</ExpandableCallout>\n);\n\nconst NextMajor = ({children}: {children: React.ReactNode}) => (\n  <ExpandableCallout type=\"major\">{children}</ExpandableCallout>\n);\n\nconst RSC = ({children}: {children: React.ReactNode}) => (\n  <ExpandableCallout type=\"rsc\">{children}</ExpandableCallout>\n);\n\nconst CanaryBadge = ({title}: {title: string}) => (\n  <span\n    title={title}\n    className={\n      'text-base font-display px-1 py-0.5 font-bold bg-gray-10 dark:bg-gray-60 text-gray-60 dark:text-gray-10 rounded'\n    }>\n    <IconCanary\n      size=\"s\"\n      className={'inline me-1 mb-0.5 text-sm text-gray-60 dark:text-gray-10'}\n    />\n    Canary only\n  </span>\n);\n\nconst ExperimentalBadge = ({title}: {title: string}) => (\n  <span\n    title={title}\n    className={\n      'text-base font-display px-1 py-0.5 font-bold bg-gray-10 dark:bg-gray-60 text-gray-60 dark:text-gray-10 rounded'\n    }>\n    <IconExperimental\n      size=\"s\"\n      className={'inline me-1 mb-0.5 text-sm text-gray-60 dark:text-gray-10'}\n    />\n    Experimental only\n  </span>\n);\n\nconst NextMajorBadge = ({title}: {title: string}) => (\n  <span\n    title={title}\n    className={\n      'text-base font-display px-2 py-0.5 font-bold bg-blue-10 dark:bg-blue-60 text-gray-60 dark:text-gray-10 rounded'\n    }>\n    React 19\n  </span>\n);\n\nconst RSCBadge = ({title}: {title: string}) => (\n  <span\n    title={title}\n    className={\n      'text-base font-display px-2 py-0.5 font-bold bg-blue-10 dark:bg-blue-50 text-gray-60 dark:text-gray-10 rounded'\n    }>\n    RSC\n  </span>\n);\n\nconst Blockquote = ({children, ...props}: HTMLAttributes<HTMLQuoteElement>) => {\n  return (\n    <blockquote\n      className=\"mdx-blockquote py-4 px-8 my-8 shadow-inner-border dark:shadow-inner-border-dark bg-highlight dark:bg-highlight-dark bg-opacity-50 rounded-2xl leading-6 flex relative\"\n      {...props}>\n      <span className=\"block relative\">{children}</span>\n    </blockquote>\n  );\n};\n\nfunction LearnMore({\n  children,\n  path,\n}: {\n  title: string;\n  path?: string;\n  children: any;\n}) {\n  return (\n    <>\n      <section className=\"p-8 mt-16 mb-16 flex flex-row shadow-inner-border dark:shadow-inner-border-dark justify-between items-center bg-card dark:bg-card-dark rounded-2xl\">\n        <div className=\"flex-col\">\n          <h2 className=\"text-primary font-display dark:text-primary-dark font-bold text-2xl leading-tight\">\n            Ready to learn this topic?\n          </h2>\n          {children}\n          {path ? (\n            <ButtonLink\n              className=\"mt-1\"\n              label=\"Read More\"\n              href={path}\n              type=\"primary\">\n              Read More\n              <IconNavArrow displayDirection=\"end\" className=\"inline ms-1\" />\n            </ButtonLink>\n          ) : null}\n        </div>\n      </section>\n      <hr className=\"border-border dark:border-border-dark mb-14\" />\n    </>\n  );\n}\n\nfunction ReadBlogPost({path}: {path: string}) {\n  return (\n    <ButtonLink className=\"mt-1\" label=\"Read Post\" href={path} type=\"primary\">\n      Read Post\n      <IconNavArrow displayDirection=\"end\" className=\"inline ms-1\" />\n    </ButtonLink>\n  );\n}\n\nfunction Math({children}: {children: any}) {\n  return (\n    <span\n      style={{\n        fontFamily: 'STIXGeneral-Regular, Georgia, serif',\n        fontSize: '1.2rem',\n      }}>\n      {children}\n    </span>\n  );\n}\n\nfunction MathI({children}: {children: any}) {\n  return (\n    <span\n      style={{\n        fontFamily: 'STIXGeneral-Italic, Georgia, serif',\n        fontSize: '1.2rem',\n      }}>\n      {children}\n    </span>\n  );\n}\n\nfunction YouWillLearn({\n  children,\n  isChapter,\n}: {\n  children: any;\n  isChapter?: boolean;\n}) {\n  let title = isChapter ? 'この章で学ぶこと' : 'このページで学ぶこと';\n  return <SimpleCallout title={title}>{children}</SimpleCallout>;\n}\n\n// TODO: typing.\nfunction Recipes(props: any) {\n  return <Challenges {...props} isRecipes={true} />;\n}\n\nfunction AuthorCredit({\n  author = 'Rachel Lee Nabors',\n  authorLink = 'https://nearestnabors.com/',\n}: {\n  author: string;\n  authorLink: string;\n}) {\n  return (\n    <div className=\"sr-only group-hover:not-sr-only group-focus-within:not-sr-only hover:sr-only\">\n      <p className=\"bg-card dark:bg-card-dark text-center text-sm text-secondary dark:text-secondary-dark leading-tight p-2 rounded-lg absolute start-1/2 -top-4 -translate-x-1/2 -translate-y-full group-hover:flex group-hover:opacity-100 after:content-[''] after:absolute after:start-1/2 after:top-[95%] after:-translate-x-1/2 after:border-8 after:border-x-transparent after:border-b-transparent after:border-t-card after:dark:border-t-card-dark opacity-0 transition-opacity duration-300\">\n        <cite>\n          Illustrated by{' '}\n          {authorLink ? (\n            <a\n              target=\"_blank\"\n              rel=\"noreferrer\"\n              className=\"text-link dark:text-link-dark\"\n              href={authorLink}>\n              {author}\n            </a>\n          ) : (\n            author\n          )}\n        </cite>\n      </p>\n    </div>\n  );\n}\n\nconst IllustrationContext = React.createContext<{\n  isInBlock?: boolean;\n}>({\n  isInBlock: false,\n});\n\nfunction Illustration({\n  caption,\n  src,\n  alt,\n  author,\n  authorLink,\n}: {\n  caption: string;\n  src: string;\n  alt: string;\n  author: string;\n  authorLink: string;\n}) {\n  const {isInBlock} = React.useContext(IllustrationContext);\n\n  return (\n    <div className=\"relative group before:absolute before:-inset-y-16 before:inset-x-0 my-16 mx-0 2xl:mx-auto max-w-4xl 2xl:max-w-6xl\">\n      <figure className=\"my-8 flex justify-center\">\n        <img\n          src={src}\n          alt={alt}\n          style={{maxHeight: 300}}\n          className=\"rounded-lg\"\n        />\n        {caption ? (\n          <figcaption className=\"text-center leading-tight mt-4\">\n            {caption}\n          </figcaption>\n        ) : null}\n      </figure>\n      {!isInBlock && <AuthorCredit author={author} authorLink={authorLink} />}\n    </div>\n  );\n}\n\nconst isInBlockTrue = {isInBlock: true};\n\nfunction IllustrationBlock({\n  sequential,\n  author,\n  authorLink,\n  children,\n}: {\n  author: string;\n  authorLink: string;\n  sequential: boolean;\n  children: any;\n}) {\n  const imageInfos = Children.toArray(children).map(\n    (child: any) => child.props\n  );\n  const images = imageInfos.map((info, index) => (\n    <figure key={index}>\n      <div className=\"bg-white rounded-lg p-4 flex-1 flex xl:p-6 justify-center items-center my-4\">\n        <img\n          className=\"text-primary\"\n          src={info.src}\n          alt={info.alt}\n          height={info.height}\n        />\n      </div>\n      {info.caption ? (\n        <figcaption className=\"text-secondary dark:text-secondary-dark text-center leading-tight mt-4\">\n          {info.caption}\n        </figcaption>\n      ) : null}\n    </figure>\n  ));\n  return (\n    <IllustrationContext value={isInBlockTrue}>\n      <div className=\"relative group before:absolute before:-inset-y-16 before:inset-x-0 my-16 mx-0 2xl:mx-auto max-w-4xl 2xl:max-w-6xl\">\n        {sequential ? (\n          <ol className=\"mdx-illustration-block flex\">\n            {images.map((x: any, i: number) => (\n              <li className=\"flex-1\" key={i}>\n                {x}\n              </li>\n            ))}\n          </ol>\n        ) : (\n          <div className=\"mdx-illustration-block\">{images}</div>\n        )}\n        <AuthorCredit author={author} authorLink={authorLink} />\n      </div>\n    </IllustrationContext>\n  );\n}\n\ntype NestedTocRoot = {\n  item: null;\n  children: Array<NestedTocNode>;\n};\n\ntype NestedTocNode = {\n  item: TocItem;\n  children: Array<NestedTocNode>;\n};\n\nfunction calculateNestedToc(toc: Toc): NestedTocRoot {\n  const currentAncestors = new Map<number, NestedTocNode | NestedTocRoot>();\n  const root: NestedTocRoot = {\n    item: null,\n    children: [],\n  };\n  const startIndex = 1; // Skip \"Overview\"\n  for (let i = startIndex; i < toc.length; i++) {\n    const item = toc[i];\n    const currentParent: NestedTocNode | NestedTocRoot =\n      currentAncestors.get(item.depth - 1) || root;\n    const node: NestedTocNode = {\n      item,\n      children: [],\n    };\n    currentParent.children.push(node);\n    currentAncestors.set(item.depth, node);\n  }\n  return root;\n}\n\nfunction InlineToc() {\n  const toc = useContext(TocContext);\n  const root = useMemo(() => calculateNestedToc(toc), [toc]);\n  if (root.children.length < 2) {\n    return null;\n  }\n  return <InlineTocItem items={root.children} />;\n}\n\nfunction InlineTocItem({items}: {items: Array<NestedTocNode>}) {\n  return (\n    <UL>\n      {items.map((node) => (\n        <LI key={node.item.url}>\n          <Link href={node.item.url}>{node.item.text}</Link>\n          {node.children.length > 0 && <InlineTocItem items={node.children} />}\n        </LI>\n      ))}\n    </UL>\n  );\n}\n\ntype TranslationProgress = 'complete' | 'in-progress';\n\nfunction LanguageList({progress}: {progress: TranslationProgress}) {\n  const allLanguages = React.useContext(LanguagesContext) ?? [];\n  const languages = allLanguages\n    .filter(\n      ({code}) =>\n        code !== 'en' &&\n        (progress === 'complete'\n          ? finishedTranslations.includes(code)\n          : !finishedTranslations.includes(code))\n    )\n    .sort((a, b) => a.enName.localeCompare(b.enName));\n  return (\n    <UL>\n      {languages.map(({code, name, enName}) => {\n        return (\n          <LI key={code}>\n            <Link href={`https://${code}.react.dev/`}>\n              {enName} ({name})\n            </Link>{' '}\n            &mdash;{' '}\n            <Link href={`https://github.com/reactjs/${code}.react.dev`}>\n              Contribute\n            </Link>\n          </LI>\n        );\n      })}\n    </UL>\n  );\n}\n\nfunction YouTubeIframe(props: any) {\n  return (\n    <div className=\"relative h-0 overflow-hidden pt-[56.25%]\">\n      <iframe\n        className=\"absolute inset-0 w-full h-full\"\n        frameBorder=\"0\"\n        allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n        allowFullScreen\n        title=\"YouTube video player\"\n        {...props}\n      />\n    </div>\n  );\n}\n\nfunction Image(props: any) {\n  const {alt, ...rest} = props;\n  return <img alt={alt} className=\"max-w-[calc(min(700px,100%))]\" {...rest} />;\n}\n\nexport const MDXComponents = {\n  p: P,\n  strong: Strong,\n  blockquote: Blockquote,\n  ol: OL,\n  ul: UL,\n  li: LI,\n  h1: H1,\n  h2: H2,\n  h3: H3,\n  h4: H4,\n  h5: H5,\n  hr: Divider,\n  a: Link,\n  img: Image,\n  BlogCard,\n  code: InlineCode,\n  pre: CodeBlock,\n  CodeDiagram,\n  ConsoleBlock,\n  ConsoleBlockMulti,\n  ConsoleLogLine,\n  DeepDive: (props: {\n    children: React.ReactNode;\n    title: string;\n    excerpt: string;\n  }) => <ExpandableExample {...props} type=\"DeepDive\" />,\n  Diagram,\n  DiagramGroup,\n  FullWidth({children}: {children: any}) {\n    return children;\n  },\n  MaxWidth({children}: {children: any}) {\n    return <div className=\"max-w-4xl ms-0 2xl:mx-auto\">{children}</div>;\n  },\n  Pitfall,\n  Deprecated,\n  Wip,\n  Illustration,\n  IllustrationBlock,\n  Intro,\n  InlineToc,\n  LanguageList,\n  LearnMore,\n  Math,\n  MathI,\n  Note,\n  RC,\n  Canary,\n  Experimental,\n  ExperimentalBadge,\n  CanaryBadge,\n  NextMajor,\n  NextMajorBadge,\n  RSC,\n  RSCBadge,\n  PackageImport,\n  ReadBlogPost,\n  Recap,\n  Recipes,\n  Sandpack,\n  SandpackWithHTMLOutput,\n  TeamMember,\n  TerminalBlock,\n  YouWillLearn,\n  YouWillLearnCard,\n  Challenges,\n  Hint,\n  Solution,\n  CodeStep,\n  YouTubeIframe,\n  ErrorDecoder,\n};\n\nfor (let key in MDXComponents) {\n  if (MDXComponents.hasOwnProperty(key)) {\n    const MDXComponent: any = (MDXComponents as any)[key];\n    MDXComponent.mdxName = key;\n  }\n}\n"
  },
  {
    "path": "src/components/MDX/PackageImport.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {Children} from 'react';\nimport * as React from 'react';\nimport CodeBlock from './CodeBlock';\n\ninterface PackageImportProps {\n  children: React.ReactNode;\n}\n\nexport function PackageImport({children}: PackageImportProps) {\n  const terminal = Children.toArray(children).filter((child: any) => {\n    return child.type?.mdxName !== 'pre';\n  });\n  const code = Children.toArray(children).map((child: any, i: number) => {\n    if (child.type?.mdxName === 'pre') {\n      return (\n        <CodeBlock\n          {...child.props}\n          isFromPackageImport\n          key={i}\n          noMargin={true}\n          noMarkers={true}\n        />\n      );\n    } else {\n      return null;\n    }\n  });\n  return (\n    <section className=\"my-8 grid grid-cols-1 lg:grid-cols-2 gap-x-8 gap-y-4\">\n      <div className=\"flex flex-col justify-center\">{terminal}</div>\n      <div className=\"flex flex-col justify-center\">{code}</div>\n    </section>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/Recap.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nimport {H2} from './Heading';\n\ninterface RecapProps {\n  children: React.ReactNode;\n}\n\nfunction Recap({children}: RecapProps) {\n  return (\n    <section>\n      <H2 isPageAnchor id=\"recap\">\n        まとめ\n      </H2>\n      {children}\n    </section>\n  );\n}\n\nexport default Recap;\n"
  },
  {
    "path": "src/components/MDX/Sandpack/ClearButton.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nimport {IconClose} from '../../Icon/IconClose';\nexport interface ClearButtonProps {\n  onClear: () => void;\n}\n\nexport function ClearButton({onClear}: ClearButtonProps) {\n  return (\n    <button\n      className=\"text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1\"\n      onClick={onClear}\n      title=\"Clear all edits and reload sandbox\"\n      type=\"button\">\n      <IconClose className=\"inline mx-1 relative\" />\n      <span className=\"hidden md:block\">Clear</span>\n    </button>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/Sandpack/Console.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\nimport cn from 'classnames';\nimport {useState, useRef, useEffect} from 'react';\nimport {IconChevron} from 'components/Icon/IconChevron';\n\nimport {\n  SandpackCodeViewer,\n  useSandpack,\n} from '@codesandbox/sandpack-react/unstyled';\nimport type {SandpackMessageConsoleMethods} from '@codesandbox/sandpack-client';\n\nconst getType = (\n  message: SandpackMessageConsoleMethods\n): 'info' | 'warning' | 'error' => {\n  if (message === 'log' || message === 'info') {\n    return 'info';\n  }\n\n  if (message === 'warn') {\n    return 'warning';\n  }\n\n  return 'error';\n};\n\nconst getColor = (message: SandpackMessageConsoleMethods): string => {\n  if (message === 'warn') {\n    return 'text-yellow-50';\n  } else if (message === 'error') {\n    return 'text-red-40';\n  } else {\n    return 'text-secondary dark:text-secondary-dark';\n  }\n};\n\n// based on https://github.com/tmpfs/format-util/blob/0e62d430efb0a1c51448709abd3e2406c14d8401/format.js#L1\n// based on https://developer.mozilla.org/en-US/docs/Web/API/console#Using_string_substitutions\n// Implements s, d, i and f placeholders\nfunction formatStr(...inputArgs: any[]): any[] {\n  const maybeMessage = inputArgs[0];\n  if (typeof maybeMessage !== 'string') {\n    return inputArgs;\n  }\n  // If the first argument is a string, check for substitutions.\n  const args = inputArgs.slice(1);\n  let formatted: string = String(maybeMessage);\n  if (args.length) {\n    const REGEXP = /(%?)(%([jds]))/g;\n\n    formatted = formatted.replace(REGEXP, (match, escaped, ptn, flag) => {\n      let arg = args.shift();\n      switch (flag) {\n        case 's':\n          arg += '';\n          break;\n        case 'd':\n        case 'i':\n          arg = parseInt(arg, 10).toString();\n          break;\n        case 'f':\n          arg = parseFloat(arg).toString();\n          break;\n      }\n      if (!escaped) {\n        return arg;\n      }\n      args.unshift(arg);\n      return match;\n    });\n  }\n\n  // Arguments that remain after formatting.\n  if (args.length) {\n    for (let i = 0; i < args.length; i++) {\n      formatted += ' ' + String(args[i]);\n    }\n  }\n\n  // Update escaped %% values.\n  return [formatted.replace(/%{2,2}/g, '%')];\n}\n\ntype ConsoleData = Array<{\n  data: Array<string | Record<string, string>>;\n  id: string;\n  method: SandpackMessageConsoleMethods;\n}>;\n\nconst MAX_MESSAGE_COUNT = 100;\n\nexport const SandpackConsole = ({visible}: {visible: boolean}) => {\n  const {listen} = useSandpack();\n  const [logs, setLogs] = useState<ConsoleData>([]);\n  const wrapperRef = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    let isActive = true;\n    const unsubscribe = listen((message) => {\n      if (!isActive) {\n        console.warn('Received an unexpected log from Sandpack.');\n        return;\n      }\n      if (\n        (message.type === 'start' && message.firstLoad) ||\n        message.type === 'refresh'\n      ) {\n        setLogs([]);\n      }\n      if (message.type === 'console' && message.codesandbox) {\n        setLogs((prev) => {\n          const newLogs = message.log\n            .filter((consoleData) => {\n              if (!consoleData.method) {\n                return false;\n              }\n              if (\n                typeof consoleData.data[0] === 'string' &&\n                consoleData.data[0].indexOf('The above error occurred') !== -1\n              ) {\n                // Don't show React error addendum because\n                // we have a custom error overlay.\n                return false;\n              }\n              return true;\n            })\n            .map((consoleData) => {\n              return {\n                ...consoleData,\n                data: formatStr(...consoleData.data),\n              };\n            });\n          let messages = [...prev, ...newLogs];\n          while (messages.length > MAX_MESSAGE_COUNT) {\n            messages.shift();\n          }\n          return messages;\n        });\n      }\n    });\n\n    return () => {\n      unsubscribe();\n      isActive = false;\n    };\n  }, [listen]);\n\n  const [isExpanded, setIsExpanded] = useState(true);\n\n  useEffect(() => {\n    if (wrapperRef.current) {\n      wrapperRef.current.scrollTop = wrapperRef.current.scrollHeight;\n    }\n  }, [logs]);\n\n  if (!visible || logs.length === 0) {\n    return null;\n  }\n\n  return (\n    <div className=\"absolute dark:border-gray-700 bg-white dark:bg-gray-95 border-t bottom-0 w-full dark:text-white\">\n      <div className=\"flex justify-between\">\n        <button\n          className=\"flex items-center p-1\"\n          onClick={() => setIsExpanded(!isExpanded)}>\n          <IconChevron displayDirection={isExpanded ? 'down' : 'right'} />\n          <span className=\"ps-1 text-sm\">Console ({logs.length})</span>\n        </button>\n        <button\n          className=\"p-1\"\n          onClick={() => {\n            setLogs([]);\n          }}>\n          <svg\n            viewBox=\"0 0 24 24\"\n            width=\"18\"\n            height=\"18\"\n            stroke=\"currentColor\"\n            strokeWidth=\"2\"\n            fill=\"none\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\">\n            <circle cx=\"12\" cy=\"12\" r=\"10\"></circle>\n            <line x1=\"4.93\" y1=\"4.93\" x2=\"19.07\" y2=\"19.07\"></line>\n          </svg>\n        </button>\n      </div>\n      {isExpanded && (\n        <div className=\"w-full h-full border-t bg-white dark:border-gray-700 dark:bg-gray-95 min-h-[28px] console\">\n          <div className=\"max-h-40 h-auto overflow-auto\" ref={wrapperRef}>\n            {logs.map(({data, id, method}) => {\n              return (\n                <div\n                  key={id}\n                  className={cn(\n                    'first:border-none border-t dark:border-gray-700 text-md p-1 ps-2 leading-6 font-mono min-h-[32px] whitespace-pre-wrap',\n                    `console-${getType(method)}`,\n                    getColor(method)\n                  )}>\n                  <span className=\"console-message\">\n                    {data.map((msg, index) => {\n                      if (typeof msg === 'string') {\n                        return <span key={`${msg}-${index}`}>{msg}</span>;\n                      }\n\n                      let children;\n                      if (msg != null && typeof msg['@t'] === 'string') {\n                        // CodeSandbox wraps custom types\n                        children = msg['@t'];\n                      } else {\n                        try {\n                          children = JSON.stringify(msg, null, 2);\n                        } catch (error) {\n                          try {\n                            children = Object.prototype.toString.call(msg);\n                          } catch (err) {\n                            children = '[' + typeof msg + ']';\n                          }\n                        }\n                      }\n\n                      return (\n                        <span\n                          className={cn('console-span')}\n                          key={`${msg}-${index}`}>\n                          <SandpackCodeViewer\n                            initMode=\"user-visible\"\n                            showTabs={false}\n                            // fileType=\"js\"\n                            code={children}\n                          />\n                        </span>\n                      );\n                    })}\n                  </span>\n                </div>\n              );\n            })}\n          </div>\n        </div>\n      )}\n    </div>\n  );\n};\n"
  },
  {
    "path": "src/components/MDX/Sandpack/CustomPreset.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\nimport {memo, useRef, useState} from 'react';\nimport {flushSync} from 'react-dom';\nimport {\n  useSandpack,\n  useActiveCode,\n  SandpackCodeEditor,\n  SandpackLayout,\n} from '@codesandbox/sandpack-react/unstyled';\nimport cn from 'classnames';\n\nimport {IconChevron} from 'components/Icon/IconChevron';\nimport {NavigationBar} from './NavigationBar';\nimport {Preview} from './Preview';\n\nimport {useSandpackLint} from './useSandpackLint';\n\nexport const CustomPreset = memo(function CustomPreset({\n  providedFiles,\n}: {\n  providedFiles: Array<string>;\n}) {\n  const {lintErrors, lintExtensions} = useSandpackLint();\n  const {sandpack} = useSandpack();\n  const {code} = useActiveCode();\n  const {activeFile} = sandpack;\n  const lineCountRef = useRef<{[key: string]: number}>({});\n  if (!lineCountRef.current[activeFile]) {\n    // eslint-disable-next-line react-compiler/react-compiler\n    lineCountRef.current[activeFile] = code.split('\\n').length;\n  }\n  const lineCount = lineCountRef.current[activeFile];\n  const isExpandable = lineCount > 16;\n  return (\n    <SandboxShell\n      providedFiles={providedFiles}\n      lintErrors={lintErrors}\n      lintExtensions={lintExtensions}\n      isExpandable={isExpandable}\n    />\n  );\n});\n\nconst SandboxShell = memo(function SandboxShell({\n  providedFiles,\n  lintErrors,\n  lintExtensions,\n  isExpandable,\n}: {\n  providedFiles: Array<string>;\n  lintErrors: Array<any>;\n  lintExtensions: Array<any>;\n  isExpandable: boolean;\n}) {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const [isExpanded, setIsExpanded] = useState(false);\n  return (\n    <>\n      <div\n        className=\"shadow-lg dark:shadow-lg-dark rounded-lg\"\n        ref={containerRef}\n        style={{\n          contain: 'content',\n        }}>\n        <NavigationBar providedFiles={providedFiles} />\n        <SandpackLayout\n          className={cn(\n            !(isExpandable || isExpanded) && 'rounded-b-lg overflow-hidden',\n            isExpanded && 'sp-layout-expanded'\n          )}>\n          <Editor lintExtensions={lintExtensions} />\n          <Preview\n            className=\"order-last xl:order-2\"\n            isExpanded={isExpanded}\n            lintErrors={lintErrors}\n          />\n          {(isExpandable || isExpanded) && (\n            <button\n              translate=\"yes\"\n              className=\"sandpack-expand flex text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 p-1 w-full order-2 xl:order-last border-b-1 relative top-0\"\n              onClick={() => {\n                const nextIsExpanded = !isExpanded;\n                flushSync(() => {\n                  setIsExpanded(nextIsExpanded);\n                });\n                if (!nextIsExpanded && containerRef.current !== null) {\n                  // @ts-ignore\n                  if (containerRef.current.scrollIntoViewIfNeeded) {\n                    // @ts-ignore\n                    containerRef.current.scrollIntoViewIfNeeded();\n                  } else {\n                    containerRef.current.scrollIntoView({\n                      block: 'nearest',\n                      inline: 'nearest',\n                    });\n                  }\n                }\n              }}>\n              <span className=\"flex p-2 focus:outline-none text-primary dark:text-primary-dark leading-[20px]\">\n                <IconChevron\n                  className=\"inline me-1.5 text-xl\"\n                  displayDirection={isExpanded ? 'up' : 'down'}\n                />\n                {isExpanded ? 'Show less' : 'Show more'}\n              </span>\n            </button>\n          )}\n        </SandpackLayout>\n      </div>\n    </>\n  );\n});\n\nconst Editor = memo(function Editor({\n  lintExtensions,\n}: {\n  lintExtensions: Array<any>;\n}) {\n  return (\n    <SandpackCodeEditor\n      showLineNumbers\n      showInlineErrors\n      showTabs={false}\n      showRunButton={false}\n      extensions={lintExtensions}\n    />\n  );\n});\n"
  },
  {
    "path": "src/components/MDX/Sandpack/DownloadButton.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {useSyncExternalStore} from 'react';\nimport {useSandpack} from '@codesandbox/sandpack-react/unstyled';\nimport {IconDownload} from '../../Icon/IconDownload';\nimport {AppJSPath, StylesCSSPath, SUPPORTED_FILES} from './createFileMap';\nexport interface DownloadButtonProps {}\n\nlet supportsImportMap = false;\n\nfunction subscribe(cb: () => void) {\n  // This shouldn't actually need to update, but this works around\n  // https://github.com/facebook/react/issues/26095\n  let timeout = setTimeout(() => {\n    supportsImportMap =\n      (HTMLScriptElement as any).supports &&\n      (HTMLScriptElement as any).supports('importmap');\n    cb();\n  }, 0);\n  return () => clearTimeout(timeout);\n}\n\nfunction useSupportsImportMap() {\n  function getCurrentValue() {\n    return supportsImportMap;\n  }\n  function getServerSnapshot() {\n    return false;\n  }\n\n  return useSyncExternalStore(subscribe, getCurrentValue, getServerSnapshot);\n}\n\nexport function DownloadButton({\n  providedFiles,\n}: {\n  providedFiles: Array<string>;\n}) {\n  const {sandpack} = useSandpack();\n  const supported = useSupportsImportMap();\n  if (!supported) {\n    return null;\n  }\n  if (providedFiles.some((file) => !SUPPORTED_FILES.includes(file))) {\n    return null;\n  }\n\n  const downloadHTML = () => {\n    const css = sandpack.files[StylesCSSPath]?.code ?? '';\n    const code = sandpack.files[AppJSPath]?.code ?? '';\n    const blob = new Blob([\n      `<!DOCTYPE html>\n<html>\n<body>\n  <div id=\"root\"></div>\n</body>\n<!-- This setup is not suitable for production. -->\n<!-- Only use it in development! -->\n<script src=\"https://unpkg.com/@babel/standalone/babel.min.js\"></script>\n<script async src=\"https://ga.jspm.io/npm:es-module-shims@1.7.0/dist/es-module-shims.js\"></script>\n<script type=\"importmap\">\n{\n  \"imports\": {\n    \"react\": \"https://esm.sh/react?dev\",\n    \"react-dom/client\": \"https://esm.sh/react-dom/client?dev\"\n  }\n}\n</script>\n<script type=\"text/babel\" data-type=\"module\">\nimport React, { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\n\n${code.replace('export default ', 'let App = ')}\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);\n</script>\n<style>\n${css}\n</style>\n</html>`,\n    ]);\n    const url = window.URL.createObjectURL(blob);\n    const a = document.createElement('a');\n    a.style.display = 'none';\n    a.href = url;\n    a.download = 'sandbox.html';\n    document.body.appendChild(a);\n    a.click();\n    window.URL.revokeObjectURL(url);\n  };\n\n  return (\n    <button\n      className=\"text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1\"\n      onClick={downloadHTML}\n      title=\"Download Sandbox\"\n      type=\"button\">\n      <IconDownload className=\"inline me-1\" /> Download\n    </button>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/Sandpack/ErrorMessage.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\ninterface ErrorType {\n  title?: string;\n  message: string;\n  column?: number;\n  line?: number;\n  path?: string;\n}\n\nexport function ErrorMessage({error, ...props}: {error: ErrorType}) {\n  const {message, title} = error;\n\n  return (\n    <div className=\"bg-white border-2 border-red-40 rounded-lg p-6\" {...props}>\n      <h2 className=\"text-red-40 text-xl mb-4\">{title || 'Error'}</h2>\n      <pre className=\"text-secondary whitespace-pre-wrap break-words leading-tight\">\n        {message}\n      </pre>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/Sandpack/LoadingOverlay.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport {useState} from 'react';\n\nimport {\n  LoadingOverlayState,\n  OpenInCodeSandboxButton,\n  useSandpack,\n} from '@codesandbox/sandpack-react/unstyled';\nimport {useEffect} from 'react';\n\nconst FADE_ANIMATION_DURATION = 200;\n\nexport const LoadingOverlay = ({\n  clientId,\n  dependenciesLoading,\n  forceLoading,\n}: {\n  clientId: string;\n  dependenciesLoading: boolean;\n  forceLoading: boolean;\n} & React.HTMLAttributes<HTMLDivElement>): React.ReactNode | null => {\n  const loadingOverlayState = useLoadingOverlayState(\n    clientId,\n    dependenciesLoading,\n    forceLoading\n  );\n\n  if (loadingOverlayState === 'HIDDEN') {\n    return null;\n  }\n\n  if (loadingOverlayState === 'TIMEOUT') {\n    return (\n      <div className=\"sp-overlay sp-error\">\n        <div className=\"sp-error-message\">\n          Unable to establish connection with the sandpack bundler. Make sure\n          you are online or try again later. If the problem persists, please\n          report it via{' '}\n          <a\n            className=\"sp-error-message\"\n            href=\"mailto:hello@codesandbox.io?subject=Sandpack Timeout Error\">\n            email\n          </a>{' '}\n          or submit an issue on{' '}\n          <a\n            className=\"sp-error-message\"\n            href=\"https://github.com/codesandbox/sandpack/issues\"\n            rel=\"noreferrer noopener\"\n            target=\"_blank\">\n            GitHub.\n          </a>\n        </div>\n      </div>\n    );\n  }\n\n  const stillLoading =\n    loadingOverlayState === 'LOADING' || loadingOverlayState === 'PRE_FADING';\n\n  return (\n    <div\n      className=\"sp-overlay sp-loading\"\n      style={{\n        opacity: stillLoading ? 1 : 0,\n        transition: `opacity ${FADE_ANIMATION_DURATION}ms ease-out`,\n      }}>\n      <div className=\"sp-cube-wrapper\" title=\"Open in CodeSandbox\">\n        {/* @ts-ignore: the OpenInCodeSandboxButton type from '@codesandbox/sandpack-react/unstyled' is incompatible with JSX in React 19 */}\n        <OpenInCodeSandboxButton />\n        <div className=\"sp-cube\">\n          <div className=\"sp-sides\">\n            <div className=\"top\" />\n            <div className=\"right\" />\n            <div className=\"bottom\" />\n            <div className=\"left\" />\n            <div className=\"front\" />\n            <div className=\"back\" />\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nconst useLoadingOverlayState = (\n  clientId: string,\n  dependenciesLoading: boolean,\n  forceLoading: boolean\n): LoadingOverlayState => {\n  const {sandpack, listen} = useSandpack();\n  const [state, setState] = useState<LoadingOverlayState>('HIDDEN');\n\n  if (state !== 'LOADING' && forceLoading) {\n    setState('LOADING');\n  }\n\n  /**\n   * Sandpack listener\n   */\n  const sandpackIdle = sandpack.status === 'idle';\n  useEffect(() => {\n    const unsubscribe = listen((message) => {\n      if (message.type === 'done') {\n        setState((prev) => {\n          return prev === 'LOADING' ? 'PRE_FADING' : 'HIDDEN';\n        });\n      }\n    }, clientId);\n\n    return () => {\n      unsubscribe();\n    };\n  }, [listen, clientId, sandpackIdle]);\n\n  /**\n   * Fading transient state\n   */\n  useEffect(() => {\n    let fadeTimeout: ReturnType<typeof setTimeout>;\n\n    if (state === 'PRE_FADING' && !dependenciesLoading) {\n      setState('FADING');\n    } else if (state === 'FADING') {\n      fadeTimeout = setTimeout(\n        () => setState('HIDDEN'),\n        FADE_ANIMATION_DURATION\n      );\n    }\n\n    return () => {\n      clearTimeout(fadeTimeout);\n    };\n  }, [state, dependenciesLoading]);\n\n  if (sandpack.status === 'timeout') {\n    return 'TIMEOUT';\n  }\n\n  if (sandpack.status !== 'running') {\n    return 'HIDDEN';\n  }\n\n  return state;\n};\n"
  },
  {
    "path": "src/components/MDX/Sandpack/NavigationBar.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {\n  useRef,\n  useInsertionEffect,\n  useCallback,\n  useState,\n  useEffect,\n  Fragment,\n} from 'react';\nimport cn from 'classnames';\nimport {\n  FileTabs,\n  useSandpack,\n  useSandpackNavigation,\n} from '@codesandbox/sandpack-react/unstyled';\nimport {OpenInCodeSandboxButton} from './OpenInCodeSandboxButton';\nimport {ReloadButton} from './ReloadButton';\nimport {ClearButton} from './ClearButton';\nimport {DownloadButton} from './DownloadButton';\nimport {IconChevron} from '../../Icon/IconChevron';\nimport {Listbox} from '@headlessui/react';\nimport {OpenInTypeScriptPlaygroundButton} from './OpenInTypeScriptPlayground';\n\nexport function useEvent(fn: any): any {\n  const ref = useRef(null);\n  useInsertionEffect(() => {\n    ref.current = fn;\n  }, [fn]);\n  return useCallback((...args: any) => {\n    const f = ref.current!;\n    // @ts-ignore\n    return f(...args);\n  }, []);\n}\n\nconst getFileName = (filePath: string): string => {\n  const lastIndexOfSlash = filePath.lastIndexOf('/');\n  return filePath.slice(lastIndexOfSlash + 1);\n};\n\nexport function NavigationBar({providedFiles}: {providedFiles: Array<string>}) {\n  const {sandpack} = useSandpack();\n  const containerRef = useRef<HTMLDivElement | null>(null);\n  const tabsRef = useRef<HTMLDivElement | null>(null);\n  // By default, show the dropdown because all tabs may not fit.\n  // We don't know whether they'll fit or not until after hydration:\n  const [showDropdown, setShowDropdown] = useState(true);\n  const {activeFile, setActiveFile, visibleFiles, clients} = sandpack;\n  const clientId = Object.keys(clients)[0];\n  const {refresh} = useSandpackNavigation(clientId);\n  const isMultiFile = visibleFiles.length > 1;\n  const hasJustToggledDropdown = useRef(false);\n\n  // Keep track of whether we can show all tabs or just the dropdown.\n  const onContainerResize = useEvent((containerWidth: number) => {\n    if (hasJustToggledDropdown.current === true) {\n      // Ignore changes likely caused by ourselves.\n      hasJustToggledDropdown.current = false;\n      return;\n    }\n    if (tabsRef.current === null) {\n      // Some ResizeObserver calls come after unmount.\n      return;\n    }\n    const tabsWidth = tabsRef.current.getBoundingClientRect().width;\n    const needsDropdown = tabsWidth >= containerWidth;\n    if (needsDropdown !== showDropdown) {\n      hasJustToggledDropdown.current = true;\n      setShowDropdown(needsDropdown);\n    }\n  });\n\n  useEffect(() => {\n    if (isMultiFile) {\n      const resizeObserver = new ResizeObserver((entries) => {\n        for (const entry of entries) {\n          if (entry.contentBoxSize) {\n            const contentBoxSize = Array.isArray(entry.contentBoxSize)\n              ? entry.contentBoxSize[0]\n              : entry.contentBoxSize;\n            const width = contentBoxSize.inlineSize;\n            onContainerResize(width);\n          }\n        }\n      });\n      const container = containerRef.current!;\n      resizeObserver.observe(container);\n      return () => resizeObserver.unobserve(container);\n    } else {\n      return;\n    }\n\n    // Note: in a real useEvent, onContainerResize would be omitted.\n  }, [isMultiFile, onContainerResize]);\n\n  const handleClear = () => {\n    /**\n     * resetAllFiles must come first, otherwise\n     * the previous content will appear for a second\n     * when the iframe loads.\n     *\n     * Plus, it should only prompt if there's any file changes\n     */\n    if (sandpack.editorState === 'dirty' && confirm('Clear all your edits?')) {\n      sandpack.resetAllFiles();\n    }\n    refresh();\n  };\n\n  const handleReload = () => {\n    refresh();\n  };\n\n  return (\n    <div className=\"bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg text-lg\">\n      {/* If Prettier reformats this block, the two @ts-ignore directives will no longer be adjacent to the problematic lines, causing TypeScript errors */}\n      {/* prettier-ignore */}\n      <div className=\"flex-1 grow min-w-0 px-4 lg:px-6\">\n        {/* @ts-ignore: the Listbox type from '@headlessui/react' is incompatible with JSX in React 19 */}\n        <Listbox value={activeFile} onChange={setActiveFile}>\n          <div ref={containerRef}>\n            <div className=\"relative overflow-hidden\">\n              <div\n                ref={tabsRef}\n                className={cn(\n                  // The container for all tabs is always in the DOM, but\n                  // not always visible. This lets us measure how much space\n                  // the tabs would take if displayed. We use this to decide\n                  // whether to keep showing the dropdown, or show all tabs.\n                  'w-[fit-content]',\n                  showDropdown ? 'invisible' : ''\n                )}>\n                {/* @ts-ignore: the FileTabs type from '@codesandbox/sandpack-react/unstyled' is incompatible with JSX in React 19 */}\n                <FileTabs />\n              </div>\n              {/* @ts-ignore: the Listbox type from '@headlessui/react' is incompatible with JSX in React 19 */}\n              <Listbox.Button as={Fragment}>\n                {({open}) => (\n                  // If tabs don't fit, display the dropdown instead.\n                  // The dropdown is absolutely positioned inside the\n                  // space that's taken by the (invisible) tab list.\n                  <button\n                    className={cn(\n                      'absolute top-0 start-[2px]',\n                      !showDropdown && 'invisible'\n                    )}>\n                    <span\n                      className={cn(\n                        'h-full py-2 px-1 mt-px -mb-px flex border-b text-link dark:text-link-dark border-link dark:border-link-dark items-center text-md leading-tight truncate'\n                      )}\n                      style={{maxWidth: '160px'}}>\n                      {getFileName(activeFile)}\n                      {isMultiFile && (\n                        <span className=\"ms-2\">\n                          <IconChevron\n                            displayDirection={open ? 'up' : 'down'}\n                          />\n                        </span>\n                      )}\n                    </span>\n                  </button>\n                )}\n              </Listbox.Button>\n            </div>\n          </div>\n          {/* @ts-ignore: the Listbox type from '@headlessui/react' is incompatible with JSX in React 19 */}\n          {isMultiFile && showDropdown && (<Listbox.Options className=\"absolute mt-0.5 bg-card dark:bg-card-dark px-2 inset-x-0 mx-0 rounded-b-lg border-1 border-border dark:border-border-dark rounded-sm shadow-md\">\n              {/* @ts-ignore: the Listbox type from '@headlessui/react' is incompatible with JSX in React 19 */}\n              {visibleFiles.map((filePath: string) => (<Listbox.Option key={filePath} value={filePath} as={Fragment}>\n                  {({active}) => (\n                    <li\n                      className={cn(\n                        'text-md mx-2 my-4 cursor-pointer',\n                        active && 'text-link dark:text-link-dark'\n                      )}>\n                      {getFileName(filePath)}\n                    </li>\n                  )}\n                </Listbox.Option>\n              ))}\n            </Listbox.Options>\n          )}\n        </Listbox>\n      </div>\n      <div\n        className=\"px-3 flex items-center justify-end text-start\"\n        translate=\"yes\">\n        <DownloadButton providedFiles={providedFiles} />\n        <ReloadButton onReload={handleReload} />\n        <ClearButton onClear={handleClear} />\n        <OpenInCodeSandboxButton />\n        {activeFile.endsWith('.tsx') && (\n          <OpenInTypeScriptPlaygroundButton\n            content={sandpack.files[activeFile]?.code || ''}\n          />\n        )}\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/Sandpack/OpenInCodeSandboxButton.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {UnstyledOpenInCodeSandboxButton} from '@codesandbox/sandpack-react/unstyled';\nimport {IconNewPage} from '../../Icon/IconNewPage';\n\nexport const OpenInCodeSandboxButton = () => {\n  return (\n    <UnstyledOpenInCodeSandboxButton\n      className=\"text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ms-2 md:ms-1\"\n      title=\"Open in CodeSandbox\">\n      <IconNewPage\n        className=\"inline mx-1 relative top-[1px]\"\n        width=\"1em\"\n        height=\"1em\"\n      />\n      <span className=\"hidden md:block\">Fork</span>\n    </UnstyledOpenInCodeSandboxButton>\n  );\n};\n"
  },
  {
    "path": "src/components/MDX/Sandpack/OpenInTypeScriptPlayground.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {IconNewPage} from '../../Icon/IconNewPage';\n\nexport const OpenInTypeScriptPlaygroundButton = (props: {content: string}) => {\n  const contentWithReactImport = `import * as React from 'react';\\n\\n${props.content}`;\n  return (\n    <a\n      className=\"text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ml-2 md:ml-1\"\n      href={`https://www.typescriptlang.org/play#src=${encodeURIComponent(\n        contentWithReactImport\n      )}`}\n      title=\"Open in TypeScript Playground\"\n      target=\"_blank\"\n      rel=\"noreferrer\">\n      <IconNewPage\n        className=\"inline mx-1 relative top-[1px]\"\n        width=\"1em\"\n        height=\"1em\"\n      />\n      <span className=\"hidden md:block\">TypeScript Playground</span>\n    </a>\n  );\n};\n"
  },
  {
    "path": "src/components/MDX/Sandpack/Preview.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\n// eslint-disable-next-line react-compiler/react-compiler\n/* eslint-disable react-hooks/exhaustive-deps */\nimport {useRef, useState, useEffect, useMemo, useId} from 'react';\nimport {useSandpack, SandpackStack} from '@codesandbox/sandpack-react/unstyled';\nimport cn from 'classnames';\nimport {ErrorMessage} from './ErrorMessage';\nimport {SandpackConsole} from './Console';\nimport type {LintDiagnostic} from './useSandpackLint';\nimport {CSSProperties} from 'react';\nimport {LoadingOverlay} from './LoadingOverlay';\n\ntype CustomPreviewProps = {\n  className?: string;\n  isExpanded: boolean;\n  lintErrors: LintDiagnostic;\n};\n\nfunction useDebounced(value: any): any {\n  const ref = useRef<any>(null);\n  const [saved, setSaved] = useState(value);\n  useEffect(() => {\n    clearTimeout(ref.current);\n    ref.current = setTimeout(() => {\n      setSaved(value);\n    }, 300);\n  }, [value]);\n  return saved;\n}\n\nexport function Preview({\n  isExpanded,\n  className,\n  lintErrors,\n}: CustomPreviewProps) {\n  const {sandpack, listen} = useSandpack();\n  const [bundlerIsReady, setBundlerIsReady] = useState(false);\n  const [showLoading, setShowLoading] = useState(false);\n  const [iframeComputedHeight, setComputedAutoHeight] = useState<number | null>(\n    null\n  );\n\n  let {error: rawError, registerBundler, unregisterBundler} = sandpack;\n\n  if (\n    rawError &&\n    rawError.message === '_csbRefreshUtils.prelude is not a function'\n  ) {\n    // Work around a noisy internal error.\n    rawError = null;\n  }\n\n  // When throwing a new Error in Sandpack - we want to disable the dev error dialog\n  // to show the Error Boundary fallback\n  if (rawError && rawError.message.includes('Example Error:')) {\n    rawError = null;\n  }\n\n  // Memoized because it's fed to debouncing.\n  const firstLintError = useMemo(() => {\n    if (lintErrors.length === 0) {\n      return null;\n    } else {\n      const {line, column, message} = lintErrors[0];\n      return {\n        title: 'Lint Error',\n        message: `${line}:${column} - ${message}`,\n      };\n    }\n  }, [lintErrors]);\n\n  if (rawError == null || rawError.title === 'Runtime Exception') {\n    if (firstLintError !== null) {\n      rawError = firstLintError;\n    }\n  }\n\n  if (rawError != null && rawError.title === 'Runtime Exception') {\n    rawError.title = 'Runtime Error';\n  }\n\n  // It changes too fast, causing flicker.\n  const error = useDebounced(rawError);\n\n  const clientId = useId();\n  const iframeRef = useRef<HTMLIFrameElement | null>(null);\n\n  const sandpackIdle = sandpack.status === 'idle';\n\n  useEffect(function createBundler() {\n    const iframeElement = iframeRef.current!;\n    registerBundler(iframeElement, clientId);\n\n    return () => {\n      unregisterBundler(clientId);\n    };\n  }, []);\n\n  useEffect(\n    function bundlerListener() {\n      let timeout: ReturnType<typeof setTimeout>;\n\n      const unsubscribe = listen((message) => {\n        if (message.type === 'resize') {\n          setComputedAutoHeight(message.height);\n        } else if (message.type === 'start') {\n          if (message.firstLoad) {\n            setBundlerIsReady(false);\n          }\n\n          /**\n           * The spinner component transition might be longer than\n           * the bundler loading, so we only show the spinner if\n           * it takes more than 500s to load the bundler.\n           */\n          timeout = setTimeout(() => {\n            setShowLoading(true);\n          }, 500);\n        } else if (message.type === 'done') {\n          setBundlerIsReady(true);\n          setShowLoading(false);\n          clearTimeout(timeout);\n        }\n      }, clientId);\n\n      return () => {\n        clearTimeout(timeout);\n        setBundlerIsReady(false);\n        setComputedAutoHeight(null);\n        unsubscribe();\n      };\n    },\n    [sandpackIdle]\n  );\n\n  // WARNING:\n  // The layout and styling here is convoluted and really easy to break.\n  // If you make changes to it, you need to test different cases:\n  // - Content -> (compile | runtime) error -> content editing flow should work.\n  // - Errors should expand parent height rather than scroll.\n  // - Long sandboxes should scroll unless \"show more\" is toggled.\n  // - Expanded sandboxes (\"show more\") have sticky previews and errors.\n  // - Sandboxes have autoheight based on content.\n  // - That autoheight should be measured correctly! (Check some long ones.)\n  // - You shouldn't see nested scrolls (that means autoheight is borked).\n  // - Ideally you shouldn't see a blank preview tile while recompiling.\n  // - Container shouldn't be horizontally scrollable (even while loading).\n  // - It should work on mobile.\n  // The best way to test it is to actually go through some challenges.\n\n  const hideContent = error || !iframeComputedHeight || !bundlerIsReady;\n\n  const iframeWrapperPosition = (): CSSProperties => {\n    if (hideContent) {\n      return {position: 'relative'};\n    }\n\n    if (isExpanded) {\n      return {position: 'sticky', top: 'calc(2em + 64px)'};\n    }\n\n    return {};\n  };\n\n  return (\n    <SandpackStack className={className}>\n      <div\n        className={cn(\n          'p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative md:rounded-b-lg lg:rounded-b-none',\n          // Allow content to be scrolled if it's too high to fit.\n          // Note we don't want this in the expanded state\n          // because it breaks position: sticky (and isn't needed anyway).\n          !isExpanded && (error || bundlerIsReady) ? 'overflow-auto' : null\n        )}>\n        <div style={iframeWrapperPosition()}>\n          <iframe\n            ref={iframeRef}\n            className={cn(\n              'rounded-t-none bg-white md:shadow-md sm:rounded-lg w-full max-w-full transition-opacity',\n              // We can't *actually* hide content because that would\n              // break calculating the computed height in the iframe\n              // (which we're using for autosizing). This is noticeable\n              // if you make a compiler error and then fix it with code\n              // that expands the content. You want to measure that.\n              hideContent\n                ? 'absolute opacity-0 pointer-events-none duration-75'\n                : 'opacity-100 duration-150'\n            )}\n            title=\"Sandbox Preview\"\n            style={{\n              height: iframeComputedHeight || '15px',\n              zIndex: isExpanded ? 'initial' : -1,\n            }}\n          />\n        </div>\n\n        {error && (\n          <div\n            className={cn(\n              'z-50',\n              // This isn't absolutely positioned so that\n              // the errors can also expand the parent height.\n              isExpanded ? 'sticky top-8 ' : null\n            )}>\n            <ErrorMessage error={error} />\n          </div>\n        )}\n\n        <LoadingOverlay\n          clientId={clientId}\n          dependenciesLoading={!bundlerIsReady && iframeComputedHeight === null}\n          forceLoading={showLoading}\n        />\n      </div>\n      <SandpackConsole visible={!error} />\n    </SandpackStack>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/Sandpack/ReloadButton.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nimport {IconRestart} from '../../Icon/IconRestart';\nexport interface ReloadButtonProps {\n  onReload: () => void;\n}\n\nexport function ReloadButton({onReload}: ReloadButtonProps) {\n  return (\n    <button\n      className=\"text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1\"\n      onClick={onReload}\n      title=\"Keep your edits and reload sandbox\"\n      type=\"button\">\n      <IconRestart className=\"inline mx-1 relative\" />\n      <span className=\"hidden md:block\">Reload</span>\n    </button>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/Sandpack/SandpackRoot.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {Children} from 'react';\nimport * as React from 'react';\nimport {SandpackProvider} from '@codesandbox/sandpack-react/unstyled';\nimport {SandpackLogLevel} from '@codesandbox/sandpack-client';\nimport {CustomPreset} from './CustomPreset';\nimport {createFileMap} from './createFileMap';\nimport {CustomTheme} from './Themes';\nimport {template} from './template';\n\ntype SandpackProps = {\n  children: React.ReactNode;\n  autorun?: boolean;\n};\n\nconst sandboxStyle = `\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\nh1 {\n  margin-top: 0;\n  font-size: 22px;\n}\n\nh2 {\n  margin-top: 0;\n  font-size: 20px;\n}\n\nh3 {\n  margin-top: 0;\n  font-size: 18px;\n}\n\nh4 {\n  margin-top: 0;\n  font-size: 16px;\n}\n\nh5 {\n  margin-top: 0;\n  font-size: 14px;\n}\n\nh6 {\n  margin-top: 0;\n  font-size: 12px;\n}\n\ncode {\n  font-size: 1.2em;\n}\n\nul {\n  padding-inline-start: 20px;\n}\n`.trim();\n\nfunction SandpackRoot(props: SandpackProps) {\n  let {children, autorun = true} = props;\n  const codeSnippets = Children.toArray(children) as React.ReactElement[];\n  const files = createFileMap(codeSnippets);\n\n  if ('/index.html' in files) {\n    throw new Error(\n      'You cannot use `index.html` file in sandboxes. ' +\n        'Only `public/index.html` is respected by Sandpack and CodeSandbox (where forks are created).'\n    );\n  }\n\n  files['/src/styles.css'] = {\n    code: [sandboxStyle, files['/src/styles.css']?.code ?? ''].join('\\n\\n'),\n    hidden: !files['/src/styles.css']?.visible,\n  };\n\n  return (\n    <div className=\"sandpack sandpack--playground w-full my-8\" dir=\"ltr\">\n      <SandpackProvider\n        files={{...template, ...files}}\n        theme={CustomTheme}\n        customSetup={{\n          environment: 'create-react-app',\n        }}\n        options={{\n          autorun,\n          initMode: 'user-visible',\n          initModeObserverOptions: {rootMargin: '1400px 0px'},\n          bundlerURL: 'https://786946de.sandpack-bundler-4bw.pages.dev',\n          logLevel: SandpackLogLevel.None,\n        }}>\n        <CustomPreset providedFiles={Object.keys(files)} />\n      </SandpackProvider>\n    </div>\n  );\n}\n\nexport default SandpackRoot;\n"
  },
  {
    "path": "src/components/MDX/Sandpack/Themes.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport tailwindConfig from '../../../../tailwind.config';\n\nexport const CustomTheme = {\n  colors: {\n    accent: 'inherit',\n    base: 'inherit',\n    clickable: 'inherit',\n    disabled: 'inherit',\n    error: 'inherit',\n    errorSurface: 'inherit',\n    hover: 'inherit',\n    surface1: 'inherit',\n    surface2: 'inherit',\n    surface3: 'inherit',\n    warning: 'inherit',\n    warningSurface: 'inherit',\n  },\n  syntax: {\n    plain: 'inherit',\n    comment: 'inherit',\n    keyword: 'inherit',\n    tag: 'inherit',\n    punctuation: 'inherit',\n    definition: 'inherit',\n    property: 'inherit',\n    static: 'inherit',\n    string: 'inherit',\n  },\n  font: {\n    body: tailwindConfig.theme.extend.fontFamily.text\n      .join(', ')\n      .replace(/\"/gm, ''),\n    mono: tailwindConfig.theme.extend.fontFamily.mono\n      .join(', ')\n      .replace(/\"/gm, ''),\n    size: tailwindConfig.theme.extend.fontSize.code,\n    lineHeight: '24px',\n  },\n};\n"
  },
  {
    "path": "src/components/MDX/Sandpack/createFileMap.ts",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport type {SandpackFile} from '@codesandbox/sandpack-react/unstyled';\nimport type {PropsWithChildren, ReactElement, HTMLAttributes} from 'react';\n\nexport const AppJSPath = `/src/App.js`;\nexport const StylesCSSPath = `/src/styles.css`;\nexport const SUPPORTED_FILES = [AppJSPath, StylesCSSPath];\n\n/**\n * Tokenize meta attributes while ignoring brace-wrapped metadata (e.g. {expectedErrors: …}).\n */\nfunction splitMeta(meta: string): string[] {\n  const tokens: string[] = [];\n  let current = '';\n  let depth = 0;\n  const trimmed = meta.trim();\n\n  for (let ii = 0; ii < trimmed.length; ii++) {\n    const char = trimmed[ii];\n\n    if (char === '{') {\n      if (depth === 0 && current) {\n        tokens.push(current);\n        current = '';\n      }\n      depth += 1;\n      continue;\n    }\n\n    if (char === '}') {\n      if (depth > 0) {\n        depth -= 1;\n      }\n      if (depth === 0) {\n        current = '';\n      }\n      if (depth < 0) {\n        throw new Error(`Unexpected closing brace in meta: ${meta}`);\n      }\n      continue;\n    }\n\n    if (depth > 0) {\n      continue;\n    }\n\n    if (/\\s/.test(char)) {\n      if (current) {\n        tokens.push(current);\n        current = '';\n      }\n      continue;\n    }\n\n    current += char;\n  }\n\n  if (current) {\n    tokens.push(current);\n  }\n\n  if (depth !== 0) {\n    throw new Error(`Unclosed brace in meta: ${meta}`);\n  }\n\n  return tokens;\n}\n\nexport const createFileMap = (codeSnippets: any) => {\n  return codeSnippets.reduce(\n    (result: Record<string, SandpackFile>, codeSnippet: React.ReactElement) => {\n      if (\n        (codeSnippet.type as any).mdxName !== 'pre' &&\n        codeSnippet.type !== 'pre'\n      ) {\n        return result;\n      }\n      const {props} = (\n        codeSnippet.props as PropsWithChildren<{\n          children: ReactElement<\n            HTMLAttributes<HTMLDivElement> & {meta?: string}\n          >;\n        }>\n      ).children;\n      let filePath; // path in the folder structure\n      let fileHidden = false; // if the file is available as a tab\n      let fileActive = false; // if the file tab is shown by default\n\n      if (props.meta) {\n        const tokens = splitMeta(props.meta);\n        const name = tokens.find(\n          (token) => token.includes('/') || token.includes('.')\n        );\n        if (name) {\n          filePath = name.startsWith('/') ? name : `/${name}`;\n        }\n        if (tokens.includes('hidden')) {\n          fileHidden = true;\n        }\n        if (tokens.includes('active')) {\n          fileActive = true;\n        }\n      } else {\n        if (props.className === 'language-js') {\n          filePath = AppJSPath;\n        } else if (props.className === 'language-css') {\n          filePath = StylesCSSPath;\n        } else {\n          throw new Error(\n            `Code block is missing a filename: ${props.children}`\n          );\n        }\n      }\n\n      if (!filePath) {\n        if (props.className === 'language-js') {\n          filePath = AppJSPath;\n        } else if (props.className === 'language-css') {\n          filePath = StylesCSSPath;\n        } else {\n          throw new Error(\n            `Code block is missing a filename: ${props.children}`\n          );\n        }\n      }\n\n      if (result[filePath]) {\n        throw new Error(\n          `File ${filePath} was defined multiple times. Each file snippet should have a unique path name`\n        );\n      }\n      result[filePath] = {\n        code: (props.children || '') as string,\n        hidden: fileHidden,\n        active: fileActive,\n      };\n\n      return result;\n    },\n    {}\n  );\n};\n"
  },
  {
    "path": "src/components/MDX/Sandpack/index.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {lazy, memo, Children, Suspense} from 'react';\nimport {AppJSPath, createFileMap} from './createFileMap';\n\nconst SandpackRoot = lazy(() => import('./SandpackRoot'));\n\nconst SandpackGlimmer = ({code}: {code: string}) => (\n  <div className=\"sandpack sandpack--playground my-8\">\n    <div className=\"sp-wrapper\">\n      <div className=\"shadow-lg dark:shadow-lg-dark rounded-lg\">\n        <div className=\"bg-wash h-10 dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg rounded-b-none\">\n          <div className=\"px-4 lg:px-6\">\n            <div className=\"sp-tabs\"></div>\n          </div>\n          <div className=\"px-3 flex items-center justify-end grow text-right\"></div>\n        </div>\n        <div className=\"sp-layout min-h-[216px] flex items-stretch flex-wrap\">\n          <div className=\"sp-stack sp-editor max-h-[406px] h-auto overflow-auto\">\n            <div className=\"sp-code-editor\">\n              <div className=\"sp-cm sp-pristine\">\n                <div className=\"cm-editor\">\n                  <div>\n                    <div className=\"cm-gutters ps-9 sticky min-h-[192px]\">\n                      <div className=\"cm-gutter cm-lineNumbers whitespace-pre sp-pre-placeholder\">\n                        {code}\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n          <div className=\"sp-stack order-last xl:order-2 max-h-[406px] h-auto\">\n            <div className=\"p-0 sm:p-2 md:p-4 lg:p-8 bg-card dark:bg-wash-dark h-full relative rounded-b-lg lg:rounded-b-none overflow-auto\"></div>\n          </div>\n          {code.split('\\n').length > 16 && (\n            <div className=\"flex h-[45px] text-base justify-between dark:border-card-dark bg-wash dark:bg-card-dark items-center z-10 rounded-t-none p-1 w-full order-2 xl:order-last border-b-1 relative top-0\"></div>\n          )}\n        </div>\n      </div>\n    </div>\n  </div>\n);\n\nexport default memo(function SandpackWrapper(props: any): any {\n  const codeSnippet = createFileMap(Children.toArray(props.children));\n\n  // To set the active file in the fallback we have to find the active file first.\n  // If there are no active files we fallback to App.js as default.\n  let activeCodeSnippet = Object.keys(codeSnippet).filter(\n    (fileName) =>\n      codeSnippet[fileName]?.active === true &&\n      codeSnippet[fileName]?.hidden === false\n  );\n  let activeCode;\n  if (!activeCodeSnippet.length) {\n    activeCode = codeSnippet[AppJSPath].code;\n  } else {\n    activeCode = codeSnippet[activeCodeSnippet[0]].code;\n  }\n\n  return (\n    <Suspense fallback={<SandpackGlimmer code={activeCode} />}>\n      <SandpackRoot {...props} />\n    </Suspense>\n  );\n});\n"
  },
  {
    "path": "src/components/MDX/Sandpack/runESLint.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n// @ts-nocheck\n\nimport {Linter} from 'eslint/lib/linter/linter';\n\nimport type {Diagnostic} from '@codemirror/lint';\nimport type {Text} from '@codemirror/text';\n\nconst getCodeMirrorPosition = (\n  doc: Text,\n  {line, column}: {line: number; column?: number}\n): number => {\n  return doc.line(line).from + (column ?? 0) - 1;\n};\n\nconst linter = new Linter();\n\nconst reactRules = require('eslint-plugin-react-hooks').rules;\nlinter.defineRules({\n  'react-hooks/rules-of-hooks': reactRules['rules-of-hooks'],\n  'react-hooks/exhaustive-deps': reactRules['exhaustive-deps'],\n});\n\nconst options = {\n  parserOptions: {\n    ecmaVersion: 12,\n    sourceType: 'module',\n    ecmaFeatures: {jsx: true},\n  },\n  rules: {\n    'react-hooks/rules-of-hooks': 'error',\n    'react-hooks/exhaustive-deps': 'error',\n  },\n};\n\nexport const runESLint = (\n  doc: Text\n): {errors: any[]; codeMirrorErrors: Diagnostic[]} => {\n  const codeString = doc.toString();\n  const errors = linter.verify(codeString, options) as any[];\n\n  const severity = {\n    1: 'warning',\n    2: 'error',\n  };\n\n  const codeMirrorErrors = errors\n    .map((error) => {\n      if (!error) return undefined;\n\n      const from = getCodeMirrorPosition(doc, {\n        line: error.line,\n        column: error.column,\n      });\n\n      const to = getCodeMirrorPosition(doc, {\n        line: error.endLine ?? error.line,\n        column: error.endColumn ?? error.column,\n      });\n\n      return {\n        ruleId: error.ruleId,\n        from,\n        to,\n        severity: severity[error.severity],\n        message: error.message,\n      };\n    })\n    .filter(Boolean) as Diagnostic[];\n\n  return {\n    codeMirrorErrors,\n    errors: errors.map((item) => {\n      return {\n        ...item,\n        severity: severity[item.severity],\n      };\n    }),\n  };\n};\n"
  },
  {
    "path": "src/components/MDX/Sandpack/template.ts",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nexport const template = {\n  '/src/index.js': {\n    hidden: true,\n    code: `import { StrictMode } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport \"./styles.css\";\n\nimport App from \"./App\";\n\nconst root = createRoot(document.getElementById(\"root\"));\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);`,\n  },\n  '/package.json': {\n    hidden: true,\n    code: JSON.stringify(\n      {\n        name: 'react.dev',\n        version: '0.0.0',\n        main: '/src/index.js',\n        scripts: {\n          start: 'react-scripts start',\n          build: 'react-scripts build',\n          test: 'react-scripts test --env=jsdom',\n          eject: 'react-scripts eject',\n        },\n        dependencies: {\n          react: '^19.2.1',\n          'react-dom': '^19.2.1',\n          'react-scripts': '^5.0.0',\n        },\n      },\n      null,\n      2\n    ),\n  },\n  '/public/index.html': {\n    hidden: true,\n    code: `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Document</title>\n</head>\n<body>\n  <div id=\"root\"></div>\n</body>\n</html>`,\n  },\n};\n"
  },
  {
    "path": "src/components/MDX/Sandpack/useSandpackLint.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\n// @ts-nocheck\n\nimport {useState, useEffect} from 'react';\nimport type {EditorView} from '@codemirror/view';\n\nexport type LintDiagnostic = {\n  line: number;\n  column: number;\n  severity: 'warning' | 'error';\n  message: string;\n}[];\n\nexport const useSandpackLint = () => {\n  const [lintErrors, setLintErrors] = useState<LintDiagnostic>([]);\n  const [lintExtensions, setLintExtensions] = useState<any>([]);\n  useEffect(() => {\n    const loadLinter = async () => {\n      const {linter} = await import('@codemirror/lint');\n      const onLint = linter(async (props: EditorView) => {\n        // This is intentionally delayed until CodeMirror calls it\n        // so that we don't take away bandwidth from things loading early.\n        const {runESLint} = await import('./runESLint');\n        const editorState = props.state.doc;\n        let {errors, codeMirrorErrors} = runESLint(editorState);\n        // Ignore parsing or internal linter errors.\n        const isReactRuleError = (error: any) => error.ruleId != null;\n        setLintErrors(errors.filter(isReactRuleError));\n        return codeMirrorErrors.filter(isReactRuleError);\n      });\n      setLintExtensions([onLint]);\n    };\n\n    loadLinter();\n  }, []);\n  return {lintErrors, lintExtensions};\n};\n"
  },
  {
    "path": "src/components/MDX/SandpackWithHTMLOutput.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport {Children, memo} from 'react';\nimport InlineCode from './InlineCode';\nimport Sandpack from './Sandpack';\n\nconst ShowRenderedHTML = `\nimport { renderToStaticMarkup } from 'react-dom/server';\nimport formatHTML from './formatHTML.js';\n\nexport default function ShowRenderedHTML({children}) {\n  const markup = renderToStaticMarkup(\n    <html>\n      <head />\n      <body>{children}</body>\n    </html>\n  );\n  return (\n    <>\n      <h1>Rendered HTML:</h1>\n      <pre>\n        {formatHTML(markup)}\n      </pre>\n    </>\n  );\n}`;\n\nconst formatHTML = `\nimport format from 'html-format';\n\nexport default function formatHTML(markup) {\n  // Cheap tricks to format the HTML readably -- haven't been able to\n  // find a package that runs in browser and prettifies the HTML if it\n  // lacks line-breaks.\n  return format(markup\n    .replace('<html>', '<html>\\\\n')\n    .replace('<head>', '<head>\\\\n')\n    .replaceAll(/<\\\\/script>/g, '<\\\\/script>\\\\n')\n    .replaceAll(/<style([^>]*)\\\\/>/g, '<style $1/>\\\\n\\\\n')\n    .replaceAll(/<\\\\/style>/g, '\\\\n    <\\\\/style>\\\\n')\n    .replaceAll(/<link([^>]*)\\\\/>/g, '<link $1/>\\\\n')\n    .replaceAll(/<meta([^/]*)\\\\/>/g, '<meta $1/>\\\\n')\n    .replace('</head>', '</head>\\\\n')\n    .replace('<body>', '<body>\\\\n')\n    .replace('</body>', '\\\\n</body>\\\\n')\n    .replace('</h1>', '</h1>\\\\n')\n  );\n}\n`;\n\nconst packageJSON = `\n{\n  \"dependencies\": {\n    \"react\": \"^19.2.1\",\n    \"react-dom\": \"^19.2.1\",\n    \"react-scripts\": \"^5.0.0\",\n    \"html-format\": \"^1.1.2\"\n  },\n  \"main\": \"/index.js\",\n  \"devDependencies\": {}\n}\n`;\n\n// Intentionally not a React component because <Sandpack> will read\n// through its childrens' props. This imitates the output of ```\n// codeblocks in MDX.\nfunction createFile(meta: string, source: string) {\n  return (\n    <pre key={meta}>\n      <InlineCode meta={meta} className=\"language-js\">\n        {source}\n      </InlineCode>\n    </pre>\n  );\n}\n\nexport default memo(function SandpackWithHTMLOutput(\n  props: React.ComponentProps<typeof Sandpack>\n) {\n  const children = [\n    ...Children.toArray(props.children),\n    createFile('src/ShowRenderedHTML.js', ShowRenderedHTML),\n    createFile('src/formatHTML.js hidden', formatHTML),\n    createFile('package.json hidden', packageJSON),\n  ];\n  return <Sandpack {...props}>{children}</Sandpack>;\n});\n"
  },
  {
    "path": "src/components/MDX/SimpleCallout.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nimport cn from 'classnames';\nimport {H3} from './Heading';\n\ninterface SimpleCalloutProps {\n  title: string;\n  children: React.ReactNode;\n  className?: string;\n}\nfunction SimpleCallout({title, children, className}: SimpleCalloutProps) {\n  return (\n    <div\n      className={cn(\n        'p-6 xl:p-8 pb-4 xl:pb-6 bg-card dark:bg-card-dark rounded-2xl shadow-inner-border dark:shadow-inner-border-dark text-base text-secondary dark:text-secondary-dark my-8',\n        className\n      )}>\n      <H3\n        className=\"text-primary dark:text-primary-dark mt-0 mb-3 leading-tight\"\n        isPageAnchor={false}>\n        {title}\n      </H3>\n      {children}\n    </div>\n  );\n}\n\nexport default SimpleCallout;\n"
  },
  {
    "path": "src/components/MDX/TeamMember.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nimport Image from 'next/legacy/image';\nimport {IconTwitter} from '../Icon/IconTwitter';\nimport {IconThreads} from '../Icon/IconThreads';\nimport {IconBsky} from '../Icon/IconBsky';\nimport {IconGitHub} from '../Icon/IconGitHub';\nimport {ExternalLink} from '../ExternalLink';\nimport {H3} from './Heading';\nimport {IconLink} from 'components/Icon/IconLink';\n\ninterface TeamMemberProps {\n  name: string;\n  title: string;\n  permalink: string;\n  children: React.ReactNode;\n  photo: string;\n  twitter?: string;\n  threads?: string;\n  bsky?: string;\n  github?: string;\n  personal?: string;\n}\n\n// TODO: good alt text for images/links\nexport function TeamMember({\n  name,\n  title,\n  permalink,\n  children,\n  photo,\n  github,\n  twitter,\n  threads,\n  bsky,\n  personal,\n}: TeamMemberProps) {\n  if (name == null || title == null || permalink == null || children == null) {\n    const identifier = name ?? title ?? permalink ?? 'unknown';\n    throw new Error(\n      `Expected name, title, permalink, and children for ${identifier}`\n    );\n  }\n  return (\n    <div className=\"pb-6 sm:pb-10\">\n      <div className=\"flex flex-col sm:flex-row height-auto\">\n        <div\n          className=\"hidden sm:block basis-2/5 rounded overflow-hidden relative\"\n          style={{width: 300, height: 250}}>\n          <Image src={photo} layout=\"fill\" objectFit=\"cover\" alt={name} />\n        </div>\n        <div\n          style={{minHeight: 300}}\n          className=\"block w-full sm:hidden flex-grow basis-2/5 rounded overflow-hidden relative\">\n          <Image src={photo} layout=\"fill\" objectFit=\"cover\" alt={name} />\n        </div>\n        <div className=\"ps-0 sm:ps-6 basis-3/5 items-start\">\n          <H3 className=\"mb-1 sm:my-0\" id={permalink}>\n            {name}\n          </H3>\n          {title && <div>{title}</div>}\n          {children}\n          <div className=\"sm:flex sm:flex-row flex-wrap text-secondary dark:text-secondary-dark\">\n            {twitter && (\n              <div className=\"me-4\">\n                <ExternalLink\n                  aria-label={`${name} on Twitter`}\n                  href={`https://twitter.com/${twitter}`}\n                  className=\"hover:text-primary hover:underline dark:text-primary-dark flex flex-row items-center\">\n                  <IconTwitter className=\"pe-1\" />\n                  {twitter}\n                </ExternalLink>\n              </div>\n            )}\n            {threads && (\n              <div className=\"me-4\">\n                <ExternalLink\n                  aria-label={`${name} on Threads`}\n                  href={`https://threads.net/${threads}`}\n                  className=\"hover:text-primary hover:underline dark:text-primary-dark flex flex-row items-center\">\n                  <IconThreads className=\"pe-1\" />\n                  {threads}\n                </ExternalLink>\n              </div>\n            )}\n            {bsky && (\n              <div className=\"me-4\">\n                <ExternalLink\n                  aria-label={`${name} on Bluesky`}\n                  href={`https://bsky.app/profile/${bsky}`}\n                  className=\"hover:text-primary hover:underline dark:text-primary-dark flex flex-row items-center\">\n                  <IconBsky className=\"pe-1\" />\n                  {bsky}\n                </ExternalLink>\n              </div>\n            )}\n            {github && (\n              <div className=\"me-4\">\n                <ExternalLink\n                  aria-label=\"GitHub Profile\"\n                  href={`https://github.com/${github}`}\n                  className=\"hover:text-primary hover:underline dark:text-primary-dark flex flex-row items-center\">\n                  <IconGitHub className=\"pe-1\" /> {github}\n                </ExternalLink>\n              </div>\n            )}\n            {personal && (\n              <ExternalLink\n                aria-label=\"Personal Site\"\n                href={`https://${personal}`}\n                className=\"hover:text-primary hover:underline dark:text-primary-dark flex flex-row items-center\">\n                <IconLink className=\"pe-1\" /> {personal}\n              </ExternalLink>\n            )}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/MDX/TerminalBlock.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {isValidElement, useState, useEffect} from 'react';\nimport * as React from 'react';\nimport {IconTerminal} from '../Icon/IconTerminal';\nimport {IconCopy} from 'components/Icon/IconCopy';\n\ntype LogLevel = 'info' | 'warning' | 'error';\n\ninterface TerminalBlockProps {\n  level?: LogLevel;\n  children: React.ReactNode;\n}\n\nfunction LevelText({type}: {type: LogLevel}) {\n  switch (type) {\n    case 'warning':\n      return <span className=\"text-yellow-50 bg-none me-1\">Warning: </span>;\n    case 'error':\n      return <span className=\"text-red-40 me-1\">Error: </span>;\n    default:\n      return null;\n  }\n}\n\nfunction TerminalBlock({level = 'info', children}: TerminalBlockProps) {\n  let message: string | undefined;\n  if (typeof children === 'string') {\n    message = children;\n  } else if (\n    isValidElement(children) &&\n    typeof (children as React.ReactElement<{children: string}>).props\n      .children === 'string'\n  ) {\n    message = (children as React.ReactElement<{children: string}>).props\n      .children;\n  } else {\n    throw Error('Expected TerminalBlock children to be a plain string.');\n  }\n\n  const [copied, setCopied] = useState(false);\n  useEffect(() => {\n    if (!copied) {\n      return;\n    } else {\n      const timer = setTimeout(() => {\n        setCopied(false);\n      }, 2000);\n      return () => clearTimeout(timer);\n    }\n  }, [copied]);\n\n  return (\n    <div className=\"rounded-lg bg-secondary dark:bg-gray-50 h-full\">\n      <div className=\"bg-gray-90 dark:bg-gray-60 w-full rounded-t-lg\">\n        <div className=\"text-primary-dark dark:text-primary-dark flex text-sm px-4 py-0.5 relative justify-between\">\n          <div>\n            <IconTerminal className=\"inline-flex me-2 self-center\" /> Terminal\n          </div>\n          <div>\n            <button\n              className=\"w-full text-start text-primary-dark dark:text-primary-dark \"\n              onClick={() => {\n                window.navigator.clipboard.writeText(message ?? '');\n                setCopied(true);\n              }}>\n              <IconCopy className=\"inline-flex me-2 self-center\" />{' '}\n              {copied ? 'Copied' : 'Copy'}\n            </button>\n          </div>\n        </div>\n      </div>\n      <pre\n        className=\"px-8 pt-4 pb-6 text-primary-dark dark:text-primary-dark font-mono text-code whitespace-pre overflow-x-auto\"\n        translate=\"no\"\n        dir=\"ltr\">\n        <code>\n          <LevelText type={level} />\n          {message}\n        </code>\n      </pre>\n    </div>\n  );\n}\n\nexport default TerminalBlock;\n"
  },
  {
    "path": "src/components/MDX/TocContext.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {createContext} from 'react';\nimport type {ReactNode} from 'react';\n\nexport type TocItem = {\n  url: string;\n  text: ReactNode;\n  depth: number;\n};\nexport type Toc = Array<TocItem>;\n\nexport const TocContext = createContext<Toc>([]);\n"
  },
  {
    "path": "src/components/MDX/YouWillLearnCard.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nimport ButtonLink from 'components/ButtonLink';\nimport {IconNavArrow} from 'components/Icon/IconNavArrow';\n\ninterface YouWillLearnCardProps {\n  title: string;\n  path: string;\n  children: React.ReactNode;\n}\n\nfunction YouWillLearnCard({title, path, children}: YouWillLearnCardProps) {\n  return (\n    <div className=\"flex flex-col h-full bg-card dark:bg-card-dark shadow-inner justify-between rounded-lg pb-8 p-6 xl:p-8 mt-3\">\n      <div>\n        <h4 className=\"text-primary dark:text-primary-dark font-bold text-2xl leading-tight\">\n          {title}\n        </h4>\n        <div className=\"my-4\">{children}</div>\n      </div>\n      <div>\n        <ButtonLink\n          href={path}\n          className=\"mt-1\"\n          type=\"primary\"\n          size=\"md\"\n          label={title}>\n          Read More\n          <IconNavArrow displayDirection=\"end\" className=\"inline ms-1\" />\n        </ButtonLink>\n      </div>\n    </div>\n  );\n}\n\nexport default YouWillLearnCard;\n"
  },
  {
    "path": "src/components/PageHeading.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport Breadcrumbs from 'components/Breadcrumbs';\nimport Tag from 'components/Tag';\nimport {H1} from './MDX/Heading';\nimport type {RouteTag, RouteItem} from './Layout/getRouteMeta';\nimport * as React from 'react';\nimport {IconCanary} from './Icon/IconCanary';\nimport {IconExperimental} from './Icon/IconExperimental';\n\ninterface PageHeadingProps {\n  title: string;\n  version?: 'experimental' | 'canary' | 'rc';\n  experimental?: boolean;\n  status?: string;\n  description?: string;\n  tags?: RouteTag[];\n  breadcrumbs: RouteItem[];\n}\n\nfunction PageHeading({\n  title,\n  status,\n  version,\n  tags = [],\n  breadcrumbs,\n}: PageHeadingProps) {\n  return (\n    <div className=\"px-5 sm:px-12 pt-3.5\">\n      <div className=\"max-w-4xl ms-0 2xl:mx-auto\">\n        {breadcrumbs ? <Breadcrumbs breadcrumbs={breadcrumbs} /> : null}\n        <H1 className=\"mt-0 text-primary dark:text-primary-dark -mx-.5 break-words\">\n          {title}\n          {version === 'canary' && (\n            <IconCanary\n              title=\" - This feature is available in the latest Canary version of React\"\n              className=\"ms-4 mt-1 text-gray-50 dark:text-gray-40 inline-block w-6 h-6 align-[-1px]\"\n            />\n          )}\n          {version === 'rc' && (\n            <IconCanary\n              title=\" - This feature is available in the latest RC version\"\n              className=\"ms-4 mt-1 text-gray-50 dark:text-gray-40 inline-block w-6 h-6 align-[-1px]\"\n            />\n          )}\n          {version === 'experimental' && (\n            <IconExperimental\n              title=\" - This feature is available in the latest Experimental version of React\"\n              className=\"ms-4 mt-1 text-gray-50 dark:text-gray-40 inline-block w-6 h-6 align-[-1px]\"\n            />\n          )}\n          {status ? <em>—{status}</em> : ''}\n        </H1>\n        {tags?.length > 0 && (\n          <div className=\"mt-4\">\n            {tags.map((tag) => (\n              <Tag key={tag} variant={tag as RouteTag} />\n            ))}\n          </div>\n        )}\n      </div>\n    </div>\n  );\n}\n\nexport default PageHeading;\n"
  },
  {
    "path": "src/components/Search.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport Head from 'next/head';\nimport Link from 'next/link';\nimport Router from 'next/router';\nimport {lazy, useEffect} from 'react';\nimport * as React from 'react';\nimport {createPortal} from 'react-dom';\nimport {siteConfig} from 'siteConfig';\nimport type {ComponentType, PropsWithChildren} from 'react';\nimport type {DocSearchModalProps} from '@docsearch/react/modal';\n\nexport interface SearchProps {\n  appId?: string;\n  apiKey?: string;\n  indexName?: string;\n  searchParameters?: any;\n  isOpen: boolean;\n  onOpen: () => void;\n  onClose: () => void;\n}\n\nfunction Hit({hit, children}: any) {\n  return <Link href={hit.url.replace()}>{children}</Link>;\n}\n\n// Copy-pasted from @docsearch/react to avoid importing the whole bundle.\n// Slightly trimmed to features we use.\n// (c) Algolia, Inc.\nfunction isEditingContent(event: any) {\n  var element = event.target;\n  var tagName = element.tagName;\n  return (\n    element.isContentEditable ||\n    tagName === 'INPUT' ||\n    tagName === 'SELECT' ||\n    tagName === 'TEXTAREA'\n  );\n}\nfunction useDocSearchKeyboardEvents({\n  isOpen,\n  onOpen,\n  onClose,\n}: {\n  isOpen: boolean;\n  onOpen: () => void;\n  onClose: () => void;\n}) {\n  useEffect(() => {\n    function onKeyDown(event: any) {\n      function open() {\n        // We check that no other DocSearch modal is showing before opening\n        // another one.\n        if (!document.body.classList.contains('DocSearch--active')) {\n          onOpen();\n        }\n      }\n      if (\n        (event.keyCode === 27 && isOpen) ||\n        (event.key === 'k' && (event.metaKey || event.ctrlKey)) ||\n        (!isEditingContent(event) && event.key === '/' && !isOpen)\n      ) {\n        event.preventDefault();\n        if (isOpen) {\n          onClose();\n        } else if (!document.body.classList.contains('DocSearch--active')) {\n          open();\n        }\n      }\n    }\n\n    window.addEventListener('keydown', onKeyDown);\n    return function () {\n      window.removeEventListener('keydown', onKeyDown);\n    };\n  }, [isOpen, onOpen, onClose]);\n}\n\nconst options = {\n  appId: siteConfig.algolia.appId,\n  apiKey: siteConfig.algolia.apiKey,\n  indexName: siteConfig.algolia.indexName,\n};\n\nconst DocSearchModal: any = lazy(() =>\n  import('@docsearch/react/modal').then((mod) => ({\n    default: mod.DocSearchModal as ComponentType<\n      PropsWithChildren<DocSearchModalProps>\n    >,\n  }))\n);\n\nexport function Search({\n  isOpen,\n  onOpen,\n  onClose,\n  searchParameters = {\n    hitsPerPage: 30,\n    attributesToHighlight: [\n      'hierarchy.lvl0',\n      'hierarchy.lvl1',\n      'hierarchy.lvl2',\n      'hierarchy.lvl3',\n      'hierarchy.lvl4',\n      'hierarchy.lvl5',\n      'hierarchy.lvl6',\n      'content',\n    ],\n  },\n}: SearchProps) {\n  useDocSearchKeyboardEvents({isOpen, onOpen, onClose});\n  return (\n    <>\n      <Head>\n        <link\n          rel=\"preconnect\"\n          href={`https://${options.appId}-dsn.algolia.net`}\n        />\n      </Head>\n      {isOpen &&\n        createPortal(\n          <DocSearchModal\n            {...options}\n            searchParameters={searchParameters}\n            onClose={onClose}\n            navigator={{\n              navigate({itemUrl}: any) {\n                Router.push(itemUrl);\n              },\n            }}\n            transformItems={(items: any[]) => {\n              return items.map((item) => {\n                const url = new URL(item.url);\n                return {\n                  ...item,\n                  url: item.url.replace(url.origin, '').replace('#__next', ''),\n                };\n              });\n            }}\n            hitComponent={Hit}\n          />,\n          document.body\n        )}\n    </>\n  );\n}\n"
  },
  {
    "path": "src/components/Seo.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\nimport Head from 'next/head';\nimport {withRouter, Router} from 'next/router';\nimport {siteConfig} from '../siteConfig';\nimport {finishedTranslations} from 'utils/finishedTranslations';\n\nexport interface SeoProps {\n  title: string;\n  titleForTitleTag: undefined | string;\n  description?: string;\n  image?: string;\n  // jsonld?: JsonLDType | Array<JsonLDType>;\n  children?: React.ReactNode;\n  isHomePage: boolean;\n  searchOrder?: number;\n}\n\n// If you are a maintainer of a language fork,\n// deployedTranslations has been moved to src/utils/finishedTranslations.ts.\n\nfunction getDomain(languageCode: string): string {\n  const subdomain = languageCode === 'en' ? '' : languageCode + '.';\n  return subdomain + 'react.dev';\n}\n\nexport const Seo = withRouter(\n  ({\n    title,\n    titleForTitleTag,\n    image = '/images/og-default.png',\n    router,\n    children,\n    isHomePage,\n    searchOrder,\n  }: SeoProps & {router: Router}) => {\n    const siteDomain = getDomain(siteConfig.languageCode);\n    const canonicalUrl = `https://${siteDomain}${\n      router.asPath.split(/[\\?\\#]/)[0]\n    }`;\n    // Allow setting a different title for Google results\n    const pageTitle =\n      (titleForTitleTag ?? title) + (isHomePage ? '' : ' – React');\n    // Twitter's meta parser is not very good.\n    const twitterTitle = pageTitle.replace(/[<>]/g, '');\n    let description = isHomePage\n      ? 'React is the library for web and native user interfaces. Build user interfaces out of individual pieces called components written in JavaScript. React is designed to let you seamlessly combine components written by independent people, teams, and organizations.'\n      : 'The library for web and native user interfaces';\n    return (\n      <Head>\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        {title != null && <title key=\"title\">{pageTitle}</title>}\n        {isHomePage && (\n          // Let Google figure out a good description for each page.\n          <meta name=\"description\" key=\"description\" content={description} />\n        )}\n        <link rel=\"canonical\" href={canonicalUrl} />\n        <link\n          rel=\"alternate\"\n          href={canonicalUrl.replace(siteDomain, getDomain('en'))}\n          hrefLang=\"x-default\"\n        />\n        {finishedTranslations.map((languageCode) => (\n          <link\n            key={'alt-' + languageCode}\n            rel=\"alternate\"\n            hrefLang={languageCode}\n            href={canonicalUrl.replace(siteDomain, getDomain(languageCode))}\n          />\n        ))}\n        <meta property=\"fb:app_id\" content=\"623268441017527\" />\n        <meta property=\"og:type\" key=\"og:type\" content=\"website\" />\n        <meta property=\"og:url\" key=\"og:url\" content={canonicalUrl} />\n        {title != null && (\n          <meta property=\"og:title\" content={pageTitle} key=\"og:title\" />\n        )}\n        {description != null && (\n          <meta\n            property=\"og:description\"\n            key=\"og:description\"\n            content={description}\n          />\n        )}\n        <meta\n          property=\"og:image\"\n          key=\"og:image\"\n          content={`https://${siteDomain}${image}`}\n        />\n        <meta\n          name=\"twitter:card\"\n          key=\"twitter:card\"\n          content=\"summary_large_image\"\n        />\n        <meta name=\"twitter:site\" key=\"twitter:site\" content=\"@reactjs\" />\n        <meta name=\"twitter:creator\" key=\"twitter:creator\" content=\"@reactjs\" />\n        {title != null && (\n          <meta\n            name=\"twitter:title\"\n            key=\"twitter:title\"\n            content={twitterTitle}\n          />\n        )}\n        {description != null && (\n          <meta\n            name=\"twitter:description\"\n            key=\"twitter:description\"\n            content={description}\n          />\n        )}\n        <meta\n          name=\"twitter:image\"\n          key=\"twitter:image\"\n          content={`https://${siteDomain}${image}`}\n        />\n        <meta\n          name=\"google-site-verification\"\n          content=\"sIlAGs48RulR4DdP95YSWNKZIEtCqQmRjzn-Zq-CcD0\"\n        />\n        {searchOrder != null && (\n          <meta name=\"algolia-search-order\" content={'' + searchOrder} />\n        )}\n        <link\n          rel=\"preload\"\n          href=\"https://react.dev/fonts/Source-Code-Pro-Regular.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n        <link\n          rel=\"preload\"\n          href=\"https://react.dev/fonts/Source-Code-Pro-Bold.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n        <link\n          rel=\"preload\"\n          href=\"https://react.dev/fonts/Optimistic_Display_W_Md.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n        <link\n          rel=\"preload\"\n          href=\"https://react.dev/fonts/Optimistic_Display_W_SBd.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n        <link\n          rel=\"preload\"\n          href=\"https://react.dev/fonts/Optimistic_Display_W_Bd.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n        <link\n          rel=\"preload\"\n          href=\"https://react.dev/fonts/Optimistic_Text_W_Md.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n        <link\n          rel=\"preload\"\n          href=\"https://react.dev/fonts/Optimistic_Text_W_Bd.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n        <link\n          rel=\"preload\"\n          href=\"https://react.dev/fonts/Optimistic_Text_W_Rg.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n        <link\n          rel=\"preload\"\n          href=\"https://react.dev/fonts/Optimistic_Text_W_It.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n        {children}\n      </Head>\n    );\n  }\n);\n"
  },
  {
    "path": "src/components/SocialBanner.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport {useRef, useEffect} from 'react';\nimport cn from 'classnames';\nimport {ExternalLink} from './ExternalLink';\n\nconst bannerText = 'Join us for React Conf on Oct 7-8.';\nconst bannerLink = 'https://conf.react.dev/';\nconst bannerLinkText = 'Learn more.';\n\nexport default function SocialBanner() {\n  const ref = useRef<HTMLDivElement | null>(null);\n  useEffect(() => {\n    function patchedScrollTo(x: number, y: number) {\n      if (y === 0) {\n        // We're trying to reset scroll.\n        // If we already scrolled past the banner, consider it as y = 0.\n        const bannerHeight = ref.current?.offsetHeight ?? 0; // Could be zero (e.g. mobile)\n        y = Math.min(window.scrollY, bannerHeight);\n      }\n      return realScrollTo(x, y);\n    }\n    const realScrollTo = window.scrollTo;\n    (window as any).scrollTo = patchedScrollTo;\n    return () => {\n      (window as any).scrollTo = realScrollTo;\n    };\n  }, []);\n  return (\n    <div\n      ref={ref}\n      className={cn(\n        `h-[40px] hidden lg:flex w-full bg-gray-100 dark:bg-gray-700 text-base md:text-lg py-2 sm:py-0 items-center justify-center flex-col sm:flex-row z-[100]`\n      )}>\n      <div className=\"hidden sm:block\">{bannerText}</div>\n      <ExternalLink\n        className=\"ms-0 sm:ms-1 text-link dark:text-link-dark hover:underline\"\n        href={bannerLink}>\n        {bannerLinkText}\n      </ExternalLink>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/components/Tag.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport cn from 'classnames';\nimport type {RouteTag} from './Layout/getRouteMeta';\n\nconst variantMap = {\n  foundation: {\n    name: 'Foundation',\n    classes: 'bg-yellow-50 text-white',\n  },\n  intermediate: {\n    name: 'Intermediate',\n    classes: 'bg-purple-40 text-white',\n  },\n  advanced: {\n    name: 'Advanced',\n    classes: 'bg-green-40 text-white',\n  },\n  experimental: {\n    name: 'Experimental',\n    classes: 'bg-ui-orange text-white',\n  },\n  deprecated: {\n    name: 'Deprecated',\n    classes: 'bg-red-40 text-white',\n  },\n};\n\ninterface TagProps {\n  variant: RouteTag;\n  text?: string;\n  className?: string;\n}\n\nfunction Tag({text, variant, className}: TagProps) {\n  const {name, classes} = variantMap[variant];\n  return (\n    <span className={cn('me-2', className)}>\n      <span\n        className={cn(\n          'inline font-bold text-sm uppercase py-1 px-2 rounded',\n          classes\n        )}>\n        {text || name}\n      </span>\n    </span>\n  );\n}\n\nexport default Tag;\n"
  },
  {
    "path": "src/content/blog/2020/12/21/data-fetching-with-react-server-components.md",
    "content": "---\ntitle: \"バンドルサイズゼロの React Server Components の紹介\"\nauthor: Dan Abramov, Lauren Tan, Joseph Savona, and Sebastian Markbage\ndate: 2020/12/21\ndescription: 2020 年は長い 1 年でした。本年の最後に、我々の研究における特別なホリデーアップデートとして、バンドルサイズゼロで動作する React サーバコンポーネントの紹介をしたいと思います。\n---\n\nDecember 21, 2020 by [Dan Abramov](https://bsky.app/profile/danabra.mov), [Lauren Tan](https://twitter.com/potetotes), [Joseph Savona](https://twitter.com/en_JS), and [Sebastian Markbåge](https://twitter.com/sebmarkbage)\n\n---\n\n<Intro>\n\n2020 年は長い 1 年でした。本年の最後に、我々の研究における特別なホリデーアップデートとして、バンドルサイズゼロで動作する **React サーバコンポーネント**の紹介をしたいと思います。\n\n</Intro>\n\n---\n\nサーバコンポーネントの紹介のためのトークとデモを用意しました。休暇期間中にチェックするもよし、来年仕事に戻ってきたときに見てみるのでもよいでしょう。\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/TQQPAU21ZUw\" />\n\n**React サーバコンポーネントは現在も研究開発中です。**開発の透明性を高め、React コミュニティから初期のフィードバックを頂くために共有しています。そのための時間はまだまだありますので、**今すぐ追いつく必要はありません**！\n\nもし気になったなら以下の順番で見ていくのをお勧めします。\n\n1. **トークを見て** React サーバコンポーネントについて学び、デモを見る\n\n2. **[デモをクローン](http://github.com/reactjs/server-components-demo)**して自分のコンピュータ上で React サーバコンポーネントで遊んでみる\n\n3. 技術的に深い部分について **[RFC を読み（FAQ も最後にあります）](https://github.com/reactjs/rfcs/pull/188)**、フィードバックを送る\n\nRFC や Twitter の [@reactjs](https://twitter.com/reactjs) へのリプライで、皆さんの意見を聞けるのを楽しみにしています。それでは良い休暇を！ また来年お元気でお会いしましょう！\n"
  },
  {
    "path": "src/content/blog/2021/06/08/the-plan-for-react-18.md",
    "content": "---\ntitle: \"React 18に向けてのプラン\"\nauthor: Andrew Clark, Brian Vaughn, Christine Abernathy, Dan Abramov, Rachel Nabors, Rick Hanlon, Sebastian Markbage, and Seth Webster\ndate: 2021/06/08\ndescription: React チームより幾つかのお知らせがあります！ 次のメジャーバージョンとなる React 18 リリースに向けての作業を開始しました。コミュニティが React 18 の新機能を段階的に導入できるようにするため、ワーキンググループを作成しました。ライブラリの作者が試用してフィードバックを送れるようにするため、React 18 のアルファ版を公開しました。\n---\n\nJune 8, 2021 by [Andrew Clark](https://twitter.com/acdlite), [Brian Vaughn](https://github.com/bvaughn), [Christine Abernathy](https://twitter.com/abernathyca), [Dan Abramov](https://bsky.app/profile/danabra.mov), [Rachel Nabors](https://twitter.com/rachelnabors), [Rick Hanlon](https://twitter.com/rickhanlonii), [Sebastian Markbåge](https://twitter.com/sebmarkbage), and [Seth Webster](https://twitter.com/sethwebster)\n\n---\n\n<Intro>\n\nReact チームより幾つかのお知らせがあります！\n\n1. 次のメジャーバージョンとなる React 18 リリースに向けての作業を開始しました。\n2. コミュニティが React 18 の新機能を段階的に導入できるようにするため、ワーキンググループを作成しました。\n3. ライブラリの作者が試用してフィードバックを送れるようにするため、React 18 のアルファ版を公開しました。\n\nこれらのお知らせは、主にサードパーティのライブラリメンテナ向けです。ユーザ向けのアプリケーションを作成するために React を学んでいる、教えている、あるいは利用している方は、この投稿を無視していただいて問題ありません。もちろん興味があれば React 18 ワーキンググループでの議論をフォローすることは歓迎です！\n\n---\n\n</Intro>\n\n## React 18 の新機能 {/*whats-coming-in-react-18*/}\n\nReact 18 のリリース時点で、自動で有効になるパフォーマンス改善（例：[自動バッチング](https://github.com/reactwg/react-18/discussions/21)）、新たな API（例：[`startTransition`](https://github.com/reactwg/react-18/discussions/41)）、そして `React.lazy` の組み込みサポートを有する[新しいストリーミングサーバレンダラ](https://github.com/reactwg/react-18/discussions/37)が含まれています。\n\nこれらは、React 18 で我々が追加しようとしている新たなオプトインのメカニズムのおかげで実現可能になるものです。そのメカニズムとは「並行レンダリング」であり、これにより React は、UI の複数のバージョンを同時に用意しておくことができるようになります。これはほとんどの場面では裏で勝手に行われますが、あなたのアプリの実パフォーマンスおよび体感上のパフォーマンスを向上させる可能性を秘めています。\n\n我々が React の未来に向けて行ってきた様々なリサーチをフォローしてきた方であれば（そうすべきと言っている訳ではないですよ）、\"Concurrent Mode\" なる言葉を聞いたこと、あるいはそれのせいであなたのアプリが動かなくなるかもしれないということを聞いたことがあるかもしれません。これに関してのコミュニティのフィードバックを受けて、我々は段階的な導入に向けてのアップグレード戦略を再設計しました。イチかゼロかの「モード」の代わりに、並行レンダリングは新機能のどれかを利用するような更新がある場合にのみ有効化されるようになりました。実用上、これはつまり**書き換えをせずに React 18 を導入し、自分のペースで React 18 の新機能を試していけるようになる**ということです。\n\n## 段階的な導入戦略 {/*a-gradual-adoption-strategy*/}\n\nReact 18 における並行レンダリングはオプトインになるため、コンポーネントの振る舞いにおいてデフォルトで大きな破壊的変更があるということはなくなります。**いつものメジャーリリースのときに要する労力と大差ないレベルの最小限の書き換えで、あるいは書き換えゼロで、React 18 にアップグレードすることができます**。我々がいくつかのアプリを React 18 に移植した経験から、多くのユーザは半日以内にアップグレード作業を完了できるだろうと考えています。\n\nFacebook 内では既に数万のコンポーネントを並行レンダリングの機能を用いてリリースすることができました。我々の経験ではほとんどの React コンポーネントは追加の開発なしで「ごく普通に」動作することが分かっています。しかしコミュニティ全体で今回のアップグレードが確実にスムースに行くようにしたいと考えており、そのために本日、React 18 ワーキンググループを発表します。\n\n## コミュニティとの共同作業 {/*working-with-the-community*/}\n\nこのリリースで、私たちは新たな試みをしようとしています。React コミュニティ全体から、少数のエキスパート、開発者、ライブラリ作者、教育関係者を [React 18 ワーキンググループ](https://github.com/reactwg/react-18)に招待して、フィードバックをもらったり、質問をもらったり、リリースに向けての共同作業を行ってもらったりしているのです。初期は小さなグループであり、メンバーとして我々が望む全員を招待することはできませんでしたが、この試みがうまくいけば、将来的により多くの人を招待できると考えています！\n\nReact 18 ワーキンググループの目的は、既存のアプリケーションやライブラリが React 18 をスムースかつ段階的に採用できるように、エコシステムを整えることです。ワーキングループは [GitHub Discussions](https://github.com/reactwg/react-18/discussions) でホストされており、誰でも読めるようになっています。ワーキンググループのメンバーのみがフィードバックを送ったり、質問したり、アイディアを共有したりできるようになっています。このディスカッション用のリポジトリは、コアチームのメンバーも研究の成果を共有するために使用します。安定リリースが近づけば、あらゆる重要な情報はこのブログにも投稿されます。\n\nReact 18 へのアップグレードに関する詳細やリリースに関するその他の情報については、[React 18 アナウンス](https://github.com/reactwg/react-18/discussions/4)をご覧ください。\n\n## ワーキンググループへのアクセス方法 {/*accessing-the-react-18-working-group*/}\n\n[React 18 ワーキンググループのリポジトリ](https://github.com/reactwg/react-18)でのディスカッションは、誰でも閲覧可能です。\n\n当初はワーキンググループ内部で一気に興味が高まることが予想されますので、招待されたメンバーのみがスレッドを作成したりコメントしたりできるようになっています。しかしスレッドは誰でも見えるようになっていますので、全員が同じ情報にアクセスできます。ワーキンググループのメンバーにとって生産的な環境を整えつつ、より広いコミュニティへの透明性も確保するという意味で、これが良い落とし所だと考えています。\n\nもちろんこれまで通り、バグレポートや質問や一般的なフィードバックについては誰でも[イシュートラッカ](https://github.com/facebook/react/issues)に投稿することができます。\n\n## React 18 アルファ版を今すぐ試す {/*how-to-try-react-18-alpha-today*/}\n\n新しいアルファ版は [npm に `@alpha` タグ付きで定期的に公開](https://github.com/reactwg/react-18/discussions/9)されます。これらのリリースは我々のメインリポジトリにある最新のコミットを使ってビルドされます。新しい機能やバグ修正がマージされた場合、次の平日にはアルファ版で利用可能になります。\n\nアルファ版同士の間では動作や API について大きな変更が加わる可能性があります。**ユーザが利用する本番用アプリケーションではアルファ版は推奨されない**ということを銘記してください。\n\n## React 18 リリースタイミングの見通し {/*projected-react-18-release-timeline*/}\n\n具体的に予定されたリリース日時はまだありませんが、React 18 が本番アプリケーションで使えるようになるまでに、フィードバックを受けて改善を繰り返す期間が数か月ほど必要だろうと考えています。\n\n* ライブラリ作者向けのアルファ：本日より利用可能\n* 公開ベータ：少なくとも数か月後\n* リリース候補 (RC)：ベータから少なくとも数週間後\n* 一般向けリリース：RC から少なくとも数週間後\n\nリリースタイミングの見通しについての更なる詳細は[ワーキンググループ内の投稿](https://github.com/reactwg/react-18/discussions/9)で見ることができます。公開リリースが近づいたらこのブログでも情報をお伝えします。\n"
  },
  {
    "path": "src/content/blog/2021/12/17/react-conf-2021-recap.md",
    "content": "---\ntitle: \"React Conf 2021 振り返り\"\nauthor: Jesslyn Tannady and Rick Hanlon\ndate: 2021/12/17\ndescription: 先週、第 6 回の React Conf を開催しました。これまでの年度において、我々は React Conf のステージ上で、React Native や React Hooks といった業界を変えるような発表をお届けしてきました。本年度は、React 18 のリリースと並行レンダリング機能の段階的な採用から始まる我々のマルチプラットフォーム戦略についての話題を共有しました。\n---\n\nDecember 17, 2021 by [Jesslyn Tannady](https://twitter.com/jtannady) and [Rick Hanlon](https://twitter.com/rickhanlonii)\n\n---\n\n<Intro>\n\n先週、第 6 回の React Conf を開催しました。これまでの年度において、我々は React Conf のステージ上で、[_React Native_](https://engineering.fb.com/2015/03/26/android/react-native-bringing-modern-web-techniques-to-mobile/) や [_React Hooks_](https://reactjs.org/docs/hooks-intro.html) といった業界を変えるような発表をお届けしてきました。本年度は、React 18 のリリースと並行レンダリング機能の段階的な採用から始まる我々のマルチプラットフォーム戦略についての話題を共有しました。\n\n</Intro>\n\n---\n\nReact Conf がオンラインで開催されたのは今回が初めてですが、イベントは 8 つの言語に翻訳され、無料でストリーミング配信されました。世界中の参加者が、カンファレンスの Discord や、すべてのタイムゾーンの方がアクセスしやすいように行われたリプレイイベントに参加しました。登録者数は 50,000 人以上に達し、19 の演題は 60,000 回以上閲覧され、両イベントを通じて Discord には 5,000 人の参加者が集まりました。\n\nすべての発表は[オンラインストリーミングで閲覧可能です](https://www.youtube.com/watch?v=FZ0cG47msEk&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa)。\n\n以下が、ステージ上で共有された内容のおさらいとなります。\n\n## React 18 と並行レンダリング機能 {/*react-18-and-concurrent-features*/}\n\nキーノートでは、React 18 から始まる React の将来のビジョンについて共有しました。\n\nReact 18 には、長らく待望されてきた並行レンダラと、サスペンスへの機能追加が、大きな破壊的変更なしに入ります。アプリは、他のメジャーリリースのときと変わらない程度の労力で React 18 にアップグレードして、並行レンダリングの機能を段階的に採用していくことが可能です。\n\n**これはつまり、並行モードというものがなくなった、ということです。並行レンダリング機能のみが存在します。**\n\nキーノートではさらに、サスペンス、サーバコンポーネント、新たな React ワーキンググループ、そして React Native の長期的な多プラットフォーム戦略についてお話ししました。\n\n[Andrew Clark](https://twitter.com/acdlite)、[Juan Tejada](https://twitter.com/_jstejada)、[Lauren Tan](https://twitter.com/potetotes)、[Rick Hanlon](https://twitter.com/rickhanlonii) によるキーノートの全編は以下でご覧ください：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/FZ0cG47msEk\" />\n\n## アプリ開発者にとっての React 18 {/*react-18-for-application-developers*/}\n\nキーノートでは、React 18 RC が評価のために今すぐ利用可能であることを発表しました。さらなるフィードバックを待ちつつも、これが来年初頭に我々が安定版として公開する予定の React のバージョンそのものとなります。\n\nReact 18 RC を試すには、dependencies をアップグレードしてください：\n\n```bash\nnpm install react@rc react-dom@rc\n```\n\nそして新しい `createRoot` API を使うように切り替えます：\n\n```js\n// before\nconst container = document.getElementById('root');\nReactDOM.render(<App />, container);\n\n// after\nconst container = document.getElementById('root');\nconst root = ReactDOM.createRoot(container);\nroot.render(<App/>);\n```\n\nReact 18 へのアップグレードのデモについては、[Shruti Kapoor](https://twitter.com/shrutikapoor08) による以下の発表をご覧ください：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/ytudH8je5ko\" />\n\n## サスペンスを使ったストリーミングサーバレンダリング {/*streaming-server-rendering-with-suspense*/}\n\nReact 18 にはサスペンスを使ったサーバサイドレンダリングのパフォーマンス改善が含まれています。\n\nストリーミングサーバレンダリングによって、サーバ側で React コンポーネントから HTML を作成し、それをユーザにストリームで送ることができます。React 18 では、`Suspense` を使ってアプリを小さな単位に分割し、それぞれがアプリの他の部分をブロックせずに独立してストリーミング処理できるようになります。これによりユーザはより早くコンテンツを見ることができ、素早くインタラクションができるようになる、ということです。\n\n詳しくは、[Shaundai Person](https://twitter.com/shaundai) による以下の発表をご覧ください：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/pj5N-Khihgc\" />\n\n## React ワーキンググループの立ち上げ {/*the-first-react-working-group*/}\n\nReact 18 では、エキスパートや開発者、ライブラリメンテナ、教育者のグループと協力して作業するため、初めてワーキンググループを立ち上げました。彼らとともに、段階的な採用戦略を作成し、`useId`、`useSyncExternalStore`, `useInsertionEffect` といった API の改善を行ってきました。\n\nこの試みの概要については、[Aakansha' Doshi](https://twitter.com/aakansha1216) による発表をご覧ください：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/qn7gRClrC9U\" />\n\n## React の開発者向けツーリング {/*react-developer-tooling*/}\n\nこのリリースにおける新機能をサポートするため、新たに構成された React DevTools チームと、開発者が React アプリをデバッグしやすくするための新たなタイムラインプロファイラについて発表しました。\n\n新たな DevTools の機能についての詳細およびデモについては、[Brian Vaughn](https://twitter.com/brian_d_vaughn) による発表をご覧ください：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/oxDfrke8rZg\" />\n\n## memo 不要の React {/*react-without-memo*/}\n\nより将来に目を向けた話として、[Xuan Huang (黄玄)](https://twitter.com/Huxpro) は、React Labs が行っている自動メモ化コンパイラに関する研究の現状についてお話ししました。この発表とコンパイラのプロトタイプについての詳細・デモは以下でご覧ください：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/lGEMwh32soc\" />\n\n## React ドキュメントキーノート {/*react-docs-keynote*/}\n\nReact の学習や React による設計についての一連の発表は [Rachel Nabors](https://twitter.com/rachelnabors) からスタートしました。その中では React の新ドキュメントに対する我々の注力についてのキーノートがありました（[react.dev としてリリース済み](/blog/2023/03/16/introducing-react-dev)）：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/mneDaMYOKP8\" />\n\n## さらに… {/*and-more*/}\n\n**React の学習や React における設計についての以下のような発表がありました：**\n\n* Debbie O'Brien: [Things I learnt from the new React docs](https://youtu.be/-7odLW_hG7s).\n* Sarah Rainsberger: [Learning in the Browser](https://youtu.be/5X-WEQflCL0).\n* Linton Ye: [The ROI of Designing with React](https://youtu.be/7cPWmID5XAk).\n* Delba de Oliveira: [Interactive playgrounds with React](https://youtu.be/zL8cz2W0z34).\n\n**Relay、React Native、PyTorch チームからの発表：**\n\n* Robert Balicki: [Re-introducing Relay](https://youtu.be/lhVGdErZuN4).\n* Eric Rozell and Steven Moyes: [React Native Desktop](https://youtu.be/9L4FFrvwJwY).\n* Roman Rädle: [On-device Machine Learning for React Native](https://youtu.be/NLj73vrc2I8)\n\n**アクセシビリティ、ツーリング、サーバコンポーネントについてコミュニティからの発表：**\n\n* Daishi Kato: [React 18 for External Store Libraries](https://youtu.be/oPfSC5bQPR8).\n* Diego Haz: [Building Accessible Components in React 18](https://youtu.be/dcm8fjBfro8).\n* Tafu Nakazaki: [Accessible Japanese Form Components with React](https://youtu.be/S4a0QlsH0pU).\n* Lyle Troxell: [UI tools for artists](https://youtu.be/b3l4WxipFsE).\n* Helen Lin: [Hydrogen + React 18](https://youtu.be/HS6vIYkSNks).\n\n## 謝辞 {/*thank-you*/}\n\n我々自身でカンファレンスを計画したのは今年が初めてでした。多くの方に感謝したいと思います。\n\nまずは発表者の方々に感謝します。[Aakansha Doshi](https://twitter.com/aakansha1216)、[Andrew Clark](https://twitter.com/acdlite)、[Brian Vaughn](https://twitter.com/brian_d_vaughn)、[Daishi Kato](https://twitter.com/dai_shi)、[Debbie O'Brien](https://twitter.com/debs_obrien)、[Delba de Oliveira](https://twitter.com/delba_oliveira)、[Diego Haz](https://twitter.com/diegohaz)、[Eric Rozell](https://twitter.com/EricRozell)、[Helen Lin](https://twitter.com/wizardlyhel)、[Juan Tejada](https://twitter.com/_jstejada)、[Lauren Tan](https://twitter.com/potetotes)、[Linton Ye](https://twitter.com/lintonye)、[Lyle Troxell](https://twitter.com/lyle)、[Rachel Nabors](https://twitter.com/rachelnabors)、[Rick Hanlon](https://twitter.com/rickhanlonii)、[Robert Balicki](https://twitter.com/StatisticsFTW)、[Roman Rädle](https://twitter.com/raedle)、[Sarah Rainsberger](https://twitter.com/sarah11918)、[Shaundai Person](https://twitter.com/shaundai)、[Shruti Kapoor](https://twitter.com/shrutikapoor08)、[Steven Moyes](https://twitter.com/moyessa)、[Tafu Nakazaki](https://twitter.com/hawaiiman0)、[Xuan Huang (黄玄)](https://twitter.com/Huxpro)。\n\n発表についてのフィードバックを頂いた方々に感謝します。[Andrew Clark](https://twitter.com/acdlite)、[Dan Abramov](https://bsky.app/profile/danabra.mov)、[Dave McCabe](https://twitter.com/mcc_abe)、[Eli White](https://twitter.com/Eli_White)、[Joe Savona](https://twitter.com/en_JS)、[Lauren Tan](https://twitter.com/potetotes)、[Rachel Nabors](https://twitter.com/rachelnabors)、[Tim Yung](https://twitter.com/yungsters)。\n\nカンファレンス Discord のセットアップを行い Discord 管理者になっていただいた [Lauren Tan](https://twitter.com/potetotes) に感謝します。\n\n全体の方向性や、多様性とその受け入れについて助言をいただいた [Seth Webster](https://twitter.com/sethwebster) に感謝します。\n\nモデレーション関係業務の先頭に立っていただいた [Rachel Nabors](https://twitter.com/rachelnabors)、そしてモデレーションガイドを作成し、モデレーションチームを率い、翻訳者とモデレータの教育を行い、両イベントのモデレーションに協力していただいた [Aisha Blake](https://twitter.com/AishaBlake) に感謝します。\n\nモデレータの方々に感謝します。[Jesslyn Tannady](https://twitter.com/jtannady)、[Suzie Grange](https://twitter.com/missuze)、[Becca Bailey](https://twitter.com/beccaliz)、[Luna Wei](https://twitter.com/lunaleaps)、[Joe Previte](https://twitter.com/jsjoeio)、[Nicola Corti](https://twitter.com/Cortinico)、[Gijs Weterings](https://twitter.com/gweterings)、[Claudio Procida](https://twitter.com/claudiopro)、Julia Neumann、Mengdi Chen、Jean Zhang、Ricky Li、[Xuan Huang (黄玄)](https://twitter.com/Huxpro).\n\nリプレイイベントのモデレーションやコミュニティへの活動に協力頂いた、[React India](https://www.reactindia.io/) の [Manjula Dube](https://twitter.com/manjula_dube)、[Sahil Mhapsekar](https://twitter.com/apheri0)、Vihang Patel、および [React China](https://twitter.com/ReactChina) の [Jasmine Xie](https://twitter.com/jasmine_xby)、[QiChang Li](https://twitter.com/QCL15)、[YanLun Li](https://twitter.com/anneincoding) に感謝します。\n\nカンファレンスのウェブサイトを構築するのに使った [Virtual Event Starter Kit](https://vercel.com/virtual-event-starter-kit) を公開していただいた Vercel、そして Next.js Conf での経験を共有していただいた [Lee Robinson](https://twitter.com/leeerob) と [Delba de Oliveira](https://twitter.com/delba_oliveira) に感謝します。\n\nカンファレンス運営の経験や [RustConf](https://rustconf.com/) 運営からの教訓を共有していただいた [Leah Silber](https://twitter.com/wifelette) に感謝します。彼女の書籍 [Event Driven](https://leanpub.com/eventdriven/) とそこに含まれたカンファレンス運営に関する助言に感謝します。\n\nWomen of React Conf の運営経験について共有していただいた [Kevin Lewis](https://twitter.com/_phzn) と [Rachel Nabors](https://twitter.com/rachelnabors) に感謝します。\n\n企画全体にわたってアドバイスやアイディアをいただいた [Aakansha Doshi](https://twitter.com/aakansha1216)、[Laurie Barth](https://twitter.com/laurieontech)、[Michael Chan](https://twitter.com/chantastic)、[Shaundai Person](https://twitter.com/shaundai) に感謝します。\n\nカンファレンスウェブサイトとチケットのデザイン・構築に協力いただいた [Dan Lebowitz](https://twitter.com/lebo) に感謝します。\n\nキーノートや Meta 従業員の発表の録画をしていただいた Laura Podolak Waddell、Desmond Osei-Acheampong、Mark Rossi、Josh Toberman および Facebook Video Productions の他の方々に感謝します。\n\nカンファレンスの運営、ストリームされる全ビデオの編集、全発表の翻訳、そして多言語での Discord のモデレーションについて協力いただいたパートナーの HitPlay に感謝します。\n\n最後に、この React Conf を素晴らしいものにしていただいたすべての参加者の皆さんに感謝します！\n"
  },
  {
    "path": "src/content/blog/2022/03/08/react-18-upgrade-guide.md",
    "content": "---\ntitle: \"How to Upgrade to React 18\"\nauthor: Rick Hanlon\ndate: 2022/03/08\ndescription: リリース告知の記事でお伝えしたとおり、React 18 には新たな並行レンダラを用いた機能が加わっており、既存のアプリケーションが段階的に採用できる方法も提供しています。この投稿では、React 18 にアップグレードするためのステップについてご案内します。\n---\n\nMarch 08, 2022 by [Rick Hanlon](https://twitter.com/rickhanlonii)\n\n---\n\n<Intro>\n\n[リリース告知の記事](/blog/2022/03/29/react-v18)でお伝えしたとおり、React 18 には新たな並行レンダラを用いた機能が加わっており、既存のアプリケーションが段階的に採用できる方法も提供しています。この投稿では、React 18 にアップグレードするためのステップについてご案内します。\n\nReact 18 にアップグレードする際に遭遇した[問題は報告](https://github.com/facebook/react/issues/new/choose)をお願いします。\n\n</Intro>\n\n<Note>\n\nReact Native ユーザ向け：React 18 のリリースは React Native の将来のバージョンで行います。これは、このブログ記事で紹介する新機能を活用した新たな React Native アーキテクチャに React 18 が依存しているからです。詳細は[こちらの React Conf キーノート](https://www.youtube.com/watch?v=FZ0cG47msEk&t=1530s)を参照してください。\n\n</Note>\n\n---\n\n## インストール {/*installing*/}\n\nReact の最新バージョンをインストールするには：\n\n```bash\nnpm install react react-dom\n```\n\nYarn をお使いの場合：\n\n```bash\nyarn add react react-dom\n```\n\n## クライアントレンダリング API への変更 {/*updates-to-client-rendering-apis*/}\n\nまず React 18 をインストールすると、以下のような警告がコンソールに表示されます：\n\n<ConsoleBlock level=\"error\">\n\nReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot\n\n</ConsoleBlock>\n\nReact では複数のルートを管理する際の使い勝手を改善する、新しいルート API を導入しています。新しいルート API によって新しい並行レンダラも有効になるため、並行処理機能にオプトインできるようになります。\n\n```js\n// Before\nimport { render } from 'react-dom';\nconst container = document.getElementById('app');\nrender(<App tab=\"home\" />, container);\n\n// After\nimport { createRoot } from 'react-dom/client';\nconst container = document.getElementById('app');\nconst root = createRoot(container); // createRoot(container!) if you use TypeScript\nroot.render(<App tab=\"home\" />);\n```\n\n`unmountComponentAtNode` も `root.unmount` に置き換わりました：\n\n```js\n// Before\nunmountComponentAtNode(container);\n\n// After\nroot.unmount();\n```\n\nまたレンダー後のコールバックも削除されました。サスペンスを使った場合に、大抵は期待される結果にならないためです：\n\n```js\n// Before\nconst container = document.getElementById('app');\nrender(<App tab=\"home\" />, container, () => {\n  console.log('rendered');\n});\n\n// After\nfunction AppWithCallbackAfterRender() {\n  useEffect(() => {\n    console.log('rendered');\n  });\n\n  return <App tab=\"home\" />\n}\n\nconst container = document.getElementById('app');\nconst root = createRoot(container);\nroot.render(<AppWithCallbackAfterRender />);\n```\n\n<Note>\n\n以前の render のコールバック API に一対一で対応するものは存在せず、ユースケースによって対応は異なります。詳細はワーキンググループの投稿 [Replacing render with createRoot](https://github.com/reactwg/react-18/discussions/5) を参照してください。\n\n</Note>\n\n最後に、あなたのアプリでサーバサイドレンダリングとハイドレーションを使用している場合は、`hydrate` を `hydrateRoot` にアップグレードしてください：\n\n```js\n// Before\nimport { hydrate } from 'react-dom';\nconst container = document.getElementById('app');\nhydrate(<App tab=\"home\" />, container);\n\n// After\nimport { hydrateRoot } from 'react-dom/client';\nconst container = document.getElementById('app');\nconst root = hydrateRoot(container, <App tab=\"home\" />);\n// Unlike with createRoot, you don't need a separate root.render() call here.\n```\n\n詳細は[こちらのワーキングループのディスカッション](https://github.com/reactwg/react-18/discussions/5)を参照してください。\n\n<Note>\n\n**アップグレード後にアプリが動かなくなった場合は、アプリを `<StrictMode>` でラップしていないか確認してください。**[strict モードは React 18 でより厳密になっている](#updates-to-strict-mode)ため、開発モードで新たに追加されたチェックにあなたのコンポーネントがすべて適合していないのかもしれません。もし strict モードを外したらアプリが動くようになった場合、アップグレード中は外したままにして、指摘された問題を修正してから元に戻す（トップにでもツリーの一部に対してでも）のでも構いません。\n\n</Note>\n\n## サーバレンダリング API への変更 {/*updates-to-server-rendering-apis*/}\n\nこのリリースでは、`react-dom/server` を刷新し、サーバ側でのサスペンスやストリーミング SSR がフルサポートされるようになりました。この変更の一環として、サーバ側での逐次的なサスペンスのストリーミング処理をサポートしない既存の Node ストリーミング API を非推奨としました。\n\n以下の API を使うと警告が出るようになります：\n\n* `renderToNodeStream`: **非推奨 ⛔️️**\n\n代わりに Node 環境でのストリーミングには以下を使ってください：\n* `renderToPipeableStream`: **New ✨**\n\nまた、Deno や Cloudflare Workers のようなモダンなエッジランタイム環境でサスペンス付き SSR ストリーミングをサポートする、新たな API を導入します：\n* `renderToReadableStream`: **New ✨**\n\n以下の API はこれからも動作しますが、サスペンスのサポートに制限がつきます：\n* `renderToString`: **制限付き** ⚠️\n* `renderToStaticMarkup`: **制限付き** ⚠️\n\n最後に、電子メールをレンダーする目的であれば以下の API を引き続き利用できます：\n* `renderToStaticNodeStream`\n\nサーバレンダリング用 API についての詳細は、ワーキンググループの投稿 [Upgrading to React 18 on the server](https://github.com/reactwg/react-18/discussions/22) と [deep dive on the new Suspense SSR Architecture](https://github.com/reactwg/react-18/discussions/37) を、また React Conf 2011 での [Shaundai Person](https://twitter.com/shaundai) の発表 [Streaming Server Rendering with Suspense](https://www.youtube.com/watch?v=pj5N-Khihgc) をご覧ください。\n\n## TypeScript 型定義の変更 {/*updates-to-typescript-definitions*/}\n\nプロジェクトで TypeScript を使っている場合、依存の `@types/react` と `@types/react-dom` を最新バージョンに更新する必要があります。新たな型はより安全であり、これまで型チェッカに無視されていた問題を捕捉することができます。最も大きな変更は、props を定義する際に `children` プロパティを明示的に列挙する必要があるということです。例えば：\n\n```typescript{3}\ninterface MyButtonProps {\n  color: string;\n  children?: React.ReactNode;\n}\n```\n\n型にのみ関連する変更の全リストについては [React 18 の型についてのプルリクエスト](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/56210) を参照してください。ライブラリ型についての修正のサンプルへのリンクがあり、コードをどのように調整すればいいか分かるようになっています。[自動移行スクリプト](https://github.com/eps1lon/types-react-codemod)を使うことで、あなたのアプリケーションコードをより新しく、より安全な型定義にすばやく移行しやすくなります。\n\n型に関するバグを見つけた場合は、DefinitelyTyped リポジトリで [issue を登録](https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/new?category=issues-with-a-types-package)してください。\n\n## 自動バッチング {/*automatic-batching*/}\n\nReact 18 はデフォルトでより多くのバッチング (batching) を行うことで、標準状態でのパフォーマンスを改善します。バッチングとは React がパフォーマンスのために複数のステート更新をグループ化して、単一の再レンダーにまとめることを指します。React 18 より前は、React のイベントハンドラ内での更新のみバッチ処理されていました。promise や setTimeout、ネイティブのイベントハンドラやその他あらゆるイベント内で起きる更新はデフォルトではバッチ処理されていませんでした。\n\n```js\n// Before React 18 only React events were batched\n\nfunction handleClick() {\n  setCount(c => c + 1);\n  setFlag(f => !f);\n  // React will only re-render once at the end (that's batching!)\n}\n\nsetTimeout(() => {\n  setCount(c => c + 1);\n  setFlag(f => !f);\n  // React will render twice, once for each state update (no batching)\n}, 1000);\n```\n\n\nReact 18 以降で `createRoot` を使うと、すべての更新はどこで発生したかに関わらず、自動でバッチ処理されます。つまり、タイムアウト、promise、ネイティブイベントハンドラおよびその他のあらゆるイベント内で起きた更新は、React イベントで起こった更新と同様にバッチ処理されます：\n\n```js\n// After React 18 updates inside of timeouts, promises,\n// native event handlers or any other event are batched.\n\nfunction handleClick() {\n  setCount(c => c + 1);\n  setFlag(f => !f);\n  // React will only re-render once at the end (that's batching!)\n}\n\nsetTimeout(() => {\n  setCount(c => c + 1);\n  setFlag(f => !f);\n  // React will only re-render once at the end (that's batching!)\n}, 1000);\n```\n\nこれは破壊的変更ですが、これによりレンダー処理が軽くなり、したがってアプリのパフォーマンスが向上することが期待されます。自動バッチングからオプトアウトするために `flushSync` を使うことができます：\n\n```js\nimport { flushSync } from 'react-dom';\n\nfunction handleClick() {\n  flushSync(() => {\n    setCounter(c => c + 1);\n  });\n  // React has updated the DOM by now\n  flushSync(() => {\n    setFlag(f => !f);\n  });\n  // React has updated the DOM by now\n}\n```\n\n詳細については、[Automatic batching 詳解](https://github.com/reactwg/react-18/discussions/21) を参照してください。\n\n## ライブラリ向けの新 API {/*new-apis-for-libraries*/}\n\nReact 18 ワーキンググループでライブラリメンテナと共同で作業を行い、スタイルや外部ストアといった分野に特有のユースケースで並行レンダー機能をサポートするため、新しい API を作成しました。一部のライブラリは、React 18 をサポートするために以下の API に切り替える必要があるかもしれません：\n\n* `useSyncExternalStore` は、ストアへの更新を強制的に同期的に行うことで、並行読み取りを外部ストアがサポートできるようにするための新たなフックです。この新しい API は React 外部の状態を扱うあらゆるライブラリにとって推奨されるものです。詳細は [useSyncExternalStore 概要](https://github.com/reactwg/react-18/discussions/70)および [useSyncExternalStore API 詳細](https://github.com/reactwg/react-18/discussions/86)を参照してください。\n* `useInsertionEffect` は、CSS-in-JS ライブラリがレンダー時にスタイルを注入する際のパフォーマンス上の問題に対処できるようにするための新しいフックです。すでに CSS-in-JS ライブラリを構築しているのでなければ、これを使うことはまずないでしょう。このフックは、DOM が書き換えられた後、レイアウト副作用 (layout effect) が新しいレイアウトを読み込む前に実行されます。これにより React 17 およびそれ以前から既に存在した問題が解決されますが、React 18 では並行レンダー中にブラウザに処理が渡り、そこでレイアウトが再計算される可能性があるため、より重要です。詳細は [Library Upgrade Guide for `<style>`](https://github.com/reactwg/react-18/discussions/110) を参照してください。\n\nReact 18 では `startTransition`、`useDeferredValue` や `useId` のような新しい API も導入しており、[リリース告知記事](/blog/2022/03/29/react-v18)にて詳細をお伝えします。\n\n## strict モードへの変更 {/*updates-to-strict-mode*/}\n\n将来的に、React が state を保ったままで UI の一部分を追加・削除できるような機能を導入したいと考えています。例えば、ユーザがタブを切り替えて画面を離れてから戻ってきた場合に、React が以前の画面をすぐに表示できるようにしたいのです。これを可能にするため、React は同じ state を使用してツリーをアンマウント・再マウントします。\n\nこの機能により、React の標準状態でのパフォーマンスが向上しますが、コンポーネントは副作用が何度も登録されたり破棄されたりすることに対して耐性を持つことが必要になります。ほとんどの副作用は何の変更もなく動作しますが、一部の副作用は一度しか登録・破棄されないものと想定しています。\n\nこの問題に気付きやすくするために、React 18 は strict モードに新しい開発時専用のチェックを導入します。この新しいチェックは、コンポーネントが初めてマウントされるたびに、すべてのコンポーネントを自動的にアンマウント・再マウントし、かつ 2 回目のマウントで以前の state を復元します。\n\nこれまでは、React はコンポーネントをマウントして以下のように副作用を作成してきました：\n\n```\n* React がコンポーネントをマウント\n  * レイアウト副作用 (layout effect) を作成\n  * （通常の）副作用を作成\n```\n\nReact 18 の strict モードでは、開発時にコンポーネントがマウントされた場合、React はコンポーネントの即時アンマウント・再マウントをシミュレーションします：\n\n```\n* React がコンポーネントをマウント\n    * レイアウト副作用を作成\n    * 副作用を作成\n* マウントされたコンポーネント内で副作用の破棄をシミュレート\n    * レイアウト副作用を破棄\n    * 副作用を破棄\n* マウントされたコンポーネント内で以前の state を復元し副作用の再生成をシミュレート\n    * レイアウト副作用を作成\n    * 副作用の作成用コードの実行\n```\n\n詳細については、ワーキンググループの投稿 [Adding Reusable State to StrictMode](https://github.com/reactwg/react-18/discussions/19) と [How to support Reusable State in Effects](https://github.com/reactwg/react-18/discussions/18) を参照してください。\n\n## テスト環境の設定 {/*configuring-your-testing-environment*/}\n\nまずテストで `createRoot` を使うようにアップデートした場合、テストコンソールに以下の警告が表示されます：\n\n<ConsoleBlock level=\"error\">\n\nThe current testing environment is not configured to support act(...)\n\n</ConsoleBlock>\n\nこれを修正するには、テスト実行前に `globalThis.IS_REACT_ACT_ENVIRONMENT` を `true` に設定します：\n\n```js\n// In your test setup file\nglobalThis.IS_REACT_ACT_ENVIRONMENT = true;\n```\n\nこのフラグの目的は、React がユニットテスト的な環境で実行されている、と React に伝えることです。React は更新を `act` でラップし忘れた場合に、有用な警告を表示するようになります。\n\nこのフラグを `false` に設定することで `act` が必要ないと React に伝えることもできます。これはフル機能のブラウザ環境をシミュレートする end-to-end テストにおいて有用です。\n\n将来的には、テストライブラリがこれを自動で設定するようになることを期待しています。例えば [React Testing Library の次期バージョンは React 18 の組み込みサポートを有しており](https://github.com/testing-library/react-testing-library/issues/509#issuecomment-917989936)、追加の設定が不要となっています。\n\nワーキングループで[テスト用 `act` API および関連する変更に関しての背景](https://github.com/reactwg/react-18/discussions/102)が閲覧可能です。\n\n## Internet Explorer のサポート終了 {/*dropping-support-for-internet-explorer*/}\n\nこのリリースで、React は [2022 年 6 月 15 日にサポート外](https://blogs.windows.com/windowsexperience/2021/05/19/the-future-of-internet-explorer-on-windows-10-is-in-microsoft-edge) となる Internet Explorer のサポートを終了します。この変更を今行うのは、React 18 で導入される新機能はマイクロタスクのようなモダンなブラウザの機能を使っており、IE でうまくポリフィルできないからです。\n\nInternet Explorer のサポートが必要な場合は、React 17 を使い続けることをお勧めします。\n\n## 非推奨化 {/*deprecations*/}\n\n* `react-dom`: `ReactDOM.render` は非推奨となりました。使うと警告が表示され、アプリは React 17 モードで動作します。\n* `react-dom`: `ReactDOM.hydrate` は非推奨となりました。使うと警告が表示され、アプリは React 17 モードで動作します。\n* `react-dom`: `ReactDOM.unmountComponentAtNode` は非推奨となりました。\n* `react-dom`: `ReactDOM.renderSubtreeIntoContainer` は非推奨となりました。\n* `react-dom/server`: `ReactDOMServer.renderToNodeStream` は非推奨となりました。\n\n## その他の破壊的変更 {/*other-breaking-changes*/}\n\n* **useEffect のタイミング統一**：React はクリックやキー押下のような個別のユーザ入力によって更新がトリガされた場合に副作用関数を常に同期的に処理するようになりました。これまで、この挙動は必ずしも予測可能な一貫したものではありませんでした。\n* **ハイドレーション時のエラーの厳格化**：テキストコンテンツが存在しない、あるいは余分に存在することによるハイドレーションのミスマッチは、警告ではなくエラーとして扱われるようになりました。今後 React はサーバのマークアップに適合させるために個別のノードをクライアント側で挿入したり削除したりといった「応急処置」を試みないようになるため、ツリー内の直近の `<Suspense>` バウンダリまでクライアント側のレンダーを使うための逆戻りが発生してしまいます。この変更により、ハイドレーションされたツリーに一貫性があることが保証され、ハイドレーション時のミスマッチにより起こりうる個人情報やセキュリティ絡みの問題を防止できます。\n* **サスペンス内のツリーが常に一貫性のあるものに**：コンポーネントがツリーに完全に追加される前にサスペンドした場合、React はそれを不完全な状態のままツリーに追加したり、副作用を起動したりはしません。その代わり、React は新しいツリーを完全に破棄し、非同期の操作が完了するのを待ち、最初からレンダーを再試行します。React は再試行を、並行的に、ブラウザをブロックせずに行います。\n* **サスペンスとレイアウト副作用**：ツリーが再サスペンドしてフォールバックに逆戻りする場合、React はレイアウト用の副作用をクリーンアップし、バウンダリ内のコンテンツが再表示されるときにそれらを再作成するようになりました。これにより、サスペンスと一緒に使用されたときにコンポーネントライブラリがレイアウトを正しく測定できないという問題が修正されます。\n* **JS 環境の要件変更**：React は `Promise`、`Symbol`、`Object.assign` のようなモダンブラウザの機能に依存するようになりました。モダンなブラウザ機能についてネイティブ実装していないか非標準な実装をしている Internet Explorer のような古いブラウザやデバイスをサポートする場合は、バンドルされたアプリにグローバルなポリフィルを含めることを検討してください。\n\n## その他の注目すべき変更 {/*other-notable-changes*/}\n\n### React {/*react*/}\n\n* **コンポーネントが `undefined` を return できるように**：React はコンポーネントから `undefined` が返された場合でも警告しなくなりました。これにより、コンポーネントからの返り値として許される値が、コンポーネントツリーの中間で許可される値と合致するようになりました。JSX の前に `return` 文を書き忘れるといったミスを防ぐためには、リンタを使用することをお勧めします。\n* **テストにおいて `act` 警告がオプトインに**：End-to-end のテストを実行している場合、`act` 警告は不要です。[オプトインする](https://github.com/reactwg/react-18/discussions/102)メカニズムを用意しましたので、それが有用であるユニットテストの場合にのみ有効化できるようになりました。\n* **アンマウント済みコンポーネントにおける `setState` で警告を表示しないように**：これまで React は、`setState` がアンマウント済みのコンポーネントでコールされた場合、メモリリークに関する警告を表示してきました。この警告は購読に関する問題のために存在していましたが、state をセットしても問題ないシナリオでもこの警告にぶつかることが多く、また回避しようとした場合余計に悪いコードになってしまっていました。この警告は[削除](https://github.com/facebook/react/pull/22114)されました。\n* **コンソールログの抑止を廃止**：strict モードを利用する場合、React はコンポーネントを 2 回レンダーして、予期しない副作用がないか見つけやすくします。React 17 では、ログが見やすくなるようにそのうちの 1 回ではコンソールログを抑止するようにしていました。これが混乱を招くという[コミュニティからのフィードバック](https://github.com/facebook/react/issues/21783)を受けて、このような抑止を行うことを止めました。代わりに、React DevTools をインストールしている場合は、2 回目のレンダーでのログはグレーで表示されるようになりました。完全に抑止するためのオプション（デフォルトではオフ）も存在します。\n* **メモリ使用量の改善**：React はアンマウント時に内部のフィールドをより多く消去するようになったため、あなたのアプリに未修正のメモリリークがあった場合の悪影響が軽減されます。\n\n### React DOM Server {/*react-dom-server*/}\n\n* **`renderToString`**：サーバ側でサスペンドが起きた場合でもエラーにならなくなりました。代わりに、直近の `<Suspense>` にあるフォールバック HTML を出力し、クライアント側で同じコンテンツのレンダーを再試行するようになります。とはいえ、`renderToPipeableStream` や `renderToReadableStream` のようなストリーミング API に切り替えることが推奨されます。\n* **`renderToStaticMarkup`**：サーバ側でサスペンドが起きた場合でもエラーにならなくなりました。代わりに、直近の `<Suspense>` にあるフォールバック HTML を出力します。\n\n## Changelog {/*changelog*/}\n\n[変更履歴の全リストはこちら](https://github.com/facebook/react/blob/main/CHANGELOG.md)を参照してください。\n"
  },
  {
    "path": "src/content/blog/2022/03/29/react-v18.md",
    "content": "---\ntitle: \"React v18.0\"\nauthor: The React Team\ndate: 2022/03/08\ndescription: React 18 が npm で利用可能になりました！ 前回の投稿にて、アプリを React 18 にアップグレードするためのステップバイステップガイドを共有しました。この投稿では、React 18 の新機能や、将来に向けての展望をお伝えします。\n---\n\nMarch 29, 2022 by [The React Team](/community/team)\n\n---\n\n<Intro>\n\nReact 18 が npm で利用可能になりました！ 前回の投稿にて、[アプリを React 18 にアップグレードする](/blog/2022/03/08/react-18-upgrade-guide)ためのステップバイステップガイドを共有しました。この投稿では、React 18 の新機能や、将来に向けての展望をお伝えします。\n\n</Intro>\n\n---\n\nこの最新のメジャーバージョンには、自動バッチング (automatic batching) のような自動で有効になる機能改善、startTransition のような新たな API、そしてサスペンス (suspense) に対応したストリーミングでのサーバサイドレンダリング機能が含まれています。\n\nReact 18 の機能の多くが基盤としているのは新たに加わった並行レンダラ (concurrent renderer) であり、これが強力な新機能群を実現するために裏で働くようになっています。React の並行処理機能はオプトインであり、並行処理機能を使う場合にのみ有効になるものですが、これは皆さんのアプリ作成方法に大きな影響を与えるものであると思っています。\n\n我々は React で並行処理をサポートするために何年ものあいだ研究開発を重ねてきており、特に既存ユーザが段階的に採用できる方法を提供することに関しては注意を払ってきました。昨年の夏に [React 18 ワーキンググループ](/blog/2021/06/08/the-plan-for-react-18)を作成し、エキスパートやコミュニティからフィードバックを集め、React のエコシステム全体がスムースにアップグレードできるようにしてきました。\n\nまた、React Conf 2021 でも多くのことを共有してきました。\n\n* [キーノート](https://www.youtube.com/watch?v=FZ0cG47msEk&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa)では、素晴らしいユーザ体験を開発者が簡単に構築できるようにするという我々の使命に React 18 がどう関わるのかについて説明しています。\n* [Shruti Kapoor](https://twitter.com/shrutikapoor08) は [React 18 の新機能の使い方についてデモを行っています](https://www.youtube.com/watch?v=ytudH8je5ko&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=2)。\n* [Shaundai Person](https://twitter.com/shaundai) は[サスペンスを用いたストリーミングサーバレンダリング](https://www.youtube.com/watch?v=pj5N-Khihgc&list=PLNG_1j3cPCaZZ7etkzWA7JfdmKWT0pMsa&index=3)についての概要を説明しています。\n\n以下が、並行レンダー機能をはじめとする、このリリースで期待される新要素の概要です。\n\n<Note>\n\nReact Native ユーザ向け：React 18 は新たなアーキテクチャの React Native の一部としてリリースされます。詳細については[こちらの React Conf キーノート](https://www.youtube.com/watch?v=FZ0cG47msEk&t=1530s)を参照してください。*\n\n</Note>\n\n## React の並行処理機能とは？ {/*what-is-concurrent-react*/}\n\nReact 18 で加わった中で最も重要なものとはすなわち「並行処理機能」ですが、これはできることなら皆さんが全く考えないで済むのが望ましいことです。ただしアプリケーション開発者にとっては概ねその通りでしょうが、ライブラリのメンテナにとっては話はちょっと複雑かもしれません。\n\n並行処理は、それ自体が何か機能だというわけではありません。これは、同時に UI の複数のバージョンを React が準備しておけるようにするための、新たな裏方のメカニズムです。並行処理機能そのものは実装の詳細 (implementation detail) に過ぎず、それが有用なのはそれで実現できる様々な機能が存在するからこそだ、と考えてください。React は内部の実装において priority queue や multiple buffering などの洗練された手法を用いています。ですがこれらのコンセプトは公開 API のどこにも現れません。\n\n我々が API を設計する際には、開発者に実装の詳細を見せないように努力しています。React 開発者としてみなさんはユーザ体験を*どんな*見た目にしたいかを考えることに集中し、その見た目を*どのように*実現するのかは React が受け持ちます。React 開発者が、裏で並行処理機能がどのように働いているのかを知る必要はありません。\n\nしかし、React の並行処理機能は、普通の「実装の詳細」と比べてより重要なものであり、React のコアのレンダリングモデルに対する本質的な変更です。ですので並行処理の動作について詳しく知ることがもの凄く重要ということではないにせよ、どのようなものかについて高レベルの概観を知っておくことは有用かもしれません。\n\nReact の並行処理機能の重要な特性は、処理を中断可能であるということです。React 18 にアップグレードしても、何らかの並行処理機能を加えるまでは、更新は React の以前のバージョンと同じく、まとめて、中断されず、かつ同期的にレンダーされます。同期的なレンダーでは、更新のレンダーが始まったら、ユーザが結果を画面で見られるようになるまでそれを中断することはできません。\n\n並行レンダーにおいては、これが必ずしも正しくなくなります。React は更新のレンダーを開始し、途中で一時停止し、後で再開することができます。途中まで終わったレンダーを完全に捨ててしまうこともありえます。レンダーが中断したとしても、React は UI の見た目に一貫性があることを保証します。これを実現するために、React はツリー全体の評価が終わるまで DOM の書き換えをせずに待機します。これにより、React はメインスレッドをブロックせずにバックグラウンドで次の画面を用意しておけるようになります。つまり、大きなレンダー作業の最中でもユーザの入力に UI が即座に反応できるということであり、ユーザ体験がスムースになります。\n\nもう 1 つの例は、state の再利用です。React の並行処理機能により、画面から UI の一部分をいったん削除し、前回の state を再利用しながら後で戻す、ということが可能です。例えば、ユーザがタブを切り替えて画面から離れて戻ってきた場合、React は以前の画面を以前と同様の state で復帰させる必要があります。将来のマイナーリリースにおいて、このパターンを実装した `<Offscreen>` というコンポーネントを新たに加える予定です。同様に、`<Offscreen>` を使ってバックグラウンドで新しい UI を用意し、ユーザが表示させようとする前に準備完了にしておく、ということもできるようになるでしょう。\n\n並行レンダーは React における新しいパワフルなツールであり、サスペンス、トランジション、ストリーミング付きサーバレンダリングといった新たな機能のほとんどはこれを活用して構築されています。しかし React 18 はこの新しい基盤の上に我々が構築しようとしているものの始まりに過ぎません。\n\n## 並行処理機能の段階的な採用 {/*gradually-adopting-concurrent-features*/}\n\n厳密には、並行レンダーは破壊的変更です。並行レンダーは中断可能なため、それが有効になるとコンポーネントはわずかに異なった動作をします。\n\n我々はテストにおいて、数千のコンポーネントを React 18 のためにアップグレードしました。そこで分かったことは、ほとんどすべての既存のコンポーネントは並行レンダーにおいても「普通に」動作するということです。しかしいくつかのコンポーネントでは移行のための追加作業が必要です。変更は通常小さなものですが、自分のペースで更新作業を行うことも可能です。React 18 の新たなレンダーの挙動は、**あなたのアプリ内で新機能を使っている部分でのみ有効化されます**。\n\n大まかな移行作業の流れとしては、まず既存コードの挙動を壊さずにアプリが React 18 で動作するようにします。それから自分のペースで並行処理機能を徐々に追加し始めることができます。[`<StrictMode>`](/reference/react/StrictMode) を利用すれば、並行処理に関連するバグに開発時に気付きやすいようにできます。strict モードは本番での動作に影響を与えませんが、開発中には追加の警告を表示したり、べき等 (idempotent) であるべき関数を 2 回呼び出したりします。すべての間違いを捕捉することはできませんが、最もよくある間違いを防ぐのに効果的です。\n\nReact 18 にアップグレード後、並行処理機能をすぐに使い始めることができます。例えばユーザの入力をブロックせずに画面遷移を行うために startTransition を使うことができます。あるいは高価な再レンダーの頻度を落とすために useDeferredValue を使うことも可能です。\n\nしかし長期的には、あなたのアプリに並行処理を加えるためのメインの方法は、並行処理に対応したライブラリやフレームワークを使うことになるだろうと考えています。ほとんどの場合、あなたが並行処理の API を直接触ることはないはずです。例えば新しい画面に遷移するたびに毎回開発者が startTransition をコールするのではなく、ルータのライブラリがナビゲーションを startTransition で自動でラップするようになるでしょう。\n\nライブラリがアップグレードされて並行処理機能対応になるまでには、多少時間がかかるかもしれません。並行処理機能をライブラリが活用しやすくするために新しい API を提供しています。当面は、React エコシステムが徐々に移行していくまで、ライブラリメンテナが作業するのをお待ちください。\n\n詳細は、前回の投稿をご覧ください：[React 18 アップグレードガイド](/blog/2022/03/08/react-18-upgrade-guide).\n\n## データフレームワークにおけるサスペンス {/*suspense-in-data-frameworks*/}\n\nReact 18 では、Relay、Next.js、Hydrogen、Remix のような、使い方の規約がある (opinionated) フレームワークにおいて、データ取得のためのサスペンスを使い始めることができます。単発的なデータ取得にサスペンスを使うことも技術的には可能ですが、現時点では一般的な戦略としてはお勧めしません。\n\n将来的には、上記のようなフレームワークを用いなくてもあなたのデータにサスペンスを使ってアクセスしやすくするため、新たに基本機能を提供するかもしれません。しかしサスペンスは、ルータやデータレイヤ、サーバレンダリング環境といったあなたのアプリのアーキテクチャと深く結合して利用される場合に、最も効果を発揮します。長期的にも、ライブラリやフレームワークが React のエコシステムにおいて重要な働きをすることを期待しています。\n\nReact の以前のバージョンと同様、サスペンスはクライアントで React.lazy を使ってコードを分割する際にも利用できます。しかし我々がサスペンスを使って実現したいと構想しているのは、コードのロードよりもずっと多くのことです。目標は、サスペンスのサポートを拡張していき、いずれはサスペンスによるひとつの宣言的なフォールバックが、あらゆる非同期的な操作（コード、データ、画像などのロード）を扱えるようにすることです。\n\n## サーバコンポーネントはまだ開発中です {/*server-components-is-still-in-development*/}\n\n[**サーバコンポーネント**](/blog/2020/12/21/data-fetching-with-react-server-components) は実装予定の機能であり、クライアントサイドアプリにおけるリッチなインタラクティビティと伝統的なサーバレンダリングによるパフォーマンス改善とを兼ね備えた、クライアント・サーバ両方にまたがるアプリの開発を可能にするものです。サーバコンポーネントは React の並行処理機能と本質的に結合してはいませんが、サスペンスやストリーミングサーバレンダリングのような並行処理機能と併用した際に最もうまく働くようデザインされています。\n\nサーバコンポーネントはまだ実験的機能ですが、18.x のマイナーリリースで初期バージョンをリリースできる見込みです。それまでは、プロポーザルを推し進めて広く採用できる準備が整うよう、Next.js、Hydrogen、Remix のようなフレームワークと協力していきます。\n\n## React 18 の新要素 {/*whats-new-in-react-18*/}\n\n### 新機能：自動バッチング {/*new-feature-automatic-batching*/}\n\nバッチングとは React がパフォーマンスのために複数のステート更新をグループ化して、単一の再レンダーにまとめることを指します。自動バッチング以前は、React のイベントハンドラ内での更新のみバッチ処理されていました。promise や setTimeout、ネイティブのイベントハンドラやその他あらゆるイベント内で起きる更新はデフォルトではバッチ処理されていませんでした。自動バッチングにより、これらの更新も自動でバッチ処理されるようになります：\n\n\n```js\n// Before: only React events were batched.\nsetTimeout(() => {\n  setCount(c => c + 1);\n  setFlag(f => !f);\n  // React will render twice, once for each state update (no batching)\n}, 1000);\n\n// After: updates inside of timeouts, promises,\n// native event handlers or any other event are batched.\nsetTimeout(() => {\n  setCount(c => c + 1);\n  setFlag(f => !f);\n  // React will only re-render once at the end (that's batching!)\n}, 1000);\n```\n\n詳細については、[Automatic batching for fewer renders in React 18](https://github.com/reactwg/react-18/discussions/21) を参照してください。\n\n### 新機能：トランジション {/*new-feature-transitions*/}\n\nトランジション（transition; 段階的推移）とは React における新たな概念であり、緊急性の高い更新 (urgent update) と高くない更新 (non-urgent update) を区別するためのものです。\n\n* **緊急性の高い更新**とはタイプ、クリック、プレスといったユーザ操作を直接反映するものです。\n* **トランジションによる更新**は UI をある画面から別の画面に段階的に遷移させるものです。\n\nタイプ、クリック、プレスのような緊急性の高い更新は、物理的な物体の挙動に関する我々の直観に反しないよう、即座に反応する必要があり、そうでないと「おかしい」と認識されてしまいます。一方でトランジション内では、ユーザは画面上であらゆる中間の値が見えることを期待していません。\n\n例えば、ドロップダウン内でフィルタを選択した場合、フィルタボタン自体はクリックした瞬間に反応することを期待するでしょう。しかしフィルタの結果は、ボタンの反応とは別に徐々に現れても構いません。小さな遅延は認識できませんし、あって構わないものです。また、前のレンダーが終わっていない段階で再びフィルタを変更した場合、最終的な結果以外は気にしません。\n\n典型的には、最良のユーザ体験のためには、あるひとつのユーザ入力は緊急性の高い更新と高くない更新の両方を引き起こすようにするべきです。input イベント内で startTransition API を用い、React にどの更新の緊急性が高く、どれが「トランジション」なのかを伝えることができます：\n\n\n```js\nimport { startTransition } from 'react';\n\n// Urgent: Show what was typed\nsetInputValue(input);\n\n// Mark any state updates inside as transitions\nstartTransition(() => {\n  // Transition: Show the results\n  setSearchQuery(input);\n});\n```\n\n\nstartTransition でラップした更新は緊急性の低いものとして扱われ、クリックやキー押下のような緊急性の高い更新がやってきた場合には中断されます。トランジションがユーザによって中断された場合（例えば素早く複数のタイプが起こった場合）、React は完了しないままに古くなったレンダーを破棄して、最後の更新のみレンダーします。\n\n\n* `useTransition`: トランジションを開始するためのフックであり、保留中かどうかの状態を追跡するための値も含まれます。\n* `startTransition`: フックが使えない場合にトランジションを開始するためのメソッドです。\n\nトランジションを使うと並行レンダー機能にオプトインし、更新が中断可能になります。また、コンテンツが再サスペンドした場合、バックグラウンドでトランジション中のコンテンツをレンダーしつつ、現在のコンテンツを表示し続けるよう React に伝えます（詳細については [サスペンス RFC](https://github.com/reactjs/rfcs/blob/main/text/0213-suspense-in-react-18.md) を参照）。\n\n[トランジションのドキュメントはこちら](/reference/react/useTransition)。\n\n### サスペンスの新機能 {/*new-suspense-features*/}\n\nサスペンスにより、コンポーネントツリーの一部がまだ表示できない場合に、ロード中という状態を宣言的に記述できるようになります：\n\n```js\n<Suspense fallback={<Spinner />}>\n  <Comments />\n</Suspense>\n```\n\nサスペンスにより、「UI ロード中状態」というものが、React のプログラミングモデルで宣言的に記述可能な主要コンセプトに昇格します。その上により高レベルな機能を構築していけるようになります。\n\n我々は数年前に機能限定版のサスペンスを導入しました。しかしサポートされているユースケースは React.lazy によるコード分割のみであり、サーバでのレンダーにおいては一切サポートされていませんでした。\n\nReact 18 ではサーバ側でのサスペンスのサポートを追加し、並行レンダリング機能を用いてその能力を向上させました。\n\nReact 18 におけるサスペンスはトランジション API と併用した場合に能力を発揮します。トランジション中でサスペンドが発生すると、React は既に見えているコンテンツがフォールバックによって隠されてしまわないようにするのです。代わりに、十分なデータがロードされるまでレンダーを遅らせて、望ましくないロード中状態が見えないようにします。\n\n詳しくは、[Suspense in React 18](https://github.com/reactjs/rfcs/blob/main/text/0213-suspense-in-react-18.md) の RFC を参照してください。\n\n### 新たなクライアントおよびサーバ用のレンダー API {/*new-client-and-server-rendering-apis*/}\n\nこのリリースを機に、クライアントおよびサーバ用に公開している API を再設計することにしました。これにより React 18 の新しい API にアップグレードするまでの間、React 17 の古い API を利用し続けることができるようになります。\n\n#### React DOM Client {/*react-dom-client*/}\n\n以下の新たな API は `react-dom/client` からエクスポートされるようになっています：\n\n* `createRoot`: `render` したり `unmount` したりできる新たなルートを作成するための新メソッドです。`ReactDOM.render` の代わりに利用してください。これを使わないと React 18 の新機能は動作しません。\n* `hydrateRoot`: サーバでレンダーされたアプリをハイドレーションするための新メソッドです。`ReactDOM.hydrate` の代わりに、新たな React DOM サーバ API と併せて利用してください。これを使わないと React 18 の新機能は動作しません。\n\n`createRoot` と `hydrateRoot` のいずれも、`onRecoverableError` という新たなオプションを受け取るようになっており、レンダーあるいはハイドレーション中に起きたエラーから React が復帰した場合に通知を受けてログを残したい場合に利用できます。デフォルトでは React は [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError) か、古いブラウザの場合は `console.error` を利用します。\n\n[React DOM Client のドキュメントはこちら](/reference/react-dom/client)。\n\n#### React DOM Server {/*react-dom-server*/}\n\n以下の新たな API は `react-dom/client` からエクスポートされるようになっており、サーバでサスペンスをストリーミングする機能を完全にサポートしています：\n\n* `renderToPipeableStream`: Node 環境でのストリーミング用。\n* `renderToReadableStream`: Deno や Cloudflare Workers のようなモダンなエッジランタイム環境用。\n\n既存の `renderToString` メソッドは今後も動作しますが、推奨されません。\n\n[React DOM Server のドキュメントはこちら](/reference/react-dom/server)。\n\n### Strict モードの新たな挙動 {/*new-strict-mode-behaviors*/}\n\n将来的に、React が state を保ったままで UI の一部分を追加・削除できるような機能を導入したいと考えています。例えば、ユーザがタブを切り替えて画面を離れてから戻ってきた場合に、React が以前の画面をすぐに表示できるようにしたいのです。これを可能にするため、React は同じ state を使用してツリーをアンマウント・再マウントします。\n\nこの機能により、React の標準状態でのパフォーマンスが向上しますが、コンポーネントは副作用が何度も登録されたり破棄されたりすることに対して耐性を持つことが必要になります。ほとんどの副作用は何の変更もなく動作しますが、一部の副作用は一度しか登録・破棄されないものと想定しています。\n\nこの問題に気付きやすくするために、React 18 は strict モードに新しい開発時専用のチェックを導入します。この新しいチェックは、コンポーネントが初めてマウントされるたびに、すべてのコンポーネントを自動的にアンマウント・再マウントし、かつ 2 回目のマウントで以前の state を復元します。\n\nこれまでは、React はコンポーネントをマウントして以下のように副作用を作成してきました：\n\n```\n* React がコンポーネントをマウント\n  * レイアウト副作用 (layout effect) を作成\n  * （通常の）副作用を作成\n```\n\n\nReact 18 の strict モードでは、開発時にコンポーネントがマウントされた場合、React はコンポーネントの即時アンマウント・再マウントをシミュレーションします：\n\n```\n* React がコンポーネントをマウント\n    * レイアウト副作用を作成\n    * 副作用を作成\n* マウントされたコンポーネント内で副作用の破棄をシミュレート\n    * レイアウト副作用を破棄\n    * 副作用を破棄\n* マウントされたコンポーネント内で以前の state を復元し副作用の再生成をシミュレート\n    * レイアウト副作用を作成\n    * 副作用の作成用コードの実行\n```\n\n[state 再利用可能性の保証についてのドキュメントはこちら](/reference/react/StrictMode#fixing-bugs-found-by-re-running-effects-in-development)。\n\n### 新たなフック {/*new-hooks*/}\n\n#### useId {/*useid*/}\n\n`useId` はハイドレーション時の不整合を防ぎつつクライアントとサーバで一意な ID を生成するためのフックです。これは主に、一意な ID を必要とするアクセシビリティ API を組み込むようなコンポーネントライブラリで有用なものです。これにより React 17 およびそれ以前から既に存在した問題が解決されますが、React 18 では新しいストリーミング対応のサーバレンダラが HTML を順番通りに送信しなくなるため、この問題はより重要です。[こちらのドキュメントを参照](/reference/react/useId)。\n\n> 補足\n>\n> `useId` は[リスト内の key](/learn/rendering-lists#where-to-get-your-key) を作成するのに使うためのものでは**ありません**。key はあなたのデータから作成されるべきです。\n\n#### useTransition {/*usetransition*/}\n\n`useTransition` と `startTransition` により、一部の更新は緊急性が低いということをマークできるようになります。その他の更新はデフォルトで緊急性が高いものとして扱われます。React は緊急性の高い更新（例えばテキスト入力の更新）が、緊急性の低い更新（例えば検索結果のリストのレンダー）を中断できるようになります。[こちらのドキュメントを参照](/reference/react/useTransition)。\n\n#### useDeferredValue {/*usedeferredvalue*/}\n\n`useDeferredValue` により、ツリー内の緊急性の低い更新の再レンダーを遅延させることができます。デバウンス (debounce) に似ていますが、それと比べていくつかの利点があります。遅延時間が固定でないため、最初のレンダーが画面に反映された時点ですぐに遅延されていた方のレンダーを始められるのです。また遅延されたレンダーは中断可能であり、ユーザインプットをブロックしません。[こちらのドキュメントを参照](/reference/react/useDeferredValue)。\n\n#### useSyncExternalStore {/*usesyncexternalstore*/}\n\n`useSyncExternalStore` は、外部ストアへの更新を強制的に同期的に行うことで、外部ストアが並行読み取りを行えるようにします。これにより外部のデータソースに購読する際に `useEffect` を使う必要性がなくなるので、React 外部の状態を扱うあらゆるライブラリにとって推奨されるものです。[こちらのドキュメントを参照](/reference/react/useSyncExternalStore)。\n\n> 補足\n>\n> `useSyncExternalStore` はアプリケーションコードではなくライブラリで使用されることを意図しています。\n\n#### useInsertionEffect {/*useinsertioneffect*/}\n\n`useInsertionEffect` は、CSS-in-JS ライブラリがレンダー時にスタイルを注入する際のパフォーマンス上の問題に対処できるようにするための新しいフックです。すでに CSS-in-JS ライブラリを構築しているのでなければ、これを使うことはまずないでしょう。このフックは、DOM が書き換えられた後、レイアウト副作用 (layout effect) が新しいレイアウトを読み込む前に実行されます。これにより React 17 およびそれ以前から既に存在した問題が解決されますが、React 18 では並行レンダー中にブラウザに処理が渡り、そこでレイアウトが再計算される可能性があるため、より重要です。[こちらのドキュメントを参照](/reference/react/useInsertionEffect)。\n\n> 補足\n>\n> `useInsertionEffect` はアプリケーションコードではなくライブラリで使用されることを意図しています。\n\n## アップグレード方法 {/*how-to-upgrade*/}\n\nステップバイステップのガイド、および破壊的変更・注目すべき変更の全リストについては [React 18 アップグレードガイド](/blog/2022/03/08/react-18-upgrade-guide)を参照してください。\n\n## Changelog {/*changelog*/}\n\n### React {/*react*/}\n\n* Add `useTransition` and `useDeferredValue` to separate urgent updates from transitions. ([#10426](https://github.com/facebook/react/pull/10426), [#10715](https://github.com/facebook/react/pull/10715), [#15593](https://github.com/facebook/react/pull/15593), [#15272](https://github.com/facebook/react/pull/15272), [#15578](https://github.com/facebook/react/pull/15578), [#15769](https://github.com/facebook/react/pull/15769), [#17058](https://github.com/facebook/react/pull/17058), [#18796](https://github.com/facebook/react/pull/18796), [#19121](https://github.com/facebook/react/pull/19121), [#19703](https://github.com/facebook/react/pull/19703), [#19719](https://github.com/facebook/react/pull/19719), [#19724](https://github.com/facebook/react/pull/19724), [#20672](https://github.com/facebook/react/pull/20672), [#20976](https://github.com/facebook/react/pull/20976) by [@acdlite](https://github.com/acdlite), [@lunaruan](https://github.com/lunaruan), [@rickhanlonii](https://github.com/rickhanlonii), and [@sebmarkbage](https://github.com/sebmarkbage))\n* Add `useId` for generating unique IDs. ([#17322](https://github.com/facebook/react/pull/17322), [#18576](https://github.com/facebook/react/pull/18576), [#22644](https://github.com/facebook/react/pull/22644), [#22672](https://github.com/facebook/react/pull/22672), [#21260](https://github.com/facebook/react/pull/21260) by [@acdlite](https://github.com/acdlite), [@lunaruan](https://github.com/lunaruan), and [@sebmarkbage](https://github.com/sebmarkbage))\n* Add `useSyncExternalStore` to help external store libraries integrate with React. ([#15022](https://github.com/facebook/react/pull/15022), [#18000](https://github.com/facebook/react/pull/18000), [#18771](https://github.com/facebook/react/pull/18771), [#22211](https://github.com/facebook/react/pull/22211), [#22292](https://github.com/facebook/react/pull/22292), [#22239](https://github.com/facebook/react/pull/22239), [#22347](https://github.com/facebook/react/pull/22347), [#23150](https://github.com/facebook/react/pull/23150) by [@acdlite](https://github.com/acdlite), [@bvaughn](https://github.com/bvaughn), and [@drarmstr](https://github.com/drarmstr))\n* Add `startTransition` as a version of `useTransition` without pending feedback. ([#19696](https://github.com/facebook/react/pull/19696)  by [@rickhanlonii](https://github.com/rickhanlonii))\n* Add `useInsertionEffect` for CSS-in-JS libraries. ([#21913](https://github.com/facebook/react/pull/21913)  by [@rickhanlonii](https://github.com/rickhanlonii))\n* Make Suspense remount layout effects when content reappears.  ([#19322](https://github.com/facebook/react/pull/19322), [#19374](https://github.com/facebook/react/pull/19374), [#19523](https://github.com/facebook/react/pull/19523), [#20625](https://github.com/facebook/react/pull/20625), [#21079](https://github.com/facebook/react/pull/21079) by [@acdlite](https://github.com/acdlite), [@bvaughn](https://github.com/bvaughn), and [@lunaruan](https://github.com/lunaruan))\n* Make `<StrictMode>` re-run effects to check for restorable state. ([#19523](https://github.com/facebook/react/pull/19523) , [#21418](https://github.com/facebook/react/pull/21418)  by [@bvaughn](https://github.com/bvaughn) and [@lunaruan](https://github.com/lunaruan))\n* Assume Symbols are always available. ([#23348](https://github.com/facebook/react/pull/23348)  by [@sebmarkbage](https://github.com/sebmarkbage))\n* Remove `object-assign` polyfill. ([#23351](https://github.com/facebook/react/pull/23351)  by [@sebmarkbage](https://github.com/sebmarkbage))\n* Remove unsupported `unstable_changedBits` API.  ([#20953](https://github.com/facebook/react/pull/20953)  by [@acdlite](https://github.com/acdlite))\n* Allow components to render undefined. ([#21869](https://github.com/facebook/react/pull/21869)  by [@rickhanlonii](https://github.com/rickhanlonii))\n* Flush `useEffect` resulting from discrete events like clicks synchronously. ([#21150](https://github.com/facebook/react/pull/21150)  by [@acdlite](https://github.com/acdlite))\n* Suspense `fallback={undefined}` now behaves the same as `null` and isn't ignored. ([#21854](https://github.com/facebook/react/pull/21854)  by [@rickhanlonii](https://github.com/rickhanlonii))\n* Consider all `lazy()` resolving to the same component equivalent. ([#20357](https://github.com/facebook/react/pull/20357)  by [@sebmarkbage](https://github.com/sebmarkbage))\n* Don't patch console during first render. ([#22308](https://github.com/facebook/react/pull/22308)  by [@lunaruan](https://github.com/lunaruan))\n* Improve memory usage. ([#21039](https://github.com/facebook/react/pull/21039)  by [@bgirard](https://github.com/bgirard))\n* Improve messages if string coercion throws (Temporal.*, Symbol, etc.) ([#22064](https://github.com/facebook/react/pull/22064)  by [@justingrant](https://github.com/justingrant))\n* Use `setImmediate` when available over `MessageChannel`. ([#20834](https://github.com/facebook/react/pull/20834)  by [@gaearon](https://github.com/gaearon))\n* Fix context failing to propagate inside suspended trees. ([#23095](https://github.com/facebook/react/pull/23095)  by [@gaearon](https://github.com/gaearon))\n* Fix `useReducer` observing incorrect props by removing the eager bailout mechanism. ([#22445](https://github.com/facebook/react/pull/22445)  by [@josephsavona](https://github.com/josephsavona))\n* Fix `setState` being ignored in Safari when appending iframes. ([#23111](https://github.com/facebook/react/pull/23111)  by [@gaearon](https://github.com/gaearon))\n* Fix a crash when rendering `ZonedDateTime` in the tree. ([#20617](https://github.com/facebook/react/pull/20617)  by [@dimaqq](https://github.com/dimaqq))\n* Fix a crash when document is set to `null` in tests. ([#22695](https://github.com/facebook/react/pull/22695)  by [@SimenB](https://github.com/SimenB))\n* Fix `onLoad` not triggering when concurrent features are on. ([#23316](https://github.com/facebook/react/pull/23316)  by [@gnoff](https://github.com/gnoff))\n* Fix a warning when a selector returns `NaN`.  ([#23333](https://github.com/facebook/react/pull/23333)  by [@hachibeeDI](https://github.com/hachibeeDI))\n* Fix a crash when document is set to `null` in tests. ([#22695](https://github.com/facebook/react/pull/22695) by [@SimenB](https://github.com/SimenB))\n* Fix the generated license header. ([#23004](https://github.com/facebook/react/pull/23004)  by [@vitaliemiron](https://github.com/vitaliemiron))\n* Add `package.json` as one of the entry points. ([#22954](https://github.com/facebook/react/pull/22954)  by [@Jack](https://github.com/Jack-Works))\n* Allow suspending outside a Suspense boundary. ([#23267](https://github.com/facebook/react/pull/23267)  by [@acdlite](https://github.com/acdlite))\n* Log a recoverable error whenever hydration fails. ([#23319](https://github.com/facebook/react/pull/23319)  by [@acdlite](https://github.com/acdlite))\n\n### React DOM {/*react-dom*/}\n\n* Add `createRoot` and `hydrateRoot`. ([#10239](https://github.com/facebook/react/pull/10239), [#11225](https://github.com/facebook/react/pull/11225), [#12117](https://github.com/facebook/react/pull/12117), [#13732](https://github.com/facebook/react/pull/13732), [#15502](https://github.com/facebook/react/pull/15502), [#15532](https://github.com/facebook/react/pull/15532), [#17035](https://github.com/facebook/react/pull/17035), [#17165](https://github.com/facebook/react/pull/17165), [#20669](https://github.com/facebook/react/pull/20669), [#20748](https://github.com/facebook/react/pull/20748), [#20888](https://github.com/facebook/react/pull/20888), [#21072](https://github.com/facebook/react/pull/21072), [#21417](https://github.com/facebook/react/pull/21417), [#21652](https://github.com/facebook/react/pull/21652), [#21687](https://github.com/facebook/react/pull/21687), [#23207](https://github.com/facebook/react/pull/23207), [#23385](https://github.com/facebook/react/pull/23385) by [@acdlite](https://github.com/acdlite), [@bvaughn](https://github.com/bvaughn), [@gaearon](https://github.com/gaearon), [@lunaruan](https://github.com/lunaruan), [@rickhanlonii](https://github.com/rickhanlonii), [@trueadm](https://github.com/trueadm), and [@sebmarkbage](https://github.com/sebmarkbage))\n* Add selective hydration. ([#14717](https://github.com/facebook/react/pull/14717), [#14884](https://github.com/facebook/react/pull/14884), [#16725](https://github.com/facebook/react/pull/16725), [#16880](https://github.com/facebook/react/pull/16880), [#17004](https://github.com/facebook/react/pull/17004), [#22416](https://github.com/facebook/react/pull/22416), [#22629](https://github.com/facebook/react/pull/22629), [#22448](https://github.com/facebook/react/pull/22448), [#22856](https://github.com/facebook/react/pull/22856), [#23176](https://github.com/facebook/react/pull/23176) by [@acdlite](https://github.com/acdlite), [@gaearon](https://github.com/gaearon), [@salazarm](https://github.com/salazarm), and [@sebmarkbage](https://github.com/sebmarkbage))\n* Add `aria-description` to the list of known ARIA attributes. ([#22142](https://github.com/facebook/react/pull/22142)  by [@mahyareb](https://github.com/mahyareb))\n* Add `onResize` event to video elements. ([#21973](https://github.com/facebook/react/pull/21973)  by [@rileyjshaw](https://github.com/rileyjshaw))\n* Add `imageSizes` and `imageSrcSet` to known props. ([#22550](https://github.com/facebook/react/pull/22550)  by [@eps1lon](https://github.com/eps1lon))\n* Allow non-string `<option>` children if `value` is provided.  ([#21431](https://github.com/facebook/react/pull/21431)  by [@sebmarkbage](https://github.com/sebmarkbage))\n* Fix `aspectRatio` style not being applied. ([#21100](https://github.com/facebook/react/pull/21100)  by [@gaearon](https://github.com/gaearon))\n* Warn if `renderSubtreeIntoContainer` is called. ([#23355](https://github.com/facebook/react/pull/23355)  by [@acdlite](https://github.com/acdlite))\n\n### React DOM Server {/*react-dom-server-1*/}\n\n* Add the new streaming renderer. ([#14144](https://github.com/facebook/react/pull/14144), [#20970](https://github.com/facebook/react/pull/20970), [#21056](https://github.com/facebook/react/pull/21056), [#21255](https://github.com/facebook/react/pull/21255), [#21200](https://github.com/facebook/react/pull/21200), [#21257](https://github.com/facebook/react/pull/21257), [#21276](https://github.com/facebook/react/pull/21276), [#22443](https://github.com/facebook/react/pull/22443), [#22450](https://github.com/facebook/react/pull/22450), [#23247](https://github.com/facebook/react/pull/23247), [#24025](https://github.com/facebook/react/pull/24025), [#24030](https://github.com/facebook/react/pull/24030) by [@sebmarkbage](https://github.com/sebmarkbage))\n* Fix context providers in SSR when handling multiple requests. ([#23171](https://github.com/facebook/react/pull/23171)  by [@frandiox](https://github.com/frandiox))\n* Revert to client render on text mismatch. ([#23354](https://github.com/facebook/react/pull/23354)  by [@acdlite](https://github.com/acdlite))\n* Deprecate `renderToNodeStream`. ([#23359](https://github.com/facebook/react/pull/23359)  by [@sebmarkbage](https://github.com/sebmarkbage))\n* Fix a spurious error log in the new server renderer. ([#24043](https://github.com/facebook/react/pull/24043)  by [@eps1lon](https://github.com/eps1lon))\n* Fix a bug in the new server renderer. ([#22617](https://github.com/facebook/react/pull/22617)  by [@shuding](https://github.com/shuding))\n* Ignore function and symbol values inside custom elements on the server. ([#21157](https://github.com/facebook/react/pull/21157)  by [@sebmarkbage](https://github.com/sebmarkbage))\n\n### React DOM Test Utils {/*react-dom-test-utils*/}\n\n* Throw when `act` is used in production. ([#21686](https://github.com/facebook/react/pull/21686)  by [@acdlite](https://github.com/acdlite))\n* Support disabling spurious act warnings with `global.IS_REACT_ACT_ENVIRONMENT`. ([#22561](https://github.com/facebook/react/pull/22561)  by [@acdlite](https://github.com/acdlite))\n* Expand act warning to cover all APIs that might schedule React work. ([#22607](https://github.com/facebook/react/pull/22607)  by [@acdlite](https://github.com/acdlite))\n* Make `act` batch updates. ([#21797](https://github.com/facebook/react/pull/21797)  by [@acdlite](https://github.com/acdlite))\n* Remove warning for dangling passive effects. ([#22609](https://github.com/facebook/react/pull/22609)  by [@acdlite](https://github.com/acdlite))\n\n### React Refresh {/*react-refresh*/}\n\n* Track late-mounted roots in Fast Refresh. ([#22740](https://github.com/facebook/react/pull/22740)  by [@anc95](https://github.com/anc95))\n* Add `exports` field to `package.json`. ([#23087](https://github.com/facebook/react/pull/23087)  by [@otakustay](https://github.com/otakustay))\n\n### Server Components (Experimental) {/*server-components-experimental*/}\n\n* Add Server Context support. ([#23244](https://github.com/facebook/react/pull/23244)  by [@salazarm](https://github.com/salazarm))\n* Add `lazy` support. ([#24068](https://github.com/facebook/react/pull/24068)  by [@gnoff](https://github.com/gnoff))\n* Update webpack plugin for webpack 5 ([#22739](https://github.com/facebook/react/pull/22739)  by [@michenly](https://github.com/michenly))\n* Fix a mistake in the Node loader. ([#22537](https://github.com/facebook/react/pull/22537)  by [@btea](https://github.com/btea))\n* Use `globalThis` instead of `window` for edge environments. ([#22777](https://github.com/facebook/react/pull/22777)  by [@huozhi](https://github.com/huozhi))\n"
  },
  {
    "path": "src/content/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022.md",
    "content": "---\ntitle: \"React Labs: 私達のこれまでの取り組み - 2022年6月版\"\nauthor:  Andrew Clark, Dan Abramov, Jan Kassens, Joseph Savona, Josh Story, Lauren Tan, Luna Ruan, Mengdi Chen, Rick Hanlon, Robert Zhang, Sathya Gunasekaran, Sebastian Markbage, and Xuan Huang\ndate: 2022/06/15\ndescription: React 18 の完成は数年がかりの仕事であり、React チームはそこから貴重な教訓を得ることになりました。このリリースは何年も研究を行い、様々なアプローチを試した結果として生まれたものです。いくつかのアプローチはうまく行った一方で、多くは行き詰まって新たな知見のみをもたらすことになりました。ここから我々が学んだことは、我々がどんなことを試しているのかをコミュニティに知らせることなくただお待たせするというのは、フラストレーションの元だ、ということです。\n---\n\nJune 15, 2022 by [Andrew Clark](https://twitter.com/acdlite), [Dan Abramov](https://bsky.app/profile/danabra.mov), [Jan Kassens](https://twitter.com/kassens), [Joseph Savona](https://twitter.com/en_JS), [Josh Story](https://twitter.com/joshcstory), [Lauren Tan](https://twitter.com/potetotes), [Luna Ruan](https://twitter.com/lunaruan), [Mengdi Chen](https://twitter.com/mengdi_en), [Rick Hanlon](https://twitter.com/rickhanlonii), [Robert Zhang](https://twitter.com/jiaxuanzhang01), [Sathya Gunasekaran](https://twitter.com/_gsathya), [Sebastian Markbåge](https://twitter.com/sebmarkbage), and [Xuan Huang](https://twitter.com/Huxpro)\n\n---\n\n<Intro>\n\n[React 18](/blog/2022/03/29/react-v18) の完成は数年がかりの仕事であり、React チームはそこから貴重な教訓を得ることになりました。このリリースは何年も研究を行い、様々なアプローチを試した結果として生まれたものです。いくつかのアプローチはうまく行った一方で、多くは行き詰まって新たな知見のみをもたらすことになりました。ここから我々が学んだことは、我々がどんなことを試しているのかをコミュニティに知らせることなくただお待たせするというのは、フラストレーションの元だ、ということです。\n\n</Intro>\n\n---\n\n我々は、かなり実験的なものから明確に定義されているものまで、ほぼ常に様々なプロジェクトを同時に進めています。今後は、これらのプロジェクトに関する我々の取り組みについて、より多くのことを定期的にコミュニティと共有していくようにしたいと思います。\n\nあらかじめ申しあげておくと、この記事は明確なタイムラインのあるロードマップではありません。以下に挙げるプロジェクトの多くはまだ活発に探求中のものであり、具体的なリリース日を設定することは困難です。我々の学びの結果次第では、そもそも現在のイテレーションではリリースされないという可能性すらあります。その代わりここでは、現在まさに我々が何を問題と考えており、現時点でどこまでを学んだのかについて、共有していきたいと思います。\n\n## サーバコンポーネント {/*server-components*/}\n\n2020 年 12 月に、[React サーバコンポーネント (RSC) に関する実験的なデモ](https://legacy.reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html)を発表しました。それ以降、必要となる準備作業を React 18 で済ませ、実験のフィードバックから得られた改善に取り組んできました。\n\n特に、I/O ライブラリをフォークして例えば react-fetch のようなものを作成していく、というアイディアは捨て去ることにし、代わりに互換性のために async/await モデルを採用することにします。データフェッチングにはルータを使うこともできるのでこれにより RSC のリリースが遅れるということはありません。もうひとつの変更は、ファイルの拡張子でサーバコンポーネントかどうかを区別するというアプローチをやめ、[区別を注釈で行う](https://github.com/reactjs/rfcs/pull/189#issuecomment-1116482278)ことにする、というものです。\n\nwebpack と Vite の両方で振る舞いが同じになるようバンドラのサポートを共通化することについて、Vercel および Shopify とともに取り組んでいます。リリース前に、React のエコシステム全体で RSC の振る舞いが同じになるようにしたいと考えています。この部分が安定化前の最大の障壁となっています。\n\n## アセットのロード {/*asset-loading*/}\n\n現在のところ、スクリプトや外部スタイル・フォント・画像のようなアセットは、典型的には外部のシステムを通じてプリロードあるいはロードされています。しかしこれにより、ストリーミングやサーバコンポーネントといった新たな環境でうまく動くようにするのが難しくなることがあります。\nあらゆる React の環境で動作する API を追加し、それを通じて外部に分離されたアセットをプリロードあるいはロードできるよう、検討を進めています。\n\nまた、この新たな API がサスペンスをサポートすることで、画像や CSS やフォントを使っても、これらがロードされるまで表示はブロックするがストリーミングや並行レンダーはブロックしない、というようにしたいと考えています。これにより、画面上の要素がポップしレイアウトがずれるという[「ポップコーン」現象](https://twitter.com/sebmarkbage/status/1516852731251724293)を防ぐことができます。\n\n## サーバレンダリングの静的な最適化 {/*static-server-rendering-optimizations*/}\n\nStatic Site Generation (SSG) と Incremental Static Regeneration (ISR) はキャッシュ可能なページにおいてパフォーマンスを向上するための素晴らしい手法です。が、ダイナミックなサーバサイドレンダリング (SSR) についてもパフォーマンス改善のために何らかの機能を追加できるのではと考えています。特に、全コンテンツのキャッシュはできないが大部分は可能という状況について検討しています。コンパイルや静的パスを用いてサーバレンダリングを最適化する方法について、探索を行っています。\n\n## React 最適化コンパイラ {/*react-compiler*/}\n\nReact Conf 2021 において、React Forget についての[アーリープレビュー](https://www.youtube.com/watch?v=lGEMwh32soc)をお届けしました。これは、React のプログラミングモデルを保ちつつ、`useMemo` や `useCallback` の同等物を自動で作成して再レンダーのコストを最小化するためのコンパイラです。\n\n最近になって、このコンパイラの安定性と機能を向上するための書き直しが完了しました。新しいアーキテクチャは、[ローカルでの変数書き換え](/learn/keeping-components-pure#local-mutation-your-components-little-secret)のような、より複雑なパターンも解析してメモ化を適用することが可能であり、単にメモ化用フックと同じ事ができるという以上に、様々なコンパイル時最適化の可能性を開くことができます。\n\nまた、このコンパイラの様々な機能について試すためのプレイグラウンドを作成しています。プレイグラウンドの主目的はコンパイラ自体の開発をやりやすくすることですが、コンパイラを試して何をやっているのか直観で理解できるようにすることにも役立つと考えています。これにより裏で何をやっているのか洞察できるようになりますし、タイプして即座にコンパイル出力を見ることもできるようになります。こちらはコンパイラのリリースの際に同時にリリースされる予定です。\n\n## オフスクリーン {/*offscreen*/}\n\n現在のところ、コンポーネントを非表示にしたり表示したりしたい場合、選択肢はふたつあります。ひとつは、ツリーから完全に削除するというものです。これによる問題は、アンマウントするたびに UI の state や、DOM 内に保持されているスクロール位置のような状態が失われてしまうことです。\n\nもうひとつの選択肢は、コンポーネントをマウントしたままで CSS を使って表示・非表示を切り替える、というものです。これにより UI の state は保持されますが、React は非表示のコンポーネントやその子コンポーネントに更新があったときにそれをレンダーし続ける必要があるため、パフォーマンス面ではコストがかかります。\n\nオフスクリーン (offscreen) 機能は第 3 の選択肢を提供します。UI を見た目に非表示とした上で、内容の更新の優先度を下げるのです。これは考え方の点では `content-visibility` CSS プロパティと似ています。コンテンツが非表示の場合、UI 内の他の要素と同期をとる必要はありません。React はレンダー作業を、アプリがアイドル状態になるかコンテンツが再び表示されるようになるまで遅延させることができます。\n\nオフスクリーン機能は、高レベルの機能を実現するための低レベル機能です。`startTransition` のような React の他の並行レンダー機能と同様ですが、大抵の場合、あなたが直接オフスクリーン API を利用することはありません。代わりに、フレームワークが実装する以下のようなパターンを通じて利用することになるでしょう。\n\n* **即時の画面遷移**。現在でもルーティングフレームワークの中にはナビゲーションを高速化するため、リンクをホバーした際などにデータをプリフェッチするものが存在します。オフスクリーン機能を使えば、さらに後続の画面をバックグラウンドでプリレンダーしておくことが可能になります。\n* **ステートの再利用**。同様に、オフスクリーン機能を使うことで、ページやタブを切り替えたときに前の画面の state を保持しておき、切り替えて戻ってきたときに前の状況を復元できるようになります。\n* **リストのレンダーの仮想化**。大きなリストを表示している際に、リスト仮想化を提供するフレームワークでは現在見えているもの以外にも多くの項目をプリレンダーします。オフスクリーン機能を使えば、見えていない項目をリスト内の見えている項目よりも低優先度でプリレンダーすることができるようになります。\n* **背景コンテンツ**。また、モーダルをオーバーレイで表示している場合の背景要素など、非表示でないコンテンツのレンダー優先度を下げるような関連機能についても検討しています。\n\n## トランジションのトレース {/*transition-tracing*/}\n\n現在 React にはプロファイリングのためのツールがふたつ存在します。[オリジナルのプロファイラ](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html)は、プロファイリングセッション中に起こったすべてのコミットについて概要を表示します。それぞれのコミットに対して、レンダーされた全コンポーネントとレンダーにかかった時間も表示します。これと別に、React 18 で導入された[タイムラインプロファイラ](https://github.com/reactwg/react-18/discussions/76)のベータ版も存在します。これはコンポーネントのスケジュールの変化や React が更新をいつ行うのかについての情報を表示します。これらのプロファイラはいずれも、開発者がコード内のパフォーマンスに関する問題を特定するのに役立ちます。\n\n我々は、文脈なしに個々の遅いコミットやコンポーネントに関する情報だけ分かったところで開発者にとってはさほど役立たない、ということを学びました。知りたい情報は、現に遅いコミットになってしまっている理由の方です。開発者は、ボタンクリック、初回ロード、ページ移動といった特定の操作を追跡してパフォーマンスの悪化を監視し、なぜ操作が遅かったのか、どのように修正するのかを知りたいのです。\n\nこの問題を解決するために、以前 [Interaction Tracing API](https://gist.github.com/bvaughn/8de925562903afd2e7a12554adcdda16) の作成を試みたことがありますが、これには根本的な設計の問題があったため、操作が遅い理由を正確に知ることが難しく、場合によっては操作が全く終わらなくなってしまうこともありました。このため[この API は削除](https://github.com/facebook/react/pull/20037)せざるを得ませんでした。\n\n現在、この問題を解決するための Interaction Tracing API の新バージョン（`startTransition` で起動するので Transition Tracing と仮に呼んでいます）に取り組んでいます。\n\n## 新たな React ドキュメント {/*new-react-docs*/}\n\n昨年、新たな React のドキュメントサイトのベータ版（[現在は react.dev として公開済](/blog/2023/03/16/introducing-react-dev)）を発表しました。この新たな教材ではフックを優先的に学ぶことができ、新たな図やイラスト、インタラクティブに試せる例やチャレンジ問題が存在します。React 18 のリリースに集中するために作業をしばらくお休みしていましたが、React 18 のリリースも終わりましたので、新ドキュメントの仕上げとリリースに活発に取り組んでいます。\n\n副作用 (effect) は新規ユーザにとっても経験のあるユーザにとっても難しいトピックのひとつだと聞いていますので、現在我々は副作用についての詳細な説明を準備中です。[Synchronizing with Effects](/learn/synchronizing-with-effects) が一連の記事のうちで最初に公開されているものですが、これからの数週間でより多くが公開される予定です。また副作用についての詳細な記事を書き始めたことで、副作用に関するよくあるパターンの多くは、React に基本機能を加えることでシンプルにできる、ということに気付きました。初期アイディアの一部は [useEvent RFC](https://github.com/reactjs/rfcs/pull/220) で共有されています。これはまだ初期の研究段階でありアイディアについて見直しをしているところです。RFC にコメントを寄せてくださったコミュニティの方々や、ドキュメントの書き直しに関して[フィードバック](https://github.com/reactjs/react.dev/issues/3308)や貢献をしてくださった方に感謝します。特に新サイトの実装に関して多くのレビューや改善を寄せてくださった [Harish Kumar](https://github.com/harish-sethuraman) に感謝します。\n\n*このブログ記事のレビューをしてくださった [Sophie Alpert](https://twitter.com/sophiebits) に感謝します。*\n"
  },
  {
    "path": "src/content/blog/2023/03/16/introducing-react-dev.md",
    "content": "---\ntitle: \"react.dev のご紹介\"\nauthor: Dan Abramov and Rachel Nabors\ndate: 2023/03/16\ndescription: 本日、React とそのドキュメントの新しいホームとなる react.dev の立ち上げを発表することができ、大変うれしく思います。この記事では、新しいサイトの見どころをご紹介します。\n---\n\nMarch 16, 2023 by [Dan Abramov](https://bsky.app/profile/danabra.mov) and [Rachel Nabors](https://twitter.com/rachelnabors)\n\n---\n\n<Intro>\n\n本日、React とそのドキュメントの新しいホームとなる [react.dev](https://react.dev) の立ち上げを発表することができ、大変うれしく思います。この記事では、新しいサイトの見どころをご紹介します。\n\n</Intro>\n\n---\n\n## tl;dr {/*tldr*/}\n\n* 新しい React サイト ([react.dev](https://react.dev)) では、関数コンポーネントとフックを用いて、モダンな React を学べます。\n* 図解、イラスト、チャレンジ問題、そして 600 以上の新しいインタラクティブなサンプルが含まれています。\n* これまでの React ドキュメントサイトは、[legacy.reactjs.org](https://legacy.reactjs.org) に移転しました。\n\n## 新しいサイト、新しいドメイン、新しいホームページ {/*new-site-new-domain-new-homepage*/}\n\nまずは少々事務的なところから。\n\n新しいドキュメントの立ち上げを祝うために、そして何よりも、古いコンテンツと新しいコンテンツを明確に分離するために、より短い [react.dev](https://react.dev) ドメインに移行しました。古い [reactjs.org](https://reactjs.org) ドメインは、こちらのサイトにリダイレクトされるようになります。\n\n古い React ドキュメントは、[legacy.reactjs.org](https://legacy.reactjs.org) にアーカイブされました。「ウェブを破壊」してしまわないよう、古いコンテンツへの既存のリンクは、すべてそちらへ自動的にリダイレクトされるようになっていますが、レガシーサイトへのアップデートはほぼ行われなくなります。\n\n信じられないかもしれませんが、React はもうすぐ 10 歳になります。JavaScript 時間に換算すれば丸々 1 世紀のようなものです！ [React のホームページ](https://react.dev)をリフレッシュし、ユーザインターフェースを作成するために React が最適な方法であると私たちが考える理由を反映させました。また、スタートガイドも更新し、現代の React ベースのフレームワークにも目立つように言及しました。\n\nまだ新しいホームページをご覧になっていない方は、ぜひチェックしてみてください！\n\n## フックのあるモダン React に全面移行 {/*going-all-in-on-modern-react-with-hooks*/}\n\n2018 年に React フック (hook) をリリースした際、フックのドキュメントはクラスコンポーネントに精通していることを前提としていました。これにより、コミュニティは非常に迅速にフックを採用することができましたが、しばらくすると古いドキュメントは新しい読者に対応できなくなりました。新しく React を学ぶ人は、クラスコンポーネントを使った学習とフックを使った学習の 2 回に分けて React を学ばなければなりませんでした。\n\n**新しいドキュメントでは、最初からフックを用いて React を学びます**。ドキュメントは主に 2 つのセクションに分かれています。\n\n* **[React を学ぶ](/learn)** は、ゼロから React を学ぶ自己学習型のコースです。\n* **[API リファレンス](/reference)** では、すべての React API の詳細と使い方のサンプルが提供されています。\n\nそれぞれのセクションで見ることができる内容を詳しく見ていきましょう。\n\n<Note>\n\nフックが対応していないクラスコンポーネントの稀なユースケースが、まだわずかに存在します。クラスコンポーネントは引き続きサポートされており、新しいサイトの [レガシー API](/reference/react/legacy) セクションにドキュメントがあります。\n\n</Note>\n\n## クイックスタート {/*quick-start*/}\n\n学習セクションは[クイックスタート](/learn)ページから始まります。これは React の短い紹介ツアーです。コンポーネント、props、state などの概念に対応する構文を紹介しますが、それらの使い方については詳しく説明していません。\n\n実際にやりながら学ぶことが好きな方は、次に[三目並べのチュートリアル](/learn/tutorial-tic-tac-toe)をチェックしてください。React を使って小さなゲームを作る方法を説明しながら、毎日使うスキルを教えてくれます。以下が実際に作成するものです。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nfunction Square({ value, onSquareClick }) {\n  return (\n    <button className=\"square\" onClick={onSquareClick}>\n      {value}\n    </button>\n  );\n}\n\nfunction Board({ xIsNext, squares, onPlay }) {\n  function handleClick(i) {\n    if (calculateWinner(squares) || squares[i]) {\n      return;\n    }\n    const nextSquares = squares.slice();\n    if (xIsNext) {\n      nextSquares[i] = 'X';\n    } else {\n      nextSquares[i] = 'O';\n    }\n    onPlay(nextSquares);\n  }\n\n  const winner = calculateWinner(squares);\n  let status;\n  if (winner) {\n    status = 'Winner: ' + winner;\n  } else {\n    status = 'Next player: ' + (xIsNext ? 'X' : 'O');\n  }\n\n  return (\n    <>\n      <div className=\"status\">{status}</div>\n      <div className=\"board-row\">\n        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />\n        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />\n        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />\n        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />\n        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />\n        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />\n        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />\n      </div>\n    </>\n  );\n}\n\nexport default function Game() {\n  const [history, setHistory] = useState([Array(9).fill(null)]);\n  const [currentMove, setCurrentMove] = useState(0);\n  const xIsNext = currentMove % 2 === 0;\n  const currentSquares = history[currentMove];\n\n  function handlePlay(nextSquares) {\n    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];\n    setHistory(nextHistory);\n    setCurrentMove(nextHistory.length - 1);\n  }\n\n  function jumpTo(nextMove) {\n    setCurrentMove(nextMove);\n  }\n\n  const moves = history.map((squares, move) => {\n    let description;\n    if (move > 0) {\n      description = 'Go to move #' + move;\n    } else {\n      description = 'Go to game start';\n    }\n    return (\n      <li key={move}>\n        <button onClick={() => jumpTo(move)}>{description}</button>\n      </li>\n    );\n  });\n\n  return (\n    <div className=\"game\">\n      <div className=\"game-board\">\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n      </div>\n      <div className=\"game-info\">\n        <ol>{moves}</ol>\n      </div>\n    </div>\n  );\n}\n\nfunction calculateWinner(squares) {\n  const lines = [\n    [0, 1, 2],\n    [3, 4, 5],\n    [6, 7, 8],\n    [0, 3, 6],\n    [1, 4, 7],\n    [2, 5, 8],\n    [0, 4, 8],\n    [2, 4, 6],\n  ];\n  for (let i = 0; i < lines.length; i++) {\n    const [a, b, c] = lines[i];\n    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {\n      return squares[a];\n    }\n  }\n  return null;\n}\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\nまた、[React の流儀](/learn/thinking-in-react)についても特筆したいと思います。これが多くの人にとって React が「ピンとくる」きっかけとなったチュートリアルです。**これらの古典的なチュートリアルも、関数コンポーネントとフックを使用するものにアップデートされ**、新品同様となっています。\n\n<Note>\n\n上記のサンプルは*サンドボックス*となっています。サイト全体にたくさんのサンドボックスを追加しました。その数 600 個以上です！ どのサンドボックスも編集でき、右上隅の \"Fork\" ボタンを押すと別のタブで開くことができます。サンドボックスでは、React の API をすばやく試すことができ、アイデアの探求や、理解度のチェックができます。\n\n</Note>\n\n## React をステップバイステップで学ぶ {/*learn-react-step-by-step*/}\n\n世界中の誰もが、無料で自分のペースで React を学ぶ機会があるようにしたいと考えています。\n\nこのため、学習セクションは章に分かれた自己学習型のコースのように構成されています。最初の 2 章では React の基本が説明されています。これから React を始める方、または記憶をリフレッシュしたい方は、こちらから始めてください：\n\n- **[UI の記述](/learn/describing-the-ui)** では、コンポーネントを使って情報を表示する方法を学びます。\n- **[インタラクティビティの追加](/learn/adding-interactivity)** では、ユーザ入力に応じて画面を更新する方法を学びます。\n\n次の 2 つの章はより高度であり、やや難しいトピックについて深い洞察が得られるようになっています：\n\n- **[state の管理](/learn/managing-state)** では、アプリの複雑性が増すにつれて、ロジックをどのように整理するかを学びます。\n- **[避難ハッチ](/learn/escape-hatches)** では、React の「外に出る」方法と、どんなときにそれをする意味があるのかについて学びます。\n\n各章はいくつかの関連するページで構成されています。これらのページのほとんどは、特定のスキルやテクニックを教えるためのものです。例えば、[JSX でマークアップを書く](/learn/writing-markup-with-jsx)、[state 内のオブジェクトを更新する](/learn/updating-objects-in-state)、[コンポーネント間で state を共有する](/learn/sharing-state-between-components)、などです。ページの中には、[レンダーとコミット](/learn/render-and-commit) や [スナップショットとしての state](/learn/state-as-a-snapshot) のような、概念を説明するものもあります。また、[そこに副作用は要らないかもしれない](/learn/you-might-not-need-an-effect) など、これまでの経験から得られた提案を共有するためのページもあります。\n\nこれらの章をこの順番で読む必要はありません。そんな時間がどこにあるでしょうか？ ですがそうしても構いません。学習セクションのページは、より前の段階で紹介された概念にのみ依存しています。本のように読みたい場合は、順番に読み進めてください！\n\n### チャレンジ問題で理解度を確認する {/*check-your-understanding-with-challenges*/}\n\n学習セクションのほとんどのページは、理解度を確認するためのいくつかのチャレンジ問題で終わります。例えば、[条件付きレンダー](/learn/conditional-rendering#challenges) に関するページでは、以下のようなチャレンジ問題がいくつか提供されています。\n\n今すぐここで解いてみる必要はありません！ ただし*本当に*やりたい場合はどうぞ。\n\n<Challenges>\n\n#### `? :` を使って未梱包アイコンを表示 {/*show-an-icon-for-incomplete-items-with--*/}\n\n条件演算子 (`cond ? a : b`) を使って、`isPacked` が `true` でない場合は ❌ をレンダーするようにしてください。\n\n<Sandpack>\n\n```js\nfunction Item({ name, isPacked }) {\n  return (\n    <li className=\"item\">\n      {name} {isPacked && '✅'}\n    </li>\n  );\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          isPacked={true} \n          name=\"Space suit\" \n        />\n        <Item \n          isPacked={true} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          isPacked={false} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n<Sandpack>\n\n```js\nfunction Item({ name, isPacked }) {\n  return (\n    <li className=\"item\">\n      {name} {isPacked ? '✅' : '❌'}\n    </li>\n  );\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          isPacked={true} \n          name=\"Space suit\" \n        />\n        <Item \n          isPacked={true} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          isPacked={false} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n#### `&&` 演算子を使ったアイテムの重要度の表示 {/*show-the-item-importance-with*/}\n\nこの例では、それぞれの `Item` が数値型の `importance` プロパティを受け取ります。重要度が 0 以外の場合に限り、`&&` 演算子を使用して、斜体で \"_(Importance: X)_\" と表示するようにしてください。以下のような結果になるようにしましょう。\n\n* Space suit _(Importance: 9)_\n* Helmet with a golden leaf\n* Photo of Tam _(Importance: 6)_\n\n重要度を表示する場合は 2 つのテキストの間にスペースを入れることを忘れないでください！\n\n<Sandpack>\n\n```js\nfunction Item({ name, importance }) {\n  return (\n    <li className=\"item\">\n      {name}\n    </li>\n  );\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          importance={9} \n          name=\"Space suit\" \n        />\n        <Item \n          importance={0} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          importance={6} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n以下のようにすれば動きます：\n\n<Sandpack>\n\n```js\nfunction Item({ name, importance }) {\n  return (\n    <li className=\"item\">\n      {name}\n      {importance > 0 && ' '}\n      {importance > 0 &&\n        <i>(Importance: {importance})</i>\n      }\n    </li>\n  );\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          importance={9} \n          name=\"Space suit\" \n        />\n        <Item \n          importance={0} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          importance={6} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\n`importance` が `0` の場合に `0` が結果として表示されてしまわないよう、`importance && ...` ではなく `importance > 0 && ...` と書く必要があることに注意してください。\n\nこの答えでは、名前と重要度ラベルの間にスペースを挿入するために、2 つの条件が使用されています。代わりに、先頭にスペースを入れたフラグメントを使用することができます：`importance > 0 && <> <i>...</i></>` あるいは、`<i>` の直接内側にスペースを追加することもできます：`importance > 0 && <i> ...</i>`。\n\n</Solution>\n\n</Challenges>\n\n左下の角にある「答えを見る」ボタンに気付きましたか？ 自分でチェックしたい場合に便利です。\n\n### 図解とイラストで直感を養う {/*build-an-intuition-with-diagrams-and-illustrations*/}\n\nコードと言葉だけでは説明が難しかった場合、直感を養うための図解を追加しました。例えば以下は、[state の保持とリセット](/learn/preserving-and-resetting-state) にある図のひとつです。\n\n<Diagram name=\"preserving_state_diff_same_pt1\" height={350} width={794} alt=\"Diagram with three sections, with an arrow transitioning each section in between. The first section contains a React component labeled 'div' with a single child labeled 'section', which has a single child labeled 'Counter' containing a state bubble labeled 'count' with value 3. The middle section has the same 'div' parent, but the child components have now been deleted, indicated by a yellow 'proof' image. The third section has the same 'div' parent again, now with a new child labeled 'div', highlighted in yellow, also with a new child labeled 'Counter' containing a state bubble labeled 'count' with value 0, all highlighted in yellow.\">\n\n`section` が `div` に変わると、`section` は削除され、新しい `div` が追加される\n\n</Diagram>\n\nドキュメントの中にはイラストもいくつかあります。こちらは[ブラウザが画面を描画しているイラスト](/learn/render-and-commit#epilogue-browser-paint)となります：\n\n<Illustration alt=\"A browser painting 'still life with card element'.\" src=\"/images/docs/illustrations/i_browser-paint.png\" />\n\nブラウザベンダに確認したところ、この描写は 100% 科学的に正確であるとのことです。\n\n## 新しい、詳細な API リファレンス {/*a-new-detailed-api-reference*/}\n\n[API リファレンス](/reference/react) では、すべての React API に専用のページが用意されています。これにはあらゆる種類の API が含まれます：\n\n- 組み込みフックである [`useState`](/reference/react/useState) など。\n- 組み込みコンポーネントである [`<Suspense>`](/reference/react/Suspense) など。\n- 組み込みブラウザコンポーネントである [`<input>`](/reference/react-dom/components/input) など。\n- フレームワーク向けの API である [`renderToPipeableStream`](/reference/react-dom/server/renderToReadableStream) など。\n- その他の React API である [`memo`](/reference/react/memo) など。\n\n各 API ページは少なくとも*リファレンス*と*使用法*の 2 つのセグメントに分かれていることに気付くでしょう。\n\n[リファレンス](/reference/react/useState#reference)は、API の引数と返り値をリストアップすることによって、正式な API シグネチャを説明します。簡潔ですが、その API に慣れていない場合は少し抽象的に感じることがあります。API が何をするのかは説明しますが、我々がどのように使用するのかは説明しません。\n\n[使用法](/reference/react/useState#usage)では、実際にどのようにこの API を使用するのかを、同僚や友人が説明するような形で示します。これは、**React チームが各 API を使用するために意図した標準的なシナリオ**を示しています。色分けされたスニペット、異なる API を一緒に使用するサンプル、コピーペーストできるレシピも追加しました：\n\n<Recipes titleText=\"Basic useState examples\" titleId=\"examples-basic\">\n\n#### カウンタ（数値） {/*counter-number*/}\n\nこの例では、`count` state 変数が数値を保持しています。ボタンをクリックすると、それが増加します。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [count, setCount] = useState(0);\n\n  function handleClick() {\n    setCount(count + 1);\n  }\n\n  return (\n    <button onClick={handleClick}>\n      You pressed me {count} times\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### テキストフィールド（文字列） {/*text-field-string*/}\n\nこの例では、`text` state 変数に文字列を保持しています。入力すると、`handleChange` がブラウザの入力 DOM 要素から最新の入力値を読み取り、`setText` を呼び出して state を更新します。これにより、現在の `text` を下に表示することができます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function MyInput() {\n  const [text, setText] = useState('hello');\n\n  function handleChange(e) {\n    setText(e.target.value);\n  }\n\n  return (\n    <>\n      <input value={text} onChange={handleChange} />\n      <p>You typed: {text}</p>\n      <button onClick={() => setText('hello')}>\n        Reset\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### チェックボックス（真偽値） {/*checkbox-boolean*/}\n\nこの例では、`liked` state 変数に真偽値を保持しています。入力をクリックすると、`setLiked` が `liked` state 変数を更新して、ブラウザのチェックボックス入力がチェックされた状態かどうか保存します。`liked` 変数は、チェックボックスの下にあるテキストをレンダーするために使用されます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function MyCheckbox() {\n  const [liked, setLiked] = useState(true);\n\n  function handleChange(e) {\n    setLiked(e.target.checked);\n  }\n\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={liked}\n          onChange={handleChange}\n        />\n        I liked this\n      </label>\n      <p>You {liked ? 'liked' : 'did not like'} this.</p>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### フォーム（2 つの変数） {/*form-two-variables*/}\n\n同じコンポーネントで複数の state 変数を宣言することができます。それぞれの state 変数は完全に独立しています。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [name, setName] = useState('Taylor');\n  const [age, setAge] = useState(42);\n\n  return (\n    <>\n      <input\n        value={name}\n        onChange={e => setName(e.target.value)}\n      />\n      <button onClick={() => setAge(age + 1)}>\n        Increment age\n      </button>\n      <p>Hello, {name}. You are {age}.</p>\n    </>\n  );\n}\n```\n\n```css\nbutton { display: block; margin-top: 10px; }\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n一部の API ページには、（よくある問題に対する）[トラブルシューティング](/reference/react/useEffect#troubleshooting)や、（非推奨の API に対する）[代替手段](/reference/react-dom/findDOMNode#alternatives)のセクションも含まれています。\n\nこのアプローチにより、API リファレンスが引数を調べる手段としてだけでなく、その API を使用してどれだけ色々なことができるのか、他の API とどのように繋がっているのかを示す手段として役立つことを願っています。\n\n## この次は？ {/*whats-next*/}\n\nこれで私たちのミニツアーは終了です！ 新しいウェブサイトを見て回り、好きな部分や嫌いな部分を見つけたら、これからも[イシュートラッカ](https://github.com/reactjs/react.dev/issues)にフィードバックを送ってください。\n\nこのプロジェクトのリリースまでに長い時間がかかってしまったことを認識しています。私たちは React コミュニティに読んでいただくに値する、高い品質のハードルを維持したかったのです。これらのドキュメントを書き、数々のサンプルを作っていくにあたり、私たちは自身のこれまでの説明が誤っていたことに気づき、React のバグを見つけ、さらには React の設計自体の不備を見つけて対処を始めたりすらしました。新しいドキュメントが、今後 React 自体の品質を一段階引き上げるのに役立つことを願っています。\n\nウェブサイトのコンテンツや機能を拡充するための要望を、既に皆さんから多数いただいています。例えば：\n\n- すべての例に TypeScript バージョンを提供する\n- パフォーマンス、テスト、アクセシビリティガイドの更新版を作成する\n- React Server Components のドキュメント化を、既にこれをサポートするフレームワークのドキュメントとは独立して行う\n- 国際コミュニティと協力して新しいドキュメントを翻訳する\n- 新サイトに欠けている機能を追加する（例えば、このブログの RSS）\n\n今回 [react.dev](https://react.dev/) が無事リリースされたことで、サードパーティの React 学習リソースに「追いつく」という目標から、新しい情報を追加して新しいウェブサイトをさらに改善することへと、焦点を移すことができるようになりました。\n\nReact を学ぶには、今がこれまでで最高のタイミングだと思います。\n\n## 誰がこのプロジェクトに取り組んだのか？ {/*who-worked-on-this*/}\n\nReact チームでは、[Rachel Nabors](https://twitter.com/rachelnabors/) がプロジェクトをリードし（イラストも提供）、[Dan Abramov](https://bsky.app/profile/danabra.mov) がカリキュラムをデザインしました。この 2 人が共同でほとんどのコンテンツを執筆しました。\n\nもちろん、これほど大きなプロジェクトが少人数で進むことはありません。お礼を言いたい方がたくさんいます！\n\n[Sylwia Vargas](https://twitter.com/SylwiaVargas) は、\"foo/bar/baz\" や猫を安易に使ったものではない、世界中の科学者・芸術家・都市をフィーチャーしたサンプルを作成しました。[Maggie Appleton](https://twitter.com/Mappletons) は、我々の落書きをクリアな図解にしました。\n\n執筆の協力に感謝します：[David McCabe](https://twitter.com/mcc_abe)、[Sophie Alpert](https://twitter.com/sophiebits)、[Rick Hanlon](https://twitter.com/rickhanlonii)、[Andrew Clark](https://twitter.com/acdlite)、[Matt Carroll](https://twitter.com/mattcarrollcode)。アイディアとフィードバックを頂いた方に感謝します：[Natalia Tepluhina](https://twitter.com/n_tepluhina)、[Sebastian Markbåge](https://twitter.com/sebmarkbage)。\n\nサイトデザインに感謝します：[Dan Lebowitz](https://twitter.com/lebo)。サンドボックスデザインに感謝します：[Razvan Gradinar](https://dribbble.com/GradinarRazvan)。\n\n開発面では、プロトタイプ開発に感謝します：[Jared Palmer](https://twitter.com/jaredpalmer)。UI 開発のサポートに感謝します：[Dane Grant](https://twitter.com/danecando)、[Dustin Goodman](https://twitter.com/dustinsgoodman) ([ThisDotLabs](https://www.thisdot.co/))。サンドボックス統合に感謝します：[Ives van Hoorne](https://twitter.com/CompuIves)、[Alex Moldovan](https://twitter.com/alexnmoldovan)、[Jasper De Moor](https://twitter.com/JasperDeMoor)、[Danilo Woznica](https://twitter.com/danilowoz) ([CodeSandbox](https://codesandbox.io/))。開発やデザインの手直し、色づけや細かい部分の調整に感謝します：[Rick Hanlon](https://twitter.com/rickhanlonii)。サイトに新機能を追加し、メンテナンスしてくれる [Harish Kumar](https://www.strek.in/) と [Luna Ruan](https://twitter.com/lunaruan) に感謝します。\n\nアルファテスト及びベータテストプログラムに参加してくださった皆さんに心から感謝します。皆さんの情熱と貴重なフィードバックによって、このドキュメントを形作ることができました。そして React Conf 2021 で自身の経験をもとに React ドキュメントについて語っていただいた、ベータテスタの [Debbie O'Brien](https://twitter.com/debs_obrien) に特別な感謝を送ります。\n\n最後に、この取り組みの背後にあるインスピレーションを与えてくださった React コミュニティに感謝します。これを行っているのは皆さんがいるからです。新しいドキュメントが、皆さんの望むどのようなユーザインターフェースであっても、それを React で構築する際の手助けとなれば幸いです。"
  },
  {
    "path": "src/content/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023.md",
    "content": "---\ntitle: \"React Labs: 私達のこれまでの取り組み - 2023年3月版\"\nauthor: Joseph Savona, Josh Story, Lauren Tan, Mengdi Chen, Samuel Susla, Sathya Gunasekaran, Sebastian Markbage, and Andrew Clark\ndate: 2023/03/22\ndescription: React Labs 記事では、現在活発に研究・開発が行われているプロジェクトについて述べていきます。前回のアップデートから大きな進展がありましたので、我々が学んだことを共有していきます。\n---\n\nMarch 22, 2023 by [Joseph Savona](https://twitter.com/en_JS), [Josh Story](https://twitter.com/joshcstory), [Lauren Tan](https://twitter.com/potetotes), [Mengdi Chen](https://twitter.com/mengdi_en), [Samuel Susla](https://twitter.com/SamuelSusla), [Sathya Gunasekaran](https://twitter.com/_gsathya), [Sebastian Markbåge](https://twitter.com/sebmarkbage), and [Andrew Clark](https://twitter.com/acdlite)\n\n---\n\n<Intro>\n\nReact Labs 記事では、現在活発に研究・開発が行われているプロジェクトについて述べていきます。[前回のアップデート](/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022)から大きな進展がありましたので、我々が学んだことを共有していきます。\n\n</Intro>\n\n---\n\n## React Server Components {/*react-server-components*/}\n\nReact Server Components (RSC) は、React チームによって設計された新しいアプリケーションアーキテクチャです。\n\nRSC に関する研究成果を最初に共有したのは[紹介トーク](/blog/2020/12/21/data-fetching-with-react-server-components)と [RFC](https://github.com/reactjs/rfcs/pull/188) でした。簡単にまとめると、JavaScript バンドルに含まれず、事前に実行される新しい種類のコンポーネントである、サーバコンポーネントを導入する、というものです。サーバコンポーネントはビルド中に実行され、ファイルシステムからの読み込みや、静的なコンテンツのフェッチを行えます。また、サーバ上で実行することも可能であるため、API を作成せずにデータレイヤにアクセスすることができます。サーバコンポーネントからブラウザのインタラクティブなクライアントコンポーネントへと、props を使ってデータの受け渡しができます。\n\nRSC は、サーバセントリックなマルチページアプリケーションのシンプルな \"リクエスト/レスポンス\" モデルと、クライアントセントリックなシングルページアプリケーションのシームレスなインタラクティブ性を組み合わせて、両方の手法のいいところ取りが可能になります。\n\n前回のアップデート以降、[React Server Components RFC](https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md) をマージして提案を承認しました。[React Server Module Conventions](https://github.com/reactjs/rfcs/blob/main/text/0227-server-module-conventions.md) の提案に関する大きな課題を解決し、`\"use client\"` を使うという規約に関してパートナーとのコンセンサスを得ました。これらの文書は、RSC 互換実装がサポートするべき仕様としても機能します。\n\n最大の変更点は、サーバコンポーネントにおいては [`async` / `await`](https://github.com/reactjs/rfcs/pull/229) を主要なデータフェッチ方法として導入することにしたことです。また、Promise の中身を取り出す新しいフック `use` を導入してクライアントでのデータロードをサポートする予定です。クライアントのみのアプリにおいては、あらゆるコンポーネントで `async / await` をサポートすることはできませんが、RSC アプリの構造に似た方法でクライアントオンリーのアプリを作成する場合にはそれができるよう、サポートを追加する予定です。\n\nデータフェッチがかなり整理されましたので、クライアントからサーバという逆方向へのデータ送信を行って、データベース更新やフォームの実装ができる方法についても検討しています。サーバ/クライアントの境界を越えて Server Action 関数を渡せるようにすることで、クライアントがそれを呼び出し、シームレスな RPC を実現できるようする、という方法を考えています。Server Action により、JavaScript が読み込まれる前に段階的に動作するようになるフォームを提供することも可能です。\n\nReact Server Components は [Next.js App Router](/learn/creating-a-react-app#nextjs-app-router) でリリースされました。これは RSC をプリミティブとして完全採用し深く統合を行ったルータのデモとなっていますが、これが RSC 互換ルータやフレームワークを構築するための唯一の方法というわけではありません。RSC 仕様が提供するものと、その実装が提供するものとの間には明確な区切りがあります。React Server Components は、互換のある複数の React フレームワーク間で動作する、コンポーネント仕様として作られています。\n\n一般的には既存のフレームワークを使用することをお勧めしますが、自分自身のカスタムフレームワークの構築が必要な場合は、それも可能です。独自の RSC 互換フレームワークの構築は、主にバンドラとの深い統合が必要であるという理由により、我々が望むほど簡単なものにはなっていません。現行世代のバンドラはクライアントでの使用には適していますが、サーバとクライアントをまたいだ単一のモジュールグラフの分割を行うことを優先的なサポート項目として設計されてはいません。これが、現在 RSC のプリミティブを組み込んでもらうために、バンドラ開発者と直接協力している理由です。\n\n## アセットの読み込み {/*asset-loading*/}\n\n[サスペンス (Suspense)](/reference/react/Suspense) は、コンポーネントのデータやコードがまだ読み込み中の場合に画面に表示する内容を指定するものです。これにより、ページが読み込まれる間や、ルータナビゲーションにより追加データやコードが読み込まれる間、ユーザに段階的にコンテンツを表示させることができます。ただし、ユーザの視点からすると、データロードとレンダーが終わっただけでは、新しいコンテンツの準備が完了という話にはなりません。デフォルトではブラウザはスタイルシート、フォント、画像などを独立して読み込みますが、このため UI のジャンプやレイアウトのシフトが繰り返し発生することがあります。\n\nスタイルシート、フォント、画像の読み込みライフサイクルと Suspense を完全に統合し、コンテンツが表示可能かどうかを判断するために React がそれらを考慮できるようにする作業を進めています。あなたが React コンポーネントを書くやり方を一切変えずに、更新がより一貫性のある快適な方法で動作するようになるでしょう。最適化として、コンポーネントからフォントのようなアセットを手動でプリロードする手段も提供する予定です。\n\n現在、これらの機能を実装しており、近いうちに詳細を共有できる予定です。\n\n## ドキュメントメタデータ {/*document-metadata*/}\n\nアプリ内の様々なページや画面には、`<title>` タグやページの説明、その他の画面固有の `<meta>` タグなど、様々なメタデータが存在することでしょう。保守の観点からは、これらの情報をそのページや画面に対応する React コンポーネントに近いところで保持するほうがスケーラブルです。しかし、これらのメタデータの HTML タグは、通常アプリの最上位にあるコンポーネントでレンダーされる、ドキュメント `<head>` 内に配置する必要があります。\n\n現在、人々がこの問題を解決するテクニックが 2 つあります。\n\n1 つ目は、`<title>`、`<meta>` やその他内側に書いたタグをドキュメント `<head>` 内に移動するための、特別なサードパーティ製コンポーネントをレンダーする、というものです。これは主要なブラウザでは機能しますが、クライアントサイドの JavaScript を実行しない多くのクライアント、例えば Open Graph パーサなどが存在するため、この技術が普遍的に最適というわけではありません。\n\n2 つ目は、ページを 2 つのパーツに分けてサーバでレンダーする、というものです。まず、メインコンテンツをレンダーして、それ用のタグがすべて収集されます。次に、`<head>` とそれに対応するタグのレンダーを行います。最後に、`<head>` とメインコンテンツがブラウザに送信されます。このアプローチは機能しますが、`<head>` が送信できるようになる前にすべてのコンテンツをレンダーする必要があるため、[React 18 のストリーミングサーバレンダラ](/reference/react-dom/server/renderToReadableStream)を活用できなくなってしまいます。\n\n以上の理由により、我々は `<title>`、`<meta>`、およびメタデータ用 `<link>` タグをコンポーネントツリーの任意の場所でレンダーするための組み込みサポートを追加しようとしています。これは完全にクライアント側のコード、SSR、および将来的には RSC を含む、すべての環境で同じように機能する予定です。これについては、近日中に詳細を共有します。\n\n## React 最適化コンパイラ {/*react-optimizing-compiler*/}\n\n前回のアップデート以降、React の最適化コンパイラである [React Forget](/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022#react-compiler) の設計に積極的に取り組んできました。今まではこれを「自動メモ化コンパイラ」であるとお話ししてきており、ある意味においてはそれは正解です。しかし、このコンパイラを構築することで、私たちは React のプログラミングモデルをさらに深く理解できるようになりました。React Forget をよりよく理解する方法は、それを*リアクティビティ*自動化コンパイラ (automatic *reactivity* compiler) として捉えることです。\n\nReact の中核概念は、開発者が UI を現在の状態に対する関数として定義する、ということです。コンポーネントロジックは、プレーンな JavaScript の値（数値、文字列、配列、オブジェクト）、そして標準的な JavaScript のイディオム（if/else、for など）を使用して記述します。メンタルモデルは、アプリケーションの state が変更されるたびに React が再レンダーを行う、というものです。このシンプルなメンタルモデルや JavaScript のセマンティクスから離れないことが、React プログラミングモデルにおける重要な原則です。\n\n問題は、React が時々*過度に*リアクティブになる、すなわち再レンダーが過剰になることがあるということです。たとえば、JavaScript では 2 つのオブジェクトや配列が同一である（同じキーと値を持っている）かどうかを比較する安価な方法がないため、レンダーのたびに新しいオブジェクトや配列を作成すると、React が本来必要である以上の作業を行うことがあります。これは、開発者がコンポーネントを明示的にメモ化して変更に対して過剰に反応しないようにする必要があることを意味します。\n\nReact Forget の目標は、React アプリがデフォルトでちょうどよい程度のリアクティビティを有することを保証することです。つまり、state の値に対して*意味のある*変更が行われたときにのみアプリが再レンダーされるようにします。実装の観点から言えば、自動的にメモ化するということですが、リアクティブ性という枠組みで React と Forget を捉えることが理解の上でより良い方法だと考えています。ひとつの考え方としてはこうです：React は現在、オブジェクトの同一性が変更されたときに再レンダーを行います。Forget を使うと、オブジェクトに意味のある値の変更があったときにのみ React が再レンダーを行うようになります。しかし深い比較によるランタイムコストをかけずに、です。\n\n具体的な進捗としては、前回の報告以降、この自動リアクティビティというアプローチに合わせてコンパイラの設計に大幅な見直しを行い、コンパイラを社内で使用して得られたフィードバックを反映させてきました。昨年末以降コンパイラに大幅なリファクタリングを行ったので、現在では Meta の一部の領域で実運用をスタートしています。本番環境での実績が証明され次第、オープンソース化する予定です。\n\n最後に、多くの方がコンパイラの仕組みに興味を持ってくださっています。コンパイラの動作が証明され、オープンソース化されたときに、もっと多くの詳細を共有できることを楽しみにしています。しかし、現時点で共有できることがいくつかがあります。\n\nコンパイラのコアは、Babel からほぼ完全に切り離されています。コアコンパイラ API は（大まかには）古い AST から新しい AST への変換（ソース位置データを保持しながら）を行うものです。内部では、カスタムコード表現や変換パイプラインを使用して、低レベルのセマンティック解析を行います。ただし、コンパイラへの主要な公開インターフェースは、Babel やその他のビルドシステムプラグインを通すものです。テストのしやすさのために現在 Babel プラグインを作っており、これはコンパイラを呼び出して各関数の新バージョンを生成し元の関数と入れ替える作業を行う、薄いラッパとなっています。\n\n過去数ヶ月間でコンパイラをリファクタリングする中で、私たちは条件分岐、ループ、再代入、ミューテーションなどによる複雑性を確実に扱えるよう、コアコンパイルモデルを洗練させることに焦点を当ててきました。ただし JavaScript には、if/else、三項演算子、for、for-in、for-of など、それらの機能を表現する方法がたくさんあります。最初から言語のすべてをサポートしようとすると、コアモデルを検証できる時期が遅れてしまったことでしょう。そのかわり、私たちは言語の小さいながらも代表的なサブセットから始めました。`let/const`、`if/else`、`for` ループ、オブジェクト、配列、プリミティブ、関数呼び出し、その他いくつかの機能です。コアモデルに対する自信を得て、内部の抽象化を洗練させながら、サポートされる言語サブセットの範囲を拡大していきました。また、まだサポートしていない構文について明確化し、サポートされていない入力に対しては診断情報をログに記録しつつコンパイルをスキップするようにしました。Meta のコードベースでコンパイラを試すためのユーティリティが存在し、サポートされていない中で最も一般的な機能を見つけ出し、それらを次に優先するよう決めることができるようになっています。言語全体をサポートするまで、段階的にサポート範囲の拡大を続けていく予定です。\n\nReact コンポーネント内のプレーンな JavaScript をリアクティブにするには、コードが実行していることを正確に理解できる、セマンティクスを深く理解したコンパイラが必要です。このアプローチを取ることで、ドメイン固有言語に制限されるのではなく、JavaScript 言語のすべての表現力を使ってプロダクトコードを記述できる、JavaScript 内リアクティビティシステムを作成しています。\n\n## オフスクリーンレンダリング {/*offscreen-rendering*/}\n\nオフスクリーンレンダリングは今後提供予定の React 機能であり、追加のパフォーマンスオーバーヘッドなしにバックグラウンドで画面をレンダーすることができます。これは、DOM 要素だけでなく React コンポーネントでも機能する [`content-visibility` CSS プロパティ](https://developer.mozilla.org/en-US/docs/Web/CSS/content-visibility) の一形態のようなものと考えることができます。研究の過程で、さまざまなユースケースが見つかりました。\n\n- ルータは、ユーザが画面遷移を行ったときに瞬時に利用できるよう、バックグラウンドで画面を事前レンダーしておくことができます。\n- タブ切り替えコンポーネントは、非表示タブの state を保持できるため、進行済みの作業を失うことなくユーザがタブ間を切り替えることができます。\n- 仮想化リストコンポーネントは、可視範囲の上下に追加の行をプリレンダーできます。\n- モーダルやポップアップを開いたときに、アプリの残りの部分を「バックグラウンド」モードにして、モーダル以外のすべてのイベントや更新が無効になるようにできます。\n\nほとんどの React 開発者は、React のオフスクリーン API を直接触ることはありません。代わりに、オフスクリーンレンダリングはルータや UI ライブラリに組み込まれるため、それらのライブラリを使う開発者は追加の作業をせずに、自動的にメリットを享受できます。\n\n重要なのは、コンポーネントの書き方を変えることなしに、あらゆる React ツリーをオフスクリーンでもレンダーできなければならない、ということです。コンポーネントをオフスクリーンでレンダーすると、表示されるまで実際には*マウント*が起きず、副作用も実行されなくなります。例えば、初めて表示されたときに `useEffect` を使って分析データをログ出力するコンポーネントがある場合、プリレンダーのせいで分析データの正確性が乱されることはありません。同様に、コンポーネントがオフスクリーンになると、副作用もアンマウントされます。オフスクリーンレンダリングの重要な機能は、コンポーネントの表示/非表示を切り替えても state が失われないことです。\n\n前回のアップデート以降、Meta では、Android および iOS 用の React Native のアプリにおいて内部的にプリレンダリングの実験バージョンをテストしており、パフォーマンスの改善が得られています。また、サスペンス関連でのオフスクリーンレンダリングの動作の改善も行い、オフスクリーン状態のツリー内でサスペンドが起きても、サスペンスのフォールバックが起きないようにしました。残った作業は、ライブラリ開発者に公開される基本要素を最終決定することです。今年中には RFC を公開し、テストおよびフィードバック用の実験用 API を提供する予定です。\n\n## トランジショントレーシング {/*transition-tracing*/}\n\nTransition Tracing API により、[React のトランジション](/reference/react/useTransition)が遅くなったことを検出し、なぜ遅くなるのかを調査することができます。前回のお知らせ以降、API の初期設計を完了し、[RFC](https://github.com/reactjs/rfcs/pull/238) を公開しました。基本的な機能も実装されています。現在、プロジェクトは保留中です。RFC に対するフィードバックを歓迎します。React のパフォーマンス測定ツールをより良くするために開発を再開できるようになることを楽しみにしています。これは、[Next.js App Router](/learn/creating-a-react-app#nextjs-app-router) のように React トランジション上に構築されたルータでは、特に役立ちます。\n\n* * *\nこのページでのアップデートに加えて、私たちのチームは最近、コミュニティのポッドキャストやライブストリームにゲスト出演し、取り組みについてより多くのことをお話しし、質問に答える機会がありました。\n\n* [Dan Abramov](https://bsky.app/profile/danabra.mov) と [Joe Savona](https://twitter.com/en_JS) は [Kent C. Dodds の YouTube チャンネル](https://www.youtube.com/watch?v=h7tur48JSaw) でインタビューを受け、React Server Components に関する懸念について議論を行いました。\n* [Dan Abramov](https://bsky.app/profile/danabra.mov) と [Joe Savona](https://twitter.com/en_JS) は [JSParty ポッドキャスト](https://jsparty.fm/267) のゲストとして招かれ、React の未来についての考えを共有しました。\n\nこの投稿のレビューに協力していただいた [Andrew Clark](https://twitter.com/acdlite)、[Dan Abramov](https://bsky.app/profile/danabra.mov)、[Dave McCabe](https://twitter.com/mcc_abe)、[Luna Wei](https://twitter.com/lunaleaps)、[Matt Carroll](https://twitter.com/mattcarrollcode)、[Sean Keegan](https://twitter.com/DevRelSean)、[Sebastian Silbermann](https://twitter.com/sebsilbermann)、[Seth Webster](https://twitter.com/sethwebster)、[Sophie Alpert](https://twitter.com/sophiebits) に感謝します。\n\nお読みいただきありがとうございました。次のアップデートでお会いしましょう！\n"
  },
  {
    "path": "src/content/blog/2023/05/03/react-canaries.md",
    "content": "---\ntitle: \"React Canary: Meta 外での段階的な新機能導入\"\nauthor: Dan Abramov, Sophie Alpert, Rick Hanlon, Sebastian Markbage, and Andrew Clark\ndate: 2023/05/03\ndescription: 私たちは、安定版がリリースされる前に、個々の新機能の設計がほぼ確定した段階でそれらを採用できるという選択肢を、React コミュニティに提供したいと考えています。これは、Meta が長年、React の最先端バージョンを社内で使用してきたやり方に似ています。私たちは、新たに公式サポート対象となる Canary リリースチャンネルを導入します。これにより、フレームワークのような統合済セットアップが、個々の React 機能の採用を React のリリーススケジュールから切り離して行えるようになります。\n---\n\nMay 3, 2023 by [Dan Abramov](https://bsky.app/profile/danabra.mov), [Sophie Alpert](https://twitter.com/sophiebits), [Rick Hanlon](https://twitter.com/rickhanlonii), [Sebastian Markbåge](https://twitter.com/sebmarkbage), and [Andrew Clark](https://twitter.com/acdlite)\n\n---\n\n<Intro>\n\n私たちは、安定版がリリースされる前に、個々の新機能の設計がほぼ確定した段階でそれらを採用できるという選択肢を、React コミュニティに提供したいと考えています。これは、Meta が長年、React の最先端バージョンを社内で使用してきたやり方に似ています。私たちは、新たに公式サポート対象となる [Canary リリースチャンネル](/community/versioning-policy#canary-channel)を導入します。これにより、フレームワークのような統合済セットアップが、個々の React 機能の採用を React のリリーススケジュールから切り離して行えるようになります。\n\n</Intro>\n\n---\n\n## tl;dr {/*tldr*/}\n\n* React は公式にサポートされる [Canary リリースチャンネル](/community/versioning-policy#canary-channel)を導入します。公式にサポートされているため、何らかのリグレッションが発生した場合、安定版リリースのバグと同様の緊急度で対応します。\n* Canary により、semver に従う安定版リリースに達する前に、個々の新しい React 機能を使用し始めることができます。\n* [Experimental](/community/versioning-policy#experimental-channel) チャンネルとは異なり、React Canary には、採用準備が整っていると合理的に判断できる機能のみが含まれます。フレームワークは Canary React のリリースをバージョン固定した上でバンドルするよう検討することをお勧めします。\n* Canary リリースでリリースした後に発生した破壊的変更や新機能は、ブログで告知を行います。\n* **これまで通り、React はすべての安定版リリースにおいて semver に従います。**\n\n## 通常の React 機能の開発方法 {/*how-react-features-are-usually-developed*/}\n\n通常、React のすべての機能は以下のような段階を経て開発されています。\n\n1. 初期バージョンを開発し、それに `experimental_` または `unstable_` というプレフィックスを付ける。機能は `experimental` リリースチャンネルでのみ利用可能になる。この時点では、機能は大幅に変更されることが予想される。\n2. Meta 内でこの機能を試してくれるチームを見つけ、フィードバックを提供してもらう。ここで何度か変更が加えられる。機能が安定するにつれ、Meta 内のさらに別のチームと協力しながら検証を行う。\n3. やがて設計に自信が持てるようになる。API の名前からプレフィックスを削除し、Meta のほとんどの製品が使っているブランチである `main` でデフォルトで利用可能にする。これにより、Meta 内のどのチームでもこの機能を使うことができるようになる。\n4. さらに方向性に自信を持てるようになったところで、新機能について RFC を投稿する。この時点で新しい設計は様々なケースで機能することが分かっているが、最後の調整を加えることもありえる。\n5. オープンソースリリースとして切り出す時期が近づいてきたら機能についてのドキュメントを作成し、最終的に安定版の React リリースとして機能をリリースする。\n\nこの戦略はこれまでにリリースしてきたほとんどの機能に対してはうまく機能します。ただし、機能が概ね利用可能になること（ステップ 3）とオープンソースでリリースされる（ステップ 5）こととの間には、大きなギャップがあることがあります。\n\n**私たちは React コミュニティに、Meta と同じアプローチで、個々の新機能が利用可能になった段階でそれらを早期に採用し、React の次のリリースサイクルまで待たないで済む、というオプションを提供したいと考えています**。\n\nこれまで通り、すべての React の機能は最終的には安定版のリリースに含まれることになります。\n\n## ただマイナーリリースを増やせばいいのでは？ {/*can-we-just-do-more-minor-releases*/}\n\n一般論として、*現に*私たちは新機能の導入に対してはマイナーリリースを使用することにしています。\n\nしかしこれが常に可能なわけではありません。場合によってはその新機能が、まだ完全に完成しておらず活発に研究中の*ほかの*新機能と相互に関連していることがあります。実装が関連しているため、別々にリリースすることはできません。同じパッケージ（例えば `react` や `react-dom`）に影響するものなので、バージョン番号を分けることもできません。そして、semver の要求通りにやってメジャーバージョンリリースを乱発してしまうということなしに、準備が整っていない機能を頻繁に書き換えられるようにする必要もあります。\n\nMeta では、`main` ブランチから React をビルドし、毎週特定のピン留めされたコミットに手動で更新することによって、この問題を解決しています。これはまた、過去数年間にわたって React Native リリースが実施してきたアプローチでもあります。React Native のすべての*安定版*リリースは、React リポジトリの `main` ブランチの特定のコミットにピン留めされています。これにより、React Native は重要なバグ修正を取り込むことができ、フレームワークレベルで新しい React 機能を段階的に採用し、グローバルな React のリリース予定に依存しないようにできます。\n\nこのワークフローを、他のフレームワークや統合済セットアップでも利用可能にしたいと考えているのです。例えば、これにより、React の*上に作られている*フレームワークが、React 関連の破壊的変更を、それが安定版リリース入る*前に*取り込むことができます。これが特に有用なのは、一部の破壊的変更はフレームワークとの結合部分にのみ影響するものだからです。これによりフレームワークは semver ルールを破ることなく、そのような破壊的変更を独自にマイナーバージョンを付けてリリースできます。\n\nCanary チャンネルでの継続的リリースにより、より緊密なフィードバックループを実現し、新機能がコミュニティで包括的な検証を確実に受けられるようにすることができます。このワークフローは、JavaScript の標準化委員会である TC39 が[番号付きのステージで変更を処理する方法](https://tc39.es/process-document/)に近いものです。新しい React 機能は、React の安定版でリリースされる前に、React をベースにしたフレームワークにおいて先に利用可能になることがあります。これは、新しい JavaScript 機能が、仕様の公式な一部として批准されるより前に、先にブラウザで利用可能になることと同様です。\n\n## なぜ Experimental リリースを使わないのか？ {/*why-not-use-experimental-releases-instead*/}\n\n技術的には [Experimental リリース](/community/versioning-policy#canary-channel)を使うことは*可能*ですが、実験的な API は、安定化への道のりの途中でで大幅な変更が行われる（または完全に削除される）ことがあるため、実験的リリースを本番環境で使用することはお勧めしません。Canary リリースにも（どんなリリースでもそうであるように）誤りが含まれることはありますが、今後はこのブログで Canary における破壊的変更を告知する予定です。Canary は、Meta が社内で実行しているコードに最も近いため、一般的には比較的安定していると考えられます。ただし、バージョンを固定して、ピン留めされたコミットを変更する際には手作業で GitHub のコミットログを確認する必要があります。\n\n**React を統合済セットアップ（フレームワークなど）以外で使用しているほとんどの人々は、Stable リリースを引き続き使用することになると考えています**。ただし、フレームワークを開発している場合は、特定のコミットにピン留めされた React の Canary バージョンをバンドルし、自分のペースで固定バージョンを更新していくことを検討してください。これによる利点は、過去数年間 React Native で行われてきたことと同様に、完成された個々の React 機能やバグ修正をユーザに対して早期に、かつ自身のリリーススケジュールに基づいて提供できるようになることです。デメリットとしては、取り込まれる React のコミットを自身で確認し、リリースに含まれる React の変更に対してユーザに伝えるための追加の責任が生じるということです。\n\nもしあなたがフレームワークの作者であり、このアプローチを試してみたいと思っている場合は、我々にお問い合わせください。\n\n## 破壊的変更と新機能を早期に発表 {/*announcing-breaking-changes-and-new-features-early*/}\n\nCanary リリースは、その時点で我々が次の安定版 React リリースに入ると予想しているものをもっともよく反映しています。\n\n従来は、リリースサイクルの*終盤*、つまりメジャーリリースの際に、破壊的変更について告知していました。しかし、Canary リリースが React の公式にサポートされる使用方法となったため、破壊的変更や重要な新機能は *Canary でのリリース時に*順次発表する方向に移行する予定です。たとえば、Canary で破壊的変更がマージされる場合、React ブログにそれに関する投稿を行い、必要に応じて codemod や移行手順も含めます。これにより、フレームワークの作者が当該変更を含むよう React Canary の固定バージョンを更新してメジャーリリースを切り出す場合、リリースノートから React ブログの投稿にリンクすることができます。最後に、React の安定版メジャーバージョンが準備できた時点で、私たちはすでに公開されているブログ投稿へのリンクを貼るようになります。これにより、私たちのチームがより迅速に先に進めるようになることを期待しています。\n\nAPI の文書化についても、Canary に登場する時点で行われる計画です。これらの API が Canary 外では利用できない場合でもです。Canary でのみ利用可能な API は、該当するページで特別な注釈によりマークされます。これには、[`use`](https://github.com/reactjs/rfcs/pull/229) のような API や、`cache`、`createServerContext` のようなこれから RFC を作成する予定の API が含まれます。\n\n## Canary のバージョンを固定する {/*canaries-must-be-pinned*/}\n\nアプリやフレームワークで Canary のワークフローを採用することにした場合、必ず使用している Canary の*正確な*バージョンをピン止めで固定するようにしてください。Canary はあくまでプレリリース版なので、破壊的変更が含まれる可能性があります。\n\n## 例：React Server Components {/*example-react-server-components*/}\n\n[3 月に発表した](/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#react-server-components)ように、React Server Components の規約は確定しており、ユーザ向けの API に関連する大きな破壊的変更はもう起きないと予想しています。しかし、React Server Components のサポートを React の安定版としてリリースすることはまだできません。なぜなら、いくつかの密接に絡み合ったフレームワーク専用の機能（例：[アセットのローディング](/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#asset-loading)）にまだ取り組んでおり、そちらでより多くの破壊的変更が発生することが予想されるからです。\n\nこれは、React Server Components はフレームワークによって採用される準備が整っていることを意味します。ただし、次のメジャー React リリースまで、フレームワークがそれらを採用する唯一の方法は、ピン止めされた React の Canary 版と併せてリリースすることです。（React の 2 つのコピーをバンドルしてしまうのを避けるため、これを行いたいフレームワークは、`react` と `react-dom` がフレームワークとともにリリースされたバージョン固定済みの Canary に解決 (resolve) されるよう強制し、それをユーザに説明する必要があります。例えば Next.js の App Router はこれを行っています。）\n\n## 安定版と Canary 版の両方に対してライブラリをテストする {/*testing-libraries-against-both-stable-and-canary-versions*/}\n\nライブラリ作者がすべての Canary リリースをテストすることは期待していません。それは非常に困難でしょう。ただし、[3 年前に複数の React プレリリースチャンネルを初めて導入したとき](https://legacy.reactjs.org/blog/2019/10/22/react-release-channels.html)と同様に、ライブラリは、最新の安定バージョンと最新の Canary バージョンの*両方*に対してテストを実行することをお勧めします。発表されなていない振る舞いの変化が見られた場合、React リポジトリにバグを登録して、調査の手助けができるようにしてください。この慣行が広く採用されるにつれ、React の新しいメジャーバージョンにライブラリがアップグレードする際に必要な労力が減少すると予想されます。なぜなら、偶発的なリグレッションが発生したときにリリース直後に見つけることができようになるからです。\n\n<Note>\n\n厳密に言えば、Canary は*新しい*リリースチャンネルではありません。これは以前 Next と呼ばれていたものです。ただし、Next.js との混乱を避けるため、名前を変更することにしました。これを*新しい*リリースチャンネルという形で発表しているのは、新しく期待できること、つまり Canary が公式にサポートされる React の使用方法であるということなどについてお伝えするためです。\n\n</Note>\n\n## 安定版リリースはこれまで同様 {/*stable-releases-work-like-before*/}\n\n安定版 React のリリース方針に関しては何の変更も行いません。\n\n\n\n"
  },
  {
    "path": "src/content/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024.md",
    "content": "---\ntitle: \"React Labs: 私達のこれまでの取り組み - 2024年2月版\"\nauthor: Joseph Savona, Ricky Hanlon, Andrew Clark, Matt Carroll, and Dan Abramov\ndate: 2024/02/15\ndescription: React Labs 記事では、現在活発に研究・開発が行われているプロジェクトについて述べていきます。前回のアップデートから大きな進展がありましたので、我々が学んだことを共有していきます。\n---\n\nFebruary 15, 2024 by [Joseph Savona](https://twitter.com/en_JS), [Ricky Hanlon](https://twitter.com/rickhanlonii), [Andrew Clark](https://twitter.com/acdlite), [Matt Carroll](https://twitter.com/mattcarrollcode), and [Dan Abramov](https://bsky.app/profile/danabra.mov).\n\n---\n\n<Intro>\n\nReact Labs 記事では、現在活発に研究・開発が行われているプロジェクトについて述べていきます。[前回のアップデート](/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023)から大きな進展がありましたので、我々が学んだことを共有していきます。\n\n</Intro>\n\n---\n\n## React Compiler {/*react-compiler*/}\n\nReact Compiler は、もはや研究プロジェクトではありません。このコンパイラは現在 instagram.com の本番環境で動作しています。Meta の他のプラットフォームにもコンパイラを展開するための作業や、オープンソースとしての初回リリースを行うための準備を進めています。\n\n私たちが[前回の投稿](/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#react-optimizing-compiler)でお伝えしたように、React は state が変更された際、*ときどき*過剰な再レンダーを行います。React の黎明期より、このような場合の解決策は手動によるメモ化を行うことでした。現在の API においては、これは [`useMemo`](/reference/react/useMemo)、[`useCallback`](/reference/react/useCallback)、[`memo`](/reference/react/memo) の各 API を適用して、state の変更に対する React の再レンダーの量を手動で調整することを意味します。しかし、手動によるメモ化は妥協の産物です。コードが読みづらくなり、間違いが起きやすくなり、最新の状態に保つために余分な作業が必要です。\n\n手動のメモ化は合理的な妥協策ではありますが、私たちは満足していませんでした。私たちのビジョンは、state が変更されたときに React が*自動的に* UI の正しい部分だけを再レンダーすることであり、それを *React のメンタルモデルを損なうことなしに*行うことです。UI を状態に対する単純な関数として捉え、JavaScript の標準的な値や記法を使って表現する、という React のアプローチが、多くの開発者にとって React が親しみやすいものである理由の一部である、と私たちは信じています。だからこそ、React のための最適化コンパイラの構築に投資することにしたのです。\n\nJavaScript は、そのルールの緩さと動的な性質のために、最適化が非常に難しい言語として知られています。React Compiler は、JavaScript のルールと、いわゆる「React のルール」の*両方を*モデル化することによって、コードを安全にコンパイルすることができます。たとえば、React コンポーネントは冪等、すなわち同じ入力が与えられたときに同じ値を返す必要がありますし、props や state の値を変更してはなりません。これらのルールは開発者の行えることを制限する一方で、コンパイラが安全に最適化できる余地を作り出すのに役立つのです。\n\nもちろん、しばしば開発者はこのルールを少しねじ曲げる、ということも理解しています。私たちの目標はできるだけ多くのコードで、React Compiler がそのまますぐに動作するようにすることです。コンパイラはコードが React のルールに厳密に従っていない場合にそのことを検出し、安全であればコードをコンパイルし、安全でなければコンパイルをスキップしようとします。私たちは Meta の大規模かつ多様なコードベースに対してテストを行い、このアプローチを検証するのに役立てています。\n\n自分のコードが React のルールに従っているか開発者が確認したい場合は、[Strict Mode を有効にする](/reference/react/StrictMode)ことと、[React の ESLint プラグインを設定する](/learn/editor-setup#linting)ことをお勧めします。これらのツールを使うことにより、React コードの微妙なバグが捉えられ、今すぐあなたのアプリケーションの品質が向上するとともに、React Compiler のような今後の機能に対するアプリケーションの未来も担保されます。私たちはまた、チームがこれらのルールを理解してより堅牢なアプリを作成できるよう、React のルールに関する統一的なドキュメント作成作業や、ESLint プラグインの更新作業にも取り組んでいます。\n\nコンパイラが実際に動作する様子を見たい場合は、[昨年秋の私たちの講演](https://www.youtube.com/watch?v=qOQClO3g8-Y)をチェックしてください。この講演の時点では、instagram.com のひとつのページで React Compiler を試してみた初期実験データしかありませんでした。その後私たちは、コンパイラを instagram.com 全体の本番環境で導入しました。また、チームを拡大して、Meta が公開している他の場所への展開やオープンソースへの展開を加速しています。この先の数ヶ月で、さらに多くの情報を共有できることを楽しみにしています。\n\n## アクション {/*actions*/}\n\n\n[以前のブログ記事](/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#react-server-components)で、クライアントからサーバへデータを送信してデータベース更新やフォーム実装を行うためのソリューションである、Server Action という試みについてお伝えしました。Server Action を開発する中で、これらの API を拡張し、クライアントのみのアプリケーションでのデータ処理にも対応させることにしました。\n\nこのより広範な機能の集合は、単に「アクション (Action)」と呼ばれるようになります。アクションにより、[`<form/>`](/reference/react-dom/components/form) のような DOM 要素に関数を渡すことができるようになります。\n\n```js\n<form action={search}>\n  <input name=\"query\" />\n  <button type=\"submit\">Search</button>\n</form>\n```\n\nこの `action` 関数は同期的にも非同期的にも動作します。クライアント側で標準の JavaScript を使用して定義することもできますし、[`'use server'`](/reference/rsc/use-server) ディレクティブを使用してサーバ側で定義することも可能です。アクションを使用することで、React がデータ送信に関するライフサイクルを管理するようになり、[`useFormStatus`](/reference/react-dom/hooks/useFormStatus) や [`useActionState`](/reference/react/useActionState) などのフックを通じて、現在の送信ステータスやフォームアクションのレスポンスにアクセスできるようになります。\n\nデフォルトでは、アクションは[トランジション](/reference/react/useTransition)内で送信されるため、アクションが処理されている間も現在のページをインタラクティブに保ちます。アクションは非同期関数をサポートしているため、トランジション内で `async/await` を使用する機能も追加しました。これにより、`fetch` のような非同期リクエストが開始されたときにトランジションの `isPending` 状態を使って保留中 (pending) UI を表示できるようになり、更新の適用が完了するまで保留中 UI を表示し続けることができます。\n\nアクションと並行して、楽観的な state 更新を管理するための [`useOptimistic`](/reference/react/useOptimistic) という機能を導入しています。このフックを使用すると、最終 state がコミットされた際に自動的に元に戻る一時的な更新を適用できます。アクションの場合、送信が成功するものと仮定してクライアント側でデータの最終 state を楽観的に設定しておき、最終的にサーバから受け取ったデータの値に戻すことができます。これは通常の `async`/`await` を使用して動作するため、クライアントで `fetch` を使用している場合でも、サーバからの Server Action を使用している場合でも同じように動作します。\n\nライブラリの作者は、`useTransition` を使用する独自のコンポーネントでカスタムの `action={fn}` という props を実装できます。私たちの意図は、ライブラリがコンポーネントの API を設計する際に Action のパターンを採用することで、React 開発者に一貫した体験が提供されるようになることです。例えば、あなたのライブラリが `<Calendar onSelect={eventHandler}>` というコンポーネントを提供している場合、`<Calendar selectAction={action}>` API も公開することを検討してください。\n\n当初はクライアントからサーバへのデータ転送方法としての Server Action にフォーカスしてきましたが、React の哲学は、すべてのプラットフォームと環境で同じプログラミングモデルを提供することです。可能な限り、クライアントで何か機能を導入する場合はサーバでも動作するようにしようとしますし、その逆も同様です。この哲学により、アプリがどこで実行される場合でも機能する単一の API セットを作成でき、後で異なる環境にアップグレードすることが容易になるでしょう。\n\nアクションは現在 Canary チャンネルで利用可能であり、次回の React のリリースに含まれる予定です。\n\n## React Canary での新機能 {/*new-features-in-react-canary*/}\n\n私たちは、semver の安定バージョンでリリースされる前に、設計がほぼ最終的な段階に近づいた新しい安定機能を個別に採用するオプションとして、[React Canary](/blog/2023/05/03/react-canaries) を導入しました。\n\nCanary は我々の新しい React 開発方法です。これまで、新機能は Meta 内でプライベートに研究、構築されていたため、ユーザは安定版にリリースされたときに初めて最終版のプロダクトを目にすることになっていました。Canary 以降は、コミュニティの助けを借りつつ公開の場で、React Labs ブログシリーズで共有している各種機能について開発を進め、最終確定を行っています。これは、機能が完成してしまった後ではなく最終確定の途上にある段階で、新機能についていち早く知ることができる、ということです。\n\nReact Server Components、アセットローディング、ドキュメントメタデータ、およびアクションは、すべて React Canary で導入済みであり、これらの機能に関するドキュメントは react.dev に追加されています。\n\n- **ディレクティブ**：[`\"use client\"`](/reference/rsc/use-client) と [`\"use server\"`](/reference/rsc/use-server) はフルスタック React フレームワーク用に設計されたバンドラ機能であり、2 つの環境間の「切り離しポイント」をマークします。`\"use client\"` はバンドラに `<script>` タグを生成するよう指示し（[Astro Islands](https://docs.astro.build/en/concepts/islands/#creating-an-island) のように）、`\"use server\"` はバンドラに POST エンドポイントを生成するよう指示します（[tRPC Mutations](https://trpc.io/docs/concepts) のように）。これらが協調して働くことで、クライアント側でのユーザ操作と関連するサーバ側のロジックが組み合わさった、再利用可能なコンポーネントを書くことが可能になります。\n\n- **ドキュメントメタデータ**：コンポーネントツリーのどこからでも [`<title>`](/reference/react-dom/components/title)、[`<meta>`](/reference/react-dom/components/meta)、およびメタデータ用 [`<link>`](/reference/react-dom/components/link) タグをレンダーできるようにするための組み込みサポートを追加しました。これらは、完全にクライアントのみのコード、SSR、および RSC を含むすべての環境において、同様に機能します。これにより、[React Helmet](https://github.com/nfl/react-helmet) などのライブラリが先行して切り開いた機能に対するサポートが、組み込みで提供されるようになります。\n\n- **アセットローディング**：スタイルシート、フォント、スクリプトなどのリソースのローディングライフサイクルをサスペンス (Suspense) と統合し、React が [`<style>`](/reference/react-dom/components/style)、[`<link>`](/reference/react-dom/components/link)、[`<script>`](/reference/react-dom/components/script) などの要素に対応する内容の表示準備ができているかどうか判断する際にこれを考慮するようにしました。また、リソースがいつロードおよび初期化されるべきかをより細かく制御するために、`preload` や `preinit` などの新しい [リソースローディング API](/reference/react-dom#resource-preloading-apis) を追加しました。\n\n- **アクション**: 上記で述べた通り、クライアントからサーバへのデータ送信を管理する機能であるアクションを追加しました。[`<form/>`](/reference/react-dom/components/form) などの要素に `action` を追加し、[`useFormStatus`](/reference/react-dom/hooks/useFormStatus) で送信ステータスを取得し、[`useActionState`](/reference/react/useActionState) で結果を処理し、[`useOptimistic`](/reference/react/useOptimistic) で UI を楽観的に更新することが可能です。\n\nこれらの機能はすべて連携して動作するため、個別に安定版チャンネルでリリースすることは困難です。フォームステータスを取得するためのフックによる補完なしでアクションをリリースすれば、実用的な有用性は限定されてしまうでしょう。React Server Components をサーバアクションと統合せずに導入すれば、サーバ上のデータを変更することが大変になってしまうでしょう。\n\n一連の機能を安定版チャンネルにリリースする前に、それらが統合的に動作し、開発者が本番環境で使用する際に必要なすべてのものが確実に揃っているようにする必要があります。React Canary により、これらの機能を個別に開発し、最終的に機能セット全体が完成する前に、安定した API を段階的にリリースすることができるのです。\n\n現在 React Canary の機能は完全に揃った状態であり、リリースの準備ができています。\n\n## 次の React メジャーバージョン {/*the-next-major-version-of-react*/}\n\n数年のイテレーションを経て、`react@canary` を `react@latest` にリリースする準備が整いました。上記で紹介した新機能は、あなたのアプリが動作するあらゆる環境と互換性があり、本番使用に必要なすべてを提供します。アセットローディングとドキュメントメタデータは一部のアプリにとって破壊的変更となる可能性があるため、次の React のバージョンはメジャーバージョン、**React 19** になります。\n\nリリースに向けてまだやるべきことがあります。React 19 では、Web Component のサポートのような、破壊的変更を伴うが長年の要望に応えることになる改善も追加されます。現在は、これらの変更を確定し、リリースの準備をし、新機能に対するドキュメントを仕上げ、新たに含まれる機能についてのアナウンスを公開することにフォーカスしています。\n\n今後数ヶ月で、React 19 の新要素に関するすべての情報、新しいクライアント機能の採用方法、React Server Components をサポートする環境の構築方法について、さらに情報を共有していきます。\n\n## オフスクリーン（Activity に改名） {/*offscreen-renamed-to-activity*/}\n\n前回のアップデート以降に、\"オフスクリーン (Offscreen)\" という研究中機能の名称を\"Activity\" に変更しました。「オフスクリーン」という名前は、アプリの見えない部分にのみこれが適用されるという誤った印象を与えるものでしたが、この機能を研究する中で、例えばモーダルの背後のコンテンツなど、アプリの一部は見えていても非アクティブになる可能性があることに気づきました。新しい名前は、アプリの特定の部分を「アクティブ」または「非アクティブ」とマークするという動作を、より正確に反映しています。\n\nActivity はまだ研究中であり、残された作業は、ライブラリ開発者に公開されるプリミティブを最終決定することです。より完成度の高い機能をリリースすることに焦点を当てているため、この部分については優先順位を下げています。\n\n* * *\n\nこのアップデートに加えて、私たちのチームはカンファレンスでの発表やポッドキャストへの出演を通じ、我々の作業についてお伝えし、質問にお答えしています。\n\n- [Sathya Gunasekaran](https://github.com/gsathya) は [React India](https://www.youtube.com/watch?v=kjOacmVsLSE) カンファレンスで React コンパイラについて話しました。\n\n- [Dan Abramov](/community/team#dan-abramov) は [RemixConf](https://www.youtube.com/watch?v=zMf_xeGPn6s) で \"React from Another Dimension\" というタイトルの講演を行い、React Server Components やアクションの成立に際してありえたかもしれない別の歴史について紹介しました。\n\n- [Dan Abramov](/community/team#dan-abramov) は [the Changelog’s JS Party podcast](https://changelog.com/jsparty/311) で React Server Components に関するインタビューに出演しました。\n\n- [Matt Carroll](/community/team#matt-carroll) は [Front-End Fire podcast](https://www.buzzsprout.com/2226499/14462424-interview-the-two-reacts-with-rachel-nabors-evan-bacon-and-matt-carroll) のインタビューに出演し、[The Two Reacts](https://overreacted.io/the-two-reacts/) についてお話ししました。\n\nこの投稿をレビューしてくれた [Lauren Tan](https://twitter.com/potetotes)、[Sophie Alpert](https://twitter.com/sophiebits)、[Jason Bonta](https://threads.net/someextent)、[Eli White](https://twitter.com/Eli_White)、そして [Sathya Gunasekaran](https://twitter.com/_gsathya) に感謝します。\n\nここまで読んでいただきありがとうございました。[React Conf でお会いしましょう](https://conf.react.dev/)！\n"
  },
  {
    "path": "src/content/blog/2024/04/25/react-19-upgrade-guide.md",
    "content": "---\ntitle: \"React 19 アップグレードガイド\"\nauthor: Ricky Hanlon\ndate: 2024/04/25\ndescription: React 19 に追加された改善にはいくつかの破壊的変更が必要ですが、アップグレードをできるだけスムーズに行えるよう努力しているため、ほとんどのアプリには影響が出ないことを予想しています。この投稿では、アプリやライブラリを React 19 にアップグレードする手順をご案内します。\n---\n\nApril 25, 2024 by [Ricky Hanlon](https://twitter.com/rickhanlonii)\n\n---\n\n\n<Intro>\n\nReact 19 に追加された改善にはいくつかの破壊的変更が必要ですが、アップグレードをできるだけスムーズに行えるよう努力しているため、ほとんどのアプリには影響が出ないことを予想しています。\n\n</Intro>\n\n<Note>\n\n#### React 18.3 も公開されました {/*react-18-3*/}\n\nReact 19 へのアップグレードを容易にするため、`react@18.3` リリースを公開しました。これは 18.2 とほぼ同一ですが、非推奨化 API や React 19 に向けて必要なその他の変更に対する警告が追加されています。\n\nReact 19 にアップグレードする前に、問題点を見つけるためにまず React 18.3 にアップグレードすることをお勧めします。\n\n18.3 における変更点については、[リリースノート](https://github.com/facebook/react/blob/main/CHANGELOG.md#1830-april-25-2024)をご覧ください。\n\n</Note>\n\nこの投稿では、React 19 にアップグレードする手順をご案内します。\n\n- [インストール](#installing)\n- [Codemod](#codemods)\n- [破壊的変更](#breaking-changes)\n- [新たな非推奨化](#new-deprecations)\n- [注目すべき変更点](#notable-changes)\n- [TypeScript 関連の変更](#typescript-changes)\n- [Changelog](#changelog)\n\nReact 19 をテストしていただける方は、このアップグレードガイドに従い、遭遇した[問題を報告](https://github.com/facebook/react/issues/new?assignees=&labels=React+19&projects=&template=19.md&title=%5BReact+19%5D)してください。React 19 に追加された新機能のリストについては、[React 19 リリースのお知らせ](/blog/2024/12/05/react-19)をご覧ください。\n\n---\n## インストール {/*installing*/}\n\n<Note>\n\n#### 新しい JSX トランスフォームの必須化 {/*new-jsx-transform-is-now-required*/}\n\n2020 年に、バンドルサイズを改善し、React をインポートせずに JSX を使用できるようにするための[新しい JSX トランスフォーム](https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html)を導入しました。React 19 では、ref を props として使用できるようにしたり JSX の速度を向上させたりといった追加の改善を行っており、これらには新しいトランスフォームが必須です。\n\n新しいトランスフォームが有効になっていない場合、次の警告が表示されます。\n\n<ConsoleBlockMulti>\n\n<ConsoleLogLine level=\"error\">\n\nYour app (or one of its dependencies) is using an outdated JSX transform. Update to the modern JSX transform for faster performance: https://react.dev/link/new-jsx-transform\n\n</ConsoleLogLine>\n\n</ConsoleBlockMulti>\n\n\nほとんどの環境ではこのトランスフォームがすでに有効になっているため、ほとんどのアプリは影響を受けないと予想されます。手動でアップグレードする方法については、[告知記事](https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html)をご覧ください。\n\n</Note>\n\n\nReact と React DOM の最新バージョンをインストールするには以下のようにします。\n\n```bash\nnpm install --save-exact react@^19.0.0 react-dom@^19.0.0\n```\n\nYarn をお使いの場合は以下のようにします。\n\n```bash\nyarn add --exact react@^19.0.0 react-dom@^19.0.0\n```\n\nTypeScript を使用している場合は、型も更新する必要があります。\n```bash\nnpm install --save-exact @types/react@^19.0.0 @types/react-dom@^19.0.0\n```\n\nOr, if you're using Yarn:\n```bash\nyarn add --exact @types/react@^19.0.0 @types/react-dom@^19.0.0\n```\n\nまた、最も一般的な書き換えのための codemod も含まれています。下記の [TypeScript 関連の変更](#typescript-changes)を参照してください。\n\n## codemod {/*codemods*/}\n\nアップグレードを支援するため、[codemod.com](https://codemod.com) のチームと協力し、React 19 の新しい API やパターンにコードを自動的に更新するための codemod を公開しました。\n\nすべての codemod は [`react-codemod` リポジトリ](https://github.com/reactjs/react-codemod)で利用可能であり、Codemod チームも codemod の保守に参加しています。これらの codemod を実行するには、`react-codemod` コマンドではなく `codemod` コマンドの使用をお勧めします。こちらのコマンドの方が高速に実行され、複雑なコードの移行を処理でき、TypeScript のサポートもより良好です。\n\n\n<Note>\n\n#### React 19 関連の codemod をすべて実行 {/*run-all-react-19-codemods*/}\n\nこのガイドにある codemode をすべて実行するには React 19 の `codemod` レシピを以下のように実行します。\n\n```bash\nnpx codemod@latest react/19/migration-recipe\n```\n\nこれにより以下の `react-codemod` の codemod が実行されます。\n- [`replace-reactdom-render`](https://github.com/reactjs/react-codemod?tab=readme-ov-file#replace-reactdom-render) \n- [`replace-string-ref`](https://github.com/reactjs/react-codemod?tab=readme-ov-file#replace-string-ref)\n- [`replace-act-import`](https://github.com/reactjs/react-codemod?tab=readme-ov-file#replace-act-import)\n- [`replace-use-form-state`](https://github.com/reactjs/react-codemod?tab=readme-ov-file#replace-use-form-state) \n- [`prop-types-typescript`](https://github.com/reactjs/react-codemod#react-proptypes-to-prop-types)\n\nこれには TypeScript 関連の変更は含まれていません。以下の [TypeScript 関連の変更](#typescript-changes)を参照してください。\n\n</Note>\n\n変更のうち codemod が利用できるものは以下で紹介されています。\n\n利用可能なすべての codemod の一覧については、[`react-codemod` リポジトリ](https://github.com/reactjs/react-codemod)を参照してください。\n\n## 破壊的変更 {/*breaking-changes*/}\n\n### レンダー中のエラーは再スローされない {/*errors-in-render-are-not-re-thrown*/}\n\nこれまでのバージョンの React では、レンダー中にスローされたエラーはキャッチされた後に再スローされていました。開発環境では、`console.error` にもログを出力していたため、エラーログの重複が発生していました。\n\nReact 19 では、重複を減らすためにエラーの扱いを[改善し](/blog/2024/04/25/react-19#error-handling)、再スローは行わないようになりました。\n\n- **キャッチされないエラー**：エラーバウンダリによってキャッチされないエラーは `window.reportError` に報告されます。\n- **キャッチされたエラー**：エラーバウンダリによってキャッチされたエラーは `console.error` に報告されます。\n\nこの変更はほとんどのアプリに影響を与えないはずですが、本番環境におけるエラーレポートシステムが再スローの挙動に依存している場合は、エラー処理を更新する必要があります。これをサポートするために、カスタムのエラー処理を行うための新しい手段を `createRoot` と `hydrateRoot` に追加しました。\n\n```js [[1, 2, \"onUncaughtError\"], [2, 5, \"onCaughtError\"]]\nconst root = createRoot(container, {\n  onUncaughtError: (error, errorInfo) => {\n    // ... log error report\n  },\n  onCaughtError: (error, errorInfo) => {\n    // ... log error report\n  }\n});\n```\n\n詳細は、[`createRoot`](https://react.dev/reference/react-dom/client/createRoot) と [`hydrateRoot`](https://react.dev/reference/react-dom/client/hydrateRoot) のドキュメントを参照してください。\n\n\n### 非推奨化 React API の削除 {/*removed-deprecated-react-apis*/}\n\n#### 廃止：関数コンポーネントの `propTypes` と `defaultProps` {/*removed-proptypes-and-defaultprops*/}\n`PropTypes` は [2017 年 4 月 (v15.5.0)](https://legacy.reactjs.org/blog/2017/04/07/react-v15.5.0.html#new-deprecation-warnings) に非推奨化されました。\n\nReact 19 では、React パッケージから `propTypes` チェックが削除されており、使用しても無視されるようになります。`propTypes` を使用している場合は、TypeScript または他の型チェックソリューションへの移行をお勧めします。\n\nまた、関数コンポーネントから `defaultProps` を削除します。ES6 のデフォルトパラメータを代わりに使用してください。クラスコンポーネントでは ES6 による代替手段がないため、`defaultProps` を引き続きサポートします。\n\n```js\n// Before\nimport PropTypes from 'prop-types';\n\nfunction Heading({text}) {\n  return <h1>{text}</h1>;\n}\nHeading.propTypes = {\n  text: PropTypes.string,\n};\nHeading.defaultProps = {\n  text: 'Hello, world!',\n};\n```\n```ts\n// After\ninterface Props {\n  text?: string;\n}\nfunction Heading({text = 'Hello, world!'}: Props) {\n  return <h1>{text}</h1>;\n}\n```\n\n<Note>\n\ncodemod で以下のように `propTypes` を TypeScript に変換できます。\n\n```bash\nnpx codemod@latest react/prop-types-typescript\n```\n\n</Note>\n\n#### 廃止：`contextTypes` と `getChildContext` を使用したレガシーコンテクスト {/*removed-removing-legacy-context*/}\n\nレガシーコンテクストは [2018 年 10 月 (v16.6.0)](https://legacy.reactjs.org/blog/2018/10/23/react-v-16-6.html) に非推奨化されました。\n\nレガシーコンテクストは、`contextTypes` と `getChildContext` を使用するクラスコンポーネントでのみ利用可能であり、見逃しやすい微妙なバグのため、のちに `contextType` に置き換えられました。React 19 では、React を少し小さく、速くするためにレガシーコンテクストを削除しています。\n\nクラスコンポーネントでまだレガシーコンテクストを使用している場合、新しい `contextType` API に移行する必要があります。\n\n```js {5-11,19-21}\n// Before\nimport PropTypes from 'prop-types';\n\nclass Parent extends React.Component {\n  static childContextTypes = {\n    foo: PropTypes.string.isRequired,\n  };\n\n  getChildContext() {\n    return { foo: 'bar' };\n  }\n\n  render() {\n    return <Child />;\n  }\n}\n\nclass Child extends React.Component {\n  static contextTypes = {\n    foo: PropTypes.string.isRequired,\n  };\n\n  render() {\n    return <div>{this.context.foo}</div>;\n  }\n}\n```\n\n```js {2,7,9,15}\n// After\nconst FooContext = React.createContext();\n\nclass Parent extends React.Component {\n  render() {\n    return (\n      <FooContext value='bar'>\n        <Child />\n      </FooContext>\n    );\n  }\n}\n\nclass Child extends React.Component {\n  static contextType = FooContext;\n\n  render() {\n    return <div>{this.context}</div>;\n  }\n}\n```\n\n#### 削除：文字列形式の ref {/*removed-string-refs*/}\n文字列形式の ref は [2018 年 3 月 (v16.3.0)](https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html) に非推奨化されました。\n\n[いくつかの問題](https://github.com/facebook/react/issues/1373)のためコールバック形式の ref に置き換えられるまで、クラスコンポーネントは文字列形式の ref をサポートしていました。React 19 では、React をよりシンプルで理解しやすくするため、文字列形式の ref を削除します。\n\nクラスコンポーネントでまだ文字列形式の ref を使用している場合は、コールバック形式の ref に移行する必要があります。\n\n```js {4,8}\n// Before\nclass MyComponent extends React.Component {\n  componentDidMount() {\n    this.refs.input.focus();\n  }\n\n  render() {\n    return <input ref='input' />;\n  }\n}\n```\n\n```js {4,8}\n// After\nclass MyComponent extends React.Component {\n  componentDidMount() {\n    this.input.focus();\n  }\n\n  render() {\n    return <input ref={input => this.input = input} />;\n  }\n}\n```\n\n<Note>\n\ncodemod で以下のように文字列形式の ref をコールバック形式の `ref` に変換できます。\n\n```bash\nnpx codemod@latest react/19/replace-string-ref\n```\n\n</Note>\n\n#### 削除：モジュールパターンファクトリ {/*removed-module-pattern-factories*/}\nモジュールパターンファクトリは [2019 年 8 月 (v16.9.0)](https://legacy.reactjs.org/blog/2019/08/08/react-v16.9.0.html#deprecating-module-pattern-factories) に非推奨化されました。\n\nこのパターンはほとんど使用されておらず、サポートすることで React がわずかに大きく、遅くなる原因となっています。React 19 ではモジュールパターンファクトリのサポートが削除されており、通常の関数に移行する必要があります。\n\n```js\n// Before\nfunction FactoryComponent() {\n  return { render() { return <div />; } }\n}\n```\n\n```js\n// After\nfunction FactoryComponent() {\n  return <div />;\n}\n```\n\n#### 削除：`React.createFactory` {/*removed-createfactory*/}\n`createFactory` は [2020 年 2 月 (v16.13.0)](https://legacy.reactjs.org/blog/2020/02/26/react-v16.13.0.html#deprecating-createfactory) に非推奨化されました。\n\nJSX が広範にサポートされる以前は `createFactory` の使用が一般的でしたが、今ではほとんど使用されておらず、JSX に置き換えることができます。React 19 では `createFactory` が削除されており、JSX に移行する必要があります。\n\n```js\n// Before\nimport { createFactory } from 'react';\n\nconst button = createFactory('button');\n```\n\n```js\n// After\nconst button = <button />;\n```\n\n#### 削除：`react-test-renderer/shallow` {/*removed-react-test-renderer-shallow*/}\n\nReact 18 において、`react-test-renderer/shallow` を更新して [react-shallow-renderer](https://github.com/enzymejs/react-shallow-renderer) を再エクスポートするようにしていました。React 19 では、`react-test-render/shallow` が削除されており、代わりにこのパッケージを直接インストールするようになります。\n\n```bash\nnpm install react-shallow-renderer --save-dev\n```\n```diff\n- import ShallowRenderer from 'react-test-renderer/shallow';\n+ import ShallowRenderer from 'react-shallow-renderer';\n```\n\n<Note>\n\n##### シャローレンダリングの再考を {/*please-reconsider-shallow-rendering*/}\n\nシャローレンダリングは React の内部構造に依存しており、将来のアップグレードの妨げとなる可能性があります。テストを [@testing-library/react](https://testing-library.com/docs/react-testing-library/intro/) や [@testing-library/react-native](https://testing-library.com/docs/react-native-testing-library/intro) に移行することをお勧めします。\n\n</Note>\n\n### 非推奨化 React DOM API の削除 {/*removed-deprecated-react-dom-apis*/}\n\n#### 削除：`react-dom/test-utils` {/*removed-react-dom-test-utils*/}\n\n`act` を `react-dom/test-utils` から `react` パッケージに移動しました。\n\n<ConsoleBlockMulti>\n\n<ConsoleLogLine level=\"error\">\n\n`ReactDOMTestUtils.act` is deprecated in favor of `React.act`. Import `act` from `react` instead of `react-dom/test-utils`. See https://react.dev/warnings/react-dom-test-utils for more info.\n\n</ConsoleLogLine>\n\n</ConsoleBlockMulti>\n\nこの警告を修正するには、`act` を `react` からインポートするようにします。\n\n```diff\n- import {act} from 'react-dom/test-utils'\n+ import {act} from 'react';\n```\n\nその他の `test-utils` 関数はすべて削除されました。これらのユーティリティは一般的ではなく、コンポーネントや React の低レベルな内部実装の詳細へ依存しやすくなってしまうものでした。React 19 でこれらの関数を呼び出すとエラーとなります。将来のバージョンではエクスポートが削除されます。\n\n代替手段については、[警告ページ](https://react.dev/warnings/react-dom-test-utils)をご覧ください。\n\n<Note>\n\ncodemod で以下のように `ReactDOMTestUtils.act` を `React.act` に変換できます。\n\n```bash\nnpx codemod@latest react/19/replace-act-import\n```\n\n</Note>\n\n#### 削除：`ReactDOM.render` {/*removed-reactdom-render*/}\n\n`ReactDOM.render` は [2022 年 3 月 (v18.0.0)](https://react.dev/blog/2022/03/08/react-18-upgrade-guide) に非推奨化されました。React 19 では `ReactDOM.render` が削除されており、[`ReactDOM.createRoot`](https://react.dev/reference/react-dom/client/createRoot) を使用するよう移行する必要があります。\n\n```js\n// Before\nimport {render} from 'react-dom';\nrender(<App />, document.getElementById('root'));\n\n// After\nimport {createRoot} from 'react-dom/client';\nconst root = createRoot(document.getElementById('root'));\nroot.render(<App />);\n```\n\n<Note>\n\ncodemod で以下のように `ReactDOM.render` を `ReactDOMClient.createRoot` に変換できます。\n\n```bash\nnpx codemod@latest react/19/replace-reactdom-render\n```\n\n</Note>\n\n#### 削除：`ReactDOM.hydrate` {/*removed-reactdom-hydrate*/}\n\n`ReactDOM.hydrate` は [2022 年 3 月 (v18.0.0)](https://react.dev/blog/2022/03/08/react-18-upgrade-guide) に非推奨化されました。React 19 では `ReactDOM.hydrate` が削除されており、[`ReactDOM.hydrateRoot`](https://react.dev/reference/react-dom/client/hydrateRoot) を使用するよう移行する必要があります。\n\n```js\n// Before\nimport {hydrate} from 'react-dom';\nhydrate(<App />, document.getElementById('root'));\n\n// After\nimport {hydrateRoot} from 'react-dom/client';\nhydrateRoot(document.getElementById('root'), <App />);\n```\n\n<Note>\n\ncodemod で以下のように `ReactDOM.hydrate` を `ReactDOMClient.hydrateRoot` に変換できます。\n\n```bash\nnpx codemod@latest react/19/replace-reactdom-render\n```\n\n</Note>\n\n#### 削除：`unmountComponentAtNode` {/*removed-unmountcomponentatnode*/}\n\n`ReactDOM.unmountComponentAtNode` は [2022 年 3 月 (v18.0.0)](https://react.dev/blog/2022/03/08/react-18-upgrade-guide) に非推奨化されました。React 19 では、`root.unmount()` を使用するよう移行する必要があります。\n\n\n```js\n// Before\nunmountComponentAtNode(document.getElementById('root'));\n\n// After\nroot.unmount();\n```\n\n詳細については、[`createRoot`](https://react.dev/reference/react-dom/client/createRoot#root-unmount) と [`hydrateRoot`](https://react.dev/reference/react-dom/client/hydrateRoot#root-unmount) の `root.unmount()` をご覧ください。\n\n<Note>\n\ncodemod で以下のように `unmountComponentAtNode` を `root.unmount` に変換できます。\n\n```bash\nnpx codemod@latest react/19/replace-reactdom-render\n```\n\n</Note>\n\n#### 削除：`ReactDOM.findDOMNode` {/*removed-reactdom-finddomnode*/}\n\n`ReactDOM.findDOMNode` は [2018 年 10 月 (v16.6.0)](https://legacy.reactjs.org/blog/2018/10/23/react-v-16-6.html#deprecations-in-strictmode) に非推奨化されました。\n\n`findDOMNode` はレガシーな避難ハッチであり、実行速度が遅く、リファクタリングが困難で、最初の子要素しか返せず、抽象化レイヤーを破壊するといった問題があるため（詳細は[こちら](https://legacy.reactjs.org/docs/strict-mode.html#warning-about-deprecated-finddomnode-usage)）、削除されます。`ReactDOM.findDOMNode` は [DOM 用の ref](/learn/manipulating-the-dom-with-refs) で置き換えることができます。\n\n```js\n// Before\nimport {findDOMNode} from 'react-dom';\n\nfunction AutoselectingInput() {\n  useEffect(() => {\n    const input = findDOMNode(this);\n    input.select()\n  }, []);\n\n  return <input defaultValue=\"Hello\" />;\n}\n```\n\n```js\n// After\nfunction AutoselectingInput() {\n  const ref = useRef(null);\n  useEffect(() => {\n    ref.current.select();\n  }, []);\n\n  return <input ref={ref} defaultValue=\"Hello\" />\n}\n```\n\n## 新たな非推奨化 {/*new-deprecations*/}\n\n### 非推奨化：`element.ref` {/*deprecated-element-ref*/}\n\nReact 19 では [props としての `ref`](/blog/2024/04/25/react-19#ref-as-a-prop) がサポートされるため、`element.ref` を非推奨化します。代わりに `element.props.ref` を使用します。\n\n`element.ref` にアクセスすると、以下の警告が表示されます。\n\n<ConsoleBlockMulti>\n\n<ConsoleLogLine level=\"error\">\n\nAccessing element.ref is no longer supported. ref is now a regular prop. It will be removed from the JSX Element type in a future release.\n\n</ConsoleLogLine>\n\n</ConsoleBlockMulti>\n\n### 非推奨化：`react-test-renderer` {/*deprecated-react-test-renderer*/}\n\n`react-test-renderer` を非推奨化します。これはユーザが使用する環境とは異なる独自のレンダラ環境を実装しており、内部実装の詳細に対するテストを助長し、React 内部の構造に依存するものだからです。\n\nこのテストレンダラは、[React Testing Library](https://testing-library.com) のようなより実用的なテスト戦略が利用可能になる前に作成されたものです。現在では、モダンなテストライブラリの使用が推奨されます。\n\nReact 19 では、`react-test-renderer` は非推奨警告をログに記録するようになり、また並行レンダーに切り替わりました。モダンかつよりよくサポートされたテスト体験のためには、テストを [@testing-library/react](https://testing-library.com/docs/react-testing-library/intro/) または [@testing-library/react-native](https://testing-library.com/docs/react-native-testing-library/intro) に移行することを推奨します。\n\n## 注目すべき変更点 {/*notable-changes*/}\n\n### StrictMode の変更点 {/*strict-mode-improvements*/}\n\nReact 19 には、Strict Mode に関するいくつかの修正と改善が含まれています。\n\n開発中に Strict Mode で二重レンダーが発生した際、`useMemo` と `useCallback` は 1 回目のレンダー時にメモ化された結果を 2 回目のレンダーで再利用します。既に Strict Mode に対応しているコンポーネントでは、挙動に違いを感じることはないはずです。\n\nすべての Strict Mode の挙動と同様、これらの機能は開発中にコンポーネントのバグを積極的に目立たせて、本番環境にリリースされる前に修正できるよう設計されています。例えば、開発環境において Strict Mode は初回マウント時に ref コールバック関数を 2 回呼び出すことで、マウントされたコンポーネントがサスペンスフォールバックに置き換えられたときに何が起こるかをシミュレートします。\n\n### サスペンスに関する改善 {/*improvements-to-suspense*/}\n\nReact 19 では、コンポーネントがサスペンドした際には兄弟ツリー全体のレンダーを待たずに、直近のサスペンスバウンダリのフォールバックを即座にコミットするようになります。フォールバックがコミットされた後で、React は改めてツリー内でサスペンド対象となっている兄弟コンポーネントをレンダーし、遅延リクエストを開始しておけるようにします。\n\n<Diagram name=\"prerender\" height={162} width={1270} alt=\"3つのコンポーネントのツリー。親はAccordionとラベル付けされ、2つの子はPanelとラベル付けされている。両方のPanelコンポーネントは、isActiveの値がfalseになっている。\">\n\nこれまでは、コンポーネントがサスペンドされた場合、サスペンド対象の兄弟コンポーネントがレンダーされてからフォールバックがコミットされていた。\n\n</Diagram>\n\n<Diagram name=\"prewarm\" height={162} width={1270} alt=\"前の図と同じ図で、クリックにより最初の子PanelコンポーネントのisActiveがハイライトされ、isActiveがtrueに設定されている。2番目のPanelコンポーネントは引き続きfalseとなっている。\">\n\nReact 19 では、コンポーネントがサスペンドすると、フォールバックがコミットされた後で、サスペンド対象の兄弟コンポーネントがレンダーされる。\n\n</Diagram>\n\nこの変更により、サスペンスのフォールバックがより早く表示される一方で、サスペンドされたツリー内にある遅延リクエストも事前に準備されるようになります。\n\n### UMD ビルドの削除 {/*umd-builds-removed*/}\n\nUMD は過去には、ビルドステップなしで React を読み込むための便利な方法として広く使用されていました。現在では、HTML ドキュメント内でスクリプトとしてモジュールをロードするためのモダンな代替手段があります。テストとリリースプロセスの複雑性を軽減するため、React 19 からは UMD ビルドを生成しなくなります。\n\nscript タグで React 19 をロードしたい場合は、[esm.sh](https://esm.sh/) などの ESM ベースの CDN を使用することを推奨します。\n\n```html\n<script type=\"module\">\n  import React from \"https://esm.sh/react@19/?dev\"\n  import ReactDOMClient from \"https://esm.sh/react-dom@19/client?dev\"\n  ...\n</script>\n```\n\n### React の内部構造に依存するライブラリはアップグレードできない原因となる {/*libraries-depending-on-react-internals-may-block-upgrades*/}\n\n本リリースには、`SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED` のような内部構造を使用しないようにという私たちのお願いを無視しているライブラリに影響を与える可能性のある React 内部構造の変更が含まれています。これらの変更は React 19 における改善を実現するために必要なものであり、ガイドラインに従っているライブラリには影響を与えません。\n\n私たちの[バージョニングポリシー](https://react.dev/community/versioning-policy#what-counts-as-a-breaking-change)に基づき、このような更新は破壊的変更としてリストされませんし、アップグレード方法に関するドキュメントも提供されません。内部実装に依存するコードを削除することを推奨します。\n\n内部構造を使用する影響を反映するために、`SECRET_INTERNALS` という接尾辞を以下のように変更します。\n\n`_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE`\n\n将来的には、React の内部構造へのアクセスをより積極的にブロックすることで使用を抑制し、ユーザのアップグレードの妨げとならないようにする予定です。\n\n## TypeScript 関連の変更 {/*typescript-changes*/}\n\n### 非推奨化 TypeScript 型を削除 {/*removed-deprecated-typescript-types*/}\n\nReact 19 で削除された API に基づいて、TypeScript の型を整理しました。削除された型の一部は、より関連性の高いパッケージに移動され、他の型はもはや React の挙動を記述するために必要がなくなりました。\n\n<Note>\n型関連の破壊的変更に関する移行のために、[`types-react-codemod`](https://github.com/eps1lon/types-react-codemod/) を公開しました。\n\n```bash\nnpx types-react-codemod@latest preset-19 ./path-to-app\n```\n\n`element.props` への型安全ではないアクセスを多く行っている場合、以下の追加の codemod を実行できます。\n\n```bash\nnpx types-react-codemod@latest react-element-default-any-props ./path-to-your-react-ts-files\n```\n\n</Note>\n\nサポートされている書き換えのリストについては、[`types-react-codemod`](https://github.com/eps1lon/types-react-codemod/) をご覧ください。codemod が不足していると感じた場合は、[React 19 で不足している codemod のリスト](https://github.com/eps1lon/types-react-codemod/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22React+19%22+label%3Aenhancement)で追跡できます。\n\n\n### `ref` クリーンアップの必須化 {/*ref-cleanup-required*/}\n\n_この変更は、`react-19` の codemod プリセットに [`no-implicit-ref-callback-return`](https://github.com/eps1lon/types-react-codemod/#no-implicit-ref-callback-return) として含まれています。_\n\n\nref クリーンアップ関数の導入により、ref コールバックから何か他のものを返すと、TypeScript によって拒否されるようになります。通常、修正は、暗黙の return の使用をやめることです。\n\n```diff [[1, 1, \"(\"], [1, 1, \")\"], [2, 2, \"{\", 15], [2, 2, \"}\", 1]]\n- <div ref={current => (instance = current)} />\n+ <div ref={current => {instance = current}} />\n```\n\n元のコードは `HTMLDivElement` のインスタンスを返していますが、TypeScript はこれがクリーンアップ関数のつもりで書かれたものかどうかを判断できません。\n\n### `useRef` の引数の必須化 {/*useref-requires-argument*/}\n\n_この変更は、`react-19` のコードモッドプリセットに [`refobject-defaults`](https://github.com/eps1lon/types-react-codemod/#refobject-defaults) として含まれています。_\n\nTypeScript と React の動作に関する長年の不満のひとつが `useRef` でした。今後 `useRef` には引数が必須になるよう型を変更することにしました。これにより、型シグネチャが大幅に簡素化されます。これで、`createContext` と同様に動作するようになります。\n\n```ts\n// @ts-expect-error: Expected 1 argument but saw none\nuseRef();\n// Passes\nuseRef(undefined);\n// @ts-expect-error: Expected 1 argument but saw none\ncreateContext();\n// Passes\ncreateContext(undefined);\n```\n\nこれにより、すべての ref はミュータブルになります。`null` で初期化したために ref を変更できない、という問題で困ることはなくなるでしょう。\n\n```ts\nconst ref = useRef<number>(null);\n\n// Cannot assign to 'current' because it is a read-only property\nref.current = 1;\n```\n\n`MutableRef` は廃止され、`useRef` は常に単一の `RefObject` 型を返すようになりました。\n\n```ts\ninterface RefObject<T> {\n  current: T\n}\n\ndeclare function useRef<T>: RefObject<T>\n```\n\n`useRef` には便宜上のオーバーロードがまだ存在し、`useRef<T>(null)` は自動的に `RefObject<T | null>` を返すようになっています。`useRef` の引数必須化に対する移行を容易にするために、`useRef(undefined)` に対する実用的なオーバーロードが追加され、自動的に `RefObject<T | undefined>` 型を返すようになります。\n\nこの変更についてのこれまでの議論は、[[RFC] Make all refs mutable](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/64772) をご覧ください。\n\n### `ReactElement` TypeScript 型の変更 {/*changes-to-the-reactelement-typescript-type*/}\n\n_この変更は [`react-element-default-any-props`](https://github.com/eps1lon/types-react-codemod#react-element-default-any-props) codemod に含まれています。_\n\nReact 要素が `ReactElement` として型付けされている場合、その `props` のデフォルトの型は `any` ではなく `unknown` になります。`ReactElement` に型引数を渡している場合は、影響を受けません。\n\n```ts\ntype Example2 = ReactElement<{ id: string }>[\"props\"];\n//   ^? { id: string }\n```\n\nしかし、このデフォルトの型に依存していた場合、`unknown` を扱うようにする必要があります。\n\n```ts\ntype Example = ReactElement[\"props\"];\n//   ^? Before, was 'any', now 'unknown'\n```\n\n要素の props に対する型安全でないアクセスに依存している古いコードが多くある場合にのみ、これが必要です。要素の内部構造の参照は避難ハッチとしてのみ存在しており、props に安全でないアクセスを行う場合には、`any` を明示的に用いてそうであると明示するべきです。\n\n### TypeScript における JSX 名前空間 {/*the-jsx-namespace-in-typescript*/}\nこの変更は `react-19` codemod プリセットに [`scoped-jsx`](https://github.com/eps1lon/types-react-codemod#scoped-jsx) として含まれています。\n\nグローバルな `JSX` 名前空間を型から削除して `React.JSX` に置き換えるというのは長い間の要望でした。これにより、グローバル型の汚染を防ぎ、JSX を利用する異なる UI ライブラリ間での競合が防止できます。\n\nこれからは、JSX 名前空間のモジュール拡張 (module augumentation) は `declare module \"....\":` でラップする必要があります。\n\n```diff\n// global.d.ts\n+ declare module \"react\" {\n    namespace JSX {\n      interface IntrinsicElements {\n        \"my-element\": {\n          myElementProps: string;\n        };\n      }\n    }\n+ }\n```\n\n正確なモジュール指定子は、`tsconfig.json` の `compilerOptions` で指定した JSX ランタイムに依存します。\n\n- `\"jsx\": \"react-jsx\"` の場合は `react/jsx-runtime` です。\n- `\"jsx\": \"react-jsxdev\"` の場合は `react/jsx-dev-runtime` です。\n- `\"jsx\": \"react\"` および `\"jsx\": \"preserve\"` の場合は `react` です。\n\n### `useReducer` の型の改善 {/*better-usereducer-typings*/}\n\n[@mfp22](https://github.com/mfp22) の協力により、`useReducer` の型推論が改善されました。\n\nしかしこれには、`useReducer` がリデューサ全体の型を型パラメータとして受け取る代わりに、型を全く渡さない（型推論に任せる）か、もしくは state の型とアクションの型を両方渡すかのいずれかにする必要がある、という破壊的変更が必要でした。\n\n新しいベストプラクティスは、`useReducer` に型引数を渡さないことです。\n```diff\n- useReducer<React.Reducer<State, Action>>(reducer)\n+ useReducer(reducer)\n```\nこれは一部の稀なケースでは機能しないかもしれませんが、その場合は state とアクションを明示的に型付けしつつ、`Action` をタプルで渡すことで解決できます。\n```diff\n- useReducer<React.Reducer<State, Action>>(reducer)\n+ useReducer<State, [Action]>(reducer)\n```\nリデューサをインラインで定義する場合は、関数パラメータに型注釈を付けることをお勧めします。\n```diff\n- useReducer<React.Reducer<State, Action>>((state, action) => state)\n+ useReducer((state: State, action: Action) => state)\n```\nこれは、`useReducer` 呼び出しの外にリデューサを移動する場合にも行うべきでしょう。\n\n```ts\nconst reducer = (state: State, action: Action) => state;\n```\n\n## Changelog {/*changelog*/}\n\n### その他の破壊的変更 {/*other-breaking-changes*/}\n\n- **react-dom**: `src` と `href での JavaScript URL に対するエラー [#26507](https://github.com/facebook/react/pull/26507)\n- **react-dom**: `onRecoverableError` から `errorInfo.digest` を削除 [#28222](https://github.com/facebook/react/pull/28222)\n- **react-dom**: `unstable_flushControlled` を削除 [#26397](https://github.com/facebook/react/pull/26397)\n- **react-dom**: `unstable_createEventHandle` を削除 [#28271](https://github.com/facebook/react/pull/28271)\n- **react-dom**: `unstable_renderSubtreeIntoContainer` を削除 [#28271](https://github.com/facebook/react/pull/28271)\n- **react-dom**: `unstable_runWithPriority` を削除 [#28271](https://github.com/facebook/react/pull/28271)\n- **react-is**: `react-is` から非推奨のメソッドを削除 [28224](https://github.com/facebook/react/pull/28224)\n\n### その他の注目すべき変更点 {/*other-notable-changes*/}\n\n- **react**: 同期・デフォルト・連続レーンのバッチ処理 [#25700](https://github.com/facebook/react/pull/25700)\n- **react**: サスペンドされたコンポーネントの兄弟を事前レンダーしない [#26380](https://github.com/facebook/react/pull/26380)\n- **react**: レンダーフェーズでの更新によって引き起こされる無限更新ループを検出 [#26625](https://github.com/facebook/react/pull/26625)\n- **react-dom**: popstate でのトランジションを同期的に [#26025](https://github.com/facebook/react/pull/26025)\n- **react-dom**: SSR 中のレイアウトエフェクト警告を削除 [#26395](https://github.com/facebook/react/pull/26395)\n- **react-dom**: src/href に空文字列を設定しないよう警告（アンカータグを除く）[#28124](https://github.com/facebook/react/pull/28124)\n\n全変更点のリストについては、[Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md#1900-december-5-2024) を参照してください。\n\n---\n\nこの投稿のレビューと編集に協力してくれた [Andrew Clark](https://twitter.com/acdlite)、[Eli White](https://twitter.com/Eli_White)、[Jack Pope](https://github.com/jackpope)、[Jan Kassens](https://github.com/kassens)、[Josh Story](https://twitter.com/joshcstory)、[Matt Carroll](https://twitter.com/mattcarrollcode)、[Noah Lemen](https://twitter.com/noahlemen)、[Sophie Alpert](https://twitter.com/sophiebits)、そして [Sebastian Silbermann](https://twitter.com/sebsilbermann) に感謝します。\n"
  },
  {
    "path": "src/content/blog/2024/05/22/react-conf-2024-recap.md",
    "content": "---\ntitle: \"React Conf 2024 振り返り\"\nauthor: Ricky Hanlon\ndate: 2024/05/22\ndescription: 先週、ネバダ州ヘンダーソンで 700 人以上の参加者が集まり、最新の UI エンジニアリングについて議論する 2 日間のカンファレンス、React Conf 2024 を開催しました。この投稿では、イベントでの講演と発表をまとめます。\n---\n\nMay 22, 2024 by [Ricky Hanlon](https://twitter.com/rickhanlonii).\n\n---\n\n<Intro>\n\n先週、ネバダ州ヘンダーソンで 700 人以上の参加者が集まり、最新の UI エンジニアリングについて議論する 2 日間のカンファレンス、React Conf 2024 を開催しました。対面でのカンファレンスは 2019 年以来であり、コミュニティが再び一堂に会することができたことを大変に嬉しく思いました。\n\n</Intro>\n\n---\n\nReact Conf 2024 では、[React 19 RC](/blog/2024/12/05/react-19)、[React Native New Architecture Beta](https://github.com/reactwg/react-native-new-architecture/discussions/189)、および [React Compiler](/learn/react-compiler) の実験的リリースを発表しました。コミュニティもステージに立ち、[React Router v7](https://remix.run/blog/merging-remix-and-react-router)、Expo Router の [Universal Server Components](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=20765s)、[RedwoodJS](https://redwoodjs.com/blog/rsc-now-in-redwoodjs) での React Server Components など、多くの発表を行いました。\n\n[1 日目](https://www.youtube.com/watch?v=T8TZQ6k4SLE) と [2 日目](https://www.youtube.com/watch?v=0ckOUBiuxVY)の全ストリームがオンラインで視聴可能です。この投稿では、イベントでの講演と発表をまとめます。\n\n## Day 1 {/*day-1*/}\n\n_[1 日目の全ストリームはこちらから視聴できます。](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=973s)_\n\n1 日目の開始にあたり、Meta の CTO である [Andrew \"Boz\" Bosworth](https://www.threads.net/@boztank) が歓迎のメッセージを共有し、続いて Meta の React Org を管理する [Seth Webster](https://twitter.com/sethwebster) と MC の [Ashley Narcisse](https://twitter.com/_darkfadr) が紹介されました。\n\n1 日目の基調講演では [Joe Savona](https://twitter.com/en_JS) が、誰でも素晴らしいユーザ体験を構築できるようにするという React の目標とビジョンを共有しました。[Lauren Tan](https://twitter.com/potetotes) は続いて React の現状を共有し、2023 年に React が 10 億回以上ダウンロードされたことや、新しい開発者の 37% が React を使ってプログラミングを学んでいることを紹介しました。最後に、React を React たらしめているコミュニティの努力について強調しました。\n\nさらに、カンファレンス後半におけるコミュニティからの以下の講演もチェックしてください。\n\n- [Vanilla React](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=5542s) by [Ryan Florence](https://twitter.com/ryanflorence)\n- [React Rhythm & Blues](https://www.youtube.com/watch?v=0ckOUBiuxVY&t=12728s) by [Lee Robinson](https://twitter.com/leeerob)\n- [RedwoodJS, now with React Server Components](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=26815s) by [Amy Dutton](https://twitter.com/selfteachme)\n- [Introducing Universal React Server Components in Expo Router](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=20765s) by [Evan Bacon](https://twitter.com/Baconbrix)\n\n次の基調講演では、[Josh Story](https://twitter.com/joshcstory) と [Andrew Clark](https://twitter.com/acdlite) が React 19 に登場する新機能を共有し、React 19 RC が本番環境でのテストに準備が整ったことを発表しました。すべての機能については [React 19 リリースポスト](/blog/2024/12/05/react-19)をご覧ください。また、新機能について詳しく知りたい方は以下の講演をご覧ください。\n\n- [What's new in React 19](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=8880s) by [Lydia Hallie](https://twitter.com/lydiahallie)\n- [React Unpacked: A Roadmap to React 19](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=10112s) by [Sam Selikoff](https://twitter.com/samselikoff)\n- [React 19 Deep Dive: Coordinating HTML](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=24916s) by [Josh Story](https://twitter.com/joshcstory)\n- [Enhancing Forms with React Server Components](https://www.youtube.com/watch?v=0ckOUBiuxVY&t=25280s) by [Aurora Walberg Scharff](https://twitter.com/aurorascharff)\n- [React for Two Computers](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=18825s) by [Dan Abramov](https://bsky.app/profile/danabra.mov)\n- [And Now You Understand React Server Components](https://www.youtube.com/watch?v=0ckOUBiuxVY&t=11256s) by [Kent C. Dodds](https://twitter.com/kentcdodds)\n\n基調講演の締めくくりとして、[Joe Savona](https://twitter.com/en_JS)、[Sathya Gunasekaran](https://twitter.com/_gsathya)、[Mofei Zhang](https://twitter.com/zmofei) が、React Compiler が[オープンソース](https://github.com/facebook/react/pull/29061)化されたことを発表し、React Compiler の実験バージョンを共有しました。\n\nコンパイラの使用方法や動作についての詳細は、[ドキュメント](/learn/react-compiler)および以下の講演をご覧ください。\n\n- [Forget About Memo](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=12020s) by [Lauren Tan](https://twitter.com/potetotes)\n- [React Compiler Deep Dive](https://www.youtube.com/watch?v=0ckOUBiuxVY&t=9313s) by [Sathya Gunasekaran](https://twitter.com/_gsathya) and [Mofei Zhang](https://twitter.com/zmofei)\n\n1 日目の基調講演全体はこちらから視聴できます。\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/T8TZQ6k4SLE?t=973s\" />\n\n## Day 2 {/*day-2*/}\n\n_[2 日目の全体ストリームはこちらから視聴できます。](https://www.youtube.com/watch?v=0ckOUBiuxVY&t=1720s)_\n\n2 日目の開始にあたり、[Seth Webster](https://twitter.com/sethwebster) が歓迎のメッセージを共有し、続いて [Eli White](https://x.com/Eli_White) からの感謝の言葉と、Chief Vibes Officer の [Ashley Narcisse](https://twitter.com/_darkfadr) による紹介がありました。\n\n2 日目の基調講演では、[Nicola Corti](https://twitter.com/cortinico) が React Native の現状を共有し、2023 年には 7800 万回のダウンロードがあったことを報告しました。また、Meta 内で使用されている 2000 以上の画面、1 日に 20 億回以上訪問されている Facebook Marketplace の商品詳細ページ、Microsoft Windows のスタートメニューの一部、モバイルおよびデスクトップのほぼすべての Microsoft Office 製品での一部機能など、React Native を使用しているアプリを紹介しました。\n\nNicola はまた、ライブラリ、フレームワーク、プラットフォームといった、React Native をサポートするためにコミュニティが行っているすべての作業について紹介しました。詳細については、コミュニティからの以下の講演をご覧ください。\n\n- [Extending React Native beyond Mobile and Desktop Apps](https://www.youtube.com/watch?v=0ckOUBiuxVY&t=5798s) by [Chris Traganos](https://twitter.com/chris_trag) and [Anisha Malde](https://twitter.com/anisha_malde)\n- [Spatial computing with React](https://www.youtube.com/watch?v=0ckOUBiuxVY&t=22525s) by [Michał Pierzchała](https://twitter.com/thymikee)\n\n2 日目の基調講演では続けて [Riccardo Cipolleschi](https://twitter.com/cipolleschir) が、React Native の新しいアーキテクチャが現在ベータ版であり、本番環境での採用が可能であることを発表しました。彼は新しいアーキテクチャの新機能と改善点を共有し、React Native の将来のロードマップを発表しました。詳細はこちらをご覧ください。\n\n- [Cross Platform React](https://www.youtube.com/watch?v=0ckOUBiuxVY&t=26569s) by [Olga Zinoveva](https://github.com/SlyCaptainFlint) and [Naman Goel](https://twitter.com/naman34)\n\n次に基調講演で、Nicola は React Native で作成されるすべての新しいアプリに対して、Expo のようなフレームワークから始めることを推奨することを発表しました。この変更に伴い、新しい React Native ホームページと新しい Getting Started ドキュメントも発表されました。新しい Getting Started ガイドは [React Native ドキュメント](https://reactnative.dev/docs/next/environment-setup)でご覧いただけます。\n\n最後に、基調講演の締めくくりとして、[Kadi Kraman](https://twitter.com/kadikraman) が Expo の最新機能と改善点、および Expo を使用して React Native での開発を開始する方法を共有しました。\n\n2 日目の基調講演全体はこちらから視聴できます。\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/0ckOUBiuxVY?t=1720s\" />\n\n## Q&A {/*q-and-a*/}\n\n各日の終わりに React と React Native のチームは Q&A セッションを行いました。\n\n- [React Q&A](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=27518s) hosted by [Michael Chan](https://twitter.com/chantastic)\n- [React Native Q&A](https://www.youtube.com/watch?v=0ckOUBiuxVY&t=27935s) hosted by [Jamon Holmgren](https://twitter.com/jamonholmgren)\n\n## さらに… {/*and-more*/}\n\nアクセシビリティ、エラーレポート、CSS などに関する講演もありました。\n\n- [Demystifying accessibility in React apps](https://www.youtube.com/watch?v=0ckOUBiuxVY&t=20655s) by [Kateryna Porshnieva](https://twitter.com/krambertech)\n- [Pigment CSS, CSS in the server component age](https://www.youtube.com/watch?v=0ckOUBiuxVY&t=21696s) by [Olivier Tassinari](https://twitter.com/olivtassinari)\n- [Real-time React Server Components](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=24070s) by [Sunil Pai](https://twitter.com/threepointone)\n- [Let's break React Rules](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=25862s) by [Charlotte Isambert](https://twitter.com/c_isambert)\n- [Solve 100% of your errors](https://www.youtube.com/watch?v=0ckOUBiuxVY&t=19881s) by [Ryan Albrecht](https://github.com/ryan953)\n\n## 謝辞 {/*thank-you*/}\n\nReact Conf 2024 を実現していただいたすべてのスタッフ、演者、参加者の皆さまに感謝します。リストに挙げきれないほど多くの方々がいますが、特に感謝したい方々を挙げたいと思います。\n\nイベント全体の計画を手伝ってくれた [Barbara Markiewicz](https://twitter.com/barbara_markie)、[Callstack](https://www.callstack.com/) のメンバ、そして React Team Developer Advocate の [Matt Carroll](https://twitter.com/mattcarrollcode)。そしてイベントの運営を手伝ってくれた [Sunny Leggett](https://zeroslopeevents.com/about) と [Zero Slope](https://zeroslopeevents.com) の皆様に感謝します。\n\nMC および Chief Vibes Officer を務めた [Ashley Narcisse](https://twitter.com/_darkfadr)、そして Q&A セッションのホストを務めた [Michael Chan](https://twitter.com/chantastic) と [Jamon Holmgren](https://twitter.com/jamonholmgren) に感謝します。\n\n毎日私たちを迎え入れ、構成や内容についての助言をいただいた [Seth Webster](https://twitter.com/sethwebster) と [Eli White](https://x.com/Eli_White)。アフターパーティーで特別なメッセージをいただいた [Tom Occhino](https://twitter.com/tomocchino) に感謝します。\n\nトークに対する詳細なフィードバック、スライドデザインの作成、そして細部にわたるサポートをいただいた [Ricky Hanlon](https://www.youtube.com/watch?v=FxTZL2U-uKg&t=1263s) に感謝します。\n\nカンファレンスのウェブサイトを作成してくれた [Callstack](https://www.callstack.com/)、そしてカンファレンスのモバイルアプリを作成してくれた [Kadi Kraman](https://twitter.com/kadikraman) と [Expo](https://expo.dev/) チームに感謝します。\n\nイベントを実現してくださったすべてのスポンサーに感謝します：[Remix](https://remix.run/)、[Amazon](https://developer.amazon.com/apps-and-games?cmp=US_2024_05_3P_React-Conf-2024&ch=prtnr&chlast=prtnr&pub=ref&publast=ref&type=org&typelast=org)、[MUI](https://mui.com/)、[Sentry](https://sentry.io/for/react/?utm_source=sponsored-conf&utm_medium=sponsored-event&utm_campaign=frontend-fy25q2-evergreen&utm_content=logo-reactconf2024-learnmore)、[Abbott](https://www.jobs.abbott/software)、[Expo](https://expo.dev/)、[RedwoodJS](https://rwsdk.com/)、[Vercel](https://vercel.com)。\n\nビジュアル、ステージ、サウンドを担当していただいた AV チーム、そして私たちをホストしていただいた Westin Hotel に感謝します。\n\nコミュニティに知識や経験を共有していただいたすべてのスピーカに感謝します。\n\n最後に、対面およびオンラインで参加していただいたすべての方に感謝します。React が今の React でいられるのは皆さんのおかげです。React はライブラリ以上のものであり、コミュニティです。皆さんが一堂に会して共有し学び合う姿は感動的でした。\n\nまた次回お会いしましょう！\n\n"
  },
  {
    "path": "src/content/blog/2024/10/21/react-compiler-beta-release.md",
    "content": "---\ntitle: \"React Compiler Beta リリース\"\nauthor: Lauren Tan\ndate: 2024/10/21\ndescription: React Conf 2024 で、React Compiler の実験的リリースを発表しました。これは、ビルド時に自動メモ化を通じて React アプリを最適化するツールです。この投稿では、オープンソースの次のステップとコンパイラの進捗状況を共有したいと思います。\n\n---\n\nOctober 21, 2024 by [Lauren Tan](https://twitter.com/potetotes).\n\n---\n\n<Note>\n\n### React Compiler は安定版となりました！ {/*react-compiler-is-now-in-rc*/}\n\n詳細については[安定版リリース記事](/blog/2025/10/07/react-compiler-1)をご覧ください。\n\n</Note>\n\n<Intro>\n\nReact チームより以下の最新情報を共有できることを嬉しく思います。\n\n</Intro>\n\n1. React Compiler ベータ版を本日公開します。アーリーアダプタやライブラリのメンテナが試用し、フィードバックを行えるようになります。\n2. React 17+ のアプリに対して、オプションの `react-compiler-runtime` パッケージを通じた React Compiler の使用を公式にサポートします。\n3. コンパイラの段階的な採用に備えて、[React Compiler Working Group](https://github.com/reactwg/react-compiler) の公開メンバーシップを開放します。\n\n---\n\n[React Conf 2024](/blog/2024/05/22/react-conf-2024-recap) にて、React Compiler の実験的リリースを発表しました。これは、ビルド時の自動的なメモ化を通じて React アプリを最適化するツールです。[React Compiler の紹介はこちらでご覧いただけます](/learn/react-compiler)。\n\n初期のリリース以来、React コミュニティから報告された多数のバグを修正し、コンパイラに対する貴重なバグ修正や貢献[^1]をいくつか頂き、多様な JavaScript パターンに対してコンパイラをより堅牢にする作業を行い、Meta 社内でコンパイラの展開を続けてきました。\n\nこの投稿では、React Compiler の次のステップを共有したいと思います。\n\n## React Compiler Beta をすぐに試す {/*try-react-compiler-beta-today*/}\n\n[React India 2024](https://www.youtube.com/watch?v=qd5yk2gxbtg) で、React Compiler の最新情報を共有しました。本日、React Compiler と ESLint プラグインの新しいベータ版のリリースを発表できることを嬉しく思います。新しいベータ版は `@beta` タグ付きで npm に公開されています。\n\nReact Compiler ベータ版をインストールするには以下のようにします。\n\n<TerminalBlock>\nnpm install -D babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta\n</TerminalBlock>\n\nまたは、Yarn を使用している場合は以下のようにします。\n\n<TerminalBlock>\nyarn add -D babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta\n</TerminalBlock>\n\n[Sathya Gunasekaran](https://twitter.com/_gsathya) による React India での講演はこちらでご覧いただけます。\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/qd5yk2gxbtg\" />\n\n## React Compiler リンタを今日から使い始める {/*we-recommend-everyone-use-the-react-compiler-linter-today*/}\n\nReact Compiler の ESLint プラグインは、開発者が [React のルール](/reference/rules) に対する違反を事前に特定して修正するのに役立ちます。**リンタを今日から使い始めることを強くお勧めします**。リンタはコンパイラのインストールを必要としないため、コンパイラを試す準備ができていなくても独立して使用できます。\n\nリンタのみをインストールするには以下のようにします。\n\n<TerminalBlock>\nnpm install -D eslint-plugin-react-compiler@beta\n</TerminalBlock>\n\nまたは、Yarn を使用している場合は以下のようにします。\n\n<TerminalBlock>\nyarn add -D eslint-plugin-react-compiler@beta\n</TerminalBlock>\n\nインストール後、[ESLint の設定ファイルに記載を追加することでリンタを有効にできます](/learn/react-compiler/installation#eslint-integration)。リンタを使用することで、React のルールに対する違反を特定でき、コンパイラが完全にリリースされる際の導入が容易になります。\n\n## 後方互換性 {/*backwards-compatibility*/}\n\nReact Compiler は React 19 に追加されたランタイム API に依存するコードを生成しますが、React 17 および 18 でもコンパイラが動作するようにサポートを追加しました。ベータ版では、まだ React 19 に移行していない場合でも、コンパイラ設定で小さな `target` を指定し、`react-compiler-runtime` を依存ライブラリとして追加することで、React Compiler を試すことができます。[これに関するドキュメントはこちらでご覧いただけます](/reference/react-compiler/configuration#react-17-18)。\n\n## ライブラリでの React Compiler の使用 {/*using-react-compiler-in-libraries*/}\n\n初期のリリースでは、アプリケーションでコンパイラを使用する際の主要な問題を特定することに焦点を当てていました。その後素晴らしいフィードバックをいただき、コンパイラを大幅に改善してきました。コミュニティからの幅広いフィードバックを受ける準備が整い、ライブラリの作者がコンパイラを試用し、ライブラリのパフォーマンスと開発者体験を向上させることができるようになりました。\n\nReact Compiler はライブラリのコンパイルにも使用できます。React Compiler はコード変換前のオリジナルのソースコードで動作する必要があるため、アプリケーションのビルドパイプライン中で、アプリが使用するライブラリをコンパイルすることは不可能です。そのため、ライブラリのメンテナが個別にライブラリをコンパイルし、テストし、コンパイル済みのコードを npm で配布することをお勧めします。\n\nライブラリのコードが事前にコンパイルされていれば、ライブラリのユーザはコンパイラを有効にしなくても、ライブラリに適用された自動メモ化の恩恵を受けることができます。ライブラリがまだ React 19 に移行していないアプリを対象としている場合、最小の `target` を指定し、`react-compiler-runtime` を dependency として直接追加してください。ランタイムパッケージはアプリケーションのバージョンに応じて正しい API の実装を使用し、必要に応じて欠けている API をポリフィルします。\n\n[これに関するドキュメントはこちら](/reference/react-compiler/compiling-libraries)。\n\n## React Compiler Working Group を全員に開放 {/*opening-up-react-compiler-working-group-to-everyone*/}\n\n以前 React Conf にて、招待制の [React Compiler Working Group](https://github.com/reactwg/react-compiler) を発表し、コンパイラの実験的リリースに対するフィードバックを提供し、質問をし、貢献を行える場として利用してきました。\n\n本日より、React Compiler Beta のリリースに合わせ、Working Group のメンバーシップを全員に開放します。React Compiler Working Group の目標は、エコシステム全体で、既存のアプリケーションやライブラリによる React Compiler のスムーズかつ段階的な採用準備を整えることです。今後もバグ報告は [React リポジトリ](https://github.com/facebook/react)に行っていただく一方で、フィードバックや質問、アイディアの共有は、[Working Group のディスカッションフォーラム](https://github.com/reactwg/react-compiler/discussions)を利用してください。\n\nコアチームも研究結果を共有にディスカッションリポジトリを使用します。安定版リリースが近づくにつれ、重要な情報もこのフォーラムに投稿されます。\n\n## Meta における React Compiler の利用 {/*react-compiler-at-meta*/}\n\n[React Conf](/blog/2024/05/22/react-conf-2024-recap) では、Quest Store と Instagram におけるコンパイラの本番投入が成功したことを発表しました。それ以来、[Facebook](https://www.facebook.com) や [Threads](https://www.threads.net) を含む Meta 社の複数の主要ウェブアプリにおいて、React Compiler の展開を行ってきました。つまり最近これらのアプリを使用していたなら、その体験をコンパイラが支えていたということです。これらのアプリをコンパイラに乗せるために必要なコード変更はほとんどなく、10 万以上の React コンポーネントを含むモノレポで移行に成功しました。\n\nこれらのアプリ全体で、顕著なパフォーマンス向上が見られました。導入を進める中で、[以前 React Conf で発表したもの](https://youtu.be/lyEKhv8-3n0?t=3223)と同程度の成果が、引き続き確認されています。これらのアプリは長らく Meta のエンジニアや React の専門家により手作業で調整され最適化されてきたものですから、数パーセントの改善でも大きな成果と言えるでしょう。\n\nまた、React Compiler による開発者の生産性向上も期待される効果でした。これを測定するために、Meta のデータサイエンスパートナー[^2]と協力して、手動メモ化が生産性に与える影響について広範な統計分析を行いました。Meta でコンパイラを投入する以前は、React に関するプルリクエストの約 8% でしか手動メモ化が利用されておらず、そのようなプルリクエストは作成に 31-46% 長くかかっていました[^3]。この結果は、手動メモ化が認知的負担を引き起こすという我々の直感に合致するものであり、React Compiler により効率的なコード作成とレビューが実現できる可能性を示唆するものです。特に、React Compiler は、開発者が明示的にメモ化を適用してくれる一部の（私たちの場合 8%）コードだけではなく、すべてのコードがデフォルトでメモ化されることを保証してくれるのです。\n\n## 安定版に向けてのロードマップ {/*roadmap-to-stable*/}\n\n*これは最終的なロードマップではなく、変更される可能性があります*。\n\nベータ版リリースに続いて、React のルールに従うアプリやライブラリの大部分がコンパイラで問題なく動作することが証明された後で、近い将来にコンパイラのリリース候補をリリースする予定です。その後コミュニティからの最終的なフィードバックを受け付ける期間を経て、コンパイラの安定版リリースを予定しています。安定版リリースは React の新しい基盤の始まりとなるものであり、すべてのアプリとライブラリに対し、コンパイラと ESLint プラグインの使用が強く推奨されるようになります。\n\n* ✅ Experimental：React Conf 2024 でリリース。主にアーリーアダプタからのフィードバックを得るため。\n* ✅ Public Beta：本日リリース。より広いコミュニティからのフィードバックを得るため。\n* 🚧 リリース候補 (RC)：ルールに従うアプリやライブラリの大部分で React Compiler 問題なく動作するようになったら。\n* 🚧 一般提供：コミュニティからの最終的なフィードバック期間の後。\n\nこれらのリリースには、コンパイラ用の ESLint プラグインも含まれており、コンパイラによって静的に分析された診断結果を表示できます。既存の eslint-plugin-react-hooks プラグインをコンパイラ用の ESLint プラグインと統合し、1 つのプラグインだけをインストールすればよくなるよう計画しています。\n\n安定版リリース後には、コンパイラによる最適化と改善をさらに追加する予定です。これには、自動的なメモ化の継続的な改善と、全く新しい最適化の両方が含まれ、製品コードの変更は最小限または不要になる予定です。コンパイラの新しいリリースへのアップグレードは簡単に行えることを目指しています。各アップグレードはパフォーマンスを向上させ、多様な JavaScript と React パターンに対し、より良い処理が行えるようになります。\n\nこのプロセス全体を通じて、React 用の IDE 拡張機能のプロトタイプを作成する計画もあります。まだ研究の初期段階なので、将来の React Labs ブログ投稿で、より多くの知見を共有できることを期待しています。\n\n---\n\nこの投稿のレビュー・編集に協力していただいた [Sathya Gunasekaran](https://twitter.com/_gsathya)、[Joe Savona](https://twitter.com/en_JS)、[Ricky Hanlon](https://twitter.com/rickhanlonii)、[Alex Taylor](https://github.com/alexmckenley)、[Jason Bonta](https://twitter.com/someextent)、[Eli White](https://twitter.com/Eli_White) に感謝します。\n\n---\n\n[^1]: コンパイラの貢献に協力いただいた [@nikeee](https://github.com/facebook/react/pulls?q=is%3Apr+author%3Anikeee)、[@henryqdineen](https://github.com/facebook/react/pulls?q=is%3Apr+author%3Ahenryqdineen)、[@TrickyPi](https://github.com/facebook/react/pulls?q=is%3Apr+author%3ATrickyPi) に感謝します。\n\n[^2]: Meta での React コンパイラに関する本研究を主導し、この投稿をレビューしていただいた [Vaishali Garg](https://www.linkedin.com/in/vaishaligarg09) に感謝します。\n\n[^3]: 作成者の在職期間、差分の長さ・複雑さなど、潜在的な交絡因子を調整済み。"
  },
  {
    "path": "src/content/blog/2024/12/05/react-19.md",
    "content": "---\ntitle: \"React v19\"\nauthor: The React Team\ndate: 2024/12/05\ndescription: React 19 が npm で利用可能になりました！ この投稿では React 19 の新機能、およびそれらをどのように採用するかについて概説します。\n---\n\nDecember 05, 2024 by [The React Team](/community/team)\n\n---\n<Note>\n\n### React 19 は安定版になりました {/*react-19-is-now-stable*/}\n\n4 月に React 19 RC の記事として本記事が公開されて以降に、以下の内容が追加となっています。\n\n- **サスペンド中のツリーのプリウォーム**：[サスペンスに関する改善](/blog/2024/04/25/react-19-upgrade-guide#improvements-to-suspense)\n- **静的サイト用の React DOM API**: [静的サイト用の新 DOM API](#new-react-dom-static-apis)\n\n_この記事の投稿日時も、安定版リリースに合わせて変更となっています。_\n\n</Note>\n\n<Intro>\n\nnpm で React 19 が利用可能になりました！\n\n</Intro>\n\n[React 19 アップグレードガイド](/blog/2024/04/25/react-19-upgrade-guide)では、アプリを React 19 にアップグレードするためのステップバイステップガイドをお示ししました。この投稿では、React 19 の新機能と、それらをどのように採用するかについて概説します。\n\n- [React 19 の新機能](#whats-new-in-react-19)\n- [React 19 の改善点](#improvements-in-react-19)\n- [アップグレード方法](#how-to-upgrade)\n\n破壊的変更のリストについては、[アップグレードガイド](/blog/2024/04/25/react-19-upgrade-guide)を参照してください。\n\n---\n\n## React 19 の新機能 {/*whats-new-in-react-19*/}\n\n### アクション (Action) {/*actions*/}\n\nReact アプリの一般的なユースケースは、データの書き換えを行い、それに応じて state を更新するというものです。例えば、ユーザがフォームを送信してユーザ名を変更する場合、API リクエストを発行し、そのレスポンスを処理します。これまでは、送信中 (pending) 状態、エラー、楽観的更新やリクエストの順序について、手動で管理する必要がありました。\n\n例えば、送信中状態やエラーの処理は、以下のように `useState` を使って行っていました。\n\n```js\n// Before Actions\nfunction UpdateName({}) {\n  const [name, setName] = useState(\"\");\n  const [error, setError] = useState(null);\n  const [isPending, setIsPending] = useState(false);\n\n  const handleSubmit = async () => {\n    setIsPending(true);\n    const error = await updateName(name);\n    setIsPending(false);\n    if (error) {\n      setError(error);\n      return;\n    } \n    redirect(\"/path\");\n  };\n\n  return (\n    <div>\n      <input value={name} onChange={(event) => setName(event.target.value)} />\n      <button onClick={handleSubmit} disabled={isPending}>\n        Update\n      </button>\n      {error && <p>{error}</p>}\n    </div>\n  );\n}\n```\n\nReact 19 では、トランジション内で非同期関数を使用することで、送信中状態、エラー、フォーム、楽観的更新を自動的に処理するためのサポートが追加されます。\n\n例えば、`useTransition` を使用すれば以下のように送信中状態を処理できます。\n\n```js\n// Using pending state from Actions\nfunction UpdateName({}) {\n  const [name, setName] = useState(\"\");\n  const [error, setError] = useState(null);\n  const [isPending, startTransition] = useTransition();\n\n  const handleSubmit = () => {\n    startTransition(async () => {\n      const error = await updateName(name);\n      if (error) {\n        setError(error);\n        return;\n      } \n      redirect(\"/path\");\n    })\n  };\n\n  return (\n    <div>\n      <input value={name} onChange={(event) => setName(event.target.value)} />\n      <button onClick={handleSubmit} disabled={isPending}>\n        Update\n      </button>\n      {error && <p>{error}</p>}\n    </div>\n  );\n}\n```\n\nこの非同期トランジションは、開始直後に `isPending` 状態を true にセットし、非同期リクエストを実行し、すべてのトランジション終了後に `isPending` を false に切り替えます。これにより、データが変更されている最中も、現在の UI をレスポンシブかつインタラクティブに保つことができます。\n\n<Note>\n\n#### 非同期トランジションを使用する関数を規約として「アクション」と呼ぶ {/*by-convention-functions-that-use-async-transitions-are-called-actions*/}\n\nアクションはあなたの代わりに自動的にデータの送信を管理してくれます。\n\n- **送信中状態**：アクションは送信中状態を提供します。これはリクエストと共に開始され、最終的な state の更新がコミットされると自動的にリセットされます。\n- **楽観的更新**：アクションは新しい [`useOptimistic`](#new-hook-optimistic-updates) フックをサポートしており、リクエスト送信中にユーザに対し即時のフィードバックを表示することができます。\n- **エラー処理**：アクションはエラー処理を提供するため、リクエストが失敗した場合にエラーバウンダリを表示し、楽観的更新を自動的に元の状態に復元できます。\n- **フォーム**：`<form>` 要素は、props である `action` および `formAction` に関数を渡すことをサポートするようになりました。`action` に関数を渡すことでデフォルトでアクションとして扱われ、送信後にフォームを自動的にリセットします。\n\n</Note>\n\nアクションの仕組みを土台として、React 19 では楽観的更新を管理するための [`useOptimistic`](#new-hook-optimistic-updates) と、アクションの一般的なユースケースを扱うための新しいフックである [`React.useActionState`](#new-hook-useactionstate) が導入されます。`react-dom` では、フォームを自動的に管理する [`<form>` アクション](#form-actions)と、フォームにおけるアクションの一般的なユースケースをサポートする [`useFormStatus`](#new-hook-useformstatus) が追加されています。\n\nReact 19 では、上記の例は以下のように簡略化できます。\n\n```js\n// Using <form> Actions and useActionState\nfunction ChangeName({ name, setName }) {\n  const [error, submitAction, isPending] = useActionState(\n    async (previousState, formData) => {\n      const error = await updateName(formData.get(\"name\"));\n      if (error) {\n        return error;\n      }\n      redirect(\"/path\");\n      return null;\n    },\n    null,\n  );\n\n  return (\n    <form action={submitAction}>\n      <input type=\"text\" name=\"name\" />\n      <button type=\"submit\" disabled={isPending}>Update</button>\n      {error && <p>{error}</p>}\n    </form>\n  );\n}\n```\n\n以下のセクションで、React 19 の新しいアクション関連機能を詳しく説明していきます。\n\n### 新しいフック：`useActionState` {/*new-hook-useactionstate*/}\n\nアクションの一般的なユースケースを簡単に実現するため、`useActionState` という新しいフックを追加しました。\n\n```js\nconst [error, submitAction, isPending] = useActionState(\n  async (previousState, newName) => {\n    const error = await updateName(newName);\n    if (error) {\n      // You can return any result of the action.\n      // Here, we return only the error.\n      return error;\n    }\n\n    // handle success\n    return null;\n  },\n  null,\n);\n```\n\n`useActionState` は関数（「アクション」本体）を受け取り、そのアクションをラップしたものを返します。これが動作するのはアクションのコンポジションが可能だからです。ラップされたアクションが呼び出されると、`useActionState` はアクションの最終結果を `data` として、アクションの進行中状態を `pending` として返します。\n\n<Note>\n\n`React.useActionState` は以前の Canary リリースでは `ReactDOM.useFormState` と呼ばれていましたが、名前を変更し、`useFormState` を非推奨にしました。\n\n詳細は [#28491](https://github.com/facebook/react/pull/28491) を参照してください。\n\n</Note>\n\n詳細は、[`useActionState`](/reference/react/useActionState) のドキュメントをご覧ください。\n\n### React DOM：`<form>` アクション {/*form-actions*/}\n\nアクションはまた、`react-dom` における React 19 の新しい `<form>` 機能とも統合されています。`<form>`、`<input>`、`<button>` の各要素において、props である `action` および `formAction` に関数を渡すことがサポートされるようになりました。これによりアクションを用いて自動的にフォームの送信が可能です。\n\n```js [[1,1,\"actionFunction\"]]\n<form action={actionFunction}>\n```\n\n`<form>` アクションが成功すると、React は非制御コンポーネントの場合にフォームを自動的にリセットします。手動で `<form>` をリセットする必要がある場合は、新しい React DOM の API である `requestFormReset` を呼び出すことができます。\n\n詳細は、`react-dom` のドキュメントで [`<form>`](/reference/react-dom/components/form)、[`<input>`](/reference/react-dom/components/input)、`<button>` をご覧ください。\n\n### React DOM の新しいフック：`useFormStatus` {/*new-hook-useformstatus*/}\n\nデザインシステムにおいてはよく、props を深く受け渡すことなしに、自身の所属する `<form>` に関する情報にアクセスする必要があるデザインコンポーネントを書くことがあります。これはコンテクストを介して行うことも可能ですが、一般的なユースケースを簡単に行えるよう、新しいフックである `useFormStatus` を追加しました。\n\n```js [[1, 4, \"pending\"], [1, 5, \"pending\"]]\nimport {useFormStatus} from 'react-dom';\n\nfunction DesignButton() {\n  const {pending} = useFormStatus();\n  return <button type=\"submit\" disabled={pending} />\n}\n```\n\n`useFormStatus` を使うことで、まるで親フォーム自体がコンテクストプロバイダであるかのように、親 `<form>` の状態を読み取れます。\n\n詳細は、`react-dom` ドキュメントの [`useFormStatus`](/reference/react-dom/hooks/useFormStatus) をご覧ください。\n\n### 新しいフック：`useOptimistic` {/*new-hook-optimistic-updates*/}\n\nデータの書き換えを行う際に一般的な UI パターンは、非同期リクエストの進行中に、最終的にとるはずの状態を先に楽観的に表示しておくというものです。React 19 では、これを簡単に行うための新しいフックである `useOptimistic` を追加しています。\n\n```js {2,6,13,19}\nfunction ChangeName({currentName, onUpdateName}) {\n  const [optimisticName, setOptimisticName] = useOptimistic(currentName);\n\n  const submitAction = async formData => {\n    const newName = formData.get(\"name\");\n    setOptimisticName(newName);\n    const updatedName = await updateName(newName);\n    onUpdateName(updatedName);\n  };\n\n  return (\n    <form action={submitAction}>\n      <p>Your name is: {optimisticName}</p>\n      <p>\n        <label>Change Name:</label>\n        <input\n          type=\"text\"\n          name=\"name\"\n          disabled={currentName !== optimisticName}\n        />\n      </p>\n    </form>\n  );\n}\n```\n\n`useOptimistic` フックは、`updateName` リクエストが進行中になると、`optimisticName` を即座にレンダーします。更新が完了するかエラーが発生すると、React は自動的に `currentName` の値に戻します。\n\n詳細については、[`useOptimistic`](/reference/react/useOptimistic) のドキュメントをご覧ください。\n\n### 新しい API：`use` {/*new-feature-use*/}\n\nReact 19 では、レンダー中にリソースを読み取るための新しい API である `use` を導入しています。\n\nたとえば、`use` でプロミスを読み取ることができます。プロミスが解決 (resolve) するまで React はサスペンドします。\n\n```js {1,5}\nimport {use} from 'react';\n\nfunction Comments({commentsPromise}) {\n  // `use` will suspend until the promise resolves.\n  const comments = use(commentsPromise);\n  return comments.map(comment => <p key={comment.id}>{comment}</p>);\n}\n\nfunction Page({commentsPromise}) {\n  // When `use` suspends in Comments,\n  // this Suspense boundary will be shown.\n  return (\n    <Suspense fallback={<div>Loading...</div>}>\n      <Comments commentsPromise={commentsPromise} />\n    </Suspense>\n  )\n}\n```\n\n<Note>\n\n#### `use` はレンダー中に作成されたプロミスをサポートしない {/*use-does-not-support-promises-created-in-render*/}\n\nレンダー中に作成されたプロミスを `use` に渡そうとすると、React は警告を表示します。\n\n<ConsoleBlockMulti>\n\n<ConsoleLogLine level=\"error\">\n\nA component was suspended by an uncached promise. Creating promises inside a Client Component or hook is not yet supported, except via a Suspense-compatible library or framework.\n\n</ConsoleLogLine>\n\n</ConsoleBlockMulti>\n\n修正するには、プロミスをキャッシュできるサスペンス対応のライブラリまたはフレームワークで作られたプロミスを渡す必要があります。将来的には、レンダー中にプロミスをキャッシュしやすくする機能を提供する予定です。\n\n</Note>\n\nまた、`use` を使用してコンテクストを読み取ることもでき、早期リターンの後などに条件付きでコンテクストを読み取れるようになります。\n\n```js {1,11}\nimport {use} from 'react';\nimport ThemeContext from './ThemeContext'\n\nfunction Heading({children}) {\n  if (children == null) {\n    return null;\n  }\n  \n  // This would not work with useContext\n  // because of the early return.\n  const theme = use(ThemeContext);\n  return (\n    <h1 style={{color: theme.color}}>\n      {children}\n    </h1>\n  );\n}\n```\n\n`use` API は、フックと同様にレンダー中にのみ呼び出すことができます。しかしフックとは異なり、`use` は条件分岐の中でも呼び出すことが可能です。将来的には、`use` を使用してレンダー中にリソースを使用する方法をさらに拡充する予定です。\n\n詳細については、[`use`](/reference/react/use) のドキュメントをご覧ください。\n\n## 静的サイト用の新 DOM API {/*new-react-dom-static-apis*/}\n\n静的サイト生成 (static site generation) のための API を `react-dom/static` に 2 つ追加しました。\n- [`prerender`](/reference/react-dom/static/prerender)\n- [`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream)\n\nこれらは `renderToString` の改善版であり、静的な HTML 生成の際に、データの待機を行うようになっています。Node.js のストリームや Web 標準のストリームで動作するよう設計されています。例えば Web ストリームの環境では、`prerender` を使って React ツリーを静的な HTML にプリレンダーできます。\n\n```js\nimport { prerender } from 'react-dom/static';\n\nasync function handler(request) {\n  const {prelude} = await prerender(<App />, {\n    bootstrapScripts: ['/main.js']\n  });\n  return new Response(prelude, {\n    headers: { 'content-type': 'text/html' },\n  });\n}\n```\n\nプリレンダー API は、静的な HTML をストリームとして返す前に、すべてのデータの読み込みを待機します。このストリームは文字列に変換することも、レスポンスに含めて送信することも可能です。ただし、既存の [React DOM サーバレンダリング API](/reference/react-dom/server) がサポートするような、データを読み込みながらコンテンツをストリーミングする機能はサポートしていません。\n\n詳細については [React DOM 静的サイト用 API](/reference/react-dom/static) を参照してください。\n\n## React Server Components {/*react-server-components*/}\n\n### サーバコンポーネント {/*server-components*/}\n\nサーバコンポーネントは、クライアントアプリケーションや SSR サーバとは別の環境で、バンドル前にコンポーネントを事前レンダーするための新しいオプションです。React Server Components の \"server\" とはこの別の環境を指しています。サーバコンポーネントは、CI サーバでビルド時に一度だけ実行することも、ウェブサーバを使用してリクエストごとに実行することもできます。\n\nReact 19 には、Canary チャンネルにあったすべての React Server Components の機能が含まれています。これにより、サーバコンポーネントを使用するライブラリは、React 19 を peer dependency としてターゲットにすることができ、`react-server` [エクスポート条件](https://github.com/reactjs/rfcs/blob/main/text/0227-server-module-conventions.md#react-server-conditional-exports) を用いて[フルスタック React アーキテクチャ](/learn/creating-a-react-app#which-features-make-up-the-react-teams-full-stack-architecture-vision)をサポートするフレームワークで使用できます。\n\n\n<Note>\n\n#### サーバコンポーネントのサポートを追加する方法 {/*how-do-i-build-support-for-server-components*/}\n\nReact 19 の React Server Components は安定しており、マイナーバージョン間での破壊的変更はありませんが、サーバコンポーネントのバンドラやフレームワークを実装するために使用される基盤となる API は semver に従いません。React 19.x のマイナーバージョン間で変更が生じる可能性があります。\n\nReact Server Components をバンドラやフレームワークでサポートする場合は、特定の React バージョンに固定するか、Canary リリースを使用することをお勧めします。React Server Components を実装するために使用される API を安定化させるため、今後もバンドラやフレームワークと連携を続けていきます。\n\n</Note>\n\n\n詳細については、[React Server Components](/reference/rsc/server-components) のドキュメントをご覧ください。\n\n### サーバアクション {/*server-actions*/}\n\nサーバアクションにより、クライアントコンポーネントからサーバ上で実行される非同期関数を呼び出せるようになります。\n\nサーバアクションが `\"use server\"` ディレクティブを用いて定義されると、フレームワークは自動的にサーバ関数への参照を作成し、その参照をクライアントコンポーネントに渡します。クライアントでその関数が呼び出されると、React はサーバにリクエストを送り、関数を実行し、その結果を返します。\n\n<Note>\n\n#### サーバコンポーネントのためのディレクティブはない {/*there-is-no-directive-for-server-components*/}\n\nよくある誤解として、サーバコンポーネントを `\"use server\"` を用いて定義するものだと考えるというものがあります。サーバコンポーネントにはディレクティブがありません。`\"use server\"` ディレクティブは、サーバアクションのためのものです。\n\n詳細については、[ディレクティブ](/reference/rsc/directives)のドキュメントをご覧ください。\n\n</Note>\n\nサーバアクションをサーバコンポーネント内で作成し props としてクライアントコンポーネントに渡すことも、クライアントコンポーネントでインポートして使用することもできます。\n\n詳細については、[React サーバアクション](/reference/rsc/server-actions)のドキュメントをご覧ください。\n\n## React 19 の改善点 {/*improvements-in-react-19*/}\n\n### `ref` が props に {/*ref-as-a-prop*/}\n\nReact 19 から、関数コンポーネントにおいて `ref` に props としてアクセスできるようになりました。\n\n```js [[1, 1, \"ref\"], [1, 2, \"ref\", 45], [1, 6, \"ref\", 14]]\nfunction MyInput({placeholder, ref}) {\n  return <input placeholder={placeholder} ref={ref} />\n}\n\n//...\n<MyInput ref={ref} />\n```\n\n新しい関数コンポーネントでは `forwardRef` が不要になります。新しい props である `ref` を使用するようコンポーネントを自動的に更新する codemod を公開する予定です。将来のバージョンでは `forwardRef` は非推奨となり、削除されます。\n\n<Note>\n\nクラスに渡された `ref` はコンポーネントインスタンスを参照するため、props としては渡されません。\n\n</Note>\n\n### ハイドレーションエラー時の差分表示 {/*diffs-for-hydration-errors*/}\n\n`react-dom` におけるハイドレーションエラーのエラーレポートを改善しました。これまで開発時には、たとえば以下のようなエラーが複数ログとして記録されていましたが、ハイドレーション不一致に関する情報は含まれていませんでした。\n\n<ConsoleBlockMulti>\n\n<ConsoleLogLine level=\"error\">\n\nWarning: Text content did not match. Server: \"Server\" Client: \"Client\"\n{'  '}at span\n{'  '}at App\n\n</ConsoleLogLine>\n\n<ConsoleLogLine level=\"error\">\n\nWarning: An error occurred during hydration. The server HTML was replaced with client content in \\<div\\>.\n\n</ConsoleLogLine>\n\n<ConsoleLogLine level=\"error\">\n\nWarning: Text content did not match. Server: \"Server\" Client: \"Client\"\n{'  '}at span\n{'  '}at App\n\n</ConsoleLogLine>\n\n<ConsoleLogLine level=\"error\">\n\nWarning: An error occurred during hydration. The server HTML was replaced with client content in \\<div\\>.\n\n</ConsoleLogLine>\n\n<ConsoleLogLine level=\"error\">\n\nUncaught Error: Text content does not match server-rendered HTML.\n{'  '}at checkForUnmatchedText\n{'  '}...\n\n</ConsoleLogLine>\n\n</ConsoleBlockMulti>\n\n今後は、不一致部分の差分を含んだ単一のメッセージがログに記録されるようになります。\n\n\n<ConsoleBlockMulti>\n\n<ConsoleLogLine level=\"error\">\n\nUncaught Error: Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if an SSR-ed Client Component used:{'\\n'}\n\\- A server/client branch `if (typeof window !== 'undefined')`.\n\\- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n\\- Date formatting in a user's locale which doesn't match the server.\n\\- External changing data without sending a snapshot of it along with the HTML.\n\\- Invalid HTML tag nesting.{'\\n'}\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.{'\\n'}\nhttps://react.dev/link/hydration-mismatch {'\\n'}\n{'  '}\\<App\\>\n{'    '}\\<span\\>\n{'+    '}Client\n{'-    '}Server{'\\n'}\n{'  '}at throwOnHydrationMismatch\n{'  '}...\n\n</ConsoleLogLine>\n\n</ConsoleBlockMulti>\n\n### `<Context>` がプロバイダに {/*context-as-a-provider*/}\n\nReact 19 では、`<Context.Provider>` の代わりに `<Context>` をプロバイダとしてレンダーできます。\n\n\n```js {5,7}\nconst ThemeContext = createContext('');\n\nfunction App({children}) {\n  return (\n    <ThemeContext value=\"dark\">\n      {children}\n    </ThemeContext>\n  );  \n}\n```\n\n新しいコンテクストプロバイダは `<Context>` のように使用できるため、既存のプロバイダを変換するための codemod を公開する予定です。将来のバージョンでは `<Context.Provider>` を非推奨にする予定です。\n\n### ref 用のクリーンアップ関数 {/*cleanup-functions-for-refs*/}\n\n`ref` コールバックからクリーンアップ関数を返すことがサポートされるようになりました。\n\n```js {7-9}\n<input\n  ref={(ref) => {\n    // ref created\n\n    // NEW: return a cleanup function to reset\n    // the ref when element is removed from DOM.\n    return () => {\n      // ref cleanup\n    };\n  }}\n/>\n```\n\nコンポーネントがアンマウントされると、React は `ref` コールバックから返されたクリーンアップ関数を呼び出します。これは DOM への ref、クラスコンポーネントへの ref、および `useImperativeHandle` のいずれに対しても機能します。\n\n<Note>\n\nこれまで、React はコンポーネントのアンマウント時に `ref` に渡された関数を `null` を引数にして呼び出していました。`ref` がクリーンアップ関数を返す場合、React はこのステップをスキップするようになります。\n\n将来のバージョンでは、コンポーネントのアンマウント時に ref が `null` を引数に呼び出される動作は、非推奨になる予定です。\n\n</Note>\n\nref クリーンアップ関数の導入により、`ref` コールバックからそれ以外のものを返すことは TypeScript によって拒否されるようになりました。通常、これは暗黙の return をやめることで修正できます。例えば以下のようにします。\n\n```diff [[1, 1, \"(\"], [1, 1, \")\"], [2, 2, \"{\", 15], [2, 2, \"}\", 1]]\n- <div ref={current => (instance = current)} />\n+ <div ref={current => {instance = current}} />\n```\n\n元のコードは `HTMLDivElement` のインスタンスを返していますが、TypeScript はこれがクリーンアップ関数を返すつもりでミスをしたのか、クリーンアップ関数を返したくないのか判断できないのです。\n\nこのパターンは [`no-implicit-ref-callback-return`](https://github.com/eps1lon/types-react-codemod/#no-implicit-ref-callback-return) の codemod を用いて修正できます。\n\n### `useDeferredValue` の初期値 {/*use-deferred-value-initial-value*/}\n\n`useDeferredValue` に `initialValue` オプションを追加しました。\n\n```js [[1, 1, \"deferredValue\"], [1, 4, \"deferredValue\"], [2, 4, \"''\"]]\nfunction Search({deferredValue}) {\n  // On initial render the value is ''.\n  // Then a re-render is scheduled with the deferredValue.\n  const value = useDeferredValue(deferredValue, '');\n  \n  return (\n    <Results query={value} />\n  );\n}\n````\n\n<CodeStep step={2}>initialValue</CodeStep> が指定された場合、`useDeferredValue` はコンポーネントの初期レンダーの際にそれを `value` として返し、バックグラウンドで <CodeStep step={1}>deferredValue</CodeStep> を返すための再レンダーをスケジュールします。\n\n詳細については、[`useDeferredValue`](/reference/react/useDeferredValue) をご覧ください。\n\n### ドキュメントメタデータのサポート {/*support-for-metadata-tags*/}\n\nHTML では、`<title>`、`<link>`、`<meta>` などのドキュメントメタデータタグは、ドキュメントの `<head>` セクションに配置されるよう決まっています。しかし React では、アプリに必要なメタデータを決めるコンポーネントが `<head>` をレンダーしている場所から非常に遠いことや、そもそも React が `<head>` をレンダーしないことがあります。過去には、これらの要素をエフェクトで手動で挿入するか [`react-helmet`](https://github.com/nfl/react-helmet) のようなライブラリを使用しつつ、React アプリケーションをサーバでレンダーする際に特別な注意を払う必要がありました。\n\nReact 19 では、コンポーネントでドキュメントメタデータタグをレンダーするネイティブのサポートを追加しました。\n\n```js {5-8}\nfunction BlogPost({post}) {\n  return (\n    <article>\n      <h1>{post.title}</h1>\n      <title>{post.title}</title>\n      <meta name=\"author\" content=\"Josh\" />\n      <link rel=\"author\" href=\"https://twitter.com/joshcstory/\" />\n      <meta name=\"keywords\" content={post.keywords} />\n      <p>\n        Eee equals em-see-squared...\n      </p>\n    </article>\n  );\n}\n```\n\nReact がこのコンポーネントをレンダーする際、`<title>`、`<link>`、`<meta>` タグを認識し、自動的にドキュメントの `<head>` セクションに移動させます。これらのメタデータタグをネイティブにサポートすることで、クライアントのみのアプリ、ストリーミング SSR、サーバコンポーネントのいずれでも動作することを保証できます。\n\n<Note>\n\n#### メタデータライブラリはまだ必要 {/*you-may-still-want-a-metadata-library*/}\n\n単純なユースケースでは、タグとしてドキュメントメタデータをレンダーするので十分でしょうが、ライブラリは現在のルートに基づいて汎用メタデータを個別のメタデータで上書きするといった、より強力な機能を提供できます。これらの機能は、フレームワークや [`react-helmet`](https://github.com/nfl/react-helmet) のようなライブラリがメタデータタグをサポートしやすくするためのものであり、これらを置き換えるものではありません。\n\n</Note>\n\n詳細については [`<title>`](/reference/react-dom/components/title)、[`<link>`](/reference/react-dom/components/link)、[`<meta>`](/reference/react-dom/components/meta) のドキュメントをご覧ください。\n\n### スタイルシートのサポート {/*support-for-stylesheets*/}\n\nスタイルシートには優先順位に関するルールがあるため、外部リンク (`<link rel=\"stylesheet\" href=\"...\">`) される場合でもインライン (`<style>...</style>`) の場合でも、DOM 内での配置を慎重に考える必要があります。コンポーネントのコンポジション能力を保ちつつ、コンポーネント内でスタイルシート機能を構築することは困難です。そのためユーザはよく、スタイルに依存するコンポーネントから遠く離れた場所でスタイルをまとめてロードするか、この複雑さをカプセル化するスタイルライブラリを使用する必要がありました。\n\nReact 19 では、スタイルシートに対する組み込みのサポートを提供することでこの複雑さに対処し、またクライアントにおける並行レンダー機能やサーバにおけるストリーミングレンダー機能との深い統合を行います。スタイルシートの `precedence`（優先度）を React に伝えることで、スタイルシートの DOM への挿入順序を管理し、また外部スタイルシートの場合はそれがロードされてからそのスタイルルールに依存するコンテンツが表示されるよう保証します。\n\n```js {4,5,17}\nfunction ComponentOne() {\n  return (\n    <Suspense fallback=\"loading...\">\n      <link rel=\"stylesheet\" href=\"foo\" precedence=\"default\" />\n      <link rel=\"stylesheet\" href=\"bar\" precedence=\"high\" />\n      <article class=\"foo-class bar-class\">\n        {...}\n      </article>\n    </Suspense>\n  )\n}\n\nfunction ComponentTwo() {\n  return (\n    <div>\n      <p>{...}</p>\n      <link rel=\"stylesheet\" href=\"baz\" precedence=\"default\" />  <-- will be inserted between foo & bar\n    </div>\n  )\n}\n```\n\nサーバサイドレンダリングの場合、React は `<head>` にスタイルシートを含めることで、それをブラウザがロードするまで描画が起きないことを保証します。ストリーミングを既に開始した後でスタイルシートが見つかった場合でも、React はサスペンスバウンダリ内でスタイルシートに依存するコンテンツが表示される前に、クライアントの `<head>` にそのスタイルシートが挿入されることを保証します。\n\nクライアントサイドレンダリングの場合、React は新しくレンダーされたスタイルシートが読み込まれるのを待ってからレンダーをコミットします。そのコンポーネントをアプリケーション内の複数の場所からレンダーする場合でも、React はドキュメントにスタイルシートを一度だけ挿入します。\n\n```js {5}\nfunction App() {\n  return <>\n    <ComponentOne />\n    ...\n    <ComponentOne /> // won't lead to a duplicate stylesheet link in the DOM\n  </>\n}\n```\n\nスタイルシートを手動で読み込むことに慣れているユーザにとっては、これはスタイルシートをそれに依存するコンポーネントの隣に配置することで局所的な理解をしやすくできる機会となり、また実際に使用するスタイルシートのみが読み込まれることを保証しやすくなるでしょう。\n\nスタイル関連ライブラリやバンドラ統合のスタイル機能もこの新しい機能を採用できるため、自分でスタイルシートを直接レンダーしない場合でも、ツールがこの機能を使用するようにアップグレードされるにつれ、やはりメリットを享受できるようになるでしょう。\n\n詳細については、[`<link>`](/reference/react-dom/components/link) と [`<style>`](/reference/react-dom/components/style) のドキュメントを参照してください。\n\n### 非同期スクリプトのサポート {/*support-for-async-scripts*/}\n\nHTML では通常のスクリプト (`<script src=\"...\">`) と遅延スクリプト (`<script defer=\"\" src=\"...\">`) はドキュメントの順序で読み込まれるため、コンポーネントツリーの深い部分でこれらのスクリプトをレンダーすることは困難です。しかし、非同期スクリプト (`<script async=\"\" src=\"...\">`) は任意の順序で読み込まれます。\n\nReact 19 では、非同期スクリプトのサポートを拡充し、ツリーのどこにあっても、スクリプトを実際に使用するコンポーネント内でそれをレンダーできるようにします。これにより、スクリプトインスタンスの移動や重複解消処理の管理が不要になります。\n\n```js {4,15}\nfunction MyComponent() {\n  return (\n    <div>\n      <script async={true} src=\"...\" />\n      Hello World\n    </div>\n  )\n}\n\nfunction App() {\n  <html>\n    <body>\n      <MyComponent>\n      ...\n      <MyComponent> // won't lead to duplicate script in the DOM\n    </body>\n  </html>\n}\n```\n\nすべてのレンダー環境で非同期スクリプトの重複解消処理が行われます。複数の異なるコンポーネントが同じスクリプトをレンダーしている場合でも、React はそれを一度だけ読み込み、実行します。\n\nサーバサイドレンダリングでは、非同期スクリプトは `<head>` に挿入され、描画をブロックするスタイルシート・フォント・画像プリロードなどのより重要なリソースよりも低優先度で処理されます。\n\n詳細については、[`<script>`](/reference/react-dom/components/script) のドキュメントを参照してください。\n\n### リソースのプリロードのサポート {/*support-for-preloading-resources*/}\n\nドキュメントの初期読み込み時やクライアントでの画面更新時に、今後必要になりそうなリソースについてブラウザに可能な限り早く知らせておくことは、ページのパフォーマンスに劇的な影響を与えることがあります。\n\nReact 19 には、リソースの読み込みやプリロードのための新しい API が多数含まれています。非効率的なリソース読み込みによって阻害されることのない素晴らしいユーザ体験を、できるだけ容易に構築できるようになっています。\n\n```js\nimport { prefetchDNS, preconnect, preload, preinit } from 'react-dom'\nfunction MyComponent() {\n  preinit('https://.../path/to/some/script.js', {as: 'script' }) // loads and executes this script eagerly\n  preload('https://.../path/to/font.woff', { as: 'font' }) // preloads this font\n  preload('https://.../path/to/stylesheet.css', { as: 'style' }) // preloads this stylesheet\n  prefetchDNS('https://...') // when you may not actually request anything from this host\n  preconnect('https://...') // when you will request something but aren't sure what\n}\n```\n```html\n<!-- the above would result in the following DOM/HTML -->\n<html>\n  <head>\n    <!-- links/scripts are prioritized by their utility to early loading, not call order -->\n    <link rel=\"prefetch-dns\" href=\"https://...\">\n    <link rel=\"preconnect\" href=\"https://...\">\n    <link rel=\"preload\" as=\"font\" href=\"https://.../path/to/font.woff\">\n    <link rel=\"preload\" as=\"style\" href=\"https://.../path/to/stylesheet.css\">\n    <script async=\"\" src=\"https://.../path/to/some/script.js\"></script>\n  </head>\n  <body>\n    ...\n  </body>\n</html>\n```\n\nこれらの API は、フォントなどのリソースをスタイルシート外に移動させて早期に発見できるようにすることで、ページの初期ロードを最適化するために利用できます。また、予想されるナビゲーションに応じて必要となるリソースのリストを先読みし、クリックあるいはホバー時に早期にプリロードを始めることで、クライアントでの更新を高速化することもできるでしょう。\n\n詳細については、[リソースプリロード API](/reference/react-dom#resource-preloading-apis) を参照してください。\n\n### サードパーティのスクリプトおよび拡張機能との互換性改善 {/*compatibility-with-third-party-scripts-and-extensions*/}\n\nサードパーティのスクリプトやブラウザ拡張機能の存在を考慮し、ハイドレーションの改善を行いました。\n\nハイドレーション時に、クライアントでレンダーされた要素がサーバから届いた HTML に書かれた要素と一致しない場合、React は強制的にクライアントで再レンダーを行って内容を修正します。これまで、サードパーティのスクリプトやブラウザ拡張機能によって何らかの要素が挿入された場合、不一致エラーとクライアントレンダーを引き起こしていました。\n\nReact 19 では、`<head>` および `<body>` 内にある予期しないタグをスキップし、不一致エラーが回避されるようになります。それ以外のハイドレーションの不一致によりドキュメント全体を再レンダーする必要がある場合でも、サードパーティのスクリプトやブラウザ拡張機能によって挿入された既存のスタイルシートはそのままにします。\n\n### エラー報告の改善 {/*error-handling*/}\n\nReact 19 ではエラー処理を改善して重複エラーを削減しており、またキャッチされたエラーとキャッチされなかったエラーのそれぞれに対する処理オプションを提供します。たとえば、レンダー中にエラーが発生しエラーバウンダリでキャッチされた場合、これまで React ではそのエラーを 2 回スロー（1 回は元のエラー、もう 1 回は自動回復に失敗した後）し、その後エラーが発生した場所について `console.error` に記録していました。\n\nこれにより、キャッチされるエラーごとに以下のような 3 つのエラーが発生していました。\n\n<ConsoleBlockMulti>\n\n<ConsoleLogLine level=\"error\">\n\nUncaught Error: hit\n{'  '}at Throws\n{'  '}at renderWithHooks\n{'  '}...\n\n</ConsoleLogLine>\n\n<ConsoleLogLine level=\"error\">\n\nUncaught Error: hit<span className=\"ms-2 text-gray-30\">{'    <--'} Duplicate</span>\n{'  '}at Throws\n{'  '}at renderWithHooks\n{'  '}...\n\n</ConsoleLogLine>\n\n<ConsoleLogLine level=\"error\">\n\nThe above error occurred in the Throws component:\n{'  '}at Throws\n{'  '}at ErrorBoundary\n{'  '}at App{'\\n'}\nReact will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.\n\n</ConsoleLogLine>\n\n</ConsoleBlockMulti>\n\nReact 19 では、すべてのエラー情報を含む単一のエラーをログに記録します。\n\n<ConsoleBlockMulti>\n\n<ConsoleLogLine level=\"error\">\n\nError: hit\n{'  '}at Throws\n{'  '}at renderWithHooks\n{'  '}...{'\\n'}\nThe above error occurred in the Throws component:\n{'  '}at Throws\n{'  '}at ErrorBoundary\n{'  '}at App{'\\n'}\nReact will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.\n{'  '}at ErrorBoundary\n{'  '}at App\n\n</ConsoleLogLine>\n\n</ConsoleBlockMulti>\n\nさらに、`onRecoverableError` を補完するために 2 つの新しいルートオプションを追加しました。\n\n- `onCaughtError`：エラーバウンダリで React がエラーをキャッチしたときに呼び出されます。\n- `onUncaughtError`：エラーがスローされたがエラーバウンダリでキャッチされなかった場合に呼び出されます。\n- `onRecoverableError`：エラーがスローされ、自動的に回復されたときに呼び出されます。\n\n詳細と例については、[`createRoot`](/reference/react-dom/client/createRoot) および [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) のドキュメントを参照してください。\n\n### カスタム要素のサポート {/*support-for-custom-elements*/}\n\nReact 19 はカスタム要素を完全にサポートし、[Custom Elements Everywhere](https://custom-elements-everywhere.com/) のすべてのテストに合格しました。\n\n過去のバージョンの React では、認識されない props を要素のプロパティではなく属性として扱っていたため、カスタム要素を使用することが困難でした。React 19 では、以下の戦略に従って、クライアントと SSR の両方で機能するプロパティがサポートされるようになります。\n\n- **サーバサイドレンダリング**：カスタム要素に渡される props は、その型が `string`、`number` のようなプリミティブである場合や値が `true` である場合、属性としてレンダーされる。`object`、`symbol`、`function` のような非プリミティブ値である場合や値が `false` である場合、その props は無視される。\n- **クライアントサイドレンダリング**：カスタム要素のインスタンスのプロパティに一致する props はプロパティとして割り当てられ、それ以外の場合は属性として割り当てられる。\n\nReact におけるカスタム要素のサポートに関し、設計と実装を推進した [Joey Arhar](https://github.com/josepharhar) に感謝します。\n\n\n#### アップグレード方法 {/*how-to-upgrade*/}\nアップグレードに関するステップバイステップのガイドや、重要な変更点の完全なリストについては、[React 19 アップグレードガイド](/blog/2024/04/25/react-19-upgrade-guide)を参照してください。\n\n_Note: this post was originally published 04/25/2024 and has been updated to 12/05/2024 with the stable release._\n"
  },
  {
    "path": "src/content/blog/2025/02/14/sunsetting-create-react-app.md",
    "content": "---\ntitle: \"Create React App の非推奨化\"\nauthor: Matt Carroll and Ricky Hanlon\ndate: 2025/02/14\ndescription: 本日、新規アプリに対して Create React App を非推奨とし、既存のアプリにはフレームワークへの移行、または Vite、Parcel、RSBuild などのビルドツールへの移行を推奨します。また、フレームワークがプロジェクトに適していない場合や独自のフレームワークを構築したい場合、あるいは React がどのように動作するかを学ぶためにゼロから React アプリを構築したい場合のためのドキュメントも提供します。\n---\n\nFebruary 14, 2025 by [Matt Carroll](https://twitter.com/mattcarrollcode) and [Ricky Hanlon](https://bsky.app/profile/ricky.fm)\n\n---\n\n<Intro>\n\n本日、新しいアプリに対して [Create React App](https://create-react-app.dev/) を非推奨とします。既存のアプリに対しては、[フレームワーク](#how-to-migrate-to-a-framework)への移行、または Vite、Parcel、RSBuild などの[ビルドツールへの移行](#how-to-migrate-to-a-build-tool)を推奨します。\n\nまた、フレームワークがプロジェクトに適していない場合や、独自のフレームワークを構築したい場合、あるいは[ゼロから React アプリを構築する](/learn/build-a-react-app-from-scratch)ことで React がどのように動作するかを学びたい場合のためのドキュメントも提供します。\n\n</Intro>\n\n-----\n\n2016 年に Create React App をリリースした当時は、新しい React アプリを構築するための明確な方法が存在しませんでした。\n\nReact アプリを作成するには、多くのツールをインストールしてそれらを自分で組み合わせ、JSX、リンタ、ホットリロードなどの基本機能をサポートしていく必要がありました。これを正しく行うのは非常に難しかったため、[コミュニティは](https://github.com/react-boilerplate/react-boilerplate) [セットアップを](https://github.com/kriasoft/react-starter-kit) [共通化するために](https://github.com/petehunt/react-boilerplate) [様々なボイラープレートを](https://github.com/gaearon/react-hot-boilerplate) [作成しました](https://github.com/erikras/react-redux-universal-hot-example)。しかし、ボイラープレートは更新が難しく、分断化が進むにつれ React が新機能をリリースするのは困難となっていきました。\n\nCreate React App は、いくつかのツールを単一の推奨セットアップにまとめることでこれらの問題を解決しました。これにより、アプリが新しいツールの機能を使うためのアップグレードが簡単になり、React チームは大きめのツールの変更（Fast Refresh のサポートやフックのリントルールなど）を最大限広範なユーザに展開できるようになりました。\n\nこのモデルは非常に人気があるため、今日ではこのように動作するツール群が一大勢力として存在しています。\n\n## Create React App の非推奨化 {/*deprecating-create-react-app*/}\n\nCreate React App は簡単に始められるものの、[いくつかの制限](#limitations-of-build-tools)があり、本番環境用の高性能なアプリを構築することが困難となっています。原理的には、これらの問題は Create React App を[フレームワーク](#why-we-recommend-frameworks)へと発展させることで解決可能でしょう。\n\nしかし、現在 Create React App にはアクティブなメンテナがいない一方で、これらの問題をすでに解決している多くの既存のフレームワークが存在します。このため、Create React App を非推奨とすることに決定しました。\n\n本日より、新しいアプリをインストールすると、非推奨の警告が表示されます。\n\n<ConsoleBlockMulti>\n<ConsoleLogLine level=\"error\">\n\ncreate-react-app is deprecated.\n{'\\n\\n'}\nYou can find a list of up-to-date React frameworks on react.dev\nFor more info see: react.dev/link/cra\n{'\\n\\n'}\nThis error message will only be shown once per install.\n\n</ConsoleLogLine>\n</ConsoleBlockMulti>\n\nまた、Create React App の[ウェブサイト](https://create-react-app.dev/)と GitHub [リポジトリ](https://github.com/facebook/create-react-app)にも非推奨の通知を追加しました。Create React App はメンテナンスモードで動作を続けます。React 19 で動作する新しいバージョンの Create React App を公開しました。\n\n## フレームワークへの移行方法 {/*how-to-migrate-to-a-framework*/}\nフレームワークを使用して[新しい React アプリを作成する](/learn/creating-a-react-app)ことをお勧めします。私たちが推奨するすべてのフレームワークは、クライアントサイドレンダー ([CSR](https://developer.mozilla.org/en-US/docs/Glossary/CSR)) とシングルページアプリ ([SPA](https://developer.mozilla.org/en-US/docs/Glossary/SPA)) をサポートしており、サーバなしで CDN や静的ホスティングサービスにデプロイ可能です。\n\n既存のアプリをクライアント専用の SPA に移行したい場合は以下のガイドが役立ちます。\n\n* [Next.js の Create React App 移行ガイド](https://nextjs.org/docs/app/building-your-application/upgrading/from-create-react-app)\n* [React Router のフレームワーク採用ガイド](https://reactrouter.com/upgrading/component-routes)\n* [Expo webpack から Expo Router への移行ガイド](https://docs.expo.dev/router/migrate/from-expo-webpack/)\n\n## ビルドツールへの移行方法 {/*how-to-migrate-to-a-build-tool*/}\n\nアプリに特異な制約がある場合や、独自のフレームワークを構築することでこれらの問題を解決したい場合、またはゼロから React がどのように動作するかを学びたい場合は、Vite、Parcel、Rsbuild を使用して React を用いたカスタムセットアップを作成できます。\n\n既存のアプリをこのようなビルドツールに移行したい場合は以下のガイドが役立ちます。\n\n* [Vite の Create React App 移行ガイド](https://www.robinwieruch.de/vite-create-react-app/)\n* [Parcel の Create React App 移行ガイド](https://parceljs.org/migration/cra/)\n* [Rsbuild の Create React App 移行ガイド](https://rsbuild.dev/guide/migration/cra)\n\nVite、Parcel、Rsbuild を使用して始めるために、[ゼロから React アプリを構築する](/learn/build-a-react-app-from-scratch)ための新しいドキュメントを追加しました。\n\n<DeepDive>\n\n#### フレームワークは必要？ {/*do-i-need-a-framework*/}\n\nほとんどのアプリはフレームワークの恩恵を受けますが、ゼロから React アプリを構築する正当なケースもあります。目安として、アプリにルーティングが必要なら、おそらくフレームワークの恩恵を受ける可能性が高いでしょう。\n\nSvelte には Sveltekit、Vue には Nuxt、Solid には SolidStart があるように、React は、ルーティングをデータフェッチやコード分割などの機能に完全に統合した[フレームワークを使用することを推奨しています](#why-we-recommend-frameworks)。これにより、複雑な設定を自分で書いて実質的に自分用のフレームワークを構築してしまうような必要がなくなります。\n\nしかし Vite、Parcel、Rsbuild などのビルドツールを使用して[ゼロから React アプリを構築する](/learn/build-a-react-app-from-scratch)ことも可能です。\n\n</DeepDive>\n\n以下で、[ビルドツールの制約](#limitations-of-build-tools)と[フレームワークを推奨する理由](#why-we-recommend-frameworks)についてさらに述べていきます。\n\n## ビルドツールの制約とは {/*limitations-of-build-tools*/}\n\nCreate React App やそれに類するビルドツールを使えば、React アプリの構築を簡単に始められます。`npx create-react-app my-app` を実行すると、開発用サーバ、リンタ、本番用ビルド機能が完全に設定された React アプリが手に入ります。\n\nたとえば、内部向けの管理ツールを構築している場合、さっそく以下のようなランディングページから始めることができるでしょう。\n\n```js\nexport default function App() {\n  return (\n    <div>\n      <h1>Welcome to the Admin Tool!</h1>\n    </div>\n  )\n}\n```\n\nこれにより、JSX、デフォルトのリントルール、開発環境と本番環境の両方で実行するためのバンドラといった機能がある状態で、すぐに React でコーディングを始めることができます。しかし、このセットアップには、実際の本番用アプリを構築するために必要なツールが欠けています。\n\nほとんどの本番用アプリでは、ルーティング、データフェッチ、コード分割などの問題に対する解決策も必要なのです。\n\n### ルーティング {/*routing*/}\n\nCreate React App には、特定のルーティングソリューションは含まれていません。始めたばかりの場合、ページを切り替えるために `useState` を使用する、というのがひとつの選択肢です。しかし、こうするとアプリ内リンクの共有が不可能（すべてのリンクが同じページに移動してしまう）になりますし、時間が経つにつれてアプリを組み立てるのが難しくなります。\n\n```js\nimport {useState} from 'react';\n\nimport Home from './Home';\nimport Dashboard from './Dashboard';\n\nexport default function App() {\n  // ❌ Routing in state does not create URLs\n  const [route, setRoute] = useState('home');\n  return (\n    <div>\n      {route === 'home' && <Home />}\n      {route === 'dashboard' && <Dashboard />}\n    </div>\n  )\n}\n```\n\nこのため、Create React App を使用するほとんどのアプリは、[React Router](https://reactrouter.com/) や [Tanstack Router](https://tanstack.com/router/latest) などのルーティングライブラリを使用してルーティングを追加します。ルーティングライブラリを使用することで、アプリに追加のルート (route) を追加でき、アプリの組み立て方に対する指針が生まれ、各ページへのリンクを共有できるようになります。たとえば、React Router では以下のようにルートを定義できます。\n\n```js\nimport {RouterProvider, createBrowserRouter} from 'react-router';\n\nimport Home from './Home';\nimport Dashboard from './Dashboard';\n\n// ✅ Each route has it's own URL\nconst router = createBrowserRouter([\n  {path: '/', element: <Home />},\n  {path: '/dashboard', element: <Dashboard />}\n]);\n\nexport default function App() {\n  return (\n    <RouterProvider value={router} />\n  )\n}\n```\n\nこれにより、`/dashboard` というリンクを共有すれば、アプリはダッシュボードページに移動するようになります。ルーティングライブラリには、ルートのネスト、ルートガード、ルート間画面遷移効果 (route transition) などの追加機能もあり、これらはルーティングライブラリなしには実装困難です。\n\nルーティングライブラリを使うとアプリは複雑になりますが、その代わりに、それなしでは作れないような機能も使えるようになる、というトレードオフがあるのです。\n\n### データフェッチ {/*data-fetching*/}\n\nCreate React App でのもうひとつの一般的な問題はデータフェッチです。Create React App には、特定のデータフェッチソリューションは含まれていませんが、入門したばかりの方は、データをロードするためにエフェクト内で `fetch` を使う方法を選びがちです。\n\nしかし、これを行うと、データがコンポーネントのレンダー後にフェッチされるため、ネットワークウォーターフォールが発生します。ネットワークウォーターフォールは、コードとデータを並行でダウンロードするのではなく、アプリのレンダー後にデータをフェッチすることで発生します。\n\n```js\nexport default function Dashboard() {\n  const [data, setData] = useState(null);\n\n  // ❌ Fetching data in a component causes network waterfalls\n  useEffect(() => {\n    fetch('/api/data')\n      .then(response => response.json())\n      .then(data => setData(data));\n  }, []);\n\n  return (\n    <div>\n      {data.map(item => <div key={item.id}>{item.name}</div>)}\n    </div>\n  )\n}\n```\n\nエフェクト内でフェッチするということは、ユーザがコンテンツを見るまでの待ち時間が長くなるということです。データはもっと早く取得できていたかもしれないのです。これを解決するために、[TanStack Query](https://tanstack.com/query/)、[SWR](https://swr.vercel.app/)、[Apollo](https://www.apollographql.com/docs/react)、[Relay](https://relay.dev/) などのデータフェッチライブラリを使用すると、コンポーネントのレンダーより前にデータをプリフェッチできるオプションが使用可能です。\n\nこれらのライブラリは、ルートのレベルで依存データを指定するための、「ルーティングローダ」パターンと統合することで最適に機能します。これによりルータがデータフェッチを最適化可能です。\n\n```js\nexport async function loader() {\n  const response = await fetch(`/api/data`);\n  const data = await response.json();\n  return data;\n}\n\n// ✅ Fetching data in parallel while the code is downloading\nexport default function Dashboard({loaderData}) {\n  return (\n    <div>\n      {loaderData.map(item => <div key={item.id}>{item.name}</div>)}\n    </div>\n  )\n}\n```\n\n初回ロード時に、ルータはルートがレンダーされる直前にデータをフェッチできます。ユーザがアプリ内を移動する際、ルータはデータとルートのフェッチを並列化して同時に行えます。これにより、画面上のコンテンツを見るまでの時間が短縮され、ユーザエクスペリエンスが向上します。\n\nただし、このためにはアプリ内のローダを正しく設定する必要があり、パフォーマンスのために複雑さを受け入れていることになってしまいます。\n\n### コード分割 {/*code-splitting*/}\n\nCreate React App における次の一般的な問題は[コード分割](https://www.patterns.dev/vanilla/bundle-splitting)です。Create React App には、特定のコード分割ソリューションは含まれていません。始めたばかりの場合、コード分割について考えることは全くないかもしれません。\n\nその場合、アプリは単一のバンドルとしてホストされます。\n\n```txt\n- bundle.js    75kb\n```\n\nしかし、理想的なパフォーマンスのためには、コードを別々のバンドルに「分割」し、ユーザが必要とするものだけをダウンロードするようにする必要があります。これにより、ユーザは現在いるページを表示するために必要なコードだけをダウンロードするようになり、アプリをロードするまでの待ち時間が短縮されます。\n\n```txt\n- core.js      25kb\n- home.js      25kb\n- dashboard.js 25kb\n```\n\nコード分割を行う方法のひとつは、`React.lazy` を使用することです。しかし、この方法ではレンダーされる段階になって初めてコードが取得されるため、ネットワークウォーターフォールが発生する可能性があります。より良い解決策は、ルータにある、コードのダウンロード中に関連コード群を並行的にフェッチするための機能を使用することです。例えば、React Router は `lazy` オプションを提供しており、これを使ってルートをコード分割対象として指定し、読み込みタイミングを最適化できます。\n\n```js\nimport Home from './Home';\nimport Dashboard from './Dashboard';\n\n// ✅ Routes are downloaded before rendering\nconst router = createBrowserRouter([\n  {path: '/', lazy: () => import('./Home')},\n  {path: '/dashboard', lazy: () => import('Dashboard')}\n]);\n```\n\nコード分割を正しく行うことは難しく、ユーザが必要以上のコードをダウンロードしてしまうというミスがよく発生します。コード分割が最大限活躍するのは、ルータやデータロード処理と結合することで、キャッシュを最大限活用し、フェッチを並行して行い、[\"import on interaction\"](https://www.patterns.dev/vanilla/import-on-interaction) パターンをサポートした場合です。\n\n### ほかにも… {/*and-more*/}\n\n以上は、Create React App における制約のほんの一部に過ぎません。\n\nルーティング、データフェッチ、コード分割を組み込んだ後も、今度は送信中状態、ナビゲーションの中断、ユーザへのエラーメッセージ、データの再検証を考慮する必要があります。開発者が解決しなければならない問題領域は大量に存在するのです。\n\n<div style={{display: 'flex', width: '100%', justifyContent: 'space-around'}}>\n  <ul>\n    <li>アクセシビリティ</li>\n    <li>アセットのロード</li>\n    <li>認証</li>\n    <li>キャッシング</li>\n  </ul>\n  <ul>\n    <li>エラー処理</li>\n    <li>データの書き換え</li>\n    <li>ナビゲーション</li>\n    <li>楽観的更新</li>\n  </ul>\n  <ul>\n    <li>プログレッシブエンハンスメント</li>\n    <li>サーバサイドレンダー</li>\n    <li>静的サイト生成</li>\n    <li>ストリーミング</li>\n  </ul>\n</div>\n\nこれらすべてが最適な[ロードシーケンス](https://www.patterns.dev/vanilla/loading-sequence)を作成するために連携します。\n\nCreate React App でこれらの問題を個別に解決することは困難です。各問題は他の問題と相互に関連しており、開発者が慣れていない問題領域での深い専門知識を必要とすることがあります。これらの問題を解決しようとすると、開発者は Create React App の上に独自の特注ソリューションを構築していく羽目になりますが、本来 Create React App はそういうことをしないで済むためのもののはずでした。\n\n## 我々がフレームワークを推奨する理由 {/*why-we-recommend-frameworks*/}\n\nCreate React App、Vite、Parcel などのビルドツールでこれらの要素をすべて自分で解決することも可能ですが、うまくやることは困難です。Create React App 自体がいくつかのビルドツールを統合したときのように、これらの機能をすべて統合してユーザに最高のエクスペリエンスを提供するためのツールが必要です。\n\nビルドツール、レンダー、ルーティング、データフェッチ、コード分割のすべてを統合する、というツールのカテゴリは「フレームワーク」として知られています。React 自体をフレームワークと呼ぶことを好む場合は、「メタフレームワーク」と呼ぶこともできるでしょう。\n\nビルドツールにも多少の使い方に関する規約があるのと同様、フレームワークにもより良いユーザ体験を提供するためにどのようにアプリを構造化するのかについて、規約が存在します。これが、我々が [Next.js](https://nextjs.org/)、[React Router](https://reactrouter.com/)、[Expo](https://expo.dev/) などのフレームワークを新しいプロジェクトに推奨し始めた理由です。\n\nフレームワークは Create React App と同様の始めやすさを提供しつつ、実際のプロダクションアプリで開発者がいずれにせよ解決する必要のある問題に対するソリューションも提供するのです。\n\n<DeepDive>\n\n#### サーバレンダーはオプションです {/*server-rendering-is-optional*/}\n\n私たちが推奨するフレームワークはすべて、[クライアントサイドレンダー (CSR)](https://developer.mozilla.org/en-US/docs/Glossary/CSR) によるアプリを作成するためのオプションを提供しています。\n\nあるページにとって CSR が正しい選択となることはありますが、多くの場合はそうではありません。アプリのほとんどがクライアントサイドという場合でも、[静的サイト生成 (SSG)](https://developer.mozilla.org/en-US/docs/Glossary/SSG) や [サーバサイドレンダリング (SSR)](https://developer.mozilla.org/en-US/docs/Glossary/SSR) などのサーバレンダー機能の恩恵を受けることができる個別ページはしばしば存在します。たとえば利用規約ページやドキュメントなどです。\n\nサーバレンダーはクライアントに送信される JavaScript を全体的に削減し、完全な HTML ドキュメントを送信することで、[First Contentful Paint (FCP)](https://web.dev/articles/fcp) を高速化し、[Total Blocking Time (TBD)](https://web.dev/articles/tbt) を削減し、またそれにより [Interaction to Next Paint (INP)](https://web.dev/articles/inp) を低下させることができます。Chrome チームが開発者に向けて、パフォーマンス最大化のために、完全にクライアントサイドに寄せた戦略ではなく静的ないしサーバサイドレンダリングを[検討するよう促している](https://web.dev/articles/rendering-on-the-web)のには、このような理由があるのです。\n\nサーバを使用することにはトレードオフがあり、すべてのページにとって常に最良の選択肢というわけではありません。サーバでページを生成することにより追加のコストと生成時間がかかるため、[Time to First Byte (TTFB)](https://web.dev/articles/ttfb) を増加させる可能性があります。最高のパフォーマンスのアプリでは、それぞれの戦略のトレードオフに基づいて、ページごとに適切なレンダー戦略を選択できます。\n\nフレームワークは、必要に応じて好きなページでサーバを使用するオプションを提供しますが、サーバの使用を強制するわけではありません。これにより、アプリ内の各ページごとに、適したレンダー戦略を選択できるようになっています。\n\n#### サーバコンポーネントについて {/*server-components*/}\n\n私たちが推奨するフレームワークは、React Server Components のサポートも含んでいます。\n\nサーバコンポーネントでは、ルーティングとデータフェッチをサーバ側に移動し、クライアントコンポーネントのコード分割を（レンダーするページ単位のみならず）レンダーするデータの種類に基づいて行うことで、これらの問題を解決します。送信される JavaScript の量を減らすことで、最適な[ローディングシーケンス](https://www.patterns.dev/vanilla/loading-sequence)を実現します。\n\nサーバコンポーネントはサーバを必要としません。ビルド時に CI サーバで実行して静的サイト生成 (SSG) アプリを作成するためにも使えますし、ランタイム時にウェブサーバで実行してサーバサイドレンダー (SSR) アプリを作成するためにも使えます。\n\n詳細については、[バンドルサイズゼロの React サーバコンポーネントの紹介](/blog/2020/12/21/data-fetching-with-react-server-components) と [ドキュメント](/reference/rsc/server-components) を参照してください。\n\n</DeepDive>\n\n<Note>\n\n#### サーバレンダーは SEO のためだけではない {/*server-rendering-is-not-just-for-seo*/}\n\nサーバレンダーは [SEO](https://developer.mozilla.org/en-US/docs/Glossary/SEO) のためだけにある、というのはよくある誤解です。\n\nサーバレンダーは SEO も改善しますが、ユーザが画面上のコンテンツを見られるようになるまでにダウンロード・パースする必要のある JavaScript の量を減らすことで、パフォーマンスも向上させるものです。\n\nChrome チームが開発者に向けて、パフォーマンス最大化のために、完全なクライアントサイドに寄せた戦略ではなく、静的ないしサーバサイドレンダリングを検討するよう[促している](https://web.dev/articles/rendering-on-the-web)のには、このような理由があるのです。\n\n</Note>\n\n---\n\n_Create React App を作成した [Dan Abramov](https://bsky.app/profile/danabra.mov)、および Create React App を長年にわたりメンテナンスしてきた [Joe Haddad](https://github.com/Timer)、[Ian Schmitz](https://github.com/ianschmitz)、[Brody McKee](https://github.com/mrmckeb)、そして[他の多くの方々](https://github.com/facebook/create-react-app/graphs/contributors)に感謝します。この投稿をレビューし、フィードバックを提供していただいた [Brooks Lybrand](https://bsky.app/profile/brookslybrand.bsky.social)、[Dan Abramov](https://bsky.app/profile/danabra.mov)、[Devon Govett](https://bsky.app/profile/devongovett.bsky.social)、[Eli White](https://x.com/Eli_White)、[Jack Herrington](https://bsky.app/profile/jherr.dev)、[Joe Savona](https://x.com/en_JS)、[Lauren Tan](https://bsky.app/profile/no.lol)、[Lee Robinson](https://x.com/leeerob)、[Mark Erikson](https://bsky.app/profile/acemarke.dev)、[Ryan Florence](https://x.com/ryanflorence)、[Sophie Alpert](https://bsky.app/profile/sophiebits.com)、[Tanner Linsley](https://bsky.app/profile/tannerlinsley.com)、および [Theo Browne](https://x.com/theo) に感謝します。_\n\n"
  },
  {
    "path": "src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md",
    "content": "---\ntitle: \"React Labs: ビュー遷移、Activity、その他もろもろ\"\nauthor: Ricky Hanlon\ndate: 2025/04/23\ndescription: React Labs 記事では、現在活発に研究・開発が行われているプロジェクトについて述べていきます。この投稿では、今すぐ試すことができる 2 つの新しい実験的機能と、現在取り組んでいる他の分野の更新情報を共有します。\n---\n\nApril 23, 2025 by [Ricky Hanlon](https://twitter.com/rickhanlonii)\n\n---\n\n<Intro>\n\nReact Labs 記事では、現在活発に研究・開発が行われているプロジェクトについて述べていきます。この投稿では、今すぐ試すことができる 2 つの新しい実験的機能と、現在取り組んでいる他の分野の更新情報を共有します。\n\n</Intro>\n\n\n本日、試用の準備が整った 2 つの新しい実験的な機能のドキュメントをリリースします！\n\n- [ビュー遷移 (View Transition)](#view-transitions)\n- [Activity](#activity)\n\nまた、現在開発中の新機能の更新情報も共有します。\n- [React パフォーマンストラック](#react-performance-tracks)\n- [コンパイラの IDE 拡張](#compiler-ide-extension)\n- [エフェクト依存配列の自動化](#automatic-effect-dependencies)\n- [フラグメント ref](#fragment-refs)\n- [並行ストア](#concurrent-stores)\n\n---\n\n# 新しい実験的な機能 {/*new-experimental-features*/}\n\n<Note>\n\n`<Activity />` は `react@19.2` でリリースされました。\n\n`<ViewTransition />` と `addTransitionType` は現在 `react@canary` で利用可能です。\n\n</Note>\n\nビュー遷移 (View Transition) と Activity について、`react@experimental` でテストする準備が整いました。これらの機能は本番環境でテストされており安定していますが、フィードバックを取り入れる過程で最終的な API が変更される可能性が残っています。\n\nReact パッケージを最新の実験的なバージョンにアップグレードすることで試すことが可能です。\n\n- `react@experimental`\n- `react-dom@experimental`\n\nこれらの機能をアプリで使用する方法を学ぶか、新しく公開されたドキュメントをチェックしてください：\n\n- [`<ViewTransition>`](/reference/react/ViewTransition)：ビュー遷移アニメーションを有効にするコンポーネント。\n- [`addTransitionType`](/reference/react/addTransitionType)：ビュー遷移の起因 (cause) を指定するための関数。\n- [`<Activity>`](/reference/react/Activity)：UI の一部を非表示・表示するために使用するコンポーネント。\n\n## ビュー遷移 {/*view-transitions*/}\n\nReact View Transition は、アプリの UI 遷移にアニメーションを追加しやすくするための新しい実験的機能です。内部的にこれらのアニメーションは、ほとんどのモダンブラウザで利用可能な新しい API である [`startViewTransition`](https://developer.mozilla.org/en-US/docs/Web/API/Document/startViewTransition) を使用します。\n\n要素に対してアニメーションを有効化するには、新登場の `<ViewTransition>` コンポーネントで要素をラップします。\n\n```js\n// \"what\" to animate.\n<ViewTransition>\n  <div>animate me</div>\n</ViewTransition>\n```\n\nこの新しいコンポーネントを使用することで、アニメーションが起動した際に「何を」アニメーションするかを宣言的に定義できます。\n\n「いつ」アニメーションするかの方は、以下の 3 つのビュー遷移トリガのいずれかを使用して定義します。\n\n```js\n// \"when\" to animate.\n\n// Transitions\nstartTransition(() => setState(...));\n\n// Deferred Values\nconst deferred = useDeferredValue(value);\n\n// Suspense\n<Suspense fallback={<Fallback />}>\n  <div>Loading...</div>\n</Suspense>\n```\n\nデフォルトでは、これらのアニメーションは[ビュー遷移のためのデフォルト CSS アニメーション](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API/Using#customizing_your_animations) を使用します（通常はスムースなクロスフェード）。「どんな」アニメーションが発生するかを定義するために[ビュー遷移関連の擬似セレクタ](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API/Using#the_view_transition_pseudo-element_tree)を使用できます。たとえば、すべてのビュー遷移についてデフォルトのアニメーションを変更するには `*` を使用できます。\n\n```\n// \"how\" to animate.\n::view-transition-old(*) {\n  animation: 300ms ease-out fade-out;\n}\n::view-transition-new(*) {\n  animation: 300ms ease-in fade-in;\n}\n```\n\nアニメーションのトリガ（`startTransition`、`useDeferredValue`、または `Suspense` フォールバックからコンテンツへの切り替え）によって DOM が更新される場合、React は[宣言的なヒューリスティックス](/reference/react/ViewTransition#viewtransition)を使用して、どの `<ViewTransition>` コンポーネントでアニメーションを起動するかを自動的に決定します。その後、CSS で定義されたアニメーションをブラウザが実行します。\n\nブラウザのビュー遷移 API をすでにご存じで、React がそれをどのようにサポートしているかを知りたい場合は、ドキュメントの [`<ViewTransition>` の動作の仕組み](/reference/react/ViewTransition#how-does-viewtransition-work)をチェックしてください。\n\nこの投稿では、ビュー遷移を使用するいくつかの例を見てみましょう。\n\n以下のアプリから始めましょう。次のような操作ができますが、何のアニメーションも含まれてません。\n- ビデオをクリックして詳細を表示\n- \"Back\" をクリックしてフィードに戻る\n- リスト内でタイプしてビデオをフィルタリング\n\n<Sandpack>\n\n```js src/App.js active\nimport TalkDetails from './Details'; import Home from './Home'; import {useRouter} from './router';\n\nexport default function App() {\n  const {url} = useRouter();\n\n  // 🚩This version doesn't include any animations yet\n  return url === '/' ? <Home /> : <TalkDetails />;\n}\n```\n\n```js src/Details.js\nimport { fetchVideo, fetchVideoDetails } from \"./data\";\nimport { Thumbnail, VideoControls } from \"./Videos\";\nimport { useRouter } from \"./router\";\nimport Layout from \"./Layout\";\nimport { use, Suspense } from \"react\";\nimport { ChevronLeft } from \"./Icons\";\n\nfunction VideoInfo({ id }) {\n  const details = use(fetchVideoDetails(id));\n  return (\n    <>\n      <p className=\"info-title\">{details.title}</p>\n      <p className=\"info-description\">{details.description}</p>\n    </>\n  );\n}\n\nfunction VideoInfoFallback() {\n  return (\n    <>\n      <div className=\"fallback title\"></div>\n      <div className=\"fallback description\"></div>\n    </>\n  );\n}\n\nexport default function Details() {\n  const { url, navigateBack } = useRouter();\n  const videoId = url.split(\"/\").pop();\n  const video = use(fetchVideo(videoId));\n\n  return (\n    <Layout\n      heading={\n        <div\n          className=\"fit back\"\n          onClick={() => {\n            navigateBack(\"/\");\n          }}\n        >\n          <ChevronLeft /> Back\n        </div>\n      }\n    >\n      <div className=\"details\">\n        <Thumbnail video={video} large>\n          <VideoControls />\n        </Thumbnail>\n        <Suspense fallback={<VideoInfoFallback />}>\n          <VideoInfo id={video.id} />\n        </Suspense>\n      </div>\n    </Layout>\n  );\n}\n\n```\n\n```js src/Home.js\nimport { Video } from \"./Videos\";\nimport Layout from \"./Layout\";\nimport { fetchVideos } from \"./data\";\nimport { useId, useState, use } from \"react\";\nimport { IconSearch } from \"./Icons\";\n\nfunction SearchInput({ value, onChange }) {\n  const id = useId();\n  return (\n    <form className=\"search\" onSubmit={(e) => e.preventDefault()}>\n      <label htmlFor={id} className=\"sr-only\">\n        Search\n      </label>\n      <div className=\"search-input\">\n        <div className=\"search-icon\">\n          <IconSearch />\n        </div>\n        <input\n          type=\"text\"\n          id={id}\n          placeholder=\"Search\"\n          value={value}\n          onChange={(e) => onChange(e.target.value)}\n        />\n      </div>\n    </form>\n  );\n}\n\nfunction filterVideos(videos, query) {\n  const keywords = query\n    .toLowerCase()\n    .split(\" \")\n    .filter((s) => s !== \"\");\n  if (keywords.length === 0) {\n    return videos;\n  }\n  return videos.filter((video) => {\n    const words = (video.title + \" \" + video.description)\n      .toLowerCase()\n      .split(\" \");\n    return keywords.every((kw) => words.some((w) => w.includes(kw)));\n  });\n}\n\nexport default function Home() {\n  const videos = use(fetchVideos());\n  const count = videos.length;\n  const [searchText, setSearchText] = useState(\"\");\n  const foundVideos = filterVideos(videos, searchText);\n  return (\n    <Layout heading={<div className=\"fit\">{count} Videos</div>}>\n      <SearchInput value={searchText} onChange={setSearchText} />\n      <div className=\"video-list\">\n        {foundVideos.length === 0 && (\n          <div className=\"no-results\">No results</div>\n        )}\n        <div className=\"videos\">\n          {foundVideos.map((video) => (\n            <Video key={video.id} video={video} />\n          ))}\n        </div>\n      </div>\n    </Layout>\n  );\n}\n\n```\n\n```js src/Icons.js\nexport function ChevronLeft() {\n  return (\n    <svg\n      className=\"chevron-left\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\">\n      <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-446 -398)\">\n        <path\n          fill=\"currentColor\"\n          fillRule=\"nonzero\"\n          d=\"M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z\"\n          transform=\"translate(356.5 164.5)\"\n        />\n        <polygon points=\"446 418 466 418 466 398 446 398\" />\n      </g>\n    </svg>\n  );\n}\n\nexport function PauseIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      style={{padding: '4px'}}\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 512 512\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M256 0C114.617 0 0 114.615 0 256s114.617 256 256 256 256-114.615 256-256S397.383 0 256 0zm-32 320c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128zm128 0c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n\nexport function PlayIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 72 72\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M36 69C54.2254 69 69 54.2254 69 36C69 17.7746 54.2254 3 36 3C17.7746 3 3 17.7746 3 36C3 54.2254 17.7746 69 36 69ZM52.1716 38.6337L28.4366 51.5801C26.4374 52.6705 24 51.2235 24 48.9464V23.0536C24 20.7764 26.4374 19.3295 28.4366 20.4199L52.1716 33.3663C54.2562 34.5034 54.2562 37.4966 52.1716 38.6337Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\nexport function Heart({liked, animate}) {\n  return (\n    <>\n      <svg\n        className=\"absolute overflow-visible\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <circle\n          className={`circle ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n          cx=\"12\"\n          cy=\"12\"\n          r=\"11.5\"\n          fill=\"transparent\"\n          strokeWidth=\"0\"\n          stroke=\"currentColor\"\n        />\n      </svg>\n\n      <svg\n        className={`heart ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        {liked ? (\n          <path\n            d=\"M12 23a.496.496 0 0 1-.26-.074C7.023 19.973 0 13.743 0 8.68c0-4.12 2.322-6.677 6.058-6.677 2.572 0 5.108 2.387 5.134 2.41l.808.771.808-.771C12.834 4.387 15.367 2 17.935 2 21.678 2 24 4.558 24 8.677c0 5.06-7.022 11.293-11.74 14.246a.496.496 0 0 1-.26.074V23z\"\n            fill=\"currentColor\"\n          />\n        ) : (\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"m12 5.184-.808-.771-.004-.004C11.065 4.299 8.522 2.003 6 2.003c-3.736 0-6 2.558-6 6.677 0 4.47 5.471 9.848 10 13.079.602.43 1.187.82 1.74 1.167A.497.497 0 0 0 12 23v-.003c.09 0 .182-.026.26-.074C16.977 19.97 24 13.737 24 8.677 24 4.557 21.743 2 18 2c-2.569 0-5.166 2.387-5.192 2.413L12 5.184zm-.002 15.525c2.071-1.388 4.477-3.342 6.427-5.47C20.72 12.733 22 10.401 22 8.677c0-1.708-.466-2.855-1.087-3.55C20.316 4.459 19.392 4 18 4c-.726 0-1.63.364-2.5.9-.67.412-1.148.82-1.266.92-.03.025-.037.031-.019.014l-.013.013L12 7.949 9.832 5.88a10.08 10.08 0 0 0-1.33-.977C7.633 4.367 6.728 4.003 6 4.003c-1.388 0-2.312.459-2.91 1.128C2.466 5.826 2 6.974 2 8.68c0 1.726 1.28 4.058 3.575 6.563 1.948 2.127 4.352 4.078 6.423 5.466z\"\n            fill=\"currentColor\"\n          />\n        )}\n      </svg>\n    </>\n  );\n}\n\nexport function IconSearch(props) {\n  return (\n    <svg width=\"1em\" height=\"1em\" viewBox=\"0 0 20 20\">\n      <path\n        d=\"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z\"\n        stroke=\"currentColor\"\n        fill=\"none\"\n        strokeWidth=\"2\"\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"></path>\n    </svg>\n  );\n}\n```\n\n```js src/Layout.js\nimport { useIsNavPending } from \"./router\";\n\nexport default function Page({ heading, children }) {\n  const isPending = useIsNavPending();\n  return (\n    <div className=\"page\">\n      <div className=\"top\">\n        <div className=\"top-nav\">\n          {heading}\n          {isPending && <span className=\"loader\"></span>}\n        </div>\n      </div>\n\n      <div className=\"bottom\">\n        <div className=\"content\">{children}</div>\n      </div>\n    </div>\n  );\n}\n```\n\n```js src/LikeButton.js\nimport {useState} from 'react';\nimport {Heart} from './Icons';\n\n// A hack since we don't actually have a backend.\n// Unlike local state, this survives videos being filtered.\nconst likedVideos = new Set();\n\nexport default function LikeButton({video}) {\n  const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));\n  const [animate, setAnimate] = useState(false);\n  return (\n    <button\n      className={`like-button ${isLiked && 'liked'}`}\n      aria-label={isLiked ? 'Unsave' : 'Save'}\n      onClick={() => {\n        const nextIsLiked = !isLiked;\n        if (nextIsLiked) {\n          likedVideos.add(video.id);\n        } else {\n          likedVideos.delete(video.id);\n        }\n        setAnimate(true);\n        setIsLiked(nextIsLiked);\n      }}>\n      <Heart liked={isLiked} animate={animate} />\n    </button>\n  );\n}\n```\n\n```js src/Videos.js\nimport { useState } from \"react\";\nimport LikeButton from \"./LikeButton\";\nimport { useRouter } from \"./router\";\nimport { PauseIcon, PlayIcon } from \"./Icons\";\nimport { startTransition } from \"react\";\n\nexport function VideoControls() {\n  const [isPlaying, setIsPlaying] = useState(false);\n\n  return (\n    <span\n      className=\"controls\"\n      onClick={() =>\n        startTransition(() => {\n          setIsPlaying((p) => !p);\n        })\n      }\n    >\n      {isPlaying ? <PauseIcon /> : <PlayIcon />}\n    </span>\n  );\n}\n\nexport function Thumbnail({ video, children }) {\n  return (\n    <div\n      aria-hidden=\"true\"\n      tabIndex={-1}\n      className={`thumbnail ${video.image}`}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport function Video({ video }) {\n  const { navigate } = useRouter();\n\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n        onClick={(e) => {\n          e.preventDefault();\n          navigate(`/video/${video.id}`);\n        }}\n      >\n        <Thumbnail video={video}></Thumbnail>\n\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n      <LikeButton video={video} />\n    </div>\n  );\n}\n```\n\n\n```js src/data.js hidden\nconst videos = [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  },\n  {\n    id: '2',\n    title: 'Second video',\n    description: 'Video description',\n    image: 'red',\n  },\n  {\n    id: '3',\n    title: 'Third video',\n    description: 'Video description',\n    image: 'green',\n  },\n  {\n    id: '4',\n    title: 'Fourth video',\n    description: 'Video description',\n    image: 'purple',\n  },\n  {\n    id: '5',\n    title: 'Fifth video',\n    description: 'Video description',\n    image: 'yellow',\n  },\n  {\n    id: '6',\n    title: 'Sixth video',\n    description: 'Video description',\n    image: 'gray',\n  },\n];\n\nlet videosCache = new Map();\nlet videoCache = new Map();\nlet videoDetailsCache = new Map();\nconst VIDEO_DELAY = 1;\nconst VIDEO_DETAILS_DELAY = 1000;\nexport function fetchVideos() {\n  if (videosCache.has(0)) {\n    return videosCache.get(0);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos);\n    }, VIDEO_DELAY);\n  });\n  videosCache.set(0, promise);\n  return promise;\n}\n\nexport function fetchVideo(id) {\n  if (videoCache.has(id)) {\n    return videoCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DELAY);\n  });\n  videoCache.set(id, promise);\n  return promise;\n}\n\nexport function fetchVideoDetails(id) {\n  if (videoDetailsCache.has(id)) {\n    return videoDetailsCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DETAILS_DELAY);\n  });\n  videoDetailsCache.set(id, promise);\n  return promise;\n}\n```\n\n```js src/router.js\nimport {\n  useState,\n  createContext,\n  use,\n  useTransition,\n  useLayoutEffect,\n  useEffect,\n} from \"react\";\n\nconst RouterContext = createContext({ url: \"/\", params: {} });\n\nexport function useRouter() {\n  return use(RouterContext);\n}\n\nexport function useIsNavPending() {\n  return use(RouterContext).isPending;\n}\n\nexport function Router({ children }) {\n  const [routerState, setRouterState] = useState({\n    pendingNav: () => {},\n    url: document.location.pathname,\n  });\n  const [isPending, startTransition] = useTransition();\n\n  function go(url) {\n    setRouterState({\n      url,\n      pendingNav() {\n        window.history.pushState({}, \"\", url);\n      },\n    });\n  }\n  function navigate(url) {\n    // Update router state in transition.\n    startTransition(() => {\n      go(url);\n    });\n  }\n\n  function navigateBack(url) {\n    // Update router state in transition.\n    startTransition(() => {\n      go(url);\n    });\n  }\n\n  useEffect(() => {\n    function handlePopState() {\n      // This should not animate because restoration has to be synchronous.\n      // Even though it's a transition.\n      startTransition(() => {\n        setRouterState({\n          url: document.location.pathname + document.location.search,\n          pendingNav() {\n            // Noop. URL has already updated.\n          },\n        });\n      });\n    }\n    window.addEventListener(\"popstate\", handlePopState);\n    return () => {\n      window.removeEventListener(\"popstate\", handlePopState);\n    };\n  }, []);\n  const pendingNav = routerState.pendingNav;\n  useLayoutEffect(() => {\n    pendingNav();\n  }, [pendingNav]);\n\n  return (\n    <RouterContext\n      value={{\n        url: routerState.url,\n        navigate,\n        navigateBack,\n        isPending,\n        params: {},\n      }}\n    >\n      {children}\n    </RouterContext>\n  );\n}\n```\n\n```css src/styles.css\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Rg.woff2) format(\"woff2\");\n  font-weight: 400;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Md.woff2) format(\"woff2\");\n  font-weight: 500;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 600;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 700;\n  font-style: normal;\n  font-display: swap;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhtml {\n  background-image: url(https://react.dev/images/meta-gradient-dark.png);\n  background-size: 100%;\n  background-position: -100%;\n  background-color: rgb(64 71 86);\n  background-repeat: no-repeat;\n  height: 100%;\n  width: 100%;\n}\n\nbody {\n  font-family: Optimistic Text, -apple-system, ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;\n  padding: 10px 0 10px 0;\n  margin: 0;\n  display: flex;\n  justify-content: center;\n}\n\n#root {\n  flex: 1 1;\n  height: auto;\n  background-color: #fff;\n  border-radius: 10px;\n  max-width: 450px;\n  min-height: 600px;\n  padding-bottom: 10px;\n}\n\nh1 {\n  margin-top: 0;\n  font-size: 22px;\n}\n\nh2 {\n  margin-top: 0;\n  font-size: 20px;\n}\n\nh3 {\n  margin-top: 0;\n  font-size: 18px;\n}\n\nh4 {\n  margin-top: 0;\n  font-size: 16px;\n}\n\nh5 {\n  margin-top: 0;\n  font-size: 14px;\n}\n\nh6 {\n  margin-top: 0;\n  font-size: 12px;\n}\n\ncode {\n  font-size: 1.2em;\n}\n\nul {\n  padding-inline-start: 20px;\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border-width: 0;\n}\n\n.absolute {\n  position: absolute;\n}\n\n.overflow-visible {\n  overflow: visible;\n}\n\n.visible {\n  overflow: visible;\n}\n\n.fit {\n  width: fit-content;\n}\n\n\n/* Layout */\n.page {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n}\n\n.top-hero {\n  height: 200px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-image: conic-gradient(\n      from 90deg at -10% 100%,\n      #2b303b 0deg,\n      #2b303b 90deg,\n      #16181d 1turn\n  );\n}\n\n.bottom {\n  flex: 1;\n  overflow: auto;\n}\n\n.top-nav {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 0;\n  padding: 0 12px;\n  top: 0;\n  width: 100%;\n  height: 44px;\n  color: #23272f;\n  font-weight: 700;\n  font-size: 20px;\n  z-index: 100;\n  cursor: default;\n}\n\n.content {\n  padding: 0 12px;\n  margin-top: 4px;\n}\n\n\n.loader {\n  color: #23272f;\n  font-size: 3px;\n  width: 1em;\n  margin-right: 18px;\n  height: 1em;\n  border-radius: 50%;\n  position: relative;\n  text-indent: -9999em;\n  animation: loading-spinner 1.3s infinite linear;\n  animation-delay: 200ms;\n  transform: translateZ(0);\n}\n\n@keyframes loading-spinner {\n  0%,\n  100% {\n    box-shadow: 0 -3em 0 0.2em,\n    2em -2em 0 0em, 3em 0 0 -1em,\n    2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 0;\n  }\n  12.5% {\n    box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em,\n    3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  25% {\n    box-shadow: 0 -3em 0 -0.5em,\n    2em -2em 0 0, 3em 0 0 0.2em,\n    2em 2em 0 0, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  37.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em,\n    -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  50% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em,\n    -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  62.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0,\n    -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em;\n  }\n  75% {\n    box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0;\n  }\n  87.5% {\n    box-shadow: 0em -3em 0 0, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;\n  }\n}\n\n/* LikeButton */\n.like-button {\n  outline-offset: 2px;\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 2.5rem;\n  height: 2.5rem;\n  cursor: pointer;\n  border-radius: 9999px;\n  border: none;\n  outline: none 2px;\n  color: #5e687e;\n  background: none;\n}\n\n.like-button:focus {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n}\n\n.like-button:active {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n  transform: scaleX(0.95) scaleY(0.95);\n}\n\n.like-button:hover {\n  background-color: #f6f7f9;\n}\n\n.like-button.liked {\n  color: #a6423a;\n}\n\n/* Icons */\n@keyframes circle {\n  0% {\n    transform: scale(0);\n    stroke-width: 16px;\n  }\n\n  50% {\n    transform: scale(.5);\n    stroke-width: 16px;\n  }\n\n  to {\n    transform: scale(1);\n    stroke-width: 0;\n  }\n}\n\n.circle {\n  color: rgba(166, 66, 58, .5);\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4,0,.2,1);\n}\n\n.circle.liked.animate {\n  animation: circle .3s forwards;\n}\n\n.heart {\n  width: 1.5rem;\n  height: 1.5rem;\n}\n\n.heart.liked {\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4, 0, .2, 1);\n}\n\n.heart.liked.animate {\n  animation: scale .35s ease-in-out forwards;\n}\n\n.control-icon {\n  color: hsla(0, 0%, 100%, .5);\n  filter:  drop-shadow(0 20px 13px rgba(0, 0, 0, .03)) drop-shadow(0 8px 5px rgba(0, 0, 0, .08));\n}\n\n.chevron-left {\n  margin-top: 2px;\n  rotate: 90deg;\n}\n\n\n/* Video */\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n\n.thumbnail.red {\n  background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);\n}\n\n.thumbnail.green {\n  background-image: conic-gradient(at top right, #c76a15, #388f7f, #2b3491);\n}\n\n.thumbnail.purple {\n  background-image: conic-gradient(at top right, #c76a15, #575fb7, #2b3491);\n}\n\n.thumbnail.yellow {\n  background-image: conic-gradient(at top right, #c76a15, #FABD62, #2b3491);\n}\n\n.thumbnail.gray {\n  background-image: conic-gradient(at top right, #c76a15, #4E5769, #2b3491);\n}\n\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n}\n\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n\n.video .info:hover {\n  text-decoration: underline;\n}\n\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n\n/* Details */\n.details .thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 100%;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.video-details-title {\n  margin-top: 8px;\n}\n\n.video-details-speaker {\n  display: flex;\n  gap: 8px;\n  margin-top: 10px\n}\n\n.back {\n  display: flex;\n  align-items: center;\n  margin-left: -5px;\n  cursor: pointer;\n}\n\n.back:hover {\n  text-decoration: underline;\n}\n\n.info-title {\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1.25;\n  margin: 8px 0 0 0 ;\n}\n\n.info-description {\n  margin: 8px 0 0 0;\n}\n\n.controls {\n  cursor: pointer;\n}\n\n.fallback {\n  background: #f6f7f8 linear-gradient(to right, #e6e6e6 5%, #cccccc 25%, #e6e6e6 35%) no-repeat;\n  background-size: 800px 104px;\n  display: block;\n  line-height: 1.25;\n  margin: 8px 0 0 0;\n  border-radius: 5px;\n  overflow: hidden;\n\n  animation: 1s linear 1s infinite shimmer;\n  animation-delay: 300ms;\n  animation-duration: 1s;\n  animation-fill-mode: forwards;\n  animation-iteration-count: infinite;\n  animation-name: shimmer;\n  animation-timing-function: linear;\n}\n\n\n.fallback.title {\n  width: 130px;\n  height: 30px;\n\n}\n\n.fallback.description {\n  width: 150px;\n  height: 21px;\n}\n\n@keyframes shimmer {\n  0% {\n    background-position: -468px 0;\n  }\n\n  100% {\n    background-position: 468px 0;\n  }\n}\n\n.search {\n  margin-bottom: 10px;\n}\n.search-input {\n  width: 100%;\n  position: relative;\n}\n\n.search-icon {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  inset-inline-start: 0;\n  display: flex;\n  align-items: center;\n  padding-inline-start: 1rem;\n  pointer-events: none;\n  color: #99a1b3;\n}\n\n.search-input input {\n  display: flex;\n  padding-inline-start: 2.75rem;\n  padding-top: 10px;\n  padding-bottom: 10px;\n  width: 100%;\n  text-align: start;\n  background-color: rgb(235 236 240);\n  outline: 2px solid transparent;\n  cursor: pointer;\n  border: none;\n  align-items: center;\n  color: rgb(35 39 47);\n  border-radius: 9999px;\n  vertical-align: middle;\n  font-size: 15px;\n}\n\n.search-input input:hover, .search-input input:active {\n  background-color: rgb(235 236 240/ 0.8);\n  color: rgb(35 39 47/ 0.8);\n}\n\n/* Home */\n.video-list {\n  position: relative;\n}\n\n.video-list .videos {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n  overflow-y: auto;\n  height: 100%;\n}\n```\n\n```js src/index.js hidden\nimport React, {StrictMode} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport './styles.css';\n\nimport App from './App';\nimport {Router} from './router';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <Router>\n      <App />\n    </Router>\n  </StrictMode>\n);\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\n<Note>\n\n#### ビュー遷移は CSS・JS 駆動のアニメーションを置き換えるものではない {/*view-transitions-do-not-replace-css-and-js-driven-animations*/}\n\nビュー遷移は、ナビゲーション、内容展開、オープン、並べ替えといった UI 遷移に対して使用されることを意図しています。アプリ内のすべてのアニメーションを置き換えることが意図されているわけではありません。\n\n上記のサンプルアプリでは、\"Like\" ボタンをクリックしたときやサスペンスフォールバック内のローディング表示には、すでにアニメーションがあることに注意してください。これらは特定の要素だけをアニメーションしているため、CSS アニメーションの良い使用例です。\n\n</Note>\n\n### ナビゲーションのアニメーション {/*animating-navigations*/}\n\nこのアプリにはサスペンス対応のルータが含まれており、[ページの遷移はすでにトランジションとしてマークされています](/reference/react/useTransition#building-a-suspense-enabled-router)。つまり、ナビゲーションは `startTransition` 内で実行されます。\n\n```js\nfunction navigate(url) {\n  startTransition(() => {\n    go(url);\n  });\n}\n```\n\n`startTransition` はビュー遷移のトリガの一種なので、ページ間でアニメーションするために `<ViewTransition>` を追加できます。\n\n```js\n// \"what\" to animate\n<ViewTransition key={url}>\n  {url === '/' ? <Home /> : <TalkDetails />}\n</ViewTransition>\n```\n\n`url` が変化すると、`<ViewTransition>` と新しいページがレンダーされます。`<ViewTransition>` の更新が `startTransition` 内で起きたため、この `<ViewTransition>` のアニメーションが起動されます。\n\n\nデフォルトでは、ビュー遷移としてブラウザのデフォルトのクロスフェードアニメーションが含まれています。これを追加することで、ページ間を移動するたびにクロスフェードが発生するようになります。\n\n<Sandpack>\n\n```js src/App.js active\nimport {ViewTransition} from 'react'; import Details from './Details';\nimport Home from './Home'; import {useRouter} from './router';\n\nexport default function App() {\n  const {url} = useRouter();\n\n  // Use ViewTransition to animate between pages.\n  // No additional CSS needed by default.\n  return (\n    <ViewTransition>\n      {url === '/' ? <Home /> : <Details />}\n    </ViewTransition>\n  );\n}\n```\n\n```js src/Details.js hidden\nimport { fetchVideo, fetchVideoDetails } from \"./data\";\nimport { Thumbnail, VideoControls } from \"./Videos\";\nimport { useRouter } from \"./router\";\nimport Layout from \"./Layout\";\nimport { use, Suspense } from \"react\";\nimport { ChevronLeft } from \"./Icons\";\n\nfunction VideoInfo({ id }) {\n  const details = use(fetchVideoDetails(id));\n  return (\n    <>\n      <p className=\"info-title\">{details.title}</p>\n      <p className=\"info-description\">{details.description}</p>\n    </>\n  );\n}\n\nfunction VideoInfoFallback() {\n  return (\n    <>\n      <div className=\"fallback title\"></div>\n      <div className=\"fallback description\"></div>\n    </>\n  );\n}\n\nexport default function Details() {\n  const { url, navigateBack } = useRouter();\n  const videoId = url.split(\"/\").pop();\n  const video = use(fetchVideo(videoId));\n\n  return (\n    <Layout\n      heading={\n        <div\n          className=\"fit back\"\n          onClick={() => {\n            navigateBack(\"/\");\n          }}\n        >\n          <ChevronLeft /> Back\n        </div>\n      }\n    >\n      <div className=\"details\">\n        <Thumbnail video={video} large>\n          <VideoControls />\n        </Thumbnail>\n        <Suspense fallback={<VideoInfoFallback />}>\n          <VideoInfo id={video.id} />\n        </Suspense>\n      </div>\n    </Layout>\n  );\n}\n\n```\n\n```js src/Home.js hidden\nimport { Video } from \"./Videos\";\nimport Layout from \"./Layout\";\nimport { fetchVideos } from \"./data\";\nimport { useId, useState, use } from \"react\";\nimport { IconSearch } from \"./Icons\";\n\nfunction SearchInput({ value, onChange }) {\n  const id = useId();\n  return (\n    <form className=\"search\" onSubmit={(e) => e.preventDefault()}>\n      <label htmlFor={id} className=\"sr-only\">\n        Search\n      </label>\n      <div className=\"search-input\">\n        <div className=\"search-icon\">\n          <IconSearch />\n        </div>\n        <input\n          type=\"text\"\n          id={id}\n          placeholder=\"Search\"\n          value={value}\n          onChange={(e) => onChange(e.target.value)}\n        />\n      </div>\n    </form>\n  );\n}\n\nfunction filterVideos(videos, query) {\n  const keywords = query\n    .toLowerCase()\n    .split(\" \")\n    .filter((s) => s !== \"\");\n  if (keywords.length === 0) {\n    return videos;\n  }\n  return videos.filter((video) => {\n    const words = (video.title + \" \" + video.description)\n      .toLowerCase()\n      .split(\" \");\n    return keywords.every((kw) => words.some((w) => w.includes(kw)));\n  });\n}\n\nexport default function Home() {\n  const videos = use(fetchVideos());\n  const count = videos.length;\n  const [searchText, setSearchText] = useState(\"\");\n  const foundVideos = filterVideos(videos, searchText);\n  return (\n    <Layout heading={<div className=\"fit\">{count} Videos</div>}>\n      <SearchInput value={searchText} onChange={setSearchText} />\n      <div className=\"video-list\">\n        {foundVideos.length === 0 && (\n          <div className=\"no-results\">No results</div>\n        )}\n        <div className=\"videos\">\n          {foundVideos.map((video) => (\n            <Video key={video.id} video={video} />\n          ))}\n        </div>\n      </div>\n    </Layout>\n  );\n}\n\n```\n\n```js src/Icons.js hidden\nexport function ChevronLeft() {\n  return (\n    <svg\n      className=\"chevron-left\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\">\n      <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-446 -398)\">\n        <path\n          fill=\"currentColor\"\n          fillRule=\"nonzero\"\n          d=\"M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z\"\n          transform=\"translate(356.5 164.5)\"\n        />\n        <polygon points=\"446 418 466 418 466 398 446 398\" />\n      </g>\n    </svg>\n  );\n}\n\nexport function PauseIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      style={{padding: '4px'}}\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 512 512\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M256 0C114.617 0 0 114.615 0 256s114.617 256 256 256 256-114.615 256-256S397.383 0 256 0zm-32 320c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128zm128 0c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n\nexport function PlayIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 72 72\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M36 69C54.2254 69 69 54.2254 69 36C69 17.7746 54.2254 3 36 3C17.7746 3 3 17.7746 3 36C3 54.2254 17.7746 69 36 69ZM52.1716 38.6337L28.4366 51.5801C26.4374 52.6705 24 51.2235 24 48.9464V23.0536C24 20.7764 26.4374 19.3295 28.4366 20.4199L52.1716 33.3663C54.2562 34.5034 54.2562 37.4966 52.1716 38.6337Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\nexport function Heart({liked, animate}) {\n  return (\n    <>\n      <svg\n        className=\"absolute overflow-visible\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <circle\n          className={`circle ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n          cx=\"12\"\n          cy=\"12\"\n          r=\"11.5\"\n          fill=\"transparent\"\n          strokeWidth=\"0\"\n          stroke=\"currentColor\"\n        />\n      </svg>\n\n      <svg\n        className={`heart ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        {liked ? (\n          <path\n            d=\"M12 23a.496.496 0 0 1-.26-.074C7.023 19.973 0 13.743 0 8.68c0-4.12 2.322-6.677 6.058-6.677 2.572 0 5.108 2.387 5.134 2.41l.808.771.808-.771C12.834 4.387 15.367 2 17.935 2 21.678 2 24 4.558 24 8.677c0 5.06-7.022 11.293-11.74 14.246a.496.496 0 0 1-.26.074V23z\"\n            fill=\"currentColor\"\n          />\n        ) : (\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"m12 5.184-.808-.771-.004-.004C11.065 4.299 8.522 2.003 6 2.003c-3.736 0-6 2.558-6 6.677 0 4.47 5.471 9.848 10 13.079.602.43 1.187.82 1.74 1.167A.497.497 0 0 0 12 23v-.003c.09 0 .182-.026.26-.074C16.977 19.97 24 13.737 24 8.677 24 4.557 21.743 2 18 2c-2.569 0-5.166 2.387-5.192 2.413L12 5.184zm-.002 15.525c2.071-1.388 4.477-3.342 6.427-5.47C20.72 12.733 22 10.401 22 8.677c0-1.708-.466-2.855-1.087-3.55C20.316 4.459 19.392 4 18 4c-.726 0-1.63.364-2.5.9-.67.412-1.148.82-1.266.92-.03.025-.037.031-.019.014l-.013.013L12 7.949 9.832 5.88a10.08 10.08 0 0 0-1.33-.977C7.633 4.367 6.728 4.003 6 4.003c-1.388 0-2.312.459-2.91 1.128C2.466 5.826 2 6.974 2 8.68c0 1.726 1.28 4.058 3.575 6.563 1.948 2.127 4.352 4.078 6.423 5.466z\"\n            fill=\"currentColor\"\n          />\n        )}\n      </svg>\n    </>\n  );\n}\n\nexport function IconSearch(props) {\n  return (\n    <svg width=\"1em\" height=\"1em\" viewBox=\"0 0 20 20\">\n      <path\n        d=\"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z\"\n        stroke=\"currentColor\"\n        fill=\"none\"\n        strokeWidth=\"2\"\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"></path>\n    </svg>\n  );\n}\n```\n\n```js src/Layout.js\nimport {ViewTransition} from 'react'; import { useIsNavPending } from \"./router\";\n\nexport default function Page({ heading, children }) {\n  const isPending = useIsNavPending();\n\n  return (\n    <div className=\"page\">\n      <div className=\"top\">\n        <div className=\"top-nav\">\n          {heading}\n          {isPending && <span className=\"loader\"></span>}\n        </div>\n      </div>\n      {/* Opt-out of ViewTransition for the content. */}\n      {/* Content can define it's own ViewTransition. */}\n      <ViewTransition default=\"none\">\n        <div className=\"bottom\">\n          <div className=\"content\">{children}</div>\n        </div>\n      </ViewTransition>\n    </div>\n  );\n}\n```\n\n```js src/LikeButton.js hidden\nimport {useState} from 'react';\nimport {Heart} from './Icons';\n\n// A hack since we don't actually have a backend.\n// Unlike local state, this survives videos being filtered.\nconst likedVideos = new Set();\n\nexport default function LikeButton({video}) {\n  const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));\n  const [animate, setAnimate] = useState(false);\n  return (\n    <button\n      className={`like-button ${isLiked && 'liked'}`}\n      aria-label={isLiked ? 'Unsave' : 'Save'}\n      onClick={() => {\n        const nextIsLiked = !isLiked;\n        if (nextIsLiked) {\n          likedVideos.add(video.id);\n        } else {\n          likedVideos.delete(video.id);\n        }\n        setAnimate(true);\n        setIsLiked(nextIsLiked);\n      }}>\n      <Heart liked={isLiked} animate={animate} />\n    </button>\n  );\n}\n```\n\n```js src/Videos.js hidden\nimport { useState } from \"react\";\nimport LikeButton from \"./LikeButton\";\nimport { useRouter } from \"./router\";\nimport { PauseIcon, PlayIcon } from \"./Icons\";\nimport { startTransition } from \"react\";\n\nexport function VideoControls() {\n  const [isPlaying, setIsPlaying] = useState(false);\n\n  return (\n    <span\n      className=\"controls\"\n      onClick={() =>\n        startTransition(() => {\n          setIsPlaying((p) => !p);\n        })\n      }\n    >\n      {isPlaying ? <PauseIcon /> : <PlayIcon />}\n    </span>\n  );\n}\n\nexport function Thumbnail({ video, children }) {\n  return (\n    <div\n      aria-hidden=\"true\"\n      tabIndex={-1}\n      className={`thumbnail ${video.image}`}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport function Video({ video }) {\n  const { navigate } = useRouter();\n\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n        onClick={(e) => {\n          e.preventDefault();\n          navigate(`/video/${video.id}`);\n        }}\n      >\n        <Thumbnail video={video}></Thumbnail>\n\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n      <LikeButton video={video} />\n    </div>\n  );\n}\n```\n\n\n```js src/data.js hidden\nconst videos = [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  },\n  {\n    id: '2',\n    title: 'Second video',\n    description: 'Video description',\n    image: 'red',\n  },\n  {\n    id: '3',\n    title: 'Third video',\n    description: 'Video description',\n    image: 'green',\n  },\n  {\n    id: '4',\n    title: 'Fourth video',\n    description: 'Video description',\n    image: 'purple',\n  },\n  {\n    id: '5',\n    title: 'Fifth video',\n    description: 'Video description',\n    image: 'yellow',\n  },\n  {\n    id: '6',\n    title: 'Sixth video',\n    description: 'Video description',\n    image: 'gray',\n  },\n];\n\nlet videosCache = new Map();\nlet videoCache = new Map();\nlet videoDetailsCache = new Map();\nconst VIDEO_DELAY = 1;\nconst VIDEO_DETAILS_DELAY = 1000;\nexport function fetchVideos() {\n  if (videosCache.has(0)) {\n    return videosCache.get(0);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos);\n    }, VIDEO_DELAY);\n  });\n  videosCache.set(0, promise);\n  return promise;\n}\n\nexport function fetchVideo(id) {\n  if (videoCache.has(id)) {\n    return videoCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DELAY);\n  });\n  videoCache.set(id, promise);\n  return promise;\n}\n\nexport function fetchVideoDetails(id) {\n  if (videoDetailsCache.has(id)) {\n    return videoDetailsCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DETAILS_DELAY);\n  });\n  videoDetailsCache.set(id, promise);\n  return promise;\n}\n```\n\n```js src/router.js\nimport {useState, createContext,use,useTransition,useLayoutEffect,useEffect} from \"react\";\n\nexport function Router({ children }) {\n  const [isPending, startTransition] = useTransition();\n\n  function navigate(url) {\n    // Update router state in transition.\n    startTransition(() => {\n      go(url);\n    });\n  }\n\n\n\n\n  const [routerState, setRouterState] = useState({\n    pendingNav: () => {},\n    url: document.location.pathname,\n  });\n\n\n  function go(url) {\n    setRouterState({\n      url,\n      pendingNav() {\n        window.history.pushState({}, \"\", url);\n      },\n    });\n  }\n\n\n  function navigateBack(url) {\n    startTransition(() => {\n      go(url);\n    });\n  }\n\n  useEffect(() => {\n    function handlePopState() {\n      // This should not animate because restoration has to be synchronous.\n      // Even though it's a transition.\n      startTransition(() => {\n        setRouterState({\n          url: document.location.pathname + document.location.search,\n          pendingNav() {\n            // Noop. URL has already updated.\n          },\n        });\n      });\n    }\n    window.addEventListener(\"popstate\", handlePopState);\n    return () => {\n      window.removeEventListener(\"popstate\", handlePopState);\n    };\n  }, []);\n  const pendingNav = routerState.pendingNav;\n  useLayoutEffect(() => {\n    pendingNav();\n  }, [pendingNav]);\n\n  return (\n    <RouterContext\n      value={{\n        url: routerState.url,\n        navigate,\n        navigateBack,\n        isPending,\n        params: {},\n      }}\n    >\n      {children}\n    </RouterContext>\n  );\n}\n\nconst RouterContext = createContext({ url: \"/\", params: {} });\n\nexport function useRouter() {\n  return use(RouterContext);\n}\n\nexport function useIsNavPending() {\n  return use(RouterContext).isPending;\n}\n```\n\n```css src/styles.css hidden\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Rg.woff2) format(\"woff2\");\n  font-weight: 400;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Md.woff2) format(\"woff2\");\n  font-weight: 500;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 600;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 700;\n  font-style: normal;\n  font-display: swap;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhtml {\n  background-image: url(https://react.dev/images/meta-gradient-dark.png);\n  background-size: 100%;\n  background-position: -100%;\n  background-color: rgb(64 71 86);\n  background-repeat: no-repeat;\n  height: 100%;\n  width: 100%;\n}\n\nbody {\n  font-family: Optimistic Text, -apple-system, ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;\n  padding: 10px 0 10px 0;\n  margin: 0;\n  display: flex;\n  justify-content: center;\n}\n\n#root {\n  flex: 1 1;\n  height: auto;\n  background-color: #fff;\n  border-radius: 10px;\n  max-width: 450px;\n  min-height: 600px;\n  padding-bottom: 10px;\n}\n\nh1 {\n  margin-top: 0;\n  font-size: 22px;\n}\n\nh2 {\n  margin-top: 0;\n  font-size: 20px;\n}\n\nh3 {\n  margin-top: 0;\n  font-size: 18px;\n}\n\nh4 {\n  margin-top: 0;\n  font-size: 16px;\n}\n\nh5 {\n  margin-top: 0;\n  font-size: 14px;\n}\n\nh6 {\n  margin-top: 0;\n  font-size: 12px;\n}\n\ncode {\n  font-size: 1.2em;\n}\n\nul {\n  padding-inline-start: 20px;\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border-width: 0;\n}\n\n.absolute {\n  position: absolute;\n}\n\n.overflow-visible {\n  overflow: visible;\n}\n\n.visible {\n  overflow: visible;\n}\n\n.fit {\n  width: fit-content;\n}\n\n\n/* Layout */\n.page {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n}\n\n.top-hero {\n  height: 200px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-image: conic-gradient(\n      from 90deg at -10% 100%,\n      #2b303b 0deg,\n      #2b303b 90deg,\n      #16181d 1turn\n  );\n}\n\n.bottom {\n  flex: 1;\n  overflow: auto;\n}\n\n.top-nav {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 0;\n  padding: 0 12px;\n  top: 0;\n  width: 100%;\n  height: 44px;\n  color: #23272f;\n  font-weight: 700;\n  font-size: 20px;\n  z-index: 100;\n  cursor: default;\n}\n\n.content {\n  padding: 0 12px;\n  margin-top: 4px;\n}\n\n\n.loader {\n  color: #23272f;\n  font-size: 3px;\n  width: 1em;\n  margin-right: 18px;\n  height: 1em;\n  border-radius: 50%;\n  position: relative;\n  text-indent: -9999em;\n  animation: loading-spinner 1.3s infinite linear;\n  animation-delay: 200ms;\n  transform: translateZ(0);\n}\n\n@keyframes loading-spinner {\n  0%,\n  100% {\n    box-shadow: 0 -3em 0 0.2em,\n    2em -2em 0 0em, 3em 0 0 -1em,\n    2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 0;\n  }\n  12.5% {\n    box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em,\n    3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  25% {\n    box-shadow: 0 -3em 0 -0.5em,\n    2em -2em 0 0, 3em 0 0 0.2em,\n    2em 2em 0 0, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  37.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em,\n    -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  50% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em,\n    -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  62.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0,\n    -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em;\n  }\n  75% {\n    box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0;\n  }\n  87.5% {\n    box-shadow: 0em -3em 0 0, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;\n  }\n}\n\n/* LikeButton */\n.like-button {\n  outline-offset: 2px;\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 2.5rem;\n  height: 2.5rem;\n  cursor: pointer;\n  border-radius: 9999px;\n  border: none;\n  outline: none 2px;\n  color: #5e687e;\n  background: none;\n}\n\n.like-button:focus {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n}\n\n.like-button:active {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n  transform: scaleX(0.95) scaleY(0.95);\n}\n\n.like-button:hover {\n  background-color: #f6f7f9;\n}\n\n.like-button.liked {\n  color: #a6423a;\n}\n\n/* Icons */\n@keyframes circle {\n  0% {\n    transform: scale(0);\n    stroke-width: 16px;\n  }\n\n  50% {\n    transform: scale(.5);\n    stroke-width: 16px;\n  }\n\n  to {\n    transform: scale(1);\n    stroke-width: 0;\n  }\n}\n\n.circle {\n  color: rgba(166, 66, 58, .5);\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4,0,.2,1);\n}\n\n.circle.liked.animate {\n  animation: circle .3s forwards;\n}\n\n.heart {\n  width: 1.5rem;\n  height: 1.5rem;\n}\n\n.heart.liked {\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4, 0, .2, 1);\n}\n\n.heart.liked.animate {\n  animation: scale .35s ease-in-out forwards;\n}\n\n.control-icon {\n  color: hsla(0, 0%, 100%, .5);\n  filter:  drop-shadow(0 20px 13px rgba(0, 0, 0, .03)) drop-shadow(0 8px 5px rgba(0, 0, 0, .08));\n}\n\n.chevron-left {\n  margin-top: 2px;\n  rotate: 90deg;\n}\n\n\n/* Video */\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n\n.thumbnail.red {\n  background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);\n}\n\n.thumbnail.green {\n  background-image: conic-gradient(at top right, #c76a15, #388f7f, #2b3491);\n}\n\n.thumbnail.purple {\n  background-image: conic-gradient(at top right, #c76a15, #575fb7, #2b3491);\n}\n\n.thumbnail.yellow {\n  background-image: conic-gradient(at top right, #c76a15, #FABD62, #2b3491);\n}\n\n.thumbnail.gray {\n  background-image: conic-gradient(at top right, #c76a15, #4E5769, #2b3491);\n}\n\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n}\n\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n\n.video .info:hover {\n  text-decoration: underline;\n}\n\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n\n/* Details */\n.details .thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 100%;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.video-details-title {\n  margin-top: 8px;\n}\n\n.video-details-speaker {\n  display: flex;\n  gap: 8px;\n  margin-top: 10px\n}\n\n.back {\n  display: flex;\n  align-items: center;\n  margin-left: -5px;\n  cursor: pointer;\n}\n\n.back:hover {\n  text-decoration: underline;\n}\n\n.info-title {\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1.25;\n  margin: 8px 0 0 0 ;\n}\n\n.info-description {\n  margin: 8px 0 0 0;\n}\n\n.controls {\n  cursor: pointer;\n}\n\n.fallback {\n  background: #f6f7f8 linear-gradient(to right, #e6e6e6 5%, #cccccc 25%, #e6e6e6 35%) no-repeat;\n  background-size: 800px 104px;\n  display: block;\n  line-height: 1.25;\n  margin: 8px 0 0 0;\n  border-radius: 5px;\n  overflow: hidden;\n\n  animation: 1s linear 1s infinite shimmer;\n  animation-delay: 300ms;\n  animation-duration: 1s;\n  animation-fill-mode: forwards;\n  animation-iteration-count: infinite;\n  animation-name: shimmer;\n  animation-timing-function: linear;\n}\n\n\n.fallback.title {\n  width: 130px;\n  height: 30px;\n\n}\n\n.fallback.description {\n  width: 150px;\n  height: 21px;\n}\n\n@keyframes shimmer {\n  0% {\n    background-position: -468px 0;\n  }\n\n  100% {\n    background-position: 468px 0;\n  }\n}\n\n.search {\n  margin-bottom: 10px;\n}\n.search-input {\n  width: 100%;\n  position: relative;\n}\n\n.search-icon {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  inset-inline-start: 0;\n  display: flex;\n  align-items: center;\n  padding-inline-start: 1rem;\n  pointer-events: none;\n  color: #99a1b3;\n}\n\n.search-input input {\n  display: flex;\n  padding-inline-start: 2.75rem;\n  padding-top: 10px;\n  padding-bottom: 10px;\n  width: 100%;\n  text-align: start;\n  background-color: rgb(235 236 240);\n  outline: 2px solid transparent;\n  cursor: pointer;\n  border: none;\n  align-items: center;\n  color: rgb(35 39 47);\n  border-radius: 9999px;\n  vertical-align: middle;\n  font-size: 15px;\n}\n\n.search-input input:hover, .search-input input:active {\n  background-color: rgb(235 236 240/ 0.8);\n  color: rgb(35 39 47/ 0.8);\n}\n\n/* Home */\n.video-list {\n  position: relative;\n}\n\n.video-list .videos {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n  overflow-y: auto;\n  height: 100%;\n}\n```\n\n```js src/index.js hidden\nimport React, {StrictMode} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport './styles.css';\n\nimport App from './App';\nimport {Router} from './router';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <Router>\n      <App />\n    </Router>\n  </StrictMode>\n);\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\nルータのページ更新はすでに `startTransition` を使用して行われているため、`<ViewTransition>` を追加するというこの 1 行の変更だけで、デフォルトのクロスフェードアニメーションが有効になるのです。\n\nこれがどのように機能するかに興味がある場合は、ドキュメントの [`<ViewTransition>` の動作の仕組み](/reference/react/ViewTransition#how-does-viewtransition-work)を参照してください。\n\n<Note>\n\n#### `<ViewTransition>` アニメーションのオプトアウト {/*opting-out-of-viewtransition-animations*/}\n\n今回の例では単純化のため、アプリのルート (root) を `<ViewTransition>` でラップしていますが、これによりアプリ内のすべての UI 遷移がアニメーション化され、予期しないアニメーションが発生する可能性があります。\n\n修正するには、ルートの子を `\"none\"` でラップして、各ページが独自のアニメーションを制御できるようにします。\n\n```js\n// Layout.js\n<ViewTransition default=\"none\">\n  {children}\n</ViewTransition>\n```\n\n実際には、ナビゲーションは props の \"enter\" と \"exit\" を介して、または遷移タイプを使用して行うべきです。\n\n</Note>\n\n### アニメーションのカスタマイズ {/*customizing-animations*/}\n\nデフォルトでは、`<ViewTransition>` にはブラウザのデフォルトのクロスフェードが含まれています。\n\nアニメーションをカスタマイズするには、`<ViewTransition>` コンポーネントに props を渡して、[`<ViewTransition>` の起動方法](/reference/react/ViewTransition#props)に記載されているようにして使用するアニメーションを指定できます。\n\nたとえば、`default` クロスフェードアニメーションを遅くするには以下のようにします。\n\n```js\n<ViewTransition default=\"slow-fade\">\n  <Home />\n</ViewTransition>\n```\n\nその後に[ビュー遷移クラス](/reference/react/ViewTransition#view-transition-class)を用いて CSS で `slow-fade` を定義します。\n\n```css\n::view-transition-old(.slow-fade) {\n    animation-duration: 500ms;\n}\n\n::view-transition-new(.slow-fade) {\n    animation-duration: 500ms;\n}\n```\n\nこれで、クロスフェードが遅くなりました。\n\n<Sandpack>\n\n```js src/App.js active\nimport { ViewTransition } from \"react\";\nimport Details from \"./Details\";\nimport Home from \"./Home\";\nimport { useRouter } from \"./router\";\n\nexport default function App() {\n  const { url } = useRouter();\n\n  // Define a default animation of .slow-fade.\n  // See animations.css for the animation definition.\n  return (\n    <ViewTransition default=\"slow-fade\">\n      {url === '/' ? <Home /> : <Details />}\n    </ViewTransition>\n  );\n}\n```\n\n```js src/Details.js hidden\nimport { fetchVideo, fetchVideoDetails } from \"./data\";\nimport { Thumbnail, VideoControls } from \"./Videos\";\nimport { useRouter } from \"./router\";\nimport Layout from \"./Layout\";\nimport { use, Suspense } from \"react\";\nimport { ChevronLeft } from \"./Icons\";\n\nfunction VideoInfo({ id }) {\n  const details = use(fetchVideoDetails(id));\n  return (\n    <>\n      <p className=\"info-title\">{details.title}</p>\n      <p className=\"info-description\">{details.description}</p>\n    </>\n  );\n}\n\nfunction VideoInfoFallback() {\n  return (\n    <>\n      <div className=\"fallback title\"></div>\n      <div className=\"fallback description\"></div>\n    </>\n  );\n}\n\nexport default function Details() {\n  const { url, navigateBack } = useRouter();\n  const videoId = url.split(\"/\").pop();\n  const video = use(fetchVideo(videoId));\n\n  return (\n    <Layout\n      heading={\n        <div\n          className=\"fit back\"\n          onClick={() => {\n            navigateBack(\"/\");\n          }}\n        >\n          <ChevronLeft /> Back\n        </div>\n      }\n    >\n      <div className=\"details\">\n        <Thumbnail video={video} large>\n          <VideoControls />\n        </Thumbnail>\n        <Suspense fallback={<VideoInfoFallback />}>\n          <VideoInfo id={video.id} />\n        </Suspense>\n      </div>\n    </Layout>\n  );\n}\n\n```\n\n```js src/Home.js hidden\nimport { Video } from \"./Videos\";\nimport Layout from \"./Layout\";\nimport { fetchVideos } from \"./data\";\nimport { useId, useState, use } from \"react\";\nimport { IconSearch } from \"./Icons\";\n\nfunction SearchInput({ value, onChange }) {\n  const id = useId();\n  return (\n    <form className=\"search\" onSubmit={(e) => e.preventDefault()}>\n      <label htmlFor={id} className=\"sr-only\">\n        Search\n      </label>\n      <div className=\"search-input\">\n        <div className=\"search-icon\">\n          <IconSearch />\n        </div>\n        <input\n          type=\"text\"\n          id={id}\n          placeholder=\"Search\"\n          value={value}\n          onChange={(e) => onChange(e.target.value)}\n        />\n      </div>\n    </form>\n  );\n}\n\nfunction filterVideos(videos, query) {\n  const keywords = query\n    .toLowerCase()\n    .split(\" \")\n    .filter((s) => s !== \"\");\n  if (keywords.length === 0) {\n    return videos;\n  }\n  return videos.filter((video) => {\n    const words = (video.title + \" \" + video.description)\n      .toLowerCase()\n      .split(\" \");\n    return keywords.every((kw) => words.some((w) => w.includes(kw)));\n  });\n}\n\nexport default function Home() {\n  const videos = use(fetchVideos());\n  const count = videos.length;\n  const [searchText, setSearchText] = useState(\"\");\n  const foundVideos = filterVideos(videos, searchText);\n  return (\n    <Layout heading={<div className=\"fit\">{count} Videos</div>}>\n      <SearchInput value={searchText} onChange={setSearchText} />\n      <div className=\"video-list\">\n        {foundVideos.length === 0 && (\n          <div className=\"no-results\">No results</div>\n        )}\n        <div className=\"videos\">\n          {foundVideos.map((video) => (\n            <Video key={video.id} video={video} />\n          ))}\n        </div>\n      </div>\n    </Layout>\n  );\n}\n\n```\n\n```js src/Icons.js hidden\nexport function ChevronLeft() {\n  return (\n    <svg\n      className=\"chevron-left\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\">\n      <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-446 -398)\">\n        <path\n          fill=\"currentColor\"\n          fillRule=\"nonzero\"\n          d=\"M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z\"\n          transform=\"translate(356.5 164.5)\"\n        />\n        <polygon points=\"446 418 466 418 466 398 446 398\" />\n      </g>\n    </svg>\n  );\n}\n\nexport function PauseIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      style={{padding: '4px'}}\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 512 512\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M256 0C114.617 0 0 114.615 0 256s114.617 256 256 256 256-114.615 256-256S397.383 0 256 0zm-32 320c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128zm128 0c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n\nexport function PlayIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 72 72\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M36 69C54.2254 69 69 54.2254 69 36C69 17.7746 54.2254 3 36 3C17.7746 3 3 17.7746 3 36C3 54.2254 17.7746 69 36 69ZM52.1716 38.6337L28.4366 51.5801C26.4374 52.6705 24 51.2235 24 48.9464V23.0536C24 20.7764 26.4374 19.3295 28.4366 20.4199L52.1716 33.3663C54.2562 34.5034 54.2562 37.4966 52.1716 38.6337Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\nexport function Heart({liked, animate}) {\n  return (\n    <>\n      <svg\n        className=\"absolute overflow-visible\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <circle\n          className={`circle ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n          cx=\"12\"\n          cy=\"12\"\n          r=\"11.5\"\n          fill=\"transparent\"\n          strokeWidth=\"0\"\n          stroke=\"currentColor\"\n        />\n      </svg>\n\n      <svg\n        className={`heart ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        {liked ? (\n          <path\n            d=\"M12 23a.496.496 0 0 1-.26-.074C7.023 19.973 0 13.743 0 8.68c0-4.12 2.322-6.677 6.058-6.677 2.572 0 5.108 2.387 5.134 2.41l.808.771.808-.771C12.834 4.387 15.367 2 17.935 2 21.678 2 24 4.558 24 8.677c0 5.06-7.022 11.293-11.74 14.246a.496.496 0 0 1-.26.074V23z\"\n            fill=\"currentColor\"\n          />\n        ) : (\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"m12 5.184-.808-.771-.004-.004C11.065 4.299 8.522 2.003 6 2.003c-3.736 0-6 2.558-6 6.677 0 4.47 5.471 9.848 10 13.079.602.43 1.187.82 1.74 1.167A.497.497 0 0 0 12 23v-.003c.09 0 .182-.026.26-.074C16.977 19.97 24 13.737 24 8.677 24 4.557 21.743 2 18 2c-2.569 0-5.166 2.387-5.192 2.413L12 5.184zm-.002 15.525c2.071-1.388 4.477-3.342 6.427-5.47C20.72 12.733 22 10.401 22 8.677c0-1.708-.466-2.855-1.087-3.55C20.316 4.459 19.392 4 18 4c-.726 0-1.63.364-2.5.9-.67.412-1.148.82-1.266.92-.03.025-.037.031-.019.014l-.013.013L12 7.949 9.832 5.88a10.08 10.08 0 0 0-1.33-.977C7.633 4.367 6.728 4.003 6 4.003c-1.388 0-2.312.459-2.91 1.128C2.466 5.826 2 6.974 2 8.68c0 1.726 1.28 4.058 3.575 6.563 1.948 2.127 4.352 4.078 6.423 5.466z\"\n            fill=\"currentColor\"\n          />\n        )}\n      </svg>\n    </>\n  );\n}\n\nexport function IconSearch(props) {\n  return (\n    <svg width=\"1em\" height=\"1em\" viewBox=\"0 0 20 20\">\n      <path\n        d=\"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z\"\n        stroke=\"currentColor\"\n        fill=\"none\"\n        strokeWidth=\"2\"\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"></path>\n    </svg>\n  );\n}\n```\n\n```js src/Layout.js hidden\nimport {ViewTransition} from 'react'; import { useIsNavPending } from \"./router\";\n\nexport default function Page({ heading, children }) {\n  const isPending = useIsNavPending();\n\n  return (\n    <div className=\"page\">\n      <div className=\"top\">\n        <div className=\"top-nav\">\n          {heading}\n          {isPending && <span className=\"loader\"></span>}\n        </div>\n      </div>\n      {/* Opt-out of ViewTransition for the content. */}\n      {/* Content can define it's own ViewTransition. */}\n      <ViewTransition default=\"none\">\n        <div className=\"bottom\">\n          <div className=\"content\">{children}</div>\n        </div>\n      </ViewTransition>\n    </div>\n  );\n}\n```\n\n```js src/LikeButton.js hidden\nimport {useState} from 'react';\nimport {Heart} from './Icons';\n\n// A hack since we don't actually have a backend.\n// Unlike local state, this survives videos being filtered.\nconst likedVideos = new Set();\n\nexport default function LikeButton({video}) {\n  const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));\n  const [animate, setAnimate] = useState(false);\n  return (\n    <button\n      className={`like-button ${isLiked && 'liked'}`}\n      aria-label={isLiked ? 'Unsave' : 'Save'}\n      onClick={() => {\n        const nextIsLiked = !isLiked;\n        if (nextIsLiked) {\n          likedVideos.add(video.id);\n        } else {\n          likedVideos.delete(video.id);\n        }\n        setAnimate(true);\n        setIsLiked(nextIsLiked);\n      }}>\n      <Heart liked={isLiked} animate={animate} />\n    </button>\n  );\n}\n```\n\n```js src/Videos.js hidden\nimport { useState } from \"react\";\nimport LikeButton from \"./LikeButton\";\nimport { useRouter } from \"./router\";\nimport { PauseIcon, PlayIcon } from \"./Icons\";\nimport { startTransition } from \"react\";\n\nexport function VideoControls() {\n  const [isPlaying, setIsPlaying] = useState(false);\n\n  return (\n    <span\n      className=\"controls\"\n      onClick={() =>\n        startTransition(() => {\n          setIsPlaying((p) => !p);\n        })\n      }\n    >\n      {isPlaying ? <PauseIcon /> : <PlayIcon />}\n    </span>\n  );\n}\n\nexport function Thumbnail({ video, children }) {\n  return (\n    <div\n      aria-hidden=\"true\"\n      tabIndex={-1}\n      className={`thumbnail ${video.image}`}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport function Video({ video }) {\n  const { navigate } = useRouter();\n\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n        onClick={(e) => {\n          e.preventDefault();\n          navigate(`/video/${video.id}`);\n        }}\n      >\n        <Thumbnail video={video}></Thumbnail>\n\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n      <LikeButton video={video} />\n    </div>\n  );\n}\n```\n\n\n```js src/data.js hidden\nconst videos = [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  },\n  {\n    id: '2',\n    title: 'Second video',\n    description: 'Video description',\n    image: 'red',\n  },\n  {\n    id: '3',\n    title: 'Third video',\n    description: 'Video description',\n    image: 'green',\n  },\n  {\n    id: '4',\n    title: 'Fourth video',\n    description: 'Video description',\n    image: 'purple',\n  },\n  {\n    id: '5',\n    title: 'Fifth video',\n    description: 'Video description',\n    image: 'yellow',\n  },\n  {\n    id: '6',\n    title: 'Sixth video',\n    description: 'Video description',\n    image: 'gray',\n  },\n];\n\nlet videosCache = new Map();\nlet videoCache = new Map();\nlet videoDetailsCache = new Map();\nconst VIDEO_DELAY = 1;\nconst VIDEO_DETAILS_DELAY = 1000;\nexport function fetchVideos() {\n  if (videosCache.has(0)) {\n    return videosCache.get(0);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos);\n    }, VIDEO_DELAY);\n  });\n  videosCache.set(0, promise);\n  return promise;\n}\n\nexport function fetchVideo(id) {\n  if (videoCache.has(id)) {\n    return videoCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DELAY);\n  });\n  videoCache.set(id, promise);\n  return promise;\n}\n\nexport function fetchVideoDetails(id) {\n  if (videoDetailsCache.has(id)) {\n    return videoDetailsCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DETAILS_DELAY);\n  });\n  videoDetailsCache.set(id, promise);\n  return promise;\n}\n```\n\n```js src/router.js hidden\nimport {\n  useState,\n  createContext,\n  use,\n  useTransition,\n  useLayoutEffect,\n  useEffect,\n} from \"react\";\n\nconst RouterContext = createContext({ url: \"/\", params: {} });\n\nexport function useRouter() {\n  return use(RouterContext);\n}\n\nexport function useIsNavPending() {\n  return use(RouterContext).isPending;\n}\n\nexport function Router({ children }) {\n  const [routerState, setRouterState] = useState({\n    pendingNav: () => {},\n    url: document.location.pathname,\n  });\n  const [isPending, startTransition] = useTransition();\n\n  function go(url) {\n    setRouterState({\n      url,\n      pendingNav() {\n        window.history.pushState({}, \"\", url);\n      },\n    });\n  }\n  function navigate(url) {\n    // Update router state in transition.\n    startTransition(() => {\n      go(url);\n    });\n  }\n\n  function navigateBack(url) {\n    // Update router state in transition.\n    startTransition(() => {\n      go(url);\n    });\n  }\n\n  useEffect(() => {\n    function handlePopState() {\n      // This should not animate because restoration has to be synchronous.\n      // Even though it's a transition.\n      startTransition(() => {\n        setRouterState({\n          url: document.location.pathname + document.location.search,\n          pendingNav() {\n            // Noop. URL has already updated.\n          },\n        });\n      });\n    }\n    window.addEventListener(\"popstate\", handlePopState);\n    return () => {\n      window.removeEventListener(\"popstate\", handlePopState);\n    };\n  }, []);\n  const pendingNav = routerState.pendingNav;\n  useLayoutEffect(() => {\n    pendingNav();\n  }, [pendingNav]);\n\n  return (\n    <RouterContext\n      value={{\n        url: routerState.url,\n        navigate,\n        navigateBack,\n        isPending,\n        params: {},\n      }}\n    >\n      {children}\n    </RouterContext>\n  );\n}\n```\n\n```css src/styles.css hidden\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Rg.woff2) format(\"woff2\");\n  font-weight: 400;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Md.woff2) format(\"woff2\");\n  font-weight: 500;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 600;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 700;\n  font-style: normal;\n  font-display: swap;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhtml {\n  background-image: url(https://react.dev/images/meta-gradient-dark.png);\n  background-size: 100%;\n  background-position: -100%;\n  background-color: rgb(64 71 86);\n  background-repeat: no-repeat;\n  height: 100%;\n  width: 100%;\n}\n\nbody {\n  font-family: Optimistic Text, -apple-system, ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;\n  padding: 10px 0 10px 0;\n  margin: 0;\n  display: flex;\n  justify-content: center;\n}\n\n#root {\n  flex: 1 1;\n  height: auto;\n  background-color: #fff;\n  border-radius: 10px;\n  max-width: 450px;\n  min-height: 600px;\n  padding-bottom: 10px;\n}\n\nh1 {\n  margin-top: 0;\n  font-size: 22px;\n}\n\nh2 {\n  margin-top: 0;\n  font-size: 20px;\n}\n\nh3 {\n  margin-top: 0;\n  font-size: 18px;\n}\n\nh4 {\n  margin-top: 0;\n  font-size: 16px;\n}\n\nh5 {\n  margin-top: 0;\n  font-size: 14px;\n}\n\nh6 {\n  margin-top: 0;\n  font-size: 12px;\n}\n\ncode {\n  font-size: 1.2em;\n}\n\nul {\n  padding-inline-start: 20px;\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border-width: 0;\n}\n\n.absolute {\n  position: absolute;\n}\n\n.overflow-visible {\n  overflow: visible;\n}\n\n.visible {\n  overflow: visible;\n}\n\n.fit {\n  width: fit-content;\n}\n\n\n/* Layout */\n.page {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n}\n\n.top-hero {\n  height: 200px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-image: conic-gradient(\n      from 90deg at -10% 100%,\n      #2b303b 0deg,\n      #2b303b 90deg,\n      #16181d 1turn\n  );\n}\n\n.bottom {\n  flex: 1;\n  overflow: auto;\n}\n\n.top-nav {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 0;\n  padding: 0 12px;\n  top: 0;\n  width: 100%;\n  height: 44px;\n  color: #23272f;\n  font-weight: 700;\n  font-size: 20px;\n  z-index: 100;\n  cursor: default;\n}\n\n.content {\n  padding: 0 12px;\n  margin-top: 4px;\n}\n\n\n.loader {\n  color: #23272f;\n  font-size: 3px;\n  width: 1em;\n  margin-right: 18px;\n  height: 1em;\n  border-radius: 50%;\n  position: relative;\n  text-indent: -9999em;\n  animation: loading-spinner 1.3s infinite linear;\n  animation-delay: 200ms;\n  transform: translateZ(0);\n}\n\n@keyframes loading-spinner {\n  0%,\n  100% {\n    box-shadow: 0 -3em 0 0.2em,\n    2em -2em 0 0em, 3em 0 0 -1em,\n    2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 0;\n  }\n  12.5% {\n    box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em,\n    3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  25% {\n    box-shadow: 0 -3em 0 -0.5em,\n    2em -2em 0 0, 3em 0 0 0.2em,\n    2em 2em 0 0, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  37.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em,\n    -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  50% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em,\n    -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  62.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0,\n    -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em;\n  }\n  75% {\n    box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0;\n  }\n  87.5% {\n    box-shadow: 0em -3em 0 0, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;\n  }\n}\n\n/* LikeButton */\n.like-button {\n  outline-offset: 2px;\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 2.5rem;\n  height: 2.5rem;\n  cursor: pointer;\n  border-radius: 9999px;\n  border: none;\n  outline: none 2px;\n  color: #5e687e;\n  background: none;\n}\n\n.like-button:focus {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n}\n\n.like-button:active {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n  transform: scaleX(0.95) scaleY(0.95);\n}\n\n.like-button:hover {\n  background-color: #f6f7f9;\n}\n\n.like-button.liked {\n  color: #a6423a;\n}\n\n/* Icons */\n@keyframes circle {\n  0% {\n    transform: scale(0);\n    stroke-width: 16px;\n  }\n\n  50% {\n    transform: scale(.5);\n    stroke-width: 16px;\n  }\n\n  to {\n    transform: scale(1);\n    stroke-width: 0;\n  }\n}\n\n.circle {\n  color: rgba(166, 66, 58, .5);\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4,0,.2,1);\n}\n\n.circle.liked.animate {\n  animation: circle .3s forwards;\n}\n\n.heart {\n  width: 1.5rem;\n  height: 1.5rem;\n}\n\n.heart.liked {\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4, 0, .2, 1);\n}\n\n.heart.liked.animate {\n  animation: scale .35s ease-in-out forwards;\n}\n\n.control-icon {\n  color: hsla(0, 0%, 100%, .5);\n  filter:  drop-shadow(0 20px 13px rgba(0, 0, 0, .03)) drop-shadow(0 8px 5px rgba(0, 0, 0, .08));\n}\n\n.chevron-left {\n  margin-top: 2px;\n  rotate: 90deg;\n}\n\n\n/* Video */\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n\n.thumbnail.red {\n  background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);\n}\n\n.thumbnail.green {\n  background-image: conic-gradient(at top right, #c76a15, #388f7f, #2b3491);\n}\n\n.thumbnail.purple {\n  background-image: conic-gradient(at top right, #c76a15, #575fb7, #2b3491);\n}\n\n.thumbnail.yellow {\n  background-image: conic-gradient(at top right, #c76a15, #FABD62, #2b3491);\n}\n\n.thumbnail.gray {\n  background-image: conic-gradient(at top right, #c76a15, #4E5769, #2b3491);\n}\n\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n}\n\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n\n.video .info:hover {\n  text-decoration: underline;\n}\n\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n\n/* Details */\n.details .thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 100%;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.video-details-title {\n  margin-top: 8px;\n}\n\n.video-details-speaker {\n  display: flex;\n  gap: 8px;\n  margin-top: 10px\n}\n\n.back {\n  display: flex;\n  align-items: center;\n  margin-left: -5px;\n  cursor: pointer;\n}\n\n.back:hover {\n  text-decoration: underline;\n}\n\n.info-title {\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1.25;\n  margin: 8px 0 0 0 ;\n}\n\n.info-description {\n  margin: 8px 0 0 0;\n}\n\n.controls {\n  cursor: pointer;\n}\n\n.fallback {\n  background: #f6f7f8 linear-gradient(to right, #e6e6e6 5%, #cccccc 25%, #e6e6e6 35%) no-repeat;\n  background-size: 800px 104px;\n  display: block;\n  line-height: 1.25;\n  margin: 8px 0 0 0;\n  border-radius: 5px;\n  overflow: hidden;\n\n  animation: 1s linear 1s infinite shimmer;\n  animation-delay: 300ms;\n  animation-duration: 1s;\n  animation-fill-mode: forwards;\n  animation-iteration-count: infinite;\n  animation-name: shimmer;\n  animation-timing-function: linear;\n}\n\n\n.fallback.title {\n  width: 130px;\n  height: 30px;\n\n}\n\n.fallback.description {\n  width: 150px;\n  height: 21px;\n}\n\n@keyframes shimmer {\n  0% {\n    background-position: -468px 0;\n  }\n\n  100% {\n    background-position: 468px 0;\n  }\n}\n\n.search {\n  margin-bottom: 10px;\n}\n.search-input {\n  width: 100%;\n  position: relative;\n}\n\n.search-icon {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  inset-inline-start: 0;\n  display: flex;\n  align-items: center;\n  padding-inline-start: 1rem;\n  pointer-events: none;\n  color: #99a1b3;\n}\n\n.search-input input {\n  display: flex;\n  padding-inline-start: 2.75rem;\n  padding-top: 10px;\n  padding-bottom: 10px;\n  width: 100%;\n  text-align: start;\n  background-color: rgb(235 236 240);\n  outline: 2px solid transparent;\n  cursor: pointer;\n  border: none;\n  align-items: center;\n  color: rgb(35 39 47);\n  border-radius: 9999px;\n  vertical-align: middle;\n  font-size: 15px;\n}\n\n.search-input input:hover, .search-input input:active {\n  background-color: rgb(235 236 240/ 0.8);\n  color: rgb(35 39 47/ 0.8);\n}\n\n/* Home */\n.video-list {\n  position: relative;\n}\n\n.video-list .videos {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n  overflow-y: auto;\n  height: 100%;\n}\n```\n\n\n```css src/animations.css\n/* Define .slow-fade using view transition classes */\n::view-transition-old(.slow-fade) {\n    animation-duration: 500ms;\n}\n\n::view-transition-new(.slow-fade) {\n    animation-duration: 500ms;\n}\n```\n\n```js src/index.js hidden\nimport React, {StrictMode} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport './styles.css';\nimport './animations.css';\n\nimport App from './App';\nimport {Router} from './router';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <Router>\n      <App />\n    </Router>\n  </StrictMode>\n);\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\n[ビュー遷移のスタイリング](/reference/react/ViewTransition#styling-view-transitions)に、`<ViewTransition>` のスタイリングに関する完全なガイドがあります。\n\n### 共通要素の遷移 {/*shared-element-transitions*/}\n\n2 つのページに同一の要素が含まれている場合、その要素をページをまたいでアニメーションさせたいことがあります。\n\nこれを行うには、`<ViewTransition>` に一意の `name` を追加します。\n\n```js\n<ViewTransition name={`video-${video.id}`}>\n  <Thumbnail video={video} />\n</ViewTransition>\n```\n\nこれで、ビデオのサムネイルが 2 つのページをまたいでアニメーションします。\n\n<Sandpack>\n\n```js src/App.js\nimport { ViewTransition } from \"react\";\nimport Details from \"./Details\";\nimport Home from \"./Home\";\nimport { useRouter } from \"./router\";\n\nexport default function App() {\n  const { url } = useRouter();\n\n  // Keeping our default slow-fade.\n  // This allows the content not in the shared\n  // element transition to cross-fade.\n  return (\n    <ViewTransition default=\"slow-fade\">\n      {url === \"/\" ? <Home /> : <Details />}\n    </ViewTransition>\n  );\n}\n```\n\n```js src/Details.js hidden\nimport { fetchVideo, fetchVideoDetails } from \"./data\";\nimport { Thumbnail, VideoControls } from \"./Videos\";\nimport { useRouter } from \"./router\";\nimport Layout from \"./Layout\";\nimport { use, Suspense } from \"react\";\nimport { ChevronLeft } from \"./Icons\";\n\nfunction VideoInfo({ id }) {\n  const details = use(fetchVideoDetails(id));\n  return (\n    <>\n      <p className=\"info-title\">{details.title}</p>\n      <p className=\"info-description\">{details.description}</p>\n    </>\n  );\n}\n\nfunction VideoInfoFallback() {\n  return (\n    <>\n      <div className=\"fallback title\"></div>\n      <div className=\"fallback description\"></div>\n    </>\n  );\n}\n\nexport default function Details() {\n  const { url, navigateBack } = useRouter();\n  const videoId = url.split(\"/\").pop();\n  const video = use(fetchVideo(videoId));\n\n  return (\n    <Layout\n      heading={\n        <div\n          className=\"fit back\"\n          onClick={() => {\n            navigateBack(\"/\");\n          }}\n        >\n          <ChevronLeft /> Back\n        </div>\n      }\n    >\n      <div className=\"details\">\n        <Thumbnail video={video} large>\n          <VideoControls />\n        </Thumbnail>\n        <Suspense fallback={<VideoInfoFallback />}>\n          <VideoInfo id={video.id} />\n        </Suspense>\n      </div>\n    </Layout>\n  );\n}\n\n```\n\n```js src/Home.js hidden\nimport { Video } from \"./Videos\";\nimport Layout from \"./Layout\";\nimport { fetchVideos } from \"./data\";\nimport { useId, useState, use } from \"react\";\nimport { IconSearch } from \"./Icons\";\n\nfunction SearchInput({ value, onChange }) {\n  const id = useId();\n  return (\n    <form className=\"search\" onSubmit={(e) => e.preventDefault()}>\n      <label htmlFor={id} className=\"sr-only\">\n        Search\n      </label>\n      <div className=\"search-input\">\n        <div className=\"search-icon\">\n          <IconSearch />\n        </div>\n        <input\n          type=\"text\"\n          id={id}\n          placeholder=\"Search\"\n          value={value}\n          onChange={(e) => onChange(e.target.value)}\n        />\n      </div>\n    </form>\n  );\n}\n\nfunction filterVideos(videos, query) {\n  const keywords = query\n    .toLowerCase()\n    .split(\" \")\n    .filter((s) => s !== \"\");\n  if (keywords.length === 0) {\n    return videos;\n  }\n  return videos.filter((video) => {\n    const words = (video.title + \" \" + video.description)\n      .toLowerCase()\n      .split(\" \");\n    return keywords.every((kw) => words.some((w) => w.includes(kw)));\n  });\n}\n\nexport default function Home() {\n  const videos = use(fetchVideos());\n  const count = videos.length;\n  const [searchText, setSearchText] = useState(\"\");\n  const foundVideos = filterVideos(videos, searchText);\n  return (\n    <Layout heading={<div className=\"fit\">{count} Videos</div>}>\n      <SearchInput value={searchText} onChange={setSearchText} />\n      <div className=\"video-list\">\n        {foundVideos.length === 0 && (\n          <div className=\"no-results\">No results</div>\n        )}\n        <div className=\"videos\">\n          {foundVideos.map((video) => (\n            <Video key={video.id} video={video} />\n          ))}\n        </div>\n      </div>\n    </Layout>\n  );\n}\n\n```\n\n```js src/Icons.js hidden\nexport function ChevronLeft() {\n  return (\n    <svg\n      className=\"chevron-left\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\">\n      <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-446 -398)\">\n        <path\n          fill=\"currentColor\"\n          fillRule=\"nonzero\"\n          d=\"M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z\"\n          transform=\"translate(356.5 164.5)\"\n        />\n        <polygon points=\"446 418 466 418 466 398 446 398\" />\n      </g>\n    </svg>\n  );\n}\n\nexport function PauseIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      style={{padding: '4px'}}\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 512 512\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M256 0C114.617 0 0 114.615 0 256s114.617 256 256 256 256-114.615 256-256S397.383 0 256 0zm-32 320c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128zm128 0c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n\nexport function PlayIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 72 72\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M36 69C54.2254 69 69 54.2254 69 36C69 17.7746 54.2254 3 36 3C17.7746 3 3 17.7746 3 36C3 54.2254 17.7746 69 36 69ZM52.1716 38.6337L28.4366 51.5801C26.4374 52.6705 24 51.2235 24 48.9464V23.0536C24 20.7764 26.4374 19.3295 28.4366 20.4199L52.1716 33.3663C54.2562 34.5034 54.2562 37.4966 52.1716 38.6337Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\nexport function Heart({liked, animate}) {\n  return (\n    <>\n      <svg\n        className=\"absolute overflow-visible\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <circle\n          className={`circle ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n          cx=\"12\"\n          cy=\"12\"\n          r=\"11.5\"\n          fill=\"transparent\"\n          strokeWidth=\"0\"\n          stroke=\"currentColor\"\n        />\n      </svg>\n\n      <svg\n        className={`heart ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        {liked ? (\n          <path\n            d=\"M12 23a.496.496 0 0 1-.26-.074C7.023 19.973 0 13.743 0 8.68c0-4.12 2.322-6.677 6.058-6.677 2.572 0 5.108 2.387 5.134 2.41l.808.771.808-.771C12.834 4.387 15.367 2 17.935 2 21.678 2 24 4.558 24 8.677c0 5.06-7.022 11.293-11.74 14.246a.496.496 0 0 1-.26.074V23z\"\n            fill=\"currentColor\"\n          />\n        ) : (\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"m12 5.184-.808-.771-.004-.004C11.065 4.299 8.522 2.003 6 2.003c-3.736 0-6 2.558-6 6.677 0 4.47 5.471 9.848 10 13.079.602.43 1.187.82 1.74 1.167A.497.497 0 0 0 12 23v-.003c.09 0 .182-.026.26-.074C16.977 19.97 24 13.737 24 8.677 24 4.557 21.743 2 18 2c-2.569 0-5.166 2.387-5.192 2.413L12 5.184zm-.002 15.525c2.071-1.388 4.477-3.342 6.427-5.47C20.72 12.733 22 10.401 22 8.677c0-1.708-.466-2.855-1.087-3.55C20.316 4.459 19.392 4 18 4c-.726 0-1.63.364-2.5.9-.67.412-1.148.82-1.266.92-.03.025-.037.031-.019.014l-.013.013L12 7.949 9.832 5.88a10.08 10.08 0 0 0-1.33-.977C7.633 4.367 6.728 4.003 6 4.003c-1.388 0-2.312.459-2.91 1.128C2.466 5.826 2 6.974 2 8.68c0 1.726 1.28 4.058 3.575 6.563 1.948 2.127 4.352 4.078 6.423 5.466z\"\n            fill=\"currentColor\"\n          />\n        )}\n      </svg>\n    </>\n  );\n}\n\nexport function IconSearch(props) {\n  return (\n    <svg width=\"1em\" height=\"1em\" viewBox=\"0 0 20 20\">\n      <path\n        d=\"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z\"\n        stroke=\"currentColor\"\n        fill=\"none\"\n        strokeWidth=\"2\"\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"></path>\n    </svg>\n  );\n}\n```\n\n```js src/Layout.js hidden\nimport {ViewTransition} from 'react'; import { useIsNavPending } from \"./router\";\n\nexport default function Page({ heading, children }) {\n  const isPending = useIsNavPending();\n\n  return (\n    <div className=\"page\">\n      <div className=\"top\">\n        <div className=\"top-nav\">\n          {heading}\n          {isPending && <span className=\"loader\"></span>}\n        </div>\n      </div>\n      {/* Opt-out of ViewTransition for the content. */}\n      {/* Content can define it's own ViewTransition. */}\n      <ViewTransition default=\"none\">\n        <div className=\"bottom\">\n          <div className=\"content\">{children}</div>\n        </div>\n      </ViewTransition>\n    </div>\n  );\n}\n```\n\n```js src/LikeButton.js hidden\nimport {useState} from 'react';\nimport {Heart} from './Icons';\n\n// A hack since we don't actually have a backend.\n// Unlike local state, this survives videos being filtered.\nconst likedVideos = new Set();\n\nexport default function LikeButton({video}) {\n  const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));\n  const [animate, setAnimate] = useState(false);\n  return (\n    <button\n      className={`like-button ${isLiked && 'liked'}`}\n      aria-label={isLiked ? 'Unsave' : 'Save'}\n      onClick={() => {\n        const nextIsLiked = !isLiked;\n        if (nextIsLiked) {\n          likedVideos.add(video.id);\n        } else {\n          likedVideos.delete(video.id);\n        }\n        setAnimate(true);\n        setIsLiked(nextIsLiked);\n      }}>\n      <Heart liked={isLiked} animate={animate} />\n    </button>\n  );\n}\n```\n\n```js src/Videos.js active\nimport { useState, ViewTransition } from \"react\"; import LikeButton from \"./LikeButton\"; import { useRouter } from \"./router\"; import { PauseIcon, PlayIcon } from \"./Icons\"; import { startTransition } from \"react\";\n\nexport function Thumbnail({ video, children }) {\n  // Add a name to animate with a shared element transition.\n  // This uses the default animation, no additional css needed.\n  return (\n    <ViewTransition name={`video-${video.id}`}>\n      <div\n        aria-hidden=\"true\"\n        tabIndex={-1}\n        className={`thumbnail ${video.image}`}\n      >\n        {children}\n      </div>\n    </ViewTransition>\n  );\n}\n\nexport function VideoControls() {\n  const [isPlaying, setIsPlaying] = useState(false);\n\n  return (\n    <span\n      className=\"controls\"\n      onClick={() =>\n        startTransition(() => {\n          setIsPlaying((p) => !p);\n        })\n      }\n    >\n      {isPlaying ? <PauseIcon /> : <PlayIcon />}\n    </span>\n  );\n}\n\nexport function Video({ video }) {\n  const { navigate } = useRouter();\n\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n        onClick={(e) => {\n          e.preventDefault();\n          navigate(`/video/${video.id}`);\n        }}\n      >\n        <Thumbnail video={video}></Thumbnail>\n\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n      <LikeButton video={video} />\n    </div>\n  );\n}\n```\n\n\n```js src/data.js hidden\nconst videos = [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  },\n  {\n    id: '2',\n    title: 'Second video',\n    description: 'Video description',\n    image: 'red',\n  },\n  {\n    id: '3',\n    title: 'Third video',\n    description: 'Video description',\n    image: 'green',\n  },\n  {\n    id: '4',\n    title: 'Fourth video',\n    description: 'Video description',\n    image: 'purple',\n  },\n  {\n    id: '5',\n    title: 'Fifth video',\n    description: 'Video description',\n    image: 'yellow',\n  },\n  {\n    id: '6',\n    title: 'Sixth video',\n    description: 'Video description',\n    image: 'gray',\n  },\n];\n\nlet videosCache = new Map();\nlet videoCache = new Map();\nlet videoDetailsCache = new Map();\nconst VIDEO_DELAY = 1;\nconst VIDEO_DETAILS_DELAY = 1000;\nexport function fetchVideos() {\n  if (videosCache.has(0)) {\n    return videosCache.get(0);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos);\n    }, VIDEO_DELAY);\n  });\n  videosCache.set(0, promise);\n  return promise;\n}\n\nexport function fetchVideo(id) {\n  if (videoCache.has(id)) {\n    return videoCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DELAY);\n  });\n  videoCache.set(id, promise);\n  return promise;\n}\n\nexport function fetchVideoDetails(id) {\n  if (videoDetailsCache.has(id)) {\n    return videoDetailsCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DETAILS_DELAY);\n  });\n  videoDetailsCache.set(id, promise);\n  return promise;\n}\n```\n\n```js src/router.js hidden\nimport {\n  useState,\n  createContext,\n  use,\n  useTransition,\n  useLayoutEffect,\n  useEffect,\n} from \"react\";\n\nconst RouterContext = createContext({ url: \"/\", params: {} });\n\nexport function useRouter() {\n  return use(RouterContext);\n}\n\nexport function useIsNavPending() {\n  return use(RouterContext).isPending;\n}\n\nexport function Router({ children }) {\n  const [routerState, setRouterState] = useState({\n    pendingNav: () => {},\n    url: document.location.pathname,\n  });\n  const [isPending, startTransition] = useTransition();\n\n  function go(url) {\n    setRouterState({\n      url,\n      pendingNav() {\n        window.history.pushState({}, \"\", url);\n      },\n    });\n  }\n  function navigate(url) {\n    // Update router state in transition.\n    startTransition(() => {\n      go(url);\n    });\n  }\n\n  function navigateBack(url) {\n    // Update router state in transition.\n    startTransition(() => {\n      go(url);\n    });\n  }\n\n  useEffect(() => {\n    function handlePopState() {\n      // This should not animate because restoration has to be synchronous.\n      // Even though it's a transition.\n      startTransition(() => {\n        setRouterState({\n          url: document.location.pathname + document.location.search,\n          pendingNav() {\n            // Noop. URL has already updated.\n          },\n        });\n      });\n    }\n    window.addEventListener(\"popstate\", handlePopState);\n    return () => {\n      window.removeEventListener(\"popstate\", handlePopState);\n    };\n  }, []);\n  const pendingNav = routerState.pendingNav;\n  useLayoutEffect(() => {\n    pendingNav();\n  }, [pendingNav]);\n\n  return (\n    <RouterContext\n      value={{\n        url: routerState.url,\n        navigate,\n        navigateBack,\n        isPending,\n        params: {},\n      }}\n    >\n      {children}\n    </RouterContext>\n  );\n}\n```\n\n```css src/styles.css hidden\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Rg.woff2) format(\"woff2\");\n  font-weight: 400;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Md.woff2) format(\"woff2\");\n  font-weight: 500;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 600;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 700;\n  font-style: normal;\n  font-display: swap;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhtml {\n  background-image: url(https://react.dev/images/meta-gradient-dark.png);\n  background-size: 100%;\n  background-position: -100%;\n  background-color: rgb(64 71 86);\n  background-repeat: no-repeat;\n  height: 100%;\n  width: 100%;\n}\n\nbody {\n  font-family: Optimistic Text, -apple-system, ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;\n  padding: 10px 0 10px 0;\n  margin: 0;\n  display: flex;\n  justify-content: center;\n}\n\n#root {\n  flex: 1 1;\n  height: auto;\n  background-color: #fff;\n  border-radius: 10px;\n  max-width: 450px;\n  min-height: 600px;\n  padding-bottom: 10px;\n}\n\nh1 {\n  margin-top: 0;\n  font-size: 22px;\n}\n\nh2 {\n  margin-top: 0;\n  font-size: 20px;\n}\n\nh3 {\n  margin-top: 0;\n  font-size: 18px;\n}\n\nh4 {\n  margin-top: 0;\n  font-size: 16px;\n}\n\nh5 {\n  margin-top: 0;\n  font-size: 14px;\n}\n\nh6 {\n  margin-top: 0;\n  font-size: 12px;\n}\n\ncode {\n  font-size: 1.2em;\n}\n\nul {\n  padding-inline-start: 20px;\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border-width: 0;\n}\n\n.absolute {\n  position: absolute;\n}\n\n.overflow-visible {\n  overflow: visible;\n}\n\n.visible {\n  overflow: visible;\n}\n\n.fit {\n  width: fit-content;\n}\n\n\n/* Layout */\n.page {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n}\n\n.top-hero {\n  height: 200px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-image: conic-gradient(\n      from 90deg at -10% 100%,\n      #2b303b 0deg,\n      #2b303b 90deg,\n      #16181d 1turn\n  );\n}\n\n.bottom {\n  flex: 1;\n  overflow: auto;\n}\n\n.top-nav {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 0;\n  padding: 0 12px;\n  top: 0;\n  width: 100%;\n  height: 44px;\n  color: #23272f;\n  font-weight: 700;\n  font-size: 20px;\n  z-index: 100;\n  cursor: default;\n}\n\n.content {\n  padding: 0 12px;\n  margin-top: 4px;\n}\n\n\n.loader {\n  color: #23272f;\n  font-size: 3px;\n  width: 1em;\n  margin-right: 18px;\n  height: 1em;\n  border-radius: 50%;\n  position: relative;\n  text-indent: -9999em;\n  animation: loading-spinner 1.3s infinite linear;\n  animation-delay: 200ms;\n  transform: translateZ(0);\n}\n\n@keyframes loading-spinner {\n  0%,\n  100% {\n    box-shadow: 0 -3em 0 0.2em,\n    2em -2em 0 0em, 3em 0 0 -1em,\n    2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 0;\n  }\n  12.5% {\n    box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em,\n    3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  25% {\n    box-shadow: 0 -3em 0 -0.5em,\n    2em -2em 0 0, 3em 0 0 0.2em,\n    2em 2em 0 0, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  37.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em,\n    -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  50% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em,\n    -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  62.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0,\n    -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em;\n  }\n  75% {\n    box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0;\n  }\n  87.5% {\n    box-shadow: 0em -3em 0 0, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;\n  }\n}\n\n/* LikeButton */\n.like-button {\n  outline-offset: 2px;\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 2.5rem;\n  height: 2.5rem;\n  cursor: pointer;\n  border-radius: 9999px;\n  border: none;\n  outline: none 2px;\n  color: #5e687e;\n  background: none;\n}\n\n.like-button:focus {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n}\n\n.like-button:active {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n  transform: scaleX(0.95) scaleY(0.95);\n}\n\n.like-button:hover {\n  background-color: #f6f7f9;\n}\n\n.like-button.liked {\n  color: #a6423a;\n}\n\n/* Icons */\n@keyframes circle {\n  0% {\n    transform: scale(0);\n    stroke-width: 16px;\n  }\n\n  50% {\n    transform: scale(.5);\n    stroke-width: 16px;\n  }\n\n  to {\n    transform: scale(1);\n    stroke-width: 0;\n  }\n}\n\n.circle {\n  color: rgba(166, 66, 58, .5);\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4,0,.2,1);\n}\n\n.circle.liked.animate {\n  animation: circle .3s forwards;\n}\n\n.heart {\n  width: 1.5rem;\n  height: 1.5rem;\n}\n\n.heart.liked {\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4, 0, .2, 1);\n}\n\n.heart.liked.animate {\n  animation: scale .35s ease-in-out forwards;\n}\n\n.control-icon {\n  color: hsla(0, 0%, 100%, .5);\n  filter:  drop-shadow(0 20px 13px rgba(0, 0, 0, .03)) drop-shadow(0 8px 5px rgba(0, 0, 0, .08));\n}\n\n.chevron-left {\n  margin-top: 2px;\n  rotate: 90deg;\n}\n\n\n/* Video */\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n\n.thumbnail.red {\n  background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);\n}\n\n.thumbnail.green {\n  background-image: conic-gradient(at top right, #c76a15, #388f7f, #2b3491);\n}\n\n.thumbnail.purple {\n  background-image: conic-gradient(at top right, #c76a15, #575fb7, #2b3491);\n}\n\n.thumbnail.yellow {\n  background-image: conic-gradient(at top right, #c76a15, #FABD62, #2b3491);\n}\n\n.thumbnail.gray {\n  background-image: conic-gradient(at top right, #c76a15, #4E5769, #2b3491);\n}\n\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n}\n\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n\n.video .info:hover {\n  text-decoration: underline;\n}\n\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n\n/* Details */\n.details .thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 100%;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.video-details-title {\n  margin-top: 8px;\n}\n\n.video-details-speaker {\n  display: flex;\n  gap: 8px;\n  margin-top: 10px\n}\n\n.back {\n  display: flex;\n  align-items: center;\n  margin-left: -5px;\n  cursor: pointer;\n}\n\n.back:hover {\n  text-decoration: underline;\n}\n\n.info-title {\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1.25;\n  margin: 8px 0 0 0 ;\n}\n\n.info-description {\n  margin: 8px 0 0 0;\n}\n\n.controls {\n  cursor: pointer;\n}\n\n.fallback {\n  background: #f6f7f8 linear-gradient(to right, #e6e6e6 5%, #cccccc 25%, #e6e6e6 35%) no-repeat;\n  background-size: 800px 104px;\n  display: block;\n  line-height: 1.25;\n  margin: 8px 0 0 0;\n  border-radius: 5px;\n  overflow: hidden;\n\n  animation: 1s linear 1s infinite shimmer;\n  animation-delay: 300ms;\n  animation-duration: 1s;\n  animation-fill-mode: forwards;\n  animation-iteration-count: infinite;\n  animation-name: shimmer;\n  animation-timing-function: linear;\n}\n\n\n.fallback.title {\n  width: 130px;\n  height: 30px;\n\n}\n\n.fallback.description {\n  width: 150px;\n  height: 21px;\n}\n\n@keyframes shimmer {\n  0% {\n    background-position: -468px 0;\n  }\n\n  100% {\n    background-position: 468px 0;\n  }\n}\n\n.search {\n  margin-bottom: 10px;\n}\n.search-input {\n  width: 100%;\n  position: relative;\n}\n\n.search-icon {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  inset-inline-start: 0;\n  display: flex;\n  align-items: center;\n  padding-inline-start: 1rem;\n  pointer-events: none;\n  color: #99a1b3;\n}\n\n.search-input input {\n  display: flex;\n  padding-inline-start: 2.75rem;\n  padding-top: 10px;\n  padding-bottom: 10px;\n  width: 100%;\n  text-align: start;\n  background-color: rgb(235 236 240);\n  outline: 2px solid transparent;\n  cursor: pointer;\n  border: none;\n  align-items: center;\n  color: rgb(35 39 47);\n  border-radius: 9999px;\n  vertical-align: middle;\n  font-size: 15px;\n}\n\n.search-input input:hover, .search-input input:active {\n  background-color: rgb(235 236 240/ 0.8);\n  color: rgb(35 39 47/ 0.8);\n}\n\n/* Home */\n.video-list {\n  position: relative;\n}\n\n.video-list .videos {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n  overflow-y: auto;\n  height: 100%;\n}\n```\n\n\n```css src/animations.css\n/* No additional animations needed */\n\n\n\n\n\n\n\n\n\n/* Previously defined animations below */\n\n\n\n\n\n::view-transition-old(.slow-fade) {\n    animation-duration: 500ms;\n}\n\n::view-transition-new(.slow-fade) {\n    animation-duration: 500ms;\n}\n```\n\n```js src/index.js hidden\nimport React, {StrictMode} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport './styles.css';\nimport './animations.css';\n\nimport App from './App';\nimport {Router} from './router';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <Router>\n      <App />\n    </Router>\n  </StrictMode>\n);\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\nデフォルトでは、React はビュー遷移で起動された各要素に一意の `name` を自動的に生成します（[`<ViewTransition>` の動作の仕組み](/reference/react/ViewTransition#how-does-viewtransition-work)を参照）。`name` 付きの `<ViewTransition>` が削除され、新しい `<ViewTransition>` が同じ `name` で追加されるようなビュー遷移を React が検出すると、共通要素のビュー遷移 (shared element transition) が起動します。\n\n詳細については、ドキュメントで[共通要素のアニメーション](/reference/react/ViewTransition#animating-a-shared-element)を参照してください。\n\n### 起因別のアニメーション {/*animating-based-on-cause*/}\n\n場合によっては、起因 (cause) に基づいてアニメーションを変えたいことがあります。このユースケースのために、ビュー遷移の起因を指定するための新しい API である `addTransitionType` を追加しました。\n\n```js {4,11}\nfunction navigate(url) {\n  startTransition(() => {\n    // Transition type for the cause \"nav forward\"\n    addTransitionType('nav-forward');\n    go(url);\n  });\n}\nfunction navigateBack(url) {\n  startTransition(() => {\n    // Transition type for the cause \"nav backward\"\n    addTransitionType('nav-back');\n    go(url);\n  });\n}\n```\n\n遷移タイプ (transition type) を使用することで、`<ViewTransition>` に渡す props を介して、カスタムアニメーションを提供できます。ヘッダの \"6 Videos\" と \"Back\" の部分に共通要素のビュー遷移を追加してみましょう。\n\n```js {4,5}\n<ViewTransition\n  name=\"nav\"\n  share={{\n    'nav-forward': 'slide-forward',\n    'nav-back': 'slide-back',\n  }}>\n  {heading}\n</ViewTransition>\n```\n\nここでは、遷移タイプに基づいてどのようにアニメーションするかを定義するため、props として `share` にオブジェクトを渡しています。共通要素ビュー遷移が `nav-forward` によって起動した場合には、ビュー遷移クラスとして `slide-forward` が適用されます。`nav-back` による場合には、`slide-back` のアニメーションが起動します。これらのアニメーションを CSS で定義しましょう。\n\n```css\n::view-transition-old(.slide-forward) {\n    /* when sliding forward, the \"old\" page should slide out to left. */\n    animation: ...\n}\n\n::view-transition-new(.slide-forward) {\n    /* when sliding forward, the \"new\" page should slide in from right. */\n    animation: ...\n}\n\n::view-transition-old(.slide-back) {\n    /* when sliding back, the \"old\" page should slide out to right. */\n    animation: ...\n}\n\n::view-transition-new(.slide-back) {\n    /* when sliding back, the \"new\" page should slide in from left. */\n    animation: ...\n}\n```\n\nこれで、サムネールだけではなくヘッダも、ナビゲーションの種類に基づいてアニメーションできるようになりました。\n\n<Sandpack>\n\n```js src/App.js hidden\nimport { ViewTransition } from \"react\";\nimport Details from \"./Details\";\nimport Home from \"./Home\";\nimport { useRouter } from \"./router\";\n\nexport default function App() {\n  const { url } = useRouter();\n\n  // Keeping our default slow-fade.\n  return (\n    <ViewTransition default=\"slow-fade\">\n      {url === \"/\" ? <Home /> : <Details />}\n    </ViewTransition>\n  );\n}\n```\n\n```js src/Details.js hidden\nimport { fetchVideo, fetchVideoDetails } from \"./data\";\nimport { Thumbnail, VideoControls } from \"./Videos\";\nimport { useRouter } from \"./router\";\nimport Layout from \"./Layout\";\nimport { use, Suspense } from \"react\";\nimport { ChevronLeft } from \"./Icons\";\n\nfunction VideoInfo({ id }) {\n  const details = use(fetchVideoDetails(id));\n  return (\n    <>\n      <p className=\"info-title\">{details.title}</p>\n      <p className=\"info-description\">{details.description}</p>\n    </>\n  );\n}\n\nfunction VideoInfoFallback() {\n  return (\n    <>\n      <div className=\"fallback title\"></div>\n      <div className=\"fallback description\"></div>\n    </>\n  );\n}\n\nexport default function Details() {\n  const { url, navigateBack } = useRouter();\n  const videoId = url.split(\"/\").pop();\n  const video = use(fetchVideo(videoId));\n\n  return (\n    <Layout\n      heading={\n        <div\n          className=\"fit back\"\n          onClick={() => {\n            navigateBack(\"/\");\n          }}\n        >\n          <ChevronLeft /> Back\n        </div>\n      }\n    >\n      <div className=\"details\">\n        <Thumbnail video={video} large>\n          <VideoControls />\n        </Thumbnail>\n        <Suspense fallback={<VideoInfoFallback />}>\n          <VideoInfo id={video.id} />\n        </Suspense>\n      </div>\n    </Layout>\n  );\n}\n\n```\n\n```js src/Home.js hidden\nimport { Video } from \"./Videos\";\nimport Layout from \"./Layout\";\nimport { fetchVideos } from \"./data\";\nimport { useId, useState, use } from \"react\";\nimport { IconSearch } from \"./Icons\";\n\nfunction SearchInput({ value, onChange }) {\n  const id = useId();\n  return (\n    <form className=\"search\" onSubmit={(e) => e.preventDefault()}>\n      <label htmlFor={id} className=\"sr-only\">\n        Search\n      </label>\n      <div className=\"search-input\">\n        <div className=\"search-icon\">\n          <IconSearch />\n        </div>\n        <input\n          type=\"text\"\n          id={id}\n          placeholder=\"Search\"\n          value={value}\n          onChange={(e) => onChange(e.target.value)}\n        />\n      </div>\n    </form>\n  );\n}\n\nfunction filterVideos(videos, query) {\n  const keywords = query\n    .toLowerCase()\n    .split(\" \")\n    .filter((s) => s !== \"\");\n  if (keywords.length === 0) {\n    return videos;\n  }\n  return videos.filter((video) => {\n    const words = (video.title + \" \" + video.description)\n      .toLowerCase()\n      .split(\" \");\n    return keywords.every((kw) => words.some((w) => w.includes(kw)));\n  });\n}\n\nexport default function Home() {\n  const videos = use(fetchVideos());\n  const count = videos.length;\n  const [searchText, setSearchText] = useState(\"\");\n  const foundVideos = filterVideos(videos, searchText);\n  return (\n    <Layout heading={<div className=\"fit\">{count} Videos</div>}>\n      <SearchInput value={searchText} onChange={setSearchText} />\n      <div className=\"video-list\">\n        {foundVideos.length === 0 && (\n          <div className=\"no-results\">No results</div>\n        )}\n        <div className=\"videos\">\n          {foundVideos.map((video) => (\n            <Video key={video.id} video={video} />\n          ))}\n        </div>\n      </div>\n    </Layout>\n  );\n}\n\n```\n\n```js src/Icons.js hidden\nexport function ChevronLeft() {\n  return (\n    <svg\n      className=\"chevron-left\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\">\n      <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-446 -398)\">\n        <path\n          fill=\"currentColor\"\n          fillRule=\"nonzero\"\n          d=\"M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z\"\n          transform=\"translate(356.5 164.5)\"\n        />\n        <polygon points=\"446 418 466 418 466 398 446 398\" />\n      </g>\n    </svg>\n  );\n}\n\nexport function PauseIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      style={{padding: '4px'}}\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 512 512\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M256 0C114.617 0 0 114.615 0 256s114.617 256 256 256 256-114.615 256-256S397.383 0 256 0zm-32 320c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128zm128 0c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n\nexport function PlayIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 72 72\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M36 69C54.2254 69 69 54.2254 69 36C69 17.7746 54.2254 3 36 3C17.7746 3 3 17.7746 3 36C3 54.2254 17.7746 69 36 69ZM52.1716 38.6337L28.4366 51.5801C26.4374 52.6705 24 51.2235 24 48.9464V23.0536C24 20.7764 26.4374 19.3295 28.4366 20.4199L52.1716 33.3663C54.2562 34.5034 54.2562 37.4966 52.1716 38.6337Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\nexport function Heart({liked, animate}) {\n  return (\n    <>\n      <svg\n        className=\"absolute overflow-visible\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <circle\n          className={`circle ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n          cx=\"12\"\n          cy=\"12\"\n          r=\"11.5\"\n          fill=\"transparent\"\n          strokeWidth=\"0\"\n          stroke=\"currentColor\"\n        />\n      </svg>\n\n      <svg\n        className={`heart ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        {liked ? (\n          <path\n            d=\"M12 23a.496.496 0 0 1-.26-.074C7.023 19.973 0 13.743 0 8.68c0-4.12 2.322-6.677 6.058-6.677 2.572 0 5.108 2.387 5.134 2.41l.808.771.808-.771C12.834 4.387 15.367 2 17.935 2 21.678 2 24 4.558 24 8.677c0 5.06-7.022 11.293-11.74 14.246a.496.496 0 0 1-.26.074V23z\"\n            fill=\"currentColor\"\n          />\n        ) : (\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"m12 5.184-.808-.771-.004-.004C11.065 4.299 8.522 2.003 6 2.003c-3.736 0-6 2.558-6 6.677 0 4.47 5.471 9.848 10 13.079.602.43 1.187.82 1.74 1.167A.497.497 0 0 0 12 23v-.003c.09 0 .182-.026.26-.074C16.977 19.97 24 13.737 24 8.677 24 4.557 21.743 2 18 2c-2.569 0-5.166 2.387-5.192 2.413L12 5.184zm-.002 15.525c2.071-1.388 4.477-3.342 6.427-5.47C20.72 12.733 22 10.401 22 8.677c0-1.708-.466-2.855-1.087-3.55C20.316 4.459 19.392 4 18 4c-.726 0-1.63.364-2.5.9-.67.412-1.148.82-1.266.92-.03.025-.037.031-.019.014l-.013.013L12 7.949 9.832 5.88a10.08 10.08 0 0 0-1.33-.977C7.633 4.367 6.728 4.003 6 4.003c-1.388 0-2.312.459-2.91 1.128C2.466 5.826 2 6.974 2 8.68c0 1.726 1.28 4.058 3.575 6.563 1.948 2.127 4.352 4.078 6.423 5.466z\"\n            fill=\"currentColor\"\n          />\n        )}\n      </svg>\n    </>\n  );\n}\n\nexport function IconSearch(props) {\n  return (\n    <svg width=\"1em\" height=\"1em\" viewBox=\"0 0 20 20\">\n      <path\n        d=\"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z\"\n        stroke=\"currentColor\"\n        fill=\"none\"\n        strokeWidth=\"2\"\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"></path>\n    </svg>\n  );\n}\n```\n\n```js src/Layout.js active\nimport {ViewTransition} from 'react'; import { useIsNavPending } from \"./router\";\n\nexport default function Page({ heading, children }) {\n  const isPending = useIsNavPending();\n  return (\n    <div className=\"page\">\n      <div className=\"top\">\n        <div className=\"top-nav\">\n          {/* Custom classes based on transition type. */}\n          <ViewTransition\n            name=\"nav\"\n            share={{\n              'nav-forward': 'slide-forward',\n              'nav-back': 'slide-back',\n            }}>\n            {heading}\n          </ViewTransition>\n          {isPending && <span className=\"loader\"></span>}\n        </div>\n      </div>\n      {/* Opt-out of ViewTransition for the content. */}\n      {/* Content can define it's own ViewTransition. */}\n      <ViewTransition default=\"none\">\n        <div className=\"bottom\">\n          <div className=\"content\">{children}</div>\n        </div>\n      </ViewTransition>\n    </div>\n  );\n}\n```\n\n```js src/LikeButton.js hidden\nimport {useState} from 'react';\nimport {Heart} from './Icons';\n\n// A hack since we don't actually have a backend.\n// Unlike local state, this survives videos being filtered.\nconst likedVideos = new Set();\n\nexport default function LikeButton({video}) {\n  const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));\n  const [animate, setAnimate] = useState(false);\n  return (\n    <button\n      className={`like-button ${isLiked && 'liked'}`}\n      aria-label={isLiked ? 'Unsave' : 'Save'}\n      onClick={() => {\n        const nextIsLiked = !isLiked;\n        if (nextIsLiked) {\n          likedVideos.add(video.id);\n        } else {\n          likedVideos.delete(video.id);\n        }\n        setAnimate(true);\n        setIsLiked(nextIsLiked);\n      }}>\n      <Heart liked={isLiked} animate={animate} />\n    </button>\n  );\n}\n```\n\n```js src/Videos.js hidden\nimport { useState, ViewTransition } from \"react\";\nimport LikeButton from \"./LikeButton\";\nimport { useRouter } from \"./router\";\nimport { PauseIcon, PlayIcon } from \"./Icons\";\nimport { startTransition } from \"react\";\n\nexport function Thumbnail({ video, children }) {\n  // Add a name to animate with a shared element transition.\n  // This uses the default animation, no additional css needed.\n  return (\n    <ViewTransition name={`video-${video.id}`}>\n      <div\n        aria-hidden=\"true\"\n        tabIndex={-1}\n        className={`thumbnail ${video.image}`}\n      >\n        {children}\n      </div>\n    </ViewTransition>\n  );\n}\n\nexport function VideoControls() {\n  const [isPlaying, setIsPlaying] = useState(false);\n\n  return (\n    <span\n      className=\"controls\"\n      onClick={() =>\n        startTransition(() => {\n          setIsPlaying((p) => !p);\n        })\n      }\n    >\n      {isPlaying ? <PauseIcon /> : <PlayIcon />}\n    </span>\n  );\n}\n\nexport function Video({ video }) {\n  const { navigate } = useRouter();\n\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n        onClick={(e) => {\n          e.preventDefault();\n          navigate(`/video/${video.id}`);\n        }}\n      >\n        <Thumbnail video={video}></Thumbnail>\n\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n      <LikeButton video={video} />\n    </div>\n  );\n}\n```\n\n\n```js src/data.js hidden\nconst videos = [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  },\n  {\n    id: '2',\n    title: 'Second video',\n    description: 'Video description',\n    image: 'red',\n  },\n  {\n    id: '3',\n    title: 'Third video',\n    description: 'Video description',\n    image: 'green',\n  },\n  {\n    id: '4',\n    title: 'Fourth video',\n    description: 'Video description',\n    image: 'purple',\n  },\n  {\n    id: '5',\n    title: 'Fifth video',\n    description: 'Video description',\n    image: 'yellow',\n  },\n  {\n    id: '6',\n    title: 'Sixth video',\n    description: 'Video description',\n    image: 'gray',\n  },\n];\n\nlet videosCache = new Map();\nlet videoCache = new Map();\nlet videoDetailsCache = new Map();\nconst VIDEO_DELAY = 1;\nconst VIDEO_DETAILS_DELAY = 1000;\nexport function fetchVideos() {\n  if (videosCache.has(0)) {\n    return videosCache.get(0);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos);\n    }, VIDEO_DELAY);\n  });\n  videosCache.set(0, promise);\n  return promise;\n}\n\nexport function fetchVideo(id) {\n  if (videoCache.has(id)) {\n    return videoCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DELAY);\n  });\n  videoCache.set(id, promise);\n  return promise;\n}\n\nexport function fetchVideoDetails(id) {\n  if (videoDetailsCache.has(id)) {\n    return videoDetailsCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DETAILS_DELAY);\n  });\n  videoDetailsCache.set(id, promise);\n  return promise;\n}\n```\n\n```js src/router.js\nimport {useState, createContext, use, useTransition, useLayoutEffect, useEffect, addTransitionType} from \"react\";\n\nexport function Router({ children }) {\n  const [isPending, startTransition] = useTransition();\n\n  function navigate(url) {\n    startTransition(() => {\n      // Transition type for the cause \"nav forward\"\n      addTransitionType('nav-forward');\n      go(url);\n    });\n  }\n  function navigateBack(url) {\n    startTransition(() => {\n      // Transition type for the cause \"nav backward\"\n      addTransitionType('nav-back');\n      go(url);\n    });\n  }\n\n\n  const [routerState, setRouterState] = useState({pendingNav: () => {}, url: document.location.pathname});\n\n  function go(url) {\n    setRouterState({\n      url,\n      pendingNav() {\n        window.history.pushState({}, \"\", url);\n      },\n    });\n  }\n\n  useEffect(() => {\n    function handlePopState() {\n      // This should not animate because restoration has to be synchronous.\n      // Even though it's a transition.\n      startTransition(() => {\n        setRouterState({\n          url: document.location.pathname + document.location.search,\n          pendingNav() {\n            // Noop. URL has already updated.\n          },\n        });\n      });\n    }\n    window.addEventListener(\"popstate\", handlePopState);\n    return () => {\n      window.removeEventListener(\"popstate\", handlePopState);\n    };\n  }, []);\n  const pendingNav = routerState.pendingNav;\n  useLayoutEffect(() => {\n    pendingNav();\n  }, [pendingNav]);\n\n  return (\n    <RouterContext\n      value={{\n        url: routerState.url,\n        navigate,\n        navigateBack,\n        isPending,\n        params: {},\n      }}\n    >\n      {children}\n    </RouterContext>\n  );\n}\n\nconst RouterContext = createContext({ url: \"/\", params: {} });\n\nexport function useRouter() {\n  return use(RouterContext);\n}\n\nexport function useIsNavPending() {\n  return use(RouterContext).isPending;\n}\n\n```\n\n```css src/styles.css hidden\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Rg.woff2) format(\"woff2\");\n  font-weight: 400;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Md.woff2) format(\"woff2\");\n  font-weight: 500;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 600;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 700;\n  font-style: normal;\n  font-display: swap;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhtml {\n  background-image: url(https://react.dev/images/meta-gradient-dark.png);\n  background-size: 100%;\n  background-position: -100%;\n  background-color: rgb(64 71 86);\n  background-repeat: no-repeat;\n  height: 100%;\n  width: 100%;\n}\n\nbody {\n  font-family: Optimistic Text, -apple-system, ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;\n  padding: 10px 0 10px 0;\n  margin: 0;\n  display: flex;\n  justify-content: center;\n}\n\n#root {\n  flex: 1 1;\n  height: auto;\n  background-color: #fff;\n  border-radius: 10px;\n  max-width: 450px;\n  min-height: 600px;\n  padding-bottom: 10px;\n}\n\nh1 {\n  margin-top: 0;\n  font-size: 22px;\n}\n\nh2 {\n  margin-top: 0;\n  font-size: 20px;\n}\n\nh3 {\n  margin-top: 0;\n  font-size: 18px;\n}\n\nh4 {\n  margin-top: 0;\n  font-size: 16px;\n}\n\nh5 {\n  margin-top: 0;\n  font-size: 14px;\n}\n\nh6 {\n  margin-top: 0;\n  font-size: 12px;\n}\n\ncode {\n  font-size: 1.2em;\n}\n\nul {\n  padding-inline-start: 20px;\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border-width: 0;\n}\n\n.absolute {\n  position: absolute;\n}\n\n.overflow-visible {\n  overflow: visible;\n}\n\n.visible {\n  overflow: visible;\n}\n\n.fit {\n  width: fit-content;\n}\n\n\n/* Layout */\n.page {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n}\n\n.top-hero {\n  height: 200px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-image: conic-gradient(\n      from 90deg at -10% 100%,\n      #2b303b 0deg,\n      #2b303b 90deg,\n      #16181d 1turn\n  );\n}\n\n.bottom {\n  flex: 1;\n  overflow: auto;\n}\n\n.top-nav {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 0;\n  padding: 0 12px;\n  top: 0;\n  width: 100%;\n  height: 44px;\n  color: #23272f;\n  font-weight: 700;\n  font-size: 20px;\n  z-index: 100;\n  cursor: default;\n}\n\n.content {\n  padding: 0 12px;\n  margin-top: 4px;\n}\n\n\n.loader {\n  color: #23272f;\n  font-size: 3px;\n  width: 1em;\n  margin-right: 18px;\n  height: 1em;\n  border-radius: 50%;\n  position: relative;\n  text-indent: -9999em;\n  animation: loading-spinner 1.3s infinite linear;\n  animation-delay: 200ms;\n  transform: translateZ(0);\n}\n\n@keyframes loading-spinner {\n  0%,\n  100% {\n    box-shadow: 0 -3em 0 0.2em,\n    2em -2em 0 0em, 3em 0 0 -1em,\n    2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 0;\n  }\n  12.5% {\n    box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em,\n    3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  25% {\n    box-shadow: 0 -3em 0 -0.5em,\n    2em -2em 0 0, 3em 0 0 0.2em,\n    2em 2em 0 0, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  37.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em,\n    -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  50% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em,\n    -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  62.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0,\n    -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em;\n  }\n  75% {\n    box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0;\n  }\n  87.5% {\n    box-shadow: 0em -3em 0 0, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;\n  }\n}\n\n/* LikeButton */\n.like-button {\n  outline-offset: 2px;\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 2.5rem;\n  height: 2.5rem;\n  cursor: pointer;\n  border-radius: 9999px;\n  border: none;\n  outline: none 2px;\n  color: #5e687e;\n  background: none;\n}\n\n.like-button:focus {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n}\n\n.like-button:active {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n  transform: scaleX(0.95) scaleY(0.95);\n}\n\n.like-button:hover {\n  background-color: #f6f7f9;\n}\n\n.like-button.liked {\n  color: #a6423a;\n}\n\n/* Icons */\n@keyframes circle {\n  0% {\n    transform: scale(0);\n    stroke-width: 16px;\n  }\n\n  50% {\n    transform: scale(.5);\n    stroke-width: 16px;\n  }\n\n  to {\n    transform: scale(1);\n    stroke-width: 0;\n  }\n}\n\n.circle {\n  color: rgba(166, 66, 58, .5);\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4,0,.2,1);\n}\n\n.circle.liked.animate {\n  animation: circle .3s forwards;\n}\n\n.heart {\n  width: 1.5rem;\n  height: 1.5rem;\n}\n\n.heart.liked {\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4, 0, .2, 1);\n}\n\n.heart.liked.animate {\n  animation: scale .35s ease-in-out forwards;\n}\n\n.control-icon {\n  color: hsla(0, 0%, 100%, .5);\n  filter:  drop-shadow(0 20px 13px rgba(0, 0, 0, .03)) drop-shadow(0 8px 5px rgba(0, 0, 0, .08));\n}\n\n.chevron-left {\n  margin-top: 2px;\n  rotate: 90deg;\n}\n\n\n/* Video */\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n\n.thumbnail.red {\n  background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);\n}\n\n.thumbnail.green {\n  background-image: conic-gradient(at top right, #c76a15, #388f7f, #2b3491);\n}\n\n.thumbnail.purple {\n  background-image: conic-gradient(at top right, #c76a15, #575fb7, #2b3491);\n}\n\n.thumbnail.yellow {\n  background-image: conic-gradient(at top right, #c76a15, #FABD62, #2b3491);\n}\n\n.thumbnail.gray {\n  background-image: conic-gradient(at top right, #c76a15, #4E5769, #2b3491);\n}\n\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n}\n\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n\n.video .info:hover {\n  text-decoration: underline;\n}\n\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n\n/* Details */\n.details .thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 100%;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.video-details-title {\n  margin-top: 8px;\n}\n\n.video-details-speaker {\n  display: flex;\n  gap: 8px;\n  margin-top: 10px\n}\n\n.back {\n  display: flex;\n  align-items: center;\n  margin-left: -5px;\n  cursor: pointer;\n}\n\n.back:hover {\n  text-decoration: underline;\n}\n\n.info-title {\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1.25;\n  margin: 8px 0 0 0 ;\n}\n\n.info-description {\n  margin: 8px 0 0 0;\n}\n\n.controls {\n  cursor: pointer;\n}\n\n.fallback {\n  background: #f6f7f8 linear-gradient(to right, #e6e6e6 5%, #cccccc 25%, #e6e6e6 35%) no-repeat;\n  background-size: 800px 104px;\n  display: block;\n  line-height: 1.25;\n  margin: 8px 0 0 0;\n  border-radius: 5px;\n  overflow: hidden;\n\n  animation: 1s linear 1s infinite shimmer;\n  animation-delay: 300ms;\n  animation-duration: 1s;\n  animation-fill-mode: forwards;\n  animation-iteration-count: infinite;\n  animation-name: shimmer;\n  animation-timing-function: linear;\n}\n\n\n.fallback.title {\n  width: 130px;\n  height: 30px;\n\n}\n\n.fallback.description {\n  width: 150px;\n  height: 21px;\n}\n\n@keyframes shimmer {\n  0% {\n    background-position: -468px 0;\n  }\n\n  100% {\n    background-position: 468px 0;\n  }\n}\n\n.search {\n  margin-bottom: 10px;\n}\n.search-input {\n  width: 100%;\n  position: relative;\n}\n\n.search-icon {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  inset-inline-start: 0;\n  display: flex;\n  align-items: center;\n  padding-inline-start: 1rem;\n  pointer-events: none;\n  color: #99a1b3;\n}\n\n.search-input input {\n  display: flex;\n  padding-inline-start: 2.75rem;\n  padding-top: 10px;\n  padding-bottom: 10px;\n  width: 100%;\n  text-align: start;\n  background-color: rgb(235 236 240);\n  outline: 2px solid transparent;\n  cursor: pointer;\n  border: none;\n  align-items: center;\n  color: rgb(35 39 47);\n  border-radius: 9999px;\n  vertical-align: middle;\n  font-size: 15px;\n}\n\n.search-input input:hover, .search-input input:active {\n  background-color: rgb(235 236 240/ 0.8);\n  color: rgb(35 39 47/ 0.8);\n}\n\n/* Home */\n.video-list {\n  position: relative;\n}\n\n.video-list .videos {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n  overflow-y: auto;\n  height: 100%;\n}\n```\n\n\n```css src/animations.css\n/* Animations for view transition classed added by transition type */\n::view-transition-old(.slide-forward) {\n    /* when sliding forward, the \"old\" page should slide out to left. */\n    animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;\n}\n\n::view-transition-new(.slide-forward) {\n    /* when sliding forward, the \"new\" page should slide in from right. */\n    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;\n}\n\n::view-transition-old(.slide-back) {\n    /* when sliding back, the \"old\" page should slide out to right. */\n    animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;\n}\n\n::view-transition-new(.slide-back) {\n    /* when sliding back, the \"new\" page should slide in from left. */\n    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-left;\n}\n\n/* New keyframes to support our animations above. */\n@keyframes fade-in {\n    from {\n        opacity: 0;\n    }\n}\n\n@keyframes fade-out {\n    to {\n        opacity: 0;\n    }\n}\n\n@keyframes slide-to-right {\n    to {\n        transform: translateX(50px);\n    }\n}\n\n@keyframes slide-from-right {\n    from {\n        transform: translateX(50px);\n    }\n    to {\n        transform: translateX(0);\n    }\n}\n\n@keyframes slide-to-left {\n    to {\n        transform: translateX(-50px);\n    }\n}\n\n@keyframes slide-from-left {\n    from {\n        transform: translateX(-50px);\n    }\n    to {\n        transform: translateX(0);\n    }\n}\n\n/* Previously defined animations. */\n\n/* Default .slow-fade. */\n::view-transition-old(.slow-fade) {\n    animation-duration: 500ms;\n}\n\n::view-transition-new(.slow-fade) {\n    animation-duration: 500ms;\n}\n```\n\n```js src/index.js hidden\nimport React, {StrictMode} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport './styles.css';\nimport './animations.css';\n\nimport App from './App';\nimport {Router} from './router';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <Router>\n      <App />\n    </Router>\n  </StrictMode>\n);\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\n### サスペンスバウンダリのアニメーション {/*animating-suspense-boundaries*/}\n\nサスペンスもビュー遷移を起動できます。\n\nフォールバックからコンテンツへのアニメーションを行うには、`<ViewTransition>` で `Suspense` をラップします。\n\n```js\n<ViewTransition>\n  <Suspense fallback={<VideoInfoFallback />}>\n    <VideoInfo />\n  </Suspense>\n</ViewTransition>\n```\n\nこれを追加することで、フォールバックからコンテンツにクロスフェードするようになります。ビデオをクリックし、ビデオ情報がアニメーションで表示されるようになったことを確認してください。\n\n<Sandpack>\n\n```js src/App.js hidden\nimport { ViewTransition } from \"react\";\nimport Details from \"./Details\";\nimport Home from \"./Home\";\nimport { useRouter } from \"./router\";\n\nexport default function App() {\n  const { url } = useRouter();\n\n  // Default slow-fade animation.\n  return (\n    <ViewTransition default=\"slow-fade\">\n      {url === \"/\" ? <Home /> : <Details />}\n    </ViewTransition>\n  );\n}\n```\n\n```js src/Details.js active\nimport { use, Suspense, ViewTransition } from \"react\"; import { fetchVideo, fetchVideoDetails } from \"./data\"; import { Thumbnail, VideoControls } from \"./Videos\"; import { useRouter } from \"./router\"; import Layout from \"./Layout\"; import { ChevronLeft } from \"./Icons\";\n\nfunction VideoDetails({ id }) {\n  // Cross-fade the fallback to content.\n  return (\n    <ViewTransition default=\"slow-fade\">\n      <Suspense fallback={<VideoInfoFallback />}>\n          <VideoInfo id={id} />\n      </Suspense>\n    </ViewTransition>\n  );\n}\n\nfunction VideoInfoFallback() {\n  return (\n    <div>\n      <div className=\"fit fallback title\"></div>\n      <div className=\"fit fallback description\"></div>\n    </div>\n  );\n}\n\nexport default function Details() {\n  const { url, navigateBack } = useRouter();\n  const videoId = url.split(\"/\").pop();\n  const video = use(fetchVideo(videoId));\n\n  return (\n    <Layout\n      heading={\n        <div\n          className=\"fit back\"\n          onClick={() => {\n            navigateBack(\"/\");\n          }}\n        >\n          <ChevronLeft /> Back\n        </div>\n      }\n    >\n      <div className=\"details\">\n        <Thumbnail video={video} large>\n          <VideoControls />\n        </Thumbnail>\n        <VideoDetails id={video.id} />\n      </div>\n    </Layout>\n  );\n}\n\nfunction VideoInfo({ id }) {\n  const details = use(fetchVideoDetails(id));\n  return (\n    <div>\n      <p className=\"fit info-title\">{details.title}</p>\n      <p className=\"fit info-description\">{details.description}</p>\n    </div>\n  );\n}\n```\n\n```js src/Home.js hidden\nimport { Video } from \"./Videos\";\nimport Layout from \"./Layout\";\nimport { fetchVideos } from \"./data\";\nimport { useId, useState, use } from \"react\";\nimport { IconSearch } from \"./Icons\";\n\nfunction SearchInput({ value, onChange }) {\n  const id = useId();\n  return (\n    <form className=\"search\" onSubmit={(e) => e.preventDefault()}>\n      <label htmlFor={id} className=\"sr-only\">\n        Search\n      </label>\n      <div className=\"search-input\">\n        <div className=\"search-icon\">\n          <IconSearch />\n        </div>\n        <input\n          type=\"text\"\n          id={id}\n          placeholder=\"Search\"\n          value={value}\n          onChange={(e) => onChange(e.target.value)}\n        />\n      </div>\n    </form>\n  );\n}\n\nfunction filterVideos(videos, query) {\n  const keywords = query\n    .toLowerCase()\n    .split(\" \")\n    .filter((s) => s !== \"\");\n  if (keywords.length === 0) {\n    return videos;\n  }\n  return videos.filter((video) => {\n    const words = (video.title + \" \" + video.description)\n      .toLowerCase()\n      .split(\" \");\n    return keywords.every((kw) => words.some((w) => w.includes(kw)));\n  });\n}\n\nexport default function Home() {\n  const videos = use(fetchVideos());\n  const count = videos.length;\n  const [searchText, setSearchText] = useState(\"\");\n  const foundVideos = filterVideos(videos, searchText);\n  return (\n    <Layout heading={<div className=\"fit\">{count} Videos</div>}>\n      <SearchInput value={searchText} onChange={setSearchText} />\n      <div className=\"video-list\">\n        {foundVideos.length === 0 && (\n          <div className=\"no-results\">No results</div>\n        )}\n        <div className=\"videos\">\n          {foundVideos.map((video) => (\n            <Video key={video.id} video={video} />\n          ))}\n        </div>\n      </div>\n    </Layout>\n  );\n}\n\n```\n\n```js src/Icons.js hidden\nexport function ChevronLeft() {\n  return (\n    <svg\n      className=\"chevron-left\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\">\n      <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-446 -398)\">\n        <path\n          fill=\"currentColor\"\n          fillRule=\"nonzero\"\n          d=\"M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z\"\n          transform=\"translate(356.5 164.5)\"\n        />\n        <polygon points=\"446 418 466 418 466 398 446 398\" />\n      </g>\n    </svg>\n  );\n}\n\nexport function PauseIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      style={{padding: '4px'}}\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 512 512\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M256 0C114.617 0 0 114.615 0 256s114.617 256 256 256 256-114.615 256-256S397.383 0 256 0zm-32 320c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128zm128 0c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n\nexport function PlayIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 72 72\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M36 69C54.2254 69 69 54.2254 69 36C69 17.7746 54.2254 3 36 3C17.7746 3 3 17.7746 3 36C3 54.2254 17.7746 69 36 69ZM52.1716 38.6337L28.4366 51.5801C26.4374 52.6705 24 51.2235 24 48.9464V23.0536C24 20.7764 26.4374 19.3295 28.4366 20.4199L52.1716 33.3663C54.2562 34.5034 54.2562 37.4966 52.1716 38.6337Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\nexport function Heart({liked, animate}) {\n  return (\n    <>\n      <svg\n        className=\"absolute overflow-visible\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <circle\n          className={`circle ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n          cx=\"12\"\n          cy=\"12\"\n          r=\"11.5\"\n          fill=\"transparent\"\n          strokeWidth=\"0\"\n          stroke=\"currentColor\"\n        />\n      </svg>\n\n      <svg\n        className={`heart ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        {liked ? (\n          <path\n            d=\"M12 23a.496.496 0 0 1-.26-.074C7.023 19.973 0 13.743 0 8.68c0-4.12 2.322-6.677 6.058-6.677 2.572 0 5.108 2.387 5.134 2.41l.808.771.808-.771C12.834 4.387 15.367 2 17.935 2 21.678 2 24 4.558 24 8.677c0 5.06-7.022 11.293-11.74 14.246a.496.496 0 0 1-.26.074V23z\"\n            fill=\"currentColor\"\n          />\n        ) : (\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"m12 5.184-.808-.771-.004-.004C11.065 4.299 8.522 2.003 6 2.003c-3.736 0-6 2.558-6 6.677 0 4.47 5.471 9.848 10 13.079.602.43 1.187.82 1.74 1.167A.497.497 0 0 0 12 23v-.003c.09 0 .182-.026.26-.074C16.977 19.97 24 13.737 24 8.677 24 4.557 21.743 2 18 2c-2.569 0-5.166 2.387-5.192 2.413L12 5.184zm-.002 15.525c2.071-1.388 4.477-3.342 6.427-5.47C20.72 12.733 22 10.401 22 8.677c0-1.708-.466-2.855-1.087-3.55C20.316 4.459 19.392 4 18 4c-.726 0-1.63.364-2.5.9-.67.412-1.148.82-1.266.92-.03.025-.037.031-.019.014l-.013.013L12 7.949 9.832 5.88a10.08 10.08 0 0 0-1.33-.977C7.633 4.367 6.728 4.003 6 4.003c-1.388 0-2.312.459-2.91 1.128C2.466 5.826 2 6.974 2 8.68c0 1.726 1.28 4.058 3.575 6.563 1.948 2.127 4.352 4.078 6.423 5.466z\"\n            fill=\"currentColor\"\n          />\n        )}\n      </svg>\n    </>\n  );\n}\n\nexport function IconSearch(props) {\n  return (\n    <svg width=\"1em\" height=\"1em\" viewBox=\"0 0 20 20\">\n      <path\n        d=\"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z\"\n        stroke=\"currentColor\"\n        fill=\"none\"\n        strokeWidth=\"2\"\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"></path>\n    </svg>\n  );\n}\n```\n\n```js src/Layout.js hidden\nimport {ViewTransition} from 'react';\nimport { useIsNavPending } from \"./router\";\n\nexport default function Page({ heading, children }) {\n  const isPending = useIsNavPending();\n  return (\n    <div className=\"page\">\n      <div className=\"top\">\n        <div className=\"top-nav\">\n          {/* Custom classes based on transition type. */}\n          <ViewTransition\n            name=\"nav\"\n            share={{\n              'nav-forward': 'slide-forward',\n              'nav-back': 'slide-back',\n            }}>\n            {heading}\n          </ViewTransition>\n          {isPending && <span className=\"loader\"></span>}\n        </div>\n      </div>\n      {/* Opt-out of ViewTransition for the content. */}\n      {/* Content can define it's own ViewTransition. */}\n      <ViewTransition default=\"none\">\n        <div className=\"bottom\">\n          <div className=\"content\">{children}</div>\n        </div>\n      </ViewTransition>\n    </div>\n  );\n}\n```\n\n```js src/LikeButton.js hidden\nimport {useState} from 'react';\nimport {Heart} from './Icons';\n\n// A hack since we don't actually have a backend.\n// Unlike local state, this survives videos being filtered.\nconst likedVideos = new Set();\n\nexport default function LikeButton({video}) {\n  const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));\n  const [animate, setAnimate] = useState(false);\n  return (\n    <button\n      className={`like-button ${isLiked && 'liked'}`}\n      aria-label={isLiked ? 'Unsave' : 'Save'}\n      onClick={() => {\n        const nextIsLiked = !isLiked;\n        if (nextIsLiked) {\n          likedVideos.add(video.id);\n        } else {\n          likedVideos.delete(video.id);\n        }\n        setAnimate(true);\n        setIsLiked(nextIsLiked);\n      }}>\n      <Heart liked={isLiked} animate={animate} />\n    </button>\n  );\n}\n```\n\n```js src/Videos.js hidden\nimport { useState, ViewTransition } from \"react\";\nimport LikeButton from \"./LikeButton\";\nimport { useRouter } from \"./router\";\nimport { PauseIcon, PlayIcon } from \"./Icons\";\nimport { startTransition } from \"react\";\n\nexport function Thumbnail({ video, children }) {\n  // Add a name to animate with a shared element transition.\n  // This uses the default animation, no additional css needed.\n  return (\n    <ViewTransition name={`video-${video.id}`}>\n      <div\n        aria-hidden=\"true\"\n        tabIndex={-1}\n        className={`thumbnail ${video.image}`}\n      >\n        {children}\n      </div>\n    </ViewTransition>\n  );\n}\n\nexport function VideoControls() {\n  const [isPlaying, setIsPlaying] = useState(false);\n\n  return (\n    <span\n      className=\"controls\"\n      onClick={() =>\n        startTransition(() => {\n          setIsPlaying((p) => !p);\n        })\n      }\n    >\n      {isPlaying ? <PauseIcon /> : <PlayIcon />}\n    </span>\n  );\n}\n\nexport function Video({ video }) {\n  const { navigate } = useRouter();\n\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n        onClick={(e) => {\n          e.preventDefault();\n          navigate(`/video/${video.id}`);\n        }}\n      >\n        <Thumbnail video={video}></Thumbnail>\n\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n      <LikeButton video={video} />\n    </div>\n  );\n}\n```\n\n\n```js src/data.js hidden\nconst videos = [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  },\n  {\n    id: '2',\n    title: 'Second video',\n    description: 'Video description',\n    image: 'red',\n  },\n  {\n    id: '3',\n    title: 'Third video',\n    description: 'Video description',\n    image: 'green',\n  },\n  {\n    id: '4',\n    title: 'Fourth video',\n    description: 'Video description',\n    image: 'purple',\n  },\n  {\n    id: '5',\n    title: 'Fifth video',\n    description: 'Video description',\n    image: 'yellow',\n  },\n  {\n    id: '6',\n    title: 'Sixth video',\n    description: 'Video description',\n    image: 'gray',\n  },\n];\n\nlet videosCache = new Map();\nlet videoCache = new Map();\nlet videoDetailsCache = new Map();\nconst VIDEO_DELAY = 1;\nconst VIDEO_DETAILS_DELAY = 1000;\nexport function fetchVideos() {\n  if (videosCache.has(0)) {\n    return videosCache.get(0);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos);\n    }, VIDEO_DELAY);\n  });\n  videosCache.set(0, promise);\n  return promise;\n}\n\nexport function fetchVideo(id) {\n  if (videoCache.has(id)) {\n    return videoCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DELAY);\n  });\n  videoCache.set(id, promise);\n  return promise;\n}\n\nexport function fetchVideoDetails(id) {\n  if (videoDetailsCache.has(id)) {\n    return videoDetailsCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DETAILS_DELAY);\n  });\n  videoDetailsCache.set(id, promise);\n  return promise;\n}\n```\n\n```js src/router.js hidden\nimport {useState, createContext, use, useTransition, useLayoutEffect, useEffect, addTransitionType} from \"react\";\n\nexport function Router({ children }) {\n  const [isPending, startTransition] = useTransition();\n  const [routerState, setRouterState] = useState({pendingNav: () => {}, url: document.location.pathname});\n  function navigate(url) {\n    startTransition(() => {\n      // Transition type for the cause \"nav forward\"\n      addTransitionType('nav-forward');\n      go(url);\n    });\n  }\n  function navigateBack(url) {\n    startTransition(() => {\n      // Transition type for the cause \"nav backward\"\n      addTransitionType('nav-back');\n      go(url);\n    });\n  }\n\n  function go(url) {\n    setRouterState({\n      url,\n      pendingNav() {\n        window.history.pushState({}, \"\", url);\n      },\n    });\n  }\n\n  useEffect(() => {\n    function handlePopState() {\n      // This should not animate because restoration has to be synchronous.\n      // Even though it's a transition.\n      startTransition(() => {\n        setRouterState({\n          url: document.location.pathname + document.location.search,\n          pendingNav() {\n            // Noop. URL has already updated.\n          },\n        });\n      });\n    }\n    window.addEventListener(\"popstate\", handlePopState);\n    return () => {\n      window.removeEventListener(\"popstate\", handlePopState);\n    };\n  }, []);\n  const pendingNav = routerState.pendingNav;\n  useLayoutEffect(() => {\n    pendingNav();\n  }, [pendingNav]);\n\n  return (\n    <RouterContext\n      value={{\n        url: routerState.url,\n        navigate,\n        navigateBack,\n        isPending,\n        params: {},\n      }}\n    >\n      {children}\n    </RouterContext>\n  );\n}\n\nconst RouterContext = createContext({ url: \"/\", params: {} });\n\nexport function useRouter() {\n  return use(RouterContext);\n}\n\nexport function useIsNavPending() {\n  return use(RouterContext).isPending;\n}\n\n```\n\n```css src/styles.css hidden\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Rg.woff2) format(\"woff2\");\n  font-weight: 400;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Md.woff2) format(\"woff2\");\n  font-weight: 500;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 600;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 700;\n  font-style: normal;\n  font-display: swap;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhtml {\n  background-image: url(https://react.dev/images/meta-gradient-dark.png);\n  background-size: 100%;\n  background-position: -100%;\n  background-color: rgb(64 71 86);\n  background-repeat: no-repeat;\n  height: 100%;\n  width: 100%;\n}\n\nbody {\n  font-family: Optimistic Text, -apple-system, ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;\n  padding: 10px 0 10px 0;\n  margin: 0;\n  display: flex;\n  justify-content: center;\n}\n\n#root {\n  flex: 1 1;\n  height: auto;\n  background-color: #fff;\n  border-radius: 10px;\n  max-width: 450px;\n  min-height: 600px;\n  padding-bottom: 10px;\n}\n\nh1 {\n  margin-top: 0;\n  font-size: 22px;\n}\n\nh2 {\n  margin-top: 0;\n  font-size: 20px;\n}\n\nh3 {\n  margin-top: 0;\n  font-size: 18px;\n}\n\nh4 {\n  margin-top: 0;\n  font-size: 16px;\n}\n\nh5 {\n  margin-top: 0;\n  font-size: 14px;\n}\n\nh6 {\n  margin-top: 0;\n  font-size: 12px;\n}\n\ncode {\n  font-size: 1.2em;\n}\n\nul {\n  padding-inline-start: 20px;\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border-width: 0;\n}\n\n.absolute {\n  position: absolute;\n}\n\n.overflow-visible {\n  overflow: visible;\n}\n\n.visible {\n  overflow: visible;\n}\n\n.fit {\n  width: fit-content;\n}\n\n\n/* Layout */\n.page {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n}\n\n.top-hero {\n  height: 200px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-image: conic-gradient(\n      from 90deg at -10% 100%,\n      #2b303b 0deg,\n      #2b303b 90deg,\n      #16181d 1turn\n  );\n}\n\n.bottom {\n  flex: 1;\n  overflow: auto;\n}\n\n.top-nav {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 0;\n  padding: 0 12px;\n  top: 0;\n  width: 100%;\n  height: 44px;\n  color: #23272f;\n  font-weight: 700;\n  font-size: 20px;\n  z-index: 100;\n  cursor: default;\n}\n\n.content {\n  padding: 0 12px;\n  margin-top: 4px;\n}\n\n\n.loader {\n  color: #23272f;\n  font-size: 3px;\n  width: 1em;\n  margin-right: 18px;\n  height: 1em;\n  border-radius: 50%;\n  position: relative;\n  text-indent: -9999em;\n  animation: loading-spinner 1.3s infinite linear;\n  animation-delay: 200ms;\n  transform: translateZ(0);\n}\n\n@keyframes loading-spinner {\n  0%,\n  100% {\n    box-shadow: 0 -3em 0 0.2em,\n    2em -2em 0 0em, 3em 0 0 -1em,\n    2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 0;\n  }\n  12.5% {\n    box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em,\n    3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  25% {\n    box-shadow: 0 -3em 0 -0.5em,\n    2em -2em 0 0, 3em 0 0 0.2em,\n    2em 2em 0 0, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  37.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em,\n    -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  50% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em,\n    -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  62.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0,\n    -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em;\n  }\n  75% {\n    box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0;\n  }\n  87.5% {\n    box-shadow: 0em -3em 0 0, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;\n  }\n}\n\n/* LikeButton */\n.like-button {\n  outline-offset: 2px;\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 2.5rem;\n  height: 2.5rem;\n  cursor: pointer;\n  border-radius: 9999px;\n  border: none;\n  outline: none 2px;\n  color: #5e687e;\n  background: none;\n}\n\n.like-button:focus {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n}\n\n.like-button:active {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n  transform: scaleX(0.95) scaleY(0.95);\n}\n\n.like-button:hover {\n  background-color: #f6f7f9;\n}\n\n.like-button.liked {\n  color: #a6423a;\n}\n\n/* Icons */\n@keyframes circle {\n  0% {\n    transform: scale(0);\n    stroke-width: 16px;\n  }\n\n  50% {\n    transform: scale(.5);\n    stroke-width: 16px;\n  }\n\n  to {\n    transform: scale(1);\n    stroke-width: 0;\n  }\n}\n\n.circle {\n  color: rgba(166, 66, 58, .5);\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4,0,.2,1);\n}\n\n.circle.liked.animate {\n  animation: circle .3s forwards;\n}\n\n.heart {\n  width: 1.5rem;\n  height: 1.5rem;\n}\n\n.heart.liked {\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4, 0, .2, 1);\n}\n\n.heart.liked.animate {\n  animation: scale .35s ease-in-out forwards;\n}\n\n.control-icon {\n  color: hsla(0, 0%, 100%, .5);\n  filter:  drop-shadow(0 20px 13px rgba(0, 0, 0, .03)) drop-shadow(0 8px 5px rgba(0, 0, 0, .08));\n}\n\n.chevron-left {\n  margin-top: 2px;\n  rotate: 90deg;\n}\n\n\n/* Video */\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n\n.thumbnail.red {\n  background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);\n}\n\n.thumbnail.green {\n  background-image: conic-gradient(at top right, #c76a15, #388f7f, #2b3491);\n}\n\n.thumbnail.purple {\n  background-image: conic-gradient(at top right, #c76a15, #575fb7, #2b3491);\n}\n\n.thumbnail.yellow {\n  background-image: conic-gradient(at top right, #c76a15, #FABD62, #2b3491);\n}\n\n.thumbnail.gray {\n  background-image: conic-gradient(at top right, #c76a15, #4E5769, #2b3491);\n}\n\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n}\n\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n\n.video .info:hover {\n  text-decoration: underline;\n}\n\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n\n/* Details */\n.details .thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 100%;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.video-details-title {\n  margin-top: 8px;\n}\n\n.video-details-speaker {\n  display: flex;\n  gap: 8px;\n  margin-top: 10px\n}\n\n.back {\n  display: flex;\n  align-items: center;\n  margin-left: -5px;\n  cursor: pointer;\n}\n\n.back:hover {\n  text-decoration: underline;\n}\n\n.info-title {\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1.25;\n  margin: 8px 0 0 0 ;\n}\n\n.info-description {\n  margin: 8px 0 0 0;\n}\n\n.controls {\n  cursor: pointer;\n}\n\n.fallback {\n  background: #f6f7f8 linear-gradient(to right, #e6e6e6 5%, #cccccc 25%, #e6e6e6 35%) no-repeat;\n  background-size: 800px 104px;\n  display: block;\n  line-height: 1.25;\n  margin: 8px 0 0 0;\n  border-radius: 5px;\n  overflow: hidden;\n\n  animation: 1s linear 1s infinite shimmer;\n  animation-delay: 300ms;\n  animation-duration: 1s;\n  animation-fill-mode: forwards;\n  animation-iteration-count: infinite;\n  animation-name: shimmer;\n  animation-timing-function: linear;\n}\n\n\n.fallback.title {\n  width: 130px;\n  height: 30px;\n\n}\n\n.fallback.description {\n  width: 150px;\n  height: 21px;\n}\n\n@keyframes shimmer {\n  0% {\n    background-position: -468px 0;\n  }\n\n  100% {\n    background-position: 468px 0;\n  }\n}\n\n.search {\n  margin-bottom: 10px;\n}\n.search-input {\n  width: 100%;\n  position: relative;\n}\n\n.search-icon {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  inset-inline-start: 0;\n  display: flex;\n  align-items: center;\n  padding-inline-start: 1rem;\n  pointer-events: none;\n  color: #99a1b3;\n}\n\n.search-input input {\n  display: flex;\n  padding-inline-start: 2.75rem;\n  padding-top: 10px;\n  padding-bottom: 10px;\n  width: 100%;\n  text-align: start;\n  background-color: rgb(235 236 240);\n  outline: 2px solid transparent;\n  cursor: pointer;\n  border: none;\n  align-items: center;\n  color: rgb(35 39 47);\n  border-radius: 9999px;\n  vertical-align: middle;\n  font-size: 15px;\n}\n\n.search-input input:hover, .search-input input:active {\n  background-color: rgb(235 236 240/ 0.8);\n  color: rgb(35 39 47/ 0.8);\n}\n\n/* Home */\n.video-list {\n  position: relative;\n}\n\n.video-list .videos {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n  overflow-y: auto;\n  height: 100%;\n}\n```\n\n\n```css src/animations.css\n/* Slide the fallback down */\n::view-transition-old(.slide-down) {\n    animation: 150ms ease-out both fade-out, 150ms ease-out both slide-down;\n}\n\n/* Slide the content up */\n::view-transition-new(.slide-up) {\n    animation: 210ms ease-in 150ms both fade-in, 400ms ease-in both slide-up;\n}\n\n/* Define the new keyframes */\n@keyframes slide-up {\n    from {\n        transform: translateY(10px);\n    }\n    to {\n        transform: translateY(0);\n    }\n}\n\n@keyframes slide-down {\n    from {\n        transform: translateY(0);\n    }\n    to {\n        transform: translateY(10px);\n    }\n}\n\n/* Previously defined animations below */\n\n/* Animations for view transition classed added by transition type */\n::view-transition-old(.slide-forward) {\n    /* when sliding forward, the \"old\" page should slide out to left. */\n    animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;\n}\n\n::view-transition-new(.slide-forward) {\n    /* when sliding forward, the \"new\" page should slide in from right. */\n    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;\n}\n\n::view-transition-old(.slide-back) {\n    /* when sliding back, the \"old\" page should slide out to right. */\n    animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;\n}\n\n::view-transition-new(.slide-back) {\n    /* when sliding back, the \"new\" page should slide in from left. */\n    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-left;\n}\n\n/* Keyframes to support our animations above. */\n@keyframes fade-in {\n    from {\n        opacity: 0;\n    }\n}\n\n@keyframes fade-out {\n    to {\n        opacity: 0;\n    }\n}\n\n@keyframes slide-to-right {\n    to {\n        transform: translateX(50px);\n    }\n}\n\n@keyframes slide-from-right {\n    from {\n        transform: translateX(50px);\n    }\n    to {\n        transform: translateX(0);\n    }\n}\n\n@keyframes slide-to-left {\n    to {\n        transform: translateX(-50px);\n    }\n}\n\n@keyframes slide-from-left {\n    from {\n        transform: translateX(-50px);\n    }\n    to {\n        transform: translateX(0);\n    }\n}\n\n/* Default .slow-fade. */\n::view-transition-old(.slow-fade) {\n    animation-duration: 500ms;\n}\n\n::view-transition-new(.slow-fade) {\n    animation-duration: 500ms;\n}\n```\n\n```js src/index.js hidden\nimport React, {StrictMode} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport './styles.css';\nimport './animations.css';\n\nimport App from './App';\nimport {Router} from './router';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <Router>\n      <App />\n    </Router>\n  </StrictMode>\n);\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\nまた、フォールバック側に `exit` を、コンテンツ側に `enter` を使用して、カスタムアニメーションを提供することもできます。\n\n```js {3,8}\n<Suspense\n  fallback={\n    <ViewTransition exit=\"slide-down\">\n      <VideoInfoFallback />\n    </ViewTransition>\n  }\n>\n  <ViewTransition enter=\"slide-up\">\n    <VideoInfo id={id} />\n  </ViewTransition>\n</Suspense>\n```\n\n以下のようにして CSS を使用して `slide-down` と `slide-up` を定義します。\n\n```css {1, 6}\n::view-transition-old(.slide-down) {\n  /* Slide the fallback down */\n  animation: ...;\n}\n\n::view-transition-new(.slide-up) {\n  /* Slide the content up */\n  animation: ...;\n}\n```\n\nこれで、サスペンス内のコンテンツがフォールバックを置き換える際にスライドアニメーションが使われます。\n\n<Sandpack>\n\n```js src/App.js hidden\nimport { ViewTransition } from \"react\";\nimport Details from \"./Details\";\nimport Home from \"./Home\";\nimport { useRouter } from \"./router\";\n\nexport default function App() {\n  const { url } = useRouter();\n\n  // Default slow-fade animation.\n  return (\n    <ViewTransition default=\"slow-fade\">\n      {url === \"/\" ? <Home /> : <Details />}\n    </ViewTransition>\n  );\n}\n```\n\n```js src/Details.js active\nimport { use, Suspense, ViewTransition } from \"react\"; import { fetchVideo, fetchVideoDetails } from \"./data\"; import { Thumbnail, VideoControls } from \"./Videos\"; import { useRouter } from \"./router\"; import Layout from \"./Layout\"; import { ChevronLeft } from \"./Icons\";\n\nfunction VideoDetails({ id }) {\n  return (\n    <Suspense\n      fallback={\n        // Animate the fallback down.\n        <ViewTransition exit=\"slide-down\">\n          <VideoInfoFallback />\n        </ViewTransition>\n      }\n    >\n      {/* Animate the content up */}\n      <ViewTransition enter=\"slide-up\">\n        <VideoInfo id={id} />\n      </ViewTransition>\n    </Suspense>\n  );\n}\n\nfunction VideoInfoFallback() {\n  return (\n    <>\n      <div className=\"fallback title\"></div>\n      <div className=\"fallback description\"></div>\n    </>\n  );\n}\n\nexport default function Details() {\n  const { url, navigateBack } = useRouter();\n  const videoId = url.split(\"/\").pop();\n  const video = use(fetchVideo(videoId));\n\n  return (\n    <Layout\n      heading={\n        <div\n          className=\"fit back\"\n          onClick={() => {\n            navigateBack(\"/\");\n          }}\n        >\n          <ChevronLeft /> Back\n        </div>\n      }\n    >\n      <div className=\"details\">\n        <Thumbnail video={video} large>\n          <VideoControls />\n        </Thumbnail>\n        <VideoDetails id={video.id} />\n      </div>\n    </Layout>\n  );\n}\n\nfunction VideoInfo({ id }) {\n  const details = use(fetchVideoDetails(id));\n  return (\n    <>\n      <p className=\"info-title\">{details.title}</p>\n      <p className=\"info-description\">{details.description}</p>\n    </>\n  );\n}\n```\n\n```js src/Home.js hidden\nimport { Video } from \"./Videos\";\nimport Layout from \"./Layout\";\nimport { fetchVideos } from \"./data\";\nimport { useId, useState, use } from \"react\";\nimport { IconSearch } from \"./Icons\";\n\nfunction SearchInput({ value, onChange }) {\n  const id = useId();\n  return (\n    <form className=\"search\" onSubmit={(e) => e.preventDefault()}>\n      <label htmlFor={id} className=\"sr-only\">\n        Search\n      </label>\n      <div className=\"search-input\">\n        <div className=\"search-icon\">\n          <IconSearch />\n        </div>\n        <input\n          type=\"text\"\n          id={id}\n          placeholder=\"Search\"\n          value={value}\n          onChange={(e) => onChange(e.target.value)}\n        />\n      </div>\n    </form>\n  );\n}\n\nfunction filterVideos(videos, query) {\n  const keywords = query\n    .toLowerCase()\n    .split(\" \")\n    .filter((s) => s !== \"\");\n  if (keywords.length === 0) {\n    return videos;\n  }\n  return videos.filter((video) => {\n    const words = (video.title + \" \" + video.description)\n      .toLowerCase()\n      .split(\" \");\n    return keywords.every((kw) => words.some((w) => w.includes(kw)));\n  });\n}\n\nexport default function Home() {\n  const videos = use(fetchVideos());\n  const count = videos.length;\n  const [searchText, setSearchText] = useState(\"\");\n  const foundVideos = filterVideos(videos, searchText);\n  return (\n    <Layout heading={<div className=\"fit\">{count} Videos</div>}>\n      <SearchInput value={searchText} onChange={setSearchText} />\n      <div className=\"video-list\">\n        {foundVideos.length === 0 && (\n          <div className=\"no-results\">No results</div>\n        )}\n        <div className=\"videos\">\n          {foundVideos.map((video) => (\n            <Video key={video.id} video={video} />\n          ))}\n        </div>\n      </div>\n    </Layout>\n  );\n}\n\n```\n\n```js src/Icons.js hidden\nexport function ChevronLeft() {\n  return (\n    <svg\n      className=\"chevron-left\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\">\n      <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-446 -398)\">\n        <path\n          fill=\"currentColor\"\n          fillRule=\"nonzero\"\n          d=\"M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z\"\n          transform=\"translate(356.5 164.5)\"\n        />\n        <polygon points=\"446 418 466 418 466 398 446 398\" />\n      </g>\n    </svg>\n  );\n}\n\nexport function PauseIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      style={{padding: '4px'}}\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 512 512\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M256 0C114.617 0 0 114.615 0 256s114.617 256 256 256 256-114.615 256-256S397.383 0 256 0zm-32 320c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128zm128 0c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n\nexport function PlayIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 72 72\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M36 69C54.2254 69 69 54.2254 69 36C69 17.7746 54.2254 3 36 3C17.7746 3 3 17.7746 3 36C3 54.2254 17.7746 69 36 69ZM52.1716 38.6337L28.4366 51.5801C26.4374 52.6705 24 51.2235 24 48.9464V23.0536C24 20.7764 26.4374 19.3295 28.4366 20.4199L52.1716 33.3663C54.2562 34.5034 54.2562 37.4966 52.1716 38.6337Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\nexport function Heart({liked, animate}) {\n  return (\n    <>\n      <svg\n        className=\"absolute overflow-visible\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <circle\n          className={`circle ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n          cx=\"12\"\n          cy=\"12\"\n          r=\"11.5\"\n          fill=\"transparent\"\n          strokeWidth=\"0\"\n          stroke=\"currentColor\"\n        />\n      </svg>\n\n      <svg\n        className={`heart ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        {liked ? (\n          <path\n            d=\"M12 23a.496.496 0 0 1-.26-.074C7.023 19.973 0 13.743 0 8.68c0-4.12 2.322-6.677 6.058-6.677 2.572 0 5.108 2.387 5.134 2.41l.808.771.808-.771C12.834 4.387 15.367 2 17.935 2 21.678 2 24 4.558 24 8.677c0 5.06-7.022 11.293-11.74 14.246a.496.496 0 0 1-.26.074V23z\"\n            fill=\"currentColor\"\n          />\n        ) : (\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"m12 5.184-.808-.771-.004-.004C11.065 4.299 8.522 2.003 6 2.003c-3.736 0-6 2.558-6 6.677 0 4.47 5.471 9.848 10 13.079.602.43 1.187.82 1.74 1.167A.497.497 0 0 0 12 23v-.003c.09 0 .182-.026.26-.074C16.977 19.97 24 13.737 24 8.677 24 4.557 21.743 2 18 2c-2.569 0-5.166 2.387-5.192 2.413L12 5.184zm-.002 15.525c2.071-1.388 4.477-3.342 6.427-5.47C20.72 12.733 22 10.401 22 8.677c0-1.708-.466-2.855-1.087-3.55C20.316 4.459 19.392 4 18 4c-.726 0-1.63.364-2.5.9-.67.412-1.148.82-1.266.92-.03.025-.037.031-.019.014l-.013.013L12 7.949 9.832 5.88a10.08 10.08 0 0 0-1.33-.977C7.633 4.367 6.728 4.003 6 4.003c-1.388 0-2.312.459-2.91 1.128C2.466 5.826 2 6.974 2 8.68c0 1.726 1.28 4.058 3.575 6.563 1.948 2.127 4.352 4.078 6.423 5.466z\"\n            fill=\"currentColor\"\n          />\n        )}\n      </svg>\n    </>\n  );\n}\n\nexport function IconSearch(props) {\n  return (\n    <svg width=\"1em\" height=\"1em\" viewBox=\"0 0 20 20\">\n      <path\n        d=\"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z\"\n        stroke=\"currentColor\"\n        fill=\"none\"\n        strokeWidth=\"2\"\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"></path>\n    </svg>\n  );\n}\n```\n\n```js src/Layout.js hidden\nimport {ViewTransition} from 'react';\nimport { useIsNavPending } from \"./router\";\n\nexport default function Page({ heading, children }) {\n  const isPending = useIsNavPending();\n  return (\n    <div className=\"page\">\n      <div className=\"top\">\n        <div className=\"top-nav\">\n          {/* Custom classes based on transition type. */}\n          <ViewTransition\n            name=\"nav\"\n            share={{\n              'nav-forward': 'slide-forward',\n              'nav-back': 'slide-back',\n            }}>\n            {heading}\n          </ViewTransition>\n          {isPending && <span className=\"loader\"></span>}\n        </div>\n      </div>\n      {/* Opt-out of ViewTransition for the content. */}\n      {/* Content can define it's own ViewTransition. */}\n      <ViewTransition default=\"none\">\n        <div className=\"bottom\">\n          <div className=\"content\">{children}</div>\n        </div>\n      </ViewTransition>\n    </div>\n  );\n}\n```\n\n```js src/LikeButton.js hidden\nimport {useState} from 'react';\nimport {Heart} from './Icons';\n\n// A hack since we don't actually have a backend.\n// Unlike local state, this survives videos being filtered.\nconst likedVideos = new Set();\n\nexport default function LikeButton({video}) {\n  const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));\n  const [animate, setAnimate] = useState(false);\n  return (\n    <button\n      className={`like-button ${isLiked && 'liked'}`}\n      aria-label={isLiked ? 'Unsave' : 'Save'}\n      onClick={() => {\n        const nextIsLiked = !isLiked;\n        if (nextIsLiked) {\n          likedVideos.add(video.id);\n        } else {\n          likedVideos.delete(video.id);\n        }\n        setAnimate(true);\n        setIsLiked(nextIsLiked);\n      }}>\n      <Heart liked={isLiked} animate={animate} />\n    </button>\n  );\n}\n```\n\n```js src/Videos.js hidden\nimport { useState, ViewTransition } from \"react\";\nimport LikeButton from \"./LikeButton\";\nimport { useRouter } from \"./router\";\nimport { PauseIcon, PlayIcon } from \"./Icons\";\nimport { startTransition } from \"react\";\n\nexport function Thumbnail({ video, children }) {\n  // Add a name to animate with a shared element transition.\n  // This uses the default animation, no additional css needed.\n  return (\n    <ViewTransition name={`video-${video.id}`}>\n      <div\n        aria-hidden=\"true\"\n        tabIndex={-1}\n        className={`thumbnail ${video.image}`}\n      >\n        {children}\n      </div>\n    </ViewTransition>\n  );\n}\n\nexport function VideoControls() {\n  const [isPlaying, setIsPlaying] = useState(false);\n\n  return (\n    <span\n      className=\"controls\"\n      onClick={() =>\n        startTransition(() => {\n          setIsPlaying((p) => !p);\n        })\n      }\n    >\n      {isPlaying ? <PauseIcon /> : <PlayIcon />}\n    </span>\n  );\n}\n\nexport function Video({ video }) {\n  const { navigate } = useRouter();\n\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n        onClick={(e) => {\n          e.preventDefault();\n          navigate(`/video/${video.id}`);\n        }}\n      >\n        <Thumbnail video={video}></Thumbnail>\n\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n      <LikeButton video={video} />\n    </div>\n  );\n}\n```\n\n\n```js src/data.js hidden\nconst videos = [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  },\n  {\n    id: '2',\n    title: 'Second video',\n    description: 'Video description',\n    image: 'red',\n  },\n  {\n    id: '3',\n    title: 'Third video',\n    description: 'Video description',\n    image: 'green',\n  },\n  {\n    id: '4',\n    title: 'Fourth video',\n    description: 'Video description',\n    image: 'purple',\n  },\n  {\n    id: '5',\n    title: 'Fifth video',\n    description: 'Video description',\n    image: 'yellow',\n  },\n  {\n    id: '6',\n    title: 'Sixth video',\n    description: 'Video description',\n    image: 'gray',\n  },\n];\n\nlet videosCache = new Map();\nlet videoCache = new Map();\nlet videoDetailsCache = new Map();\nconst VIDEO_DELAY = 1;\nconst VIDEO_DETAILS_DELAY = 1000;\nexport function fetchVideos() {\n  if (videosCache.has(0)) {\n    return videosCache.get(0);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos);\n    }, VIDEO_DELAY);\n  });\n  videosCache.set(0, promise);\n  return promise;\n}\n\nexport function fetchVideo(id) {\n  if (videoCache.has(id)) {\n    return videoCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DELAY);\n  });\n  videoCache.set(id, promise);\n  return promise;\n}\n\nexport function fetchVideoDetails(id) {\n  if (videoDetailsCache.has(id)) {\n    return videoDetailsCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DETAILS_DELAY);\n  });\n  videoDetailsCache.set(id, promise);\n  return promise;\n}\n```\n\n```js src/router.js hidden\nimport {useState, createContext, use, useTransition, useLayoutEffect, useEffect, addTransitionType} from \"react\";\n\nexport function Router({ children }) {\n  const [isPending, startTransition] = useTransition();\n  const [routerState, setRouterState] = useState({pendingNav: () => {}, url: document.location.pathname});\n  function navigate(url) {\n    startTransition(() => {\n      // Transition type for the cause \"nav forward\"\n      addTransitionType('nav-forward');\n      go(url);\n    });\n  }\n  function navigateBack(url) {\n    startTransition(() => {\n      // Transition type for the cause \"nav backward\"\n      addTransitionType('nav-back');\n      go(url);\n    });\n  }\n\n  function go(url) {\n    setRouterState({\n      url,\n      pendingNav() {\n        window.history.pushState({}, \"\", url);\n      },\n    });\n  }\n\n  useEffect(() => {\n    function handlePopState() {\n      // This should not animate because restoration has to be synchronous.\n      // Even though it's a transition.\n      startTransition(() => {\n        setRouterState({\n          url: document.location.pathname + document.location.search,\n          pendingNav() {\n            // Noop. URL has already updated.\n          },\n        });\n      });\n    }\n    window.addEventListener(\"popstate\", handlePopState);\n    return () => {\n      window.removeEventListener(\"popstate\", handlePopState);\n    };\n  }, []);\n  const pendingNav = routerState.pendingNav;\n  useLayoutEffect(() => {\n    pendingNav();\n  }, [pendingNav]);\n\n  return (\n    <RouterContext\n      value={{\n        url: routerState.url,\n        navigate,\n        navigateBack,\n        isPending,\n        params: {},\n      }}\n    >\n      {children}\n    </RouterContext>\n  );\n}\n\nconst RouterContext = createContext({ url: \"/\", params: {} });\n\nexport function useRouter() {\n  return use(RouterContext);\n}\n\nexport function useIsNavPending() {\n  return use(RouterContext).isPending;\n}\n\n```\n\n```css src/styles.css hidden\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Rg.woff2) format(\"woff2\");\n  font-weight: 400;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Md.woff2) format(\"woff2\");\n  font-weight: 500;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 600;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 700;\n  font-style: normal;\n  font-display: swap;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhtml {\n  background-image: url(https://react.dev/images/meta-gradient-dark.png);\n  background-size: 100%;\n  background-position: -100%;\n  background-color: rgb(64 71 86);\n  background-repeat: no-repeat;\n  height: 100%;\n  width: 100%;\n}\n\nbody {\n  font-family: Optimistic Text, -apple-system, ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;\n  padding: 10px 0 10px 0;\n  margin: 0;\n  display: flex;\n  justify-content: center;\n}\n\n#root {\n  flex: 1 1;\n  height: auto;\n  background-color: #fff;\n  border-radius: 10px;\n  max-width: 450px;\n  min-height: 600px;\n  padding-bottom: 10px;\n}\n\nh1 {\n  margin-top: 0;\n  font-size: 22px;\n}\n\nh2 {\n  margin-top: 0;\n  font-size: 20px;\n}\n\nh3 {\n  margin-top: 0;\n  font-size: 18px;\n}\n\nh4 {\n  margin-top: 0;\n  font-size: 16px;\n}\n\nh5 {\n  margin-top: 0;\n  font-size: 14px;\n}\n\nh6 {\n  margin-top: 0;\n  font-size: 12px;\n}\n\ncode {\n  font-size: 1.2em;\n}\n\nul {\n  padding-inline-start: 20px;\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border-width: 0;\n}\n\n.absolute {\n  position: absolute;\n}\n\n.overflow-visible {\n  overflow: visible;\n}\n\n.visible {\n  overflow: visible;\n}\n\n.fit {\n  width: fit-content;\n}\n\n\n/* Layout */\n.page {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n}\n\n.top-hero {\n  height: 200px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-image: conic-gradient(\n      from 90deg at -10% 100%,\n      #2b303b 0deg,\n      #2b303b 90deg,\n      #16181d 1turn\n  );\n}\n\n.bottom {\n  flex: 1;\n  overflow: auto;\n}\n\n.top-nav {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 0;\n  padding: 0 12px;\n  top: 0;\n  width: 100%;\n  height: 44px;\n  color: #23272f;\n  font-weight: 700;\n  font-size: 20px;\n  z-index: 100;\n  cursor: default;\n}\n\n.content {\n  padding: 0 12px;\n  margin-top: 4px;\n}\n\n\n.loader {\n  color: #23272f;\n  font-size: 3px;\n  width: 1em;\n  margin-right: 18px;\n  height: 1em;\n  border-radius: 50%;\n  position: relative;\n  text-indent: -9999em;\n  animation: loading-spinner 1.3s infinite linear;\n  animation-delay: 200ms;\n  transform: translateZ(0);\n}\n\n@keyframes loading-spinner {\n  0%,\n  100% {\n    box-shadow: 0 -3em 0 0.2em,\n    2em -2em 0 0em, 3em 0 0 -1em,\n    2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 0;\n  }\n  12.5% {\n    box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em,\n    3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  25% {\n    box-shadow: 0 -3em 0 -0.5em,\n    2em -2em 0 0, 3em 0 0 0.2em,\n    2em 2em 0 0, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  37.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em,\n    -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  50% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em,\n    -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  62.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0,\n    -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em;\n  }\n  75% {\n    box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0;\n  }\n  87.5% {\n    box-shadow: 0em -3em 0 0, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;\n  }\n}\n\n/* LikeButton */\n.like-button {\n  outline-offset: 2px;\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 2.5rem;\n  height: 2.5rem;\n  cursor: pointer;\n  border-radius: 9999px;\n  border: none;\n  outline: none 2px;\n  color: #5e687e;\n  background: none;\n}\n\n.like-button:focus {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n}\n\n.like-button:active {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n  transform: scaleX(0.95) scaleY(0.95);\n}\n\n.like-button:hover {\n  background-color: #f6f7f9;\n}\n\n.like-button.liked {\n  color: #a6423a;\n}\n\n/* Icons */\n@keyframes circle {\n  0% {\n    transform: scale(0);\n    stroke-width: 16px;\n  }\n\n  50% {\n    transform: scale(.5);\n    stroke-width: 16px;\n  }\n\n  to {\n    transform: scale(1);\n    stroke-width: 0;\n  }\n}\n\n.circle {\n  color: rgba(166, 66, 58, .5);\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4,0,.2,1);\n}\n\n.circle.liked.animate {\n  animation: circle .3s forwards;\n}\n\n.heart {\n  width: 1.5rem;\n  height: 1.5rem;\n}\n\n.heart.liked {\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4, 0, .2, 1);\n}\n\n.heart.liked.animate {\n  animation: scale .35s ease-in-out forwards;\n}\n\n.control-icon {\n  color: hsla(0, 0%, 100%, .5);\n  filter:  drop-shadow(0 20px 13px rgba(0, 0, 0, .03)) drop-shadow(0 8px 5px rgba(0, 0, 0, .08));\n}\n\n.chevron-left {\n  margin-top: 2px;\n  rotate: 90deg;\n}\n\n\n/* Video */\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n\n.thumbnail.red {\n  background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);\n}\n\n.thumbnail.green {\n  background-image: conic-gradient(at top right, #c76a15, #388f7f, #2b3491);\n}\n\n.thumbnail.purple {\n  background-image: conic-gradient(at top right, #c76a15, #575fb7, #2b3491);\n}\n\n.thumbnail.yellow {\n  background-image: conic-gradient(at top right, #c76a15, #FABD62, #2b3491);\n}\n\n.thumbnail.gray {\n  background-image: conic-gradient(at top right, #c76a15, #4E5769, #2b3491);\n}\n\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n}\n\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n\n.video .info:hover {\n  text-decoration: underline;\n}\n\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n\n/* Details */\n.details .thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 100%;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.video-details-title {\n  margin-top: 8px;\n}\n\n.video-details-speaker {\n  display: flex;\n  gap: 8px;\n  margin-top: 10px\n}\n\n.back {\n  display: flex;\n  align-items: center;\n  margin-left: -5px;\n  cursor: pointer;\n}\n\n.back:hover {\n  text-decoration: underline;\n}\n\n.info-title {\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1.25;\n  margin: 8px 0 0 0 ;\n}\n\n.info-description {\n  margin: 8px 0 0 0;\n}\n\n.controls {\n  cursor: pointer;\n}\n\n.fallback {\n  background: #f6f7f8 linear-gradient(to right, #e6e6e6 5%, #cccccc 25%, #e6e6e6 35%) no-repeat;\n  background-size: 800px 104px;\n  display: block;\n  line-height: 1.25;\n  margin: 8px 0 0 0;\n  border-radius: 5px;\n  overflow: hidden;\n\n  animation: 1s linear 1s infinite shimmer;\n  animation-delay: 300ms;\n  animation-duration: 1s;\n  animation-fill-mode: forwards;\n  animation-iteration-count: infinite;\n  animation-name: shimmer;\n  animation-timing-function: linear;\n}\n\n\n.fallback.title {\n  width: 130px;\n  height: 30px;\n\n}\n\n.fallback.description {\n  width: 150px;\n  height: 21px;\n}\n\n@keyframes shimmer {\n  0% {\n    background-position: -468px 0;\n  }\n\n  100% {\n    background-position: 468px 0;\n  }\n}\n\n.search {\n  margin-bottom: 10px;\n}\n.search-input {\n  width: 100%;\n  position: relative;\n}\n\n.search-icon {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  inset-inline-start: 0;\n  display: flex;\n  align-items: center;\n  padding-inline-start: 1rem;\n  pointer-events: none;\n  color: #99a1b3;\n}\n\n.search-input input {\n  display: flex;\n  padding-inline-start: 2.75rem;\n  padding-top: 10px;\n  padding-bottom: 10px;\n  width: 100%;\n  text-align: start;\n  background-color: rgb(235 236 240);\n  outline: 2px solid transparent;\n  cursor: pointer;\n  border: none;\n  align-items: center;\n  color: rgb(35 39 47);\n  border-radius: 9999px;\n  vertical-align: middle;\n  font-size: 15px;\n}\n\n.search-input input:hover, .search-input input:active {\n  background-color: rgb(235 236 240/ 0.8);\n  color: rgb(35 39 47/ 0.8);\n}\n\n/* Home */\n.video-list {\n  position: relative;\n}\n\n.video-list .videos {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n  overflow-y: auto;\n  height: 100%;\n}\n```\n\n\n```css src/animations.css\n/* Slide the fallback down */\n::view-transition-old(.slide-down) {\n    animation: 150ms ease-out both fade-out, 150ms ease-out both slide-down;\n}\n\n/* Slide the content up */\n::view-transition-new(.slide-up) {\n    animation: 210ms ease-in 150ms both fade-in, 400ms ease-in both slide-up;\n}\n\n/* Define the new keyframes */\n@keyframes slide-up {\n    from {\n        transform: translateY(10px);\n    }\n    to {\n        transform: translateY(0);\n    }\n}\n\n@keyframes slide-down {\n    from {\n        transform: translateY(0);\n    }\n    to {\n        transform: translateY(10px);\n    }\n}\n\n/* Previously defined animations below */\n\n/* Animations for view transition classed added by transition type */\n::view-transition-old(.slide-forward) {\n    /* when sliding forward, the \"old\" page should slide out to left. */\n    animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;\n}\n\n::view-transition-new(.slide-forward) {\n    /* when sliding forward, the \"new\" page should slide in from right. */\n    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;\n}\n\n::view-transition-old(.slide-back) {\n    /* when sliding back, the \"old\" page should slide out to right. */\n    animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;\n}\n\n::view-transition-new(.slide-back) {\n    /* when sliding back, the \"new\" page should slide in from left. */\n    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-left;\n}\n\n/* Keyframes to support our animations above. */\n@keyframes fade-in {\n    from {\n        opacity: 0;\n    }\n}\n\n@keyframes fade-out {\n    to {\n        opacity: 0;\n    }\n}\n\n@keyframes slide-to-right {\n    to {\n        transform: translateX(50px);\n    }\n}\n\n@keyframes slide-from-right {\n    from {\n        transform: translateX(50px);\n    }\n    to {\n        transform: translateX(0);\n    }\n}\n\n@keyframes slide-to-left {\n    to {\n        transform: translateX(-50px);\n    }\n}\n\n@keyframes slide-from-left {\n    from {\n        transform: translateX(-50px);\n    }\n    to {\n        transform: translateX(0);\n    }\n}\n\n/* Default .slow-fade. */\n::view-transition-old(.slow-fade) {\n    animation-duration: 500ms;\n}\n\n::view-transition-new(.slow-fade) {\n    animation-duration: 500ms;\n}\n```\n\n```js src/index.js hidden\nimport React, {StrictMode} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport './styles.css';\nimport './animations.css';\n\nimport App from './App';\nimport {Router} from './router';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <Router>\n      <App />\n    </Router>\n  </StrictMode>\n);\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\n\n### リストのアニメーション {/*animating-lists*/}\n\n`<ViewTransition>` を使用すれば、以下の検索可能なアイテムリストのように、アイテムの並べ替えをアニメーション化することもできます。\n\n```js {3,5}\n<div className=\"videos\">\n  {filteredVideos.map((video) => (\n    <ViewTransition key={video.id}>\n      <Video video={video} />\n    </ViewTransition>\n  ))}\n</div>\n```\n\nこの ViewTransition を起動するために `useDeferredValue` を使用します。\n\n```js {2}\nconst [searchText, setSearchText] = useState('');\nconst deferredSearchText = useDeferredValue(searchText);\nconst filteredVideos = filterVideos(videos, deferredSearchText);\n```\n\nこれで、検索バーに入力するたびにアイテムがアニメーションします。\n\n<Sandpack>\n\n```js src/App.js hidden\nimport { ViewTransition } from \"react\";\nimport Details from \"./Details\";\nimport Home from \"./Home\";\nimport { useRouter } from \"./router\";\n\nexport default function App() {\n  const { url } = useRouter();\n\n  // Default slow-fade animation.\n  return (\n    <ViewTransition default=\"slow-fade\">\n      {url === \"/\" ? <Home /> : <Details />}\n    </ViewTransition>\n  );\n}\n```\n\n```js src/Details.js hidden\nimport { use, Suspense, ViewTransition } from \"react\";\nimport { fetchVideo, fetchVideoDetails } from \"./data\";\nimport { Thumbnail, VideoControls } from \"./Videos\";\nimport { useRouter } from \"./router\";\nimport Layout from \"./Layout\";\nimport { ChevronLeft } from \"./Icons\";\n\nfunction VideoDetails({id}) {\n  // Animate from Suspense fallback to content\n  return (\n    <Suspense\n      fallback={\n        // Animate the fallback down.\n        <ViewTransition exit=\"slide-down\">\n          <VideoInfoFallback />\n        </ViewTransition>\n      }\n    >\n      {/* Animate the content up */}\n      <ViewTransition enter=\"slide-up\">\n        <VideoInfo id={id} />\n      </ViewTransition>\n    </Suspense>\n  );\n}\n\nfunction VideoInfoFallback() {\n  return (\n    <>\n      <div className=\"fallback title\"></div>\n      <div className=\"fallback description\"></div>\n    </>\n  );\n}\n\nexport default function Details() {\n  const { url, navigateBack } = useRouter();\n  const videoId = url.split(\"/\").pop();\n  const video = use(fetchVideo(videoId));\n\n  return (\n    <Layout\n      heading={\n        <div\n          className=\"fit back\"\n          onClick={() => {\n            navigateBack(\"/\");\n          }}\n        >\n          <ChevronLeft /> Back\n        </div>\n      }\n    >\n      <div className=\"details\">\n        <Thumbnail video={video} large>\n          <VideoControls />\n        </Thumbnail>\n        <VideoDetails id={video.id} />\n      </div>\n    </Layout>\n  );\n}\n\nfunction VideoInfo({ id }) {\n  const details = use(fetchVideoDetails(id));\n  return (\n    <>\n      <p className=\"info-title\">{details.title}</p>\n      <p className=\"info-description\">{details.description}</p>\n    </>\n  );\n}\n```\n\n```js src/Home.js\nimport { useId, useState, use, useDeferredValue, ViewTransition } from \"react\";import { Video } from \"./Videos\";import Layout from \"./Layout\";import { fetchVideos } from \"./data\";import { IconSearch } from \"./Icons\";\n\nfunction SearchList({searchText, videos}) {\n  // Activate with useDeferredValue (\"when\")\n  const deferredSearchText = useDeferredValue(searchText);\n  const filteredVideos = filterVideos(videos, deferredSearchText);\n  return (\n    <div className=\"video-list\">\n      <div className=\"videos\">\n        {filteredVideos.map((video) => (\n          // Animate each item in list (\"what\")\n          <ViewTransition key={video.id}>\n            <Video video={video} />\n          </ViewTransition>\n        ))}\n      </div>\n      {filteredVideos.length === 0 && (\n        <div className=\"no-results\">No results</div>\n      )}\n    </div>\n  );\n}\n\nexport default function Home() {\n  const videos = use(fetchVideos());\n  const count = videos.length;\n  const [searchText, setSearchText] = useState('');\n\n  return (\n    <Layout heading={<div className=\"fit\">{count} Videos</div>}>\n      <SearchInput value={searchText} onChange={setSearchText} />\n      <SearchList videos={videos} searchText={searchText} />\n    </Layout>\n  );\n}\n\nfunction SearchInput({ value, onChange }) {\n  const id = useId();\n  return (\n    <form className=\"search\" onSubmit={(e) => e.preventDefault()}>\n      <label htmlFor={id} className=\"sr-only\">\n        Search\n      </label>\n      <div className=\"search-input\">\n        <div className=\"search-icon\">\n          <IconSearch />\n        </div>\n        <input\n          type=\"text\"\n          id={id}\n          placeholder=\"Search\"\n          value={value}\n          onChange={(e) => onChange(e.target.value)}\n        />\n      </div>\n    </form>\n  );\n}\n\nfunction filterVideos(videos, query) {\n  const keywords = query\n    .toLowerCase()\n    .split(\" \")\n    .filter((s) => s !== \"\");\n  if (keywords.length === 0) {\n    return videos;\n  }\n  return videos.filter((video) => {\n    const words = (video.title + \" \" + video.description)\n      .toLowerCase()\n      .split(\" \");\n    return keywords.every((kw) => words.some((w) => w.includes(kw)));\n  });\n}\n```\n\n```js src/Icons.js hidden\nexport function ChevronLeft() {\n  return (\n    <svg\n      className=\"chevron-left\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\">\n      <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-446 -398)\">\n        <path\n          fill=\"currentColor\"\n          fillRule=\"nonzero\"\n          d=\"M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z\"\n          transform=\"translate(356.5 164.5)\"\n        />\n        <polygon points=\"446 418 466 418 466 398 446 398\" />\n      </g>\n    </svg>\n  );\n}\n\nexport function PauseIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      style={{padding: '4px'}}\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 512 512\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M256 0C114.617 0 0 114.615 0 256s114.617 256 256 256 256-114.615 256-256S397.383 0 256 0zm-32 320c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128zm128 0c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n\nexport function PlayIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 72 72\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M36 69C54.2254 69 69 54.2254 69 36C69 17.7746 54.2254 3 36 3C17.7746 3 3 17.7746 3 36C3 54.2254 17.7746 69 36 69ZM52.1716 38.6337L28.4366 51.5801C26.4374 52.6705 24 51.2235 24 48.9464V23.0536C24 20.7764 26.4374 19.3295 28.4366 20.4199L52.1716 33.3663C54.2562 34.5034 54.2562 37.4966 52.1716 38.6337Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\nexport function Heart({liked, animate}) {\n  return (\n    <>\n      <svg\n        className=\"absolute overflow-visible\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <circle\n          className={`circle ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n          cx=\"12\"\n          cy=\"12\"\n          r=\"11.5\"\n          fill=\"transparent\"\n          strokeWidth=\"0\"\n          stroke=\"currentColor\"\n        />\n      </svg>\n\n      <svg\n        className={`heart ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        {liked ? (\n          <path\n            d=\"M12 23a.496.496 0 0 1-.26-.074C7.023 19.973 0 13.743 0 8.68c0-4.12 2.322-6.677 6.058-6.677 2.572 0 5.108 2.387 5.134 2.41l.808.771.808-.771C12.834 4.387 15.367 2 17.935 2 21.678 2 24 4.558 24 8.677c0 5.06-7.022 11.293-11.74 14.246a.496.496 0 0 1-.26.074V23z\"\n            fill=\"currentColor\"\n          />\n        ) : (\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"m12 5.184-.808-.771-.004-.004C11.065 4.299 8.522 2.003 6 2.003c-3.736 0-6 2.558-6 6.677 0 4.47 5.471 9.848 10 13.079.602.43 1.187.82 1.74 1.167A.497.497 0 0 0 12 23v-.003c.09 0 .182-.026.26-.074C16.977 19.97 24 13.737 24 8.677 24 4.557 21.743 2 18 2c-2.569 0-5.166 2.387-5.192 2.413L12 5.184zm-.002 15.525c2.071-1.388 4.477-3.342 6.427-5.47C20.72 12.733 22 10.401 22 8.677c0-1.708-.466-2.855-1.087-3.55C20.316 4.459 19.392 4 18 4c-.726 0-1.63.364-2.5.9-.67.412-1.148.82-1.266.92-.03.025-.037.031-.019.014l-.013.013L12 7.949 9.832 5.88a10.08 10.08 0 0 0-1.33-.977C7.633 4.367 6.728 4.003 6 4.003c-1.388 0-2.312.459-2.91 1.128C2.466 5.826 2 6.974 2 8.68c0 1.726 1.28 4.058 3.575 6.563 1.948 2.127 4.352 4.078 6.423 5.466z\"\n            fill=\"currentColor\"\n          />\n        )}\n      </svg>\n    </>\n  );\n}\n\nexport function IconSearch(props) {\n  return (\n    <svg width=\"1em\" height=\"1em\" viewBox=\"0 0 20 20\">\n      <path\n        d=\"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z\"\n        stroke=\"currentColor\"\n        fill=\"none\"\n        strokeWidth=\"2\"\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"></path>\n    </svg>\n  );\n}\n```\n\n```js src/Layout.js hidden\nimport {ViewTransition} from 'react';\nimport { useIsNavPending } from \"./router\";\n\nexport default function Page({ heading, children }) {\n  const isPending = useIsNavPending();\n  return (\n    <div className=\"page\">\n      <div className=\"top\">\n        <div className=\"top-nav\">\n          {/* Custom classes based on transition type. */}\n          <ViewTransition\n            name=\"nav\"\n            share={{\n              'nav-forward': 'slide-forward',\n              'nav-back': 'slide-back',\n            }}>\n            {heading}\n          </ViewTransition>\n          {isPending && <span className=\"loader\"></span>}\n        </div>\n      </div>\n      {/* Opt-out of ViewTransition for the content. */}\n      {/* Content can define it's own ViewTransition. */}\n      <ViewTransition default=\"none\">\n        <div className=\"bottom\">\n          <div className=\"content\">{children}</div>\n        </div>\n      </ViewTransition>\n    </div>\n  );\n}\n```\n\n```js src/LikeButton.js hidden\nimport {useState} from 'react';\nimport {Heart} from './Icons';\n\n// A hack since we don't actually have a backend.\n// Unlike local state, this survives videos being filtered.\nconst likedVideos = new Set();\n\nexport default function LikeButton({video}) {\n  const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));\n  const [animate, setAnimate] = useState(false);\n  return (\n    <button\n      className={`like-button ${isLiked && 'liked'}`}\n      aria-label={isLiked ? 'Unsave' : 'Save'}\n      onClick={() => {\n        const nextIsLiked = !isLiked;\n        if (nextIsLiked) {\n          likedVideos.add(video.id);\n        } else {\n          likedVideos.delete(video.id);\n        }\n        setAnimate(true);\n        setIsLiked(nextIsLiked);\n      }}>\n      <Heart liked={isLiked} animate={animate} />\n    </button>\n  );\n}\n```\n\n```js src/Videos.js hidden\nimport { useState, ViewTransition } from \"react\";\nimport LikeButton from \"./LikeButton\";\nimport { useRouter } from \"./router\";\nimport { PauseIcon, PlayIcon } from \"./Icons\";\nimport { startTransition } from \"react\";\n\nexport function Thumbnail({ video, children }) {\n  // Add a name to animate with a shared element transition.\n  // This uses the default animation, no additional css needed.\n  return (\n    <ViewTransition name={`video-${video.id}`}>\n      <div\n        aria-hidden=\"true\"\n        tabIndex={-1}\n        className={`thumbnail ${video.image}`}\n      >\n        {children}\n      </div>\n    </ViewTransition>\n  );\n}\n\nexport function VideoControls() {\n  const [isPlaying, setIsPlaying] = useState(false);\n\n  return (\n    <span\n      className=\"controls\"\n      onClick={() =>\n        startTransition(() => {\n          setIsPlaying((p) => !p);\n        })\n      }\n    >\n      {isPlaying ? <PauseIcon /> : <PlayIcon />}\n    </span>\n  );\n}\n\nexport function Video({ video }) {\n  const { navigate } = useRouter();\n\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n        onClick={(e) => {\n          e.preventDefault();\n          navigate(`/video/${video.id}`);\n        }}\n      >\n        <Thumbnail video={video}></Thumbnail>\n\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n      <LikeButton video={video} />\n    </div>\n  );\n}\n```\n\n\n```js src/data.js hidden\nconst videos = [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  },\n  {\n    id: '2',\n    title: 'Second video',\n    description: 'Video description',\n    image: 'red',\n  },\n  {\n    id: '3',\n    title: 'Third video',\n    description: 'Video description',\n    image: 'green',\n  },\n  {\n    id: '4',\n    title: 'Fourth video',\n    description: 'Video description',\n    image: 'purple',\n  },\n  {\n    id: '5',\n    title: 'Fifth video',\n    description: 'Video description',\n    image: 'yellow',\n  },\n  {\n    id: '6',\n    title: 'Sixth video',\n    description: 'Video description',\n    image: 'gray',\n  },\n];\n\nlet videosCache = new Map();\nlet videoCache = new Map();\nlet videoDetailsCache = new Map();\nconst VIDEO_DELAY = 1;\nconst VIDEO_DETAILS_DELAY = 1000;\nexport function fetchVideos() {\n  if (videosCache.has(0)) {\n    return videosCache.get(0);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos);\n    }, VIDEO_DELAY);\n  });\n  videosCache.set(0, promise);\n  return promise;\n}\n\nexport function fetchVideo(id) {\n  if (videoCache.has(id)) {\n    return videoCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DELAY);\n  });\n  videoCache.set(id, promise);\n  return promise;\n}\n\nexport function fetchVideoDetails(id) {\n  if (videoDetailsCache.has(id)) {\n    return videoDetailsCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DETAILS_DELAY);\n  });\n  videoDetailsCache.set(id, promise);\n  return promise;\n}\n```\n\n```js src/router.js hidden\nimport {useState, createContext, use, useTransition, useLayoutEffect, useEffect, addTransitionType} from \"react\";\n\nexport function Router({ children }) {\n  const [isPending, startTransition] = useTransition();\n  const [routerState, setRouterState] = useState({pendingNav: () => {}, url: document.location.pathname});\n  function navigate(url) {\n    startTransition(() => {\n      // Transition type for the cause \"nav forward\"\n      addTransitionType('nav-forward');\n      go(url);\n    });\n  }\n  function navigateBack(url) {\n    startTransition(() => {\n      // Transition type for the cause \"nav backward\"\n      addTransitionType('nav-back');\n      go(url);\n    });\n  }\n\n  function go(url) {\n    setRouterState({\n      url,\n      pendingNav() {\n        window.history.pushState({}, \"\", url);\n      },\n    });\n  }\n\n  useEffect(() => {\n    function handlePopState() {\n      // This should not animate because restoration has to be synchronous.\n      // Even though it's a transition.\n      startTransition(() => {\n        setRouterState({\n          url: document.location.pathname + document.location.search,\n          pendingNav() {\n            // Noop. URL has already updated.\n          },\n        });\n      });\n    }\n    window.addEventListener(\"popstate\", handlePopState);\n    return () => {\n      window.removeEventListener(\"popstate\", handlePopState);\n    };\n  }, []);\n  const pendingNav = routerState.pendingNav;\n  useLayoutEffect(() => {\n    pendingNav();\n  }, [pendingNav]);\n\n  return (\n    <RouterContext\n      value={{\n        url: routerState.url,\n        navigate,\n        navigateBack,\n        isPending,\n        params: {},\n      }}\n    >\n      {children}\n    </RouterContext>\n  );\n}\n\nconst RouterContext = createContext({ url: \"/\", params: {} });\n\nexport function useRouter() {\n  return use(RouterContext);\n}\n\nexport function useIsNavPending() {\n  return use(RouterContext).isPending;\n}\n\n```\n\n```css src/styles.css hidden\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Rg.woff2) format(\"woff2\");\n  font-weight: 400;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Md.woff2) format(\"woff2\");\n  font-weight: 500;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 600;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 700;\n  font-style: normal;\n  font-display: swap;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhtml {\n  background-image: url(https://react.dev/images/meta-gradient-dark.png);\n  background-size: 100%;\n  background-position: -100%;\n  background-color: rgb(64 71 86);\n  background-repeat: no-repeat;\n  height: 100%;\n  width: 100%;\n}\n\nbody {\n  font-family: Optimistic Text, -apple-system, ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;\n  padding: 10px 0 10px 0;\n  margin: 0;\n  display: flex;\n  justify-content: center;\n}\n\n#root {\n  flex: 1 1;\n  height: auto;\n  background-color: #fff;\n  border-radius: 10px;\n  max-width: 450px;\n  min-height: 600px;\n  padding-bottom: 10px;\n}\n\nh1 {\n  margin-top: 0;\n  font-size: 22px;\n}\n\nh2 {\n  margin-top: 0;\n  font-size: 20px;\n}\n\nh3 {\n  margin-top: 0;\n  font-size: 18px;\n}\n\nh4 {\n  margin-top: 0;\n  font-size: 16px;\n}\n\nh5 {\n  margin-top: 0;\n  font-size: 14px;\n}\n\nh6 {\n  margin-top: 0;\n  font-size: 12px;\n}\n\ncode {\n  font-size: 1.2em;\n}\n\nul {\n  padding-inline-start: 20px;\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border-width: 0;\n}\n\n.absolute {\n  position: absolute;\n}\n\n.overflow-visible {\n  overflow: visible;\n}\n\n.visible {\n  overflow: visible;\n}\n\n.fit {\n  width: fit-content;\n}\n\n\n/* Layout */\n.page {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n}\n\n.top-hero {\n  height: 200px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-image: conic-gradient(\n      from 90deg at -10% 100%,\n      #2b303b 0deg,\n      #2b303b 90deg,\n      #16181d 1turn\n  );\n}\n\n.bottom {\n  flex: 1;\n  overflow: auto;\n}\n\n.top-nav {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 0;\n  padding: 0 12px;\n  top: 0;\n  width: 100%;\n  height: 44px;\n  color: #23272f;\n  font-weight: 700;\n  font-size: 20px;\n  z-index: 100;\n  cursor: default;\n}\n\n.content {\n  padding: 0 12px;\n  margin-top: 4px;\n}\n\n\n.loader {\n  color: #23272f;\n  font-size: 3px;\n  width: 1em;\n  margin-right: 18px;\n  height: 1em;\n  border-radius: 50%;\n  position: relative;\n  text-indent: -9999em;\n  animation: loading-spinner 1.3s infinite linear;\n  animation-delay: 200ms;\n  transform: translateZ(0);\n}\n\n@keyframes loading-spinner {\n  0%,\n  100% {\n    box-shadow: 0 -3em 0 0.2em,\n    2em -2em 0 0em, 3em 0 0 -1em,\n    2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 0;\n  }\n  12.5% {\n    box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em,\n    3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  25% {\n    box-shadow: 0 -3em 0 -0.5em,\n    2em -2em 0 0, 3em 0 0 0.2em,\n    2em 2em 0 0, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  37.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em,\n    -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  50% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em,\n    -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  62.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0,\n    -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em;\n  }\n  75% {\n    box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0;\n  }\n  87.5% {\n    box-shadow: 0em -3em 0 0, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;\n  }\n}\n\n/* LikeButton */\n.like-button {\n  outline-offset: 2px;\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 2.5rem;\n  height: 2.5rem;\n  cursor: pointer;\n  border-radius: 9999px;\n  border: none;\n  outline: none 2px;\n  color: #5e687e;\n  background: none;\n}\n\n.like-button:focus {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n}\n\n.like-button:active {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n  transform: scaleX(0.95) scaleY(0.95);\n}\n\n.like-button:hover {\n  background-color: #f6f7f9;\n}\n\n.like-button.liked {\n  color: #a6423a;\n}\n\n/* Icons */\n@keyframes circle {\n  0% {\n    transform: scale(0);\n    stroke-width: 16px;\n  }\n\n  50% {\n    transform: scale(.5);\n    stroke-width: 16px;\n  }\n\n  to {\n    transform: scale(1);\n    stroke-width: 0;\n  }\n}\n\n.circle {\n  color: rgba(166, 66, 58, .5);\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4,0,.2,1);\n}\n\n.circle.liked.animate {\n  animation: circle .3s forwards;\n}\n\n.heart {\n  width: 1.5rem;\n  height: 1.5rem;\n}\n\n.heart.liked {\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4, 0, .2, 1);\n}\n\n.heart.liked.animate {\n  animation: scale .35s ease-in-out forwards;\n}\n\n.control-icon {\n  color: hsla(0, 0%, 100%, .5);\n  filter:  drop-shadow(0 20px 13px rgba(0, 0, 0, .03)) drop-shadow(0 8px 5px rgba(0, 0, 0, .08));\n}\n\n.chevron-left {\n  margin-top: 2px;\n  rotate: 90deg;\n}\n\n\n/* Video */\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n\n.thumbnail.red {\n  background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);\n}\n\n.thumbnail.green {\n  background-image: conic-gradient(at top right, #c76a15, #388f7f, #2b3491);\n}\n\n.thumbnail.purple {\n  background-image: conic-gradient(at top right, #c76a15, #575fb7, #2b3491);\n}\n\n.thumbnail.yellow {\n  background-image: conic-gradient(at top right, #c76a15, #FABD62, #2b3491);\n}\n\n.thumbnail.gray {\n  background-image: conic-gradient(at top right, #c76a15, #4E5769, #2b3491);\n}\n\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n}\n\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n\n.video .info:hover {\n  text-decoration: underline;\n}\n\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n\n/* Details */\n.details .thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 100%;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.video-details-title {\n  margin-top: 8px;\n}\n\n.video-details-speaker {\n  display: flex;\n  gap: 8px;\n  margin-top: 10px\n}\n\n.back {\n  display: flex;\n  align-items: center;\n  margin-left: -5px;\n  cursor: pointer;\n}\n\n.back:hover {\n  text-decoration: underline;\n}\n\n.info-title {\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1.25;\n  margin: 8px 0 0 0 ;\n}\n\n.info-description {\n  margin: 8px 0 0 0;\n}\n\n.controls {\n  cursor: pointer;\n}\n\n.fallback {\n  background: #f6f7f8 linear-gradient(to right, #e6e6e6 5%, #cccccc 25%, #e6e6e6 35%) no-repeat;\n  background-size: 800px 104px;\n  display: block;\n  line-height: 1.25;\n  margin: 8px 0 0 0;\n  border-radius: 5px;\n  overflow: hidden;\n\n  animation: 1s linear 1s infinite shimmer;\n  animation-delay: 300ms;\n  animation-duration: 1s;\n  animation-fill-mode: forwards;\n  animation-iteration-count: infinite;\n  animation-name: shimmer;\n  animation-timing-function: linear;\n}\n\n\n.fallback.title {\n  width: 130px;\n  height: 30px;\n\n}\n\n.fallback.description {\n  width: 150px;\n  height: 21px;\n}\n\n@keyframes shimmer {\n  0% {\n    background-position: -468px 0;\n  }\n\n  100% {\n    background-position: 468px 0;\n  }\n}\n\n.search {\n  margin-bottom: 10px;\n}\n.search-input {\n  width: 100%;\n  position: relative;\n}\n\n.search-icon {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  inset-inline-start: 0;\n  display: flex;\n  align-items: center;\n  padding-inline-start: 1rem;\n  pointer-events: none;\n  color: #99a1b3;\n}\n\n.search-input input {\n  display: flex;\n  padding-inline-start: 2.75rem;\n  padding-top: 10px;\n  padding-bottom: 10px;\n  width: 100%;\n  text-align: start;\n  background-color: rgb(235 236 240);\n  outline: 2px solid transparent;\n  cursor: pointer;\n  border: none;\n  align-items: center;\n  color: rgb(35 39 47);\n  border-radius: 9999px;\n  vertical-align: middle;\n  font-size: 15px;\n}\n\n.search-input input:hover, .search-input input:active {\n  background-color: rgb(235 236 240/ 0.8);\n  color: rgb(35 39 47/ 0.8);\n}\n\n/* Home */\n.video-list {\n  position: relative;\n}\n\n.video-list .videos {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n  overflow-y: auto;\n  height: 100%;\n}\n```\n\n\n```css src/animations.css\n/* No additional animations needed */\n\n\n\n\n\n\n\n\n\n/* Previously defined animations below */\n\n\n\n\n\n\n/* Slide animation for Suspense */\n::view-transition-old(.slide-down) {\n    animation: 150ms ease-out both fade-out, 150ms ease-out both slide-down;\n}\n\n::view-transition-new(.slide-up) {\n    animation: 210ms ease-in 150ms both fade-in, 400ms ease-in both slide-up;\n}\n\n/* Animations for view transition classed added by transition type */\n::view-transition-old(.slide-forward) {\n    /* when sliding forward, the \"old\" page should slide out to left. */\n    animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;\n}\n\n::view-transition-new(.slide-forward) {\n    /* when sliding forward, the \"new\" page should slide in from right. */\n    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;\n}\n\n::view-transition-old(.slide-back) {\n    /* when sliding back, the \"old\" page should slide out to right. */\n    animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;\n}\n\n::view-transition-new(.slide-back) {\n    /* when sliding back, the \"new\" page should slide in from left. */\n    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-left;\n}\n\n/* Keyframes to support our animations above. */\n@keyframes slide-up {\n    from {\n        transform: translateY(10px);\n    }\n    to {\n        transform: translateY(0);\n    }\n}\n\n@keyframes slide-down {\n    from {\n        transform: translateY(0);\n    }\n    to {\n        transform: translateY(10px);\n    }\n}\n\n@keyframes fade-in {\n    from {\n        opacity: 0;\n    }\n}\n\n@keyframes fade-out {\n    to {\n        opacity: 0;\n    }\n}\n\n@keyframes slide-to-right {\n    to {\n        transform: translateX(50px);\n    }\n}\n\n@keyframes slide-from-right {\n    from {\n        transform: translateX(50px);\n    }\n    to {\n        transform: translateX(0);\n    }\n}\n\n@keyframes slide-to-left {\n    to {\n        transform: translateX(-50px);\n    }\n}\n\n@keyframes slide-from-left {\n    from {\n        transform: translateX(-50px);\n    }\n    to {\n        transform: translateX(0);\n    }\n}\n\n\n/* Default .slow-fade. */\n::view-transition-old(.slow-fade) {\n    animation-duration: 500ms;\n}\n\n::view-transition-new(.slow-fade) {\n    animation-duration: 500ms;\n}\n```\n\n```js src/index.js hidden\nimport React, {StrictMode} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport './styles.css';\nimport './animations.css';\n\nimport App from './App';\nimport {Router} from './router';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <Router>\n      <App />\n    </Router>\n  </StrictMode>\n);\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\n### 最終結果 {/*final-result*/}\n\nいくつかの `<ViewTransition>` コンポーネントと数行の CSS を追加することで、上記のすべてのアニメーションを最終結果に追加することができました。\n\n我々はビュー遷移機能に非常に期待しており、みなさんが構築できるアプリのレベルアップに役立つと考えています。React リリースの実験的チャンネルで、今日から試す準備が整っています。\n\n遅いフェードを削除して、最終結果を見ておきましょう。\n\n<Sandpack>\n\n```js src/App.js\nimport {ViewTransition} from 'react'; import Details from './Details'; import Home from './Home'; import {useRouter} from './router';\n\nexport default function App() {\n  const {url} = useRouter();\n\n  // Animate with a cross fade between pages.\n  return (\n    <ViewTransition key={url}>\n      {url === '/' ? <Home /> : <Details />}\n    </ViewTransition>\n  );\n}\n```\n\n```js src/Details.js\nimport { use, Suspense, ViewTransition } from \"react\"; import { fetchVideo, fetchVideoDetails } from \"./data\"; import { Thumbnail, VideoControls } from \"./Videos\"; import { useRouter } from \"./router\"; import Layout from \"./Layout\"; import { ChevronLeft } from \"./Icons\";\n\nfunction VideoDetails({id}) {\n  // Animate from Suspense fallback to content\n  return (\n    <Suspense\n      fallback={\n        // Animate the fallback down.\n        <ViewTransition exit=\"slide-down\">\n          <VideoInfoFallback />\n        </ViewTransition>\n      }\n    >\n      {/* Animate the content up */}\n      <ViewTransition enter=\"slide-up\">\n        <VideoInfo id={id} />\n      </ViewTransition>\n    </Suspense>\n  );\n}\n\nfunction VideoInfoFallback() {\n  return (\n    <>\n      <div className=\"fallback title\"></div>\n      <div className=\"fallback description\"></div>\n    </>\n  );\n}\n\nexport default function Details() {\n  const { url, navigateBack } = useRouter();\n  const videoId = url.split(\"/\").pop();\n  const video = use(fetchVideo(videoId));\n\n  return (\n    <Layout\n      heading={\n        <div\n          className=\"fit back\"\n          onClick={() => {\n            navigateBack(\"/\");\n          }}\n        >\n          <ChevronLeft /> Back\n        </div>\n      }\n    >\n      <div className=\"details\">\n        <Thumbnail video={video} large>\n          <VideoControls />\n        </Thumbnail>\n        <VideoDetails id={video.id} />\n      </div>\n    </Layout>\n  );\n}\n\nfunction VideoInfo({ id }) {\n  const details = use(fetchVideoDetails(id));\n  return (\n    <>\n      <p className=\"info-title\">{details.title}</p>\n      <p className=\"info-description\">{details.description}</p>\n    </>\n  );\n}\n```\n\n```js src/Home.js\nimport { useId, useState, use, useDeferredValue, ViewTransition } from \"react\";import { Video } from \"./Videos\";import Layout from \"./Layout\";import { fetchVideos } from \"./data\";import { IconSearch } from \"./Icons\";\n\nfunction SearchList({searchText, videos}) {\n  // Activate with useDeferredValue (\"when\")\n  const deferredSearchText = useDeferredValue(searchText);\n  const filteredVideos = filterVideos(videos, deferredSearchText);\n  return (\n    <div className=\"video-list\">\n      <div className=\"videos\">\n        {filteredVideos.map((video) => (\n          // Animate each item in list (\"what\")\n          <ViewTransition key={video.id}>\n            <Video video={video} />\n          </ViewTransition>\n        ))}\n      </div>\n      {filteredVideos.length === 0 && (\n        <div className=\"no-results\">No results</div>\n      )}\n    </div>\n  );\n}\n\nexport default function Home() {\n  const videos = use(fetchVideos());\n  const count = videos.length;\n  const [searchText, setSearchText] = useState('');\n\n  return (\n    <Layout heading={<div className=\"fit\">{count} Videos</div>}>\n      <SearchInput value={searchText} onChange={setSearchText} />\n      <SearchList videos={videos} searchText={searchText} />\n    </Layout>\n  );\n}\n\nfunction SearchInput({ value, onChange }) {\n  const id = useId();\n  return (\n    <form className=\"search\" onSubmit={(e) => e.preventDefault()}>\n      <label htmlFor={id} className=\"sr-only\">\n        Search\n      </label>\n      <div className=\"search-input\">\n        <div className=\"search-icon\">\n          <IconSearch />\n        </div>\n        <input\n          type=\"text\"\n          id={id}\n          placeholder=\"Search\"\n          value={value}\n          onChange={(e) => onChange(e.target.value)}\n        />\n      </div>\n    </form>\n  );\n}\n\nfunction filterVideos(videos, query) {\n  const keywords = query\n    .toLowerCase()\n    .split(\" \")\n    .filter((s) => s !== \"\");\n  if (keywords.length === 0) {\n    return videos;\n  }\n  return videos.filter((video) => {\n    const words = (video.title + \" \" + video.description)\n      .toLowerCase()\n      .split(\" \");\n    return keywords.every((kw) => words.some((w) => w.includes(kw)));\n  });\n}\n```\n\n```js src/Icons.js hidden\nexport function ChevronLeft() {\n  return (\n    <svg\n      className=\"chevron-left\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\">\n      <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-446 -398)\">\n        <path\n          fill=\"currentColor\"\n          fillRule=\"nonzero\"\n          d=\"M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z\"\n          transform=\"translate(356.5 164.5)\"\n        />\n        <polygon points=\"446 418 466 418 466 398 446 398\" />\n      </g>\n    </svg>\n  );\n}\n\nexport function PauseIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      style={{padding: '4px'}}\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 512 512\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M256 0C114.617 0 0 114.615 0 256s114.617 256 256 256 256-114.615 256-256S397.383 0 256 0zm-32 320c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128zm128 0c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n\nexport function PlayIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 72 72\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M36 69C54.2254 69 69 54.2254 69 36C69 17.7746 54.2254 3 36 3C17.7746 3 3 17.7746 3 36C3 54.2254 17.7746 69 36 69ZM52.1716 38.6337L28.4366 51.5801C26.4374 52.6705 24 51.2235 24 48.9464V23.0536C24 20.7764 26.4374 19.3295 28.4366 20.4199L52.1716 33.3663C54.2562 34.5034 54.2562 37.4966 52.1716 38.6337Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\nexport function Heart({liked, animate}) {\n  return (\n    <>\n      <svg\n        className=\"absolute overflow-visible\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <circle\n          className={`circle ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n          cx=\"12\"\n          cy=\"12\"\n          r=\"11.5\"\n          fill=\"transparent\"\n          strokeWidth=\"0\"\n          stroke=\"currentColor\"\n        />\n      </svg>\n\n      <svg\n        className={`heart ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        {liked ? (\n          <path\n            d=\"M12 23a.496.496 0 0 1-.26-.074C7.023 19.973 0 13.743 0 8.68c0-4.12 2.322-6.677 6.058-6.677 2.572 0 5.108 2.387 5.134 2.41l.808.771.808-.771C12.834 4.387 15.367 2 17.935 2 21.678 2 24 4.558 24 8.677c0 5.06-7.022 11.293-11.74 14.246a.496.496 0 0 1-.26.074V23z\"\n            fill=\"currentColor\"\n          />\n        ) : (\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"m12 5.184-.808-.771-.004-.004C11.065 4.299 8.522 2.003 6 2.003c-3.736 0-6 2.558-6 6.677 0 4.47 5.471 9.848 10 13.079.602.43 1.187.82 1.74 1.167A.497.497 0 0 0 12 23v-.003c.09 0 .182-.026.26-.074C16.977 19.97 24 13.737 24 8.677 24 4.557 21.743 2 18 2c-2.569 0-5.166 2.387-5.192 2.413L12 5.184zm-.002 15.525c2.071-1.388 4.477-3.342 6.427-5.47C20.72 12.733 22 10.401 22 8.677c0-1.708-.466-2.855-1.087-3.55C20.316 4.459 19.392 4 18 4c-.726 0-1.63.364-2.5.9-.67.412-1.148.82-1.266.92-.03.025-.037.031-.019.014l-.013.013L12 7.949 9.832 5.88a10.08 10.08 0 0 0-1.33-.977C7.633 4.367 6.728 4.003 6 4.003c-1.388 0-2.312.459-2.91 1.128C2.466 5.826 2 6.974 2 8.68c0 1.726 1.28 4.058 3.575 6.563 1.948 2.127 4.352 4.078 6.423 5.466z\"\n            fill=\"currentColor\"\n          />\n        )}\n      </svg>\n    </>\n  );\n}\n\nexport function IconSearch(props) {\n  return (\n    <svg width=\"1em\" height=\"1em\" viewBox=\"0 0 20 20\">\n      <path\n        d=\"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z\"\n        stroke=\"currentColor\"\n        fill=\"none\"\n        strokeWidth=\"2\"\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"></path>\n    </svg>\n  );\n}\n```\n\n```js src/Layout.js\nimport {ViewTransition} from 'react'; import { useIsNavPending } from \"./router\";\n\nexport default function Page({ heading, children }) {\n  const isPending = useIsNavPending();\n  return (\n    <div className=\"page\">\n      <div className=\"top\">\n        <div className=\"top-nav\">\n          {/* Custom classes based on transition type. */}\n          <ViewTransition\n            name=\"nav\"\n            share={{\n              'nav-forward': 'slide-forward',\n              'nav-back': 'slide-back',\n            }}>\n            {heading}\n          </ViewTransition>\n          {isPending && <span className=\"loader\"></span>}\n        </div>\n      </div>\n      {/* Opt-out of ViewTransition for the content. */}\n      {/* Content can define it's own ViewTransition. */}\n      <ViewTransition default=\"none\">\n        <div className=\"bottom\">\n          <div className=\"content\">{children}</div>\n        </div>\n      </ViewTransition>\n    </div>\n  );\n}\n```\n\n```js src/LikeButton.js hidden\nimport {useState} from 'react';\nimport {Heart} from './Icons';\n\n// A hack since we don't actually have a backend.\n// Unlike local state, this survives videos being filtered.\nconst likedVideos = new Set();\n\nexport default function LikeButton({video}) {\n  const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));\n  const [animate, setAnimate] = useState(false);\n  return (\n    <button\n      className={`like-button ${isLiked && 'liked'}`}\n      aria-label={isLiked ? 'Unsave' : 'Save'}\n      onClick={() => {\n        const nextIsLiked = !isLiked;\n        if (nextIsLiked) {\n          likedVideos.add(video.id);\n        } else {\n          likedVideos.delete(video.id);\n        }\n        setAnimate(true);\n        setIsLiked(nextIsLiked);\n      }}>\n      <Heart liked={isLiked} animate={animate} />\n    </button>\n  );\n}\n```\n\n```js src/Videos.js\nimport { useState, ViewTransition } from \"react\"; import LikeButton from \"./LikeButton\"; import { useRouter } from \"./router\"; import { PauseIcon, PlayIcon } from \"./Icons\"; import { startTransition } from \"react\";\n\nexport function Thumbnail({ video, children }) {\n  // Add a name to animate with a shared element transition.\n  return (\n    <ViewTransition name={`video-${video.id}`}>\n      <div\n        aria-hidden=\"true\"\n        tabIndex={-1}\n        className={`thumbnail ${video.image}`}\n      >\n        {children}\n      </div>\n    </ViewTransition>\n  );\n}\n\n\n\nexport function VideoControls() {\n  const [isPlaying, setIsPlaying] = useState(false);\n\n  return (\n    <span\n      className=\"controls\"\n      onClick={() =>\n        startTransition(() => {\n          setIsPlaying((p) => !p);\n        })\n      }\n    >\n      {isPlaying ? <PauseIcon /> : <PlayIcon />}\n    </span>\n  );\n}\n\nexport function Video({ video }) {\n  const { navigate } = useRouter();\n\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n        onClick={(e) => {\n          e.preventDefault();\n          navigate(`/video/${video.id}`);\n        }}\n      >\n        <Thumbnail video={video}></Thumbnail>\n\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n      <LikeButton video={video} />\n    </div>\n  );\n}\n```\n\n\n```js src/data.js hidden\nconst videos = [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  },\n  {\n    id: '2',\n    title: 'Second video',\n    description: 'Video description',\n    image: 'red',\n  },\n  {\n    id: '3',\n    title: 'Third video',\n    description: 'Video description',\n    image: 'green',\n  },\n  {\n    id: '4',\n    title: 'Fourth video',\n    description: 'Video description',\n    image: 'purple',\n  },\n  {\n    id: '5',\n    title: 'Fifth video',\n    description: 'Video description',\n    image: 'yellow',\n  },\n  {\n    id: '6',\n    title: 'Sixth video',\n    description: 'Video description',\n    image: 'gray',\n  },\n];\n\nlet videosCache = new Map();\nlet videoCache = new Map();\nlet videoDetailsCache = new Map();\nconst VIDEO_DELAY = 1;\nconst VIDEO_DETAILS_DELAY = 1000;\nexport function fetchVideos() {\n  if (videosCache.has(0)) {\n    return videosCache.get(0);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos);\n    }, VIDEO_DELAY);\n  });\n  videosCache.set(0, promise);\n  return promise;\n}\n\nexport function fetchVideo(id) {\n  if (videoCache.has(id)) {\n    return videoCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DELAY);\n  });\n  videoCache.set(id, promise);\n  return promise;\n}\n\nexport function fetchVideoDetails(id) {\n  if (videoDetailsCache.has(id)) {\n    return videoDetailsCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DETAILS_DELAY);\n  });\n  videoDetailsCache.set(id, promise);\n  return promise;\n}\n```\n\n```js src/router.js\nimport {useState, createContext, use, useTransition, useLayoutEffect, useEffect, addTransitionType} from \"react\";\n\nexport function Router({ children }) {\n  const [isPending, startTransition] = useTransition();\n  function navigate(url) {\n    startTransition(() => {\n      // Transition type for the cause \"nav forward\"\n      addTransitionType('nav-forward');\n      go(url);\n    });\n  }\n  function navigateBack(url) {\n    startTransition(() => {\n      // Transition type for the cause \"nav backward\"\n      addTransitionType('nav-back');\n      go(url);\n    });\n  }\n\n  const [routerState, setRouterState] = useState({pendingNav: () => {}, url: document.location.pathname});\n\n  function go(url) {\n    setRouterState({\n      url,\n      pendingNav() {\n        window.history.pushState({}, \"\", url);\n      },\n    });\n  }\n\n  useEffect(() => {\n    function handlePopState() {\n      // This should not animate because restoration has to be synchronous.\n      // Even though it's a transition.\n      startTransition(() => {\n        setRouterState({\n          url: document.location.pathname + document.location.search,\n          pendingNav() {\n            // Noop. URL has already updated.\n          },\n        });\n      });\n    }\n    window.addEventListener(\"popstate\", handlePopState);\n    return () => {\n      window.removeEventListener(\"popstate\", handlePopState);\n    };\n  }, []);\n  const pendingNav = routerState.pendingNav;\n  useLayoutEffect(() => {\n    pendingNav();\n  }, [pendingNav]);\n\n  return (\n    <RouterContext\n      value={{\n        url: routerState.url,\n        navigate,\n        navigateBack,\n        isPending,\n        params: {},\n      }}\n    >\n      {children}\n    </RouterContext>\n  );\n}\n\nconst RouterContext = createContext({ url: \"/\", params: {} });\n\nexport function useRouter() {\n  return use(RouterContext);\n}\n\nexport function useIsNavPending() {\n  return use(RouterContext).isPending;\n}\n\n```\n\n```css src/styles.css hidden\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Rg.woff2) format(\"woff2\");\n  font-weight: 400;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Md.woff2) format(\"woff2\");\n  font-weight: 500;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 600;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 700;\n  font-style: normal;\n  font-display: swap;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhtml {\n  background-image: url(https://react.dev/images/meta-gradient-dark.png);\n  background-size: 100%;\n  background-position: -100%;\n  background-color: rgb(64 71 86);\n  background-repeat: no-repeat;\n  height: 100%;\n  width: 100%;\n}\n\nbody {\n  font-family: Optimistic Text, -apple-system, ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;\n  padding: 10px 0 10px 0;\n  margin: 0;\n  display: flex;\n  justify-content: center;\n}\n\n#root {\n  flex: 1 1;\n  height: auto;\n  background-color: #fff;\n  border-radius: 10px;\n  max-width: 450px;\n  min-height: 600px;\n  padding-bottom: 10px;\n}\n\nh1 {\n  margin-top: 0;\n  font-size: 22px;\n}\n\nh2 {\n  margin-top: 0;\n  font-size: 20px;\n}\n\nh3 {\n  margin-top: 0;\n  font-size: 18px;\n}\n\nh4 {\n  margin-top: 0;\n  font-size: 16px;\n}\n\nh5 {\n  margin-top: 0;\n  font-size: 14px;\n}\n\nh6 {\n  margin-top: 0;\n  font-size: 12px;\n}\n\ncode {\n  font-size: 1.2em;\n}\n\nul {\n  padding-inline-start: 20px;\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border-width: 0;\n}\n\n.absolute {\n  position: absolute;\n}\n\n.overflow-visible {\n  overflow: visible;\n}\n\n.visible {\n  overflow: visible;\n}\n\n.fit {\n  width: fit-content;\n}\n\n\n/* Layout */\n.page {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n}\n\n.top-hero {\n  height: 200px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-image: conic-gradient(\n      from 90deg at -10% 100%,\n      #2b303b 0deg,\n      #2b303b 90deg,\n      #16181d 1turn\n  );\n}\n\n.bottom {\n  flex: 1;\n  overflow: auto;\n}\n\n.top-nav {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 0;\n  padding: 0 12px;\n  top: 0;\n  width: 100%;\n  height: 44px;\n  color: #23272f;\n  font-weight: 700;\n  font-size: 20px;\n  z-index: 100;\n  cursor: default;\n}\n\n.content {\n  padding: 0 12px;\n  margin-top: 4px;\n}\n\n\n.loader {\n  color: #23272f;\n  font-size: 3px;\n  width: 1em;\n  margin-right: 18px;\n  height: 1em;\n  border-radius: 50%;\n  position: relative;\n  text-indent: -9999em;\n  animation: loading-spinner 1.3s infinite linear;\n  animation-delay: 200ms;\n  transform: translateZ(0);\n}\n\n@keyframes loading-spinner {\n  0%,\n  100% {\n    box-shadow: 0 -3em 0 0.2em,\n    2em -2em 0 0em, 3em 0 0 -1em,\n    2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 0;\n  }\n  12.5% {\n    box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em,\n    3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  25% {\n    box-shadow: 0 -3em 0 -0.5em,\n    2em -2em 0 0, 3em 0 0 0.2em,\n    2em 2em 0 0, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  37.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em,\n    -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  50% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em,\n    -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  62.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0,\n    -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em;\n  }\n  75% {\n    box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0;\n  }\n  87.5% {\n    box-shadow: 0em -3em 0 0, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;\n  }\n}\n\n/* LikeButton */\n.like-button {\n  outline-offset: 2px;\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 2.5rem;\n  height: 2.5rem;\n  cursor: pointer;\n  border-radius: 9999px;\n  border: none;\n  outline: none 2px;\n  color: #5e687e;\n  background: none;\n}\n\n.like-button:focus {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n}\n\n.like-button:active {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n  transform: scaleX(0.95) scaleY(0.95);\n}\n\n.like-button:hover {\n  background-color: #f6f7f9;\n}\n\n.like-button.liked {\n  color: #a6423a;\n}\n\n/* Icons */\n@keyframes circle {\n  0% {\n    transform: scale(0);\n    stroke-width: 16px;\n  }\n\n  50% {\n    transform: scale(.5);\n    stroke-width: 16px;\n  }\n\n  to {\n    transform: scale(1);\n    stroke-width: 0;\n  }\n}\n\n.circle {\n  color: rgba(166, 66, 58, .5);\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4,0,.2,1);\n}\n\n.circle.liked.animate {\n  animation: circle .3s forwards;\n}\n\n.heart {\n  width: 1.5rem;\n  height: 1.5rem;\n}\n\n.heart.liked {\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4, 0, .2, 1);\n}\n\n.heart.liked.animate {\n  animation: scale .35s ease-in-out forwards;\n}\n\n.control-icon {\n  color: hsla(0, 0%, 100%, .5);\n  filter:  drop-shadow(0 20px 13px rgba(0, 0, 0, .03)) drop-shadow(0 8px 5px rgba(0, 0, 0, .08));\n}\n\n.chevron-left {\n  margin-top: 2px;\n  rotate: 90deg;\n}\n\n\n/* Video */\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n\n.thumbnail.red {\n  background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);\n}\n\n.thumbnail.green {\n  background-image: conic-gradient(at top right, #c76a15, #388f7f, #2b3491);\n}\n\n.thumbnail.purple {\n  background-image: conic-gradient(at top right, #c76a15, #575fb7, #2b3491);\n}\n\n.thumbnail.yellow {\n  background-image: conic-gradient(at top right, #c76a15, #FABD62, #2b3491);\n}\n\n.thumbnail.gray {\n  background-image: conic-gradient(at top right, #c76a15, #4E5769, #2b3491);\n}\n\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n}\n\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n\n.video .info:hover {\n  text-decoration: underline;\n}\n\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n\n/* Details */\n.details .thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 100%;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.video-details-title {\n  margin-top: 8px;\n}\n\n.video-details-speaker {\n  display: flex;\n  gap: 8px;\n  margin-top: 10px\n}\n\n.back {\n  display: flex;\n  align-items: center;\n  margin-left: -5px;\n  cursor: pointer;\n}\n\n.back:hover {\n  text-decoration: underline;\n}\n\n.info-title {\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1.25;\n  margin: 8px 0 0 0 ;\n}\n\n.info-description {\n  margin: 8px 0 0 0;\n}\n\n.controls {\n  cursor: pointer;\n}\n\n.fallback {\n  background: #f6f7f8 linear-gradient(to right, #e6e6e6 5%, #cccccc 25%, #e6e6e6 35%) no-repeat;\n  background-size: 800px 104px;\n  display: block;\n  line-height: 1.25;\n  margin: 8px 0 0 0;\n  border-radius: 5px;\n  overflow: hidden;\n\n  animation: 1s linear 1s infinite shimmer;\n  animation-delay: 300ms;\n  animation-duration: 1s;\n  animation-fill-mode: forwards;\n  animation-iteration-count: infinite;\n  animation-name: shimmer;\n  animation-timing-function: linear;\n}\n\n\n.fallback.title {\n  width: 130px;\n  height: 30px;\n\n}\n\n.fallback.description {\n  width: 150px;\n  height: 21px;\n}\n\n@keyframes shimmer {\n  0% {\n    background-position: -468px 0;\n  }\n\n  100% {\n    background-position: 468px 0;\n  }\n}\n\n.search {\n  margin-bottom: 10px;\n}\n.search-input {\n  width: 100%;\n  position: relative;\n}\n\n.search-icon {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  inset-inline-start: 0;\n  display: flex;\n  align-items: center;\n  padding-inline-start: 1rem;\n  pointer-events: none;\n  color: #99a1b3;\n}\n\n.search-input input {\n  display: flex;\n  padding-inline-start: 2.75rem;\n  padding-top: 10px;\n  padding-bottom: 10px;\n  width: 100%;\n  text-align: start;\n  background-color: rgb(235 236 240);\n  outline: 2px solid transparent;\n  cursor: pointer;\n  border: none;\n  align-items: center;\n  color: rgb(35 39 47);\n  border-radius: 9999px;\n  vertical-align: middle;\n  font-size: 15px;\n}\n\n.search-input input:hover, .search-input input:active {\n  background-color: rgb(235 236 240/ 0.8);\n  color: rgb(35 39 47/ 0.8);\n}\n\n/* Home */\n.video-list {\n  position: relative;\n}\n\n.video-list .videos {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n  overflow-y: auto;\n  height: 100%;\n}\n```\n\n\n```css src/animations.css\n/* Slide animations for Suspense the fallback down */\n::view-transition-old(.slide-down) {\n    animation: 150ms ease-out both fade-out, 150ms ease-out both slide-down;\n}\n\n::view-transition-new(.slide-up) {\n    animation: 210ms ease-in 150ms both fade-in, 400ms ease-in both slide-up;\n}\n\n/* Animations for view transition classed added by transition type */\n::view-transition-old(.slide-forward) {\n    /* when sliding forward, the \"old\" page should slide out to left. */\n    animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;\n}\n\n::view-transition-new(.slide-forward) {\n    /* when sliding forward, the \"new\" page should slide in from right. */\n    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;\n}\n\n::view-transition-old(.slide-back) {\n    /* when sliding back, the \"old\" page should slide out to right. */\n    animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;\n}\n\n::view-transition-new(.slide-back) {\n    /* when sliding back, the \"new\" page should slide in from left. */\n    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-left;\n}\n\n/* Keyframes to support our animations above. */\n@keyframes slide-up {\n    from {\n        transform: translateY(10px);\n    }\n    to {\n        transform: translateY(0);\n    }\n}\n\n@keyframes slide-down {\n    from {\n        transform: translateY(0);\n    }\n    to {\n        transform: translateY(10px);\n    }\n}\n\n@keyframes fade-in {\n    from {\n        opacity: 0;\n    }\n}\n\n@keyframes fade-out {\n    to {\n        opacity: 0;\n    }\n}\n\n@keyframes slide-to-right {\n    to {\n        transform: translateX(50px);\n    }\n}\n\n@keyframes slide-from-right {\n    from {\n        transform: translateX(50px);\n    }\n    to {\n        transform: translateX(0);\n    }\n}\n\n@keyframes slide-to-left {\n    to {\n        transform: translateX(-50px);\n    }\n}\n\n@keyframes slide-from-left {\n    from {\n        transform: translateX(-50px);\n    }\n    to {\n        transform: translateX(0);\n    }\n}\n```\n\n```js src/index.js hidden\nimport React, {StrictMode} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport './styles.css';\nimport './animations.css';\n\nimport App from './App';\nimport {Router} from './router';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <Router>\n      <App />\n    </Router>\n  </StrictMode>\n);\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\nこれらがどのように機能するかについてもっと知りたい場合は、ドキュメントで [`<ViewTransition>` の動作の仕組み](/reference/react/ViewTransition#how-does-viewtransition-work)をチェックしてください。\n\n_ビュー遷移機能の開発経緯についての詳細は、[@sebmarkbage](https://twitter.com/sebmarkbage) による [#31975](https://github.com/facebook/react/pull/31975)、[#32105](https://github.com/facebook/react/pull/32105)、[#32041](https://github.com/facebook/react/pull/32041)、[#32734](https://github.com/facebook/react/pull/32734)、[#32797](https://github.com/facebook/react/pull/32797)、[#31999](https://github.com/facebook/react/pull/31999)、[#32031](https://github.com/facebook/react/pull/32031)、[#32050](https://github.com/facebook/react/pull/32050)、[#32820](https://github.com/facebook/react/pull/32820)、[#32029](https://github.com/facebook/react/pull/32029)、[#32028](https://github.com/facebook/react/pull/32028)、および [#32038](https://github.com/facebook/react/pull/32038) を参照してください (thanks Seb!)。_\n\n---\n\n## Activity {/*activity*/}\n\n<Note>\n\n**`<Activity />` は React の Canary チャンネルで利用可能となりました**。\n\n[React のリリースチャンネルについてさらに読む](/community/versioning-policy#all-release-channels)\n\n</Note>\n\n[過去の](/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022#offscreen) [お知らせ](/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024#offscreen-renamed-to-activity) で、コンポーネントを視覚的に隠して優先度を下げることで、UI の state を保持しつつ、アンマウントや CSS による非表示と比べてパフォーマンスコストを削減する API を研究していることをお話ししました。\n\nこの API とその仕組みを共有する準備が整い、React の実験的バージョンでテストを始められるようになりました。\n\n`<Activity>` は、UI を部分的に隠したり表示したりするための新しいコンポーネントです。\n\n```js [[1, 1, \"'visible'\"], [2, 1, \"'hidden'\"]]\n<Activity mode={isVisible ? 'visible' : 'hidden'}>\n  <Page />\n</Activity>\n```\n\nActivity が <CodeStep step={1}>visible</CodeStep> のときは通常通りレンダーされます。Activity が <CodeStep step={2}>hidden</CodeStep> のときはアンマウントされますが、その state を保存し、画面上に表示されているものよりも低い優先度でレンダーを続けます。\n\n`Activity` を使用して、ユーザが使用していない UI の state を保存したり、ユーザが次に使用する可能性が高い部分を事前にレンダーしておくことが可能です。\n\n以下で、上記のビュー遷移サンプルを改善する例を見ていきましょう。\n\n<Note>\n\n**Activity が非表示のとき、エフェクトはマウントされない**\n\n`<Activity>` が `hidden` のとき、エフェクトはアンマウントされます。概念的にはコンポーネントはアンマウントされているのですが、React は state を後で使用するために保存します。\n\n実際には、[そのエフェクトは不要かも](/learn/you-might-not-need-an-effect)のガイドに従っている限りは、これは期待通りに動作します。問題のあるエフェクトを積極的に見つけるために、[`<StrictMode>`](/reference/react/StrictMode) を追加することをお勧めします。これにより、予期しない副作用をキャッチするために Activity のアンマウントとマウントが積極的に行われます。\n\n</Note>\n\n### Activity で state を復元する {/*restoring-state-with-activity*/}\n\nユーザがページから離れる際は、以前のページのレンダーを止めてしまうのが一般的です。\n\n```js {6,7}\nfunction App() {\n  const { url } = useRouter();\n\n  return (\n    <>\n      {url === '/' && <Home />}\n      {url !== '/' && <Details />}\n    </>\n  );\n}\n```\n\nしかしこれでは、ユーザが前のページに戻ったとき、以前のすべての state が失われていることになります。たとえば、`<Home />` ページに `<input>` フィールドがある場合、ユーザがページを離れると `<input>` はアンマウントされ、入力したすべてのテキストは消えてしまいます。\n\nActivity を使用すると、ユーザがページを移動しても state を保持できるため、戻ってきたときに以前の状態から再開できます。これは、ツリーの一部を `<Activity>` でラップし、`mode` を切り替えることで実現できます。\n\n```js {6-8}\nfunction App() {\n  const { url } = useRouter();\n\n  return (\n    <>\n      <Activity mode={url === '/' ? 'visible' : 'hidden'}>\n        <Home />\n      </Activity>\n      {url !== '/' && <Details />}\n    </>\n  );\n}\n```\n\n以前のビュー遷移の例を、これで改善できます。以前は、ビデオを検索し、選択して戻ってくると、入力された検索フィルタが失われていました。Activity を使用すると検索フィルタが復元され、以前の状態から再開できます。\n\nビデオを検索し、選択したあと \"Back\" をクリックしてみてください。\n\n<Sandpack>\n\n```js src/App.js\nimport { Activity, ViewTransition } from \"react\"; import Details from \"./Details\"; import Home from \"./Home\"; import { useRouter } from \"./router\";\n\nexport default function App() {\n  const { url } = useRouter();\n\n  return (\n    // View Transitions know about Activity\n    <ViewTransition>\n      {/* Render Home in Activity so we don't lose state */}\n      <Activity mode={url === '/' ? 'visible' : 'hidden'}>\n        <Home />\n      </Activity>\n      {url !== '/' && <Details />}\n    </ViewTransition>\n  );\n}\n```\n\n```js src/Details.js hidden\nimport { use, Suspense, ViewTransition } from \"react\";\nimport { fetchVideo, fetchVideoDetails } from \"./data\";\nimport { Thumbnail, VideoControls } from \"./Videos\";\nimport { useRouter } from \"./router\";\nimport Layout from \"./Layout\";\nimport { ChevronLeft } from \"./Icons\";\n\nfunction VideoDetails({id}) {\n  // Animate from Suspense fallback to content\n  return (\n    <Suspense\n      fallback={\n        // Animate the fallback down.\n        <ViewTransition exit=\"slide-down\">\n          <VideoInfoFallback />\n        </ViewTransition>\n      }\n    >\n      {/* Animate the content up */}\n      <ViewTransition enter=\"slide-up\">\n        <VideoInfo id={id} />\n      </ViewTransition>\n    </Suspense>\n  );\n}\n\nfunction VideoInfoFallback() {\n  return (\n    <>\n      <div className=\"fallback title\"></div>\n      <div className=\"fallback description\"></div>\n    </>\n  );\n}\n\nexport default function Details() {\n  const { url, navigateBack } = useRouter();\n  const videoId = url.split(\"/\").pop();\n  const video = use(fetchVideo(videoId));\n\n  return (\n    <Layout\n      heading={\n        <div\n          className=\"fit back\"\n          onClick={() => {\n            navigateBack(\"/\");\n          }}\n        >\n          <ChevronLeft /> Back\n        </div>\n      }\n    >\n      <div className=\"details\">\n        <Thumbnail video={video} large>\n          <VideoControls />\n        </Thumbnail>\n        <VideoDetails id={video.id} />\n      </div>\n    </Layout>\n  );\n}\n\nfunction VideoInfo({ id }) {\n  const details = use(fetchVideoDetails(id));\n  return (\n    <>\n      <p className=\"info-title\">{details.title}</p>\n      <p className=\"info-description\">{details.description}</p>\n    </>\n  );\n}\n```\n\n```js src/Home.js hidden\nimport { useId, useState, use, useDeferredValue, ViewTransition } from \"react\";import { Video } from \"./Videos\";import Layout from \"./Layout\";import { fetchVideos } from \"./data\";import { IconSearch } from \"./Icons\";\n\nfunction SearchList({searchText, videos}) {\n  // Activate with useDeferredValue (\"when\")\n  const deferredSearchText = useDeferredValue(searchText);\n  const filteredVideos = filterVideos(videos, deferredSearchText);\n  return (\n    <div className=\"video-list\">\n      {filteredVideos.length === 0 && (\n        <div className=\"no-results\">No results</div>\n      )}\n      <div className=\"videos\">\n        {filteredVideos.map((video) => (\n          // Animate each item in list (\"what\")\n          <ViewTransition key={video.id}>\n            <Video video={video} />\n          </ViewTransition>\n        ))}\n      </div>\n    </div>\n  );\n}\n\nexport default function Home() {\n  const videos = use(fetchVideos());\n  const count = videos.length;\n  const [searchText, setSearchText] = useState('');\n\n  return (\n    <Layout heading={<div className=\"fit\">{count} Videos</div>}>\n      <SearchInput value={searchText} onChange={setSearchText} />\n      <SearchList videos={videos} searchText={searchText} />\n    </Layout>\n  );\n}\n\nfunction SearchInput({ value, onChange }) {\n  const id = useId();\n  return (\n    <form className=\"search\" onSubmit={(e) => e.preventDefault()}>\n      <label htmlFor={id} className=\"sr-only\">\n        Search\n      </label>\n      <div className=\"search-input\">\n        <div className=\"search-icon\">\n          <IconSearch />\n        </div>\n        <input\n          type=\"text\"\n          id={id}\n          placeholder=\"Search\"\n          value={value}\n          onChange={(e) => onChange(e.target.value)}\n        />\n      </div>\n    </form>\n  );\n}\n\nfunction filterVideos(videos, query) {\n  const keywords = query\n    .toLowerCase()\n    .split(\" \")\n    .filter((s) => s !== \"\");\n  if (keywords.length === 0) {\n    return videos;\n  }\n  return videos.filter((video) => {\n    const words = (video.title + \" \" + video.description)\n      .toLowerCase()\n      .split(\" \");\n    return keywords.every((kw) => words.some((w) => w.includes(kw)));\n  });\n}\n```\n\n```js src/Icons.js hidden\nexport function ChevronLeft() {\n  return (\n    <svg\n      className=\"chevron-left\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\">\n      <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-446 -398)\">\n        <path\n          fill=\"currentColor\"\n          fillRule=\"nonzero\"\n          d=\"M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z\"\n          transform=\"translate(356.5 164.5)\"\n        />\n        <polygon points=\"446 418 466 418 466 398 446 398\" />\n      </g>\n    </svg>\n  );\n}\n\nexport function PauseIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      style={{padding: '4px'}}\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 512 512\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M256 0C114.617 0 0 114.615 0 256s114.617 256 256 256 256-114.615 256-256S397.383 0 256 0zm-32 320c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128zm128 0c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n\nexport function PlayIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 72 72\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M36 69C54.2254 69 69 54.2254 69 36C69 17.7746 54.2254 3 36 3C17.7746 3 3 17.7746 3 36C3 54.2254 17.7746 69 36 69ZM52.1716 38.6337L28.4366 51.5801C26.4374 52.6705 24 51.2235 24 48.9464V23.0536C24 20.7764 26.4374 19.3295 28.4366 20.4199L52.1716 33.3663C54.2562 34.5034 54.2562 37.4966 52.1716 38.6337Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\nexport function Heart({liked, animate}) {\n  return (\n    <>\n      <svg\n        className=\"absolute overflow-visible\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <circle\n          className={`circle ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n          cx=\"12\"\n          cy=\"12\"\n          r=\"11.5\"\n          fill=\"transparent\"\n          strokeWidth=\"0\"\n          stroke=\"currentColor\"\n        />\n      </svg>\n\n      <svg\n        className={`heart ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        {liked ? (\n          <path\n            d=\"M12 23a.496.496 0 0 1-.26-.074C7.023 19.973 0 13.743 0 8.68c0-4.12 2.322-6.677 6.058-6.677 2.572 0 5.108 2.387 5.134 2.41l.808.771.808-.771C12.834 4.387 15.367 2 17.935 2 21.678 2 24 4.558 24 8.677c0 5.06-7.022 11.293-11.74 14.246a.496.496 0 0 1-.26.074V23z\"\n            fill=\"currentColor\"\n          />\n        ) : (\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"m12 5.184-.808-.771-.004-.004C11.065 4.299 8.522 2.003 6 2.003c-3.736 0-6 2.558-6 6.677 0 4.47 5.471 9.848 10 13.079.602.43 1.187.82 1.74 1.167A.497.497 0 0 0 12 23v-.003c.09 0 .182-.026.26-.074C16.977 19.97 24 13.737 24 8.677 24 4.557 21.743 2 18 2c-2.569 0-5.166 2.387-5.192 2.413L12 5.184zm-.002 15.525c2.071-1.388 4.477-3.342 6.427-5.47C20.72 12.733 22 10.401 22 8.677c0-1.708-.466-2.855-1.087-3.55C20.316 4.459 19.392 4 18 4c-.726 0-1.63.364-2.5.9-.67.412-1.148.82-1.266.92-.03.025-.037.031-.019.014l-.013.013L12 7.949 9.832 5.88a10.08 10.08 0 0 0-1.33-.977C7.633 4.367 6.728 4.003 6 4.003c-1.388 0-2.312.459-2.91 1.128C2.466 5.826 2 6.974 2 8.68c0 1.726 1.28 4.058 3.575 6.563 1.948 2.127 4.352 4.078 6.423 5.466z\"\n            fill=\"currentColor\"\n          />\n        )}\n      </svg>\n    </>\n  );\n}\n\nexport function IconSearch(props) {\n  return (\n    <svg width=\"1em\" height=\"1em\" viewBox=\"0 0 20 20\">\n      <path\n        d=\"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z\"\n        stroke=\"currentColor\"\n        fill=\"none\"\n        strokeWidth=\"2\"\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"></path>\n    </svg>\n  );\n}\n```\n\n```js src/Layout.js hidden\nimport {ViewTransition} from 'react'; import { useIsNavPending } from \"./router\";\n\nexport default function Page({ heading, children }) {\n  const isPending = useIsNavPending();\n  return (\n    <div className=\"page\">\n      <div className=\"top\">\n        <div className=\"top-nav\">\n          {/* Custom classes based on transition type. */}\n          <ViewTransition\n            name=\"nav\"\n            share={{\n              'nav-forward': 'slide-forward',\n              'nav-back': 'slide-back',\n            }}>\n            {heading}\n          </ViewTransition>\n          {isPending && <span className=\"loader\"></span>}\n        </div>\n      </div>\n      {/* Opt-out of ViewTransition for the content. */}\n      {/* Content can define it's own ViewTransition. */}\n      <ViewTransition default=\"none\">\n        <div className=\"bottom\">\n          <div className=\"content\">{children}</div>\n        </div>\n      </ViewTransition>\n    </div>\n  );\n}\n```\n\n```js src/LikeButton.js hidden\nimport {useState} from 'react';\nimport {Heart} from './Icons';\n\n// A hack since we don't actually have a backend.\n// Unlike local state, this survives videos being filtered.\nconst likedVideos = new Set();\n\nexport default function LikeButton({video}) {\n  const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));\n  const [animate, setAnimate] = useState(false);\n  return (\n    <button\n      className={`like-button ${isLiked && 'liked'}`}\n      aria-label={isLiked ? 'Unsave' : 'Save'}\n      onClick={() => {\n        const nextIsLiked = !isLiked;\n        if (nextIsLiked) {\n          likedVideos.add(video.id);\n        } else {\n          likedVideos.delete(video.id);\n        }\n        setAnimate(true);\n        setIsLiked(nextIsLiked);\n      }}>\n      <Heart liked={isLiked} animate={animate} />\n    </button>\n  );\n}\n```\n\n```js src/Videos.js hidden\nimport { useState, ViewTransition } from \"react\";\nimport LikeButton from \"./LikeButton\";\nimport { useRouter } from \"./router\";\nimport { PauseIcon, PlayIcon } from \"./Icons\";\nimport { startTransition } from \"react\";\n\nexport function Thumbnail({ video, children }) {\n  // Add a name to animate with a shared element transition.\n  // This uses the default animation, no additional css needed.\n  return (\n    <ViewTransition name={`video-${video.id}`}>\n      <div\n        aria-hidden=\"true\"\n        tabIndex={-1}\n        className={`thumbnail ${video.image}`}\n      >\n        {children}\n      </div>\n    </ViewTransition>\n  );\n}\n\nexport function VideoControls() {\n  const [isPlaying, setIsPlaying] = useState(false);\n\n  return (\n    <span\n      className=\"controls\"\n      onClick={() =>\n        startTransition(() => {\n          setIsPlaying((p) => !p);\n        })\n      }\n    >\n      {isPlaying ? <PauseIcon /> : <PlayIcon />}\n    </span>\n  );\n}\n\nexport function Video({ video }) {\n  const { navigate } = useRouter();\n\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n        onClick={(e) => {\n          e.preventDefault();\n          navigate(`/video/${video.id}`);\n        }}\n      >\n        <Thumbnail video={video}></Thumbnail>\n\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n      <LikeButton video={video} />\n    </div>\n  );\n}\n```\n\n\n```js src/data.js hidden\nconst videos = [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  },\n  {\n    id: '2',\n    title: 'Second video',\n    description: 'Video description',\n    image: 'red',\n  },\n  {\n    id: '3',\n    title: 'Third video',\n    description: 'Video description',\n    image: 'green',\n  },\n  {\n    id: '4',\n    title: 'Fourth video',\n    description: 'Video description',\n    image: 'purple',\n  },\n  {\n    id: '5',\n    title: 'Fifth video',\n    description: 'Video description',\n    image: 'yellow',\n  },\n  {\n    id: '6',\n    title: 'Sixth video',\n    description: 'Video description',\n    image: 'gray',\n  },\n];\n\nlet videosCache = new Map();\nlet videoCache = new Map();\nlet videoDetailsCache = new Map();\nconst VIDEO_DELAY = 1;\nconst VIDEO_DETAILS_DELAY = 1000;\nexport function fetchVideos() {\n  if (videosCache.has(0)) {\n    return videosCache.get(0);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos);\n    }, VIDEO_DELAY);\n  });\n  videosCache.set(0, promise);\n  return promise;\n}\n\nexport function fetchVideo(id) {\n  if (videoCache.has(id)) {\n    return videoCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DELAY);\n  });\n  videoCache.set(id, promise);\n  return promise;\n}\n\nexport function fetchVideoDetails(id) {\n  if (videoDetailsCache.has(id)) {\n    return videoDetailsCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DETAILS_DELAY);\n  });\n  videoDetailsCache.set(id, promise);\n  return promise;\n}\n```\n\n```js src/router.js hidden\nimport {useState, createContext, use, useTransition, useLayoutEffect, useEffect, addTransitionType} from \"react\";\n\nexport function Router({ children }) {\n  const [isPending, startTransition] = useTransition();\n  const [routerState, setRouterState] = useState({pendingNav: () => {}, url: document.location.pathname});\n  function navigate(url) {\n    startTransition(() => {\n      // Transition type for the cause \"nav forward\"\n      addTransitionType('nav-forward');\n      go(url);\n    });\n  }\n  function navigateBack(url) {\n    startTransition(() => {\n      // Transition type for the cause \"nav backward\"\n      addTransitionType('nav-back');\n      go(url);\n    });\n  }\n\n  function go(url) {\n    setRouterState({\n      url,\n      pendingNav() {\n        window.history.pushState({}, \"\", url);\n      },\n    });\n  }\n\n  useEffect(() => {\n    function handlePopState() {\n      // This should not animate because restoration has to be synchronous.\n      // Even though it's a transition.\n      startTransition(() => {\n        setRouterState({\n          url: document.location.pathname + document.location.search,\n          pendingNav() {\n            // Noop. URL has already updated.\n          },\n        });\n      });\n    }\n    window.addEventListener(\"popstate\", handlePopState);\n    return () => {\n      window.removeEventListener(\"popstate\", handlePopState);\n    };\n  }, []);\n  const pendingNav = routerState.pendingNav;\n  useLayoutEffect(() => {\n    pendingNav();\n  }, [pendingNav]);\n\n  return (\n    <RouterContext\n      value={{\n        url: routerState.url,\n        navigate,\n        navigateBack,\n        isPending,\n        params: {},\n      }}\n    >\n      {children}\n    </RouterContext>\n  );\n}\n\nconst RouterContext = createContext({ url: \"/\", params: {} });\n\nexport function useRouter() {\n  return use(RouterContext);\n}\n\nexport function useIsNavPending() {\n  return use(RouterContext).isPending;\n}\n\n```\n\n```css src/styles.css hidden\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Rg.woff2) format(\"woff2\");\n  font-weight: 400;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Md.woff2) format(\"woff2\");\n  font-weight: 500;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 600;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 700;\n  font-style: normal;\n  font-display: swap;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhtml {\n  background-image: url(https://react.dev/images/meta-gradient-dark.png);\n  background-size: 100%;\n  background-position: -100%;\n  background-color: rgb(64 71 86);\n  background-repeat: no-repeat;\n  height: 100%;\n  width: 100%;\n}\n\nbody {\n  font-family: Optimistic Text, -apple-system, ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;\n  padding: 10px 0 10px 0;\n  margin: 0;\n  display: flex;\n  justify-content: center;\n}\n\n#root {\n  flex: 1 1;\n  height: auto;\n  background-color: #fff;\n  border-radius: 10px;\n  max-width: 450px;\n  min-height: 600px;\n  padding-bottom: 10px;\n}\n\nh1 {\n  margin-top: 0;\n  font-size: 22px;\n}\n\nh2 {\n  margin-top: 0;\n  font-size: 20px;\n}\n\nh3 {\n  margin-top: 0;\n  font-size: 18px;\n}\n\nh4 {\n  margin-top: 0;\n  font-size: 16px;\n}\n\nh5 {\n  margin-top: 0;\n  font-size: 14px;\n}\n\nh6 {\n  margin-top: 0;\n  font-size: 12px;\n}\n\ncode {\n  font-size: 1.2em;\n}\n\nul {\n  padding-inline-start: 20px;\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border-width: 0;\n}\n\n.absolute {\n  position: absolute;\n}\n\n.overflow-visible {\n  overflow: visible;\n}\n\n.visible {\n  overflow: visible;\n}\n\n.fit {\n  width: fit-content;\n}\n\n\n/* Layout */\n.page {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n}\n\n.top-hero {\n  height: 200px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-image: conic-gradient(\n      from 90deg at -10% 100%,\n      #2b303b 0deg,\n      #2b303b 90deg,\n      #16181d 1turn\n  );\n}\n\n.bottom {\n  flex: 1;\n  overflow: auto;\n}\n\n.top-nav {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 0;\n  padding: 0 12px;\n  top: 0;\n  width: 100%;\n  height: 44px;\n  color: #23272f;\n  font-weight: 700;\n  font-size: 20px;\n  z-index: 100;\n  cursor: default;\n}\n\n.content {\n  padding: 0 12px;\n  margin-top: 4px;\n}\n\n\n.loader {\n  color: #23272f;\n  font-size: 3px;\n  width: 1em;\n  margin-right: 18px;\n  height: 1em;\n  border-radius: 50%;\n  position: relative;\n  text-indent: -9999em;\n  animation: loading-spinner 1.3s infinite linear;\n  animation-delay: 200ms;\n  transform: translateZ(0);\n}\n\n@keyframes loading-spinner {\n  0%,\n  100% {\n    box-shadow: 0 -3em 0 0.2em,\n    2em -2em 0 0em, 3em 0 0 -1em,\n    2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 0;\n  }\n  12.5% {\n    box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em,\n    3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  25% {\n    box-shadow: 0 -3em 0 -0.5em,\n    2em -2em 0 0, 3em 0 0 0.2em,\n    2em 2em 0 0, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  37.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em,\n    -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  50% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em,\n    -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  62.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0,\n    -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em;\n  }\n  75% {\n    box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0;\n  }\n  87.5% {\n    box-shadow: 0em -3em 0 0, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;\n  }\n}\n\n/* LikeButton */\n.like-button {\n  outline-offset: 2px;\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 2.5rem;\n  height: 2.5rem;\n  cursor: pointer;\n  border-radius: 9999px;\n  border: none;\n  outline: none 2px;\n  color: #5e687e;\n  background: none;\n}\n\n.like-button:focus {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n}\n\n.like-button:active {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n  transform: scaleX(0.95) scaleY(0.95);\n}\n\n.like-button:hover {\n  background-color: #f6f7f9;\n}\n\n.like-button.liked {\n  color: #a6423a;\n}\n\n/* Icons */\n@keyframes circle {\n  0% {\n    transform: scale(0);\n    stroke-width: 16px;\n  }\n\n  50% {\n    transform: scale(.5);\n    stroke-width: 16px;\n  }\n\n  to {\n    transform: scale(1);\n    stroke-width: 0;\n  }\n}\n\n.circle {\n  color: rgba(166, 66, 58, .5);\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4,0,.2,1);\n}\n\n.circle.liked.animate {\n  animation: circle .3s forwards;\n}\n\n.heart {\n  width: 1.5rem;\n  height: 1.5rem;\n}\n\n.heart.liked {\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4, 0, .2, 1);\n}\n\n.heart.liked.animate {\n  animation: scale .35s ease-in-out forwards;\n}\n\n.control-icon {\n  color: hsla(0, 0%, 100%, .5);\n  filter:  drop-shadow(0 20px 13px rgba(0, 0, 0, .03)) drop-shadow(0 8px 5px rgba(0, 0, 0, .08));\n}\n\n.chevron-left {\n  margin-top: 2px;\n  rotate: 90deg;\n}\n\n\n/* Video */\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n\n.thumbnail.red {\n  background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);\n}\n\n.thumbnail.green {\n  background-image: conic-gradient(at top right, #c76a15, #388f7f, #2b3491);\n}\n\n.thumbnail.purple {\n  background-image: conic-gradient(at top right, #c76a15, #575fb7, #2b3491);\n}\n\n.thumbnail.yellow {\n  background-image: conic-gradient(at top right, #c76a15, #FABD62, #2b3491);\n}\n\n.thumbnail.gray {\n  background-image: conic-gradient(at top right, #c76a15, #4E5769, #2b3491);\n}\n\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n}\n\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n\n.video .info:hover {\n  text-decoration: underline;\n}\n\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n\n/* Details */\n.details .thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 100%;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.video-details-title {\n  margin-top: 8px;\n}\n\n.video-details-speaker {\n  display: flex;\n  gap: 8px;\n  margin-top: 10px\n}\n\n.back {\n  display: flex;\n  align-items: center;\n  margin-left: -5px;\n  cursor: pointer;\n}\n\n.back:hover {\n  text-decoration: underline;\n}\n\n.info-title {\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1.25;\n  margin: 8px 0 0 0 ;\n}\n\n.info-description {\n  margin: 8px 0 0 0;\n}\n\n.controls {\n  cursor: pointer;\n}\n\n.fallback {\n  background: #f6f7f8 linear-gradient(to right, #e6e6e6 5%, #cccccc 25%, #e6e6e6 35%) no-repeat;\n  background-size: 800px 104px;\n  display: block;\n  line-height: 1.25;\n  margin: 8px 0 0 0;\n  border-radius: 5px;\n  overflow: hidden;\n\n  animation: 1s linear 1s infinite shimmer;\n  animation-delay: 300ms;\n  animation-duration: 1s;\n  animation-fill-mode: forwards;\n  animation-iteration-count: infinite;\n  animation-name: shimmer;\n  animation-timing-function: linear;\n}\n\n\n.fallback.title {\n  width: 130px;\n  height: 30px;\n\n}\n\n.fallback.description {\n  width: 150px;\n  height: 21px;\n}\n\n@keyframes shimmer {\n  0% {\n    background-position: -468px 0;\n  }\n\n  100% {\n    background-position: 468px 0;\n  }\n}\n\n.search {\n  margin-bottom: 10px;\n}\n.search-input {\n  width: 100%;\n  position: relative;\n}\n\n.search-icon {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  inset-inline-start: 0;\n  display: flex;\n  align-items: center;\n  padding-inline-start: 1rem;\n  pointer-events: none;\n  color: #99a1b3;\n}\n\n.search-input input {\n  display: flex;\n  padding-inline-start: 2.75rem;\n  padding-top: 10px;\n  padding-bottom: 10px;\n  width: 100%;\n  text-align: start;\n  background-color: rgb(235 236 240);\n  outline: 2px solid transparent;\n  cursor: pointer;\n  border: none;\n  align-items: center;\n  color: rgb(35 39 47);\n  border-radius: 9999px;\n  vertical-align: middle;\n  font-size: 15px;\n}\n\n.search-input input:hover, .search-input input:active {\n  background-color: rgb(235 236 240/ 0.8);\n  color: rgb(35 39 47/ 0.8);\n}\n\n/* Home */\n.video-list {\n  position: relative;\n}\n\n.video-list .videos {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n  overflow-y: auto;\n  height: 100%;\n}\n```\n\n\n```css src/animations.css\n/* No additional animations needed */\n\n\n\n\n\n\n\n\n\n/* Previously defined animations below */\n\n\n\n\n\n\n/* Slide animations for Suspense the fallback down */\n::view-transition-old(.slide-down) {\n    animation: 150ms ease-out both fade-out, 150ms ease-out both slide-down;\n}\n\n::view-transition-new(.slide-up) {\n    animation: 210ms ease-in 150ms both fade-in, 400ms ease-in both slide-up;\n}\n\n/* Animations for view transition classed added by transition type */\n::view-transition-old(.slide-forward) {\n    /* when sliding forward, the \"old\" page should slide out to left. */\n    animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;\n}\n\n::view-transition-new(.slide-forward) {\n    /* when sliding forward, the \"new\" page should slide in from right. */\n    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;\n}\n\n::view-transition-old(.slide-back) {\n    /* when sliding back, the \"old\" page should slide out to right. */\n    animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;\n}\n\n::view-transition-new(.slide-back) {\n    /* when sliding back, the \"new\" page should slide in from left. */\n    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-left;\n}\n\n/* Keyframes to support our animations above. */\n@keyframes slide-up {\n    from {\n        transform: translateY(10px);\n    }\n    to {\n        transform: translateY(0);\n    }\n}\n\n@keyframes slide-down {\n    from {\n        transform: translateY(0);\n    }\n    to {\n        transform: translateY(10px);\n    }\n}\n\n@keyframes fade-in {\n    from {\n        opacity: 0;\n    }\n}\n\n@keyframes fade-out {\n    to {\n        opacity: 0;\n    }\n}\n\n@keyframes slide-to-right {\n    to {\n        transform: translateX(50px);\n    }\n}\n\n@keyframes slide-from-right {\n    from {\n        transform: translateX(50px);\n    }\n    to {\n        transform: translateX(0);\n    }\n}\n\n@keyframes slide-to-left {\n    to {\n        transform: translateX(-50px);\n    }\n}\n\n@keyframes slide-from-left {\n    from {\n        transform: translateX(-50px);\n    }\n    to {\n        transform: translateX(0);\n    }\n}\n\n/* Default .slow-fade. */\n::view-transition-old(.slow-fade) {\n    animation-duration: 500ms;\n}\n\n::view-transition-new(.slow-fade) {\n    animation-duration: 500ms;\n}\n```\n\n```js src/index.js hidden\nimport React, {StrictMode} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport './styles.css';\nimport './animations.css';\n\nimport App from './App';\nimport {Router} from './router';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <Router>\n      <App />\n    </Router>\n  </StrictMode>\n);\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\n### Activity によるプリレンダー {/*prerender-with-activity*/}\n\nときには、ユーザが次に使用する可能性が高い UI を事前に準備しておき、ユーザが使用したくなったときにすぐに利用できるようにしたいことがあります。これは、次のページがレンダーに必要なデータをサスペンドする必要がある場合に特に有用です。ユーザがナビゲートする前にデータをすでに取得済みにできるからです。\n\nたとえば、現在のアプリでは、ビデオを選択する際に各ビデオの詳細データを読み込むためにサスペンスを使用しています。これを改善するために、ユーザがナビゲートする前に、すべてのページを非表示の `<Activity>` でレンダーしておきます。\n\n```js {2,5,8}\n<ViewTransition>\n  <Activity mode={url === '/' ? 'visible' : 'hidden'}>\n    <Home />\n  </Activity>\n  <Activity mode={url === '/details/1' ? 'visible' : 'hidden'}>\n    <Details id={id} />\n  </Activity>\n  <Activity mode={url === '/details/1' ? 'visible' : 'hidden'}>\n    <Details id={id} />\n  </Activity>\n<ViewTransition>\n```\n\nこれにより、次のページのコンテンツをプリレンダーする余裕がある場合は、サスペンスフォールバックなしでアニメーションが表示されます。ビデオをクリックしてみて、詳細ページのビデオタイトルと説明がフォールバックなしで即座にレンダーされることを確認してください。\n\n<Sandpack>\n\n```js src/App.js\nimport { Activity, ViewTransition, use } from \"react\"; import Details from \"./Details\"; import Home from \"./Home\"; import { useRouter } from \"./router\"; import {fetchVideos} from './data';\n\nexport default function App() {\n  const { url } = useRouter();\n  const videoId = url.split(\"/\").pop();\n  const videos = use(fetchVideos());\n\n  return (\n    <ViewTransition>\n      {/* Render videos in Activity to pre-render them */}\n      {videos.map(({id}) => (\n        <Activity key={id} mode={videoId === id ? 'visible' : 'hidden'}>\n          <Details id={id}/>\n        </Activity>\n      ))}\n      <Activity mode={url === '/' ? 'visible' : 'hidden'}>\n        <Home />\n      </Activity>\n    </ViewTransition>\n  );\n}\n```\n\n```js src/Details.js\nimport { use, Suspense, ViewTransition } from \"react\"; import { fetchVideo, fetchVideoDetails } from \"./data\"; import { Thumbnail, VideoControls } from \"./Videos\"; import { useRouter } from \"./router\"; import Layout from \"./Layout\"; import { ChevronLeft } from \"./Icons\";\n\nfunction VideoDetails({id}) {\n  // Animate from Suspense fallback to content.\n  // If this is pre-rendered then the fallback\n  // won't need to show.\n  return (\n    <Suspense\n      fallback={\n        // Animate the fallback down.\n        <ViewTransition exit=\"slide-down\">\n          <VideoInfoFallback />\n        </ViewTransition>\n      }\n    >\n      {/* Animate the content up */}\n      <ViewTransition enter=\"slide-up\">\n        <VideoInfo id={id} />\n      </ViewTransition>\n    </Suspense>\n  );\n}\n\nfunction VideoInfoFallback() {\n  return (\n    <>\n      <div className=\"fallback title\"></div>\n      <div className=\"fallback description\"></div>\n    </>\n  );\n}\n\nexport default function Details({id}) {\n  const { url, navigateBack } = useRouter();\n  const video = use(fetchVideo(id));\n\n  return (\n    <Layout\n      heading={\n        <div\n          className=\"fit back\"\n          onClick={() => {\n            navigateBack(\"/\");\n          }}\n        >\n          <ChevronLeft /> Back\n        </div>\n      }\n    >\n      <div className=\"details\">\n        <Thumbnail video={video} large>\n          <VideoControls />\n        </Thumbnail>\n        <VideoDetails id={video.id} />\n      </div>\n    </Layout>\n  );\n}\n\nfunction VideoInfo({ id }) {\n  const details = use(fetchVideoDetails(id));\n  return (\n    <>\n      <p className=\"info-title\">{details.title}</p>\n      <p className=\"info-description\">{details.description}</p>\n    </>\n  );\n}\n```\n\n```js src/Home.js hidden\nimport { useId, useState, use, useDeferredValue, ViewTransition } from \"react\";import { Video } from \"./Videos\";import Layout from \"./Layout\";import { fetchVideos } from \"./data\";import { IconSearch } from \"./Icons\";\n\nfunction SearchList({searchText, videos}) {\n  // Activate with useDeferredValue (\"when\")\n  const deferredSearchText = useDeferredValue(searchText);\n  const filteredVideos = filterVideos(videos, deferredSearchText);\n  return (\n    <div className=\"video-list\">\n      {filteredVideos.length === 0 && (\n        <div className=\"no-results\">No results</div>\n      )}\n      <div className=\"videos\">\n        {filteredVideos.map((video) => (\n          // Animate each item in list (\"what\")\n          <ViewTransition key={video.id}>\n            <Video video={video} />\n          </ViewTransition>\n        ))}\n      </div>\n    </div>\n  );\n}\n\nexport default function Home() {\n  const videos = use(fetchVideos());\n  const count = videos.length;\n  const [searchText, setSearchText] = useState('');\n\n  return (\n    <Layout heading={<div className=\"fit\">{count} Videos</div>}>\n      <SearchInput value={searchText} onChange={setSearchText} />\n      <SearchList videos={videos} searchText={searchText} />\n    </Layout>\n  );\n}\n\nfunction SearchInput({ value, onChange }) {\n  const id = useId();\n  return (\n    <form className=\"search\" onSubmit={(e) => e.preventDefault()}>\n      <label htmlFor={id} className=\"sr-only\">\n        Search\n      </label>\n      <div className=\"search-input\">\n        <div className=\"search-icon\">\n          <IconSearch />\n        </div>\n        <input\n          type=\"text\"\n          id={id}\n          placeholder=\"Search\"\n          value={value}\n          onChange={(e) => onChange(e.target.value)}\n        />\n      </div>\n    </form>\n  );\n}\n\nfunction filterVideos(videos, query) {\n  const keywords = query\n    .toLowerCase()\n    .split(\" \")\n    .filter((s) => s !== \"\");\n  if (keywords.length === 0) {\n    return videos;\n  }\n  return videos.filter((video) => {\n    const words = (video.title + \" \" + video.description)\n      .toLowerCase()\n      .split(\" \");\n    return keywords.every((kw) => words.some((w) => w.includes(kw)));\n  });\n}\n```\n\n```js src/Icons.js hidden\nexport function ChevronLeft() {\n  return (\n    <svg\n      className=\"chevron-left\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\">\n      <g fill=\"none\" fillRule=\"evenodd\" transform=\"translate(-446 -398)\">\n        <path\n          fill=\"currentColor\"\n          fillRule=\"nonzero\"\n          d=\"M95.8838835,240.366117 C95.3957281,239.877961 94.6042719,239.877961 94.1161165,240.366117 C93.6279612,240.854272 93.6279612,241.645728 94.1161165,242.133883 L98.6161165,246.633883 C99.1042719,247.122039 99.8957281,247.122039 100.383883,246.633883 L104.883883,242.133883 C105.372039,241.645728 105.372039,240.854272 104.883883,240.366117 C104.395728,239.877961 103.604272,239.877961 103.116117,240.366117 L99.5,243.982233 L95.8838835,240.366117 Z\"\n          transform=\"translate(356.5 164.5)\"\n        />\n        <polygon points=\"446 418 466 418 466 398 446 398\" />\n      </g>\n    </svg>\n  );\n}\n\nexport function PauseIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      style={{padding: '4px'}}\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 512 512\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M256 0C114.617 0 0 114.615 0 256s114.617 256 256 256 256-114.615 256-256S397.383 0 256 0zm-32 320c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128zm128 0c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16V192c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v128z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n\nexport function PlayIcon() {\n  return (\n    <svg\n      className=\"control-icon\"\n      width=\"100\"\n      height=\"100\"\n      viewBox=\"0 0 72 72\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M36 69C54.2254 69 69 54.2254 69 36C69 17.7746 54.2254 3 36 3C17.7746 3 3 17.7746 3 36C3 54.2254 17.7746 69 36 69ZM52.1716 38.6337L28.4366 51.5801C26.4374 52.6705 24 51.2235 24 48.9464V23.0536C24 20.7764 26.4374 19.3295 28.4366 20.4199L52.1716 33.3663C54.2562 34.5034 54.2562 37.4966 52.1716 38.6337Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\nexport function Heart({liked, animate}) {\n  return (\n    <>\n      <svg\n        className=\"absolute overflow-visible\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        <circle\n          className={`circle ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n          cx=\"12\"\n          cy=\"12\"\n          r=\"11.5\"\n          fill=\"transparent\"\n          strokeWidth=\"0\"\n          stroke=\"currentColor\"\n        />\n      </svg>\n\n      <svg\n        className={`heart ${liked ? 'liked' : ''} ${animate ? 'animate' : ''}`}\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\">\n        {liked ? (\n          <path\n            d=\"M12 23a.496.496 0 0 1-.26-.074C7.023 19.973 0 13.743 0 8.68c0-4.12 2.322-6.677 6.058-6.677 2.572 0 5.108 2.387 5.134 2.41l.808.771.808-.771C12.834 4.387 15.367 2 17.935 2 21.678 2 24 4.558 24 8.677c0 5.06-7.022 11.293-11.74 14.246a.496.496 0 0 1-.26.074V23z\"\n            fill=\"currentColor\"\n          />\n        ) : (\n          <path\n            fillRule=\"evenodd\"\n            clipRule=\"evenodd\"\n            d=\"m12 5.184-.808-.771-.004-.004C11.065 4.299 8.522 2.003 6 2.003c-3.736 0-6 2.558-6 6.677 0 4.47 5.471 9.848 10 13.079.602.43 1.187.82 1.74 1.167A.497.497 0 0 0 12 23v-.003c.09 0 .182-.026.26-.074C16.977 19.97 24 13.737 24 8.677 24 4.557 21.743 2 18 2c-2.569 0-5.166 2.387-5.192 2.413L12 5.184zm-.002 15.525c2.071-1.388 4.477-3.342 6.427-5.47C20.72 12.733 22 10.401 22 8.677c0-1.708-.466-2.855-1.087-3.55C20.316 4.459 19.392 4 18 4c-.726 0-1.63.364-2.5.9-.67.412-1.148.82-1.266.92-.03.025-.037.031-.019.014l-.013.013L12 7.949 9.832 5.88a10.08 10.08 0 0 0-1.33-.977C7.633 4.367 6.728 4.003 6 4.003c-1.388 0-2.312.459-2.91 1.128C2.466 5.826 2 6.974 2 8.68c0 1.726 1.28 4.058 3.575 6.563 1.948 2.127 4.352 4.078 6.423 5.466z\"\n            fill=\"currentColor\"\n          />\n        )}\n      </svg>\n    </>\n  );\n}\n\nexport function IconSearch(props) {\n  return (\n    <svg width=\"1em\" height=\"1em\" viewBox=\"0 0 20 20\">\n      <path\n        d=\"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z\"\n        stroke=\"currentColor\"\n        fill=\"none\"\n        strokeWidth=\"2\"\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"></path>\n    </svg>\n  );\n}\n```\n\n```js src/Layout.js hidden\nimport {ViewTransition} from 'react'; import { useIsNavPending } from \"./router\";\n\nexport default function Page({ heading, children }) {\n  const isPending = useIsNavPending();\n  return (\n    <div className=\"page\">\n      <div className=\"top\">\n        <div className=\"top-nav\">\n          {/* Custom classes based on transition type. */}\n          <ViewTransition\n            name=\"nav\"\n            share={{\n              'nav-forward': 'slide-forward',\n              'nav-back': 'slide-back',\n            }}>\n            {heading}\n          </ViewTransition>\n          {isPending && <span className=\"loader\"></span>}\n        </div>\n      </div>\n      {/* Opt-out of ViewTransition for the content. */}\n      {/* Content can define it's own ViewTransition. */}\n      <ViewTransition default=\"none\">\n        <div className=\"bottom\">\n          <div className=\"content\">{children}</div>\n        </div>\n      </ViewTransition>\n    </div>\n  );\n}\n```\n\n```js src/LikeButton.js hidden\nimport {useState} from 'react';\nimport {Heart} from './Icons';\n\n// A hack since we don't actually have a backend.\n// Unlike local state, this survives videos being filtered.\nconst likedVideos = new Set();\n\nexport default function LikeButton({video}) {\n  const [isLiked, setIsLiked] = useState(() => likedVideos.has(video.id));\n  const [animate, setAnimate] = useState(false);\n  return (\n    <button\n      className={`like-button ${isLiked && 'liked'}`}\n      aria-label={isLiked ? 'Unsave' : 'Save'}\n      onClick={() => {\n        const nextIsLiked = !isLiked;\n        if (nextIsLiked) {\n          likedVideos.add(video.id);\n        } else {\n          likedVideos.delete(video.id);\n        }\n        setAnimate(true);\n        setIsLiked(nextIsLiked);\n      }}>\n      <Heart liked={isLiked} animate={animate} />\n    </button>\n  );\n}\n```\n\n```js src/Videos.js hidden\nimport { useState, ViewTransition } from \"react\";\nimport LikeButton from \"./LikeButton\";\nimport { useRouter } from \"./router\";\nimport { PauseIcon, PlayIcon } from \"./Icons\";\nimport { startTransition } from \"react\";\n\nexport function Thumbnail({ video, children }) {\n  // Add a name to animate with a shared element transition.\n  // This uses the default animation, no additional css needed.\n  return (\n    <ViewTransition name={`video-${video.id}`}>\n      <div\n        aria-hidden=\"true\"\n        tabIndex={-1}\n        className={`thumbnail ${video.image}`}\n      >\n        {children}\n      </div>\n    </ViewTransition>\n  );\n}\n\nexport function VideoControls() {\n  const [isPlaying, setIsPlaying] = useState(false);\n\n  return (\n    <span\n      className=\"controls\"\n      onClick={() =>\n        startTransition(() => {\n          setIsPlaying((p) => !p);\n        })\n      }\n    >\n      {isPlaying ? <PauseIcon /> : <PlayIcon />}\n    </span>\n  );\n}\n\nexport function Video({ video }) {\n  const { navigate } = useRouter();\n\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n        onClick={(e) => {\n          e.preventDefault();\n          navigate(`/video/${video.id}`);\n        }}\n      >\n        <Thumbnail video={video}></Thumbnail>\n\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n      <LikeButton video={video} />\n    </div>\n  );\n}\n```\n\n\n```js src/data.js hidden\nconst videos = [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  },\n  {\n    id: '2',\n    title: 'Second video',\n    description: 'Video description',\n    image: 'red',\n  },\n  {\n    id: '3',\n    title: 'Third video',\n    description: 'Video description',\n    image: 'green',\n  },\n  {\n    id: '4',\n    title: 'Fourth video',\n    description: 'Video description',\n    image: 'purple',\n  },\n  {\n    id: '5',\n    title: 'Fifth video',\n    description: 'Video description',\n    image: 'yellow',\n  },\n  {\n    id: '6',\n    title: 'Sixth video',\n    description: 'Video description',\n    image: 'gray',\n  },\n];\n\nlet videosCache = new Map();\nlet videoCache = new Map();\nlet videoDetailsCache = new Map();\nconst VIDEO_DELAY = 1;\nconst VIDEO_DETAILS_DELAY = 1000;\nexport function fetchVideos() {\n  if (videosCache.has(0)) {\n    return videosCache.get(0);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos);\n    }, VIDEO_DELAY);\n  });\n  videosCache.set(0, promise);\n  return promise;\n}\n\nexport function fetchVideo(id) {\n  if (videoCache.has(id)) {\n    return videoCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DELAY);\n  });\n  videoCache.set(id, promise);\n  return promise;\n}\n\nexport function fetchVideoDetails(id) {\n  if (videoDetailsCache.has(id)) {\n    return videoDetailsCache.get(id);\n  }\n  const promise = new Promise((resolve) => {\n    setTimeout(() => {\n      resolve(videos.find((video) => video.id === id));\n    }, VIDEO_DETAILS_DELAY);\n  });\n  videoDetailsCache.set(id, promise);\n  return promise;\n}\n```\n\n```js src/router.js hidden\nimport {useState, createContext, use, useTransition, useLayoutEffect, useEffect, addTransitionType} from \"react\";\n\nexport function Router({ children }) {\n  const [isPending, startTransition] = useTransition();\n  const [routerState, setRouterState] = useState({pendingNav: () => {}, url: document.location.pathname});\n  function navigate(url) {\n    startTransition(() => {\n      // Transition type for the cause \"nav forward\"\n      addTransitionType('nav-forward');\n      go(url);\n    });\n  }\n  function navigateBack(url) {\n    startTransition(() => {\n      // Transition type for the cause \"nav backward\"\n      addTransitionType('nav-back');\n      go(url);\n    });\n  }\n\n  function go(url) {\n    setRouterState({\n      url,\n      pendingNav() {\n        window.history.pushState({}, \"\", url);\n      },\n    });\n  }\n\n  useEffect(() => {\n    function handlePopState() {\n      // This should not animate because restoration has to be synchronous.\n      // Even though it's a transition.\n      startTransition(() => {\n        setRouterState({\n          url: document.location.pathname + document.location.search,\n          pendingNav() {\n            // Noop. URL has already updated.\n          },\n        });\n      });\n    }\n    window.addEventListener(\"popstate\", handlePopState);\n    return () => {\n      window.removeEventListener(\"popstate\", handlePopState);\n    };\n  }, []);\n  const pendingNav = routerState.pendingNav;\n  useLayoutEffect(() => {\n    pendingNav();\n  }, [pendingNav]);\n\n  return (\n    <RouterContext\n      value={{\n        url: routerState.url,\n        navigate,\n        navigateBack,\n        isPending,\n        params: {},\n      }}\n    >\n      {children}\n    </RouterContext>\n  );\n}\n\nconst RouterContext = createContext({ url: \"/\", params: {} });\n\nexport function useRouter() {\n  return use(RouterContext);\n}\n\nexport function useIsNavPending() {\n  return use(RouterContext).isPending;\n}\n\n```\n\n```css src/styles.css hidden\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Rg.woff2) format(\"woff2\");\n  font-weight: 400;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Md.woff2) format(\"woff2\");\n  font-weight: 500;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 600;\n  font-style: normal;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Optimistic Text;\n  src: url(https://react.dev/fonts/Optimistic_Text_W_Bd.woff2) format(\"woff2\");\n  font-weight: 700;\n  font-style: normal;\n  font-display: swap;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhtml {\n  background-image: url(https://react.dev/images/meta-gradient-dark.png);\n  background-size: 100%;\n  background-position: -100%;\n  background-color: rgb(64 71 86);\n  background-repeat: no-repeat;\n  height: 100%;\n  width: 100%;\n}\n\nbody {\n  font-family: Optimistic Text, -apple-system, ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;\n  padding: 10px 0 10px 0;\n  margin: 0;\n  display: flex;\n  justify-content: center;\n}\n\n#root {\n  flex: 1 1;\n  height: auto;\n  background-color: #fff;\n  border-radius: 10px;\n  max-width: 450px;\n  min-height: 600px;\n  padding-bottom: 10px;\n}\n\nh1 {\n  margin-top: 0;\n  font-size: 22px;\n}\n\nh2 {\n  margin-top: 0;\n  font-size: 20px;\n}\n\nh3 {\n  margin-top: 0;\n  font-size: 18px;\n}\n\nh4 {\n  margin-top: 0;\n  font-size: 16px;\n}\n\nh5 {\n  margin-top: 0;\n  font-size: 14px;\n}\n\nh6 {\n  margin-top: 0;\n  font-size: 12px;\n}\n\ncode {\n  font-size: 1.2em;\n}\n\nul {\n  padding-inline-start: 20px;\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  white-space: nowrap;\n  border-width: 0;\n}\n\n.absolute {\n  position: absolute;\n}\n\n.overflow-visible {\n  overflow: visible;\n}\n\n.visible {\n  overflow: visible;\n}\n\n.fit {\n  width: fit-content;\n}\n\n\n/* Layout */\n.page {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n}\n\n.top-hero {\n  height: 200px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-image: conic-gradient(\n      from 90deg at -10% 100%,\n      #2b303b 0deg,\n      #2b303b 90deg,\n      #16181d 1turn\n  );\n}\n\n.bottom {\n  flex: 1;\n  overflow: auto;\n}\n\n.top-nav {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  margin-bottom: 0;\n  padding: 0 12px;\n  top: 0;\n  width: 100%;\n  height: 44px;\n  color: #23272f;\n  font-weight: 700;\n  font-size: 20px;\n  z-index: 100;\n  cursor: default;\n}\n\n.content {\n  padding: 0 12px;\n  margin-top: 4px;\n}\n\n\n.loader {\n  color: #23272f;\n  font-size: 3px;\n  width: 1em;\n  margin-right: 18px;\n  height: 1em;\n  border-radius: 50%;\n  position: relative;\n  text-indent: -9999em;\n  animation: loading-spinner 1.3s infinite linear;\n  animation-delay: 200ms;\n  transform: translateZ(0);\n}\n\n@keyframes loading-spinner {\n  0%,\n  100% {\n    box-shadow: 0 -3em 0 0.2em,\n    2em -2em 0 0em, 3em 0 0 -1em,\n    2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 0;\n  }\n  12.5% {\n    box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em,\n    3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  25% {\n    box-shadow: 0 -3em 0 -0.5em,\n    2em -2em 0 0, 3em 0 0 0.2em,\n    2em 2em 0 0, 0 3em 0 -1em,\n    -2em 2em 0 -1em, -3em 0 0 -1em,\n    -2em -2em 0 -1em;\n  }\n  37.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em,\n    -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  50% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em,\n    -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em;\n  }\n  62.5% {\n    box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0,\n    -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em;\n  }\n  75% {\n    box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em,\n    3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0;\n  }\n  87.5% {\n    box-shadow: 0em -3em 0 0, 2em -2em 0 -1em,\n    3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em,\n    -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em;\n  }\n}\n\n/* LikeButton */\n.like-button {\n  outline-offset: 2px;\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 2.5rem;\n  height: 2.5rem;\n  cursor: pointer;\n  border-radius: 9999px;\n  border: none;\n  outline: none 2px;\n  color: #5e687e;\n  background: none;\n}\n\n.like-button:focus {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n}\n\n.like-button:active {\n  color: #a6423a;\n  background-color: rgba(166, 66, 58, .05);\n  transform: scaleX(0.95) scaleY(0.95);\n}\n\n.like-button:hover {\n  background-color: #f6f7f9;\n}\n\n.like-button.liked {\n  color: #a6423a;\n}\n\n/* Icons */\n@keyframes circle {\n  0% {\n    transform: scale(0);\n    stroke-width: 16px;\n  }\n\n  50% {\n    transform: scale(.5);\n    stroke-width: 16px;\n  }\n\n  to {\n    transform: scale(1);\n    stroke-width: 0;\n  }\n}\n\n.circle {\n  color: rgba(166, 66, 58, .5);\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4,0,.2,1);\n}\n\n.circle.liked.animate {\n  animation: circle .3s forwards;\n}\n\n.heart {\n  width: 1.5rem;\n  height: 1.5rem;\n}\n\n.heart.liked {\n  transform-origin: center;\n  transition-property: all;\n  transition-duration: .15s;\n  transition-timing-function: cubic-bezier(.4, 0, .2, 1);\n}\n\n.heart.liked.animate {\n  animation: scale .35s ease-in-out forwards;\n}\n\n.control-icon {\n  color: hsla(0, 0%, 100%, .5);\n  filter:  drop-shadow(0 20px 13px rgba(0, 0, 0, .03)) drop-shadow(0 8px 5px rgba(0, 0, 0, .08));\n}\n\n.chevron-left {\n  margin-top: 2px;\n  rotate: 90deg;\n}\n\n\n/* Video */\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n\n.thumbnail.red {\n  background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);\n}\n\n.thumbnail.green {\n  background-image: conic-gradient(at top right, #c76a15, #388f7f, #2b3491);\n}\n\n.thumbnail.purple {\n  background-image: conic-gradient(at top right, #c76a15, #575fb7, #2b3491);\n}\n\n.thumbnail.yellow {\n  background-image: conic-gradient(at top right, #c76a15, #FABD62, #2b3491);\n}\n\n.thumbnail.gray {\n  background-image: conic-gradient(at top right, #c76a15, #4E5769, #2b3491);\n}\n\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n}\n\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n\n.video .info:hover {\n  text-decoration: underline;\n}\n\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n\n/* Details */\n.details .thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 100%;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n\n.video-details-title {\n  margin-top: 8px;\n}\n\n.video-details-speaker {\n  display: flex;\n  gap: 8px;\n  margin-top: 10px\n}\n\n.back {\n  display: flex;\n  align-items: center;\n  margin-left: -5px;\n  cursor: pointer;\n}\n\n.back:hover {\n  text-decoration: underline;\n}\n\n.info-title {\n  font-size: 1.5rem;\n  font-weight: 700;\n  line-height: 1.25;\n  margin: 8px 0 0 0 ;\n}\n\n.info-description {\n  margin: 8px 0 0 0;\n}\n\n.controls {\n  cursor: pointer;\n}\n\n.fallback {\n  background: #f6f7f8 linear-gradient(to right, #e6e6e6 5%, #cccccc 25%, #e6e6e6 35%) no-repeat;\n  background-size: 800px 104px;\n  display: block;\n  line-height: 1.25;\n  margin: 8px 0 0 0;\n  border-radius: 5px;\n  overflow: hidden;\n\n  animation: 1s linear 1s infinite shimmer;\n  animation-delay: 300ms;\n  animation-duration: 1s;\n  animation-fill-mode: forwards;\n  animation-iteration-count: infinite;\n  animation-name: shimmer;\n  animation-timing-function: linear;\n}\n\n\n.fallback.title {\n  width: 130px;\n  height: 30px;\n\n}\n\n.fallback.description {\n  width: 150px;\n  height: 21px;\n}\n\n@keyframes shimmer {\n  0% {\n    background-position: -468px 0;\n  }\n\n  100% {\n    background-position: 468px 0;\n  }\n}\n\n.search {\n  margin-bottom: 10px;\n}\n.search-input {\n  width: 100%;\n  position: relative;\n}\n\n.search-icon {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  inset-inline-start: 0;\n  display: flex;\n  align-items: center;\n  padding-inline-start: 1rem;\n  pointer-events: none;\n  color: #99a1b3;\n}\n\n.search-input input {\n  display: flex;\n  padding-inline-start: 2.75rem;\n  padding-top: 10px;\n  padding-bottom: 10px;\n  width: 100%;\n  text-align: start;\n  background-color: rgb(235 236 240);\n  outline: 2px solid transparent;\n  cursor: pointer;\n  border: none;\n  align-items: center;\n  color: rgb(35 39 47);\n  border-radius: 9999px;\n  vertical-align: middle;\n  font-size: 15px;\n}\n\n.search-input input:hover, .search-input input:active {\n  background-color: rgb(235 236 240/ 0.8);\n  color: rgb(35 39 47/ 0.8);\n}\n\n/* Home */\n.video-list {\n  position: relative;\n}\n\n.video-list .videos {\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n  overflow-y: auto;\n  height: 100%;\n}\n```\n\n\n```css src/animations.css\n/* No additional animations needed */\n\n\n\n\n\n\n\n\n\n/* Previously defined animations below */\n\n\n\n\n\n\n/* Slide animations for Suspense the fallback down */\n::view-transition-old(.slide-down) {\n    animation: 150ms ease-out both fade-out, 150ms ease-out both slide-down;\n}\n\n::view-transition-new(.slide-up) {\n    animation: 210ms ease-in 150ms both fade-in, 400ms ease-in both slide-up;\n}\n\n/* Animations for view transition classed added by transition type */\n::view-transition-old(.slide-forward) {\n    /* when sliding forward, the \"old\" page should slide out to left. */\n    animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;\n}\n\n::view-transition-new(.slide-forward) {\n    /* when sliding forward, the \"new\" page should slide in from right. */\n    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;\n}\n\n::view-transition-old(.slide-back) {\n    /* when sliding back, the \"old\" page should slide out to right. */\n    animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;\n}\n\n::view-transition-new(.slide-back) {\n    /* when sliding back, the \"new\" page should slide in from left. */\n    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 150ms both fade-in,\n    400ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-left;\n}\n\n/* Keyframes to support our animations above. */\n@keyframes slide-up {\n    from {\n        transform: translateY(10px);\n    }\n    to {\n        transform: translateY(0);\n    }\n}\n\n@keyframes slide-down {\n    from {\n        transform: translateY(0);\n    }\n    to {\n        transform: translateY(10px);\n    }\n}\n\n@keyframes fade-in {\n    from {\n        opacity: 0;\n    }\n}\n\n@keyframes fade-out {\n    to {\n        opacity: 0;\n    }\n}\n\n@keyframes slide-to-right {\n    to {\n        transform: translateX(50px);\n    }\n}\n\n@keyframes slide-from-right {\n    from {\n        transform: translateX(50px);\n    }\n    to {\n        transform: translateX(0);\n    }\n}\n\n@keyframes slide-to-left {\n    to {\n        transform: translateX(-50px);\n    }\n}\n\n@keyframes slide-from-left {\n    from {\n        transform: translateX(-50px);\n    }\n    to {\n        transform: translateX(0);\n    }\n}\n\n/* Default .slow-fade. */\n::view-transition-old(.slow-fade) {\n    animation-duration: 500ms;\n}\n\n::view-transition-new(.slow-fade) {\n    animation-duration: 500ms;\n}\n```\n\n```js src/index.js hidden\nimport React, {StrictMode} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport './styles.css';\nimport './animations.css';\n\nimport App from './App';\nimport {Router} from './router';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <Router>\n      <App />\n    </Router>\n  </StrictMode>\n);\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\n### Activity によるサーバサイドレンダリング {/*server-side-rendering-with-activity*/}\n\nサーバサイドレンダリング (server-side rendering; SSR) を使用するページで Activity を使用する場合、追加の最適化が行われます。\n\nページの一部が `mode=\"hidden\"` でレンダーされる場合、それは SSR のレスポンスに含まれません。代わりに React は、画面上の可視コンテンツのハイドレーションを優先しつつ、Activity 内のコンテンツのクライアントレンダーをスケジュールします。\n\nUI の一部が `mode=\"visible\"` でレンダーされる場合、React は Activity 内のコンテンツのハイドレーションを、優先度を下げて行います。これは、サスペンスのコンテンツが低い優先度でハイドレーションされるのと似ています。ユーザがページを操作した場合は、必要に応じてサスペンスバウンダリのハイドレーションを優先します。\n\nこれらは高度なユースケースですが、Activity を使用することで得られる可能性がある追加の利点となっています。\n\n### 将来の Activity モード {/*future-modes-for-activity*/}\n\n将来的には、Activity にさらに多くのモードを追加するかもしれません。\n\nたとえば、一般的なユースケースはモーダルのレンダーです。「アクティブ」なモーダルビューの背後に、「非アクティブ」なページが表示され続けます。このケースで \"hidden\" モードを使うと、表示自体がされず SSR にも含まれないことになるため、うまくいきません。\n\n代わりに、コンテンツを表示したままにして SSR にも含めるが、アンマウント状態に保って更新の優先度を下げる、という新しいモードを検討しています。モーダルが開いている間に背後のコンテンツが更新されると気が散るため、このモードでは DOM の更新を「一時停止」するようにするかもしれません。\n\nActivity の別のモードとして我々が考慮しているのは、メモリ使用量が多すぎる場合に非表示の Activity の state を自動的に破棄する機能です。コンポーネントはすでにアンマウントされているため、アプリの非表示の部分のうち最近使用されていない部分から state を破棄していくことは、リソースを過剰に消費するよりも好ましいかもしれません。\n\nこれらはまだ探求中の領域であり、進展があれば共有します。現段階で Activity に含まれている機能の詳細は、[ドキュメントをチェックしてください](/reference/react/Activity)。\n\n---\n\n# 開発中の機能 {/*features-in-development*/}\n\n他にも、一般的な問題を解決するために、以下のような機能を開発中です。\n\n考えうるソリューションを私たちが検討していく中で、マージ予定の PR 内でテスト中の API 候補が共有されることがあります。様々なアイデアが試された後に、これらのソリューションは変更されたり削除されたりすることがよくあることをご承知おきください。\n\n私たちが取り組んでいるソリューションが時期尚早に共有されると、コミュニティに混乱を招く可能性があります。透明性を保ちつつ混乱を最小限に抑えるために、私たちは現在解決法を開発しようとしている問題点については共有しますが、具体的なソリューションについては共有しないこととします。\n\nこれらの機能に進捗があれば、ブログでの発表とドキュメントの公開を行い、試していただけるようになる予定です。\n\n## React パフォーマンストラック {/*react-performance-tracks*/}\n\n現在、ブラウザ API を使用してパフォーマンスプロファイラに[カスタムのトラック (track) を追加](https://developer.chrome.com/docs/devtools/performance/extension)できる機能を開発しており、これにより React アプリケーションのパフォーマンスに関するより多くの情報を提供できるようになります。\n\nこの機能はまだ開発中でありドキュメントの準備が整っていないため、実験的機能として完全にリリースできてはいません。ですが React の実験的バージョンを使用すれば、パフォーマンストラックが自動的にプロファイルに追加されるため、一足早く試すことが可能です。\n\n<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}>\n  <picture >\n      <source srcset=\"/images/blog/react-labs-april-2025/perf_tracks.png\" />\n      <img className=\"w-full light-image\" src=\"/images/blog/react-labs-april-2025/perf_tracks.webp\" />\n  </picture>\n  <picture >\n      <source srcset=\"/images/blog/react-labs-april-2025/perf_tracks_dark.png\" />\n      <img className=\"w-full dark-image\" src=\"/images/blog/react-labs-april-2025/perf_tracks_dark.webp\" />\n  </picture>\n</div>\n\n対応予定の既知の問題がいくつか存在しています。例えば、パフォーマンスに関する問題や、スケジューラーのトラックがサスペンドされたツリー間で作業を「接続」できない場合がある、といった問題です。そのため、まだ試せる段階にはありません。また、トラックのデザインと使いやすさを改善するために、初期ユーザからのフィードバックを収集している最中です。\n\nこれらの問題を解決後に、実験的機能としてドキュメントを公開し、試す準備が整ったことをお知らせする予定です。\n\n---\n\n## エフェクト依存配列の自動化 {/*automatic-effect-dependencies*/}\n\nフックをリリースしたとき、私たちには 3 つの動機がありました。\n\n- **コンポーネント間でのコード共有**：フックがレンダープロップや高階コンポーネントといったパターンを置き換えたことで、コンポーネントの階層構造を変更せずにステートフルなロジックを再利用できるようになりました。\n- **ライフサイクルではなく関数指向で考える**：フックのおかげで、1 つのコンポーネントを、ライフサイクルメソッドに基づいて無理矢理分割するのではなく、コードの意味的な関連性（サブスクリプションの設定やデータフェッチ）に基づいて小さな関数へと分割できるようになりました。\n- **事前コンパイルのサポート**：フックは、事前コンパイルをサポートし、ライフサイクルメソッドやクラスの制限によって引き起こされる最適化漏れといった落とし穴を減らせるように設計されました。\n\nリリース以来、フックは*コンポーネント間でのコード共有*という点では成功しています。フックは、コンポーネント間でロジックを共有するための望ましい方法となり、レンダープロップや高階コンポーネントの使用例は減少しています。フックはまた、クラスコンポーネントでは不可能だった Fast Refresh のような機能をサポートすることにも成功しています。\n\n### エフェクトは難しい {/*effects-can-be-hard*/}\n\n残念ながら一部のフックは、ライフサイクルではなく関数の観点で考えることがいまだに困難です。特にエフェクトは理解しにくく、この辛さは開発者から最もよく聞かれるところとなっています。昨年私たちは、エフェクトの使われ方や、エフェクトのユースケースを簡素化し理解しやすくするための方法について、多くの時間を費やし研究を行いました。\n\n多くの場合に混乱の原因は、必要もなくエフェクトが使われていることにあるとわかりました。[そのエフェクトは不要かも](/learn/you-might-not-need-an-effect)のガイドは、エフェクトがソリューションとして適切ではないパターンの多くをカバーしています。しかし、ある問題に対してエフェクトが適切であるという場合ですら、エフェクトはクラスコンポーネントのライフサイクルよりも理解しにくい場合があります。\n\n混乱の一因は、開発者がエフェクトをエフェクト自体の視点（エフェクトが何をするか）ではなく、ライフサイクルのような*コンポーネント*の視点から考えてしまっていることにあると考えています。\n\n[ドキュメントにあるこちらの例](/learn/lifecycle-of-reactive-effects#thinking-from-the-effects-perspective)を見てみましょう。\n\n```js\nuseEffect(() => {\n  // Your Effect connected to the room specified with roomId...\n  const connection = createConnection(serverUrl, roomId);\n  connection.connect();\n  return () => {\n    // ...until it disconnected\n    connection.disconnect();\n  };\n}, [roomId]);\n```\n\n多くのユーザはこのコードを「マウント時に `roomId` に接続し、`roomId` が変更されるたびに古いルームから切断して接続を再確立する」のように解釈してしまいます。しかし、このようにコンポーネントのライフサイクルの視点で考えると、エフェクトを正しく書くにはコンポーネントライフサイクルの全状態を考慮する必要があることになってしまいます。これは難しいことなので、コンポーネント視点だとクラスのライフサイクルよりもエフェクトの方が難しく見えるのは、無理もないことです。\n\n### 依存配列のないエフェクト {/*effects-without-dependencies*/}\n\n代わりに、エフェクトの視点から考える方がベターです。エフェクトはコンポーネントのライフサイクルについて知りません。同期を開始する方法と停止する方法が記述されているだけです。ユーザがこのようにエフェクトを考えることでエフェクトは書きやすくなり、必要次第で何度も開始・停止されることに対して、より頑強になります。\n\n私たちは、エフェクトをコンポーネントの視点から考えてしまう理由について時間をかけて調査し、その一因が依存配列にあると考えるようになりました。常に目の前にあって書かなければならないもののため、コードが何に「反応」しているのかを意識せざるを得ず、だから「これらの値が変わったらこれを行え」式のメンタルモデルに誘い込まれてしまうのです。\n\nフックをリリースした当時から、事前コンパイルでこの使いやすさを改善できることは分かっていました。React Compiler を使用すると、ほとんどの場合、自分で `useCallback` や `useMemo` を書く必要がなくなります。エフェクトの場合、コンパイラが依存配列を自動的に挿入できるようになります。\n\n```js\nuseEffect(() => {\n  const connection = createConnection(serverUrl, roomId);\n  connection.connect();\n  return () => {\n    connection.disconnect();\n  };\n}); // compiler inserted dependencies.\n```\n\nこのコードでは、依存配列を React Compiler が自動的に推論して挿入するため、見たり書いたりする必要がありません。[IDE 拡張](#compiler-ide-extension)や [`useEffectEvent`](/reference/react/useEffectEvent) のような機能を使用することで、デバッグが必要なときや依存値を削除して最適化したい時のために、コンパイラが挿入したものを表示する CodeLens を提供できます。これにより、エフェクトを書くための正しいメンタルモデル、つまり、エフェクトはコンポーネントやフックの state を他のものと同期させるためにいつでも実行されうる、というメンタルモデルが補強されます。\n\n依存配列の自動挿入について我々が期待しているのは、ただ書きやすくなるというだけのことではありません。コンポーネントのライフサイクルではなく「エフェクトが何をするのか」という視点で考えることを強制し、理解がしやすくなることを期待しています。\n\n---\n\n## コンパイラの IDE 拡張 {/*compiler-ide-extension*/}\n\n2025 年に React Compiler の最初の安定版を[共有しました](/blog/2025/10/07/react-compiler-1)。今後もさらに改善を続けていく予定です。\n\nまた、React Compiler を使用してコードの理解やデバッグ体験を改善するための情報を提供する方法を模索し始めました。私たちが探求し始めたアイデアのひとつは、[Lauren Tan の React Conf での講演](https://conf2024.react.dev/talks/5)で使用された拡張に似た、React Compiler によって駆動される新しい実験的な LSP ベースの React IDE 拡張です。\n\n考え方としては、IDE 内で情報やサジェスチョン、最適化候補を直接表示するために、コンパイラの静的解析を活用する、というものです。たとえば、React のルールに違反しているコードに対する診断、コンポーネントやフックがコンパイラによって最適化されたかどうかを示すホバー、または[自動挿入されたエフェクトの依存配列](#automatic-effect-dependencies)を表示する CodeLens といったものを提供できます。\n\nIDE 拡張はまだ初期の探求段階ですが、今後の更新で進捗を共有していきます。\n\n---\n\n## フラグメント ref {/*fragment-refs*/}\n\nイベント管理、位置決め、フォーカスのために使われる DOM API の多くは、React ではうまく組み合わせて書くことが困難です。このため開発者はよく、エフェクトを使用したり、複数の ref を取り回したり、React 19 で削除された `findDOMNode` のような API を使用したりしています。\n\nフラグメントに ref を追加し、単一の DOM 要素ではなく DOM 要素のグループを参照できるようにすることを検討しています。これにより、複数の子を管理することが簡単になり、DOM API を呼び出すときにより組み合わせしやすい React コードが書きやすくなることを期待しています。\n\nフラグメント ref はまだ研究中です。最終的な API が完成に近づいたらお知らせします。\n\n---\n\n## ジェスチャーアニメーション {/*gesture-animations*/}\n\nビュー遷移機能を強化して、メニューをスワイプして開く、またはフォトカルーセルをスクロールする、といったジェスチャーアニメーションをサポートする方法についても研究しています。\n\nジェスチャーには以下のような幾つかの新たな課題があります。\n\n- **ジェスチャーは連続的**：スワイプの最中、アニメーションはユーザの指の位置と結びついており、起動したらただ最後まで再生されるというわけではありません。\n- **ジェスチャーは完了しない場合がある**：指を離した際にジェスチャーアニメーションは最後まで再生されるかもしれませんが、進んだ距離によっては元の状態に戻る（メニューを少しだけ開いた際のように）かもしれません。\n- **ジェスチャーでは「新」と「旧」が逆**：アニメーション中は、アニメーション元のページを「生きた」状態でインタラクティブに保ちたいはずです。ブラウザのビュー遷移モデルでは「古い」状態がスナップショットで「新しい」状態が生きた DOM ですので、動作が逆転しています。\n\n私たちはうまく機能するアプローチを発見できたと考えているため、ジェスチャー遷移をトリガする新しい API を導入するかもしれません。今のところは `<ViewTransition>` のリリースに集中していますが、その後にジェスチャーを再検討する予定です。\n\n---\n\n## 並行ストア {/*concurrent-stores*/}\n\nReact 18 を並行レンダー (concurrent rendering) 機能と共にリリースした際、`useSyncExternalStore` もリリースしました。これによって、React の state やコンテクストを使用しない外部ストアライブラリは、当該ストアが更新されたときに同期レンダーを強制することで、[並列レンダーをサポート](https://github.com/reactwg/react-18/discussions/70)できるようになりました。\n\nしかし `useSyncExternalStore` の使用にはコストが伴います。トランジションのような並行レンダー機能からの離脱を強制し、既存のコンテンツにサスペンスのフォールバックを表示させてしまうからです。\n\nReact 19 がリリースされましたので、`use` API を使用して並行レンダー対応の外部ストアを完全にサポートするプリミティブを作成できないか、この問題領域について再検討を行っています。\n\n```js\nconst value = use(store);\n```\n\n私たちの目標は、外部の状態を、レンダー中に不整合 (tearing) を起こさず読み取ることができ、React が提供するすべての並行レンダー機能とシームレスに連携できるようにすることです。\n\nこの研究はまだ初期段階です。進展があれば、どのような新しい API になるかを共有する予定です。\n\n---\n\n_この投稿をレビューしていただいた [Aurora Scharff](https://bsky.app/profile/aurorascharff.no), [Dan Abramov](https://bsky.app/profile/danabra.mov), [Eli White](https://twitter.com/Eli_White), [Lauren Tan](https://bsky.app/profile/no.lol), [Luna Wei](https://github.com/lunaleaps), [Matt Carroll](https://twitter.com/mattcarrollcode), [Jack Pope](https://jackpope.me), [Jason Bonta](https://threads.net/someextent), [Jordan Brown](https://github.com/jbrown215), [Jordan Eldredge](https://bsky.app/profile/capt.dev), [Mofei Zhang](https://threads.net/z_mofei), [Sebastien Lorber](https://bsky.app/profile/sebastienlorber.com), [Sebastian Markbåge](https://bsky.app/profile/sebmarkbage.calyptus.eu), [Tim Yung](https://github.com/yungsters) に感謝します。_\n"
  },
  {
    "path": "src/content/blog/2025/10/01/react-19-2.md",
    "content": "---\ntitle: \"React 19.2\"\nauthor: The React Team\ndate: 2025/10/01\ndescription: React 19.2 では Activity、パフォーマンストラック、useEffectEvent などの新機能が追加されます。\n---\n\nOctober 1, 2025 by [The React Team](/community/team)\n\n---\n\n<Intro>\n\nReact 19.2 が npm で利用可能になりました！\n\n</Intro>\n\nこれは、昨年 12 月の React 19 と今年 6 月の React 19.1 に続く、ここ 1 年で 3 回目となるリリースです。この投稿では React 19.2 の新機能の概要を説明し、いくつかの注目すべき変更点をハイライトとして紹介します。\n\n<InlineToc />\n\n---\n\n## 新しい React の機能 {/*new-react-features*/}\n\n### `<Activity />` {/*activity*/}\n\n`<Activity>` を使うことで、アプリを制御・優先順位付けが可能な \"activity\" に分割できます。\n\nActivity は、条件付きレンダーに対する代替手段として、アプリの一部の表示を切り替えるのに使用できます。\n\n```js\n// Before\n{isVisible && <Page />}\n\n// After\n<Activity mode={isVisible ? 'visible' : 'hidden'}>\n  <Page />\n</Activity>\n```\n\nReact 19.2 では、Activity は `visible` と `hidden` の 2 つのモードをサポートしています。\n\n- `hidden`: 子要素を非表示にし、エフェクトをアンマウントし、React が他に処理するものがなくなるまで、すべての更新を遅延させます。\n- `visible`: 子要素を表示し、エフェクトをマウントし、更新を通常通り処理できるようにします。\n\nこれにより、画面上で表示されているコンテンツのパフォーマンスに影響を与えることなく、アプリの非表示の部分を事前レンダーし続けることができます。\n\nActivity 機能を使用することで、ユーザが次に移動する可能性の高い不可視部分をレンダーしておくことや、ユーザが離れたあとに state を保持しておくことが可能です。これにより、データ、CSS、画像をバックグラウンドで読み込んでナビゲーションを高速化したり、戻るナビゲーションで入力フィールドなどの状態を維持したりできるようになります。\n\n将来的には、さまざまなユースケースに対応するために、Activity にさらに多くのモードを追加する予定です。\n\nActivity の使用方法の例については、[Activity のドキュメント](/reference/react/Activity)を参照してください。\n\n---\n\n### `useEffectEvent` {/*use-effect-event*/}\n\n`useEffect` でよくあるパターンの 1 つは、外部システムからの「イベント」に応答してアプリコードに通知を行うことです。例えば、チャットルームが接続されたときに通知を表示したい場合を考えましょう。\n\n```js {5,11}\nfunction ChatRoom({ roomId, theme }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.on('connected', () => {\n      showNotification('Connected!', theme);\n    });\n    connection.connect();\n    return () => {\n      connection.disconnect()\n    };\n  }, [roomId, theme]);\n  // ...\n```\n\n上記のコードの問題は、このような「イベント」内で使用されている値が変更されると、それを囲んでいるエフェクトが再実行されてしまうことです。例えば、`theme` を変更するとチャットルームが再接続されます。エフェクトのロジック自体に関連する `roomId` のような値の場合はこれで正しいですが、`theme` の場合は理に適っていません。\n\nこれを解決するため、多くのユーザはリンタのルールを単に無効化して、依存値を除外してきました。しかしこれでは、後でエフェクトを更新したくなったときに、リンタが依存配列をそれに追従させてくれなくなるため、バグに繋がります。\n\n`useEffectEvent` を使うことで、このロジックの「イベント」部分を、それをトリガするエフェクトから分離できます。\n\n```js {2,3,4,9}\nfunction ChatRoom({ roomId, theme }) {\n  const onConnected = useEffectEvent(() => {\n    showNotification('Connected!', theme);\n  });\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.on('connected', () => {\n      onConnected();\n    });\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]); // ✅ All dependencies declared (Effect Events aren't dependencies)\n  // ...\n```\n\nDOM イベントと同様、エフェクトイベントは常に最新の props や state を「見る」ことができます。\n\n**エフェクトイベントは依存配列に宣言すべきではありません**。リンタがエフェクトイベントを依存値として挿入しないようにするため、`eslint-plugin-react-hooks@latest` にアップグレードする必要があります。エフェクトイベントは、それを使うエフェクトと同じコンポーネントまたはフック内でのみ宣言できることに注意してください。この制限はリンタによってチェックされます。\n\n<Note>\n\n#### `useEffectEvent` をいつ使うべきか {/*when-to-use-useeffectevent*/}\n\n`useEffectEvent` は概念的には、ユーザに起因するイベントではなくエフェクトから発火する「イベント」となる関数に使用すべきです（それが「エフェクトイベント」の意味です）。すべてを `useEffectEvent` でラップする必要はありませんし、リンタのエラーを黙らせるためだけに使うとバグにつながる可能性があります。\n\nエフェクトイベントについての考え方の詳細については、[イベントとエフェクトを分離する](/learn/separating-events-from-effects#extracting-non-reactive-logic-out-of-effects)を参照してください。\n\n</Note>\n\n---\n\n### `cacheSignal` {/*cache-signal*/}\n\n<RSC>\n\n`cacheSignal` は [React Server Components](/reference/rsc/server-components) でのみ使用できます。\n\n</RSC>\n\n`cacheSignal` を使うことで、[`cache()`](/reference/react/cache) のライフタイムの終了タイミングを知ることができます。\n\n```\nimport {cache, cacheSignal} from 'react';\nconst dedupedFetch = cache(fetch);\n\nasync function Component() {\n  await dedupedFetch(url, { signal: cacheSignal() });\n}\n```\n\nこれにより、キャッシュされる結果がもはや必要なくなった場合に、作業をクリーンアップまたは中止できるようになります。以下のような場合が該当します。\n\n- React がレンダーを正常に完了した\n- レンダーが中断された\n- レンダーが失敗した\n\n詳細については、[`cacheSignal` のドキュメント](/reference/react/cacheSignal)を参照してください。\n\n---\n\n### パフォーマンストラック {/*performance-tracks*/}\n\nReact 19.2 では、Chrome DevTools のパフォーマンスプロファイルに新しい[カスタムトラック](https://developer.chrome.com/docs/devtools/performance/extension)が複数追加され、React アプリのパフォーマンスに関するより詳しい情報が提供されます。\n\n<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}>\n  <picture >\n      <source srcset=\"/images/blog/react-labs-april-2025/perf_tracks.png\" />\n      <img className=\"w-full light-image\" src=\"/images/blog/react-labs-april-2025/perf_tracks.webp\" />\n  </picture>\n  <picture >\n      <source srcset=\"/images/blog/react-labs-april-2025/perf_tracks_dark.png\" />\n      <img className=\"w-full dark-image\" src=\"/images/blog/react-labs-april-2025/perf_tracks_dark.webp\" />\n  </picture>\n</div>\n\n[React パフォーマンストラックのドキュメント](/reference/dev-tools/react-performance-tracks)では、トラックに含まれるすべての内容を説明していますが、ここでは概要を説明します。\n\n#### Scheduler ⚛ {/*scheduler-*/}\n\nScheduler トラックは、React が様々な優先度で何を処理しているかを表示します。例えばユーザ操作のための \"Blocking\" や startTransition 内の更新のための \"Transition\" などです。各トラック内では、更新をスケジュールしたイベントといった作業種別や、更新に対応するレンダーの開始タイミングが表示されます。\n\nまた、更新が別の優先度の処理を待つためにブロックされている、React が作業継続前にペイントの完了を待っている、といった情報も表示されます。Scheduler トラックは、React がコードを異なる優先度に分割する方法や、作業を完了した順序を理解するのに役立ちます。\n\n含まれるすべての内容については、[Scheduler トラック](/reference/dev-tools/react-performance-tracks#scheduler)のドキュメントを参照してください。\n\n#### Components ⚛ {/*components-*/}\n\nComponents トラックは、React がレンダーまたはエフェクト実行のために処理しているコンポーネントのツリーを示します。内部には、子やエフェクトがマウントされたときの \"Mount\" や、React 外の作業を待つためにレンダーがブロックされたときの \"Blocked\" といったラベルが表示されます。\n\nComponents トラックは、コンポーネントのレンダーやエフェクトの実行タイミング、あるいはそれらの作業にかかる時間を知ることで、パフォーマンスの問題を特定するのに役立ちます。\n\n含まれるすべての内容については、[Components トラックのドキュメント](/reference/dev-tools/react-performance-tracks#components)を参照してください。\n\n---\n\n## 新しい React DOM の機能 {/*new-react-dom-features*/}\n\n### 部分プリレンダー {/*partial-pre-rendering*/}\n\n19.2 では、アプリの一部を事前にレンダーし、後でレンダーを再開するための新機能を追加しています。\n\nこの機能は「部分プリレンダー (Partial Pre-rendering)」と呼ばれ、アプリの静的な部分を事前レンダーして CDN から提供し、後でシェルのレンダーを再開して動的コンテンツで埋められるようにするためのものです。\n\n後で再開できるようアプリを事前レンダーするには、まず `AbortController` を使って `prerender` を呼び出します。\n\n```\nconst {prelude, postponed} = await prerender(<App />, {\n  signal: controller.signal,\n});\n\n// Save the postponed state for later\nawait savePostponedState(postponed);\n\n// Send prelude to client or CDN.\n```\n\nその後、`prelude` シェルをクライアントに返し、後で `resume` を呼び出して SSR ストリーム内に「再開」できます。\n\n```\nconst postponed = await getPostponedState(request);\nconst resumeStream = await resume(<App />, postponed);\n\n// Send stream to client.\n```\n\nまたは `resumeAndPrerender` を呼び出して、SSG 用の静的 HTML を取得することもできます。\n\n```\nconst postponedState = await getPostponedState(request);\nconst { prelude } = await resumeAndPrerender(<App />, postponedState);\n\n// Send complete HTML prelude to CDN.\n```\n\n詳細については、新しい API のドキュメントを参照してください。\n- `react-dom/server`\n  - [`resume`](/reference/react-dom/server/resume): Web ストリーム用\n  - [`resumeToPipeableStream`](/reference/react-dom/server/resumeToPipeableStream): Node ストリーム用\n- `react-dom/static`\n  - [`resumeAndPrerender`](/reference/react-dom/static/resumeAndPrerender): Web ストリーム用\n  - [`resumeAndPrerenderToNodeStream`](/reference/react-dom/static/resumeAndPrerenderToNodeStream): Node ストリーム用\n\n加えて、prerender 系 API は、`resume` 系 API に渡すための `postpone` ステートを返すようになりました。\n\n---\n\n## 注目すべき変更点 {/*notable-changes*/}\n\n### SSR におけるサスペンスバウンダリのバッチ処理 {/*batching-suspense-boundaries-for-ssr*/}\n\nサスペンスバウンダリの表示のされ方が、クライアントでレンダーされる場合とサーバサイドレンダリングからストリーミングされる場合とで異なる、という動作上のバグを修正しました。\n\n19.2 以降の React では、サーバでレンダーされた複数のサスペンスバウンダリが短時間で立て続けに表示へ切り替わるとき、それをバッチ処理することで、より多くのコンテンツが同時に表示へ切り替わるようにし、クライアントでレンダーされた場合の動作と一致させます。\n\n<Diagram name=\"19_2_batching_before\" height={162} width={1270} alt=\"Diagram with three sections, with an arrow transitioning each section in between. The first section contains a page rectangle showing a glimmer loading state with faded bars. The second panel shows the top half of the page revealed and highlighted in blue. The third panel shows the entire the page revealed and highlighted in blue.\">\n\nこれまでは、サーバサイドレンダリングのストリーミング中に、サスペンスのコンテンツがフォールバックを即座に置き換えていた。\n\n</Diagram>\n\n<Diagram name=\"19_2_batching_after\" height={162} width={1270} alt=\"Diagram with three sections, with an arrow transitioning each section in between. The first section contains a page rectangle showing a glimmer loading state with faded bars. The second panel shows the same page. The third panel shows the entire the page revealed and highlighted in blue.\">\n\nReact 19.2 では、サスペンスバウンダリは短い時間バッチ処理され、より多くのコンテンツを同じタイミングで表示できるようになる。\n\n</Diagram>\n\nこの修正は、SSR 中のサスペンスに対する `<ViewTransition>` サポートの準備にもなっています。より多くのコンテンツをまとめて表示に切り替えることで、アニメーションがより大きなコンテンツのまとまりで実行され、五月雨式に小刻みなコンテンツのアニメーションが発生するのを回避できます。\n\n<Note>\n\nReact は、このスロットリングが core web vital や検索ランキングに影響を与えないようにするためのヒューリスティックを使用します。\n\n例えば、ページのトータル読み込み時間が 2.5 秒（[LCP](https://web.dev/articles/lcp) で \"good\" と見なされる時間）に近づいている場合、スロットリングが指標悪化の原因とならないよう、React はバッチ処理を停止して即座にコンテンツを表示します。\n\n</Note>\n\n---\n\n### SSR：Node での Web ストリームサポート {/*ssr-web-streams-support-for-node*/}\n\nReact 19.2 では、Node.js においてもストリーミング SSR のための Web ストリームのサポートが追加されます。\n- [`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream) が Node.js で利用可能になりました。\n- [`prerender`](/reference/react-dom/static/prerender) が Node.js で利用可能になりました。\n\n新しい `resume` API も同様です。\n- [`resume`](/reference/react-dom/server/resume) が Node.js で利用可能です。\n- [`resumeAndPrerender`](/reference/react-dom/static/resumeAndPrerender) が Node.js で利用可能です。\n\n\n<Pitfall>\n\n#### Node.js でのサーバサイドレンダリングには Node ストリームを推奨 {/*prefer-node-streams-for-server-side-rendering-in-nodejs*/}\n\nNode.js 環境では、引き続き Node ストリーム API の使用を強く推奨します。\n\n- [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream)\n- [`resumeToPipeableStream`](/reference/react-dom/server/resumeToPipeableStream)\n- [`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream)\n- [`resumeAndPrerenderToNodeStream`](/reference/react-dom/static/resumeAndPrerenderToNodeStream)\n\nこれは、Node においては Node ストリームが Web ストリームよりはるかに高速であり、また Web ストリームはデフォルトで圧縮をサポートしていないため、ユーザがストリーミングの恩恵を受けられなくなってしまう可能性があるためです。\n\n</Pitfall>\n\n---\n\n### `eslint-plugin-react-hooks` v6 {/*eslint-plugin-react-hooks*/}\n\n`eslint-plugin-react-hooks@latest` も公開されました。`recommended` プリセットではデフォルトで flat config が使用され、新しい React Compiler を利用したルールがオプトインで利用できます。\n\nレガシー config を引き続き使用するには、`recommended-legacy` に変更できます。\n\n```diff\n- extends: ['plugin:react-hooks/recommended']\n+ extends: ['plugin:react-hooks/recommended-legacy']\n```\n\nコンパイラ対応のルールの完全なリストについては、[リンタのドキュメントを確認してください](/reference/eslint-plugin-react-hooks#recommended)。\n\n変更の完全なリストについては、`eslint-plugin-react-hooks` の [changelog を確認してください](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/CHANGELOG.md#610)。\n\n---\n\n### デフォルトの `useId` プレフィックスの更新 {/*update-the-default-useid-prefix*/}\n\n19.2 では、デフォルトの `useId` プレフィックスを `:r:` (19.0.0) または `«r»` (19.1.0) から `_r_` に更新しています。\n\nCSS セレクタとして無効な特殊文字を使用していた元々の意図は、ユーザが記述する ID と衝突する可能性を下げるためでした。しかし、View Transitions をサポートするためには、`useId` によって生成される ID が `view-transition-name` および XML 1.0 name として有効であることを保証する必要があります。\n\n---\n\n## 変更履歴 {/*changelog*/}\n\nその他の注目すべき変更\n- `react-dom`: 巻き上げ可能な style で nonce を使用できるように [#32461](https://github.com/facebook/react/pull/32461)\n- `react-dom`: React が所有するノードをコンテナとして使用し、テキストコンテンツも含まれている場合に警告を表示 [#32774](https://github.com/facebook/react/pull/32774)\n\n注目すべきバグ修正\n- `react`: コンテクストを \"SomeContext.Provider\" ではなく \"SomeContext\" として文字列化 [#33507](https://github.com/facebook/react/pull/33507)\n- `react`: popstate イベントでの無限 useDeferredValue ループを修正 [#32821](https://github.com/facebook/react/pull/32821)\n- `react`: useDeferredValue に初期値が渡されたときのバグを修正 [#34376](https://github.com/facebook/react/pull/34376)\n- `react`: クライアント Action でフォームを送信するときのクラッシュを修正 [#33055](https://github.com/facebook/react/pull/33055)\n- `react`: dehydrated suspense バウンダリが再サスペンドした場合にコンテンツを非表示/表示 [#32900](https://github.com/facebook/react/pull/32900)\n- `react`: Hot Reload 中に大きなツリーで発生するスタックオーバーフローを回避 [#34145](https://github.com/facebook/react/pull/34145)\n- `react`: 複数のコンポーネントスタック改善 [#33629](https://github.com/facebook/react/pull/33629), [#33724](https://github.com/facebook/react/pull/33724), [#32735](https://github.com/facebook/react/pull/32735), [#33723](https://github.com/facebook/react/pull/33723)\n- `react`: React.lazy されたコンポーネント内での React.use のバグ修正 [#33941](https://github.com/facebook/react/pull/33941)\n- `react-dom`: ARIA 1.3 属性が使用されたときの警告を停止 [#34264](https://github.com/facebook/react/pull/34264)\n- `react-dom`: Suspense フォールバック内の深くネストされた Suspense のバグを修正 [#33467](https://github.com/facebook/react/pull/33467)\n- `react-dom`: レンダー中に中断した後にサスペンドするときのハングを回避 [#34192](https://github.com/facebook/react/pull/34192)\n\n変更の完全なリストについては、[Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) を参照してください。\n\n\n---\n\n_この投稿を[書いてくれた](https://www.youtube.com/shorts/T9X3YkgZRG0) [Ricky Hanlon](https://bsky.app/profile/ricky.fm)、およびこの投稿をレビューしてくれた [Dan Abramov](https://bsky.app/profile/danabra.mov)、[Matt Carroll](https://twitter.com/mattcarrollcode)、[Jack Pope](https://jackpope.me)、[Joe Savona](https://x.com/en_JS) に感謝します。_\n"
  },
  {
    "path": "src/content/blog/2025/10/07/introducing-the-react-foundation.md",
    "content": "---\ntitle: \"React Foundation 設立\"\nauthor: Seth Webster, Matt Carroll, Joe Savona\ndate: 2025/10/07\ndescription: 本日、React Foundation の設立と新しい技術ガバナンス構造について発表します。\n---\n\nOctober 7, 2025 by [Seth Webster](https://x.com/sethwebster), [Matt Carroll](https://x.com/mattcarrollcode), [Joe Savona](https://x.com/en_JS), [Sophie Alpert](https://x.com/sophiebits)\n\n---\n\n\n<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem', marginLeft: '7rem', marginRight: '7rem' }}>\n  <picture >\n      <source srcset=\"/images/blog/react-foundation/react_foundation_logo.png\" />\n      <img className=\"w-full light-image\" src=\"/images/blog/react-foundation/react_foundation_logo.webp\" />\n  </picture>\n  <picture >\n      <source srcset=\"/images/blog/react-foundation/react_foundation_logo_dark.png\" />\n      <img className=\"w-full dark-image\" src=\"/images/blog/react-foundation/react_foundation_logo_dark.webp\" />\n  </picture>\n</div>\n\n<Intro>\n\n本日、React Foundation の設立と新しい技術ガバナンス構造について発表します。\n\n</Intro>\n\n---\n\n開発者がすばらしいユーザ体験を構築できるようにするため、私たちが React をオープンソース化してから 10 年以上が経過しました。その最初期から、React は Meta 以外のコントリビュータからの多大な貢献を受けてきました。時が経つにつれ、コントリビュータの数とその貢献の範囲は大幅に拡大しました。Meta のために開発されたツールとして始まったプロジェクトは、複数の企業にまたがってエコシステム全体から常に貢献を受け続けるプロジェクトへと成長しました。React は、もはや単一の企業の枠組みを超えたのです。\n\nReact コミュニティによりよいサービスを提供するために、React と React Native を、Meta から新しく設立する React Foundation に移管する計画を発表します。この変更の一環として、新しい独立した技術ガバナンス構造も導入する予定です。これらの変更により、React エコシステムに属するプロジェクトに、より多くのリソースを提供できるようになると信じています。\n\n## React Foundation {/*the-react-foundation*/}\n\nReact Foundation は、React、React Native、および JSX などのサポートプロジェクトの新しいホームとなります。React Foundation のミッションは、React コミュニティとエコシステムをサポートすることです。設立後に、React Foundation は以下を行います。\n\n* GitHub、CI、商標といった React のインフラストラクチャの維持\n* React Conf の運営\n* エコシステムのプロジェクトへの資金提供、助成金の発行、プログラムの作成など、React エコシステムをサポートするためのイニシアチブを作り上げる\n\nReact Foundation は取締役会によって運営され、Seth Webster がエグゼクティブディレクターとして務めます。この取締役会が、React の開発、コミュニティ、およびエコシステムをサポートするための資金とリソースを指揮します。React Foundation がベンダ中立を保ち、コミュニティの最善の利益を反映するため、これが最適な構造であると信じています。\n\nReact Foundation の企業創設メンバーは、Amazon、Callstack、Expo、Meta、Microsoft、Software Mansion、Vercel です。これらの企業は React および React Native エコシステムに大きな影響を与えており、我々はそのサポートに感謝しています。今後、さらに多くのメンバーを迎えられることを楽しみにしています。\n\n<div style={{display: 'flex', justifyContent: 'center', margin: '2rem'}}>\n  <picture >\n      <source srcset=\"/images/blog/react-foundation/react_foundation_member_logos.png\" />\n      <img className=\"w-full light-image\" src=\"/images/blog/react-foundation/react_foundation_member_logos.webp\" />\n  </picture>\n  <picture >\n      <source srcset=\"/images/blog/react-foundation/react_foundation_member_logos_dark.png\" />\n      <img className=\"w-full dark-image\" src=\"/images/blog/react-foundation/react_foundation_member_logos_dark.webp\" />\n  </picture>\n</div>\n\n## React の技術ガバナンス {/*reacts-technical-governance*/}\n\nReact の技術的な方向性は、React に貢献し、維持している人々によって設定されるべきだと考えています。React が財団に移行するにあたり、単一の企業や組織が過度に影響力を行使しないことが重要です。これを達成するために、React Foundation から独立した新しい技術ガバナンス構造を React に導入する予定です。\n\nReact の新しい技術ガバナンス構造を決定する取り組みの一環として、コミュニティからフィードバックを求めます。最終決定の後、詳細については今後の投稿で共有します。\n\n## 謝辞 {/*thank-you*/}\n\nReact の驚異的な成長は、React を形作ってきた何千もの人々、企業、プロジェクトのおかげです。React Foundation の設立は、React コミュニティの強さと活力の証です。React Foundation と React の新しい技術ガバナンスが一体となることで、React の未来が今後何年にもわたって確実なものになることを期待しています。\n"
  },
  {
    "path": "src/content/blog/2025/10/07/react-compiler-1.md",
    "content": "---\ntitle: \"React Compiler v1.0\"\nauthor: Lauren Tan, Joe Savona, and Mofei Zhang\ndate: 2025/10/07\ndescription: 本日、コンパイラの最初の安定版リリースを公開します。\n\n---\n\nOct 7, 2025 by [Lauren Tan](https://x.com/potetotes), [Joe Savona](https://x.com/en_JS), and [Mofei Zhang](https://x.com/zmofei).\n\n---\n\n<Intro>\n\nReact チームから以下の新しいお知らせを共有できることを嬉しく思います。\n\n</Intro>\n\n1. React Compiler 1.0 が本日利用可能になりました。\n2. コンパイラを使用した lint ルールが `eslint-plugin-react-hooks` の `recommended` および `recommended-latest` プリセットに同梱されます。\n3. 段階的採用ガイドを公開し、Expo、Vite、Next.js と提携することで、新しいアプリがコンパイラを有効にした状態で開始できるようにしました。\n\n---\n\n本日、コンパイラの最初の安定版リリースを公開します。React Compiler は React と React Native の両方で動作し、書き換えを必要とせずにコンポーネントとフックを自動的に最適化します。このコンパイラは Meta の主要なアプリで現場テストが行われており、フルに本番対応済みです。\n\n[React Compiler](/learn/react-compiler) は、ビルド時の自動メモ化によって React アプリを最適化するツールです。昨年、React Compiler の[最初のベータ版](/blog/2024/10/21/react-compiler-beta-release)を公開し、多くの素晴らしいフィードバックと貢献を受けることができました。コンパイラを採用した人々からの成功事例（[Sanity Studio](https://github.com/reactwg/react-compiler/discussions/33) や [Wakelet](https://github.com/reactwg/react-compiler/discussions/52) のケーススタディを参照）を嬉しく思っており、React コミュニティのより多くのユーザにこのコンパイラを届けられることを楽しみにしています。\n\nこのリリースは、10 年近くに及ぶ、巨大で複雑なエンジニアリング作業の集大成となるものです。React チームのコンパイラに関する最初の探求は、2017 年の [Prepack](https://github.com/facebookarchive/prepack) から始まりました。このプロジェクトは最終的に終了しましたが、そこから得られた多くの学びは、将来のコンパイラを見据えたフックの設計に影響を与えました。2021 年には [Xuan Huang](https://x.com/Huxpro) が、React Compiler という新アプローチの[初期バージョン](https://www.youtube.com/watch?v=lGEMwh32soc)をデモしました。\n\nReact Compiler のこの初期バージョンは後に書き直されることとなりましたが、この最初のプロトタイプによって、これが解決可能な問題であるという確信が深まり、また、別のコンパイラアーキテクチャによって我々が望む形のメモ化特性が正確に実現できるという学びが得られました。[Joe Savona](https://x.com/en_JS)、[Sathya Gunasekaran](https://x.com/_gsathya)、[Mofei Zhang](https://x.com/zmofei)、[Lauren Tan](https://x.com/potetotes) が最初の書き直しを行い、コンパイラのアーキテクチャを制御フローグラフ (Control Flow Graph; CFG) ベースの高レベル中間表現 (High-Level Intermediate Representation; HIR) に移行しました。これにより、より精密な解析や、React Compiler 内での型推論さえも可能になりました。それ以降、ひとつ前の書き直しからの教訓に基づきながら、コンパイラの多くの重要な部分が何度も書き直されてきました。また、その過程で [React チーム](/community/team)の多くのメンバから、多大なサポートと貢献を受けました。\n\nこの安定版リリースは、今後の多くのリリースの始まりとなるものです。コンパイラが進化と改善を続け、今後 10 年以上にわたる React の新たな基盤と時代を築くことになることを期待しています。\n\nここで[クイックスタート](/learn/react-compiler)に直接ジャンプしても構いませんし、続けて以下の React Conf 2025 のハイライトを読んでいただいても構いません。\n\n<DeepDive>\n\n#### React Compiler の動作の仕組み {/*how-does-react-compiler-work*/}\n\nReact Compiler は、自動メモ化を通じてコンポーネントとフックを最適化する最適化コンパイラです。現在は Babel プラグインとして実装されていますが、コンパイラは Babel から大部分が切り離されています。Babel が提供する抽象構文木 (AST) を独自の新しい HIR に変換し、複数のコンパイラパスを経由して、あなたの React コードのデータフローと可変性を注意深く理解します。これにより、コンパイラはレンダーに使用される値を細かくメモ化でき、手動でのメモ化では不可能な条件付きのメモ化も可能になります。\n\n```js {8}\nimport { use } from 'react';\n\nexport default function ThemeProvider(props) {\n  if (!props.children) {\n    return null;\n  }\n  // The compiler can still memoize code after a conditional return\n  const theme = mergeTheme(props.theme, use(ThemeContext));\n  return (\n    <ThemeContext value={theme}>\n      {props.children}\n    </ThemeContext>\n  );\n}\n```\n_この例を [React Compiler Playground](https://playground.react.dev/#N4Igzg9grgTgxgUxALhASwLYAcIwC4AEwBUYCBAvgQGYwQYEDkMCAhnHowNwA6AdvwQAPHPgIATBNVZQANoWpQ+HNBD4EAKgAsEGBAAU6ANzSSYACix0sYAJRF+BAmmoFzAQisQbAOjha0WXEWPntgRycCFjxYdT45WV51Sgi4NTBCPB09AgBeAj0YAHMEbV0ES2swHyzygBoSMnMyvQBhNTxhPFtbJKdo2LcIpwAeFoR2vk6hQiNWWSgEXOBavQoAPmHI4C9ff0DghD4KLZGAenHJ6bxN5N7+ChA6kDS+ajQilHRsXEyATyw5GI+gWRTQfAA8lg8Ko+GBKDQ6AxGAAjVgohCyAC0WFB4KxLHYeCxaWwgQQMDO4jQGW4-H45nCyTOZ1JWECrBhagAshBJMgCDwQPNZEKHgQwJyae8EPCQVAwZDobC7FwnuAtBAAO4ASSmFL48zAKGksjIFCAA) で確認する_\n\n自動メモ化に加え、React Compiler にはあなたの React コードに対して実行される検証パスも含まれています。これは [React のルール](/reference/rules)をエンコードしたものであり、コンパイラのデータフローおよびミュータビリティに関する理解を用いて、React のルールに違反している箇所に関する診断情報を提供します。この情報は主に `eslint-plugin-react-hooks` を通じて提供され、しばしば React コード内に隠れている潜在的なバグを明らかにしてくれます。\n\nコンパイラがどのようにコードを最適化するかについてさらに詳しく知るには、[Playground](https://playground.react.dev) を参照してください。\n\n</DeepDive>\n\n## 今すぐ React Compiler を使用する {/*use-react-compiler-today*/}\nコンパイラをインストールするには以下のようにします。\n\nnpm\n<TerminalBlock>\nnpm install --save-dev --save-exact babel-plugin-react-compiler@latest\n</TerminalBlock>\n\npnpm\n<TerminalBlock>\npnpm add --save-dev --save-exact babel-plugin-react-compiler@latest\n</TerminalBlock>\n\nyarn\n<TerminalBlock>\nyarn add --dev --exact babel-plugin-react-compiler@latest\n</TerminalBlock>\n\nこの安定版リリースの一環として、React Compiler をプロジェクトに追加しやすくするための作業や、コンパイラがメモ化を行う方法に関しての最適化を行ってきました。React Compiler はオプショナルチェーンをサポートし、配列の添字を依存値として利用できるようになりました。これらの改善により、最終的な再レンダーが減少してよりレスポンシブな UI が実現される一方で、開発者が自然で宣言的なコードを書き続けることができます。\n\nコンパイラの使い方に関する詳細は[ドキュメント](/learn/react-compiler)を参照してください。\n\n## プロダクションで得られている成果 {/*react-compiler-at-meta*/}\n[コンパイラはすでに Meta Quest Store などのアプリで利用されています](https://youtu.be/lyEKhv8-3n0?t=3002)。初回ロードとページ間ナビゲーションが最大 12% 改善され、一部のユーザ操作は 2.5 倍以上高速になりました。これらの改善があってもメモリ使用量は不変です。環境によって効果は異なるかもしれませんが、あなたのアプリでもコンパイラを試し、同様のパフォーマンス向上があるか確認することをお勧めします。\n\n## 後方互換性 {/*backwards-compatibility*/}\nBeta 版の発表で述べたとおり、React Compiler は React 17 以降と互換性があります。まだ React 19 を使用していない場合でも、コンパイラ設定で最小ターゲットを指定し、`react-compiler-runtime` を依存ライブラリとして追加することで React Compiler を使用できます。これに関するドキュメントは[こちら](/reference/react-compiler/target#targeting-react-17-or-18)を参照してください。\n\n## コンパイラ駆動のリンタで React のルールを強制する {/*migrating-from-eslint-plugin-react-compiler-to-eslint-plugin-react-hooks*/}\nReact Compiler には、[React のルール](/reference/rules)に違反したコードを特定するための ESLint ルールが含まれています。リンタはコンパイラの導入なしに動作するため、eslint-plugin-react-hooks をアップグレードするリスクはありません。今すぐすべての人がアップグレードすることをお勧めします。\n\nすでに `eslint-plugin-react-compiler` をインストールしている場合は、それを削除して `eslint-plugin-react-hooks@latest` を使用できるようになりました。この改善に貢献していただいた [@michaelfaith](https://bsky.app/profile/michael.faith) に感謝します！\n\nインストールするには以下のようにします。\n\nnpm\n<TerminalBlock>\nnpm install --save-dev eslint-plugin-react-hooks@latest\n</TerminalBlock>\n\npnpm\n<TerminalBlock>\npnpm add --save-dev eslint-plugin-react-hooks@latest\n</TerminalBlock>\n\nyarn\n<TerminalBlock>\nyarn add --dev eslint-plugin-react-hooks@latest\n</TerminalBlock>\n\n```js {6}\n// eslint.config.js (Flat Config)\nimport reactHooks from 'eslint-plugin-react-hooks';\nimport { defineConfig } from 'eslint/config';\n\nexport default defineConfig([\n  reactHooks.configs.flat.recommended,\n]);\n```\n\n```js {3}\n// eslintrc.json (Legacy Config)\n{\n  \"extends\": [\"plugin:react-hooks/recommended\"],\n  // ...\n}\n```\n\nReact Compiler ルールを有効にするには、`recommended` プリセットを使用することをお勧めします。詳しい手順については [README](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/README.md) も確認してください。以下は、React Conf で紹介したいくつかの例です。\n\n- [`set-state-in-render`](/reference/eslint-plugin-react-hooks/lints/set-state-in-render) でレンダーループを引き起こす `setState` パターンをキャッチする。\n- [`set-state-in-effect`](/reference/eslint-plugin-react-hooks/lints/set-state-in-effect) でエフェクト内の高コストな処理にフラグを立てる。\n- [`refs`](/reference/eslint-plugin-react-hooks/lints/refs) でレンダー中の安全でない ref アクセスを防止する。\n\n## useMemo、useCallback、React.memo をどうすべきか？ {/*what-should-i-do-about-usememo-usecallback-and-reactmemo*/}\nデフォルトでは、React Compiler はコードの分析結果とヒューリスティックに基づいてコードをメモ化します。ほとんどの場合このメモ化は、あなたが書くであろうものと同等か、それ以上に正確です。そして上述のように、コンパイラは早期リターン後などの、`useMemo`/`useCallback` が使用できない場面でもメモ化を行えます。\n\nただし、場合によっては開発者がメモ化をより細かく制御する必要があるかもしれません。`useMemo` と `useCallback` フックは、React Compiler と併用して、どの値をメモ化するかを制御するための避難ハッチ (escape hatch) として使用し続けることができます。一般的なユースケースは、メモ化された値がエフェクトの依存値として使用される場合で、依存値が実質的に変化しないならエフェクトが繰り返し発火しないようにする、というものです。\n\n新しいコードにおいては、メモ化をコンパイラに任せ、詳細な制御が必要な場合に `useMemo`/`useCallback` を使用することをお勧めします。\n\n既存のコードの場合は、既存のメモ化をそのまま残すか（削除するとコンパイル出力が変わる可能性があります）、メモ化を削除する前に慎重にテストすることをお勧めします。\n\n## 新しいアプリでは React Compiler の利用を推奨 {/*new-apps-should-use-react-compiler*/}\nExpo、Vite、Next.js のチームと提携し、新規アプリ作成時のフローにコンパイラを追加しました。\n\n[Expo SDK 54](https://docs.expo.dev/guides/react-compiler/) 以降では、デフォルトでコンパイラが有効になっているため、新しいアプリが最初から自動的にコンパイラを活用できるようになります。\n\n<TerminalBlock>\nnpx create-expo-app@latest\n</TerminalBlock>\n\n[Vite](https://vite.dev/guide/) および [Next.js](https://nextjs.org/docs/app/api-reference/cli/create-next-app) のユーザは、`create-vite` および `create-next-app` でコンパイラが有効化されたテンプレートを選択できます。\n\n<TerminalBlock>\nnpm create vite@latest\n</TerminalBlock>\n\n<br />\n\n<TerminalBlock>\nnpx create-next-app@latest\n</TerminalBlock>\n\n## React Compiler を段階的に採用する {/*adopt-react-compiler-incrementally*/}\n既存のアプリケーションをメンテナンスしている場合は、自分のペースでコンパイラを展開できます。新しく公開したステップバイステップの[段階的採用ガイド](/learn/react-compiler/incremental-adoption)では、自信を持ってコンパイラを有効化できるようにするためのゲーティング戦略、互換性チェック、ロールアウトツールについて説明しています。\n\n## swc サポート（実験的） {/*swc-support-experimental*/}\nReact Compiler は、Babel、Vite、Rsbuild など[様々なビルドツール](/learn/react-compiler#installation)にインストールできます。\n\nこれらのツールに加えて、[swc](https://swc.rs/) チームの Kang Dongyoon ([@kdy1dev](https://x.com/kdy1dev)) と協力して、React Compiler を swc プラグインとして追加サポートする作業を進めてきました。この作業はまだ完了していませんが、[Next.js アプリで React Compiler を有効にした場合](https://nextjs.org/docs/app/api-reference/config/next-config-js/reactCompiler)、Next.js のビルドパフォーマンスが大幅に向上するはずです。\n\n最高のビルドパフォーマンスを得るために、Next.js [15.3.1](https://github.com/vercel/next.js/releases/tag/v15.3.1) 以降を使用することをお勧めします。\n\nVite ユーザは、引き続き [vite-plugin-react](https://github.com/vitejs/vite-plugin-react) を使用し、[Babel プラグイン](/learn/react-compiler/installation#vite)として追加することでコンパイラを有効化できます。また、[oxc](https://oxc.rs/) チームと協力して[コンパイラのサポートを追加](https://github.com/oxc-project/oxc/issues/10048)する作業も進めています。[rolldown](https://github.com/rolldown/rolldown) が正式にリリースされて Vite でサポートされ次第、また oxc に React Compiler のサポートが追加され次第、ドキュメントを更新して移行方法に関する情報を公開する予定です。\n\n## React Compiler のアップグレード {/*upgrading-react-compiler*/}\nReact Compiler は、適用される自動メモ化が厳密にパフォーマンスのためのものである場合に最も効果を発揮します。将来のバージョンのコンパイラでは、メモ化の適用方法が変更される可能性があります。例えば、より細かく正確になる可能性があります。\n\nただし、プロダクトコードは JavaScript では必ずしも静的に検出できない形で [React のルール](/reference/rules)に違反することがあるため、メモ化を変更することで予期しない結果が生じることがあります。例えば、メモ化済みの値がコンポーネントツリーのどこかで `useEffect` の依存値として使用されているかもしれません。このような値のメモ化の有無や挙動が変化することで、対応する `useEffect` の実行が過剰あるいは過少になる可能性があるのです。[useEffect は同期のためだけに使用する](/learn/synchronizing-with-effects)ことを推奨していますが、あなたのコードベースには、特定の値の変更にのみ反応する必要があるエフェクトなど、他のユースケースのための useEffect が含まれているかもしれません。\n\n言い換えれば、メモ化の挙動が変われば、まれな状況においては予期しない動作を引き起こす可能性があるということです。このため、React のルールに従い、アプリの end-to-end テストを継続的に行うことをお勧めします。これにより、コンパイラを自信を持ってアップグレードし、問題を引き起こす可能性のある React のルール違反を特定できます。\n\n十分なテストカバレッジがない場合は、コンパイラを特定のバージョン（例：`1.0.0`）に固定し、SemVer の範囲指定（例：`^1.0.0`）を用いないことをお勧めします。これを行うには、コンパイラをアップグレードする際に `--save-exact` (npm/pnpm) または `--exact` フラグ (yarn) を渡します。その後のコンパイラのアップグレードは、アプリが期待通りに動作することを確認しながら、手動で行うようにしてください。\n\n---\n\nこのポストのレビューと編集をしてくれた [Jason Bonta](https://x.com/someextent)、[Jimmy Lai](https://x.com/feedthejim)、[Kang Dongyoon](https://x.com/kdy1dev) (@kdy1dev)、[Dan Abramov](https://bsky.app/profile/danabra.mov) に感謝します。\n"
  },
  {
    "path": "src/content/blog/2025/10/16/react-conf-2025-recap.md",
    "content": "---\ntitle: \"React Conf 2025 振り返り\"\nauthor: Matt Carroll and Ricky Hanlon\ndate: 2025/10/16\ndescription: 先週 React Conf 2025 が開催されました。この投稿では、イベントでの講演と発表内容をまとめます。\n---\n\nOct 16, 2025 by [Matt Carroll](https://x.com/mattcarrollcode) and [Ricky Hanlon](https://bsky.app/profile/ricky.fm)\n\n---\n\n<Intro>\n\n先週 React Conf 2025 が開催されました。[React Foundation](/blog/2025/10/07/introducing-the-react-foundation) の発表や、React および React Native に追加される新機能の披露が行われました。\n\n</Intro>\n\n---\n\nReact Conf 2025 は、2025 年 10 月 7〜8 日にネバダ州ヘンダーソンで開催されました。\n\n[1 日目](https://www.youtube.com/watch?v=zyVRg2QR6LA&t=1067s)と [2 日目](https://www.youtube.com/watch?v=p9OcztRyDl0&t=2299s)の配信全体がオンラインで視聴可能です。イベントの写真は[こちら](https://conf.react.dev/photos)で見ることができます。\n\nこのポストでは、イベントでの講演と発表をまとめます。\n\n\n## 1 日目キーノート {/*day-1-keynote*/}\n\n_1 日目の配信全体は[こちら](https://www.youtube.com/watch?v=zyVRg2QR6LA&t=1067s)から視聴できます。_\n\n1 日目のキーノートでは、Joe Savona が前回の React Conf 以降のチームとコミュニティからのお知らせ、および React 19.0 と 19.1 のハイライトを共有しました。\n\nMofei Zhang が、React 19.2 の新機能ハイライトを紹介しました。\n* [`<Activity />`](https://react.dev/reference/react/Activity) — 可視性を管理する新しいコンポーネント。\n* [`useEffectEvent`](https://react.dev/reference/react/useEffectEvent) — エフェクトからイベントを発火させる手法。\n* [Performance Tracks](https://react.dev/reference/dev-tools/react-performance-tracks) — DevTools の新しいプロファイリングツール。\n* [Partial Pre-Rendering](https://react.dev/blog/2025/10/01/react-19-2#partial-pre-rendering) — アプリの一部を事前にプリレンダーし、後でレンダーを再開。\n\nJack Pope が、以下のような Canary の新機能を発表しました。\n\n* [`<ViewTransition />`](https://react.dev/reference/react/ViewTransition) — ページ遷移をアニメートする新しいコンポーネント。\n* [Fragment Refs](https://react.dev/reference/react/Fragment#fragmentinstance) — フラグメントを使ってラップされた DOM ノードとやり取りする新しい手法。\n\nLauren Tan は [React Compiler v1.0](https://react.dev/blog/2025/10/07/react-compiler-1) を発表し、すべてのアプリが React Compiler を使用して以下のようなメリットを享受することを推奨しました。\n* React コードを理解する[自動メモ化](/learn/react-compiler/introduction#what-does-react-compiler-do)。\n* React Compiler によって駆動される、ベストプラクティスを教えてくれる[新しい lint ルール](/learn/react-compiler/installation#eslint-integration)。\n* Vite、Next.js、Expo における新規アプリ作成時の[デフォルトサポート](/learn/react-compiler/installation#basic-setup)。\n* React Compiler に移行する既存アプリ向けの[移行ガイド](/learn/react-compiler/incremental-adoption)。\n\n最後に、Seth Webster が React のオープンソース開発とコミュニティを管理する [React Foundation](/blog/2025/10/07/introducing-the-react-foundation) を発表しました。\n\n1 日目の視聴はこちら：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/zyVRg2QR6LA?si=z-8t_xCc12HwGJH_&t=1067s\" />\n\n## 2 日目キーノート {/*day-2-keynote*/}\n\n_2 日目の配信全体は[こちら](https://www.youtube.com/watch?v=p9OcztRyDl0&t=2299s)から視聴できます。_\n\n2 日目の発表は Jorge Cohen と Nicola Corti からスタートし、週間ダウンロード数 400 万（前年比プラス 100%）という React Native の驚異的な成長と、Shopify、Zalando、HelloFresh における注目すべきアプリ移行事例、RISE、RUNNA、Partyful などの受賞アプリ、そして Mistral、Replit、v0 の AI アプリを紹介しました。\n\nRiccardo Cipolleschi は React Native より 2 つの主要な新発表を行いました。\n- [React Native 0.82 は New Architecture のみをサポート](https://reactnative.dev/blog/2025/10/08/react-native-0.82#new-architecture-only)\n- [実験的な Hermes V1 サポート](https://reactnative.dev/blog/2025/10/08/react-native-0.82#experimental-hermes-v1)\n\nキーノートの締めくくりに、Ruben Norte と Alex Hunt が以下の発表を行いました。\n- ウェブ版 React との互換性を向上させる[新しいウェブ対応 DOM API](https://reactnative.dev/blog/2025/10/08/react-native-0.82#dom-node-apis)。\n- 新しいネットワークパネルとデスクトップアプリを備えた[新しい Performance API](https://reactnative.dev/blog/2025/10/08/react-native-0.82#web-performance-apis-canary)。\n\n2 日目の視聴はこちら：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/p9OcztRyDl0?si=qPTHftsUE07cjZpS&t=2299s\" />\n\n\n## React チームの講演 {/*react-team-talks*/}\n\nカンファレンス全体を通じて、React チームからは以下の講演がありました。\n* [Async React Part I](https://www.youtube.com/watch?v=zyVRg2QR6LA&t=10907s) と [Part II](https://www.youtube.com/watch?v=p9OcztRyDl0&t=29073s) [(Ricky Hanlon)](https://x.com/rickhanlonii) で、過去 10 年の革新が何を可能にしてきたかをお伝えしました。\n* [Exploring React Performance](https://www.youtube.com/watch?v=zyVRg2QR6LA&t=20274s) [(Joe Savona)](https://x.com/en_js) で React のパフォーマンスに関する研究成果をお知らせしました。\n* [Reimagining Lists in React Native](https://www.youtube.com/watch?v=p9OcztRyDl0&t=10382s) [(Luna Wei)](https://x.com/lunaleaps) は、モードベースのレンダー (hidden/pre-render/visible) で可視性を管理する、リストのための新しいプリミティブである、Virtual View を紹介しました。\n* [Profiling with React Performance tracks](https://www.youtube.com/watch?v=zyVRg2QR6LA&t=8276s) [(Ruslan Lesiutin)](https://x.com/ruslanlesiutin) は、新しい React Performance Tracks を使用してパフォーマンスの問題をデバッグし、優れたアプリを構築する方法をお伝えしました。\n* [React Strict DOM](https://www.youtube.com/watch?v=p9OcztRyDl0&t=9026s) [(Nicolas Gallagher)](https://nicolasgallagher.com/) は、ネイティブ環境でウェブコードを使用する際の Meta のアプローチについてお話ししました。\n* [View Transitions and Activity](https://www.youtube.com/watch?v=zyVRg2QR6LA&t=4870s) [(Chance Strickland)](https://x.com/chancethedev) — Chance は React チームと協力し、`<Activity />` と `<ViewTransition />` を使い、高速でネイティブアプリのように動作するアニメーションを構築する方法を披露しました。\n* [In case you missed the memo](https://www.youtube.com/watch?v=zyVRg2QR6LA&t=9525s) [(Cody Olsen)](https://bsky.app/profile/codey.bsky.social) — Cody は React チームと協力して Sanity Studio でコンパイラを採用し、その経過を報告しました。\n## React フレームワーク関連の講演 {/*react-framework-talks*/}\n\n2 日目の後半には、React フレームワーク関連のチームより、さまざまな講演がありました。\n\n* [React Native, Amplified](https://www.youtube.com/watch?v=p9OcztRyDl0&t=5737s) by [Giovanni Laquidara](https://x.com/giolaq) and [Eric Fahsl](https://x.com/efahsl).\n* [React Everywhere: Bringing React Into Native Apps](https://www.youtube.com/watch?v=p9OcztRyDl0&t=18213s) by [Mike Grabowski](https://x.com/grabbou).\n* [How Parcel Bundles React Server Components](https://www.youtube.com/watch?v=p9OcztRyDl0&t=19538s) by [Devon Govett](https://x.com/devonovett).\n* [Designing Page Transitions](https://www.youtube.com/watch?v=p9OcztRyDl0&t=20640s) by [Delba de Oliveira](https://x.com/delba_oliveira).\n* [Build Fast, Deploy Faster — Expo in 2025](https://www.youtube.com/watch?v=p9OcztRyDl0&t=21350s) by [Evan Bacon](https://x.com/baconbrix).\n* [The React Router's take on RSC](https://www.youtube.com/watch?v=p9OcztRyDl0&t=22367s) by [Kent C. Dodds](https://x.com/kentcdodds).\n* [RedwoodSDK: Web Standards Meet Full-Stack React](https://www.youtube.com/watch?v=p9OcztRyDl0&t=24992s) by [Peter Pistorius](https://x.com/appfactory) and [Aurora Scharff](https://x.com/aurorascharff).\n* [TanStack Start](https://www.youtube.com/watch?v=p9OcztRyDl0&t=26065s) by [Tanner Linsley](https://x.com/tannerlinsley).\n\n## Q&A {/*q-and-a*/}\nカンファレンス中に、3 度の Q&A パネルが行われました。\n\n* [React Team at Meta Q&A](https://www.youtube.com/watch?v=zyVRg2QR6LA&t=26304s)（司会：[Shruti Kapoor](https://x.com/shrutikapoor08)）\n* [React Frameworks Q&A](https://www.youtube.com/watch?v=p9OcztRyDl0&t=26812s)（司会：[Jack Herrington](https://x.com/jherr)）\n* [React and AI Panel](https://www.youtube.com/watch?v=zyVRg2QR6LA&t=18741s)（司会：[Lee Robinson](https://x.com/leerob)）\n\n## さらに... {/*and-more*/}\n\nコミュニティからも以下のトークがありました。\n* [Building an MCP Server](https://www.youtube.com/watch?v=zyVRg2QR6LA&t=24204s) by [James Swinton](https://x.com/JamesSwintonDev) ([AG Grid](https://www.ag-grid.com/?utm_source=react-conf&utm_medium=react-conf-homepage&utm_campaign=react-conf-sponsorship-2025))\n* [Modern Emails using React](https://www.youtube.com/watch?v=zyVRg2QR6LA&t=25521s) by [Zeno Rocha](https://x.com/zenorocha) ([Resend](https://resend.com/))\n* [Why React Native Apps Make All the Money](https://www.youtube.com/watch?v=zyVRg2QR6LA&t=24917s) by [Perttu Lähteenlahti](https://x.com/plahteenlahti) ([RevenueCat](https://www.revenuecat.com/))\n* [The invisible craft of great UX](https://www.youtube.com/watch?v=zyVRg2QR6LA&t=23400s) by [Michał Dudak](https://x.com/michaldudak) ([MUI](https://mui.com/))\n\n## 謝辞 {/*thanks*/}\n\nReact Conf 2025 を実現していただいたすべてのスタッフ、演者、参加者の皆さまに感謝します。リストに挙げきれないほど多くの方々がいますが、特に感謝したい方々を挙げたいと思います。\n\nイベント全体を計画し、カンファレンスのウェブサイトを構築していただいた [Matt Carroll](https://x.com/mattcarrollcode) に感謝します。\n\n驚異的な献身とエネルギーで React Conf の司会を務め、イベントを通じて思慮深い紹介、楽しいジョーク、そして心からの熱意を提供していただいた [Michael Chan](https://x.com/chantastic) に感謝します。ライブ配信をホストし、各スピーカにインタビューを行い、対面のような React Conf 体験をオンラインにもたらしてくださった [Jorge Cohen](https://x.com/JorgeWritesCode) に感謝します。\n\nReact Conf を共同開催し、デザイン、エンジニアリング、マーケティングのサポートを提供していただいた [Mateusz Kornacki](https://x.com/mat_kornacki)、[Mike Grabowski](https://x.com/grabbou)、[Kris Lis](https://www.linkedin.com/in/krzysztoflisakakris/) および [Callstack](https://www.callstack.com/) のチームに感謝します。イベントの運営を手伝ってくださった [ZeroSlope チーム](https://zeroslopeevents.com/contact-us/)：Sunny Leggett、Tracey Harrison、Tara Larish、Whitney Pogue、Brianne Smythia に感謝します。\n\nDiscord からの質問をライブ配信で紹介していただいた [Jorge Cabiedes Acosta](https://github.com/jorge-cab)、[Gijs Weterings](https://x.com/gweterings)、[Tim Yung](https://x.com/yungsters)、[Jason Bonta](https://x.com/someextent) に感謝します。Discord のモデレーションをリードしていただいた [Lynn Yu](https://github.com/lynnshaoyu) に感謝します。毎日私たちを迎え入れていただいた [Seth Webster](https://x.com/sethwebster) に感謝します。そして、アフターパーティーに参加して特別なメッセージをくださった [Christopher Chedeau](https://x.com/vjeux)、[Kevin Gozali](https://x.com/fkgozali)、[Pieter De Baets](https://x.com/Javache) に感謝します。\n\nカンファレンスのモバイルアプリを構築してくださった [Kadi Kraman](https://x.com/kadikraman)、[Beto](https://x.com/betomoedano)、[Nicolas Solerieu](https://www.linkedin.com/in/nicolas-solerieu/) に感謝します。カンファレンスのウェブサイトの手伝いをしていただいた [Wojtek Szafraniec](https://x.com/wojteg1337) に感謝します。ビジュアル、ステージ、音響を提供していただいた [Mustache](https://www.mustachepower.com/) と [Cornerstone](https://cornerstoneav.com/) に感謝します。そして私たちをホストしていただいた Westin Hotel に感謝します。\n\nイベントを可能にしていただいたすべてのスポンサー：[Amazon](https://www.developer.amazon.com)、[MUI](https://mui.com/)、[Vercel](https://vercel.com/)、[Expo](https://expo.dev/)、[RedwoodSDK](https://rwsdk.com)、[Ag Grid](https://www.ag-grid.com)、[RevenueCat](https://www.revenuecat.com/)、[Resend](https://resend.com)、[Mux](https://www.mux.com/)、[Old Mission](https://www.oldmissioncapital.com/)、[Arcjet](https://arcjet.com)、[Infinite Red](https://infinite.red/)、[RenderATL](https://renderatl.com) に感謝します。\n\nコミュニティに知識と経験を共有していただいた、すべてのスピーカに感謝します。\n\n最後に、対面およびオンラインで参加していただいたすべての方に感謝します。React が今の React でいられるのは皆さんのおかげです。React はライブラリ以上のものであり、コミュニティです。皆さんが一堂に会して共有し学び合う姿は感動的でした。\n\nまた次回お会いしましょう！\n"
  },
  {
    "path": "src/content/blog/2025/12/03/critical-security-vulnerability-in-react-server-components.md",
    "content": "---\ntitle: \"React Server Components における重大なセキュリティ脆弱性\"\nauthor: The React Team\ndate: 2025/12/03\ndescription: React Server Components に、認証不要のリモートコード実行の脆弱性が存在します。バージョン 19.0.1、19.1.2、19.2.1 で修正が公開されました。直ちにアップグレードすることを推奨します。\n\n---\n\nDecember 3, 2025 by [The React Team](/community/team)\n\n---\n\n<Intro>\n\nReact Server Components に、認証不要のリモートコード実行の脆弱性が存在します。\n\n直ちにアップグレードすることを推奨します。\n\n</Intro>\n\n---\n\n11 月 29 日、Lachlan Davidson 氏が React のセキュリティ脆弱性を報告しました。これは、React Server Function のエンドポイントに送信されたペイロードを React がデコードする際の欠陥を悪用することで、未認証状態でのリモートコード実行を可能にするものです。\n\nアプリが React のサーバ関数 (Server Function) のエンドポイントを実装していない場合でも、React Server Components をサポートしている場合は脆弱性の影響を受ける可能性があります。\n\nこの脆弱性は [CVE-2025-55182](https://www.cve.org/CVERecord?id=CVE-2025-55182) として公開されており、CVSS スコアは 10.0 です。\n\n以下のパッケージのバージョン 19.0、19.1.0、19.1.1、および 19.2.0 に脆弱性が存在します。\n\n* [react-server-dom-webpack](https://www.npmjs.com/package/react-server-dom-webpack)\n* [react-server-dom-parcel](https://www.npmjs.com/package/react-server-dom-parcel)\n* [react-server-dom-turbopack](https://www.npmjs.com/package/react-server-dom-turbopack?activeTab=readme)\n\n## 直ちに対応を {/*immediate-action-required*/}\n\nバージョン [19.0.1](https://github.com/facebook/react/releases/tag/v19.0.1)、[19.1.2](https://github.com/facebook/react/releases/tag/v19.1.2)、および [19.2.1](https://github.com/facebook/react/releases/tag/v19.2.1) で修正が導入されました。上記のパッケージを使用している場合は、直ちに修正済みバージョンのいずれかにアップグレードしてください。\n\nアプリの React コードがサーバを使用していない場合、この脆弱性の影響は受けません。アプリが React Server Components をサポートするフレームワーク、バンドラ、またはバンドラプラグインを使用していない場合、この脆弱性の影響は受けません。\n\n### 影響を受けるフレームワークとバンドラ {/*affected-frameworks-and-bundlers*/}\n\n一部の React フレームワークやバンドラが、脆弱性のある React パッケージに依存しているか、peer dependency として依存しているか、あるいはそれらを含んでいました。影響を受ける React フレームワークやバンドラは以下の通りです：[next](https://www.npmjs.com/package/next)、[react-router](https://www.npmjs.com/package/react-router)、[waku](https://www.npmjs.com/package/waku)、[@parcel/rsc](https://www.npmjs.com/package/@parcel/rsc)、[@vitejs/plugin-rsc](https://www.npmjs.com/package/@vitejs/plugin-rsc)、[rwsdk](https://www.npmjs.com/package/rwsdk)\n\nアップグレード方法について、[以下の更新手順](#update-instructions)を参照してください。\n\n### ホスティングプロバイダによる緩和策 {/*hosting-provider-mitigations*/}\n\n我々は多くのホスティングプロバイダと協力し、一時的な緩和策 (mitigation) を適用しています。\n\nただしアプリの保護のためにこれらに依存しないでください。直ちにアップデートを適用するべきです。\n\n### 脆弱性の概要 {/*vulnerability-overview*/}\n\n[React サーバ関数](https://react.dev/reference/rsc/server-functions) を使用すると、クライアントからサーバ上の関数を呼び出すことができます。React コードがクライアントとサーバの両方で実行できるよう、React はフレームワークやバンドラが使用する統合ポイントとツールを提供しています。React はクライアント上で起きたリクエストを HTTP リクエストに変換し、それがサーバに転送されます。サーバ上では、React はこの HTTP リクエストを関数呼び出しに変換して、必要に応じてデータをクライアントに返します。\n\n認証されていない攻撃者が任意のサーバ関数エンドポイントに対して悪意のある HTTP リクエストを作成することで、React がそれをデシリアライズする際に、サーバ上でリモートコード実行が可能になります。脆弱性のさらなる詳細については、修正のロールアウトが完了した後に提供される予定です。\n\n## 更新手順 {/*update-instructions*/}\n\n<Note>\n\n以下のガイドは新たに発見された以下の脆弱性にも対応するよう更新済みです。\n\n- **Denial of Service - High Severity**: [CVE-2025-55184](https://www.cve.org/CVERecord?id=CVE-2025-55184) and [CVE-2025-67779](https://www.cve.org/CVERecord?id=CVE-2025-67779) (CVSS 7.5)\n- **Source Code Exposure - Medium Severity**: [CVE-2025-55183](https://www.cve.org/CVERecord?id=CVE-2025-55183) (CVSS 5.3)\n- **Denial of Service - High Severity**: January 26, 2026 [CVE-2026-23864](https://www.cve.org/CVERecord?id=CVE-2026-23864) (CVSS 7.5)\n\n詳細については[フォローアップブログ記事](/blog/2025/12/11/denial-of-service-and-source-code-exposure-in-react-server-components)を参照してください。\n\n-----\n\n_Updated January 26, 2026._\n</Note>\n\n### Next.js {/*update-next-js*/}\n\n全ユーザは、各リリースラインの最新のパッチ適用済みバージョンにアップグレードすべきです。\n\n```bash\nnpm install next@14.2.35  // for 13.3.x, 13.4.x, 13.5.x, 14.x\nnpm install next@15.0.8   // for 15.0.x\nnpm install next@15.1.12  // for 15.1.x\nnpm install next@15.2.9   // for 15.2.x\nnpm install next@15.3.9   // for 15.3.x\nnpm install next@15.4.11  // for 15.4.x\nnpm install next@15.5.10  // for 15.5.x\nnpm install next@16.0.11  // for 16.0.x\nnpm install next@16.1.5   // for 16.1.x\n\nnpm install next@15.6.0-canary.60   // for 15.x canary releases\nnpm install next@16.1.0-canary.19   // for 16.x canary releases\n```\n\n15.0.8, 15.1.12, 15.2.9, 15.3.9, 15.4.10, 15.5.10, 15.6.0-canary.61, 16.0.11, 16.1.5\n\nNext.js 13 のバージョン `13.3` 以降 (`13.3.x`、`13.4.x`、`13.5.x`) を使用している場合は、バージョン `14.2.35` にアップグレードしてください。\n\n`next@14.3.0-canary.77` またはそれ以降の canary リリースを使用している場合は、最新の安定版 14.x リリースにダウングレードしてください。\n\n```bash\nnpm install next@14\n```\n\n最新の更新手順については [Next.js ブログ](https://nextjs.org/blog/security-update-2025-12-11)を、詳細については[前回の変更履歴](https://nextjs.org/blog/CVE-2025-66478)を参照してください。\n\n### React Router {/*update-react-router*/}\n\nReact Router の安定前の RSC API を使用している場合、package.json に以下の依存ライブラリがあればアップグレードが必要です。\n\n```bash\nnpm install react@latest\nnpm install react-dom@latest\nnpm install react-server-dom-parcel@latest\nnpm install react-server-dom-webpack@latest\nnpm install @vitejs/plugin-rsc@latest\n```\n\n### Expo {/*expo*/}\n\n緩和策について詳しくは、[expo.dev/changelog](https://expo.dev/changelog/mitigating-critical-security-vulnerability-in-react-server-components) の記事を参照してください。\n\n### Redwood SDK {/*update-redwood-sdk*/}\n\nrwsdk>=1.0.0-alpha.0 であることを確認してください。\n\n最新のベータ版の場合は以下のようにします。\n\n```bash\nnpm install rwsdk@latest\n```\n\n最新の `react-server-dom-webpack` にアップグレードしてください。\n\n```bash\nnpm install react@latest react-dom@latest react-server-dom-webpack@latest\n```\n\nその他の移行手順については [Redwood ドキュメント](https://docs.rwsdk.com/migrating/)を参照してください。\n\n### Waku {/*update-waku*/}\n\n最新の `react-server-dom-webpack` にアップグレードしてください。\n\n```bash\nnpm install react@latest react-dom@latest react-server-dom-webpack@latest waku@latest\n```\n\nその他の移行手順については [Waku のアナウンス](https://github.com/wakujs/waku/discussions/1823) を参照してください。\n\n### `@vitejs/plugin-rsc` {/*vitejs-plugin-rsc*/}\n\n最新の RSC プラグインにアップグレードしてください。\n\n```bash\nnpm install react@latest react-dom@latest @vitejs/plugin-rsc@latest\n```\n\n### `react-server-dom-parcel` {/*update-react-server-dom-parcel*/}\n\n最新バージョンにアップデートしてください。\n\n ```bash\n npm install react@latest react-dom@latest react-server-dom-parcel@latest\n ```\n\n### `react-server-dom-turbopack` {/*update-react-server-dom-turbopack*/}\n\n最新バージョンにアップデートしてください。\n\n ```bash\n npm install react@latest react-dom@latest react-server-dom-turbopack@latest\n ```\n\n### `react-server-dom-webpack` {/*update-react-server-dom-webpack*/}\n\n最新バージョンにアップデートしてください。\n\n ```bash\nnpm install react@latest react-dom@latest react-server-dom-webpack@latest\n ```\n\n\n### React Native {/*react-native*/}\n\nモノレポや `react-dom` を使用していない React Native ユーザの場合、`react` バージョンは `package.json` で固定されているはずですので、追加の手順は必要ありません。\n\nモノレポで React Native を使用している場合は、以下のパッケージがインストールされている場合に*それらのみ*を更新してください。\n\n- `react-server-dom-webpack`\n- `react-server-dom-parcel`\n- `react-server-dom-turbopack`\n\nこれはセキュリティ上の問題を緩和するために必要ですが、`react` および `react-dom` を更新する必要はなく、そのため React Native でのバージョン不一致エラーが発生することはありません。\n\n詳細については[この issue](https://github.com/facebook/react-native/issues/54772#issuecomment-3617929832) を参照してください。\n\n\n## タイムライン {/*timeline*/}\n\n* **11 月 29 日**: Lachlan Davidson 氏が [Meta Bug Bounty](https://bugbounty.meta.com/) を通じて脆弱性を報告。\n* **11 月 30 日**: Meta のセキュリティ研究者が確認し、React チームと協力して修正作業を開始。\n* **12 月 1 日**: 修正が作成され、React チームは影響を受けるホスティングプロバイダやオープンソースプロジェクトと協力して修正を検証、緩和策を導入、修正のロールアウトを開始。\n* **12 月 3 日**: 修正が npm に公開され、CVE-2025-55182 として一般公開。\n\n## 謝辞 {/*attribution*/}\n\nこの脆弱性を発見・報告し、修正に協力してくださった [Lachlan Davidson](https://github.com/lachlan2k) 氏に感謝します。\n"
  },
  {
    "path": "src/content/blog/2025/12/11/denial-of-service-and-source-code-exposure-in-react-server-components.md",
    "content": "---\ntitle: \"React Server Components におけるサービス拒否攻撃とソースコード露出\"\nauthor: The React Team\ndate: 2025/12/11\ndescription: セキュリティ研究者が先週の重大な脆弱性に対するパッチを検証する過程で、React Server Components における 2 つの脆弱性を追加で発見し、開示しました。高深刻度のサービス拒否攻撃 (CVE-2025-55184) と、中程度の深刻度のソースコード露出 (CVE-2025-55183) です。\n\n\n---\n\nDecember 11, 2025 by [The React Team](/community/team)\n\n_Updated January 26, 2026._\n\n---\n\n<Intro>\n\nセキュリティ研究者が先週の重大な脆弱性に対するパッチをテストする過程で、React Server Components における 2 つの脆弱性を追加で発見し、開示しました。\n\n**これらの新しい脆弱性はリモートコード実行を許すものではありません**。React2Shell に対するパッチはリモートコード実行の悪用を防止するために引き続き有効です。\n\n</Intro>\n\n---\n\n新しい脆弱性は以下のように公開されています。\n\n- **Denial of Service - High Severity**: [CVE-2025-55184](https://www.cve.org/CVERecord?id=CVE-2025-55184), [CVE-2025-67779](https://www.cve.org/CVERecord?id=CVE-2025-67779), and [CVE-2026-23864](https://www.cve.org/CVERecord?id=CVE-2026-23864) (CVSS 7.5)\n- **Source Code Exposure - Medium Severity**: [CVE-2025-55183](https://www.cve.org/CVERecord?id=CVE-2025-55183) (CVSS 5.3)\n\n新たに開示された脆弱性の深刻さを鑑み、直ちにアップグレードすることを推奨します。\n\n<Note>\n\n#### 以前に公開されたパッチには脆弱性があります {/*the-patches-published-earlier-are-vulnerable*/}\n\n以前の脆弱性対応のために既にアップデートを行っている場合でも、再度アップデートが必要です。\n\n19.0.3、19.1.4、および 19.2.3 にアップデート済みの場合でも、[これらは不完全](#additional-fix-published)であり、再度アップデートする必要があります。\n\nアップグレード手順については、[前回記事のガイド](/blog/2025/12/03/critical-security-vulnerability-in-react-server-components#update-instructions)を参照してください。\n\n-----\n\n_Updated January 26, 2026._\n\n</Note>\n\nこれらの脆弱性の詳細については、修正のロールアウトが完了した後に提供される予定です。\n\n## 直ちに対応を {/*immediate-action-required*/}\n\nこれらの脆弱性は、[CVE-2025-55182](/blog/2025/12/03/critical-security-vulnerability-in-react-server-components) と同じパッケージおよびバージョンに存在します。\n\n以下のパッケージの 19.0.0、19.0.1、19.0.2、19.0.3、19.1.0、19.1.1、19.1.2、19.1.3、19.2.0、19.2.1、19.2.2、および 19.2.3 が該当します。\n\n* [react-server-dom-webpack](https://www.npmjs.com/package/react-server-dom-webpack)\n* [react-server-dom-parcel](https://www.npmjs.com/package/react-server-dom-parcel)\n* [react-server-dom-turbopack](https://www.npmjs.com/package/react-server-dom-turbopack?activeTab=readme)\n\n修正はバージョン 19.0.4、19.1.5、および 19.2.4 にバックポートされています。上記のパッケージを使用している場合は、直ちに修正済みバージョンのいずれかにアップグレードしてください。\n\n以前と同様、アプリの React コードがサーバを使用していない場合、アプリはこれらの脆弱性の影響を受けません。アプリが React Server Components をサポートするフレームワーク、バンドラ、またはバンドラプラグインを使用していない場合、アプリはこれらの脆弱性の影響を受けません。\n\n<Note>\n\n#### 重大な CVE の後に別の脆弱性報告が続くことはよくあります {/*its-common-for-critical-cves-to-uncover-followup-vulnerabilities*/}\n\n重大な脆弱性が開示されると、研究者は隣接するコードパスを精査し、初期の修正をバイパスする方法がないかテストし、類似の悪用手段を見つけようとします。\n\nこれは JavaScript だけでなく、業界全体で見られるパターンです。たとえば [Log4Shell](https://nvd.nist.gov/vuln/detail/cve-2021-44228) の後にも、コミュニティがオリジナルの修正を検証する中で追加の CVE ([1](https://nvd.nist.gov/vuln/detail/cve-2021-45046), [2](https://nvd.nist.gov/vuln/detail/cve-2021-45105)) が報告されました。\n\n開示が続くとフラストレーションを感じるかもしれませんが、一般的には健全な対応サイクルの兆候です。\n\n</Note>\n\n### 影響を受けるフレームワークとバンドラ {/*affected-frameworks-and-bundlers*/}\n\n一部の React フレームワークやバンドラが、脆弱性のある React パッケージに依存しているか、peer dependency として依存しているか、あるいはそれらを含んでいました。影響を受ける React フレームワークやバンドラは以下の通りです：[next](https://www.npmjs.com/package/next)、[react-router](https://www.npmjs.com/package/react-router)、[waku](https://www.npmjs.com/package/waku)、[@parcel/rsc](https://www.npmjs.com/package/@parcel/rsc)、[@vite/rsc-plugin](https://www.npmjs.com/package/@vitejs/plugin-rsc)、[rwsdk](https://www.npmjs.com/package/rwsdk)\n\nアップグレード方法について、[前回の記事の手順](/blog/2025/12/03/critical-security-vulnerability-in-react-server-components#update-instructions)を参照してください。\n\n### ホスティングプロバイダによる緩和策 {/*hosting-provider-mitigations*/}\n\n以前と同様、我々は多くのホスティングプロバイダと協力し、一時的な緩和策 (mitigation) を適用しています。\n\nただしアプリの保護のためにこれらに依存しないでください。引き続き直ちにアップデートを適用するべきです。\n\n### React Native {/*react-native*/}\n\nモノレポや `react-dom` を使用していない React Native ユーザの場合、`react` バージョンは `package.json` で固定されているはずですので、追加の手順は必要ありません。\n\nモノレポで React Native を使用している場合は、以下のパッケージがインストールされている場合に*それらのみ*を更新してください。\n\n- `react-server-dom-webpack`\n- `react-server-dom-parcel`\n- `react-server-dom-turbopack`\n\nこれはセキュリティ上の問題を緩和するために必要ですが、`react` および `react-dom` を更新する必要はなく、そのため React Native でのバージョン不一致エラーが発生することはありません。\n\n詳細については[この issue](https://github.com/facebook/react-native/issues/54772#issuecomment-3617929832) を参照してください。\n\n---\n\n## 高深刻度：複数のサービス拒否攻撃 {/*high-severity-multiple-denial-of-service*/}\n\n**CVE**: [CVE-2026-23864](https://www.cve.org/CVERecord?id=CVE-2026-23864)\n**Base Score**: 7.5 (High)\n**Date**: January 26, 2025\n\nセキュリティ研究者により、React Server Components に追加の DoS 脆弱性が残っていることが発見されました。\n\nこれらの脆弱性は、特別に細工された HTTP リクエストをサーバ関数 (Server Function) エンドポイントに送信することで引き起こされます。実行される脆弱なコード経路、アプリケーション設定、およびアプリケーションコードに応じて、サーバクラッシュ、メモリ不足例外、または過剰な CPU 使用率につながる可能性があります。\n\n1 月 26 日に公開されたパッチは、これらの DoS 脆弱性を緩和します。\n\n<Note>\n\n#### 追加の修正が公開されました {/*additional-fix-published*/}\n\n[CVE-2025-55184](https://www.cve.org/CVERecord?id=CVE-2025-55184) の DoS に対処する元の修正は不完全でした。\n\nこれにより以前のバージョンが脆弱な状態のままでした。バージョン 19.0.4、19.1.5、19.2.4 は安全です。\n\n-----\n\n_Updated January 26, 2026._\n\n</Note>\n\n---\n\n## 高深刻度：サービス拒否攻撃 {/*high-severity-denial-of-service*/}\n\n**CVE**: [CVE-2025-55184](https://www.cve.org/CVERecord?id=CVE-2025-55184) および [CVE-2025-67779](https://www.cve.org/CVERecord?id=CVE-2025-67779)\n**Base Score**: 7.5 (High)\n\nセキュリティ研究者は、悪意のある HTTP リクエストを作成して任意のサーバ関数エンドポイントに対して送信することで、React がそれをデシリアライズする際に、サーバプロセスをハングさせて CPU を消費する無限ループを引き起こすことができることを発見しました。アプリが React のサーバ関数のエンドポイントを実装していない場合でも、React Server Components をサポートしている場合は脆弱性の影響を受ける可能性があります。\n\nこれにより、攻撃者がユーザによる製品へのアクセスを不能にし、サーバ環境のパフォーマンスに影響を与えうる手段が生じます。\n\n本日公開されたパッチは、無限ループを防ぐことでこの問題を緩和します。\n\n## 中深刻度：ソースコード露出 {/*low-severity-source-code-exposure*/}\n\n**CVE**: [CVE-2025-55183](https://www.cve.org/CVERecord?id=CVE-2025-55183)\n**Base Score**: 5.3 (Medium)\n\nセキュリティ研究者は、脆弱なサーバ関数に送信された悪意のある HTTP リクエストが、安全でない方法で任意のサーバ関数のソースコードを返す可能性があることを発見しました。悪用には、明示的または暗黙的に引数の文字列化を行い露出するサーバ関数の存在が必要です。\n\n```javascript\n'use server';\n\nexport async function serverFunction(name) {\n  const conn = db.createConnection('SECRET KEY');\n  const user = await conn.createUser(name); // implicitly stringified, leaked in db\n\n  return {\n   id: user.id,\n   message: `Hello, ${name}!` // explicitly stringified, leaked in reply\n  }}\n```\n\n攻撃者は以下のような情報を漏洩させる可能性があります。\n\n```txt\n0:{\"a\":\"$@1\",\"f\":\"\",\"b\":\"Wy43RxUKdxmr5iuBzJ1pN\"}\n1:{\"id\":\"tva1sfodwq\",\"message\":\"Hello, async function(a){console.log(\\\"serverFunction\\\");let b=i.createConnection(\\\"SECRET KEY\\\");return{id:(await b.createUser(a)).id,message:`Hello, ${a}!`}}!\"}\n```\n\n本日公開されたパッチは、サーバ関数のソースコードが文字列化されるのを防ぎます。\n\n<Note>\n\n#### 漏洩可能性があるのはソースコード内の秘密情報のみ {/*only-secrets-in-source-code-may-be-exposed*/}\n\nソースコードにハードコードされた秘密情報は漏洩の可能性がありますが、`process.env.SECRET` などのランタイムシークレットは影響を受けません。\n\n漏洩されるコードの範囲は、サーバ関数内のコードに限定されますが、バンドラが行うインライン化の程度によっては他の関数が含まれる可能性があります。\n\n必ず本番バンドルに対して検証を行ってください。\n\n</Note>\n\n---\n\n## タイムライン {/*timeline*/}\n* **12 月 3 日**：[Andrew MacPherson](https://github.com/AndrewMohawk) 氏が Vercel および [Meta Bug Bounty](https://bugbounty.meta.com/) に漏洩の問題を報告。\n* **12 月 4 日**：[RyotaK](https://ryotak.net) 氏が [Meta Bug Bounty](https://bugbounty.meta.com/) に DoS 問題を初期報告。\n* **12 月 6 日**：React チームが両方の問題を確認し、調査を開始。\n* **12 月 7 日**：初期の修正が作成され、React チームが新しいパッチの検証と計画を開始。\n* **12 月 8 日**：影響を受けるホスティングプロバイダとオープンソースプロジェクトに通知。\n* **12 月 10 日**：ホスティングプロバイダの緩和策が導入、パッチの検証が完了。\n* **12 月 11 日**：Shinsaku Nomura 氏が [Meta Bug Bounty](https://bugbounty.meta.com/) に追加の DoS を報告。\n* **12 月 11 日**：パッチが公開され、[CVE-2025-55183](https://www.cve.org/CVERecord?id=CVE-2025-55183) および [CVE-2025-55184](https://www.cve.org/CVERecord?id=CVE-2025-55184) として一般公開。\n* **12 月 11 日**：不足していた DoS のケースが内部で発見され、修正が適用され [CVE-2025-67779](https://www.cve.org/CVERecord?id=CVE-2025-67779) として一般公開。\n* **1 月 26 日**：追加の DoS ケースが発見され、修正が適用され [CVE-2026-23864](https://www.cve.org/CVERecord?id=CVE-2026-23864) として一般公開。\n---\n\n## 謝辞 {/*attribution*/}\n\nソースコード漏洩を報告してくださった [Andrew MacPherson (AndrewMohawk)](https://github.com/AndrewMohawk) 氏、サービス拒否攻撃の脆弱性を報告してくださった GMO Flatt Security Inc の [RyotaK](https://ryotak.net) 氏および株式会社ビットフォレストの Shinsaku Nomura 氏に感謝します。追加の DoS 脆弱性を報告してくださった [Winfunc Research](https://winfunc.com) の [Mufeed VH](https://x.com/mufeedvh) 氏、[Joachim Viide](https://jviide.iki.fi) 氏、[GMO Flatt Security Inc](https://flatt.tech/en/) の [RyotaK](https://ryotak.net) 氏、および Tencent Security YUNDING LAB の Xiangwei Zhang 氏に感謝します。\n"
  },
  {
    "path": "src/content/blog/index.md",
    "content": "---\ntitle: React Blog\n---\n\n<Intro>\n\nReact チームからの公式な更新のお知らせはこのブログに掲載されます。リリースノートや非推奨化のお知らせなどの重要なことはすべて、まずこちらに掲載されます。\n\nBluesky の [@react.dev](https://bsky.app/profile/react.dev) や Twitter の [@reactjs](https://twitter.com/reactjs) アカウントをフォローすることもできますが、このブログさえ読んでいれば、重要なことを見逃す心配はありません。\n\n</Intro>\n\n<Note>\n日本語版サイト (ja.react.dev) のブログセクションへの記事掲載には英語版サイトと比べてタイムラグがあります。 最新のブログ記事は[英語版](https://react.dev/blog)でご確認ください。\n</Note>\n\n<div className=\"sm:-mx-5 flex flex-col gap-5 mt-12\">\n\n<BlogCard title=\"React Server Components におけるサービス拒否攻撃とソースコード露出\" date=\"December 11, 2025\" url=\"/blog/2025/12/11/denial-of-service-and-source-code-exposure-in-react-server-components\">\n\nセキュリティ研究者が先週の重大な脆弱性に対するパッチを検証する過程で、React Server Components における 2 つの脆弱性を追加で発見し、開示しました。\n\n</BlogCard>\n\n<BlogCard title=\"React Server Components における重大なセキュリティ脆弱性\" date=\"December 3, 2025\" url=\"/blog/2025/12/03/critical-security-vulnerability-in-react-server-components\">\n\nReact Server Components に、認証不要のリモートコード実行の脆弱性が存在します。バージョン 19.0.1、19.1.2、19.2.1 で修正が公開されました。直ちにアップグレードすることを推奨します。\n\n</BlogCard>\n\n<BlogCard title=\"React Conf 2025 振り返り\" date=\"October 16, 2025\" url=\"/blog/2025/10/16/react-conf-2025-recap\">\n\n先週 React Conf 2025 が開催されました。この投稿では、イベントでの講演と発表内容をまとめます。\n\n</BlogCard>\n\n<BlogCard title=\"React Compiler v1.0\" date=\"October 7, 2025\" url=\"/blog/2025/10/07/react-compiler-1\">\n\n本日、コンパイラの最初の安定版リリースを行います。また、導入を支援するためのリンタやツールの改善もリリースします。\n\n</BlogCard>\n\n<BlogCard title=\"React Foundation 設立\" date=\"October 7, 2025\" url=\"/blog/2025/10/07/introducing-the-react-foundation\">\n\n本日、React Foundation の設立と新しい技術ガバナンス構造について発表します。\n\n</BlogCard>\n\n<BlogCard title=\"React 19.2\" date=\"October 1, 2025\" url=\"/blog/2025/10/01/react-19-2\">\n\nReact 19.2 では Activity、パフォーマンストラック、useEffectEvent などの新機能が追加されます。\n\n</BlogCard>\n\n<BlogCard title=\"React Labs: ビュー遷移、Activity、その他もろもろ\" date=\"April 23, 2025\" url=\"/blog/2025/04/23/react-labs-view-transitions-activity-and-more\">\n\nReact Labs 記事では、現在活発に研究・開発が行われているプロジェクトについて述べていきます。この投稿では、今すぐ試すことができる 2 つの新しい実験的機能と、現在取り組んでいる他の分野の更新情報を共有します。\n\n</BlogCard>\n\n<BlogCard title=\"Create React App の非推奨化\" date=\"February 14, 2025\" url=\"/blog/2025/02/14/sunsetting-create-react-app\">\n\n本日、新規アプリに対して Create React App を非推奨とし、既存のアプリにはフレームワークへの移行、または Vite、Parcel、RSBuild などのビルドツールへの移行を推奨します。また、フレームワークがプロジェクトに適していない場合や独自のフレームワークを構築したい場合、あるいは React がどのように動作するかを学ぶためにゼロから React アプリを構築したい場合のためのドキュメントも提供します。\n\n</BlogCard>\n\n<BlogCard title=\"React v19 \" date=\"December 5, 2024\" url=\"/blog/2024/12/05/react-19\">\n\nReact 19 アップグレードガイドでは、アプリを React 19 にアップグレードするためのステップバイステップガイドをお示ししました。この投稿では、React 19 の新機能と、それらをどのように採用するかについて概説します。\n\n</BlogCard>\n\n<BlogCard title=\"React Compiler Beta Release\" date=\"October 21, 2024\" url=\"/blog/2024/10/21/react-compiler-beta-release\">\n\n以前 React Conf 2024 で React Compiler の実験的リリースを行いました。それ以降も多くの進展がありましたので、この投稿では React Compiler の次の展開についてお伝えしたいと思います。\n\n</BlogCard>\n\n<BlogCard title=\"React Conf 2024 振り返り\" date=\"May 22, 2024\" url=\"/blog/2024/05/22/react-conf-2024-recap\">\n\n先週、ネバダ州ヘンダーソンで 700 人以上の参加者が集まり、最新の UI エンジニアリングについて議論する 2 日間のカンファレンス、React Conf 2024 を開催しました。対面でのカンファレンスは 2019 年以来であり、コミュニティが再び一堂に会することができたことを大変に嬉しく思いました。\n\n</BlogCard>\n\n<BlogCard title=\"React 19 アップグレードガイド\" date=\"April 25, 2024\" url=\"/blog/2024/04/25/react-19-upgrade-guide\">\n\nReact 19 に追加された改善にはいくつかの破壊的変更が必要ですが、アップグレードをできるだけスムーズに行えるよう努力しているため、ほとんどのアプリには影響が出ないことを予想しています。この投稿では、ライブラリを React 19 Beta にアップグレードする手順をご案内します。\n\n</BlogCard>\n\n<BlogCard title=\"React Labs: 私達のこれまでの取り組み - 2024年2月版\" date=\"February 15, 2024\" url=\"/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024\">\n\nReact Labs 記事では、現在活発に研究・開発が行われているプロジェクトについて述べていきます。前回のアップデート以降に、React Compiler、新機能、React 19 に関する大きな進展がありましたので、我々が学んだことを共有していきます。\n\n</BlogCard>\n\n<BlogCard title=\"React Canary: Meta 外での段階的な新機能導入\" date=\"May 3, 2023\" url=\"/blog/2023/05/03/react-canaries\">\n\n従来、React の新機能は Meta 社内で先に利用可能になり、オープンソースでのリリースは後になっていました。私たちは、Meta 社内での React 使用法と同様に、安定版がリリースされる前に個々の新機能の設計がほぼ確定した段階でそれらを採用できるという選択肢を、React コミュニティに提供したいと考えています。私たちは、新たに公式サポート対象となる Canary リリースチャンネルを導入します。これにより、フレームワークのような統合済セットアップが、個々の React 機能の採用を React のリリーススケジュールから切り離して行えるようになります。\n\n</BlogCard>\n\n<BlogCard title=\"React Labs: 私達のこれまでの取り組み - 2023年3月版\" date=\"March 22, 2023\" url=\"/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023\">\n\nReact Labs 記事では、現在活発に研究・開発が行われているプロジェクトについて述べていきます。前回のアップデート以降に、React Server Components、アセットローディング、最適化コンパイラ、オフスクリーンレンダリング、トランジションのトレースに関する大きな進展がありましたので、我々が学んだことを共有していきます。\n\n</BlogCard>\n\n\n<BlogCard title=\"react.dev のご紹介\" date=\"March 16, 2023\" url=\"/blog/2023/03/16/introducing-react-dev\">\n\n本日、React とそのドキュメントの新しいホームとなる react.dev の立ち上げを発表することができ、大変うれしく思います。この記事では、新しいサイトの見どころをご紹介します。\n\n</BlogCard>\n\n\n<BlogCard title=\"React Labs: 私達のこれまでの取り組み - 2022年6月版\" date=\"June 15, 2022\" url=\"/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022\">\nReact 18 の完成は数年がかりの仕事であり、React チームはそこから貴重な教訓を得ることになりました。このリリースは何年も研究を行い、様々なアプローチを試した結果として生まれたものです。いくつかのアプローチはうまく行った一方で、多くは行き詰まって新たな知見のみをもたらすことになりました。ここから我々が学んだことは、我々がどんなことを試しているのかをコミュニティに知らせることなくただお待たせするというのは...\n</BlogCard>\n\n<BlogCard title=\"React v18.0\" date=\"March 29, 2022\" url=\"/blog/2022/03/29/react-v18\">\nReact 18 が npm で利用可能になりました！ 前回の投稿にて、アプリを React 18 にアップグレードするためのステップバイステップガイドを共有しました。この投稿では、React 18 の新機能や、将来に向けての展望をお伝えします。\n</BlogCard>\n\n<BlogCard title=\"React 18 アップグレードガイド\" date=\"March 8, 2022\" url=\"/blog/2022/03/08/react-18-upgrade-guide\">\nリリース告知の記事でお伝えしたとおり、React 18 には新たな並行レンダラを用いた機能が加わっており、既存のアプリケーションが段階的に採用できる方法も提供しています。この投稿では、React 18 にアップグレードするためのステップについてご案内します。\n</BlogCard>\n\n<BlogCard title=\"React Conf 2021 振り返り\" date=\"December 17, 2021\" url=\"/blog/2021/12/17/react-conf-2021-recap\">\n先週、第 6 回の React Conf を開催しました。これまでの年度において、我々は React Conf のステージ上で、React Native や React Hooks といった業界を変えるような発表をお届けしてきました。本年度は、React 18 のリリースと並行レンダリング機能の段階的な採用から始まる我々のマルチプラットフォーム戦略についての話題を共有しました。\n</BlogCard>\n\n<BlogCard title=\"React 18に向けてのプラン\" date=\"June 8, 2021\" url=\"/blog/2021/06/08/the-plan-for-react-18\">\nReact チームより幾つかのお知らせがあります！\n\n- 次のメジャーバージョンとなる React 18 リリースに向けての作業を開始しました。\n- コミュニティが React 18 の新機能を段階的に導入できるようにするため、ワーキンググループを作成しました。\n- ライブラリの作者が試用してフィードバックを送れるようにするため、React 18 のアルファ版を公開しました。\n</BlogCard>\n\n<BlogCard title=\"バンドルサイズゼロの React Server Components の紹介\" date=\"December 21, 2020\" url=\"/blog/2020/12/21/data-fetching-with-react-server-components\">\n2020 年は長い 1 年でした。本年の最後に、我々の研究における特別なホリデーアップデートとして、バンドルサイズゼロで動作する React サーバコンポーネントの紹介をしたいと思います。サーバコンポーネントの紹介のためのトークとデモを用意しました。休暇期間中にチェックするもよし、来年仕事に戻ってきた時に見てみるのでもよいでしょう。\n</BlogCard>\n\n</div>\n\n---\n\n### すべてのリリースノート {/*all-release-notes*/}\n\nReact のすべてのリリースが個別のブログ記事になるわけではありませんが、React リポジトリの [`CHANGELOG.md`](https://github.com/facebook/react/blob/main/CHANGELOG.md) ファイルや [Releases](https://github.com/facebook/react/releases) ページで、すべてのリリースの詳細な変更履歴を見ることができます。\n\n---\n\n### 以前の投稿 {/*older-posts*/}\n\nより以前の投稿は[こちら](https://reactjs.org/blog/all.html)。\n\n<div className=\"h-12\"></div>\n"
  },
  {
    "path": "src/content/community/acknowledgements.md",
    "content": "---\ntitle: 謝辞\n---\n\n<Intro>\n\nオリジナルの React は [Jordan Walke](https://github.com/jordwalke) によって作成されました。現在、React に取り組んでいる[フルタイムの専門チーム](/community/team)があり、また [1000 名を超えるオープンソース貢献者](https://github.com/facebook/react/graphs/contributors)がいます。\n\n</Intro>\n\n## 過去の貢献者 {/*past-contributors*/}\n\n過去長年にわたり React およびそのドキュメントに関してたくさんの貢献を行い、メンテナンスのお手伝いをしていただいた、以下の方々に感謝します。\n\n* [Almero Steyn](https://github.com/AlmeroSteyn)\n* [Andreas Svensson](https://github.com/syranide)\n* [Alex Krolick](https://github.com/alexkrolick)\n* [Alexey Pyltsyn](https://github.com/lex111)\n* [Andrey Lunyov](https://github.com/alunyov)\n* [Brandon Dail](https://github.com/aweary)\n* [Brian Vaughn](https://github.com/bvaughn)\n* [Caleb Meredith](https://github.com/calebmer)\n* [Chang Yan](https://github.com/cyan33)\n* [Cheng Lou](https://github.com/chenglou)\n* [Christoph Nakazawa](https://github.com/cpojer)\n* [Christopher Chedeau](https://github.com/vjeux)\n* [Clement Hoang](https://github.com/clemmy)\n* [Dave McCabe](https://github.com/davidmccabe)\n* [Dominic Gannaway](https://github.com/trueadm)\n* [Flarnie Marchan](https://github.com/flarnie)\n* [Jason Quense](https://github.com/jquense)\n* [Jesse Beach](https://github.com/jessebeach)\n* [Jessica Franco](https://github.com/Jessidhia)\n* [Jim Sproch](https://github.com/jimfb)\n* [Josh Duck](https://github.com/joshduck)\n* [Joe Critchley](https://github.com/joecritch)\n* [Jeff Morrison](https://github.com/jeffmo)\n* [Luna Ruan](https://github.com/lunaruan)\n* [Luna Wei](https://github.com/lunaleaps)\n* [Noah Lemen](https://github.com/noahlemen)\n* [Kathryn Middleton](https://github.com/kmiddleton14)\n* [Keyan Zhang](https://github.com/keyz)\n* [Marco Salazar](https://github.com/salazarm)\n* [Mengdi Chen](https://github.com/mondaychen)\n* [Nat Alison](https://github.com/tesseralis)\n* [Nathan Hunzaker](https://github.com/nhunzaker)\n* [Nicolas Gallagher](https://github.com/necolas)\n* [Paul O'Shannessy](https://github.com/zpao)\n* [Pete Hunt](https://github.com/petehunt)\n* [Philipp Spiess](https://github.com/philipp-spiess)\n* [Rachel Nabors](https://github.com/rachelnabors)\n* [Robert Zhang](https://github.com/robertzhidealx)\n* [Samuel Susla](https://github.com/sammy-SC)\n* [Sander Spies](https://github.com/sanderspies)\n* [Sasha Aickin](https://github.com/aickin)\n* [Sathya Gunasekaran](https://github.com/gsathya)\n* [Sophia Shoemaker](https://github.com/mrscobbler)\n* [Sunil Pai](https://github.com/threepointone)\n* [Tianyu Yao](https://github.com/)\n* [Tim Yung](https://github.com/yungsters)\n* [Xuan Huang](https://github.com/huxpro)\n\nこのリストは全員を網羅したものではありません。\n\n特に、長年にわたる指導とサポートをしていただいた [Tom Occhino](https://github.com/tomocchino) と [Adam Wolff](https://github.com/wolffiex) に感謝いたします。[React を他の言語に翻訳](https://translations.react.dev/)してくださったボランティアの皆様にも感謝いたします。\n\n## 追加の謝辞 {/*additional-thanks*/}\n\nさらに、以下の皆さんに感謝いたします。\n\n* [Jeff Barczewski](https://github.com/jeffbski): npm でのパッケージ名 `react` の使用許可をいただきました\n* [Christopher Aue](https://christopheraue.net/): ドメイン \"reactjs.com\" と Twitter ユーザ名 [@reactjs](https://twitter.com/reactjs) の使用許可をいただきました\n* [ProjectMoon](https://github.com/ProjectMoon): npm でのパッケージ名 [flux](https://www.npmjs.com/package/flux) の使用許可をいただきました\n* Shane Anderson: GitHub で組織名 [react](https://github.com/react) の使用許可をいただきました\n"
  },
  {
    "path": "src/content/community/conferences.md",
    "content": "---\ntitle: React カンファレンス\n---\n\n<Intro>\n\n各国で開催される React.js のカンファレンスをご存じの場合は、ここに追加してください（開催日順にしてください）。\n\n</Intro>\n\n## Upcoming Conferences {/*upcoming-conferences*/}\n\n### React Universe Conf 2025 {/*react-universe-conf-2025*/}\nSeptember 2-4, 2025. Wrocław, Poland.\n\n[Website](https://www.reactuniverseconf.com/) - [Twitter](https://twitter.com/react_native_eu) - [LinkedIn](https://www.linkedin.com/events/reactuniverseconf7163919537074118657/)\n\n### React Alicante 2025 {/*react-alicante-2025*/}\nOctober 2-4, 2025. Alicante, Spain.\n\n[Website](https://reactalicante.es/) - [Twitter](https://x.com/ReactAlicante) - [Bluesky](https://bsky.app/profile/reactalicante.es) - [YouTube](https://www.youtube.com/channel/UCaSdUaITU1Cz6PvC97A7e0w)\n\n### RenderCon Kenya 2025 {/*rendercon-kenya-2025*/}\nOctober 04, 2025. Nairobi, Kenya\n\n[Website](https://rendercon.org/) - [Twitter](https://twitter.com/renderconke) - [LinkedIn](https://www.linkedin.com/company/renderconke/) - [YouTube](https://www.youtube.com/channel/UC0bCcG8gHUL4njDOpQGcMIA)\n\n### React Conf 2025 {/*react-conf-2025*/}\nOctober 7-8, 2025. Henderson, Nevada, USA and free livestream\n\n[Website](https://conf.react.dev/) - [Twitter](https://x.com/reactjs) - [Bluesky](https://bsky.app/profile/react.dev)\n\n### React India 2025 {/*react-india-2025*/}\nOctober 31 - November 01, 2025. In-person in Goa, India (hybrid event) + Oct 15 2025 - remote day\n\n[Website](https://www.reactindia.io) - [Twitter](https://twitter.com/react_india) - [Facebook](https://www.facebook.com/ReactJSIndia) - [Youtube](https://www.youtube.com/channel/UCaFbHCBkPvVv1bWs_jwYt3w)\n\n### React Summit US 2025 {/*react-summit-us-2025*/}\nNovember 18 - 21, 2025. In-person in New York, USA + remote (hybrid event)\n\n[Website](https://reactsummit.us/) - [Twitter](https://x.com/reactsummit)\n\n### React Advanced London 2025 {/*react-advanced-london-2025*/}\nNovember 28 & December 1, 2025. In-person in London, UK + online (hybrid event)\n\n[Website](https://reactadvanced.com/) - [Twitter](https://x.com/reactadvanced)\n\n### CityJS Singapore 2026 {/*cityjs-singapore-2026*/}\nFebruary 4-6,  2026. In-person in Singapore\n\n[Website](https://india.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social)\n\n### CityJS New Delhi 2026 {/*cityjs-newdelhi-2026*/}\nFebruary 12-13,  2026. In-person in New Delhi, India\n\n[Website](https://india.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social)\n\n\n### React Paris 2026 {/*react-paris-2026*/}\nMarch 26 - 27, 2026. In-person in Paris, France (hybrid event)\n\n[Website](https://react.paris/) - [Twitter](https://x.com/BeJS_)\n\n\n### CityJS London 2026 {/*cityjs-london-2026*/}\nApril 14-17,  2026. In-person in London\n\n[Website](https://india.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social)\n\n\n## Past Conferences {/*past-conferences*/}\n\n\n### React Nexus 2025 {/*react-nexus-2025*/}\nJuly 03 - 05, 2025. In-person in Bangalore, India\n\n[Website](https://reactnexus.com/) - [Twitter](https://x.com/ReactNexus) - [Bluesky](https://bsky.app/profile/reactnexus.com) - [Linkedin](https://www.linkedin.com/company/react-nexus) - [YouTube](https://www.youtube.com/reactify_in)\n\n### React Summit 2025 {/*react-summit-2025*/}\nJune 13 - 17, 2025. In-person in Amsterdam, Netherlands + remote (hybrid event)\n\n[Website](https://reactsummit.com/) - [Twitter](https://x.com/reactsummit)\n\n### React Norway 2025 {/*react-norway-2025*/}\nJune 13, 2025. In-person in Oslo, Norway + remote (virtual event)\n\n[Website](https://reactnorway.com/) - [Twitter](https://x.com/ReactNorway)\n\n### CityJS Athens 2025 {/*cityjs-athens*/}\nMay 27 - 31, 2025. In-person in Athens, Greece\n\n[Website](https://athens.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) - [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social)\n\n### App.js Conf 2025 {/*appjs-conf-2025*/}\nMay 28 - 30, 2025. In-person in Kraków, Poland + remote\n\n[Website](https://appjs.co) - [Twitter](https://twitter.com/appjsconf)\n\n### CityJS London 2025 {/*cityjs-london*/}\nApril 23 - 25, 2025. In-person in London, UK\n\n[Website](https://london.cityjsconf.org/) - [Twitter](https://x.com/cityjsconf) -  [Bluesky](https://bsky.app/profile/cityjsconf.bsky.social)\n\n### React Paris 2025 {/*react-paris-2025*/}\nMarch 20 - 21, 2025. In-person in Paris, France (hybrid event)\n\n[Website](https://react.paris/) - [Twitter](https://x.com/BeJS_) - [YouTube](https://www.youtube.com/playlist?list=PL53Z0yyYnpWitP8Zv01TSEQmKLvuRh_Dj)\n\n### React Native Connection 2025 {/*react-native-connection-2025*/}\nApril 3 (Reanimated Training) + April 4 (Conference), 2025. Paris, France.\n\n[Website](https://reactnativeconnection.io/) - [X](https://x.com/reactnativeconn) - [Bluesky](https://bsky.app/profile/reactnativeconnect.bsky.social)\n\n### React Day Berlin 2024 {/*react-day-berlin-2024*/}\nDecember 13 & 16, 2024. In-person in Berlin, Germany + remote (hybrid event)\n\n[Website](https://reactday.berlin/) - [Twitter](https://x.com/reactdayberlin)\n\n### React Africa 2024 {/*react-africa-2024*/}\nNovember 29, 2024. In-person in Casablanca, Morocco (hybrid event)\n\n[Website](https://react-africa.com/) - [Twitter](https://x.com/BeJS_)\n\n### React Summit US 2024 {/*react-summit-us-2024*/}\nNovember 19 & 22, 2024. In-person in New York, USA + online (hybrid event)\n\n[Website](https://reactsummit.us/) - [Twitter](https://twitter.com/reactsummit) - [Videos](https://portal.gitnation.org/)\n\n### React Native London Conf 2024 {/*react-native-london-2024*/}\nNovember 14 & 15, 2024. In-person in London, UK\n\n[Website](https://reactnativelondon.co.uk/) - [Twitter](https://x.com/RNLConf)\n\n### React Advanced London 2024 {/*react-advanced-london-2024*/}\nOctober 25 & 28, 2024. In-person in London, UK + online (hybrid event)\n\n[Website](https://reactadvanced.com/) - [Twitter](https://x.com/reactadvanced)\n\n### reactjsday 2024 {/*reactjsday-2024*/}\nOctober 25, 2024. In-person in Verona, Italy + online (hybrid event)\n\n[Website](https://2024.reactjsday.it/) - [Twitter](https://x.com/reactjsday) - [Facebook](https://www.facebook.com/GrUSP/) - [YouTube](https://www.youtube.com/c/grusp)\n\n### React Brussels 2024 {/*react-brussels-2024*/}\nOctober 18, 2024. In-person in Brussels, Belgium (hybrid event)\n\n[Website](https://www.react.brussels/) - [Twitter](https://x.com/BrusselsReact) - [YouTube](https://www.youtube.com/playlist?list=PL53Z0yyYnpWimQ0U75woee2zNUIFsiDC3)\n\n### React India 2024 {/*react-india-2024*/}\nOctober 17 - 19, 2024. In-person in Goa, India (hybrid event) + Oct 15 2024 - remote day\n\n[Website](https://www.reactindia.io) - [Twitter](https://twitter.com/react_india) - [Facebook](https://www.facebook.com/ReactJSIndia) - [Youtube](https://www.youtube.com/channel/UCaFbHCBkPvVv1bWs_jwYt3w)\n\n### RenderCon Kenya 2024 {/*rendercon-kenya-2024*/}\nOctober 04 - 05, 2024. Nairobi, Kenya\n\n[Website](https://rendercon.org/) - [Twitter](https://twitter.com/renderconke) - [LinkedIn](https://www.linkedin.com/company/renderconke/) - [YouTube](https://www.youtube.com/channel/UC0bCcG8gHUL4njDOpQGcMIA)\n\n### React Alicante 2024 {/*react-alicante-2024*/}\nSeptember 19-21, 2024. Alicante, Spain.\n\n[Website](https://reactalicante.es/) - [Twitter](https://twitter.com/ReactAlicante) - [YouTube](https://www.youtube.com/channel/UCaSdUaITU1Cz6PvC97A7e0w)\n\n### React Universe Conf 2024 {/*react-universe-conf-2024*/}\nSeptember 5-6, 2024. Wrocław, Poland.\n\n[Website](https://www.reactuniverseconf.com/) - [Twitter](https://twitter.com/react_native_eu) - [LinkedIn](https://www.linkedin.com/events/reactuniverseconf7163919537074118657/)\n\n\n### React Rally 2024 🐙 {/*react-rally-2024*/}\nAugust 12-13, 2024. Park City, UT, USA\n\n[Website](https://reactrally.com) - [Twitter](https://twitter.com/ReactRally) - [YouTube](https://www.youtube.com/channel/UCXBhQ05nu3L1abBUGeQ0ahw)\n\n### The Geek Conf 2024 {/*the-geek-conf-2024*/}\nJuly 25, 2024. In-person in Berlin, Germany + remote (hybrid event)\n\n[Website](https://thegeekconf.com) - [Twitter](https://twitter.com/thegeekconf)\n\n### Chain React 2024 {/*chain-react-2024*/}\nJuly 17-19, 2024. In-person in Portland, OR, USA\n\n[Website](https://chainreactconf.com) - [Twitter](https://twitter.com/ChainReactConf)\n\n### React Nexus 2024 {/*react-nexus-2024*/}\nJuly 04 & 05, 2024. Bangalore, India (In-person event)\n\n[Website](https://reactnexus.com/) - [Twitter](https://twitter.com/ReactNexus) - [Linkedin](https://www.linkedin.com/company/react-nexus) - [YouTube](https://www.youtube.com/reactify_in)\n\n### React Summit 2024 {/*react-summit-2024*/}\nJune 14 & 18, 2024. In-person in Amsterdam, Netherlands + remote (hybrid event)\n\n[Website](https://reactsummit.com/) - [Twitter](https://twitter.com/reactsummit) - [Videos](https://portal.gitnation.org/)\n\n### React Norway 2024 {/*react-norway-2024*/}\nJune 14, 2024. In-person at Farris Bad Hotel in Larvik, Norway and online (hybrid event).\n\n[Website](https://reactnorway.com/) - [Twitter](https://twitter.com/ReactNorway)\n\n### Render(ATL) 2024 🍑 {/*renderatl-2024-*/}\nJune 12 - June 14, 2024. Atlanta, GA, USA\n\n[Website](https://renderatl.com) - [Discord](https://www.renderatl.com/discord) - [Twitter](https://twitter.com/renderATL) - [Instagram](https://www.instagram.com/renderatl/) - [Facebook](https://www.facebook.com/renderatl/) - [LinkedIn](https://www.linkedin.com/company/renderatl) - [Podcast](https://www.renderatl.com/culture-and-code#/)\n\n### Frontend Nation 2024 {/*frontend-nation-2024*/}\nJune 4 - 7, 2024. Online\n\n[Website](https://frontendnation.com/) - [Twitter](https://twitter.com/frontendnation)\n\n### App.js Conf 2024 {/*appjs-conf-2024*/}\nMay 22 - 24, 2024. In-person in Kraków, Poland + remote\n\n[Website](https://appjs.co) - [Twitter](https://twitter.com/appjsconf)\n\n### React Conf 2024 {/*react-conf-2024*/}\nMay 15 - 16, 2024. In-person in Henderson, NV, USA + remote\n\n[Website](https://conf.react.dev) - [Twitter](https://twitter.com/reactjs)\n\n### React Native Connection 2024 {/*react-native-connection-2024*/}\nApril 23, 2024. In-person in Paris, France \n\n[Website](https://reactnativeconnection.io/) - [Twitter](https://twitter.com/ReactNativeConn)\n\n### React Miami 2024 {/*react-miami-2024*/}\nApril 19 - 20, 2024. In-person in Miami, FL, USA\n\n[Website](https://reactmiami.com/) - [Twitter](https://twitter.com/ReactMiamiConf)\n\n### Epic Web Conf 2024 {/*epic-web-2024*/}\nApril 10 - 11, 2024. In-person in Park City, UT, USA\n\n[Website](https://www.epicweb.dev/conf) - [YouTube](https://www.youtube.com/@EpicWebDev)\n\n### React Paris 2024 {/*react-paris-2024*/}\nMarch 22, 2024. In-person in Paris, France + Remote (hybrid)\n\n[Website](https://react.paris/) - [Twitter](https://twitter.com/BeJS_) - [LinkedIn](https://www.linkedin.com/events/7150816372074192900/comments/) - [Videos](https://www.youtube.com/playlist?list=PL53Z0yyYnpWhUzgvr2Nys3kZBBLcY0TA7)\n\n### React Day Berlin 2023 {/*react-day-berlin-2023*/}\nDecember 8 & 12, 2023. In-person in Berlin, Germany + remote first interactivity (hybrid event)\n\n[Website](https://reactday.berlin) - [Twitter](https://twitter.com/reactdayberlin) - [Facebook](https://www.facebook.com/reactdayberlin/) - [Videos](https://portal.gitnation.org/events/react-day-berlin-2023)\n\n### React Summit US 2023 {/*react-summit-us-2023*/}\nNovember 13 & 15, 2023. In-person in New York, US + remote first interactivity (hybrid event)\n\n[Website](https://reactsummit.us) - [Twitter](https://twitter.com/reactsummit) - [Facebook](https://www.facebook.com/reactamsterdam) - [Videos](https://portal.gitnation.org/events/react-summit-us-2023)\n\n### reactjsday 2023 {/*reactjsday-2023*/}\nOctober 27th 2023. In-person in Verona, Italy and online (hybrid event)\n\n[Website](https://2023.reactjsday.it/) - [Twitter](https://twitter.com/reactjsday) - [Facebook](https://www.facebook.com/GrUSP/) - [YouTube](https://www.youtube.com/c/grusp)\n\n### React Advanced 2023 {/*react-advanced-2023*/}\nOctober 20 & 23, 2023. In-person in London, UK + remote first interactivity (hybrid event)\n\n[Website](https://www.reactadvanced.com/) - [Twitter](https://twitter.com/ReactAdvanced) - [Facebook](https://www.facebook.com/ReactAdvanced) - [Videos](https://portal.gitnation.org/events/react-advanced-conference-2023)\n\n### React Brussels 2023 {/*react-brussels-2023*/}\nOctober 13th 2023. In-person in Brussels, Belgium + Remote (hybrid)\n\n[Website](https://www.react.brussels/) - [Twitter](https://twitter.com/BrusselsReact) - [Videos](https://www.youtube.com/playlist?list=PL53Z0yyYnpWh85KeMomUoVz8_brrmh_aC) \n\n### React India 2023 {/*react-india-2023*/}\nOctober 5 - 7, 2023. In-person in Goa, India (hybrid event) + Oct 3 2023 - remote day\n\n[Website](https://www.reactindia.io) - [Twitter](https://x.com/react_india) - [Facebook](https://www.facebook.com/ReactJSIndia) - [Youtube](https://www.youtube.com/channel/UCaFbHCBkPvVv1bWs_jwYt3w)\n\n### RenderCon Kenya 2023 {/*rendercon-kenya-2023*/}\nSeptember 29 - 30, 2023. Nairobi, Kenya\n\n[Website](https://rendercon.org/) - [Twitter](https://twitter.com/renderconke) - [LinkedIn](https://www.linkedin.com/company/renderconke/) - [YouTube](https://www.youtube.com/channel/UC0bCcG8gHUL4njDOpQGcMIA)\n\n### React Live 2023 {/*react-live-2023*/}\nSeptember 29, 2023. Amsterdam, Netherlands\n\n[Website](https://reactlive.nl/)\n\n### React Alicante 2023 {/*react-alicante-2023*/}\nSeptember 28 - 30, 2023. Alicante, Spain\n\n[Website](https://reactalicante.es/) - [Twitter](https://twitter.com/reactalicante)\n\n### RedwoodJS Conference 2023 {/*redwoodjs-conference-2023*/}\nSeptember 26 - 29, 2023. Grants Pass, Oregon + remote (hybrid event) \n\n[Website](https://www.redwoodjsconf.com/) - [Twitter](https://twitter.com/redwoodjs)\n\n### React Native EU 2023 {/*react-native-eu-2023*/}\nSeptember 7 & 8, 2023. Wrocław, Poland\n\n[Website](https://react-native.eu) - [Twitter](https://twitter.com/react_native_eu) - [Facebook](https://www.facebook.com/reactnativeeu)\n\n### React Rally 2023 🐙 {/*react-rally-2023*/}\nAugust 17 & 18, 2023. Salt Lake City, UT, USA\n\n[Website](https://www.reactrally.com/) - [Twitter](https://twitter.com/ReactRally) - [Instagram](https://www.instagram.com/reactrally/)\n\n### React Nexus 2023 {/*react-nexus-2023*/}\nJuly 07 & 08, 2023. Bangalore, India (In-person event)\n\n[Website](https://reactnexus.com/) - [Twitter](https://twitter.com/ReactNexus) - [Linkedin](https://www.linkedin.com/company/react-nexus) - [YouTube](https://www.youtube.com/reactify_in)\n\n### ReactNext 2023 {/*reactnext-2023*/}\nJune 27th, 2023. Tel Aviv, Israel\n\n[Website](https://www.react-next.com/) - [Facebook](https://www.facebook.com/ReactNextConf) - [Youtube](https://www.youtube.com/@ReactNext)\n\n### React Norway 2023 {/*react-norway-2023*/}\nJune 16th, 2023. Larvik, Norway\n\n[Website](https://reactnorway.com/) - [Twitter](https://twitter.com/ReactNorway/) - [Facebook](https://www.facebook.com/reactdaynorway/)\n\n### React Summit 2023 {/*react-summit-2023*/}\nJune 2 & 6, 2023. In-person in Amsterdam, Netherlands + remote first interactivity (hybrid event)\n\n[Website](https://reactsummit.com) - [Twitter](https://twitter.com/reactsummit) - [Facebook](https://www.facebook.com/reactamsterdam) - [Videos](https://portal.gitnation.org/events/react-summit-2023)\n\n### Render(ATL) 2023 🍑 {/*renderatl-2023-*/}\nMay 31 - June 2, 2023. Atlanta, GA, USA\n\n[Website](https://renderatl.com) - [Discord](https://www.renderatl.com/discord) - [Twitter](https://twitter.com/renderATL) - [Instagram](https://www.instagram.com/renderatl/) - [Facebook](https://www.facebook.com/renderatl/) - [LinkedIn](https://www.linkedin.com/company/renderatl) - [Podcast](https://www.renderatl.com/culture-and-code#/)\n\n### Chain React 2023 {/*chain-react-2023*/}\nMay 17 - 19, 2023. Portland, OR, USA\n\n[Website](https://chainreactconf.com/) - [Twitter](https://twitter.com/ChainReactConf) - [Facebook](https://www.facebook.com/ChainReactConf/) - [Youtube](https://www.youtube.com/channel/UCwpSzVt7QpLDbCnPXqR97-g/playlists)\n\n### App.js Conf 2023 {/*appjs-conf-2023*/}\nMay 10 - 12, 2023. In-person in Kraków, Poland + remote\n\n[Website](https://appjs.co) - [Twitter](https://twitter.com/appjsconf)\n\n### RemixConf 2023 {/*remixconf-2023*/}\nMay, 2023. Salt Lake City, UT\n\n[Website](https://remix.run/conf/2023) - [Twitter](https://twitter.com/remix_run)\n\n### Reactathon 2023 {/*reactathon-2023*/}\nMay 2 - 3, 2023. San Francisco, CA, USA\n\n[Website](https://reactathon.com) - [Twitter](https://twitter.com/reactathon) - [YouTube](https://www.youtube.com/realworldreact)\n\n### React Miami 2023 {/*react-miami-2023*/}\nApril 20 - 21, 2023. Miami, FL, USA\n\n[Website](https://www.reactmiami.com/) - [Twitter](https://twitter.com/ReactMiamiConf)\n\n### React Day Berlin 2022 {/*react-day-berlin-2022*/}\nDecember 2, 2022. In-person in Berlin, Germany + remote (hybrid event)\n\n[Website](https://reactday.berlin) - [Twitter](https://twitter.com/reactdayberlin) - [Facebook](https://www.facebook.com/reactdayberlin/) - [Videos](https://www.youtube.com/c/ReactConferences)\n\n### React Global Online Summit 22.2 by Geekle {/*react-global-online-summit-222-by-geekle*/}\nNovember 8 - 9, 2022 - Online Summit\n\n[Website](https://events.geekle.us/react3/) - [LinkedIn](https://www.linkedin.com/posts/geekle-us_event-react-reactjs-activity-6964904611207864320-gpDx?utm_source=share&utm_medium=member_desktop)\n\n### Remix Conf Europe 2022 {/*remix-conf-europe-2022*/}\nNovember 18, 2022, 7am PST / 10am EST / 4pm CET - remote event\n\n[Website](https://remixconf.eu/) - [Twitter](https://twitter.com/remixconfeu) - [Videos](https://portal.gitnation.org/events/remix-conf-europe-2022)\n\n### React Advanced 2022 {/*react-advanced-2022*/}\nOctober 21 & 25, 2022. In-person in London, UK + remote (hybrid event)\n\n[Website](https://www.reactadvanced.com/) - [Twitter](https://twitter.com/ReactAdvanced) - [Facebook](https://www.facebook.com/ReactAdvanced) - [Videos](https://portal.gitnation.org/events/react-advanced-conference-2022)\n\n### ReactJS Day 2022 {/*reactjs-day-2022*/}\nOctober 21, 2022 in Verona, Italy\n\n[Website](https://2022.reactjsday.it/) - [Twitter](https://twitter.com/reactjsday) - [LinkedIn](https://www.linkedin.com/company/grusp/) - [Facebook](https://www.facebook.com/reactjsday/) - [Videos](https://www.youtube.com/c/grusp)\n\n### React Brussels 2022 {/*react-brussels-2022*/}\nOctober 14, 2022. In-person in Brussels, Belgium + remote (hybrid event)\n\n[Website](https://www.react.brussels/) - [Twitter](https://twitter.com/BrusselsReact) - [LinkedIn](https://www.linkedin.com/events/6938421827153088512/) - [Facebook](https://www.facebook.com/events/1289080838167252/) - [Videos](https://www.youtube.com/channel/UCvES7lMpnx-t934qGxD4w4g)\n\n### React Alicante 2022 {/*react-alicante-2022*/}\nSeptember 29 - October 1, 2022. In-person in Alicante, Spain + remote (hybrid event)\n\n[Website](https://reactalicante.es/) - [Twitter](https://twitter.com/reactalicante) - [Facebook](https://www.facebook.com/ReactAlicante) - [Videos](https://www.youtube.com/channel/UCaSdUaITU1Cz6PvC97A7e0w)\n### React India 2022 {/*react-india-2022*/}\nSeptember 22 - 24, 2022. In-person in Goa, India + remote (hybrid event)\n\n[Website](https://www.reactindia.io) - [Twitter](https://twitter.com/react_india) - [Facebook](https://www.facebook.com/ReactJSIndia) - [Videos](https://www.youtube.com/channel/UCaFbHCBkPvVv1bWs_jwYt3w)\n\n### React Finland 2022 {/*react-finland-2022*/}\nSeptember 12 - 16, 2022. In-person in Helsinki, Finland\n\n[Website](https://react-finland.fi/) - [Twitter](https://twitter.com/ReactFinland) - [Schedule](https://react-finland.fi/schedule/) - [Speakers](https://react-finland.fi/speakers/)\n\n### React Native EU 2022: Powered by callstack {/*react-native-eu-2022-powered-by-callstack*/}\nSeptember 1-2, 2022 - Remote event\n\n[Website](https://www.react-native.eu/?utm_campaign=React_Native_EU&utm_source=referral&utm_content=reactjs_community_conferences) -\n[Twitter](https://twitter.com/react_native_eu) -\n[Linkedin](https://www.linkedin.com/showcase/react-native-eu) -\n[Facebook](https://www.facebook.com/reactnativeeu/) -\n[Instagram](https://www.instagram.com/reactnative_eu/)\n\n### ReactNext 2022 {/*reactnext-2022*/}\nJune 28, 2022. Tel-Aviv, Israel\n\n[Website](https://react-next.com) - [Twitter](https://twitter.com/ReactNext) - [Videos](https://www.youtube.com/c/ReactNext)\n\n### React Norway 2022 {/*react-norway-2022*/}\nJune 24, 2022. In-person at Farris Bad Hotel in Larvik, Norway and online (hybrid event).\n\n[Website](https://reactnorway.com/) - [Twitter](https://twitter.com/ReactNorway)\n\n### React Summit 2022 {/*react-summit-2022*/}\nJune 17 & 21, 2022. In-person in Amsterdam, Netherlands + remote first interactivity (hybrid event)\n\n[Website](https://reactsummit.com) - [Twitter](https://twitter.com/reactsummit) - [Facebook](https://www.facebook.com/reactamsterdam) - [Videos](https://portal.gitnation.org/events/react-summit-2022)\n\n### App.js Conf 2022 {/*appjs-conf-2022*/}\nJune 8 - 10, 2022. In-person in Kraków, Poland + remote\n\n[Website](https://appjs.co) - [Twitter](https://twitter.com/appjsconf)\n\n### React Day Bangalore 2022 {/*react-day-bangalore-2022*/}\nJune 8 - 9, 2022.  Remote\n\n[Website](https://reactday.in/) - [Twitter](https://twitter.com/ReactDayIn) - [Linkedin](https://www.linkedin.com/company/react-day/) - [YouTube](https://www.youtube.com/reactify_in)\n\n### render(ATL) 2022 🍑 {/*renderatl-2022-*/}\nJune 1 - 4, 2022. Atlanta, GA, USA\n\n[Website](https://renderatl.com) - [Discord](https://www.renderatl.com/discord) - [Twitter](https://twitter.com/renderATL) - [Instagram](https://www.instagram.com/renderatl/) - [Facebook](https://www.facebook.com/renderatl/) - [LinkedIn](https://www.linkedin.com/company/renderatl) - [Podcast](https://www.renderatl.com/culture-and-code#/)\n\n### RemixConf 2022 {/*remixconf-2022*/}\nMay 24 - 25, 2022. Salt Lake City, UT\n\n[Website](https://remix.run/conf/2022) - [Twitter](https://twitter.com/remix_run) - [YouTube](https://www.youtube.com/playlist?list=PLXoynULbYuEC36XutMMWEuTu9uuh171wx)\n\n### Reactathon 2022 {/*reactathon-2022*/}\nMay 3 - 5, 2022. Berkeley, CA\n\n[Website](https://reactathon.com) - [Twitter](https://twitter.com/reactathon) -[YouTube](https://www.youtube.com/watch?v=-YG5cljNXIA)\n\n### React Global Online Summit 2022 by Geekle {/*react-global-online-summit-2022-by-geekle*/}\nApril 20 - 21, 2022 - Online Summit\n\n[Website](https://events.geekle.us/react2/) - [LinkedIn](https://www.linkedin.com/events/reactglobalonlinesummit-226887417664541614081/)\n\n### React Miami 2022 🌴 {/*react-miami-2022-*/}\nApril 18 - 19, 2022. Miami, Florida\n[Website](https://www.reactmiami.com/)\n\n### React Live 2022 {/*react-live-2022*/}\nApril 1, 2022. Amsterdam, The Netherlands\n\n[Website](https://www.reactlive.nl/) - [Twitter](https://twitter.com/reactlivenl)\n\n### AgentConf 2022 {/*agentconf-2022*/}\n\nJanuary 27 - 30, 2022. In-person in Dornbirn and Lech Austria\n\n[Website](https://agent.sh/) - [Twitter](https://twitter.com/AgentConf) - [Instagram](https://www.instagram.com/teamagent/)\n\n### React Conf 2021 {/*react-conf-2021*/}\nDecember 8, 2021 - remote event (replay event on December 9)\n\n[Website](https://conf.reactjs.org/)\n\n### ReactEurope 2021 {/*reacteurope-2021*/}\nDecember 9-10, 2021 - remote event\n\n[Videos](https://www.youtube.com/c/ReacteuropeOrgConf)\n\n### ReactNext 2021 {/*reactnext-2021*/}\nDecember 15, 2021. Tel-Aviv, Israel\n\n[Website](https://react-next.com) - [Twitter](https://twitter.com/ReactNext) - [Videos](https://www.youtube.com/channel/UC3BT8hh3yTTYxbLQy_wbk2w)\n\n### React India 2021 {/*react-india-2021*/}\nNovember 12-13, 2021 - remote event\n\n[Website](https://www.reactindia.io) - [Twitter](https://twitter.com/react_india) - [Facebook](https://www.facebook.com/ReactJSIndia/) - [LinkedIn](https://www.linkedin.com/showcase/14545585) - [YouTube](https://www.youtube.com/channel/UCaFbHCBkPvVv1bWs_jwYt3w/videos)\n\n### React Global by Geekle {/*react-global-by-geekle*/}\nNovember 3-4, 2021 - remote event\n\n[Website](https://geekle.us/react) - [LinkedIn](https://www.linkedin.com/events/javascriptglobalsummit6721691514176720896/) - [YouTube](https://www.youtube.com/watch?v=0HhWIvPhbu0)\n\n### React Advanced 2021 {/*react-advanced-2021*/}\nOctober 22-23, 2021. In-person in London, UK + remote (hybrid event)\n\n[Website](https://reactadvanced.com) - [Twitter](https://twitter.com/reactadvanced) - [Facebook](https://www.facebook.com/ReactAdvanced) - [Videos](https://youtube.com/c/ReactConferences)\n\n### React Conf Brasil 2021 {/*react-conf-brasil-2021*/}\nOctober 16, 2021 - remote event\n\n[Website](http://reactconf.com.br) - [Twitter](https://twitter.com/reactconfbr) - [Slack](https://react.now.sh) - [Facebook](https://facebook.com/reactconf) - [Instagram](https://instagram.com/reactconfbr) - [YouTube](https://www.youtube.com/channel/UCJL5eorStQfC0x1iiWhvqPA/videos)\n\n### React Brussels 2021 {/*react-brussels-2021*/}\nOctober 15, 2021 - remote event\n\n[Website](https://www.react.brussels/) - [Twitter](https://twitter.com/BrusselsReact) - [LinkedIn](https://www.linkedin.com/events/6805708233819336704/)\n\n### render(ATL) 2021 {/*renderatl-2021*/}\nSeptember 13-15, 2021. Atlanta, GA, USA\n\n[Website](https://renderatl.com) - [Twitter](https://twitter.com/renderATL) - [Instagram](https://www.instagram.com/renderatl/) - [Facebook](https://www.facebook.com/renderatl/) - [LinkedIn](https://www.linkedin.com/company/renderatl)\n\n### React Native EU 2021 {/*react-native-eu-2021*/}\nSeptember 1-2, 2021 - remote event\n\n[Website](https://www.react-native.eu/) - [Twitter](https://twitter.com/react_native_eu) - [Facebook](https://www.facebook.com/reactnativeeu/) - [Instagram](https://www.instagram.com/reactnative_eu/)\n\n### React Finland 2021 {/*react-finland-2021*/}\nAugust 30 - September 3, 2021 - remote event\n\n[Website](https://react-finland.fi/) - [Twitter](https://twitter.com/ReactFinland) - [LinkedIn](https://www.linkedin.com/company/react-finland/)\n\n### React Case Study Festival 2021 {/*react-case-study-festival-2021*/}\nApril 27-28, 2021 - remote event\n\n[Website](https://link.geekle.us/react/offsite) - [LinkedIn](https://www.linkedin.com/events/reactcasestudyfestival6721300943411015680/) - [Facebook](https://www.facebook.com/events/255715435820203)\n\n### React Summit - Remote Edition 2021 {/*react-summit---remote-edition-2021*/}\nApril 14-16, 2021, 7am PST / 10am EST / 4pm CEST - remote event\n\n[Website](https://remote.reactsummit.com) - [Twitter](https://twitter.com/reactsummit) - [Facebook](https://www.facebook.com/reactamsterdam) - [Videos](https://portal.gitnation.org/events/react-summit-remote-edition-2021)\n\n### React fwdays’21 {/*react-fwdays21*/}\nMarch 27, 2021 - remote event\n\n[Website](https://fwdays.com/en/event/react-fwdays-2021) - [Twitter](https://twitter.com/fwdays) - [Facebook](https://www.facebook.com/events/1133828147054286) - [LinkedIn](https://www.linkedin.com/events/reactfwdays-21onlineconference6758046347334582273) - [Meetup](https://www.meetup.com/ru-RU/Fwdays/events/275764431/)\n\n### React Next 2020 {/*react-next-2020*/}\nDecember 1-2, 2020 - remote event\n\n[Website](https://react-next.com/) - [Twitter](https://twitter.com/reactnext) - [Facebook](https://www.facebook.com/ReactNext2016/)\n\n### React Conf Brasil 2020 {/*react-conf-brasil-2020*/}\nNovember 21, 2020 - remote event\n\n[Website](https://reactconf.com.br/) - [Twitter](https://twitter.com/reactconfbr) - [Slack](https://react.now.sh/)\n\n### React Summit 2020 {/*react-summit-2020*/}\nOctober 15-16, 2020, 7am PST / 10am EST / 4pm CEST - remote event\n\n[Website](https://reactsummit.com) - [Twitter](https://twitter.com/reactsummit) - [Facebook](https://www.facebook.com/reactamsterdam) - [Videos](https://youtube.com/c/ReactConferences)\n\n### React Native EU 2020 {/*react-native-eu-2020*/}\nSeptember 3-4, 2020 - remote event\n\n[Website](https://www.react-native.eu/) - [Twitter](https://twitter.com/react_native_eu) - [Facebook](https://www.facebook.com/reactnativeeu/) - [YouTube](https://www.youtube.com/watch?v=m0GfmlGFh3E&list=PLZ3MwD-soTTHy9_88QPLF8DEJkvoB5Tl-) - [Instagram](https://www.instagram.com/reactnative_eu/)\n\n### ReactEurope 2020 {/*reacteurope-2020*/}\nMay 14-15, 2020 in Paris, France\n\n[Videos](https://www.youtube.com/c/ReacteuropeOrgConf)\n\n### Byteconf React 2020 {/*byteconf-react-2020*/}\nMay 1, 2020. Streamed online on YouTube.\n\n[Website](https://www.bytesized.xyz) - [Twitter](https://twitter.com/bytesizedcode) - [YouTube](https://www.youtube.com/channel/UC046lFvJZhiwSRWsoH8SFjg)\n\n### React Summit - Remote Edition 2020 {/*react-summit---remote-edition-2020*/}\n3pm CEST time, April 17, 2020 - remote event\n\n[Website](https://remote.reactsummit.com) - [Twitter](https://twitter.com/reactsummit) - [Facebook](https://www.facebook.com/reactamsterdam) - [Videos](https://youtube.com/c/ReactConferences)\n\n### Reactathon 2020 {/*reactathon-2020*/}\nMarch 30 - 31, 2020 in San Francisco, CA\n\n[Website](https://www.reactathon.com) - [Twitter](https://twitter.com/reactathon) - [Facebook](https://www.facebook.com/events/575942819854160/)\n\n### ReactConf AU 2020 {/*reactconf-au-2020*/}\nFebruary 27 & 28, 2020 in Sydney, Australia\n\n[Website](https://reactconfau.com/) - [Twitter](https://twitter.com/reactconfau) - [Facebook](https://www.facebook.com/reactconfau) - [Instagram](https://www.instagram.com/reactconfau/)\n\n### React Barcamp Cologne 2020 {/*react-barcamp-cologne-2020*/}\nFebruary 1-2, 2020 in Cologne, Germany\n\n[Website](https://react-barcamp.de/) - [Twitter](https://twitter.com/ReactBarcamp) - [Facebook](https://www.facebook.com/reactbarcamp)\n\n### React Day Berlin 2019 {/*react-day-berlin-2019*/}\nDecember 6, 2019 in Berlin, Germany\n\n[Website](https://reactday.berlin) - [Twitter](https://twitter.com/reactdayberlin) - [Facebook](https://www.facebook.com/reactdayberlin/) - [Videos](https://www.youtube.com/reactdayberlin)\n\n### React Summit 2019 {/*react-summit-2019*/}\nNovember 30, 2019 in Lagos, Nigeria\n\n[Website](https://reactsummit2019.splashthat.com) -[Twitter](https://twitter.com/react_summit)\n\n### React Conf Brasil 2019 {/*react-conf-brasil-2019*/}\nOctober 19, 2019 in São Paulo, BR\n\n[Website](https://reactconf.com.br/) - [Twitter](https://twitter.com/reactconfbr) - [Facebook](https://www.facebook.com/ReactAdvanced) - [Slack](https://react.now.sh/)\n\n### React Advanced 2019 {/*react-advanced-2019*/}\nOctober 25, 2019 in London, UK\n\n[Website](https://reactadvanced.com) - [Twitter](http://twitter.com/reactadvanced) - [Facebook](https://www.facebook.com/ReactAdvanced) - [Videos](https://youtube.com/c/ReactConferences)\n\n### React Conf 2019 {/*react-conf-2019*/}\nOctober 24-25, 2019 in Henderson, Nevada USA\n\n[Website](https://conf.reactjs.org/) - [Twitter](https://twitter.com/reactjs)\n\n### React Alicante 2019 {/*react-alicante-2019*/}\nSeptember 26-28, 2019 in Alicante, Spain\n\n[Website](http://reactalicante.es/) - [Twitter](https://twitter.com/reactalicante) - [Facebook](https://www.facebook.com/ReactAlicante)\n\n### React India 2019 {/*react-india-2019*/}\nSeptember 26-28, 2019 in Goa, India\n\n[Website](https://www.reactindia.io/) - [Twitter](https://twitter.com/react_india) - [Facebook](https://www.facebook.com/ReactJSIndia)\n\n### React Boston 2019 {/*react-boston-2019*/}\nSeptember 21-22, 2019 in Boston, Massachusetts USA\n\n[Website](https://www.reactboston.com/) - [Twitter](https://twitter.com/reactboston)\n\n### React Live 2019 {/*react-live-2019*/}\nSeptember 13th, 2019. Amsterdam, The Netherlands\n\n[Website](https://www.reactlive.nl/) - [Twitter](https://twitter.com/reactlivenl)\n\n### React New York 2019 {/*react-new-york-2019*/}\nSeptember 13th, 2019. New York, USA\n\n[Website](https://reactnewyork.com/) - [Twitter](https://twitter.com/reactnewyork)\n\n### ComponentsConf 2019 {/*componentsconf-2019*/}\nSeptember 6, 2019 in Melbourne, Australia\n\n[Website](https://www.componentsconf.com.au/) - [Twitter](https://twitter.com/componentsconf)\n\n### React Native EU 2019 {/*react-native-eu-2019*/}\nSeptember 5-6 in Wrocław, Poland\n\n[Website](https://react-native.eu) - [Twitter](https://twitter.com/react_native_eu) - [Facebook](https://www.facebook.com/reactnativeeu)\n\n### React Conf Iran 2019 {/*react-conf-iran-2019*/}\nAugust 29, 2019. Tehran, Iran.\n\n[Website](https://reactconf.ir/) - [Videos](https://www.youtube.com/playlist?list=PL-VNqZFI5Nf-Nsj0rD3CWXGPkH-DI_0VY) - [Highlights](https://github.com/ReactConf/react-conf-highlights)\n\n### React Rally 2019 {/*react-rally-2019*/}\nAugust 22-23, 2019. Salt Lake City, USA.\n\n[Website](https://www.reactrally.com/) - [Twitter](https://twitter.com/ReactRally) - [Instagram](https://www.instagram.com/reactrally/)\n\n### Chain React 2019 {/*chain-react-2019*/}\nJuly 11-12, 2019. Portland, OR, USA.\n\n[Website](https://infinite.red/ChainReactConf)\n\n### React Loop 2019 {/*react-loop-2019*/}\nJune 21, 2019 Chicago, Illinois USA\n\n[Website](https://reactloop.com) - [Twitter](https://twitter.com/ReactLoop)\n\n### React Norway 2019 {/*react-norway-2019*/}\nJune 12, 2019. Larvik, Norway\n\n[Website](https://reactnorway.com) - [Twitter](https://twitter.com/ReactNorway)\n\n### ReactNext 2019 {/*reactnext-2019*/}\nJune 11, 2019. Tel Aviv, Israel\n\n[Website](https://react-next.com) - [Twitter](https://twitter.com/ReactNext) - [Videos](https://www.youtube.com/channel/UC3BT8hh3yTTYxbLQy_wbk2w)\n\n### React Conf Armenia 2019 {/*react-conf-armenia-2019*/}\nMay 25, 2019 in Yerevan, Armenia\n\n[Website](https://reactconf.am/) - [Twitter](https://twitter.com/ReactConfAM) - [Facebook](https://www.facebook.com/reactconf.am/) - [YouTube](https://www.youtube.com/c/JavaScriptConferenceArmenia) - [CFP](http://bit.ly/speakReact)\n\n### ReactEurope 2019 {/*reacteurope-2019*/}\nMay 23-24, 2019 in Paris, France\n\n[Videos](https://www.youtube.com/c/ReacteuropeOrgConf)\n\n### React.NotAConf 2019 {/*reactnotaconf-2019*/}\nMay 11 in Sofia, Bulgaria\n\n[Website](http://react-not-a-conf.com/) - [Twitter](https://twitter.com/reactnotaconf) - [Facebook](https://www.facebook.com/events/780891358936156)\n\n### ReactJS Girls Conference {/*reactjs-girls-conference*/}\nMay 3, 2019 in London, UK\n\n[Website](https://reactjsgirls.com/) - [Twitter](https://twitter.com/reactjsgirls)\n\n### React Finland 2019 {/*react-finland-2019*/}\nApril 24-26 in Helsinki, Finland\n\n[Website](https://react-finland.fi/) - [Twitter](https://twitter.com/ReactFinland)\n\n### React Amsterdam 2019 {/*react-amsterdam-2019*/}\nApril 12, 2019 in Amsterdam, The Netherlands\n\n[Website](https://reactsummit.com) - [Twitter](https://twitter.com/reactsummit) - [Facebook](https://www.facebook.com/reactamsterdam) - [Videos](https://youtube.com/c/ReactConferences)\n\n### App.js Conf 2019 {/*appjs-conf-2019*/}\nApril 4-5, 2019 in Kraków, Poland\n\n[Website](https://appjs.co) - [Twitter](https://twitter.com/appjsconf)\n\n### Reactathon 2019 {/*reactathon-2019*/}\nMarch 30-31, 2019 in San Francisco, USA\n\n[Website](https://www.reactathon.com/) - [Twitter](https://twitter.com/reactathon)\n\n### React Iran 2019 {/*react-iran-2019*/}\nJanuary 31, 2019 in Tehran, Iran\n\n[Website](http://reactiran.com) - [Instagram](https://www.instagram.com/reactiran/)\n\n### React Day Berlin 2018 {/*react-day-berlin-2018*/}\nNovember 30, Berlin, Germany\n\n[Website](https://reactday.berlin) - [Twitter](https://twitter.com/reactdayberlin) - [Facebook](https://www.facebook.com/reactdayberlin/) - [Videos](https://www.youtube.com/channel/UC1EYHmQYBUJjkmL6OtK4rlw)\n\n### ReactNext 2018 {/*reactnext-2018*/}\nNovember 4 in Tel Aviv, Israel\n\n[Website](https://react-next.com) - [Twitter](https://twitter.com/ReactNext) - [Facebook](https://facebook.com/ReactNext2016)\n\n### React Conf 2018 {/*react-conf-2018*/}\nOctober 25-26 in Henderson, Nevada USA\n\n[Website](https://conf.reactjs.org/)\n\n### React Conf Brasil 2018 {/*react-conf-brasil-2018*/}\nOctober 20 in Sao Paulo, Brazil\n\n[Website](http://reactconfbr.com.br) - [Twitter](https://twitter.com/reactconfbr) - [Facebook](https://www.facebook.com/reactconf)\n\n### ReactJS Day 2018 {/*reactjs-day-2018*/}\nOctober 5 in Verona, Italy\n\n[Website](http://2018.reactjsday.it) - [Twitter](https://twitter.com/reactjsday)\n\n### React Boston 2018 {/*react-boston-2018*/}\nSeptember 29-30 in Boston, Massachusetts USA\n\n[Website](http://www.reactboston.com/) - [Twitter](https://twitter.com/ReactBoston)\n\n### React Alicante 2018 {/*react-alicante-2018*/}\nSeptember 13-15 in Alicante, Spain\n\n[Website](http://reactalicante.es) - [Twitter](https://twitter.com/ReactAlicante)\n\n### React Native EU 2018 {/*react-native-eu-2018*/}\nSeptember 5-6 in Wrocław, Poland\n\n[Website](https://react-native.eu) - [Twitter](https://twitter.com/react_native_eu) - [Facebook](https://www.facebook.com/reactnativeeu)\n\n### Byteconf React 2018 {/*byteconf-react-2018*/}\nAugust 31 streamed online, via Twitch\n\n[Website](https://byteconf.com) - [Twitch](https://twitch.tv/byteconf) - [Twitter](https://twitter.com/byteconf)\n\n### ReactFoo Delhi {/*reactfoo-delhi*/}\nAugust 18 in Delhi, India\n\n[Website](https://reactfoo.in/2018-delhi/) - [Twitter](https://twitter.com/reactfoo) - [Past talks](https://hasgeek.tv)\n\n### React DEV Conf China {/*react-dev-conf-china*/}\nAugust 18 in Guangzhou, China\n\n[Website](https://react.w3ctech.com)\n\n### React Rally 2018 {/*react-rally-2018*/}\nAugust 16-17 in Salt Lake City, Utah USA\n\n[Website](http://www.reactrally.com) - [Twitter](https://twitter.com/reactrally)\n\n### Chain React 2018 {/*chain-react-2018*/}\nJuly 11-13 in Portland, Oregon USA\n\n[Website](https://infinite.red/ChainReactConf) - [Twitter](https://twitter.com/chainreactconf)\n\n### ReactFoo Mumbai {/*reactfoo-mumbai*/}\nMay 26 in Mumbai, India\n\n[Website](https://reactfoo.in/2018-mumbai/) - [Twitter](https://twitter.com/reactfoo) - [Past talks](https://hasgeek.tv)\n\n\n### ReactEurope 2018 {/*reacteurope-2018*/}\nMay 17-18 in Paris, France\n\n[Videos](https://www.youtube.com/c/ReacteuropeOrgConf)\n\n### React.NotAConf 2018 {/*reactnotaconf-2018*/}\nApril 28 in Sofia, Bulgaria\n\n[Website](http://react-not-a-conf.com/) - [Twitter](https://twitter.com/reactnotaconf) - [Facebook](https://www.facebook.com/groups/1614950305478021/)\n\n### React Finland 2018 {/*react-finland-2018*/}\nApril 24-26 in Helsinki, Finland\n\n[Website](https://react-finland.fi/) - [Twitter](https://twitter.com/ReactFinland)\n\n### React Amsterdam 2018 {/*react-amsterdam-2018*/}\nApril 13 in Amsterdam, The Netherlands\n\n[Website](https://reactsummit.com) - [Twitter](https://twitter.com/reactsummit) - [Facebook](https://www.facebook.com/reactamsterdam)\n\n### React Native Camp UA 2018 {/*react-native-camp-ua-2018*/}\nMarch 31 in Kiev, Ukraine\n\n[Website](http://reactnative.com.ua/) - [Twitter](https://twitter.com/reactnativecamp) - [Facebook](https://www.facebook.com/reactnativecamp/)\n\n### Reactathon 2018 {/*reactathon-2018*/}\nMarch 20-22 in San Francisco, USA\n\n[Website](https://www.reactathon.com/) - [Twitter](https://twitter.com/reactathon) - [Videos (fundamentals)](https://www.youtube.com/watch?v=knn364bssQU&list=PLRvKvw42Rc7OWK5s-YGGFSmByDzzgC0HP), [Videos (advanced day1)](https://www.youtube.com/watch?v=57hmk4GvJpk&list=PLRvKvw42Rc7N0QpX2Rc5CdrqGuxzwD_0H), [Videos (advanced day2)](https://www.youtube.com/watch?v=1hvQ8p8q0a0&list=PLRvKvw42Rc7Ne46QAjWNWFo1Jf0mQdnIW)\n\n### ReactFest 2018 {/*reactfest-2018*/}\nMarch 8-9 in London, UK\n\n[Website](https://reactfest.uk/) - [Twitter](https://twitter.com/ReactFest) - [Videos](https://www.youtube.com/watch?v=YOCrJ5vRCnw&list=PLRgweB8YtNRt-Sf-A0y446wTJNUaAAmle)\n\n### AgentConf 2018 {/*agentconf-2018*/}\nJanuary 25-28 in Dornbirn, Austria\n\n[Website](http://agent.sh/)\n\n### ReactFoo Pune {/*reactfoo-pune*/}\nJanuary 19-20, Pune, India\n\n[Website](https://reactfoo.in/2018-pune/) - [Twitter](https://twitter.com/ReactFoo)\n\n### React Day Berlin 2017 {/*react-day-berlin-2017*/}\nDecember 2, Berlin, Germany\n\n[Website](https://reactday.berlin) - [Twitter](https://twitter.com/reactdayberlin) - [Facebook](https://www.facebook.com/reactdayberlin/) - [Videos](https://www.youtube.com/watch?v=UnNLJvHKfSY&list=PL-3BrJ5CiIx5GoXci54-VsrO6GwLhSHEK)\n\n### React Seoul 2017 {/*react-seoul-2017*/}\nNovember 4 in Seoul, South Korea\n\n[Website](http://seoul.reactjs.kr/en)\n\n### ReactiveConf 2017 {/*reactiveconf-2017*/}\nOctober 25–27, Bratislava, Slovakia\n\n[Website](https://reactiveconf.com) - [Videos](https://www.youtube.com/watch?v=BOKxSFB2hOE&list=PLa2ZZ09WYepMB-I7AiDjDYR8TjO8uoNjs)\n\n### React Summit 2017 {/*react-summit-2017*/}\nOctober 21 in Lagos, Nigeria\n\n[Website](https://reactsummit2017.splashthat.com/) - [Twitter](https://twitter.com/DevCircleLagos/) - [Facebook](https://www.facebook.com/groups/DevCLagos/)\n\n### State.js Conference 2017 {/*statejs-conference-2017*/}\nOctober 13 in Stockholm, Sweden\n\n[Website](https://statejs.com/)\n\n### React Conf Brasil 2017 {/*react-conf-brasil-2017*/}\nOctober 7 in Sao Paulo, Brazil\n\n[Website](http://reactconfbr.com.br) - [Twitter](https://twitter.com/reactconfbr) - [Facebook](https://www.facebook.com/reactconf/)\n\n### ReactJS Day 2017 {/*reactjs-day-2017*/}\nOctober 6 in Verona, Italy\n\n[Website](http://2017.reactjsday.it) - [Twitter](https://twitter.com/reactjsday) - [Videos](https://www.youtube.com/watch?v=bUqqJPIgjNU&list=PLWK9j6ps_unl293VhhN4RYMCISxye3xH9)\n\n### React Alicante 2017 {/*react-alicante-2017*/}\nSeptember 28-30 in Alicante, Spain\n\n[Website](http://reactalicante.es) - [Twitter](https://twitter.com/ReactAlicante) - [Videos](https://www.youtube.com/watch?v=UMZvRCWo6Dw&list=PLd7nkr8mN0sWvBH_s0foCE6eZTX8BmLUM)\n\n### React Boston 2017 {/*react-boston-2017*/}\nSeptember 23-24 in Boston, Massachusetts USA\n\n[Website](http://www.reactboston.com/) - [Twitter](https://twitter.com/ReactBoston) - [Videos](https://www.youtube.com/watch?v=2iPE5l3cl_s&list=PL-fCkV3wv4ub8zJMIhmrrLcQqSR5XPlIT)\n\n### ReactFoo 2017 {/*reactfoo-2017*/}\nSeptember 14 in Bangalore, India\n\n[Website](https://reactfoo.in/2017/) - [Videos](https://www.youtube.com/watch?v=3G6tMg29Wnw&list=PL279M8GbNsespKKm1L0NAzYLO6gU5LvfH)\n\n### ReactNext 2017 {/*reactnext-2017*/}\nSeptember 8-10 in Tel Aviv, Israel\n\n[Website](http://react-next.com/) - [Twitter](https://twitter.com/ReactNext) - [Videos (Hall A)](https://www.youtube.com/watch?v=eKXQw5kR86c&list=PLMYVq3z1QxSqq6D7jxVdqttOX7H_Brq8Z), [Videos (Hall B)](https://www.youtube.com/watch?v=1InokWxYGnE&list=PLMYVq3z1QxSqCZmaqgTXLsrcJ8mZmBF7T)\n\n### React Native EU 2017 {/*react-native-eu-2017*/}\nSeptember 6-7 in Wroclaw, Poland\n\n[Website](http://react-native.eu/) - [Videos](https://www.youtube.com/watch?v=453oKJAqfy0&list=PLzUKC1ci01h_hkn7_KoFA-Au0DXLAQZR7)\n\n### React Rally 2017 {/*react-rally-2017*/}\nAugust 24-25 in Salt Lake City, Utah USA\n\n[Website](http://www.reactrally.com) - [Twitter](https://twitter.com/reactrally) - [Videos](https://www.youtube.com/watch?v=f4KnHNCZcH4&list=PLUD4kD-wL_zZUhvAIHJjueJDPr6qHvkni)\n\n### Chain React 2017 {/*chain-react-2017*/}\nJuly 10-11 in Portland, Oregon USA\n\n[Website](https://infinite.red/ChainReactConf) - [Twitter](https://twitter.com/chainreactconf) - [Videos](https://www.youtube.com/watch?v=cz5BzwgATpc&list=PLFHvL21g9bk3RxJ1Ut5nR_uTZFVOxu522)\n\n### ReactEurope 2017 {/*reacteurope-2017*/}\nMay 18th & 19th in Paris, France\n\n[Videos](https://www.youtube.com/c/ReacteuropeOrgConf)\n\n### React Amsterdam 2017 {/*react-amsterdam-2017*/}\nApril 21st in Amsterdam, The Netherlands\n\n[Website](https://reactsummit.com) - [Twitter](https://twitter.com/reactsummit) - [Videos](https://youtube.com/c/ReactConferences)\n\n### React London 2017 {/*react-london-2017*/}\nMarch 28th at the [QEII Centre, London](http://qeiicentre.london/)\n\n[Website](http://react.london/) - [Videos](https://www.youtube.com/watch?v=2j9rSur_mnk&list=PLW6ORi0XZU0CFjdoYeC0f5QReBG-NeNKJ)\n\n### React Conf 2017 {/*react-conf-2017*/}\nMarch 13-14 in Santa Clara, CA\n\n[Website](http://conf.reactjs.org/) - [Videos](https://www.youtube.com/watch?v=7HSd1sk07uU&list=PLb0IAmt7-GS3fZ46IGFirdqKTIxlws7e0)\n\n### Agent Conference 2017 {/*agent-conference-2017*/}\nJanuary 20-21 in Dornbirn, Austria\n\n[Website](http://agent.sh/)\n\n### React Remote Conf 2016 {/*react-remote-conf-2016*/}\nOctober 26-28 online\n\n[Website](https://allremoteconfs.com/react-2016) - [Schedule](https://allremoteconfs.com/react-2016#schedule)\n\n### Reactive 2016 {/*reactive-2016*/}\nOctober 26-28 in Bratislava, Slovakia\n\n[Website](https://reactiveconf.com/)\n\n### ReactNL 2016 {/*reactnl-2016*/}\nOctober 13 in Amsterdam, The Netherlands\n\n[Website](http://reactnl.org/) - [Schedule](http://reactnl.org/#program)\n\n### ReactNext 2016 {/*reactnext-2016*/}\nSeptember 15 in Tel Aviv, Israel\n\n[Website](http://react-next.com/) - [Schedule](http://react-next.com/#schedule) - [Videos](https://www.youtube.com/channel/UC3BT8hh3yTTYxbLQy_wbk2w)\n\n### ReactRally 2016 {/*reactrally-2016*/}\nAugust 25-26 in Salt Lake City, UT\n\n[Website](http://www.reactrally.com/) - [Schedule](http://www.reactrally.com/#/schedule) - [Videos](https://www.youtube.com/playlist?list=PLUD4kD-wL_zYSfU3tIYsb4WqfFQzO_EjQ)\n\n### ReactEurope 2016 {/*reacteurope-2016*/}\nJune 2 & 3 in Paris, France\n\n[Videos](https://www.youtube.com/c/ReacteuropeOrgConf)\n\n### React Amsterdam 2016 {/*react-amsterdam-2016*/}\nApril 16 in Amsterdam, The Netherlands\n\n[Website](https://reactsummit.com) - [Twitter](https://twitter.com/reactsummit) - [Facebook](https://www.facebook.com/reactamsterdam) - [Videos](https://youtube.com/c/ReactConferences)\n\n### React.js Conf 2016 {/*reactjs-conf-2016*/}\nFebruary 22 & 23 in San Francisco, CA\n\n[Website](http://conf2016.reactjs.org/) - [Schedule](http://conf2016.reactjs.org/schedule.html) - [Videos](https://www.youtube.com/playlist?list=PLb0IAmt7-GS0M8Q95RIc2lOM6nc77q1IY)\n\n### Reactive 2015 {/*reactive-2015*/}\nNovember 2-4 in Bratislava, Slovakia\n\n[Website](https://reactive2015.com/) - [Schedule](https://reactive2015.com/schedule_speakers.html#schedule)\n\n### ReactEurope 2015 {/*reacteurope-2015*/}\nJuly 2 & 3 in Paris, France\n\n[Videos](https://www.youtube.com/c/ReacteuropeOrgConf)\n\n### React.js Conf 2015 {/*reactjs-conf-2015*/}\nJanuary 28 & 29 in Facebook HQ, CA\n\n[Website](http://conf2015.reactjs.org/) - [Schedule](http://conf2015.reactjs.org/schedule.html) - [Videos](https://www.youtube.com/playlist?list=PLb0IAmt7-GS1cbw4qonlQztYV1TAW0sCr)\n"
  },
  {
    "path": "src/content/community/docs-contributors.md",
    "content": "---\ntitle: ドキュメント貢献者\n---\n\n<Intro>\n\nReact ドキュメントは [React チーム](/community/team)および[外部貢献者](https://github.com/reactjs/react.dev/graphs/contributors)によって書かれています。このページでは本サイトに特に顕著な貢献をしていただいた方を紹介します。\n\n</Intro>\n\n## Content {/*content*/}\n\n* [Rachel Nabors](https://twitter.com/RachelNabors): editing, writing, illustrating\n* [Dan Abramov](https://bsky.app/profile/danabra.mov): writing, curriculum design\n* [Sylwia Vargas](https://twitter.com/SylwiaVargas): example code\n* [Rick Hanlon](https://twitter.com/rickhanlonii): writing\n* [David McCabe](https://twitter.com/mcc_abe): writing\n* [Sophie Alpert](https://twitter.com/sophiebits): writing\n* [Pete Hunt](https://twitter.com/floydophone): writing\n* [Andrew Clark](https://twitter.com/acdlite): writing\n* [Matt Carroll](https://twitter.com/mattcarrollcode): editing, writing\n* [Natalia Tepluhina](https://twitter.com/n_tepluhina): reviews, advice\n* [Sebastian Markbåge](https://twitter.com/sebmarkbage): feedback\n\n## Design {/*design*/}\n\n* [Dan Lebowitz](https://twitter.com/lebo): site design\n* [Razvan Gradinar](https://dribbble.com/GradinarRazvan): sandbox design\n* [Maggie Appleton](https://maggieappleton.com/): diagram system\n* [Sophie Alpert](https://twitter.com/sophiebits): color-coded explanations\n\n## Development {/*development*/}\n\n* [Jared Palmer](https://twitter.com/jaredpalmer): site development\n* [ThisDotLabs](https://www.thisdot.co/) ([Dane Grant](https://twitter.com/danecando), [Dustin Goodman](https://twitter.com/dustinsgoodman)): site development\n* [CodeSandbox](https://codesandbox.io/) ([Ives van Hoorne](https://twitter.com/CompuIves), [Alex Moldovan](https://twitter.com/alexnmoldovan), [Jasper De Moor](https://twitter.com/JasperDeMoor), [Danilo Woznica](https://twitter.com/danilowoz)): sandbox integration\n* [Dan Abramov](https://bsky.app/profile/danabra.mov): site development\n* [Rick Hanlon](https://twitter.com/rickhanlonii): site development\n* [Harish Kumar](https://www.strek.in/): development and maintenance\n* [Luna Ruan](https://twitter.com/lunaruan): sandbox improvements\n\nまたアルファ版テスタの皆様やフィードバックを寄せていただいたコミュニティメンバにも感謝します。"
  },
  {
    "path": "src/content/community/index.md",
    "content": "---\ntitle: React コミュニティ\n---\n\n<Intro>\n\nReact には何百万人もの開発者のコミュニティが存在します。このページでは、あなたが参加できる React 関連のコミュニティを紹介しています。オンラインや個別での学習資料については、このセクションの他のページをご覧ください。\n\n</Intro>\n\n## 行動規範 {/*code-of-conduct*/}\n\nReact のコミュニティに参加する前に、[私たちの行動規範 (Code of Conduct) をお読みください](https://github.com/facebook/react/blob/main/CODE_OF_CONDUCT.md)。私たちは [Contributor Covenant](https://www.contributor-covenant.org/) を採用しており、すべてのコミュニティメンバが記載されているガイドラインを遵守することを期待しています。\n\n## Stack Overflow {/*stack-overflow*/}\n\nStack Overflow は、コードレベルの質問をしたい場合や、特定のエラーで困っている場合に人気のフォーラムです。**reactjs** タグの付いた[既存の質問](https://stackoverflow.com/questions/tagged/reactjs)を読んだり、[自分で質問](https://stackoverflow.com/questions/ask?tags=reactjs)したりしてみてください！\n\n## 人気のディスカッションフォーラム {/*popular-discussion-forums*/}\n\nベストプラクティスやアプリケーションアーキテクチャ、さらには React の未来についての議論に最適なオンラインフォーラムはたくさんあります。コードレベルで答えられる質問の場合は、通常 Stack Overflow がより適しています。\n\n各コミュニティは何千人もの React ユーザで構成されています。\n\n* [DEV の React コミュニティ](https://dev.to/t/react)\n* [Hashnode の React コミュニティ](https://hashnode.com/n/reactjs)\n* [Reactiflux オンラインチャット](https://discord.gg/reactiflux)\n* [Reddit の React コミュニティ](https://www.reddit.com/r/reactjs/)\n\n## ニュース {/*news*/}\n\nReact の最新ニュースについては、[Twitter の **@reactjs**](https://twitter.com/reactjs)、[Bluesky の **@react.dev**](https://bsky.app/profile/react.dev) や、このウェブサイトの[公式 React ブログ](/blog/)をフォローしてください。\n"
  },
  {
    "path": "src/content/community/meetups.md",
    "content": "---\ntitle: React ミーティング\n---\n\n<Intro>\n\n各国で開催されている定期ミーティングをご存じの場合は、ここに追加してください（アルファベット順にしてください）。\n\n</Intro>\n\n## Albania {/*albania*/}\n* [Tirana](https://www.meetup.com/React-User-Group-Albania/)\n\n## Argentina {/*argentina*/}\n* [Buenos Aires](https://www.meetup.com/es/React-en-Buenos-Aires)\n* [Rosario](https://www.meetup.com/es/reactrosario)\n\n## Australia {/*australia*/}\n* [Brisbane](https://www.meetup.com/reactbris/)\n* [Melbourne](https://www.meetup.com/React-Melbourne/)\n* [Sydney](https://www.meetup.com/React-Sydney/)\n\n## Austria {/*austria*/}\n* [Vienna](https://www.meetup.com/Vienna-ReactJS-Meetup/)\n\n## Belgium {/*belgium*/}\n* [Belgium](https://www.meetup.com/ReactJS-Belgium/)\n\n## Brazil {/*brazil*/}\n* [Belo Horizonte](https://www.meetup.com/reactbh/)\n* [Curitiba](https://www.meetup.com/pt-br/ReactJS-CWB/)\n* [Florianópolis](https://www.meetup.com/pt-br/ReactJS-Floripa/)\n* [Joinville](https://www.meetup.com/pt-BR/React-Joinville/)\n* [São Paulo](https://www.meetup.com/pt-BR/ReactJS-SP/)\n\n## Bolivia {/*bolivia*/}\n* [Bolivia](https://www.meetup.com/ReactBolivia/)\n\n## Canada {/*canada*/}\n* [Halifax, NS](https://www.meetup.com/Halifax-ReactJS-Meetup/)\n* [Montreal, QC](https://guild.host/react-montreal/)\n* [Vancouver, BC](https://www.meetup.com/ReactJS-Vancouver-Meetup/)\n* [Ottawa, ON](https://www.meetup.com/Ottawa-ReactJS-Meetup/)\n* [Saskatoon, SK](https://www.meetup.com/saskatoon-react-meetup/)\n* [Toronto, ON](https://www.meetup.com/Toronto-React-Native/events/)\n\n## Colombia {/*colombia*/}\n* [Medellin](https://www.meetup.com/React-Medellin/)\n\n## Czechia {/*czechia*/}\n* [Prague](https://guild.host/react-prague/)\n\n## Denmark {/*denmark*/}\n* [Aalborg](https://www.meetup.com/Aalborg-React-React-Native-Meetup/)\n* [Aarhus](https://www.meetup.com/Aarhus-ReactJS-Meetup/)\n\n## England (UK) {/*england-uk*/}\n* [Manchester](https://www.meetup.com/Manchester-React-User-Group/)\n* [React.JS Girls London](https://www.meetup.com/ReactJS-Girls-London/)\n* [React Advanced London](https://guild.host/react-advanced-london)\n* [React Native Liverpool](https://www.meetup.com/react-native-liverpool/)\n* [React Native London](https://guild.host/RNLDN)\n\n## Finland {/*finland*/}\n* [Helsinki](https://www.meetabit.com/communities/react-helsinki)\n\n## France {/*france*/}\n* [Lille](https://www.meetup.com/ReactBeerLille/)\n* [Paris](https://www.meetup.com/ReactJS-Paris/)\n\n## Germany {/*germany*/}\n* [Cologne](https://www.meetup.com/React-Cologne/)\n* [Düsseldorf](https://www.meetup.com/de-DE/ReactJS-Meetup-Dusseldorf/)\n* [Hamburg](https://www.meetup.com/Hamburg-React-js-Meetup/)\n* [Karlsruhe](https://www.meetup.com/react_ka/)\n* [Kiel](https://www.meetup.com/Kiel-React-Native-Meetup/)\n* [Munich](https://www.meetup.com/ReactJS-Meetup-Munich/)\n* [React Berlin](https://guild.host/react-berlin)\n\n## Greece {/*greece*/}\n* [Athens](https://www.meetup.com/React-To-React-Athens-MeetUp/)\n* [Thessaloniki](https://www.meetup.com/Thessaloniki-ReactJS-Meetup/)\n\n## India {/*india*/}\n* [Ahmedabad](https://reactahmedabad.dev/)\n* [Bangalore (React)](https://www.meetup.com/ReactJS-Bangalore/)\n* [Bangalore (React Native)](https://www.meetup.com/React-Native-Bangalore-Meetup)\n* [Chennai](https://www.linkedin.com/company/chennaireact)\n* [Delhi NCR](https://www.meetup.com/React-Delhi-NCR/)\n* [Mumbai](https://reactmumbai.dev)\n* [Pune](https://www.meetup.com/ReactJS-and-Friends/)\n* [Rajasthan](https://reactrajasthan.com)\n\n## Indonesia {/*indonesia*/}\n* [Indonesia](https://www.meetup.com/reactindonesia/)\n\n## Ireland {/*ireland*/}\n* [Dublin](https://guild.host/reactjs-dublin)\n\n## Israel {/*israel*/}\n* [Tel Aviv](https://www.meetup.com/ReactJS-Israel/)\n\n## Italy {/*italy*/}\n* [Milan](https://www.meetup.com/React-JS-Milano/)\n\n## Japan {/*japan*/}\n* [Osaka](https://react-osaka.connpass.com/)\n\n## Kenya {/*kenya*/}\n* [Nairobi - Reactdevske](https://kommunity.com/reactjs-developer-community-kenya-reactdevske)\n\n## Malaysia {/*malaysia*/}\n* [Kuala Lumpur](https://www.kl-react.com/)\n* [Penang](https://www.facebook.com/groups/reactpenang/)\n\n## Netherlands {/*netherlands*/}\n* [Amsterdam](https://guild.host/react-amsterdam)\n\n## New Zealand {/*new-zealand*/}\n* [Wellington](https://www.meetup.com/React-Wellington/)\n\n## Norway {/*norway*/}\n* [Norway](https://reactjs-norway.webflow.io/)\n* [Oslo](https://www.meetup.com/ReactJS-Oslo-Meetup/)\n\n## Pakistan {/*pakistan*/}\n* [Karachi](https://www.facebook.com/groups/902678696597634/)\n* [Lahore](https://www.facebook.com/groups/ReactjsLahore/)\n\n## Philippines {/*philippines*/}\n* [Manila](https://www.meetup.com/reactjs-developers-manila/)\n* [Manila - ReactJS PH](https://www.meetup.com/ReactJS-Philippines/)\n\n## Poland {/*poland*/}\n* [Warsaw](https://www.meetup.com/React-js-Warsaw/)\n* [Wrocław](https://www.meetup.com/ReactJS-Wroclaw/)\n\n## Portugal {/*portugal*/}\n* [Lisbon](https://www.meetup.com/JavaScript-Lisbon/)\n\n## Scotland (UK) {/*scotland-uk*/}\n* [Edinburgh](https://www.meetup.com/react-edinburgh/)\n\n## Spain {/*spain*/}\n* [Barcelona](https://www.meetup.com/ReactJS-Barcelona/)\n\n## Sri Lanka {/*sri-lanka*/}\n* [Colombo](https://www.javascriptcolombo.com/)\n\n## Sweden {/*sweden*/}\n* [Goteborg](https://www.meetup.com/ReactJS-Goteborg/)\n* [Stockholm](https://www.meetup.com/Stockholm-ReactJS-Meetup/)\n\n## Switzerland {/*switzerland*/}\n* [Zurich](https://www.meetup.com/Zurich-ReactJS-Meetup/)\n\n## Turkey {/*turkey*/}\n* [Istanbul](https://kommunity.com/reactjs-istanbul)\n\n## Ukraine {/*ukraine*/}\n* [Kyiv](https://www.meetup.com/Kyiv-ReactJS-Meetup)\n\n## US {/*us*/}\n* [Atlanta, GA - ReactJS](https://www.meetup.com/React-ATL/)\n* [Austin, TX - ReactJS](https://www.meetup.com/ReactJS-Austin-Meetup/)\n* [Boston, MA - ReactJS](https://www.meetup.com/ReactJS-Boston/)\n* [Boston, MA - React Native](https://www.meetup.com/Boston-React-Native-Meetup/)\n* [Charlotte, NC - ReactJS](https://www.meetup.com/ReactJS-Charlotte/)\n* [Charlotte, NC - React Native](https://www.meetup.com/cltreactnative/)\n* [Chicago, IL - ReactJS](https://www.meetup.com/React-Chicago/)\n* [Cleveland, OH - ReactJS](https://www.meetup.com/Cleveland-React/)\n* [Columbus, OH - ReactJS](https://www.meetup.com/ReactJS-Columbus-meetup/)\n* [Dallas, TX - ReactJS](https://www.meetup.com/ReactDallas/)\n* [Denver, CO - React Denver](https://reactdenver.com/)\n* [Detroit, MI - Detroit React User Group](https://www.meetup.com/Detroit-React-User-Group/)\n* [Indianapolis, IN - React.Indy](https://www.meetup.com/React-Indy)\n* [Irvine, CA - ReactJS](https://www.meetup.com/ReactJS-OC/)\n* [Kansas City, MO - ReactJS](https://www.meetup.com/Kansas-City-React-Meetup/)\n* [Las Vegas, NV - ReactJS](https://www.meetup.com/ReactVegas/)\n* [Leesburg, VA - ReactJS](https://www.meetup.com/React-NOVA/)\n* [Los Angeles, CA - ReactJS](https://www.meetup.com/socal-react/)\n* [Los Angeles, CA - React Native](https://www.meetup.com/React-Native-Los-Angeles/)\n* [Miami, FL - ReactJS](https://www.meetup.com/React-Miami/)\n* [New York, NY - ReactJS](https://www.meetup.com/NYC-Javascript-React-Group/)\n* [New York, NY - React Ladies](https://www.meetup.com/React-Ladies/)\n* [New York, NY - React Native](https://www.meetup.com/React-Native-NYC/)\n* [New York, NY - useReactNYC](https://www.meetup.com/useReactNYC/)\n* [New York, NY - React.NYC](https://guild.host/react-nyc)\n* [Palo Alto, CA - React Native](https://www.meetup.com/React-Native-Silicon-Valley/)\n* [Phoenix, AZ - ReactJS](https://www.meetup.com/ReactJS-Phoenix/)\n* [Provo, UT - ReactJS](https://www.meetup.com/ReactJS-Utah/)\n* [San Diego, CA - San Diego JS](https://www.meetup.com/sandiegojs/)\n* [San Francisco - Real World React](https://www.meetup.com/Real-World-React)\n* [San Francisco - ReactJS](https://www.meetup.com/ReactJS-San-Francisco/)\n* [San Francisco, CA - React Native](https://www.meetup.com/React-Native-San-Francisco/)\n* [Santa Monica, CA - ReactJS](https://www.meetup.com/Los-Angeles-ReactJS-User-Group/)\n* [Seattle, WA - ReactJS](https://www.meetup.com/seattle-react-js/)\n* [Tampa, FL - ReactJS](https://www.meetup.com/ReactJS-Tampa-Bay/)\n* [Tucson, AZ - ReactJS](https://www.meetup.com/Tucson-ReactJS-Meetup/)\n* [Washington, DC - ReactJS](https://www.meetup.com/React-DC/)\n"
  },
  {
    "path": "src/content/community/team.md",
    "content": "---\ntitle: \"チーム紹介\"\n---\n\n<Intro>\n\nReact の開発を主導しているのは Meta でフルタイムで働く専門チームです。また、世界中の人々からの貢献も受けています。\n\n</Intro>\n\n## React コアチーム {/*react-core*/}\n\nReact コアチームのメンバーは、コアコンポーネントの API、React DOM と React Native を動かすエンジン、React DevTools、そして React のドキュメンテーションサイトにフルタイムで取り組んでいます。\n\n現在の React チームのメンバーを以下にアルファベット順で紹介します。\n\n<TeamMember name=\"Andrew Clark\" permalink=\"andrew-clark\" photo=\"/images/team/acdlite.jpg\" github=\"acdlite\" twitter=\"acdlite\" threads=\"acdlite\" title=\"Engineer at Vercel\">\n    Andrew は WordPress を使ったサイト作りからウェブ開発を始め、いつの間にか JavaScript にハマっていました。お気に入りの暇つぶしはカラオケです。日によってある時はディズニーの悪役に、またある時はディズニーのお姫様になっています。\n</TeamMember>\n\n<TeamMember name=\"Dan Abramov\" permalink=\"dan-abramov\" photo=\"/images/team/gaearon.jpg\" github=\"gaearon\" bsky=\"danabra.mov\" title=\"Independent Engineer\">\n    Dan は Microsoft PowerPoint の中に偶然 Visual Basic を発見したことからプログラミングを始めました。[Sebastian](#sebastian-markbåge) のツイートを長文のブログ投稿に翻訳することが真の使命であると感じています。Fortnite では、ゲームが終わるまで茂みの中に隠れて勝利することがあります。\n</TeamMember>\n\n<TeamMember name=\"Eli White\" permalink=\"eli-white\" photo=\"/images/team/eli-white.jpg\" github=\"elicwhite\" twitter=\"Eli_White\" threads=\"elicwhite\" title=\"Engineering Manager at Meta\">\n    Eli のプログラミング経験は、ハッキングで中学校を停学処分になったことで始まりました。2017 年から React と React Native の開発に携わっています。お菓子を食べるのが好きで、特にアイスクリームとアップルパイがお気に入りです。Eli はよくパルクールや室内スカイダイビング、エアリアルシルクなど、風変わりな活動を試しています。\n</TeamMember>\n\n<TeamMember name=\"Hendrik Liebau\" permalink=\"hendrik-liebau\" photo=\"/images/team/hendrik.jpg\" github=\"unstubbable\" bsky=\"unstubbable.bsky.social\" twitter=\"unstubbable\" title=\"Engineer at Vercel\">\n    Hendrik の技術者としての歩みは、90 年代後半に Netscape Communicator で初めてウェブサイトを構築したことから始まりました。コンピュータサイエンスの学位を取得し、デジタル製作会社で働いた後、React Server Components バンドラとライブラリを構築し、Next.js チームに加入しました。仕事以外での趣味はサイクリングと自宅の作業場でのもの作りです。\n</TeamMember>\n\n<TeamMember name=\"Jack Pope\" permalink=\"jack-pope\" photo=\"/images/team/jack-pope.jpg\" github=\"jackpope\" personal=\"jackpope.me\" title=\"Engineer at Meta\">\n    Jack は AutoHotkey を人に紹介されてから、思いつく限りの作業を自動化するスクリプトを書いてきました。そこで限界を感じてウェブアプリの開発に頭から飛び込んだ後は、それ一筋です。最近では Instagram のウェブプラットフォームに携わったのち、React チームにやってきました。好きなプログラミング言語は JSX です。\n</TeamMember>\n\n<TeamMember name=\"Jason Bonta\" permalink=\"jason-bonta\" photo=\"/images/team/jasonbonta.jpg\" threads=\"someextent\" title=\"Engineering Manager at Meta\">\n    Jason は、組み込み C 開発を完全に捨ててフロントエンドエンジニアとしてのキャリアを選びました。難解な CSS の知識と美しい UI への情熱を武器に 2010 年に Facebook に参加し、JavaScript 開発の成長を見届けることができたことを光栄に思っています。`for...of` ループの動作は理解していないかもしれませんが、素晴らしい人々と一緒に、素晴らしい UX を実現するプロジェクトに取り組むことを愛しています。\n</TeamMember>\n\n<TeamMember name=\"Joe Savona\" permalink=\"joe-savona\" photo=\"/images/team/joe.jpg\" github=\"josephsavona\" twitter=\"en_JS\" threads=\"joesavona\" title=\"Engineer at Meta\">\n    Joe は数学と哲学を専攻する予定でしたが、Matlab で物理シミュレーションを書いたことからコンピュータサイエンスに興味を持ちました。React に取り組む前は、Relay、RSocket.js、Skip プログラミング言語などに取り組んでいました。何かしらのリアクティブシステムを構築する傍らでは、ランニングをしたり、日本語を勉強したり、家族と過ごしたりしています。\n</TeamMember>\n\n<TeamMember name=\"Jordan Brown\" permalink=\"jordan-brown\" photo=\"/images/team/jordan.jpg\" github=\"jbrown215\" title=\"Engineer at Meta\">\n    Jordan のコーディングは、iPhone アプリの開発から始まり、for ループの意味を理解する前にビューコントローラーのスタック管理を行っていました。開発者に愛される技術に取り組むことが好きで、それが彼を自然と React に引き寄せました。仕事以外では、読書、カイトボード、ギター演奏を楽しんでいます。\n</TeamMember>\n\n<TeamMember name=\"Josh Story\" permalink=\"josh-story\" photo=\"/images/team/josh.jpg\" github=\"gnoff\" bsky=\"storyhb.com\" title=\"Engineer at Vercel\">\n    Josh は大学で数学を専攻し、そこでプログラミングに出会いました。プロ開発者としての最初の仕事は、リアクティブプログラミングのお手本たる Microsoft Excel で保険料計算プログラムを書くことであり、きっとそれが今 React に取り組んでいる理由なのでしょう。その間 Josh はいくつかのスタートアップで IC、マネージャー、エグゼクティブも務めてきました。仕事以外では、料理で自分の限界に挑戦することが好きです。\n</TeamMember>\n\n<TeamMember name=\"Lauren Tan\" permalink=\"lauren-tan\" photo=\"/images/team/lauren.jpg\" github=\"poteto\" twitter=\"potetotes\" threads=\"potetotes\" bsky=\"no.lol\" title=\"Engineer at Meta\">\n    Lauren のプログラミングキャリアは `<marquee>` タグを初めて見たときにピークを迎えました。それ以来、彼女はその時の高揚感を追い続けています。大学ではコンピュータサイエンスではなく経済学を学んでいたため、コーディングは Excel で学びました。Lauren はチャットでお茶目なミームを投下したり、パートナとゲームを楽しんだり、韓国語を学んだり、犬の Zelda を可愛がったりするのが好きです。\n</TeamMember>\n\n<TeamMember name=\"Matt Carroll\" permalink=\"matt-carroll\" photo=\"/images/team/matt-carroll.png\" github=\"mattcarrollcode\" twitter=\"mattcarrollcode\" threads=\"mattcarrollcode\" title=\"Developer Advocate at Meta\">\n    Matt は偶然コーディングに出会い、独りでは作り出せないようなものをコミュニティで作ることに夢中になりました。React に参加する前は、YouTube、Google アシスタント、Fuchsia、Google Cloud AI、そして Evernote に取り組んでいました。開発者ツールの改善を行う傍らで、山を楽しんだり、ジャズを聴いたり、家族と時間を過ごしたりしています。\n</TeamMember>\n\n<TeamMember name=\"Mike Vitousek\" permalink=\"mike-vitousek\" photo=\"/images/team/mike.jpg\" github=\"mvitousek\" title=\"Engineer at Meta\">\n    Mike は教授になることを夢見て大学院に進みましたが、研究費の応募書類を書いているよりも何かを開発することの方がより好きだと気付きました。Meta に入社して JavaScript インフラに携わり、最終的に React Compiler に取り組むことになりました。JavaScript か OCaml を触っていない時間は、太平洋岸北西部でハイキングやスキーを楽しんでいることが多いです。\n</TeamMember>\n\n<TeamMember name=\"Mofei Zhang\" permalink=\"mofei-zhang\" photo=\"/images/team/mofei-zhang.png\" github=\"mofeiZ\" threads=\"z_mofei\" title=\"Engineer at Meta\">\n    Mofei はゲームでチートを行うのに役立つと気づいたことでプログラミングを始めました。彼女は学部・大学院ではオペレーティングシステムを専門にしていましたが、今では React いじりを楽しんでいます。仕事の外では、ボルダリングの問題をデバッグすることや、次回のバックパック旅行の計画を楽しんでいます。\n</TeamMember>\n\n<TeamMember name=\"Pieter Vanderwerff\" permalink=\"pieter-vanderwerff\" photo=\"/images/team/pieter.jpg\" github=\"pieterv\" threads=\"pietervanderwerff\" title=\"Engineer at Meta\">\n    Pieter は建築学を学びましたが、就職できなかったため自分用のウェブサイトを作ったところ、そこから事態が発展していきました。Meta では、パフォーマンスや言語、そして現在は React の開発に取り組んでいます。プログラミングをしていない時は、道路から外れた山の中にいます。\n</TeamMember>\n\n<TeamMember name=\"Rick Hanlon\" permalink=\"rick-hanlon\" photo=\"/images/team/rickhanlonii.jpg\" github=\"rickhanlonii\" twitter=\"rickhanlonii\" threads=\"rickhanlonii\" bsky=\"ricky.fm\" title=\"Engineer at Meta\">\n    Ricky は理論数学を専攻していましたが、どういうわけか React Native チームで数年過ごしたあと、React チームにやってきました。プログラミングをしていないときは、スノーボード、自転車、クライミング、ゴルフを楽しんだり、テンプレートに合致しない GitHub の issue をクローズしたりしています。\n</TeamMember>\n\n<TeamMember name=\"Ruslan Lesiutin\" permalink=\"ruslan-lesiutin\" photo=\"/images/team/lesiutin.jpg\" github=\"hoxyq\" twitter=\"ruslanlesiutin\" threads=\"lesiutin\" title=\"Engineer at Meta\">\n    Ruslan の UI プログラミングの経験は、子供のころにゲーム掲示板で HTML テンプレートを手でカスタマイズしたのが最初でした。その後いろいろあってコンピュータサイエンスを専攻することとなりました。好きなものは音楽、ゲーム、ネットミームです。特にネットミームです。\n</TeamMember>\n\n<TeamMember name=\"Sebastian Markbåge\" permalink=\"sebastian-markbåge\" photo=\"/images/team/sebmarkbage.jpg\" github=\"sebmarkbage\" twitter=\"sebmarkbage\" threads=\"sebmarkbage\" title=\"Engineer at Vercel\">\n    Sebastian の専攻は心理学でした。普段の彼はもの静かです。彼が何かを言ったとしても、数か月後まで他の人には理解できないことがよくあります。彼の姓の発音は本来 \"mark-boa-geh\" ですが、実用性を優先して \"mark-beige\" に落ち着きました。彼の React へのアプローチも実用主義的です。\n</TeamMember>\n\n<TeamMember name=\"Sebastian Silbermann\" permalink=\"sebastian-silbermann\" photo=\"/images/team/sebsilbermann.jpg\" github=\"eps1lon\" twitter=\"sebsilbermann\" threads=\"sebsilbermann\" title=\"Engineer at Vercel\">\n    Sebastian は、授業中に遊んでいたブラウザゲームをより楽しくするためにプログラミングを学びました。それはいずれ、オープンソースコードに可能な限りの貢献をすることにつながりました。コーディング以外の時間では、彼は React コミュニティにいるほかの Sebastian や Zilberman と混同されないように頑張っています。\n</TeamMember>\n\n<TeamMember name=\"Seth Webster\" permalink=\"seth-webster\" photo=\"/images/team/seth.jpg\" github=\"sethwebster\" twitter=\"sethwebster\" threads=\"sethwebster\" personal=\"sethwebster.com\" title=\"Engineering Manager at Meta\">\n    Seth は、アリゾナ州ツーソンで育った子供時代にプログラミングを始めました。卒業後に彼は音楽の虫に噛まれ、約 10 年間ツアーミュージシャンとして活動した後、Intuit で「仕事」を始めました。余暇としては、[写真を撮る](https://www.sethwebster.com)ことと、アメリカ北東部の動物救助のために空を飛ぶことが大好きです。\n</TeamMember>\n\n<TeamMember name=\"Sophie Alpert\" permalink=\"sophie-alpert\" photo=\"/images/team/sophiebits.jpg\" github=\"sophiebits\" twitter=\"sophiebits\" threads=\"sophiebits\" personal=\"sophiebits.com\" title=\"Independent Engineer\">\n    Sophie は React がリリースされてから 4 日後、当時のプロジェクトを全部 React を使って書き直しました（今思えば少々無謀だったかもしれません）。彼女がプロジェクトのナンバーワンコミッタになった後、ほかの全員が Facebook から給料をもらっているのに自分だけがもらっていないのはなぜかと思い、成長期の React を主導するために正式にチームに参加しました。その仕事は数年前に辞めているのですが、なぜかまだチームのグループチャットで「価値を提供」しています。\n</TeamMember>\n\n<TeamMember name=\"Yuzhi Zheng\" permalink=\"yuzhi-zheng\" photo=\"/images/team/yuzhi.jpg\" github=\"yuzhi\" twitter=\"yuzhiz\" threads=\"yuzhiz\" title=\"Engineering Manager at Meta\">\n    Yuzhi は学校でコンピュータサイエンスを学びました。彼女は、実際に研究室に行かなくてもコードが生き生きと動く瞬間の喜びが好きでした。現在の彼女は React org のマネージャです。その前は、Relay のデータフェッチングフレームワークに取り組んでいました。余暇には、ガーデニングや家のリフォームを通じた生活の最適化にいそしんでいます。\n</TeamMember>\n\n## 過去の貢献者 {/*past-contributors*/}\n\n過去のチームメンバーや、過去長年にわたり React に大きく貢献した人々については、[謝辞](/community/acknowledgements)ページに掲載されています。\n"
  },
  {
    "path": "src/content/community/translations.md",
    "content": "---\ntitle: 翻訳\n---\n\n<Intro>\n\nReact ドキュメントはグローバルコミュニティによって世界中の多くの言語に翻訳されています。\n\n</Intro>\n\n## ソースサイト {/*main-site*/}\n\n翻訳は以下の元ドキュメントをベースに行われます。\n\n- [English](https://react.dev/) &mdash; [Contribute](https://github.com/reactjs/react.dev/)\n\n## 翻訳済み言語 {/*full-translations*/}\n\n{/* If you are a language maintainer and want to add your language here, finish the \"Core\" translations and edit `deployedTranslations` under `src/utils`. */}\n\n<LanguageList progress=\"complete\" />\n\n## 翻訳作業中の言語 {/*in-progress-translations*/}\n\n個々の言語の進行状況については [Is React Translated Yet?](https://translations.react.dev/) を参照してください。\n\n<LanguageList progress=\"in-progress\" />\n\n## 貢献の方法 {/*how-to-contribute*/}\n\nあなたも翻訳に参加できます！\n\nコミュニティでは、react.dev を各言語用にフォークしてそこで React ドキュメントの翻訳作業を行っています。典型的な翻訳作業は、Markdown ファイルを直接編集してプルリクエストを作成するというものです。翻訳を手伝うには、上記の \"Contribute\" リンクをクリックしてあなたの言語の GitHub リポジトリに行き、そこにある指示に従ってください。\n\nご自身の言語に対する翻訳を新たに始めたい場合は [translations.react.dev](https://github.com/reactjs/translations.react.dev) を参照してください。"
  },
  {
    "path": "src/content/community/versioning-policy.md",
    "content": "---\ntitle: バージョニングポリシー\n---\n\n<Intro>\n\nReact のすべての安定版ビルドは広範なテストに通過しており、セマンティックバージョニング (semver) に従います。また、実験的機能に対する早期フィードバックを促すため、不安定版のリリースチャンネルも提供されています。このページでは、React の各リリースで期待できることについて説明します。\n\n</Intro>\n\nこのバージョニングポリシーでは `react` や `react-dom` のようなパッケージに対する我々の考え方を説明しています。過去のリリースの一覧は [Versions](/versions) ページをご覧ください。\n\n## 安定版リリース {/*stable-releases*/}\n\n安定版 (Stable) の React リリース（別名 \"Latest\" リリースチャンネル）は、[セマンティックバージョニング (semver)](https://semver.org/) の原則に従います。\n\nつまり、バージョン番号が **x.y.z** の場合：\n\n* **重大なバグ修正**をリリースする際、**z** の数値を変更して**パッチリリース**を行います（例：15.6.2 から 15.6.3）。\n* **新機能**や**重大ではない修正**をリリースする際、**y** の数値を変更して**マイナーリリース**を行います（例：15.6.2 から 15.7.0）。\n* **破壊的な変更**をリリースする際、**x** の数値を変更して**メジャーリリース**を行います（例：15.6.2 から 16.0.0）。\n\nメジャーリリースには新機能が含まれることもあり、任意のリリースにバグ修正が含まれることがあります。\n\n最も一般的なタイプのリリースはマイナーリリースです。\n\n私たちは、ユーザが本番環境で古いバージョンの React を引き続き使用していることを認識しています。React のセキュリティ脆弱性を発見した場合、その脆弱性の影響を受けるすべてのメジャーバージョンに対して、バックポートされた修正を提供します。\n\n### 破壊的な変更 {/*breaking-changes*/}\n\n破壊的な変更は誰にとっても煩わしいため、メジャーリリースの数は最小限に抑えるようにしています。例えば、React 15 は 2016 年 4 月にリリースされ、React 16 は 2017 年 9 月にリリースされ、React 17 は 2020 年 10 月にリリースされました。\n\n代わりに、新機能はマイナーバージョンでリリースします。つまり、マイナーリリースは控えめな名前をしているにもかかわらず、メジャーリリースよりも興味深く魅力的なことがあります。\n\n### 安定性へのコミットメント {/*commitment-to-stability*/}\n\nReact に変更を加える際は、新機能を利用するために必要な労力を最小限に抑えるよう努めています。可能な限り古い API を動作させ続けますが、別のパッケージに移動させることはありえます。例えば、[ミックスインは何年も前から推奨されていません](https://legacy.reactjs.org/blog/2016/07/13/mixins-considered-harmful.html)が、それらは今日まで [create-react-class を通じて](https://legacy.reactjs.org/docs/react-without-es6.html#mixins)サポートされており、多くのコードベースがそれらを安定したレガシーコードで使用し続けています。\n\n100 万人以上の開発者が React を使用しており、合わせれば何百万ものコンポーネントをメンテナンスしています。Facebook のコードベースだけでも、React のコンポーネントは 50,000 個以上存在します。だからこそ私たちは、新バージョンの React へのアップグレードをできるだけ簡単にする必要があるのです。もし私たちが移行手段なしに大きな変更を行えば、人々は古いバージョンから移行できなくなってしまいます。私たちはこれらのアップグレード手段を Facebook 自体でテストしています。もし 10 人もいない我々のチームだけで 50,000 以上のコンポーネントをアップデートできるのであれば、React を使っている誰にとってもそのアップグレードを実行可能であることが期待されます。多くの場合、私たちはコンポーネントの構文をアップグレードするための[自動化スクリプト](https://github.com/reactjs/react-codemod)を書き、それをオープンソースのリリースに含めて、誰でも使えるようにします。\n\n### 警告を通じた段階的アップグレード {/*gradual-upgrades-via-warnings*/}\n\nReact の開発用ビルドには多くの有用な警告が含まれています。可能な限り、我々は将来の破壊的な変更に備えて、あらかじめ警告の追加を行います。ですので、最新リリースであなたのアプリに警告がないなら、それは次のメジャーリリースと互換性があるということです。これにより、あなたはアプリのコンポーネントをひとつずつアップグレードすることができます。\n\n開発時の警告はアプリのランタイムの振る舞いに影響を与えません。そのため、アプリが開発用ビルドと本番用ビルドの間で同じように振る舞うことを確信することができます。唯一の違いは、本番用ビルドは警告をログに出力せず、より効率的に動作することです。（万一それ以外の違いを見つけた場合は問題を報告してください。）\n\n### 何が破壊的な変更とみなされるのか？ {/*what-counts-as-a-breaking-change*/}\n\n一般的に、我々は以下の変更についてはメジャーバージョン番号を*上げません*。\n\n* **開発時警告**。これらは本番環境での振る舞いに影響を与えないため、我々はメジャーバージョンの途中で新しい警告を追加したり、既存の警告を変更したりすることがあります。実際このおかげで、私たちは将来の破壊的な変更について確実な警告を行えるのです。\n* **`unstable_` で始まる API**。これらは私たちがまだ自信を持てていない実験的な機能として提供されています。これらを `unstable_` のプレフィックスでリリースすることで、より早くイテレーションを行い、より早く安定した API に到達することができます。\n* **React のアルファおよび Canary バージョン**。新しい機能を早期にテストするための方法として React のアルファバージョンを提供していますが、アルファ期間で学んだことに基づいて変更を加えるための柔軟性が必要です。これらのバージョンを使用する場合、API が安定リリース前に変更される可能性があることに注意してください。\n* **ドキュメント化されていない API や内部データ構造**。もし `__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED` や `__reactInternalInstance$uk43rzhitjg` のような内部プロパティ名にアクセスするなら、保証はありません。あなたの自己責任です。\n\nポリシーがこう設計されているのは実用上の理由です。これが頭痛の種になってほしくないのです。これらすべての変更に対してメジャーバージョンを上げていたのでは、メジャーバージョンのリリースが増えてしまい、結果的にコミュニティにとってバージョニングにまつわる痛みの増加に繋がります。また、私たちが思い通りに素早く React を改善できなくなってしまうことにもなるでしょう。\n\nとはいえ、上記のリストにあるような変更がコミュニティ全体で問題を引き起こすと予想される場合、私たちは段階的な移行パスを提供するために最善を尽くします。\n\n### 新機能のないリリースをパッチではなくマイナーバージョンでリリースする理由 {/*if-a-minor-release-includes-no-new-features-why-isnt-it-a-patch*/}\n\nマイナーリリースに新機能がないこともあります。[これは semver によって許可されています](https://semver.org/#spec-item-7)：**\"[a minor version] MAY be incremented if substantial new functionality or improvements are introduced within the private code. It MAY include patch level changes.\"**\n\nしかし、そのようなリリースをなぜパッチバージョンとしないのか疑問に感じるかもしれません。\n\n答えは、React（あるいはソフトウェア一般）に対するあらゆる変更は、予期しない形で壊れてしまうリスクを伴うからです。あるバグを修正するためのパッチリリースが、誤って別のバグを引き起こすところを想像してみてください。これは開発者にとって煩わしいというだけでなく、将来のパッチリリースに対する信頼を損なうことにもなります。元の修正が実際にはほとんど遭遇しないバグのためのものである場合、もたらされる結果は特に残念なものとなるでしょう。\n\nReact のリリースをバグのない状態に保つことに関しての私たちの実績はかなり良いものではあります。しかしパッチリリースに関しては問題なく採用できることをほとんどの開発者が前提としているため、信頼性に関するハードルはさらに高くなります。\n\nこれらの理由から、私たちは非常に重要なバグやセキュリティの脆弱性に対してのみ、パッチリリースを用います。\n\nリリースが、必須ではない変更を含む場合（内部のリファクタリング、実装の詳細の変更、パフォーマンスの改善、またはマイナーなバグ修正など）、新機能がなくともマイナーバージョンを上げます。\n\n## 全リリースチャンネル {/*all-release-channels*/}\n\nバグレポートの報告、プルリクエストの提出、[RFC の提出](https://github.com/reactjs/rfcs)などについて、React は活発なオープンソースコミュニティを頼りにしています。フィードバックを促すため、ときおり未リリースの機能を含む特別なビルドの React を共有することがあります。\n\n<Note>\n\nこのセクションの内容が最も関連するのは、フレームワーク、ライブラリ、開発者向けツールの開発者です。React を主にユーザ向けアプリケーションの構築に使用している開発者は、プレリリースチャンネルについて心配する必要はありません。\n\n</Note>\n\nReact のリリースチャンネルはそれぞれ異なるユースケースに対応するように設計されています。\n\n- [**Latest**](#latest-channel) は、安定した、semver に基づく React リリースのためのものです。npm から React をインストールすると得られるものです。これが、あなたが今日すでに使用しているチャンネルです。**React を直接使用するユーザ向けアプリケーションは、このチャンネルを使用します**。\n- [**Canary**](#canary-channel) は、React ソースコードリポジトリのメインブランチを追跡します。これは、次の semver リリースのためのリリース候補だと考えてください。**[フレームワークや他の統合済みセットアップは、React のバージョンを固定してこのチャンネルを使用することができます](/blog/2023/05/03/react-canaries)。また、React とサードパーティプロジェクト間の統合テストに Canary を使用することもできます**。\n- [**Experimental**](#experimental-channel) には、安定リリースでは利用できない実験的な API や機能が含まれています。これらもメインブランチを追跡しますが、追加の機能フラグがオンになっています。リリース前に将来の機能を試すにはこちらを利用します。\n\nすべてのリリースは npm に公開されますが、semver を使用するのは Latest だけです。プレリリース（Canary と Experimental チャンネルにあるもの）は、その内容のハッシュとコミット日から生成されたバージョンになります。例えば、Canary の場合は `18.3.0-canary-388686f29-20230503`、Experimental の場合は `0.0.0-experimental-388686f29-20230503` のようになります。\n\n**Latest と Canary のチャンネルのいずれも、ユーザ向けアプリケーションでの使用が公式にサポートされるものですが、期待できることが異なります**。\n\n* Latest リリースは伝統的な semver モデルに従います。\n* Canary リリースは[バージョンを固定する必要があり](/blog/2023/05/03/react-canaries)、破壊的な変更を含むことがあります。これらは、自身のリリーススケジュールで React の新機能やバグ修正を段階的にリリースしたい統合済みセットアップ（フレームワークなど）のために存在します。\n\nExperimental リリースはテスト目的のみに提供されており、リリース間での挙動が変わらないことを保証していません。Latest チャンネルのリリースで使用している semver 規約には従いません。\n\n安定版リリースと同じレジストリにプレリリースを公開することで、[unpkg](https://unpkg.com) や [CodeSandbox](https://codesandbox.io) など、npm ワークフローをサポートする多くのツールを活用できます。\n\n### Latest チャンネル {/*latest-channel*/}\n\nLatest は、安定した React リリースに使用されるチャンネルです。npm の `latest` タグに対応しています。実際のユーザに提供されるすべての React アプリケーションに対して推奨されるチャンネルです。\n\n**どのチャンネルを使用すべきかわからない場合は、Latest を使用してください**。すでに React を直接使用している場合、これがあなたの使用しているものです。Latest での更新は非常に安定していると期待できます。[先に説明した通り](#stable-releases)、バージョンはセマンティックバージョニングの仕組みに従います。\n\n### Canary チャンネル {/*canary-channel*/}\n\nCanary チャンネルは、React リポジトリのメインブランチを追跡するプレリリースチャンネルです。Canary チャンネルのプレリリースは、Latest チャンネルのリリース候補として使用します。Canary を、より頻繁に更新される Latest のスーパーセットと考えることができます。\n\n最新の Canary リリースと最新の Latest リリースの間の違いは、概ね semver における 2 つのマイナーリリース間の違いに対応します。ただし **Canary チャンネルはセマンティックバージョニングに準拠していません**。Canary チャンネルでは連続したリリース間でときおり破壊的な変更が発生することを覚悟してください。\n\n**[Canary のワークフロー](/blog/2023/05/03/react-canaries)に従うのでない限り、ユーザ向けのアプリケーションで直接プレリリースを使用しないでください**。\n\nCanary リリースは、npm の `canary` タグで公開されます。バージョンはビルドの内容のハッシュとコミット日から生成されます。例えば `18.3.0-canary-388686f29-20230503` のようになります。\n\n#### 統合テストのための Canary チャンネルの使用 {/*using-the-canary-channel-for-integration-testing*/}\n\nCanary チャンネルは、React と他のプロジェクト間の統合テストもサポートしています。\n\nReact へのすべての更新は、一般に公開される前に広範な内部テストを経ています。しかし、React エコシステム全体で使用されている環境やセットアップは無数にあるため、私たちがすべてをテストすることは不可能です。\n\nもしあなたがサードパーティの React フレームワーク、ライブラリ、開発者ツール、または類似のインフラストラクチャ系のプロジェクトの作者であるなら、定期的にテストスイートを最新の変更に対して実行することで、あなたのユーザとすべての React コミュニティに対して React を安定したものに保つ手助けをしていただけます。興味がある方は、以下の手順に従ってください。\n\n- お好みの継続的インテグレーションプラットフォームを使用して cron ジョブを設定します。cron ジョブは [CircleCI](https://circleci.com/docs/2.0/triggers/#scheduled-builds) と [Travis CI](https://docs.travis-ci.com/user/cron-jobs/) の両方でサポートされています。\n- cron ジョブ内では、npm の `canary` タグを使用して、React パッケージを  Canary チャンネルの最新の React リリースに更新します。npm cli を使用する場合：\n\n  ```console\n  npm update react@canary react-dom@canary\n  ```\n\n  または yarn を使用する場合：\n\n  ```console\n  yarn upgrade react@canary react-dom@canary\n  ```\n- 更新されたパッケージに対してテストスイートを実行します。\n- すべてのテストが通過した場合はおめでとうございます！ あなたのプロジェクトは次のマイナー React リリースでも動作すると期待できます。\n- 何か予期せぬ問題が発生した場合、[問題を報告](https://github.com/facebook/react/issues)してください。\n\nこのワークフローを採用しているプロジェクトのひとつが Next.js です。例として彼らの [CircleCI 設定](https://github.com/zeit/next.js/blob/c0a1c0f93966fe33edd93fb53e5fafb0dcd80a9e/.circleci/config.yml)を参照してください。\n\n### Experimental チャンネル {/*experimental-channel*/}\n\nCanary と同様に、Experimental チャンネルは React リポジトリのメインブランチを追跡するプレリリースチャンネルです。しかし、Canary とは異なり、Experimental リリースには、広範にリリースする準備が整っていない追加機能と API が含まれています。\n\n通常、Canary に更新がある場合、Experimental にも対応する更新が同時に行われます。これらは同じソースリビジョンに基づいていますが、異なる機能フラグセットを使用してビルドされています。\n\nExperimental リリースは、Canary や Latest へのリリースとは大きく異なる可能性があります。**ユーザ向けのアプリケーションで実験的なリリースを使用しないでください**。Experimental チャンネルのリリース間では、頻繁に破壊的変更が発生することを覚悟してください。\n\nExperimental のリリースは npm の `experimental` タグで公開されます。バージョンはビルド内容のハッシュとコミット日から生成され、例えば `0.0.0-experimental-68053d940-20210623` のようになります。\n\n#### Experimental リリースに含まれるもの {/*what-goes-into-an-experimental-release*/}\n\n実験的な機能とは、広く公開する準備が整っておらず、最終版に至るまでに大幅に変更される可能性があるものです。実験はあくまで提案された変更の実現可能性を評価するためのものですので、一部の実験は永遠に最終版にならない可能性もあります。\n\n例えば、フックを発表したときに Experimental チャンネルが存在していたなら、Latest で利用可能になる数週間前にフックを Experimental チャンネルにリリースしていたでしょう。\n\nExperimental に対して統合テストを実行することに価値があると感じるかもしれません。これはあなた次第です。ただし、Experimental は Canary よりも安定性が低いことをご了承ください。**Experimental リリース間の安定性は一切保証されません**。\n\n#### 実験的な機能について詳しく知るには？ {/*how-can-i-learn-more-about-experimental-features*/}\n\n実験的機能は文書化される場合もされない場合もあります。通常は、実験は Canary や Latest でリリースする直前まで文書化されません。\n\n機能が文書化されていない場合でも対応する [RFC](https://github.com/reactjs/rfcs) が存在する可能性はあります。\n\n新しい実験を発表する準備が整ったときには [React ブログ](/blog) に投稿しますが、すべての実験を公表するわけではありません。\n\n変更の包括的なリストは、公開 GitHub リポジトリの[履歴](https://github.com/facebook/react/commits/main)でいつでも参照できます。\n"
  },
  {
    "path": "src/content/community/videos.md",
    "content": "---\ntitle: React 関連動画\n---\n\n<Intro>\n\nReact および React のエコシステムについて説明している動画を紹介します。\n\n</Intro>\n\n## React Conf 2024 {/*react-conf-2024*/}\n\nReact Conf 2024 では、Meta CTO の [Andrew \"Boz\" Bosworth](https://www.threads.net/@boztank) が開会の挨拶を述べました。\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/T8TZQ6k4SLE?t=975s\" title=\"Boz and Seth Intro\" />\n\n### React 19 キーノート {/*react-19-keynote*/}\n\n初日のキーノートでは、React コンパイラと React 19 から始まる React のビジョンについて発表しました。[Joe Savona](https://twitter.com/en_JS)、[Lauren Tan](https://twitter.com/potetotes)、[Andrew Clark](https://twitter.com/acdlite)、[Josh Story](https://twitter.com/joshcstory)、[Sathya Gunasekaran](https://twitter.com/_gsathya)、[Mofei Zhang](https://twitter.com/zmofei) による完全なキーノートは以下で視聴できます：\n\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/lyEKhv8-3n0\" title=\"YouTube video player\" />\n\n### React Unpacked: React 19へのロードマップ {/*react-unpacked-a-roadmap-to-react-19*/}\n\nReact 19 では Actions、`use()`、`useOptimistic` など、新機能が導入されました。React 19 の新機能についてさらに詳しく知りたい方は、[Sam Selikoff](https://twitter.com/samselikoff) の講演をご覧ください：\n\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/R0B2HsSM78s\" title=\"React Unpacked: A Roadmap to React 19\" />\n\n### React 19の新機能 {/*whats-new-in-react-19*/}\n\n[Lydia Hallie](https://twitter.com/lydiahallie) が React 19 の新機能を視覚的に分かりやすく解説しました：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/AJOGzVygGcY\" title=\"What's New in React 19\" />\n\n### React 19 Deep Dive: HTML の協調 {/*react-19-deep-dive-coordinating-html*/}\n\n[Josh Story](https://twitter.com/joshcstory) が React 19 のドキュメントおよびリソースストリーミング API について詳細な解説を行いました：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/IBBN-s77YSI\" title=\"React 19 Deep Dive: Coordinating HTML\" />\n\n### React を2つのコンピュータで {/*react-for-two-computers*/}\n\n[Dan Abramov](https://bsky.app/profile/danabra.mov) は、もし React がサーバファーストで始まっていたらどうなっていたかを想像しました：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/ozI4V_29fj4\" title=\"React for Two Computers\" />\n\n### Memo を忘れよう {/*forget-about-memo*/}\n\n[Lauren Tan](https://twitter.com/potetotes) が、React Compiler を実践的に利用する方法について講演しました：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/lvhPq5chokM\" title=\"Forget About Memo\" />\n\n### React Compiler について詳しく {/*react-compiler-deep-dive*/}\n\n[Sathya Gunasekaran](https://twitter.com/_gsathya) と [Mofei Zhang](https://twitter.com/zmofei) が React Compiler がどのように動作するか詳しく解説しました：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/uA_PVyZP7AI\" title=\"React Compiler Deep Dive\" />\n\n### さらに… {/*and-more-2024*/}\n\n**コミュニティからサーバコンポーネントに関する講演について聞きました：**\n* [Enhancing Forms with React Server Components](https://www.youtube.com/embed/0ckOUBiuxVY&t=25280s) by [Aurora Walberg Scharff](https://twitter.com/aurorascharff)\n* [And Now You Understand React Server Components](https://www.youtube.com/embed/pOo7x8OiAec) by [Kent C. Dodds](https://twitter.com/kentcdodds)\n* [Real-time Server Components](https://www.youtube.com/embed/6sMANTHWtLM) by [Sunil Pai](https://twitter.com/threepointone)\n\n**React フレームワークの最新機能に関する講演：**\n\n* [Vanilla React](https://www.youtube.com/embed/ZcwA0xt8FlQ) by [Ryan Florence](https://twitter.com/ryanflorence)\n* [React Rhythm & Blues](https://www.youtube.com/embed/rs9X5MjvC4s) by [Lee Robinson](https://twitter.com/leeerob)\n* [RedwoodJS, now with React Server Components](https://www.youtube.com/embed/sjyY4MTECUU) by [Amy Dutton](https://twitter.com/selfteachme)\n* [Introducing Universal React Server Components in Expo Router](https://www.youtube.com/embed/djhEgxQf3Kw) by [Evan Bacon](https://twitter.com/Baconbrix)\n\n**React と React Native の開発チームによる Q&A セッション：**\n- [React Q&A](https://www.youtube.com/embed/T8TZQ6k4SLE&t=27518s) hosted by [Michael Chan](https://twitter.com/chantastic)\n- [React Native Q&A](https://www.youtube.com/embed/0ckOUBiuxVY&t=27935s) hosted by [Jamon Holmgren](https://twitter.com/jamonholmgren)\n\nReact Conf 2024 のすべての講演は [conf2024.react.dev](https://conf2024.react.dev/talks) から視聴できます。\n\n## React Conf 2021 {/*react-conf-2021*/}\n\n### React 18 キーノート {/*react-18-keynote*/}\n\nキーノートでは、React 18 から始まる React の将来のビジョンについて共有しました。\n\n[Andrew Clark](https://twitter.com/acdlite)、[Juan Tejada](https://twitter.com/_jstejada)、[Lauren Tan](https://twitter.com/potetotes)、[Rick Hanlon](https://twitter.com/rickhanlonii) による完全なキーノートは以下で視聴できます。\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/FZ0cG47msEk\" title=\"React 18 Keynote\" />\n\n### アプリ開発者にとっての React 18 {/*react-18-for-application-developers*/}\n\nReact 18 へのアップグレードのデモについて、[Shruti Kapoor](https://twitter.com/shrutikapoor08) のトークを以下でご覧ください。\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/ytudH8je5ko\" title=\"React 18 for Application Developers\" />\n\n### サスペンスを使ったストリーミングサーバレンダリング {/*streaming-server-rendering-with-suspense*/}\n\nReact 18 では、サスペンスを使用することでサーバサイドレンダリングのパフォーマンスも改善されています。\n\nストリーミングサーバレンダリングによって、サーバ側で React コンポーネントから HTML を作成し、それをユーザにストリームで送ることができます。React 18 では、`Suspense` を使ってアプリを小さな単位に分割し、それぞれがアプリの他の部分をブロックせずに独立してストリーミング処理できるようになります。これによりユーザはより早くコンテンツを見ることができ、素早くインタラクションができるようになる、ということです。\n\n詳しくは、[Shaundai Person](https://twitter.com/shaundai) による以下の発表をご覧ください：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/pj5N-Khihgc\" title=\"Streaming Server Rendering with Suspense\" />\n\n### React ワーキンググループの立ち上げ {/*the-first-react-working-group*/}\n\nReact 18 では、エキスパートや開発者、ライブラリメンテナ、教育者のグループと協力して作業するため、初めてワーキンググループを立ち上げました。彼らとともに、段階的な採用戦略を作成し、`useId`、`useSyncExternalStore`, `useInsertionEffect` といった API の改善を行ってきました。\n\nこの試みの概要については、[Aakansha' Doshi](https://twitter.com/aakansha1216) による発表をご覧ください。\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/qn7gRClrC9U\" title=\"The first React working group\" />\n\n### React 開発者向けツール {/*react-developer-tooling*/}\n\nこのリリースにおける新機能をサポートするため、新たに構成された React DevTools チームと、開発者が React アプリをデバッグしやすくするための新たなタイムラインプロファイラについて発表しました。\n\n新たな DevTools の機能についての詳細およびデモについては、[Brian Vaughn](https://twitter.com/brian_d_vaughn) による発表をご覧ください。\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/oxDfrke8rZg\" title=\"React Developer Tooling\" />\n\n### メモ化不要の React {/*react-without-memo*/}\n\nより将来に目を向けた話として、[Xuan Huang (黄玄)](https://twitter.com/Huxpro) は、React Labs が行っている自動メモ化コンパイラに関する研究の現状についてお話ししました。この発表とコンパイラのプロトタイプについての詳細・デモは以下でご覧ください。\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/lGEMwh32soc\" title=\"React without memo\" />\n\n### React ドキュメントキーノート {/*react-docs-keynote*/}\n\nReact の学習や React による設計についての一連の発表は [Rachel Nabors](https://twitter.com/rachelnabors) からスタートしました。その中では React の新ドキュメントに対する我々の注力についてのキーノートがありました（[react.dev としてリリース済み](/blog/2023/03/16/introducing-react-dev)）：\n\n<YouTubeIframe src=\"https://www.youtube.com/embed/mneDaMYOKP8\" title=\"React docs keynote\" />\n\n### さらに… {/*and-more*/}\n\n**React の学習や React における設計についての以下のような発表がありました：**\n\n* Debbie O'Brien: [Things I learnt from the new React docs](https://youtu.be/-7odLW_hG7s).\n* Sarah Rainsberger: [Learning in the Browser](https://youtu.be/5X-WEQflCL0).\n* Linton Ye: [The ROI of Designing with React](https://youtu.be/7cPWmID5XAk).\n* Delba de Oliveira: [Interactive playgrounds with React](https://youtu.be/zL8cz2W0z34).\n\n**Relay、React Native、PyTorch チームからの発表：**\n\n* Robert Balicki: [Re-introducing Relay](https://youtu.be/lhVGdErZuN4).\n* Eric Rozell and Steven Moyes: [React Native Desktop](https://youtu.be/9L4FFrvwJwY).\n* Roman Rädle: [On-device Machine Learning for React Native](https://youtu.be/NLj73vrc2I8)\n\n**アクセシビリティ、ツーリング、サーバコンポーネントについてコミュニティからの発表：**\n\n* Daishi Kato: [React 18 for External Store Libraries](https://youtu.be/oPfSC5bQPR8).\n* Diego Haz: [Building Accessible Components in React 18](https://youtu.be/dcm8fjBfro8).\n* Tafu Nakazaki: [Accessible Japanese Form Components with React](https://youtu.be/S4a0QlsH0pU).\n* Lyle Troxell: [UI tools for artists](https://youtu.be/b3l4WxipFsE).\n* Helen Lin: [Hydrogen + React 18](https://youtu.be/HS6vIYkSNks).\n\n## 過去の動画 {/*older-videos*/}\n\n### React Conf 2019 {/*react-conf-2019*/}\n\nReact Conf 2019 の動画プレイリストです。\n<YouTubeIframe title=\"React Conf 2019\" src=\"https://www.youtube-nocookie.com/embed/playlist?list=PLPxbbTqCLbGHPxZpw4xj_Wwg8-fdNxJRh\" />\n\n### React Conf 2018 {/*react-conf-2018*/}\n\nReact Conf 2018 の動画プレイリストです。\n<YouTubeIframe title=\"React Conf 2018\" src=\"https://www.youtube-nocookie.com/embed/playlist?list=PLPxbbTqCLbGE5AihOSExAa4wUM-P42EIJ\" />\n\n### React.js Conf 2017 {/*reactjs-conf-2017*/}\n\nReact.js Conf 2017 の動画プレイリストです。\n<YouTubeIframe title=\"React.js Conf 2017\" src=\"https://www.youtube-nocookie.com/embed/playlist?list=PLb0IAmt7-GS3fZ46IGFirdqKTIxlws7e0\" />\n\n### React.js Conf 2016 {/*reactjs-conf-2016*/}\n\nReact.js Conf 2016 の動画プレイリストです。\n<YouTubeIframe title=\"React.js Conf 2016\" src=\"https://www.youtube-nocookie.com/embed/playlist?list=PLb0IAmt7-GS0M8Q95RIc2lOM6nc77q1IY\" />\n\n### React.js Conf 2015 {/*reactjs-conf-2015*/}\n\nReact.js Conf 2015 の動画プレイリストです。\n<YouTubeIframe title=\"React.js Conf 2015\" src=\"https://www.youtube-nocookie.com/embed/playlist?list=PLb0IAmt7-GS1cbw4qonlQztYV1TAW0sCr\" />\n\n### ベストプラクティスの再考 {/*rethinking-best-practices*/}\n\nPete Hunt の JSConf EU 2013 でのトークは「テンプレートの概念を捨てて JavaScript でビューを構築する」「データが変更されたときにアプリケーション全体を \"再レンダー\" する」「DOM とイベントの軽量な実装」という 3 つのトピックをカバーしています（2013 年 - 30 分）。\n<YouTubeIframe title=\"Pete Hunt: React: Rethinking Best Practices - JSConf EU 2013\" src=\"https://www.youtube-nocookie.com/embed/x7cQ3mrcKaY\" />\n\n### React の初紹介 {/*introduction-to-react*/}\n\nTom Occhino と Jordan Walke が Facebook Seattle で React を紹介したときの動画です（2013 年 - 1 時間 20 分）。\n<YouTubeIframe title=\"Tom Occhino and Jordan Walke introduce React at Facebook Seattle\" src=\"https://www.youtube-nocookie.com/embed/XxVg_s8xAms\" />\n"
  },
  {
    "path": "src/content/errors/377.md",
    "content": "<Intro>\n\nIn the minified production build of React, we avoid sending down full error messages in order to reduce the number of bytes sent over the wire.\n\n</Intro>\n\nWe highly recommend using the development build locally when debugging your app since it tracks additional debug info and provides helpful warnings about potential problems in your apps, but if you encounter an exception while using the production build, this page will reassemble the original error message.\n\nThe full text of the error you just encountered is:\n\n<ErrorDecoder />\n\nThis error occurs when you pass a BigInt value from a Server Component to a Client Component.\n"
  },
  {
    "path": "src/content/errors/generic.md",
    "content": "<Intro>\n\nIn the minified production build of React, we avoid sending down full error messages in order to reduce the number of bytes sent over the wire.\n\n</Intro>\n\nWe highly recommend using the development build locally when debugging your app since it tracks additional debug info and provides helpful warnings about potential problems in your apps, but if you encounter an exception while using the production build, this page will reassemble the original error message.\n\nThe full text of the error you just encountered is:\n\n<ErrorDecoder />\n"
  },
  {
    "path": "src/content/errors/index.md",
    "content": "<Intro>\n\nIn the minified production build of React, we avoid sending down full error messages in order to reduce the number of bytes sent over the wire.\n\n</Intro>\n\n\nWe highly recommend using the development build locally when debugging your app since it tracks additional debug info and provides helpful warnings about potential problems in your apps, but if you encounter an exception while using the production build, the error message will include just a link to the docs for the error.\n\nFor an example, see: [https://react.dev/errors/149](/errors/149).\n"
  },
  {
    "path": "src/content/index.md",
    "content": "---\nid: home\ntitle: React\npermalink: index.html\n---\n\n{/* See HomeContent.js */}"
  },
  {
    "path": "src/content/learn/add-react-to-an-existing-project.md",
    "content": "---\ntitle: 既存プロジェクトに React を追加する\n---\n\n<Intro>\n\n既存のプロジェクトにインタラクティブな要素を加えたい場合、プロジェクトを React で書き直す必要はありません。React を既存のスタックに追加することで、どこにでもインタラクティブな React コンポーネントをレンダーできます。\n\n</Intro>\n\n<Note>\n\n**ローカル環境で開発するには [Node.js](https://nodejs.org/en/) をインストールする必要があります**。React を[オンライン](/learn/installation#try-react)や単純な HTML ページで試すことも可能ですが、現実的には開発時に利用する大抵の JavaScript ツールには Node.js が必要です。\n\n</Note>\n\n## 既存のウェブサイトの一部に React を使う {/*using-react-for-an-entire-subroute-of-your-existing-website*/}\n\n例えば Rails などの他のサーバテクノロジで構築されている `example.com` というウェブアプリがあり、`example.com/some-app/` から始まる全ルートを React で完全に実装したいとします。\n\n以下の手順に従って設定することをお勧めします。\n\n1. [React ベースのフレームワーク](/learn/creating-a-react-app)のうちひとつを使い、アプリの **React 部分をビルド**します。\n2. フレームワークの設定で `/some-app` を ***base path* に指定**します（方法：[Next.js](https://nextjs.org/docs/app/api-reference/config/next-config-js/basePath)、[Gatsby](https://www.gatsbyjs.com/docs/how-to/previews-deploys-hosting/path-prefix/)）。\n3. **サーバまたはプロキシを設定**して、`/some-app/` 以下のすべてのリクエストを React アプリで処理するようにします。\n\nこうすることで、アプリの React 部分がこれらのフレームワークに組み込まれた[ベストプラクティスを最大限に取り入れる](/learn/build-a-react-app-from-scratch#consider-using-a-framework)ことができます。\n\n多くの React ベースのフレームワークはフルスタックであり、React アプリがサーバ機能を活用できるようになっています。ただし、サーバで JavaScript を実行できない場合や実行したくない場合でも、同じアプローチが使用できます。この場合、エクスポートされた HTML/CSS/JS（Next.js の場合は [`next export` 出力](https://nextjs.org/docs/advanced-features/static-html-export)、Gatsby の場合はデフォルト）を `/some-app` としてサーブします。\n\n## 既存ページの一部に React を使う {/*using-react-for-a-part-of-your-existing-page*/}\n\n他のテクノロジ（Rails のようなサーバ側のものでも Backbone のようなクライアント側のものでも）で構築された既存のページがあり、そのページのどこかにインタラクティブな React コンポーネントをレンダーしたいとします。これは React を結合する一般的な方法です。実際、Meta では何年もの間、ほとんどの React 使用法がこうでした！\n\nこれを行うには、2 つのステップが必要です。\n\n1. **JavaScript 開発環境を設定**して、[JSX 構文](/learn/writing-markup-with-jsx)の使用、[`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) / [`export`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export) 構文を使ったコードのモジュール分割、[npm](https://www.npmjs.com/) パッケージレジストリからのパッケージ（例えば React）の使用ができるようにする。\n2. ページ上の表示させたい場所に **React コンポーネントをレンダー**する。\n\n具体的なアプローチはあなたの既存ページのセットアップによって異なりますが、一部の詳細について見ていきましょう。\n\n### ステップ 1: モジュラーな JavaScript 環境を設定する {/*step-1-set-up-a-modular-javascript-environment*/}\n\nモジュラーな JavaScript 環境を使用すると、すべてのコードを単一のファイルに書くのではなく、React コンポーネントを別々のファイルに記述できるようになります。また、他の開発者によって [npm](https://www.npmjs.com/) パッケージレジストリに公開されている、素晴らしいパッケージ群（React 自身も含む）を使えるようにもなります。具体的なやり方はあなたの既存のセットアップ方法によって異なります：\n\n* **アプリが既に `import` 文を使ってファイル分割するよう設定されている場合**、その既存の設定を使用するようにしてみてください。JS コードで `<div />` と記述すると、構文エラーが発生するかどうかを確認してください。構文エラーが発生する場合は、[Babel を使用して JavaScript を変換](https://babeljs.io/setup)するようにし、JSX を使うために [Babel React プリセット](https://babeljs.io/docs/babel-preset-react) を有効にしてください。\n\n* **JavaScript モジュールをコンパイルする既存のセットアップがない場合**は、[Vite](https://vite.dev/) を使ってセットアップします。Vite コミュニティは、Rails、Django、Laravel をはじめ、[多くのバックエンドフレームワークとのインテグレーション](https://github.com/vitejs/awesome-vite#integrations-with-backends)をメンテナンスしています。あなたのバックエンドフレームワークがリストされていない場合は、[このガイドに従って](https://vite.dev/guide/backend-integration.html)手動で Vite ビルドをバックエンドと統合してください。\n\nセットアップがうまくいっているかどうかを確認するには、プロジェクトフォルダーで次のコマンドを実行します。\n\n<TerminalBlock>\nnpm install react react-dom\n</TerminalBlock>\n\nそして、あなたのメインの JavaScript ファイル（おそらく `index.js` や `main.js` といった名前のもの）の先頭に、以下のコードを追加します。:\n\n<Sandpack>\n\n```html public/index.html hidden\n<!DOCTYPE html>\n<html>\n  <head><title>My app</title></head>\n  <body>\n    <!-- Your existing page content (in this example, it gets replaced) -->\n    <div id=\"root\"></div>\n  </body>\n</html>\n```\n\n```js src/index.js active\nimport { createRoot } from 'react-dom/client';\n\n// Clear the existing HTML content\ndocument.body.innerHTML = '<div id=\"app\"></div>';\n\n// Render your React component instead\nconst root = createRoot(document.getElementById('app'));\nroot.render(<h1>Hello, world</h1>);\n```\n\n</Sandpack>\n\nページ全体が「Hello, world!」に置き換わった場合は、すべてがうまくいったことになります。このまま読み進めてください。\n\n<Note>\n\n既存のプロジェクトにモジュラーな JavaScript 環境を組み込むことを最初は不安に感じるかもしれませんが、その価値はあると思います！ 行き詰まったら、[コミュニティのリソース](/community)または [Vite Chat](https://chat.vite.dev/) を試してみてください。\n\n</Note>\n\n### ステップ 2: ページに React コンポーネントをレンダーする {/*step-2-render-react-components-anywhere-on-the-page*/}\n\n前のステップでは、以下のコードをメインファイルのトップに置きました：\n\n```js\nimport { createRoot } from 'react-dom/client';\n\n// Clear the existing HTML content\ndocument.body.innerHTML = '<div id=\"app\"></div>';\n\n// Render your React component instead\nconst root = createRoot(document.getElementById('app'));\nroot.render(<h1>Hello, world</h1>);\n```\n\nもちろん、実際には既存の HTML コンテンツを削除したい訳ではありません！\n\nなので上記のコードは削除してください。\n\n代わりに、あなたの HTML 内の特定の場所に React コンポーネントをレンダーしたいはずです。HTML ページ（またはそれを生成しているサーバテンプレート）を開き、次のようにして、任意のタグに一意の [`id`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id) 属性を追加します：\n\n```html\n<!-- ... somewhere in your html ... -->\n<nav id=\"navigation\"></nav>\n<!-- ... more html ... -->\n```\n\nこれにより、[`document.getElementById`](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById) で HTML 要素を検索して [`createRoot`](/reference/react-dom/client/createRoot) に渡すことができ、その内部にあなたの React コンポーネントをレンダーできるようになります：\n\n<Sandpack>\n\n```html public/index.html\n<!DOCTYPE html>\n<html>\n  <head><title>My app</title></head>\n  <body>\n    <p>This paragraph is a part of HTML.</p>\n    <nav id=\"navigation\"></nav>\n    <p>This paragraph is also a part of HTML.</p>\n  </body>\n</html>\n```\n\n```js src/index.js active\nimport { createRoot } from 'react-dom/client';\n\nfunction NavigationBar() {\n  // TODO: Actually implement a navigation bar\n  return <h1>Hello from React!</h1>;\n}\n\nconst domNode = document.getElementById('navigation');\nconst root = createRoot(domNode);\nroot.render(<NavigationBar />);\n```\n\n</Sandpack>\n\n`index.html` にあるオリジナルの HTML コンテンツはそのままに、自分の `NavigationBar` という React コンポーネントが、HTML の `<nav id=\"navigation\">` 内に表示されるようになりました。React コンポーネントを既存の HTML ページの内部にレンダーする方法の詳細については、[`createRoot` 使用方法のドキュメント](/reference/react-dom/client/createRoot#rendering-a-page-partially-built-with-react) を参照してください。\n\n既存のプロジェクトで React を使用する場合、まずはボタンのような小さなインタラクティブなコンポーネントから始め、その後、徐々に「上向きに」進んでいき、最終的にはページ全体が React で構築されるようにすることが一般的です。もしもそのような段階に到達した場合は、React の効果が最大限に得られるように、[React フレームワーク](/learn/creating-a-react-app)に移行することをお勧めします。\n\n## 既存のネイティブモバイルアプリ内で React Native を使用する {/*using-react-native-in-an-existing-native-mobile-app*/}\n\n[React Native](https://reactnative.dev/) もまた、既存のネイティブアプリに段階的に統合することができます。Android（Java または Kotlin）用または iOS（Objective-C または Swift）用の既存のネイティブアプリがある場合は、[このガイド](https://reactnative.dev/docs/integration-with-existing-apps)に従って React Native 画面を追加できます。\n"
  },
  {
    "path": "src/content/learn/adding-interactivity.md",
    "content": "---\ntitle: インタラクティビティの追加\n---\n\n<Intro>\n\n画面上の要素には、ユーザの入力に反応して更新されていくものがあります。例えば、画像ギャラリをクリックするとアクティブな画像が切り替わります。React では、時間の経過とともに変化するデータのことを *state* と呼びます。任意のコンポーネントに state を追加することができ、必要に応じて更新することができます。この章では、インタラクションを処理し、state を更新し、時間の経過とともに異なる出力を表示するコンポーネントの作成方法について学びます。\n\n</Intro>\n\n<YouWillLearn isChapter={true}>\n\n* [ユーザが発生させるイベントの扱い方](/learn/responding-to-events)\n* [state でコンポーネントに情報を 「記憶」させる方法](/learn/state-a-components-memory)\n* [React が 2 段階で UI を更新する仕組み](/learn/render-and-commit)\n* [変更後すぐに state が更新されない理由](/learn/state-as-a-snapshot)\n* [複数の state の更新をキューに入れる方法](/learn/queueing-a-series-of-state-updates)\n* [state 内のオブジェクトを更新する方法](/learn/updating-objects-in-state)\n* [state 内の配列を更新する方法](/learn/updating-arrays-in-state)\n\n</YouWillLearn>\n\n## イベントへの応答 {/*responding-to-events*/}\n\nReact では、JSX に*イベントハンドラ*を追加することができます。イベントハンドラはあなた自身で書く関数であり、クリック、ホバー、フォーム入力へのフォーカスといったユーザインタラクションに応答してトリガされます。\n\n`<button>` のような組み込みコンポーネントは `onClick` のような組み込みのブラウザイベントのみをサポートします。しかし、独自のコンポーネントを作成し、そのイベントハンドラ props にアプリケーション固有の自由な名前を付けることもできます。\n\n<Sandpack>\n\n```js\nexport default function App() {\n  return (\n    <Toolbar\n      onPlayMovie={() => alert('Playing!')}\n      onUploadImage={() => alert('Uploading!')}\n    />\n  );\n}\n\nfunction Toolbar({ onPlayMovie, onUploadImage }) {\n  return (\n    <div>\n      <Button onClick={onPlayMovie}>\n        Play Movie\n      </Button>\n      <Button onClick={onUploadImage}>\n        Upload Image\n      </Button>\n    </div>\n  );\n}\n\nfunction Button({ onClick, children }) {\n  return (\n    <button onClick={onClick}>\n      {children}\n    </button>\n  );\n}\n```\n\n```css\nbutton { margin-right: 10px; }\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/responding-to-events\">\n\n[**イベントへの応答**](/learn/responding-to-events)を読んで、イベントハンドラの追加方法を学びましょう。\n\n</LearnMore>\n\n## state：コンポーネントのメモリ {/*state-a-components-memory*/}\n\nコンポーネントによっては、ユーザ操作の結果として画面上の表示内容を変更する必要があります。フォーム上でタイプすると入力欄が更新される、画像カルーセルで「次」をクリックすると表示される画像が変わる、「購入」をクリックすると買い物かごに商品が入る、といったものです。コンポーネントは、現在の入力値、現在の画像、ショッピングカートの状態といったものを「覚えておく」必要があります。React では、このようなコンポーネント固有のメモリのことを *state* と呼びます。\n\n[`useState`](/reference/react/useState) フックを使用すると、コンポーネントに state を追加することができます。*フック*とはコンポーネントに React の機能を使用させるための特別な関数です（state はその機能の 1 つです）。`useState` フックを使うと state 変数を宣言できます。このフックは初期値 (initial state) を受け取り、現在の state と、それを更新するための state セッタ関数のペアを返します。\n\n```js\nconst [index, setIndex] = useState(0);\nconst [showMore, setShowMore] = useState(false);\n```\n\n以下は、画像ギャラリが state を使用して、クリック時に state を更新する方法です：\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { sculptureList } from './data.js';\n\nexport default function Gallery() {\n  const [index, setIndex] = useState(0);\n  const [showMore, setShowMore] = useState(false);\n  const hasNext = index < sculptureList.length - 1;\n\n  function handleNextClick() {\n    if (hasNext) {\n      setIndex(index + 1);\n    } else {\n      setIndex(0);\n    }\n  }\n\n  function handleMoreClick() {\n    setShowMore(!showMore);\n  }\n\n  let sculpture = sculptureList[index];\n  return (\n    <>\n      <button onClick={handleNextClick}>\n        Next\n      </button>\n      <h2>\n        <i>{sculpture.name} </i>\n        by {sculpture.artist}\n      </h2>\n      <h3>\n        ({index + 1} of {sculptureList.length})\n      </h3>\n      <button onClick={handleMoreClick}>\n        {showMore ? 'Hide' : 'Show'} details\n      </button>\n      {showMore && <p>{sculpture.description}</p>}\n      <img\n        src={sculpture.url}\n        alt={sculpture.alt}\n      />\n    </>\n  );\n}\n```\n\n```js src/data.js\nexport const sculptureList = [{\n  name: 'Homenaje a la Neurocirugía',\n  artist: 'Marta Colvin Andrade',\n  description: 'Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.',\n  url: 'https://i.imgur.com/Mx7dA2Y.jpg',\n  alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.'\n}, {\n  name: 'Floralis Genérica',\n  artist: 'Eduardo Catalano',\n  description: 'This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.',\n  url: 'https://i.imgur.com/ZF6s192m.jpg',\n  alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.'\n}, {\n  name: 'Eternal Presence',\n  artist: 'John Woodrow Wilson',\n  description: 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n  url: 'https://i.imgur.com/aTtVpES.jpg',\n  alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.'\n}, {\n  name: 'Moai',\n  artist: 'Unknown Artist',\n  description: 'Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.',\n  url: 'https://i.imgur.com/RCwLEoQm.jpg',\n  alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.'\n}, {\n  name: 'Blue Nana',\n  artist: 'Niki de Saint Phalle',\n  description: 'The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.',\n  url: 'https://i.imgur.com/Sd1AgUOm.jpg',\n  alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.'\n}, {\n  name: 'Ultimate Form',\n  artist: 'Barbara Hepworth',\n  description: 'This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.',\n  url: 'https://i.imgur.com/2heNQDcm.jpg',\n  alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.'\n}, {\n  name: 'Cavaliere',\n  artist: 'Lamidi Olonade Fakeye',\n  description: \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n  url: 'https://i.imgur.com/wIdGuZwm.png',\n  alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.'\n}, {\n  name: 'Big Bellies',\n  artist: 'Alina Szapocznikow',\n  description: \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n  url: 'https://i.imgur.com/AlHTAdDm.jpg',\n  alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.'\n}, {\n  name: 'Terracotta Army',\n  artist: 'Unknown Artist',\n  description: 'The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.',\n  url: 'https://i.imgur.com/HMFmH6m.jpg',\n  alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.'\n}, {\n  name: 'Lunar Landscape',\n  artist: 'Louise Nevelson',\n  description: 'Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism’s geometric abstraction of space and form.',\n  url: 'https://i.imgur.com/rN7hY6om.jpg',\n  alt: 'A black matte sculpture where the individual elements are initially indistinguishable.'\n}, {\n  name: 'Aureole',\n  artist: 'Ranjani Shettar',\n  description: 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n  url: 'https://i.imgur.com/okTpbHhm.jpg',\n  alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.'\n}, {\n  name: 'Hippos',\n  artist: 'Taipei Zoo',\n  description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.',\n  url: 'https://i.imgur.com/6o5Vuyu.jpg',\n  alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.'\n}];\n```\n\n```css\nh2 { margin-top: 10px; margin-bottom: 0; }\nh3 {\n margin-top: 5px;\n font-weight: normal;\n font-size: 100%;\n}\nimg { width: 120px; height: 120px; }\nbutton {\n  display: block;\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/state-a-components-memory\">\n\n[**state: コンポーネントのメモリ**](/learn/state-a-components-memory)を読んで、値を記憶し、インタラクションに応答してその値を更新する方法について学びましょう。\n\n</LearnMore>\n\n## レンダーとコミット {/*render-and-commit*/}\n\nコンポーネントは、画面上に表示される前に React によってレンダーされる必要があります。このプロセスが踏む段階を理解すると、コードがどのように実行されるのか考える際や、コードの振る舞いを説明する際に役立ちます。\n\nコンポーネントが料理人として厨房に立ち、食材を調理して美味しい料理を作っている様子をイメージしてみてください。このシナリオにおいて React はウェイターです。お客様の注文を伝えて、できた料理をお客様に渡します。この UI の「注文」と「提供」のプロセスは、次の 3 つのステップからなります：\n\n1. レンダーの**トリガ**（お客様の注文を厨房に伝える）\n2. コンポーネントの**レンダー**（厨房で注文の品を料理する）\n3. DOM への**コミット**（テーブルに注文の品を提供する）\n\n<IllustrationBlock sequential>\n  <Illustration caption=\"Trigger\" alt=\"レストランのウェイター役の React が、ユーザから注文を聞き取って、コンポーネントの厨房に渡している。\" src=\"/images/docs/illustrations/i_render-and-commit1.png\" />\n  <Illustration caption=\"Render\" alt=\"Card シェフが React に出来立ての Card コンポーネントを渡している。\" src=\"/images/docs/illustrations/i_render-and-commit2.png\" />\n  <Illustration caption=\"Commit\" alt=\"React がユーザの座っているテーブルに Card を提供している。\" src=\"/images/docs/illustrations/i_render-and-commit3.png\" />\n</IllustrationBlock>\n\n<LearnMore path=\"/learn/render-and-commit\">\n\n[**レンダーとコミット**](/learn/render-and-commit)を読んで、UI 更新のライフサイクルを学びましょう。\n\n</LearnMore>\n\n## state はスナップショットである {/*state-as-a-snapshot*/}\n\n通常の JavaScript の変数とは異なり、React の state はスナップショットのような動作をします。セットしても既存の state 変数は変更されず、代わりに再レンダーがトリガされます。初見ではびっくりするかもしれません。\n\n```js\nconsole.log(count);  // 0\nsetCount(count + 1); // Request a re-render with 1\nconsole.log(count);  // Still 0!\n```\n\nこのおかげで、見逃しやすい小さなバグを回避することができます。ここに小さなチャットアプリがあります。まず「送信」を押して、*次に*受信者を「ボブ」に変更したら何が起こるか、推測してみてください。5 秒後の `alert` には誰の名前が表示されるでしょうか？\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [to, setTo] = useState('Alice');\n  const [message, setMessage] = useState('Hello');\n\n  function handleSubmit(e) {\n    e.preventDefault();\n    setTimeout(() => {\n      alert(`You said ${message} to ${to}`);\n    }, 5000);\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <label>\n        To:{' '}\n        <select\n          value={to}\n          onChange={e => setTo(e.target.value)}>\n          <option value=\"Alice\">Alice</option>\n          <option value=\"Bob\">Bob</option>\n        </select>\n      </label>\n      <textarea\n        placeholder=\"Message\"\n        value={message}\n        onChange={e => setMessage(e.target.value)}\n      />\n      <button type=\"submit\">Send</button>\n    </form>\n  );\n}\n```\n\n```css\nlabel, textarea { margin-bottom: 10px; display: block; }\n```\n\n</Sandpack>\n\n\n<LearnMore path=\"/learn/state-as-a-snapshot\">\n\n[**state はスナップショットである**](/learn/state-as-a-snapshot)を読んで、イベントハンドラ内で state が「固定」され、変化していないように見える理由を学びましょう。\n\n</LearnMore>\n\n## 一連の state の更新をキューに入れる {/*queueing-a-series-of-state-updates*/}\n\nこのコンポーネントにはバグがあります：\"+3\" をクリックしても 1 しかスコアが増えません。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [score, setScore] = useState(0);\n\n  function increment() {\n    setScore(score + 1);\n  }\n\n  return (\n    <>\n      <button onClick={() => increment()}>+1</button>\n      <button onClick={() => {\n        increment();\n        increment();\n        increment();\n      }}>+3</button>\n      <h1>Score: {score}</h1>\n    </>\n  )\n}\n```\n\n```css\nbutton { display: inline-block; margin: 10px; font-size: 20px; }\n```\n\n</Sandpack>\n\n[state はスナップショットである](/learn/state-as-a-snapshot)で、なぜこのようなことが起こってしまうのかを説明しています。state をセットすると、新しい再レンダーが要求されますが、すでに実行されているコード内の state は変更されません。そのため `setScore(score + 1)` を呼び出した直後は `score` が `0` であり続けます。\n\n```js\nconsole.log(score);  // 0\nsetScore(score + 1); // setScore(0 + 1);\nconsole.log(score);  // 0\nsetScore(score + 1); // setScore(0 + 1);\nconsole.log(score);  // 0\nsetScore(score + 1); // setScore(0 + 1);\nconsole.log(score);  // 0\n```\n\nstate を設定する際に*更新用関数*を渡すことでこれを修正することができます。`setScore(score + 1)` を  `setScore(s => s + 1)` に置き換えることで、\"+3\" ボタンが修正されることに注目してください。こうすることで、複数回の state の更新がキューに入れられます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [score, setScore] = useState(0);\n\n  function increment() {\n    setScore(s => s + 1);\n  }\n\n  return (\n    <>\n      <button onClick={() => increment()}>+1</button>\n      <button onClick={() => {\n        increment();\n        increment();\n        increment();\n      }}>+3</button>\n      <h1>Score: {score}</h1>\n    </>\n  )\n}\n```\n\n```css\nbutton { display: inline-block; margin: 10px; font-size: 20px; }\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/queueing-a-series-of-state-updates\">\n\n[**一連の state の更新をキューに入れる**](/learn/queueing-a-series-of-state-updates)を読んで、次回のレンダーの前に複数の更新をキューに入れる方法を学びましょう。\n\n</LearnMore>\n\n## state 内のオブジェクトの更新 {/*updating-objects-in-state*/}\n\nstate にはどのような JavaScript の値でも保持することができます。これにはオブジェクトも含まれます。しかし、React の state に保持されたオブジェクトと配列を直接書き換えるべきではありません。オブジェクトを更新したい場合、代わりに新しいオブジェクトを作成（または既存のもののコピーを作成）し、それを使って state をセットする必要があります。\n\n通常、変更したいオブジェクトや配列をコピーするには `...` というスプレッド構文を使用します。例えば、ネストされたオブジェクトを更新する場合、次のようになります：\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [person, setPerson] = useState({\n    name: 'Niki de Saint Phalle',\n    artwork: {\n      title: 'Blue Nana',\n      city: 'Hamburg',\n      image: 'https://i.imgur.com/Sd1AgUOm.jpg',\n    }\n  });\n\n  function handleNameChange(e) {\n    setPerson({\n      ...person,\n      name: e.target.value\n    });\n  }\n\n  function handleTitleChange(e) {\n    setPerson({\n      ...person,\n      artwork: {\n        ...person.artwork,\n        title: e.target.value\n      }\n    });\n  }\n\n  function handleCityChange(e) {\n    setPerson({\n      ...person,\n      artwork: {\n        ...person.artwork,\n        city: e.target.value\n      }\n    });\n  }\n\n  function handleImageChange(e) {\n    setPerson({\n      ...person,\n      artwork: {\n        ...person.artwork,\n        image: e.target.value\n      }\n    });\n  }\n\n  return (\n    <>\n      <label>\n        Name:\n        <input\n          value={person.name}\n          onChange={handleNameChange}\n        />\n      </label>\n      <label>\n        Title:\n        <input\n          value={person.artwork.title}\n          onChange={handleTitleChange}\n        />\n      </label>\n      <label>\n        City:\n        <input\n          value={person.artwork.city}\n          onChange={handleCityChange}\n        />\n      </label>\n      <label>\n        Image:\n        <input\n          value={person.artwork.image}\n          onChange={handleImageChange}\n        />\n      </label>\n      <p>\n        <i>{person.artwork.title}</i>\n        {' by '}\n        {person.name}\n        <br />\n        (located in {person.artwork.city})\n      </p>\n      <img\n        src={person.artwork.image}\n        alt={person.artwork.title}\n      />\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 5px; margin-bottom: 5px; }\nimg { width: 200px; height: 200px; }\n```\n\n</Sandpack>\n\nコード内のオブジェクトのコピーが面倒になったら [Immer](https://github.com/immerjs/use-immer) のようなライブラリを使うと、コードの繰り返しを減らすことができます：\n\n<Sandpack>\n\n```js\nimport { useImmer } from 'use-immer';\n\nexport default function Form() {\n  const [person, updatePerson] = useImmer({\n    name: 'Niki de Saint Phalle',\n    artwork: {\n      title: 'Blue Nana',\n      city: 'Hamburg',\n      image: 'https://i.imgur.com/Sd1AgUOm.jpg',\n    }\n  });\n\n  function handleNameChange(e) {\n    updatePerson(draft => {\n      draft.name = e.target.value;\n    });\n  }\n\n  function handleTitleChange(e) {\n    updatePerson(draft => {\n      draft.artwork.title = e.target.value;\n    });\n  }\n\n  function handleCityChange(e) {\n    updatePerson(draft => {\n      draft.artwork.city = e.target.value;\n    });\n  }\n\n  function handleImageChange(e) {\n    updatePerson(draft => {\n      draft.artwork.image = e.target.value;\n    });\n  }\n\n  return (\n    <>\n      <label>\n        Name:\n        <input\n          value={person.name}\n          onChange={handleNameChange}\n        />\n      </label>\n      <label>\n        Title:\n        <input\n          value={person.artwork.title}\n          onChange={handleTitleChange}\n        />\n      </label>\n      <label>\n        City:\n        <input\n          value={person.artwork.city}\n          onChange={handleCityChange}\n        />\n      </label>\n      <label>\n        Image:\n        <input\n          value={person.artwork.image}\n          onChange={handleImageChange}\n        />\n      </label>\n      <p>\n        <i>{person.artwork.title}</i>\n        {' by '}\n        {person.name}\n        <br />\n        (located in {person.artwork.city})\n      </p>\n      <img\n        src={person.artwork.image}\n        alt={person.artwork.title}\n      />\n    </>\n  );\n}\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"use-immer\": \"0.5.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 5px; margin-bottom: 5px; }\nimg { width: 200px; height: 200px; }\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/updating-objects-in-state\">\n\n[**state 内のオブジェクトの更新**](/learn/updating-objects-in-state)を読んで、オブジェクトを正しく更新する方法を学びましょう。\n\n</LearnMore>\n\n## state 内の配列の更新 {/*updating-arrays-in-state*/}\n\n配列もまた、state 内で保持できるミュータブル（mutable, 書き換え可能）な JavaScript オブジェクトの一種ですが、読み取り専用として扱うべきものです。オブジェクトと同様に、state に保存された配列を更新したい場合、新しいものを作成し（または既存のもののコピーを作成し）、state が新しい配列を使用するようにセットする必要があります：\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nconst initialList = [\n  { id: 0, title: 'Big Bellies', seen: false },\n  { id: 1, title: 'Lunar Landscape', seen: false },\n  { id: 2, title: 'Terracotta Army', seen: true },\n];\n\nexport default function BucketList() {\n  const [list, setList] = useState(\n    initialList\n  );\n\n  function handleToggle(artworkId, nextSeen) {\n    setList(list.map(artwork => {\n      if (artwork.id === artworkId) {\n        return { ...artwork, seen: nextSeen };\n      } else {\n        return artwork;\n      }\n    }));\n  }\n\n  return (\n    <>\n      <h1>Art Bucket List</h1>\n      <h2>My list of art to see:</h2>\n      <ItemList\n        artworks={list}\n        onToggle={handleToggle} />\n    </>\n  );\n}\n\nfunction ItemList({ artworks, onToggle }) {\n  return (\n    <ul>\n      {artworks.map(artwork => (\n        <li key={artwork.id}>\n          <label>\n            <input\n              type=\"checkbox\"\n              checked={artwork.seen}\n              onChange={e => {\n                onToggle(\n                  artwork.id,\n                  e.target.checked\n                );\n              }}\n            />\n            {artwork.title}\n          </label>\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n</Sandpack>\n\nコード内の配列のコピーが面倒になったら [Immer](https://github.com/immerjs/use-immer) のようなライブラリを使うと、コードの繰り返しを減らすことができます：\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { useImmer } from 'use-immer';\n\nconst initialList = [\n  { id: 0, title: 'Big Bellies', seen: false },\n  { id: 1, title: 'Lunar Landscape', seen: false },\n  { id: 2, title: 'Terracotta Army', seen: true },\n];\n\nexport default function BucketList() {\n  const [list, updateList] = useImmer(initialList);\n\n  function handleToggle(artworkId, nextSeen) {\n    updateList(draft => {\n      const artwork = draft.find(a =>\n        a.id === artworkId\n      );\n      artwork.seen = nextSeen;\n    });\n  }\n\n  return (\n    <>\n      <h1>Art Bucket List</h1>\n      <h2>My list of art to see:</h2>\n      <ItemList\n        artworks={list}\n        onToggle={handleToggle} />\n    </>\n  );\n}\n\nfunction ItemList({ artworks, onToggle }) {\n  return (\n    <ul>\n      {artworks.map(artwork => (\n        <li key={artwork.id}>\n          <label>\n            <input\n              type=\"checkbox\"\n              checked={artwork.seen}\n              onChange={e => {\n                onToggle(\n                  artwork.id,\n                  e.target.checked\n                );\n              }}\n            />\n            {artwork.title}\n          </label>\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"use-immer\": \"0.5.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/updating-arrays-in-state\">\n\n[**state 内の配列の更新**](/learn/updating-arrays-in-state)を読んで、配列を正しく更新する方法を学びましょう。\n\n</LearnMore>\n\n## 次のステップ {/*whats-next*/}\n\n[イベントへの応答](/learn/responding-to-events)に進んで、この章をページごとに読み進めましょう！\n\nもしくは、すでにこれらのトピックに詳しい場合、[state の管理](/learn/managing-state)について読んでみましょう。\n"
  },
  {
    "path": "src/content/learn/build-a-react-app-from-scratch.md",
    "content": "---\ntitle: ゼロからの React アプリ構築\n---\n\n<Intro>\n\nあなたのアプリが既存のフレームワークではうまく対応できない制約を有している場合や、自分自身でフレームワークを構築したい場合、または React アプリの基本を学びたい場合は、ゼロから React アプリを構築することも可能です。\n\n</Intro>\n\n<DeepDive>\n\n#### まずはフレームワーク使用の検討を {/*consider-using-a-framework*/}\n\nReact の始め方としてゼロからスタートするのは簡単ですが、このアプローチを選ぶ際の重要なトレードオフとして、これがその場限りの独自フレームワークを構築しているのと変わらないのだ、ということを認識してください。アプリの要件が進化するにつれ、推奨フレームワークならすでにうまく開発もサポートもしているような、よりフレームワーク的な問題について、自前で解決する必要が出てくるかもしれません。\n\nたとえば、将来的にアプリがサーバサイドレンダリング (SSR)、静的サイト生成 (SSG)、または React Server Components (RSC) のサポートを必要とする場合、それらを自前で実装しなければなりません。同様に、フレームワークレベルでの統合を必要とする将来の React 機能を使用したい場合、それらも自分で実装しなければなりません。\n\n私たちが推奨するフレームワークは、よりパフォーマンスの良いアプリを構築するのにも役立ちます。たとえば、ネットワークリクエストのウォーターフォールを減らしたり排除したりすることで、より良いユーザ体験を提供します。小さなプロジェクトを構築している間は優先度が低いかもしれませんが、アプリにユーザが増えていけばパフォーマンスを向上させたいと思うことでしょう。\n\nまたこのアプローチでは、ルーティングやデータフェッチ、その他の機能の開発のされ方があなたの事情に特有のものになるため、サポートを受けるのも難しくなります。これらの問題に自分で取り組むことに自信がある場合、または将来的にもこれらの機能が必要になることはないと確信している場合にのみ、このオプションを選ぶようにすべきです。\n\n推奨フレームワークのリストについては、[React アプリの作成](/learn/creating-a-react-app)をご覧ください。\n\n</DeepDive>\n\n\n## ステップ 1：ビルドツールのインストール {/*step-1-install-a-build-tool*/}\n\n最初のステップは、`vite`、`parcel`、または `rsbuild` のようなビルドツールをインストールすることです。これらのビルドツールは、ソースコードをパッケージ化して実行する機能、ローカル開発用の開発サーバ、およびアプリを本番サーバにデプロイするためのビルドコマンドを提供します。\n\n### Vite {/*vite*/}\n\n[Vite](https://vite.dev/) は、モダンなウェブプロジェクトのために素早くかつスリムな開発体験を提供することを目指したビルドツールです。\n\n<TerminalBlock>\nnpm create vite@latest my-app -- --template react-ts\n</TerminalBlock>\n\nVite はデフォルトで適切な設定がされた、使い方に規約がある (opinionated) ツールです。Vite には、ファストリフレッシュ、JSX、Babel/SWC、その他の一般的な機能をサポートする豊富なプラグインのエコシステムがあります。Vite の [React プラグイン](https://vite.dev/plugins/#vitejs-plugin-react)や [React SWC プラグイン](https://vite.dev/plugins/#vitejs-plugin-react-swc)、[React SSR のサンプルプロジェクト](https://vite.dev/guide/ssr.html#example-projects) を参照して始めてみてください。\n\nVite は、[推奨フレームワーク](/learn/creating-a-react-app) のひとつである [React Router](https://reactrouter.com/start/framework/installation) でもビルドツールとして使用されています。\n\n### Parcel {/*parcel*/}\n\n[Parcel](https://parceljs.org/) は、素晴らしい初期開発体験とスケーラブルなアーキテクチャの両方を備えており、プロジェクトを始めたばかりの状態から大規模な本番アプリケーションにまで成長させることができます。\n\n<TerminalBlock>\nnpm install --save-dev parcel\n</TerminalBlock>\n\nParcel は、ファストリフレッシュ、JSX、TypeScript、Flow、スタイリングをデフォルトでサポートしています。[Parcel の React レシピ](https://parceljs.org/recipes/react/#getting-started)を参照して始めてみてください。\n\n### Rsbuild {/*rsbuild*/}\n\n[Rsbuild](https://rsbuild.dev/) は、Rspack を基にしたビルドツールで、React アプリケーションのためのシームレスな開発体験を提供します。慎重に調整されたデフォルト設定とパフォーマンス最適化が最初から利用できます。\n\n<TerminalBlock>\nnpx create-rsbuild --template react\n</TerminalBlock>\n\nRsbuild には、ファストリフレッシュ、JSX、TypeScript、スタイリングなどの React 機能のサポートが組み込まれています。[Rsbuild の React ガイド](https://rsbuild.dev/guide/framework/react)を参照して始めてみてください。\n\n<Note>\n\n#### React Native 用の Metro {/*react-native*/}\n\nReact Native をゼロから始める場合は、React Native 用の JavaScript バンドラである [Metro](https://metrobundler.dev/) を使用する必要があります。Metro は iOS や Android などのプラットフォーム向けのバンドル機能をサポートしていますが、上記で紹介したツールと比較すると多くの機能が不足しています。プロジェクトが React Native のサポートを必要としているのでない限り、Vite、Parcel、または Rsbuild から始めることをお勧めします。\n\n</Note>\n\n## ステップ 2：一般的なアプリケーションパターンの構築 {/*step-2-build-common-application-patterns*/}\n\n上記のビルドツールを使うとクライアントのみのシングルページアプリ (SPA) から始まりますが、それ以上のもの、つまりルーティング、データフェッチ、スタイリングといった一般的な機能に対するソリューションは含まれていません。\n\nReact エコシステムには、これらの問題に対するツールがたくさん存在します。手始めとして、ここでは広く使用されているものをいくつか紹介していますが、他のツールがあなたにとってより適している場合は自由に選んでください。\n\n### ルーティング {/*routing*/}\n\nルーティング (routing) は、ユーザが特定の URL にアクセスしたときにどのコンテンツやページを表示するかを決定するものです。ある URL をアプリの異なる部分にマッピングするために、ルータの設定が必要です。また、ネストされたルート、ルートパラメータ、クエリパラメータを処理する必要があります。ルータはコード内で設定することも、コンポーネントのフォルダやファイル構造に基づいて定義することもできます。\n\nルータは現代のアプリケーションの中核をなす部分であり、通常はデータフェッチ（ページ全体のデータの高速なプリフェッチを含む）、コード分割（クライアントのバンドルサイズを最小化する）、およびページのレンダー方法（各ページがどのように生成されるかを決定する）と統合されています。\n\n以下のライブラリの使用をお勧めします。\n\n- [React Router](https://reactrouter.com/start/data/custom)\n- [Tanstack Router](https://tanstack.com/router/latest)\n\n\n### データフェッチ {/*data-fetching*/}\n\nサーバやその他のデータソースからデータを取得することは、ほとんどのアプリケーションにおいて重要な作業です。これを適切に行うには、ローディング状態の管理、エラー状態の管理、および取得したデータのキャッシュ管理が必要であり、複雑になりがちです。\n\nこの目的に特化したデータフェッチライブラリは、データの取得とキャッシュに関する難しい作業を行ってくれるため、あなたはアプリが必要とするデータとその表示方法に集中できます。これらのライブラリは通常、コンポーネント内で直接使用されますが、ルーティングのローダに統合して高速な事前取得とパフォーマンスの向上を図ることもでき、サーバレンダーにも利用できます。\n\nコンポーネント内で直接データを取得すると、ネットワークリクエストのウォーターフォールに伴う遅延が発生し、読み込み時間が遅くなる可能性があることに注意してください。可能な限り、ルータローダやサーバで、データを事前フェッチすることをお勧めします！ これによりページの表示とページデータの取得をまとめて同時に行うことができます。\n\nほとんどのデータをバックエンドや REST スタイルの API から取得している場合、以下のライブラリの使用をお勧めします。\n\n- [TanStack Query](https://tanstack.com/query/)\n- [SWR](https://swr.vercel.app/)\n- [RTK Query](https://redux-toolkit.js.org/rtk-query/overview)\n\nGraphQL API からデータを取得する場合、以下の使用をお勧めします。\n\n- [Apollo](https://www.apollographql.com/docs/react)\n- [Relay](https://relay.dev/)\n\n\n### コード分割 {/*code-splitting*/}\n\nコード分割 (code splitting) とは、オンデマンドで読み込める小さな複数のバンドルへとアプリを分割するための作業です。アプリのコードサイズは、新しい機能や依存ライブラリを追加するたびに増加します。実際に使用する前にアプリ全体のコードをすべて送信する必要がある場合、読み込みが遅くなることがあります。キャッシュ、機能や依存ライブラリの削減、一部コードのサーバ側実行といった方法で読み込みの遅さを軽減することもできますが、これらは過度に使用すると機能性を犠牲にしてしまう不完全な解決策です。\n\nまた、アプリが使っている独自フレームワークにコード分割を任せていると、コード分割がまったく行われていない場合より却って読み込みが遅くなるという状況に遭遇することがあります。例えば、チャートの[遅延読み込み](/reference/react/lazy)を使えば、チャートのコードをアプリの他の部分から分離し、チャートのレンダーに必要なコードだけ送信を遅らせることができます。[Parcel は React.lazy を使用したコード分割をサポートしています](https://parceljs.org/recipes/react/#code-splitting)。ところが、チャートのコード自身が初回レンダー後にデータを読み込む場合、2 回の待機が発生することになります。これがウォーターフォールです。チャートデータとそれを表示するためのコードを同時にフェッチするのではなく、各ステップが順番に完了するのを待たなければならないという状況です。\n\nルートごとにコードを分割するだけでなく、バンドルやデータフェッチと統合することで、アプリの初期読み込み時間とアプリの最大可視コンテンツのレンダー時間 ([Largest Contentful Paint](https://web.dev/articles/lcp)) を短縮できます。\n\nコード分割の手順については、ビルドツールのドキュメントを参照してください。\n- [Vite のビルド最適化](https://vite.dev/guide/features.html#build-optimizations)\n- [Parcel のコード分割](https://parceljs.org/features/code-splitting/)\n- [Rsbuild のコード分割](https://rsbuild.dev/guide/optimization/code-splitting)\n\n### アプリケーションパフォーマンスの向上 {/*improving-application-performance*/}\n\nあなたが選択したビルドツールは、シングルページアプリ (SPA) のみをサポートしています。このためサーバサイドレンダリング (SSR)、静的サイト生成 (SSG)、あるいは React Server Components (RSC) などの他の[レンダーパターン](https://www.patterns.dev/vanilla/rendering-patterns)は自分で実装する必要があります。最初はこれらの機能が必要でなくても、将来的には SSR、SSG、または RSC の恩恵を受けるルート（ページ）があるかもしれません。\n\n* **シングルページアプリ (SPA)** は、単一の HTML ページを読み込み、ユーザがアプリを操作した際にページを動的に更新します。SPA は始めやすいですが、初期読み込み時間が遅くなることがあります。SPA はほとんどのビルドツールにおけるデフォルトのアーキテクチャです。\n\n* **ストリーミングサーバサイドレンダリング (SSR)** は、サーバでページをレンダーし、完全にレンダーされたページをクライアントに送信します。SSR はパフォーマンスを向上できますが、シングルページアプリよりも設定とメンテナンスが複雑になることがあります。ストリーミングを追加すると、SSR の構築とメンテナンスは非常に複雑になります。[Vite の SSR ガイド](https://vite.dev/guide/ssr)を参照してください。\n\n* **静的サイト生成 (SSG)** は、ビルド時にアプリの静的 HTML ファイルを生成します。SSG はパフォーマンスを向上させることができますが、サーバサイドレンダリングよりも設定とメンテナンスが複雑になることがあります。[Vite の SSG ガイド](https://vite.dev/guide/ssr.html#pre-rendering-ssg)を参照してください。\n\n* **React Server Components (RSC)** は、ビルド時専用コンポーネント、サーバ専用コンポーネント、そしてインタラクティブなコンポーネントを、単一の React ツリーで混在させることができます。RSC はパフォーマンスを向上させることができますが、現在のところ、設定とメンテナンスには深い専門知識が必要です。[Parcel の RSC の例](https://github.com/parcel-bundler/rsc-examples)を参照してください。\n\nあなたのレンダー戦略を、ルータと統合する必要があります。それによりあなたのフレームワークで構築されたアプリが、ルートごとにレンダー戦略を選択できるようにするのです。これにより、アプリ全体を書き直すことなく、異なるレンダー戦略を利用できます。たとえば、アプリの最初のページでは静的な生成 (SSG) が有益かもしれませんが、コンテンツフィードを持つページはサーバサイドレンダリングが最適かもしれません。\n\n適切なルートに適切なレンダー戦略を使用することで、コンテンツの最初のバイトが読み込まれるまでの時間 ([Time to First Byte](https://web.dev/articles/ttfb))、最初のコンテンツが表示されるまでの時間 ([First Contentful Paint](https://web.dev/articles/fcp))、およびアプリの最大の可視コンテンツが表示されるまでの時間 ([Largest Contentful Paint](https://web.dev/articles/lcp)) を短縮できるのです。\n\n### さらに... {/*and-more*/}\n\nこれらは、新しいアプリをゼロから構築する際に考慮すべき機能のほんの一例です。あなたが直面するであろう多くの問題は、それぞれが他の問題と相互に関連しており、不慣れな領域での深い専門知識を必要とする場合もあるため、解決が難しいことがあります。\n\nこれらの問題を自分で解決したくない場合は、これらの機能をデフォルトで利用できるフレームワークで[始めることができます](/learn/creating-a-react-app)。\n"
  },
  {
    "path": "src/content/learn/choosing-the-state-structure.md",
    "content": "---\ntitle: state 構造の選択\n---\n\n<Intro>\n\n快適に変更やデバッグが行えるコンポーネントと、常にバグの種になるコンポーネントの違いは、state をうまく構造化できているかどうかです。ここでは、state 構造を考慮する際に役立つ、いくつかのヒントをご紹介します。\n\n</Intro>\n\n<YouWillLearn>\n\n* 単一の state 変数と複数の state 変数の使い分け\n* state の構成において避けるべきこと\n* state 構造の一般的な問題を修正する方法\n\n</YouWillLearn>\n\n## state 構造の原則 {/*principles-for-structuring-state*/}\n\nstate を格納するコンポーネントを作成する際に、いくつ state 変数を使うのか、データ構造をどのようにするのかについて選択を行う必要があります。最適とはいえない state 構造でも正しいプログラムを作成することは可能ではありますが、より良い選択をするために役立つ原則がいくつか存在します。\n\n1. **関連する state をグループ化する**。2 つ以上の state 変数を常に同時に更新する場合、それらを単一の state 変数にまとめることを検討してください。\n2. **state の矛盾を避ける**。state の複数部分が矛盾して互いに「衝突する」構造になっている場合、ミスが発生する余地があるということです。これを避けてください。\n3. **冗長な state を避ける**。コンポーネントの props や既存の state 変数からレンダー時に何らかの情報を計算できる場合、その情報をコンポーネントの state に入れるべきではありません。\n4. **state 内の重複を避ける**。同じデータが複数の state 変数間、またはネストしたオブジェクト間で重複している場合、それらを同期させることは困難です。できる限り重複を減らしてください。\n5. **深くネストされた state を避ける**。深い階層構造となっている state はあまり更新しやすくありません。できる限り、state をフラットに構造化する方法を選ぶようにしてください。\n\nこれらの原則の背後にある目標は、*ミスを入りこませずに state を容易に更新できるようにすること*です。state から冗長で重複するデータを取り除くことで、すべての state が同期した状態を保てるようになります。これは、データベースエンジニアがバグを減らすために[データベース構造を \"正規化 (normalize)\"](https://docs.microsoft.com/en-us/office/troubleshoot/access/database-normalization-description) しようとする考え方と似ています。アルバート・アインシュタインのもじりですが、「**state はできる限りシンプルにすべきだ、だがシンプルすぎてもいけない**」ということです。\n\nこれらの原則が実際にどのように適用されるか見てみましょう。\n\n## 関連する state をグループ化する {/*group-related-state*/}\n\nときに、単一の state 変数を使用するか、複数の state 変数を使用するかで迷うことがあるかもしれません。\n\nこうすべきでしょうか？\n\n```js\nconst [x, setX] = useState(0);\nconst [y, setY] = useState(0);\n```\n\nそれともこうでしょうか？\n\n```js\nconst [position, setPosition] = useState({ x: 0, y: 0 });\n```\n\n技術的には、どちらのアプローチを採用することも可能です。しかし **2 つの state 変数が常に一緒に変更される場合は、それらを単一の state 変数にまとめると良いでしょう**。そうすれば、常に両者を同期することを忘れる心配がありません。例えば以下に、カーソルを動かすと赤い点の両方の軸の座標が更新されるという例を示します。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function MovingDot() {\n  const [position, setPosition] = useState({\n    x: 0,\n    y: 0\n  });\n  return (\n    <div\n      onPointerMove={e => {\n        setPosition({\n          x: e.clientX,\n          y: e.clientY\n        });\n      }}\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}>\n      <div style={{\n        position: 'absolute',\n        backgroundColor: 'red',\n        borderRadius: '50%',\n        transform: `translate(${position.x}px, ${position.y}px)`,\n        left: -10,\n        top: -10,\n        width: 20,\n        height: 20,\n      }} />\n    </div>\n  )\n}\n```\n\n```css\nbody { margin: 0; padding: 0; height: 250px; }\n```\n\n</Sandpack>\n\nstate をオブジェクトや配列にグループ化する別のケースとして、state の数が事前にわからない場合があります。たとえば、ユーザがカスタムフィールドを追加できるフォームがある場合に、これが有用です。\n\n<Pitfall>\n\nstate 変数がオブジェクトの場合、[1 つのフィールドだけを更新することはできず](/learn/updating-objects-in-state)、他のフィールドも明示的にコピーする必要があることを思い出してください。たとえば、上記の例で `setPosition({ x: 100 })` とはできません。`y` プロパティが全くないからです！ `x` だけを設定したい場合、`setPosition({ ...position, x: 100 })` とするか、2 つの state 変数に分割して `setX(100)` とするかのどちらかになります。\n\n</Pitfall>\n\n## state の矛盾を避ける {/*avoid-contradictions-in-state*/}\n\n以下は、`isSending` と `isSent` という state 変数があるホテルのフィードバックフォームです。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function FeedbackForm() {\n  const [text, setText] = useState('');\n  const [isSending, setIsSending] = useState(false);\n  const [isSent, setIsSent] = useState(false);\n\n  async function handleSubmit(e) {\n    e.preventDefault();\n    setIsSending(true);\n    await sendMessage(text);\n    setIsSending(false);\n    setIsSent(true);\n  }\n\n  if (isSent) {\n    return <h1>Thanks for feedback!</h1>\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <p>How was your stay at The Prancing Pony?</p>\n      <textarea\n        disabled={isSending}\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <br />\n      <button\n        disabled={isSending}\n        type=\"submit\"\n      >\n        Send\n      </button>\n      {isSending && <p>Sending...</p>}\n    </form>\n  );\n}\n\n// Pretend to send a message.\nfunction sendMessage(text) {\n  return new Promise(resolve => {\n    setTimeout(resolve, 2000);\n  });\n}\n```\n\n</Sandpack>\n\nこのコードは機能しますが、「ありえない」state になってしまう余地を残しています。たとえば、`setIsSent` と `setIsSending` を一緒に呼び出すのを忘れた場合、`isSending` と `isSent` が同時に `true` になってしまう状況に陥るかもしれません。コンポーネントが複雑になればなるほど、何が起こったのか理解しにくくなります。\n\n**`isSending` と `isSent` は同時に `true` になることはないため、それらを 1 つの `status` という state 変数に置き換えて、`typing`（初期状態）、`sending`、`sent` という *3 つの*有効な状態のうちの 1 つになるようにする方が良いでしょう**。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function FeedbackForm() {\n  const [text, setText] = useState('');\n  const [status, setStatus] = useState('typing');\n\n  async function handleSubmit(e) {\n    e.preventDefault();\n    setStatus('sending');\n    await sendMessage(text);\n    setStatus('sent');\n  }\n\n  const isSending = status === 'sending';\n  const isSent = status === 'sent';\n\n  if (isSent) {\n    return <h1>Thanks for feedback!</h1>\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <p>How was your stay at The Prancing Pony?</p>\n      <textarea\n        disabled={isSending}\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <br />\n      <button\n        disabled={isSending}\n        type=\"submit\"\n      >\n        Send\n      </button>\n      {isSending && <p>Sending...</p>}\n    </form>\n  );\n}\n\n// Pretend to send a message.\nfunction sendMessage(text) {\n  return new Promise(resolve => {\n    setTimeout(resolve, 2000);\n  });\n}\n```\n\n</Sandpack>\n\n読みやすくしたければ定数を宣言することはいつでも可能です。\n\n```js\nconst isSending = status === 'sending';\nconst isSent = status === 'sent';\n```\n\nこれらは state 変数ではなくなったので、互いに同期がとれなくなる心配をする必要はありません。\n\n## 冗長な state を避ける {/*avoid-redundant-state*/}\n\nレンダー中にコンポーネントの props や既存の state 変数から情報を計算できる場合、その情報をコンポーネントの state に入れる**べきではありません**。\n\n例として、このフォームを見てみましょう。動作はしていますが、冗長な state がないか探してみてください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [firstName, setFirstName] = useState('');\n  const [lastName, setLastName] = useState('');\n  const [fullName, setFullName] = useState('');\n\n  function handleFirstNameChange(e) {\n    setFirstName(e.target.value);\n    setFullName(e.target.value + ' ' + lastName);\n  }\n\n  function handleLastNameChange(e) {\n    setLastName(e.target.value);\n    setFullName(firstName + ' ' + e.target.value);\n  }\n\n  return (\n    <>\n      <h2>Let’s check you in</h2>\n      <label>\n        First name:{' '}\n        <input\n          value={firstName}\n          onChange={handleFirstNameChange}\n        />\n      </label>\n      <label>\n        Last name:{' '}\n        <input\n          value={lastName}\n          onChange={handleLastNameChange}\n        />\n      </label>\n      <p>\n        Your ticket will be issued to: <b>{fullName}</b>\n      </p>\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; margin-bottom: 5px; }\n```\n\n</Sandpack>\n\nこのフォームには 3 つの state 変数があります。`firstName`、`lastName`、そして `fullName` です。しかし、`fullName` は冗長です。**レンダー中に `fullName` は常に `firstName` と `lastName` から計算できるので、state から削除しましょう**。\n\n以下のようにします。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [firstName, setFirstName] = useState('');\n  const [lastName, setLastName] = useState('');\n\n  const fullName = firstName + ' ' + lastName;\n\n  function handleFirstNameChange(e) {\n    setFirstName(e.target.value);\n  }\n\n  function handleLastNameChange(e) {\n    setLastName(e.target.value);\n  }\n\n  return (\n    <>\n      <h2>Let’s check you in</h2>\n      <label>\n        First name:{' '}\n        <input\n          value={firstName}\n          onChange={handleFirstNameChange}\n        />\n      </label>\n      <label>\n        Last name:{' '}\n        <input\n          value={lastName}\n          onChange={handleLastNameChange}\n        />\n      </label>\n      <p>\n        Your ticket will be issued to: <b>{fullName}</b>\n      </p>\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; margin-bottom: 5px; }\n```\n\n</Sandpack>\n\nこれで `fullName` は *state 変数ではなくなっています*。代わりに、レンダー中に計算されます：\n\n```js\nconst fullName = firstName + ' ' + lastName;\n```\n\n結果的に、これを更新するために change ハンドラは何も特別なことをする必要がなくなりました。`setFirstName` や `setLastName` を呼び出すと、再レンダーがトリガされ、次の `fullName` は新しいデータから計算し直されます。\n\n<DeepDive>\n\n#### props を state にコピーしない {/*don-t-mirror-props-in-state*/}\n\n冗長な state の一般的な例として、このようなコードがあります：\n\n```js\nfunction Message({ messageColor }) {\n  const [color, setColor] = useState(messageColor);\n```\n\nここでは、`color` という state 変数が props である `messageColor` の値で初期化されています。問題は、**親コンポーネントが後で異なる `messageColor` 値（例えば `'blue'` から `'red'`）を渡してきた場合、*state 変数である* `color` の方は更新されない**ということです！ state は最初のレンダー時にのみ初期化されます。\n\nこれが、props を state 変数に「コピー」することが混乱を招く理由です。代わりに、`messageColor` をコードで直接使用してください。短い名前にしたい場合は、定数を使用してください：\n\n```js\nfunction Message({ messageColor }) {\n  const color = messageColor;\n```\n\nこれにより、親コンポーネントから渡された props と同期されなくなってしまうことを防げます。\n\nprops を state に「コピー」することが意味を持つのは、特定の props のすべての更新を*意図的に*無視したい場合だけです。慣習として、新しい値が来ても無視されるということを明確にしたい場合は、props の名前を `initial` または `default` で始めるようにします。\n\n```js\nfunction Message({ initialColor }) {\n  // The `color` state variable holds the *first* value of `initialColor`.\n  // Further changes to the `initialColor` prop are ignored.\n  const [color, setColor] = useState(initialColor);\n```\n\n</DeepDive>\n\n## state 内の重複を避ける {/*avoid-duplication-in-state*/}\n\nこのメニューリストコンポーネントでは、旅行に持っていくお菓子を複数の選択肢から 1 つだけ選ぶことができます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nconst initialItems = [\n  { title: 'pretzels', id: 0 },\n  { title: 'crispy seaweed', id: 1 },\n  { title: 'granola bar', id: 2 },\n];\n\nexport default function Menu() {\n  const [items, setItems] = useState(initialItems);\n  const [selectedItem, setSelectedItem] = useState(\n    items[0]\n  );\n\n  return (\n    <>\n      <h2>What's your travel snack?</h2>\n      <ul>\n        {items.map(item => (\n          <li key={item.id}>\n            {item.title}\n            {' '}\n            <button onClick={() => {\n              setSelectedItem(item);\n            }}>Choose</button>\n          </li>\n        ))}\n      </ul>\n      <p>You picked {selectedItem.title}.</p>\n    </>\n  );\n}\n```\n\n```css\nbutton { margin-top: 10px; }\n```\n\n</Sandpack>\n\n現在、選択した項目を `selectedItem` という state 変数にオブジェクトとして格納しています。しかしこれは良くありません。なぜなら、**`selectedItem` の内容は、`items` リスト内の要素のうちの 1 つと同一のオブジェクトになっている**ためです。これは、その項目に関する情報が 2 つの場所で重複していることを意味します。\n\nなぜこれが問題なのでしょうか? それぞれの項目を編集可能にしてみましょう。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nconst initialItems = [\n  { title: 'pretzels', id: 0 },\n  { title: 'crispy seaweed', id: 1 },\n  { title: 'granola bar', id: 2 },\n];\n\nexport default function Menu() {\n  const [items, setItems] = useState(initialItems);\n  const [selectedItem, setSelectedItem] = useState(\n    items[0]\n  );\n\n  function handleItemChange(id, e) {\n    setItems(items.map(item => {\n      if (item.id === id) {\n        return {\n          ...item,\n          title: e.target.value,\n        };\n      } else {\n        return item;\n      }\n    }));\n  }\n\n  return (\n    <>\n      <h2>What's your travel snack?</h2> \n      <ul>\n        {items.map((item, index) => (\n          <li key={item.id}>\n            <input\n              value={item.title}\n              onChange={e => {\n                handleItemChange(item.id, e)\n              }}\n            />\n            {' '}\n            <button onClick={() => {\n              setSelectedItem(item);\n            }}>Choose</button>\n          </li>\n        ))}\n      </ul>\n      <p>You picked {selectedItem.title}.</p>\n    </>\n  );\n}\n```\n\n```css\nbutton { margin-top: 10px; }\n```\n\n</Sandpack>\n\nいずれかの項目の \"Choose\" を*クリックしてから*編集すると、**入力欄は更新されますが、下部のラベルは編集内容を反映していません**。これは、state に重複があり、`selectedItem` 側の更新を忘れたためです。\n\n`selectedItem` 側も更新するようにしても良いのですが、簡単な解決策は重複を解消することです。この例では、`items` 内のオブジェクトと `selectedItem` オブジェクトを重複させる代わりに、state では `selectedId` を保持するようにし、その ID を持つアイテムを `items` 配列から検索することで `selectedItem` を取得するようにします。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nconst initialItems = [\n  { title: 'pretzels', id: 0 },\n  { title: 'crispy seaweed', id: 1 },\n  { title: 'granola bar', id: 2 },\n];\n\nexport default function Menu() {\n  const [items, setItems] = useState(initialItems);\n  const [selectedId, setSelectedId] = useState(0);\n\n  const selectedItem = items.find(item =>\n    item.id === selectedId\n  );\n\n  function handleItemChange(id, e) {\n    setItems(items.map(item => {\n      if (item.id === id) {\n        return {\n          ...item,\n          title: e.target.value,\n        };\n      } else {\n        return item;\n      }\n    }));\n  }\n\n  return (\n    <>\n      <h2>What's your travel snack?</h2>\n      <ul>\n        {items.map((item, index) => (\n          <li key={item.id}>\n            <input\n              value={item.title}\n              onChange={e => {\n                handleItemChange(item.id, e)\n              }}\n            />\n            {' '}\n            <button onClick={() => {\n              setSelectedId(item.id);\n            }}>Choose</button>\n          </li>\n        ))}\n      </ul>\n      <p>You picked {selectedItem.title}.</p>\n    </>\n  );\n}\n```\n\n```css\nbutton { margin-top: 10px; }\n```\n\n</Sandpack>\n\n以前は state がこのように重複していました。\n\n* `items = [{ id: 0, title: 'pretzels'}, ...]`\n* `selectedItem = {id: 0, title: 'pretzels'}`\n\nしかし、変更後は以下のようになります。\n\n* `items = [{ id: 0, title: 'pretzels'}, ...]`\n* `selectedId = 0`\n\n重複がなくなり、必要な state だけが残っています！\n\nこれで、*選択された*項目を編集すると、下のメッセージもすぐに更新されるようになります。これは、`setItems` が再レンダーをトリガし、`items.find(...)` がタイトル更新後の項目を見つけてくるためです。*選択された項目*のデータ全体を state に格納する必要はありませんでした。なぜなら*選択された項目 ID* だけが本質的なものだからです。残りはレンダー時に計算することができます。\n\n## 深くネストされた state を避ける {/*avoid-deeply-nested-state*/}\n\n惑星、大陸、国々で構成される旅行計画を想像してみてください。以下のようにして、state をネストしたオブジェクトと配列を駆使して構造化することが魅力的に思えるかもしれません。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { initialTravelPlan } from './places.js';\n\nfunction PlaceTree({ place }) {\n  const childPlaces = place.childPlaces;\n  return (\n    <li>\n      {place.title}\n      {childPlaces.length > 0 && (\n        <ol>\n          {childPlaces.map(place => (\n            <PlaceTree key={place.id} place={place} />\n          ))}\n        </ol>\n      )}\n    </li>\n  );\n}\n\nexport default function TravelPlan() {\n  const [plan, setPlan] = useState(initialTravelPlan);\n  const planets = plan.childPlaces;\n  return (\n    <>\n      <h2>Places to visit</h2>\n      <ol>\n        {planets.map(place => (\n          <PlaceTree key={place.id} place={place} />\n        ))}\n      </ol>\n    </>\n  );\n}\n```\n\n```js src/places.js active\nexport const initialTravelPlan = {\n  id: 0,\n  title: '(Root)',\n  childPlaces: [{\n    id: 1,\n    title: 'Earth',\n    childPlaces: [{\n      id: 2,\n      title: 'Africa',\n      childPlaces: [{\n        id: 3,\n        title: 'Botswana',\n        childPlaces: []\n      }, {\n        id: 4,\n        title: 'Egypt',\n        childPlaces: []\n      }, {\n        id: 5,\n        title: 'Kenya',\n        childPlaces: []\n      }, {\n        id: 6,\n        title: 'Madagascar',\n        childPlaces: []\n      }, {\n        id: 7,\n        title: 'Morocco',\n        childPlaces: []\n      }, {\n        id: 8,\n        title: 'Nigeria',\n        childPlaces: []\n      }, {\n        id: 9,\n        title: 'South Africa',\n        childPlaces: []\n      }]\n    }, {\n      id: 10,\n      title: 'Americas',\n      childPlaces: [{\n        id: 11,\n        title: 'Argentina',\n        childPlaces: []\n      }, {\n        id: 12,\n        title: 'Brazil',\n        childPlaces: []\n      }, {\n        id: 13,\n        title: 'Barbados',\n        childPlaces: []\n      }, {\n        id: 14,\n        title: 'Canada',\n        childPlaces: []\n      }, {\n        id: 15,\n        title: 'Jamaica',\n        childPlaces: []\n      }, {\n        id: 16,\n        title: 'Mexico',\n        childPlaces: []\n      }, {\n        id: 17,\n        title: 'Trinidad and Tobago',\n        childPlaces: []\n      }, {\n        id: 18,\n        title: 'Venezuela',\n        childPlaces: []\n      }]\n    }, {\n      id: 19,\n      title: 'Asia',\n      childPlaces: [{\n        id: 20,\n        title: 'China',\n        childPlaces: []\n      }, {\n        id: 21,\n        title: 'India',\n        childPlaces: []\n      }, {\n        id: 22,\n        title: 'Singapore',\n        childPlaces: []\n      }, {\n        id: 23,\n        title: 'South Korea',\n        childPlaces: []\n      }, {\n        id: 24,\n        title: 'Thailand',\n        childPlaces: []\n      }, {\n        id: 25,\n        title: 'Vietnam',\n        childPlaces: []\n      }]\n    }, {\n      id: 26,\n      title: 'Europe',\n      childPlaces: [{\n        id: 27,\n        title: 'Croatia',\n        childPlaces: [],\n      }, {\n        id: 28,\n        title: 'France',\n        childPlaces: [],\n      }, {\n        id: 29,\n        title: 'Germany',\n        childPlaces: [],\n      }, {\n        id: 30,\n        title: 'Italy',\n        childPlaces: [],\n      }, {\n        id: 31,\n        title: 'Portugal',\n        childPlaces: [],\n      }, {\n        id: 32,\n        title: 'Spain',\n        childPlaces: [],\n      }, {\n        id: 33,\n        title: 'Turkey',\n        childPlaces: [],\n      }]\n    }, {\n      id: 34,\n      title: 'Oceania',\n      childPlaces: [{\n        id: 35,\n        title: 'Australia',\n        childPlaces: [],\n      }, {\n        id: 36,\n        title: 'Bora Bora (French Polynesia)',\n        childPlaces: [],\n      }, {\n        id: 37,\n        title: 'Easter Island (Chile)',\n        childPlaces: [],\n      }, {\n        id: 38,\n        title: 'Fiji',\n        childPlaces: [],\n      }, {\n        id: 39,\n        title: 'Hawaii (the USA)',\n        childPlaces: [],\n      }, {\n        id: 40,\n        title: 'New Zealand',\n        childPlaces: [],\n      }, {\n        id: 41,\n        title: 'Vanuatu',\n        childPlaces: [],\n      }]\n    }]\n  }, {\n    id: 42,\n    title: 'Moon',\n    childPlaces: [{\n      id: 43,\n      title: 'Rheita',\n      childPlaces: []\n    }, {\n      id: 44,\n      title: 'Piccolomini',\n      childPlaces: []\n    }, {\n      id: 45,\n      title: 'Tycho',\n      childPlaces: []\n    }]\n  }, {\n    id: 46,\n    title: 'Mars',\n    childPlaces: [{\n      id: 47,\n      title: 'Corn Town',\n      childPlaces: []\n    }, {\n      id: 48,\n      title: 'Green Hill',\n      childPlaces: []      \n    }]\n  }]\n};\n```\n\n</Sandpack>\n\nここで、既に訪れた場所を削除するボタンを追加したくなったとしましょう。どのようにすればよいでしょうか？ [ネストされた state を更新する](/learn/updating-objects-in-state#updating-a-nested-object)と、変更された部分より上のすべてのオブジェクトのコピーを作成する必要が出てきます。深くネストされたところにある場所情報を削除するためには、親として繋がっている場所データをすべてコピーする必要があります。そのようなコードを書くのはとても大変です。\n\n**state が簡単に更新できないほどネストしている場合は、「フラット」にすることを検討してください**。ここでは、データを再構築する方法の 1 つを示します。`place` のそれぞれに*子となる場所情報*そのものの配列を持たせるのではなく、それぞれの場所が*子となる場所情報の ID* の配列を持つようにします。次に、それぞれの場所 ID と対応する場所情報のマッピングを格納します。\n\nこのようなデータ再構成を見ると、データベーステーブルを思い出すかもしれませんね。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { initialTravelPlan } from './places.js';\n\nfunction PlaceTree({ id, placesById }) {\n  const place = placesById[id];\n  const childIds = place.childIds;\n  return (\n    <li>\n      {place.title}\n      {childIds.length > 0 && (\n        <ol>\n          {childIds.map(childId => (\n            <PlaceTree\n              key={childId}\n              id={childId}\n              placesById={placesById}\n            />\n          ))}\n        </ol>\n      )}\n    </li>\n  );\n}\n\nexport default function TravelPlan() {\n  const [plan, setPlan] = useState(initialTravelPlan);\n  const root = plan[0];\n  const planetIds = root.childIds;\n  return (\n    <>\n      <h2>Places to visit</h2>\n      <ol>\n        {planetIds.map(id => (\n          <PlaceTree\n            key={id}\n            id={id}\n            placesById={plan}\n          />\n        ))}\n      </ol>\n    </>\n  );\n}\n```\n\n```js src/places.js active\nexport const initialTravelPlan = {\n  0: {\n    id: 0,\n    title: '(Root)',\n    childIds: [1, 42, 46],\n  },\n  1: {\n    id: 1,\n    title: 'Earth',\n    childIds: [2, 10, 19, 26, 34]\n  },\n  2: {\n    id: 2,\n    title: 'Africa',\n    childIds: [3, 4, 5, 6 , 7, 8, 9]\n  }, \n  3: {\n    id: 3,\n    title: 'Botswana',\n    childIds: []\n  },\n  4: {\n    id: 4,\n    title: 'Egypt',\n    childIds: []\n  },\n  5: {\n    id: 5,\n    title: 'Kenya',\n    childIds: []\n  },\n  6: {\n    id: 6,\n    title: 'Madagascar',\n    childIds: []\n  }, \n  7: {\n    id: 7,\n    title: 'Morocco',\n    childIds: []\n  },\n  8: {\n    id: 8,\n    title: 'Nigeria',\n    childIds: []\n  },\n  9: {\n    id: 9,\n    title: 'South Africa',\n    childIds: []\n  },\n  10: {\n    id: 10,\n    title: 'Americas',\n    childIds: [11, 12, 13, 14, 15, 16, 17, 18],   \n  },\n  11: {\n    id: 11,\n    title: 'Argentina',\n    childIds: []\n  },\n  12: {\n    id: 12,\n    title: 'Brazil',\n    childIds: []\n  },\n  13: {\n    id: 13,\n    title: 'Barbados',\n    childIds: []\n  }, \n  14: {\n    id: 14,\n    title: 'Canada',\n    childIds: []\n  },\n  15: {\n    id: 15,\n    title: 'Jamaica',\n    childIds: []\n  },\n  16: {\n    id: 16,\n    title: 'Mexico',\n    childIds: []\n  },\n  17: {\n    id: 17,\n    title: 'Trinidad and Tobago',\n    childIds: []\n  },\n  18: {\n    id: 18,\n    title: 'Venezuela',\n    childIds: []\n  },\n  19: {\n    id: 19,\n    title: 'Asia',\n    childIds: [20, 21, 22, 23, 24, 25],   \n  },\n  20: {\n    id: 20,\n    title: 'China',\n    childIds: []\n  },\n  21: {\n    id: 21,\n    title: 'India',\n    childIds: []\n  },\n  22: {\n    id: 22,\n    title: 'Singapore',\n    childIds: []\n  },\n  23: {\n    id: 23,\n    title: 'South Korea',\n    childIds: []\n  },\n  24: {\n    id: 24,\n    title: 'Thailand',\n    childIds: []\n  },\n  25: {\n    id: 25,\n    title: 'Vietnam',\n    childIds: []\n  },\n  26: {\n    id: 26,\n    title: 'Europe',\n    childIds: [27, 28, 29, 30, 31, 32, 33],   \n  },\n  27: {\n    id: 27,\n    title: 'Croatia',\n    childIds: []\n  },\n  28: {\n    id: 28,\n    title: 'France',\n    childIds: []\n  },\n  29: {\n    id: 29,\n    title: 'Germany',\n    childIds: []\n  },\n  30: {\n    id: 30,\n    title: 'Italy',\n    childIds: []\n  },\n  31: {\n    id: 31,\n    title: 'Portugal',\n    childIds: []\n  },\n  32: {\n    id: 32,\n    title: 'Spain',\n    childIds: []\n  },\n  33: {\n    id: 33,\n    title: 'Turkey',\n    childIds: []\n  },\n  34: {\n    id: 34,\n    title: 'Oceania',\n    childIds: [35, 36, 37, 38, 39, 40, 41],   \n  },\n  35: {\n    id: 35,\n    title: 'Australia',\n    childIds: []\n  },\n  36: {\n    id: 36,\n    title: 'Bora Bora (French Polynesia)',\n    childIds: []\n  },\n  37: {\n    id: 37,\n    title: 'Easter Island (Chile)',\n    childIds: []\n  },\n  38: {\n    id: 38,\n    title: 'Fiji',\n    childIds: []\n  },\n  39: {\n    id: 40,\n    title: 'Hawaii (the USA)',\n    childIds: []\n  },\n  40: {\n    id: 40,\n    title: 'New Zealand',\n    childIds: []\n  },\n  41: {\n    id: 41,\n    title: 'Vanuatu',\n    childIds: []\n  },\n  42: {\n    id: 42,\n    title: 'Moon',\n    childIds: [43, 44, 45]\n  },\n  43: {\n    id: 43,\n    title: 'Rheita',\n    childIds: []\n  },\n  44: {\n    id: 44,\n    title: 'Piccolomini',\n    childIds: []\n  },\n  45: {\n    id: 45,\n    title: 'Tycho',\n    childIds: []\n  },\n  46: {\n    id: 46,\n    title: 'Mars',\n    childIds: [47, 48]\n  },\n  47: {\n    id: 47,\n    title: 'Corn Town',\n    childIds: []\n  },\n  48: {\n    id: 48,\n    title: 'Green Hill',\n    childIds: []\n  }\n};\n```\n\n</Sandpack>\n\n**state が「フラット」な（別名「正規化された (normalized)」）状態になったので、ネストされた項目の更新が簡単になります。**\n\n場所を削除したい場合、state を 2 レベルにわたって更新するだけで済みます。\n\n- *親*の場所情報を更新して、`childIds` 配列から、削除された場所の ID を除外する。\n- ルートの「テーブル」オブジェクトを更新して、上記の更新された親の場所情報を含むようにする。\n\n以下がやり方の一例です。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { initialTravelPlan } from './places.js';\n\nexport default function TravelPlan() {\n  const [plan, setPlan] = useState(initialTravelPlan);\n\n  function handleComplete(parentId, childId) {\n    const parent = plan[parentId];\n    // Create a new version of the parent place\n    // that doesn't include this child ID.\n    const nextParent = {\n      ...parent,\n      childIds: parent.childIds\n        .filter(id => id !== childId)\n    };\n    // Update the root state object...\n    setPlan({\n      ...plan,\n      // ...so that it has the updated parent.\n      [parentId]: nextParent\n    });\n  }\n\n  const root = plan[0];\n  const planetIds = root.childIds;\n  return (\n    <>\n      <h2>Places to visit</h2>\n      <ol>\n        {planetIds.map(id => (\n          <PlaceTree\n            key={id}\n            id={id}\n            parentId={0}\n            placesById={plan}\n            onComplete={handleComplete}\n          />\n        ))}\n      </ol>\n    </>\n  );\n}\n\nfunction PlaceTree({ id, parentId, placesById, onComplete }) {\n  const place = placesById[id];\n  const childIds = place.childIds;\n  return (\n    <li>\n      {place.title}\n      <button onClick={() => {\n        onComplete(parentId, id);\n      }}>\n        Complete\n      </button>\n      {childIds.length > 0 &&\n        <ol>\n          {childIds.map(childId => (\n            <PlaceTree\n              key={childId}\n              id={childId}\n              parentId={id}\n              placesById={placesById}\n              onComplete={onComplete}\n            />\n          ))}\n        </ol>\n      }\n    </li>\n  );\n}\n```\n\n```js src/places.js\nexport const initialTravelPlan = {\n  0: {\n    id: 0,\n    title: '(Root)',\n    childIds: [1, 42, 46],\n  },\n  1: {\n    id: 1,\n    title: 'Earth',\n    childIds: [2, 10, 19, 26, 34]\n  },\n  2: {\n    id: 2,\n    title: 'Africa',\n    childIds: [3, 4, 5, 6 , 7, 8, 9]\n  }, \n  3: {\n    id: 3,\n    title: 'Botswana',\n    childIds: []\n  },\n  4: {\n    id: 4,\n    title: 'Egypt',\n    childIds: []\n  },\n  5: {\n    id: 5,\n    title: 'Kenya',\n    childIds: []\n  },\n  6: {\n    id: 6,\n    title: 'Madagascar',\n    childIds: []\n  }, \n  7: {\n    id: 7,\n    title: 'Morocco',\n    childIds: []\n  },\n  8: {\n    id: 8,\n    title: 'Nigeria',\n    childIds: []\n  },\n  9: {\n    id: 9,\n    title: 'South Africa',\n    childIds: []\n  },\n  10: {\n    id: 10,\n    title: 'Americas',\n    childIds: [11, 12, 13, 14, 15, 16, 17, 18],   \n  },\n  11: {\n    id: 11,\n    title: 'Argentina',\n    childIds: []\n  },\n  12: {\n    id: 12,\n    title: 'Brazil',\n    childIds: []\n  },\n  13: {\n    id: 13,\n    title: 'Barbados',\n    childIds: []\n  }, \n  14: {\n    id: 14,\n    title: 'Canada',\n    childIds: []\n  },\n  15: {\n    id: 15,\n    title: 'Jamaica',\n    childIds: []\n  },\n  16: {\n    id: 16,\n    title: 'Mexico',\n    childIds: []\n  },\n  17: {\n    id: 17,\n    title: 'Trinidad and Tobago',\n    childIds: []\n  },\n  18: {\n    id: 18,\n    title: 'Venezuela',\n    childIds: []\n  },\n  19: {\n    id: 19,\n    title: 'Asia',\n    childIds: [20, 21, 22, 23, 24, 25],   \n  },\n  20: {\n    id: 20,\n    title: 'China',\n    childIds: []\n  },\n  21: {\n    id: 21,\n    title: 'India',\n    childIds: []\n  },\n  22: {\n    id: 22,\n    title: 'Singapore',\n    childIds: []\n  },\n  23: {\n    id: 23,\n    title: 'South Korea',\n    childIds: []\n  },\n  24: {\n    id: 24,\n    title: 'Thailand',\n    childIds: []\n  },\n  25: {\n    id: 25,\n    title: 'Vietnam',\n    childIds: []\n  },\n  26: {\n    id: 26,\n    title: 'Europe',\n    childIds: [27, 28, 29, 30, 31, 32, 33],   \n  },\n  27: {\n    id: 27,\n    title: 'Croatia',\n    childIds: []\n  },\n  28: {\n    id: 28,\n    title: 'France',\n    childIds: []\n  },\n  29: {\n    id: 29,\n    title: 'Germany',\n    childIds: []\n  },\n  30: {\n    id: 30,\n    title: 'Italy',\n    childIds: []\n  },\n  31: {\n    id: 31,\n    title: 'Portugal',\n    childIds: []\n  },\n  32: {\n    id: 32,\n    title: 'Spain',\n    childIds: []\n  },\n  33: {\n    id: 33,\n    title: 'Turkey',\n    childIds: []\n  },\n  34: {\n    id: 34,\n    title: 'Oceania',\n    childIds: [35, 36, 37, 38, 39, 40, 41],   \n  },\n  35: {\n    id: 35,\n    title: 'Australia',\n    childIds: []\n  },\n  36: {\n    id: 36,\n    title: 'Bora Bora (French Polynesia)',\n    childIds: []\n  },\n  37: {\n    id: 37,\n    title: 'Easter Island (Chile)',\n    childIds: []\n  },\n  38: {\n    id: 38,\n    title: 'Fiji',\n    childIds: []\n  },\n  39: {\n    id: 39,\n    title: 'Hawaii (the USA)',\n    childIds: []\n  },\n  40: {\n    id: 40,\n    title: 'New Zealand',\n    childIds: []\n  },\n  41: {\n    id: 41,\n    title: 'Vanuatu',\n    childIds: []\n  },\n  42: {\n    id: 42,\n    title: 'Moon',\n    childIds: [43, 44, 45]\n  },\n  43: {\n    id: 43,\n    title: 'Rheita',\n    childIds: []\n  },\n  44: {\n    id: 44,\n    title: 'Piccolomini',\n    childIds: []\n  },\n  45: {\n    id: 45,\n    title: 'Tycho',\n    childIds: []\n  },\n  46: {\n    id: 46,\n    title: 'Mars',\n    childIds: [47, 48]\n  },\n  47: {\n    id: 47,\n    title: 'Corn Town',\n    childIds: []\n  },\n  48: {\n    id: 48,\n    title: 'Green Hill',\n    childIds: []\n  }\n};\n```\n\n```css\nbutton { margin: 10px; }\n```\n\n</Sandpack>\n\nstate は好きなだけネストさせることができますが、「フラット」にすることで多くの問題を解決できます。state の更新が容易になるだけでなく、ネストされたオブジェクトのさまざまな部分で重複がないことを保証するのにも役立ちます。\n\n<DeepDive>\n\n#### メモリ使用量の改善 {/*improving-memory-usage*/}\n\n理想的には、削除された場所アイテム（およびその子アイテム！）自体も「テーブル」オブジェクトから削除して、メモリ使用量を改善するとよいでしょう。以下のバージョンはそれを行うものです。また、アップデートロジックをより簡潔にするために [Immer を使用](/learn/updating-objects-in-state#write-concise-update-logic-with-immer)しています。\n\n<Sandpack>\n\n```js\nimport { useImmer } from 'use-immer';\nimport { initialTravelPlan } from './places.js';\n\nexport default function TravelPlan() {\n  const [plan, updatePlan] = useImmer(initialTravelPlan);\n\n  function handleComplete(parentId, childId) {\n    updatePlan(draft => {\n      // Remove from the parent place's child IDs.\n      const parent = draft[parentId];\n      parent.childIds = parent.childIds\n        .filter(id => id !== childId);\n\n      // Forget this place and all its subtree.\n      deleteAllChildren(childId);\n      function deleteAllChildren(id) {\n        const place = draft[id];\n        place.childIds.forEach(deleteAllChildren);\n        delete draft[id];\n      }\n    });\n  }\n\n  const root = plan[0];\n  const planetIds = root.childIds;\n  return (\n    <>\n      <h2>Places to visit</h2>\n      <ol>\n        {planetIds.map(id => (\n          <PlaceTree\n            key={id}\n            id={id}\n            parentId={0}\n            placesById={plan}\n            onComplete={handleComplete}\n          />\n        ))}\n      </ol>\n    </>\n  );\n}\n\nfunction PlaceTree({ id, parentId, placesById, onComplete }) {\n  const place = placesById[id];\n  const childIds = place.childIds;\n  return (\n    <li>\n      {place.title}\n      <button onClick={() => {\n        onComplete(parentId, id);\n      }}>\n        Complete\n      </button>\n      {childIds.length > 0 &&\n        <ol>\n          {childIds.map(childId => (\n            <PlaceTree\n              key={childId}\n              id={childId}\n              parentId={id}\n              placesById={placesById}\n              onComplete={onComplete}\n            />\n          ))}\n        </ol>\n      }\n    </li>\n  );\n}\n```\n\n```js src/places.js\nexport const initialTravelPlan = {\n  0: {\n    id: 0,\n    title: '(Root)',\n    childIds: [1, 42, 46],\n  },\n  1: {\n    id: 1,\n    title: 'Earth',\n    childIds: [2, 10, 19, 26, 34]\n  },\n  2: {\n    id: 2,\n    title: 'Africa',\n    childIds: [3, 4, 5, 6 , 7, 8, 9]\n  }, \n  3: {\n    id: 3,\n    title: 'Botswana',\n    childIds: []\n  },\n  4: {\n    id: 4,\n    title: 'Egypt',\n    childIds: []\n  },\n  5: {\n    id: 5,\n    title: 'Kenya',\n    childIds: []\n  },\n  6: {\n    id: 6,\n    title: 'Madagascar',\n    childIds: []\n  }, \n  7: {\n    id: 7,\n    title: 'Morocco',\n    childIds: []\n  },\n  8: {\n    id: 8,\n    title: 'Nigeria',\n    childIds: []\n  },\n  9: {\n    id: 9,\n    title: 'South Africa',\n    childIds: []\n  },\n  10: {\n    id: 10,\n    title: 'Americas',\n    childIds: [11, 12, 13, 14, 15, 16, 17, 18],   \n  },\n  11: {\n    id: 11,\n    title: 'Argentina',\n    childIds: []\n  },\n  12: {\n    id: 12,\n    title: 'Brazil',\n    childIds: []\n  },\n  13: {\n    id: 13,\n    title: 'Barbados',\n    childIds: []\n  }, \n  14: {\n    id: 14,\n    title: 'Canada',\n    childIds: []\n  },\n  15: {\n    id: 15,\n    title: 'Jamaica',\n    childIds: []\n  },\n  16: {\n    id: 16,\n    title: 'Mexico',\n    childIds: []\n  },\n  17: {\n    id: 17,\n    title: 'Trinidad and Tobago',\n    childIds: []\n  },\n  18: {\n    id: 18,\n    title: 'Venezuela',\n    childIds: []\n  },\n  19: {\n    id: 19,\n    title: 'Asia',\n    childIds: [20, 21, 22, 23, 24, 25,],   \n  },\n  20: {\n    id: 20,\n    title: 'China',\n    childIds: []\n  },\n  21: {\n    id: 21,\n    title: 'India',\n    childIds: []\n  },\n  22: {\n    id: 22,\n    title: 'Singapore',\n    childIds: []\n  },\n  23: {\n    id: 23,\n    title: 'South Korea',\n    childIds: []\n  },\n  24: {\n    id: 24,\n    title: 'Thailand',\n    childIds: []\n  },\n  25: {\n    id: 25,\n    title: 'Vietnam',\n    childIds: []\n  },\n  26: {\n    id: 26,\n    title: 'Europe',\n    childIds: [27, 28, 29, 30, 31, 32, 33],   \n  },\n  27: {\n    id: 27,\n    title: 'Croatia',\n    childIds: []\n  },\n  28: {\n    id: 28,\n    title: 'France',\n    childIds: []\n  },\n  29: {\n    id: 29,\n    title: 'Germany',\n    childIds: []\n  },\n  30: {\n    id: 30,\n    title: 'Italy',\n    childIds: []\n  },\n  31: {\n    id: 31,\n    title: 'Portugal',\n    childIds: []\n  },\n  32: {\n    id: 32,\n    title: 'Spain',\n    childIds: []\n  },\n  33: {\n    id: 33,\n    title: 'Turkey',\n    childIds: []\n  },\n  34: {\n    id: 34,\n    title: 'Oceania',\n    childIds: [35, 36, 37, 38, 39, 40, 41],   \n  },\n  35: {\n    id: 35,\n    title: 'Australia',\n    childIds: []\n  },\n  36: {\n    id: 36,\n    title: 'Bora Bora (French Polynesia)',\n    childIds: []\n  },\n  37: {\n    id: 37,\n    title: 'Easter Island (Chile)',\n    childIds: []\n  },\n  38: {\n    id: 38,\n    title: 'Fiji',\n    childIds: []\n  },\n  39: {\n    id: 39,\n    title: 'Hawaii (the USA)',\n    childIds: []\n  },\n  40: {\n    id: 40,\n    title: 'New Zealand',\n    childIds: []\n  },\n  41: {\n    id: 41,\n    title: 'Vanuatu',\n    childIds: []\n  },\n  42: {\n    id: 42,\n    title: 'Moon',\n    childIds: [43, 44, 45]\n  },\n  43: {\n    id: 43,\n    title: 'Rheita',\n    childIds: []\n  },\n  44: {\n    id: 44,\n    title: 'Piccolomini',\n    childIds: []\n  },\n  45: {\n    id: 45,\n    title: 'Tycho',\n    childIds: []\n  },\n  46: {\n    id: 46,\n    title: 'Mars',\n    childIds: [47, 48]\n  },\n  47: {\n    id: 47,\n    title: 'Corn Town',\n    childIds: []\n  },\n  48: {\n    id: 48,\n    title: 'Green Hill',\n    childIds: []\n  }\n};\n```\n\n```css\nbutton { margin: 10px; }\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"use-immer\": \"0.5.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\n</DeepDive>\n\nネストされている state の一部を子コンポーネントに移動することで、state のネストを減らすことが可能な場合もあります。これは「アイテムがホバーされているか」といった、保存する必要のない一時的な UI 関連の state で適しています。\n\n<Recap>\n\n* 2 つの state 変数が常に一緒に更新される場合は、それらを 1 つにまとめることを検討する。\n* 「ありえない」state を作成しないよう、state 変数を注意深く選択する。\n* state は、更新時に間違いが発生しづらいやり方で構成する。\n* 冗長で重複した state を避け、同期する必要がないようにする。\n* 意図的に更新されないようにしたい場合を除き、props を state にコピーしない。\n* 項目選択のような UI パターンにおいては、state にオブジェクト自体ではなく ID またはインデックスを保持する。\n* 深くネストされた state の更新が複雑な場合は、フラット化を試す。\n\n</Recap>\n\n<Challenges>\n\n#### 更新されないコンポーネントの修正 {/*fix-a-component-thats-not-updating*/}\n\nこの `Clock` コンポーネントは、`color` と `time` の 2 つの props を受け取ります。セレクトボックスで別の色を選択すると、`Clock` コンポーネントは親コンポーネントから props として異なる `color` を受け取るようになっています。しかし、何らかの理由で表示される色が更新されません。なぜでしょうか？ 問題を修正してください。\n\n<Sandpack>\n\n```js src/Clock.js active\nimport { useState } from 'react';\n\nexport default function Clock(props) {\n  const [color, setColor] = useState(props.color);\n  return (\n    <h1 style={{ color: color }}>\n      {props.time}\n    </h1>\n  );\n}\n```\n\n```js src/App.js hidden\nimport { useState, useEffect } from 'react';\nimport Clock from './Clock.js';\n\nfunction useTime() {\n  const [time, setTime] = useState(() => new Date());\n  useEffect(() => {\n    const id = setInterval(() => {\n      setTime(new Date());\n    }, 1000);\n    return () => clearInterval(id);\n  }, []);\n  return time;\n}\n\nexport default function App() {\n  const time = useTime();\n  const [color, setColor] = useState('lightcoral');\n  return (\n    <div>\n      <p>\n        Pick a color:{' '}\n        <select value={color} onChange={e => setColor(e.target.value)}>\n          <option value=\"lightcoral\">lightcoral</option>\n          <option value=\"midnightblue\">midnightblue</option>\n          <option value=\"rebeccapurple\">rebeccapurple</option>\n        </select>\n      </p>\n      <Clock color={color} time={time.toLocaleTimeString()} />\n    </div>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution>\n\nこの問題は、このコンポーネントが `color` という state を `color` という props の初期値を使って初期化していることが原因です。しかし、props としての `color` が変更されても state 変数の方は影響されません！ それらが同期されなくなってしまっているわけです。この問題を解決するには state 変数自体を削除し、props としての `color` を直接使用します。\n\n<Sandpack>\n\n```js src/Clock.js active\nimport { useState } from 'react';\n\nexport default function Clock(props) {\n  return (\n    <h1 style={{ color: props.color }}>\n      {props.time}\n    </h1>\n  );\n}\n```\n\n```js src/App.js hidden\nimport { useState, useEffect } from 'react';\nimport Clock from './Clock.js';\n\nfunction useTime() {\n  const [time, setTime] = useState(() => new Date());\n  useEffect(() => {\n    const id = setInterval(() => {\n      setTime(new Date());\n    }, 1000);\n    return () => clearInterval(id);\n  }, []);\n  return time;\n}\n\nexport default function App() {\n  const time = useTime();\n  const [color, setColor] = useState('lightcoral');\n  return (\n    <div>\n      <p>\n        Pick a color:{' '}\n        <select value={color} onChange={e => setColor(e.target.value)}>\n          <option value=\"lightcoral\">lightcoral</option>\n          <option value=\"midnightblue\">midnightblue</option>\n          <option value=\"rebeccapurple\">rebeccapurple</option>\n        </select>\n      </p>\n      <Clock color={color} time={time.toLocaleTimeString()} />\n    </div>\n  );\n}\n```\n\n</Sandpack>\n\nまたは分割代入構文を使用して以下のようにします。\n\n<Sandpack>\n\n```js src/Clock.js active\nimport { useState } from 'react';\n\nexport default function Clock({ color, time }) {\n  return (\n    <h1 style={{ color: color }}>\n      {time}\n    </h1>\n  );\n}\n```\n\n```js src/App.js hidden\nimport { useState, useEffect } from 'react';\nimport Clock from './Clock.js';\n\nfunction useTime() {\n  const [time, setTime] = useState(() => new Date());\n  useEffect(() => {\n    const id = setInterval(() => {\n      setTime(new Date());\n    }, 1000);\n    return () => clearInterval(id);\n  }, []);\n  return time;\n}\n\nexport default function App() {\n  const time = useTime();\n  const [color, setColor] = useState('lightcoral');\n  return (\n    <div>\n      <p>\n        Pick a color:{' '}\n        <select value={color} onChange={e => setColor(e.target.value)}>\n          <option value=\"lightcoral\">lightcoral</option>\n          <option value=\"midnightblue\">midnightblue</option>\n          <option value=\"rebeccapurple\">rebeccapurple</option>\n        </select>\n      </p>\n      <Clock color={color} time={time.toLocaleTimeString()} />\n    </div>\n  );\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n#### 壊れた荷物リストの修正 {/*fix-a-broken-packing-list*/}\n\nこの荷物 (packing) リストには、梱包済みアイテム数と全体のアイテム数を表示するフッタがあります。最初はうまく機能するように見えますが、バグがあります。例えば、アイテムに梱包済みとマークしてから削除しても、カウンタが正しく更新されません。カウンタが常に正確になるように修正してください。\n\n<Hint>\n\nこの例の state のどれかは冗長ではないでしょうか？\n\n</Hint>\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport AddItem from './AddItem.js';\nimport PackingList from './PackingList.js';\n\nlet nextId = 3;\nconst initialItems = [\n  { id: 0, title: 'Warm socks', packed: true },\n  { id: 1, title: 'Travel journal', packed: false },\n  { id: 2, title: 'Watercolors', packed: false },\n];\n\nexport default function TravelPlan() {\n  const [items, setItems] = useState(initialItems);\n  const [total, setTotal] = useState(3);\n  const [packed, setPacked] = useState(1);\n\n  function handleAddItem(title) {\n    setTotal(total + 1);\n    setItems([\n      ...items,\n      {\n        id: nextId++,\n        title: title,\n        packed: false\n      }\n    ]);\n  }\n\n  function handleChangeItem(nextItem) {\n    if (nextItem.packed) {\n      setPacked(packed + 1);\n    } else {\n      setPacked(packed - 1);\n    }\n    setItems(items.map(item => {\n      if (item.id === nextItem.id) {\n        return nextItem;\n      } else {\n        return item;\n      }\n    }));\n  }\n\n  function handleDeleteItem(itemId) {\n    setTotal(total - 1);\n    setItems(\n      items.filter(item => item.id !== itemId)\n    );\n  }\n\n  return (\n    <>  \n      <AddItem\n        onAddItem={handleAddItem}\n      />\n      <PackingList\n        items={items}\n        onChangeItem={handleChangeItem}\n        onDeleteItem={handleDeleteItem}\n      />\n      <hr />\n      <b>{packed} out of {total} packed!</b>\n    </>\n  );\n}\n```\n\n```js src/AddItem.js hidden\nimport { useState } from 'react';\n\nexport default function AddItem({ onAddItem }) {\n  const [title, setTitle] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add item\"\n        value={title}\n        onChange={e => setTitle(e.target.value)}\n      />\n      <button onClick={() => {\n        setTitle('');\n        onAddItem(title);\n      }}>Add</button>\n    </>\n  )\n}\n```\n\n```js src/PackingList.js hidden\nimport { useState } from 'react';\n\nexport default function PackingList({\n  items,\n  onChangeItem,\n  onDeleteItem\n}) {\n  return (\n    <ul>\n      {items.map(item => (\n        <li key={item.id}>\n          <label>\n            <input\n              type=\"checkbox\"\n              checked={item.packed}\n              onChange={e => {\n                onChangeItem({\n                  ...item,\n                  packed: e.target.checked\n                });\n              }}\n            />\n            {' '}\n            {item.title}\n          </label>\n          <button onClick={() => onDeleteItem(item.id)}>\n            Delete\n          </button>\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\n<Solution>\n\nそれぞれのイベントハンドラを注意深く書き換えて `total` と `packed` カウンタが正しく更新されるようにすることも可能ですが、根本的な問題は、そもそもこれらの state 変数が存在しているということです。`items` 配列自体からアイテム数（梱包済み・合計のいずれも）は常に計算できるので、そもそもこれらは不要です。冗長な state を削除してバグを修正しましょう。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport AddItem from './AddItem.js';\nimport PackingList from './PackingList.js';\n\nlet nextId = 3;\nconst initialItems = [\n  { id: 0, title: 'Warm socks', packed: true },\n  { id: 1, title: 'Travel journal', packed: false },\n  { id: 2, title: 'Watercolors', packed: false },\n];\n\nexport default function TravelPlan() {\n  const [items, setItems] = useState(initialItems);\n\n  const total = items.length;\n  const packed = items\n    .filter(item => item.packed)\n    .length;\n\n  function handleAddItem(title) {\n    setItems([\n      ...items,\n      {\n        id: nextId++,\n        title: title,\n        packed: false\n      }\n    ]);\n  }\n\n  function handleChangeItem(nextItem) {\n    setItems(items.map(item => {\n      if (item.id === nextItem.id) {\n        return nextItem;\n      } else {\n        return item;\n      }\n    }));\n  }\n\n  function handleDeleteItem(itemId) {\n    setItems(\n      items.filter(item => item.id !== itemId)\n    );\n  }\n\n  return (\n    <>  \n      <AddItem\n        onAddItem={handleAddItem}\n      />\n      <PackingList\n        items={items}\n        onChangeItem={handleChangeItem}\n        onDeleteItem={handleDeleteItem}\n      />\n      <hr />\n      <b>{packed} out of {total} packed!</b>\n    </>\n  );\n}\n```\n\n```js src/AddItem.js hidden\nimport { useState } from 'react';\n\nexport default function AddItem({ onAddItem }) {\n  const [title, setTitle] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add item\"\n        value={title}\n        onChange={e => setTitle(e.target.value)}\n      />\n      <button onClick={() => {\n        setTitle('');\n        onAddItem(title);\n      }}>Add</button>\n    </>\n  )\n}\n```\n\n```js src/PackingList.js hidden\nimport { useState } from 'react';\n\nexport default function PackingList({\n  items,\n  onChangeItem,\n  onDeleteItem\n}) {\n  return (\n    <ul>\n      {items.map(item => (\n        <li key={item.id}>\n          <label>\n            <input\n              type=\"checkbox\"\n              checked={item.packed}\n              onChange={e => {\n                onChangeItem({\n                  ...item,\n                  packed: e.target.checked\n                });\n              }}\n            />\n            {' '}\n            {item.title}\n          </label>\n          <button onClick={() => onDeleteItem(item.id)}>\n            Delete\n          </button>\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\nこの変更により、それぞれのイベントハンドラは `setItems` を呼び出すことだけを考えればよくなったことに注目しましょう。アイテム数は次のレンダー時に `items` から計算されるため、常に最新情報が表示されます。\n\n</Solution>\n\n#### 選択項目が消える問題を修正 {/*fix-the-disappearing-selection*/}\n\n手紙のリストである `letters` が state に保持されています。手紙のどれかにホバーまたはフォーカスすると、その手紙がハイライトされるようになっています。現在ハイライト中の手紙は、`highlightedLetter` という state 変数に格納されています。個々の手紙に対して \"Star\" や \"Unstar\"（スター解除）ができ、それにより state である `letters` 配列が更新されます。\n\n機能はしていますが、このコードには小さな UI の不具合があります。\"Star\" や \"Unstar\" を押すと、一瞬だけハイライトが消えてしまうのです。ただしポインタを動かすか、キーボードで別の手紙に切り替えるとすぐにハイライトは再表示されます。なぜこれが起こるのでしょうか？ ボタンクリック後にハイライトが消えないように修正してください。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { initialLetters } from './data.js';\nimport Letter from './Letter.js';\n\nexport default function MailClient() {\n  const [letters, setLetters] = useState(initialLetters);\n  const [highlightedLetter, setHighlightedLetter] = useState(null);\n\n  function handleHover(letter) {\n    setHighlightedLetter(letter);\n  }\n\n  function handleStar(starred) {\n    setLetters(letters.map(letter => {\n      if (letter.id === starred.id) {\n        return {\n          ...letter,\n          isStarred: !letter.isStarred\n        };\n      } else {\n        return letter;\n      }\n    }));\n  }\n\n  return (\n    <>\n      <h2>Inbox</h2>\n      <ul>\n        {letters.map(letter => (\n          <Letter\n            key={letter.id}\n            letter={letter}\n            isHighlighted={\n              letter === highlightedLetter\n            }\n            onHover={handleHover}\n            onToggleStar={handleStar}\n          />\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n```js src/Letter.js\nexport default function Letter({\n  letter,\n  isHighlighted,\n  onHover,\n  onToggleStar,\n}) {\n  return (\n    <li\n      className={\n        isHighlighted ? 'highlighted' : ''\n      }\n      onFocus={() => {\n        onHover(letter);        \n      }}\n      onPointerMove={() => {\n        onHover(letter);\n      }}\n    >\n      <button onClick={() => {\n        onToggleStar(letter);\n      }}>\n        {letter.isStarred ? 'Unstar' : 'Star'}\n      </button>\n      {letter.subject}\n    </li>\n  )\n}\n```\n\n```js src/data.js\nexport const initialLetters = [{\n  id: 0,\n  subject: 'Ready for adventure?',\n  isStarred: true,\n}, {\n  id: 1,\n  subject: 'Time to check in!',\n  isStarred: false,\n}, {\n  id: 2,\n  subject: 'Festival Begins in Just SEVEN Days!',\n  isStarred: false,\n}];\n```\n\n```css\nbutton { margin: 5px; }\nli { border-radius: 5px; }\n.highlighted { background: #d2eaff; }\n```\n\n</Sandpack>\n\n<Solution>\n\n問題は、`highlightedLetter` に手紙オブジェクトを保持していることです。しかし同じ情報を `letters` 配列にも保持しています。つまり state に重複があるのです！ ボタンクリックに応じて `letters` 配列を更新する際に、`highlightedLetter` とは異なる新しい手紙データオブジェクトが作成されます。これが、`highlightedLetter === letter` チェックが `false` になってハイライトが消えてしまう理由です。次にポインタが移動して `setHighlightedLetter` が呼び出されるとハイライトは再表示されます。\n\nこの問題を解決するには、state から重複を削除します。2 つの場所で*その手紙データ自体*を格納する代わりに、`highlightedId` を格納するようにします。これにより、`letter.id === highlightedId` とすることで、前回のレンダーから `letter` オブジェクトが変更された場合でも、各手紙の `isHighlighted` をチェックできます。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { initialLetters } from './data.js';\nimport Letter from './Letter.js';\n\nexport default function MailClient() {\n  const [letters, setLetters] = useState(initialLetters);\n  const [highlightedId, setHighlightedId ] = useState(null);\n\n  function handleHover(letterId) {\n    setHighlightedId(letterId);\n  }\n\n  function handleStar(starredId) {\n    setLetters(letters.map(letter => {\n      if (letter.id === starredId) {\n        return {\n          ...letter,\n          isStarred: !letter.isStarred\n        };\n      } else {\n        return letter;\n      }\n    }));\n  }\n\n  return (\n    <>\n      <h2>Inbox</h2>\n      <ul>\n        {letters.map(letter => (\n          <Letter\n            key={letter.id}\n            letter={letter}\n            isHighlighted={\n              letter.id === highlightedId\n            }\n            onHover={handleHover}\n            onToggleStar={handleStar}\n          />\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n```js src/Letter.js\nexport default function Letter({\n  letter,\n  isHighlighted,\n  onHover,\n  onToggleStar,\n}) {\n  return (\n    <li\n      className={\n        isHighlighted ? 'highlighted' : ''\n      }\n      onFocus={() => {\n        onHover(letter.id);        \n      }}\n      onPointerMove={() => {\n        onHover(letter.id);\n      }}\n    >\n      <button onClick={() => {\n        onToggleStar(letter.id);\n      }}>\n        {letter.isStarred ? 'Unstar' : 'Star'}\n      </button>\n      {letter.subject}\n    </li>\n  )\n}\n```\n\n```js src/data.js\nexport const initialLetters = [{\n  id: 0,\n  subject: 'Ready for adventure?',\n  isStarred: true,\n}, {\n  id: 1,\n  subject: 'Time to check in!',\n  isStarred: false,\n}, {\n  id: 2,\n  subject: 'Festival Begins in Just SEVEN Days!',\n  isStarred: false,\n}];\n```\n\n```css\nbutton { margin: 5px; }\nli { border-radius: 5px; }\n.highlighted { background: #d2eaff; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### 複数選択を実装 {/*implement-multiple-selection*/}\n\nこの例では、各 `Letter`（手紙）に props として、それが選択中かという情報である `isSelected` と `onToggle` ハンドラが渡されています。機能していますが、state が `selectedId`（`null` または ID のどちらか）という形で格納されているため、一度に 1 つの手紙しか選択できません。\n\nstate 構造を変更し、複数選択に対応させてください。（どのように構造化しますか？ コードを書く前に考えてみてください。）各チェックボックスは他のチェックボックスとは独立して動作するようにしてください。選択中の手紙をクリックすると、チェックが外れるようにしてください。最後に、フッタに選択された項目数が正しく表示されるようにしてください。\n\n<Hint>\n\n単一の選択中 ID の代わりに、選択された ID の*配列*または [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) を state に保持することができます。\n\n</Hint>\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { letters } from './data.js';\nimport Letter from './Letter.js';\n\nexport default function MailClient() {\n  const [selectedId, setSelectedId] = useState(null);\n\n  // TODO: allow multiple selection\n  const selectedCount = 1;\n\n  function handleToggle(toggledId) {\n    // TODO: allow multiple selection\n    setSelectedId(toggledId);\n  }\n\n  return (\n    <>\n      <h2>Inbox</h2>\n      <ul>\n        {letters.map(letter => (\n          <Letter\n            key={letter.id}\n            letter={letter}\n            isSelected={\n              // TODO: allow multiple selection\n              letter.id === selectedId\n            }\n            onToggle={handleToggle}\n          />\n        ))}\n        <hr />\n        <p>\n          <b>\n            You selected {selectedCount} letters\n          </b>\n        </p>\n      </ul>\n    </>\n  );\n}\n```\n\n```js src/Letter.js\nexport default function Letter({\n  letter,\n  onToggle,\n  isSelected,\n}) {\n  return (\n    <li className={\n      isSelected ? 'selected' : ''\n    }>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isSelected}\n          onChange={() => {\n            onToggle(letter.id);\n          }}\n        />\n        {letter.subject}\n      </label>\n    </li>\n  )\n}\n```\n\n```js src/data.js\nexport const letters = [{\n  id: 0,\n  subject: 'Ready for adventure?',\n  isStarred: true,\n}, {\n  id: 1,\n  subject: 'Time to check in!',\n  isStarred: false,\n}, {\n  id: 2,\n  subject: 'Festival Begins in Just SEVEN Days!',\n  isStarred: false,\n}];\n```\n\n```css\ninput { margin: 5px; }\nli { border-radius: 5px; }\nlabel { width: 100%; padding: 5px; display: inline-block; }\n.selected { background: #d2eaff; }\n```\n\n</Sandpack>\n\n<Solution>\n\n1 つの `selectedId` の代わりに、state で `selectedIds` という*配列*を保持するようにします。例えば、最初と最後の手紙を選択したときに `[0, 2]` となるようにします。何も選択されていない場合は、空の `[]` という配列になります。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { letters } from './data.js';\nimport Letter from './Letter.js';\n\nexport default function MailClient() {\n  const [selectedIds, setSelectedIds] = useState([]);\n\n  const selectedCount = selectedIds.length;\n\n  function handleToggle(toggledId) {\n    // Was it previously selected?\n    if (selectedIds.includes(toggledId)) {\n      // Then remove this ID from the array.\n      setSelectedIds(selectedIds.filter(id =>\n        id !== toggledId\n      ));\n    } else {\n      // Otherwise, add this ID to the array.\n      setSelectedIds([\n        ...selectedIds,\n        toggledId\n      ]);\n    }\n  }\n\n  return (\n    <>\n      <h2>Inbox</h2>\n      <ul>\n        {letters.map(letter => (\n          <Letter\n            key={letter.id}\n            letter={letter}\n            isSelected={\n              selectedIds.includes(letter.id)\n            }\n            onToggle={handleToggle}\n          />\n        ))}\n        <hr />\n        <p>\n          <b>\n            You selected {selectedCount} letters\n          </b>\n        </p>\n      </ul>\n    </>\n  );\n}\n```\n\n```js src/Letter.js\nexport default function Letter({\n  letter,\n  onToggle,\n  isSelected,\n}) {\n  return (\n    <li className={\n      isSelected ? 'selected' : ''\n    }>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isSelected}\n          onChange={() => {\n            onToggle(letter.id);\n          }}\n        />\n        {letter.subject}\n      </label>\n    </li>\n  )\n}\n```\n\n```js src/data.js\nexport const letters = [{\n  id: 0,\n  subject: 'Ready for adventure?',\n  isStarred: true,\n}, {\n  id: 1,\n  subject: 'Time to check in!',\n  isStarred: false,\n}, {\n  id: 2,\n  subject: 'Festival Begins in Just SEVEN Days!',\n  isStarred: false,\n}];\n```\n\n```css\ninput { margin: 5px; }\nli { border-radius: 5px; }\nlabel { width: 100%; padding: 5px; display: inline-block; }\n.selected { background: #d2eaff; }\n```\n\n</Sandpack>\n\n配列を使うことの小さな欠点の 1 つは、項目ごとに `selectedIds.includes(letter.id)` を呼び出して選択中かどうかを確認していることです。配列が非常に大きい場合 [`includes()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes) での配列検索にはリニアに時間がかかり、またこの検索は個々の項目ごとに行われているため、パフォーマンスの問題になることがあります。\n\nこれを解決するためには、代わりに state で [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) を保持するようにできます。Set には高速な [`has()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/has) 操作があります。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { letters } from './data.js';\nimport Letter from './Letter.js';\n\nexport default function MailClient() {\n  const [selectedIds, setSelectedIds] = useState(\n    new Set()\n  );\n\n  const selectedCount = selectedIds.size;\n\n  function handleToggle(toggledId) {\n    // Create a copy (to avoid mutation).\n    const nextIds = new Set(selectedIds);\n    if (nextIds.has(toggledId)) {\n      nextIds.delete(toggledId);\n    } else {\n      nextIds.add(toggledId);\n    }\n    setSelectedIds(nextIds);\n  }\n\n  return (\n    <>\n      <h2>Inbox</h2>\n      <ul>\n        {letters.map(letter => (\n          <Letter\n            key={letter.id}\n            letter={letter}\n            isSelected={\n              selectedIds.has(letter.id)\n            }\n            onToggle={handleToggle}\n          />\n        ))}\n        <hr />\n        <p>\n          <b>\n            You selected {selectedCount} letters\n          </b>\n        </p>\n      </ul>\n    </>\n  );\n}\n```\n\n```js src/Letter.js\nexport default function Letter({\n  letter,\n  onToggle,\n  isSelected,\n}) {\n  return (\n    <li className={\n      isSelected ? 'selected' : ''\n    }>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isSelected}\n          onChange={() => {\n            onToggle(letter.id);\n          }}\n        />\n        {letter.subject}\n      </label>\n    </li>\n  )\n}\n```\n\n```js src/data.js\nexport const letters = [{\n  id: 0,\n  subject: 'Ready for adventure?',\n  isStarred: true,\n}, {\n  id: 1,\n  subject: 'Time to check in!',\n  isStarred: false,\n}, {\n  id: 2,\n  subject: 'Festival Begins in Just SEVEN Days!',\n  isStarred: false,\n}];\n```\n\n```css\ninput { margin: 5px; }\nli { border-radius: 5px; }\nlabel { width: 100%; padding: 5px; display: inline-block; }\n.selected { background: #d2eaff; }\n```\n\n</Sandpack>\n\nこれで、各アイテムは `selectedIds.has(letter.id)` というチェックを行うため、非常に高速になります。\n\nstate 内のオブジェクトは[ミューテーションしない](/learn/updating-objects-in-state)ように注意してください。これには Set も含まれます。そのため、`handleToggle` 関数ではまず Set の*コピー*を作成し、そのコピーを書き換えています。\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/conditional-rendering.md",
    "content": "---\ntitle: 条件付きレンダー\n---\n\n<Intro>\n\n様々な条件に基づいて、コンポーネントに表示させる内容を変化させたいことがあります。React では、JavaScript の `if` 文や `&&`、`? :` 演算子などの構文を使うことで、JSX を条件付きでレンダーできます。\n\n</Intro>\n\n<YouWillLearn>\n\n* 条件に応じて異なる JSX を返す方法\n* JSX の一部を条件によって表示したり除外したりする方法\n* React コードベースでよく使われる条件式のショートカット記法\n\n</YouWillLearn>\n\n## 条件を満たす場合に JSX を返す {/*conditionally-returning-jsx*/}\n\n例えば、複数の `Item` をレンダーする `PackingList` コンポーネントがあり、各 `Item` に梱包が終わっているかどうかを表示させたいとしましょう。\n\n<Sandpack>\n\n```js\nfunction Item({ name, isPacked }) {\n  return <li className=\"item\">{name}</li>;\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          isPacked={true} \n          name=\"Space suit\" \n        />\n        <Item \n          isPacked={true} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          isPacked={false} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\n複数の `Item` コンポーネントのうち一部のみで、props である `isPacked` が `false` ではなく `true` になっていることに注意してください。目的は、`isPacked={true}` の場合にのみチェックマーク (✅) を表示させることです。\n\nこれは [`if`/`else` 文](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else)を使って、以下のように書くことができます。\n\n```js\nif (isPacked) {\n  return <li className=\"item\">{name} ✅</li>;\n}\nreturn <li className=\"item\">{name}</li>;\n```\n\n`isPacked` プロパティが `true` だった場合、このコードは**異なる JSX ツリーを返します**。この変更により、一部のアイテムの末尾にチェックマークが表示されるようになります。\n\n<Sandpack>\n\n```js\nfunction Item({ name, isPacked }) {\n  if (isPacked) {\n    return <li className=\"item\">{name} ✅</li>;\n  }\n  return <li className=\"item\">{name}</li>;\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          isPacked={true} \n          name=\"Space suit\" \n        />\n        <Item \n          isPacked={true} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          isPacked={false} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\nそれぞれの場合に返される内容を編集してみて、表示がどのように変化するか確認しましょう。\n\n条件分岐ロジックを実装するために JavaScript の `if` や `return` 文を使ったことに着目してください。React では、制御フロー（条件分岐など）は JavaScript で処理されます。\n\n### `null` を使って何も返さないようにする {/*conditionally-returning-nothing-with-null*/}\n\n場合によっては、何もレンダーしたくないことがあります。例えば、梱包済みの荷物は一切表示したくない、という場合です。コンポーネントは常に何かを返す必要があります。このような場合、`null` を返すことができます。\n\n```js\nif (isPacked) {\n  return null;\n}\nreturn <li className=\"item\">{name}</li>;\n```\n\n`isPacked` が `true` の場合、コンポーネントは「何も表示しない」という意味で `null` を返します。それ以外の場合、レンダーする JSX を返します。\n\n<Sandpack>\n\n```js\nfunction Item({ name, isPacked }) {\n  if (isPacked) {\n    return null;\n  }\n  return <li className=\"item\">{name}</li>;\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          isPacked={true} \n          name=\"Space suit\" \n        />\n        <Item \n          isPacked={true} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          isPacked={false} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\n実際には、レンダーしようとしている開発者を混乱させる可能性があるため、コンポーネントから `null` を返すことは一般的ではありません。代わりに、親コンポーネント側の JSX で条件付きでコンポーネントを含めたり除外したりすることが多いでしょう。以下はその方法です。\n\n## 条件付きで JSX を含める {/*conditionally-including-jsx*/}\n\n前の例では、コンポーネントが複数の JSX ツリーのうちどれを返すのか（あるいは何も返さないのか）をコントロールしていました。しかし、レンダー出力に重複があることに気付かれたでしょう。\n\n```js\n<li className=\"item\">{name} ✅</li>\n```\n\nこれは以下とほとんど同じです。\n\n```js\n<li className=\"item\">{name}</li>\n```\n\nどちらの分岐も `<li className=\"item\">...</li>` を返しています。\n\n```js\nif (isPacked) {\n  return <li className=\"item\">{name} ✅</li>;\n}\nreturn <li className=\"item\">{name}</li>;\n```\n\nこの重複に実害はありませんが、コードの保守性は悪化してしまいます。たとえば `className` を変更したくなったら？ コード内の 2 か所で変更が必要になってしまいますよね。このような状況では、条件付きで小さな JSX を含めることで、コードをより [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) に保つことができます。\n\n### 条件 (三項) 演算子 (`? :`) {/*conditional-ternary-operator--*/}\n\nJavaScript には、条件式を書くためのコンパクトな構文があります。それが[条件演算子](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator)または「三項 (ternary) 演算子」と呼ばれるものです。\n\n以下のコードは：\n\n```js\nif (isPacked) {\n  return <li className=\"item\">{name} ✅</li>;\n}\nreturn <li className=\"item\">{name}</li>;\n```\n\n代わりに以下のように書くことができます：\n\n```js\nreturn (\n  <li className=\"item\">\n    {isPacked ? name + ' ✅' : name}\n  </li>\n);\n```\n\nこれは「*`isPacked` が true なら `name + ' ✅'` をレンダーし、それ以外 (`:`) の場合は `name` をレンダーする*」というように読んでください。\n\n<DeepDive>\n\n#### この 2 つの例は完全に同等か？ {/*are-these-two-examples-fully-equivalent*/}\n\nオブジェクト指向プログラミングのバックグラウンドをお持ちの場合、2 つの例の一方では `<li>` の「インスタンス」が 2 つ作られているため、これらがわずかに異なると考えてしまうかもしれません。しかし JSX 要素は内部に状態を持たず、実際の DOM 要素でもないため、「インスタンス」ではありません。JSX は軽量な「説明書き」であり設計図のようなものです。従って上記の 2 つの例は実際に完全に同等です。[state の保持とリセット](/learn/preserving-and-resetting-state)を参照してください。\n\n</DeepDive>\n\n次に、梱包済みのアイテムを、取り消し線を表示する `<del>` のような別のタグで囲みたいとしましょう。このような場合、true と false のそれぞれの場合に対応する改行や括弧を追加することで、ネストされた JSX を読みやすくすることができます。\n\n<Sandpack>\n\n```js\nfunction Item({ name, isPacked }) {\n  return (\n    <li className=\"item\">\n      {isPacked ? (\n        <del>\n          {name + ' ✅'}\n        </del>\n      ) : (\n        name\n      )}\n    </li>\n  );\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          isPacked={true} \n          name=\"Space suit\" \n        />\n        <Item \n          isPacked={true} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          isPacked={false} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\nこれはシンプルな条件分岐の場合にはうまく動きますが、使いすぎないようにしましょう。条件のためのマークアップが増えすぎてコンポーネントが見づらくなった場合は、見やすくするために子コンポーネントを抽出することを検討してください。React ではマークアップはプログラミングコードの一種ですので、変数や関数といった道具を利用して複雑な式を読みやすく整頓することができます。\n\n### 論理 AND 演算子 (`&&`) {/*logical-and-operator-*/}\n\nもうひとつよく使われるショートカットは、[JavaScript の論理 AND (`&&`) 演算子](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND#:~:text=The%20logical%20AND%20(%20%26%26%20)%20operator,it%20returns%20a%20Boolean%20value.)です。React コンポーネント内で、条件が真の場合に JSX をレンダーし、**それ以外の場合は何もレンダーしない**という場合にしばしば使用されます。`&&` を使用すると、`isPacked` が `true` の場合にのみチェックマークを条件付きでレンダーできます。\n\n```js\nreturn (\n  <li className=\"item\">\n    {name} {isPacked && '✅'}\n  </li>\n);\n```\n\nこれは「*`isPacked` なら (`&&`)、チェックマークをレンダーし、それ以外の場合には何もレンダーしない*」のように読んでください。\n\n以下が完全に動作する例です。\n\n<Sandpack>\n\n```js\nfunction Item({ name, isPacked }) {\n  return (\n    <li className=\"item\">\n      {name} {isPacked && '✅'}\n    </li>\n  );\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          isPacked={true} \n          name=\"Space suit\" \n        />\n        <Item \n          isPacked={true} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          isPacked={false} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\n[JavaScript の && 式](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND) は、左側（条件）が `true` である場合、右側（今回の場合はチェックマーク）の値を返します。しかし、条件が `false` である場合、式全体が `false` になります。React は、`false` を JSX ツリーの「穴」と見なし、`null` や `undefined` と同様に、何もレンダーしません。\n\n\n<Pitfall>\n\n**`&&` の左辺に数値を置かない**\n\nJavaScript は条件をテストする際、左の辺を自動的に真偽値に変換します。しかし、左の辺が `0` の場合は、式全体がその `0` という値に評価されてしまうため、React は何もレンダーしないのではなく `0` を表示します。\n\nたとえば、よくある間違いとして `messageCount && <p>New messages</p>` のようにコードを書くことが挙げられます。`messageCount` が `0` の場合は何も表示しないと思われがちですが、実際には `0` そのものが表示されてしまいます！\n\nこれを修正するには、左の値を真偽値にしてください： `messageCount > 0 && <p>New messages</p>`。\n\n</Pitfall>\n\n### 条件付きで JSX を変数に割り当てる {/*conditionally-assigning-jsx-to-a-variable*/}\n\n上記のようなショートカットを使って簡潔にコードを記述するのが難しいと感じた場合は、`if` 文と変数を使用してみてください。[`let`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let) で定義した変数は再代入も可能ですので、最初に表示したいデフォルトの値 (name) を指定します。\n\n```js\nlet itemContent = name;\n```\n\nそして `if` 文を使って、`isPacked` が `true` の場合のみ `itemContent` に JSX 式を再割り当てします。\n\n```js\nif (isPacked) {\n  itemContent = name + \" ✅\";\n}\n```\n\n[波括弧は「JavaScript への窓口」](/learn/javascript-in-jsx-with-curly-braces#using-curly-braces-a-window-into-the-javascript-world)です。波括弧を使って JSX ツリーに変数を埋め込むことで、さきほど計算した値を JSX 内にネストすることができます。\n\n```js\n<li className=\"item\">\n  {itemContent}\n</li>\n```\n\nこのスタイルは最も長くなりますが、同時に最も柔軟です。以下が全体像です。\n\n<Sandpack>\n\n```js\nfunction Item({ name, isPacked }) {\n  let itemContent = name;\n  if (isPacked) {\n    itemContent = name + \" ✅\";\n  }\n  return (\n    <li className=\"item\">\n      {itemContent}\n    </li>\n  );\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          isPacked={true} \n          name=\"Space suit\" \n        />\n        <Item \n          isPacked={true} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          isPacked={false} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\n前述の通り、これはテキストのみでなく任意の JSX に対して使えます。\n\n<Sandpack>\n\n```js\nfunction Item({ name, isPacked }) {\n  let itemContent = name;\n  if (isPacked) {\n    itemContent = (\n      <del>\n        {name + \" ✅\"}\n      </del>\n    );\n  }\n  return (\n    <li className=\"item\">\n      {itemContent}\n    </li>\n  );\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          isPacked={true} \n          name=\"Space suit\" \n        />\n        <Item \n          isPacked={true} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          isPacked={false} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\nJavaScript に慣れていない場合、これだけ多様なスタイルがあると最初は圧倒されるかもしれません。しかし、これらを学ぶことは、React コンポーネントだけでなく、あらゆる JavaScript コードを読み書きできるようになるのにも役立ちます。最初は好みのスタイルを選んでスタートし、他のスタイルの仕組みを忘れた場合は、再度このリファレンスを参照してください。\n\n<Recap>\n\n* React では、JavaScript を使用して分岐ロジックを制御する。\n* `if` 文を使用して、条件に応じて JSX 式を返すことができる。\n* JSX 内で中身を条件付きで変数に保存し、波括弧を使用して他の JSX 内に含めることができる。\n* JSX 内の `{cond ? <A /> : <B />}` は、「`cond` であれば `<A />` をレンダーし、そうでなければ `<B />` をレンダーする」という意味である。\n* JSX 内の `{cond && <A />}` は、「`cond` であれば `<A />` をレンダーし、そうでなければ何もレンダーしない」という意味である。\n* これらのショートカットは一般的だが、プレーンな `if` が好きなら必ずしも使わなくて良い。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### `? :` を使って未梱包アイコンを表示 {/*show-an-icon-for-incomplete-items-with--*/}\n\n条件演算子 (`cond ? a : b`) を使って、`isPacked` が `true` でない場合は ❌ をレンダーするようにしてください。\n\n<Sandpack>\n\n```js\nfunction Item({ name, isPacked }) {\n  return (\n    <li className=\"item\">\n      {name} {isPacked && '✅'}\n    </li>\n  );\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          isPacked={true} \n          name=\"Space suit\" \n        />\n        <Item \n          isPacked={true} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          isPacked={false} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n<Sandpack>\n\n```js\nfunction Item({ name, isPacked }) {\n  return (\n    <li className=\"item\">\n      {name} {isPacked ? '✅' : '❌'}\n    </li>\n  );\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          isPacked={true} \n          name=\"Space suit\" \n        />\n        <Item \n          isPacked={true} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          isPacked={false} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n#### `&&` 演算子を使ったアイテムの重要度の表示 {/*show-the-item-importance-with*/}\n\nこの例では、それぞれの `Item` が数値型の props である `importance` を受け取ります。重要度が 0 以外の場合に限り、`&&` 演算子を使用して、斜体で \"_(Importance: X)_\" と表示するようにしてください。以下のような結果になるようにしましょう。\n\n* Space suit _(Importance: 9)_\n* Helmet with a golden leaf\n* Photo of Tam _(Importance: 6)_\n\n重要度を表示する場合は 2 つのテキストの間にスペースを入れることを忘れないでください！\n\n<Sandpack>\n\n```js\nfunction Item({ name, importance }) {\n  return (\n    <li className=\"item\">\n      {name}\n    </li>\n  );\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          importance={9} \n          name=\"Space suit\" \n        />\n        <Item \n          importance={0} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          importance={6} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n以下のようにすれば動きます。\n\n<Sandpack>\n\n```js\nfunction Item({ name, importance }) {\n  return (\n    <li className=\"item\">\n      {name}\n      {importance > 0 && ' '}\n      {importance > 0 &&\n        <i>(Importance: {importance})</i>\n      }\n    </li>\n  );\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item \n          importance={9} \n          name=\"Space suit\" \n        />\n        <Item \n          importance={0} \n          name=\"Helmet with a golden leaf\" \n        />\n        <Item \n          importance={6} \n          name=\"Photo of Tam\" \n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\n`importance` が `0` の場合に `0` が結果として表示されてしまわないよう、`importance && ...` ではなく `importance > 0 && ...` と書く必要があることに注意してください。\n\nこの答えでは、名前と重要度ラベルの間にスペースを挿入するために、2 つの条件が使用されています。代わりに、先頭にスペースを入れたフラグメントを使用することができます：`importance > 0 && <> <i>...</i></>` あるいは、`<i>` の直接内側にスペースを追加することもできます：`importance > 0 && <i> ...</i>`。\n\n</Solution>\n\n#### 連続する `? :` を `if` と変数にリファクタ {/*refactor-a-series-of---to-if-and-variables*/}\n\nこの `Drink` コンポーネントは、`name` プロパティの値が `\"tea\"` か `\"coffee\"` かによって表示する情報を変えるために、`? :` を何度も使用しています。問題は、各ドリンクの情報が複数の条件分岐に分散してしまっていることです。このコードを、3 つの `? :` ではなく、単一の `if` 文を使うようにリファクタリングしてください。\n\n<Sandpack>\n\n```js\nfunction Drink({ name }) {\n  return (\n    <section>\n      <h1>{name}</h1>\n      <dl>\n        <dt>Part of plant</dt>\n        <dd>{name === 'tea' ? 'leaf' : 'bean'}</dd>\n        <dt>Caffeine content</dt>\n        <dd>{name === 'tea' ? '15–70 mg/cup' : '80–185 mg/cup'}</dd>\n        <dt>Age</dt>\n        <dd>{name === 'tea' ? '4,000+ years' : '1,000+ years'}</dd>\n      </dl>\n    </section>\n  );\n}\n\nexport default function DrinkList() {\n  return (\n    <div>\n      <Drink name=\"tea\" />\n      <Drink name=\"coffee\" />\n    </div>\n  );\n}\n```\n\n</Sandpack>\n\nコードを `if` 文を使用するようにリファクタリングできたら、さらに簡素化するアイデアを考えてみてください。\n\n<Solution>\n\n複数のアプローチがありますが、ここでは 1 つの出発点として以下を示します。\n\n<Sandpack>\n\n```js\nfunction Drink({ name }) {\n  let part, caffeine, age;\n  if (name === 'tea') {\n    part = 'leaf';\n    caffeine = '15–70 mg/cup';\n    age = '4,000+ years';\n  } else if (name === 'coffee') {\n    part = 'bean';\n    caffeine = '80–185 mg/cup';\n    age = '1,000+ years';\n  }\n  return (\n    <section>\n      <h1>{name}</h1>\n      <dl>\n        <dt>Part of plant</dt>\n        <dd>{part}</dd>\n        <dt>Caffeine content</dt>\n        <dd>{caffeine}</dd>\n        <dt>Age</dt>\n        <dd>{age}</dd>\n      </dl>\n    </section>\n  );\n}\n\nexport default function DrinkList() {\n  return (\n    <div>\n      <Drink name=\"tea\" />\n      <Drink name=\"coffee\" />\n    </div>\n  );\n}\n```\n\n</Sandpack>\n\nこれで、各ドリンクの情報が複数の条件分岐に分散されずにグループ化されてました。将来新しいドリンクを追加する際にも簡単になります。\n\n別の解法として、情報をオブジェクトに移動することで、条件を完全に削除することも可能です。\n\n<Sandpack>\n\n```js\nconst drinks = {\n  tea: {\n    part: 'leaf',\n    caffeine: '15–70 mg/cup',\n    age: '4,000+ years'\n  },\n  coffee: {\n    part: 'bean',\n    caffeine: '80–185 mg/cup',\n    age: '1,000+ years'\n  }\n};\n\nfunction Drink({ name }) {\n  const info = drinks[name];\n  return (\n    <section>\n      <h1>{name}</h1>\n      <dl>\n        <dt>Part of plant</dt>\n        <dd>{info.part}</dd>\n        <dt>Caffeine content</dt>\n        <dd>{info.caffeine}</dd>\n        <dt>Age</dt>\n        <dd>{info.age}</dd>\n      </dl>\n    </section>\n  );\n}\n\nexport default function DrinkList() {\n  return (\n    <div>\n      <Drink name=\"tea\" />\n      <Drink name=\"coffee\" />\n    </div>\n  );\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/creating-a-react-app.md",
    "content": "---\ntitle: React アプリの作成\n---\n\n<Intro>\n\nReact を使って新しいアプリやウェブサイトを作成したい場合は、フレームワークを使って始めることをおすすめします。\n\n</Intro>\n\nあなたのアプリが既存のフレームワークではうまく対応できない制約を有している場合や、自分自身でフレームワークを構築したい場合、または React アプリの基本を学びたい場合は、[React アプリをゼロから構築する](/learn/build-a-react-app-from-scratch)ことも可能です。\n\n## フルスタックフレームワーク {/*full-stack-frameworks*/}\n\nこれらの推奨フレームワークは、アプリを本番環境でデプロイしスケールするために必要な、すべての機能をサポートしています。最新の React 機能を統合し、React のアーキテクチャを活用しています。\n\n<Note>\n\n#### フルスタックフレームワークは必ずしもサーバを必要としない {/*react-frameworks-do-not-require-a-server*/}\n\nこのページのすべてのフレームワークは、クライアントサイドレンダリング (client-side rendering; [CSR](https://developer.mozilla.org/en-US/docs/Glossary/CSR))、シングルページアプリケーション (single-page app; [SPA](https://developer.mozilla.org/en-US/docs/Glossary/SPA)) および静的サイト生成 (static-site generation; [SSG](https://developer.mozilla.org/en-US/docs/Glossary/SSG)) をサポートしています。これらのアプリは、サーバ機能なしで [CDN](https://developer.mozilla.org/en-US/docs/Glossary/CDN) や静的ホスティングサービスにデプロイできます。さらに、これらのフレームワークは、ユースケースに応じてサーバサイドレンダリングをルートごとに追加することを可能にします。\n\nこれにより、クライアントのみのアプリから始めておき、後で要件が変化した場合に、アプリを書き直すことなく個々のルートでサーバ機能を使用することを選択できます。レンダー戦略の設定については、フレームワークのドキュメントを参照してください。\n\n</Note>\n\n### Next.js (App Router) {/*nextjs-app-router*/}\n\n**[Next.js の App Router](https://nextjs.org/docs) は、React のアーキテクチャを最大限に活用してフルスタック React アプリを実現する React フレームワークです**。\n\n<TerminalBlock>\nnpx create-next-app@latest\n</TerminalBlock>\n\nNext.js は [Vercel](https://vercel.com/) によってメンテナンスされています。Next.js アプリは Node.js や Docker コンテナをサポートする任意のホスティングプロバイダ、もしくは自前のサーバ上に[デプロイできます](https://nextjs.org/docs/app/building-your-application/deploying)。さらに Next.js は、サーバ不要の[静的エクスポート](https://nextjs.org/docs/app/building-your-application/deploying/static-exports)もサポートしています。\n\n### React Router (v7) {/*react-router-v7*/}\n\n**[React Router](https://reactrouter.com/start/framework/installation) は、React 用の最も人気のあるルーティングライブラリであり、Vite と組み合わせてフルスタック React フレームワークを作成できます**。標準の Web API を重視しており、さまざまな JavaScript ランタイムやプラットフォーム向けに[そのままデプロイできるテンプレート](https://github.com/remix-run/react-router-templates)をいくつか提供しています。\n\n新しい React Router フレームワークプロジェクトを作成するには、以下のコマンドを実行します。\n\n<TerminalBlock>\nnpx create-react-router@latest\n</TerminalBlock>\n\nReact Router は [Shopify](https://www.shopify.com) によってメンテナンスされています。\n\n### Expo (ネイティブアプリ用) {/*expo*/}\n\n**[Expo](https://expo.dev/) は、真にネイティブな UI を持つユニバーサルな Android、iOS、および Web アプリを作成できる React フレームワークです**。[React Native](https://reactnative.dev/) 用の SDK を提供することでネイティブ部分を使いやすくしています。新しい Expo プロジェクトを作成するには、以下のコマンドを実行します。\n\n<TerminalBlock>\nnpx create-expo-app@latest\n</TerminalBlock>\n\nExpo を初めて使用する場合は、[Expo チュートリアル](https://docs.expo.dev/tutorial/introduction/)をチェックしてください。\n\nExpo は [Expo（社名）](https://expo.dev/about) によってメンテナンスされています。Expo を使ったアプリ構築は無料であり、Google や Apple のアプリストアにも制限なく申請できます。また Expo ではオプトインの有料クラウドサービスも提供しています。\n\n\n## その他のフレームワーク {/*other-frameworks*/}\n\n私たちのフルスタック React ビジョンに向けて取り組んでいる他の新進のフレームワークも存在します。\n\n- [TanStack Start (Beta)](https://tanstack.com/start/): TanStack Start は、TanStack Router を活用したフルスタック React フレームワークです。Nitro や Vite などのツールを使用して、フルドキュメント SSR、ストリーミング、サーバ関数、バンドル機能などを提供します。\n- [RedwoodSDK](https://rwsdk.com/): Redwood は、多くのプリインストールされたパッケージと設定を備えたフルスタック React フレームワークで、フルスタックウェブアプリケーションを簡単に構築できます。\n\n<DeepDive>\n\n#### React チームのフルスタックアーキテクチャビジョンに含まれる機能 {/*which-features-make-up-the-react-teams-full-stack-architecture-vision*/}\n\nNext.js の App Router バンドラは、公式の [React Server Components 仕様](https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md)を完全に実装しています。これにより単一の React ツリー内で、バンドル時専用コンポーネント、サーバ専用コンポーネント、インタラクティブなコンポーネントを混在させることができます。\n\n例えば、データベースやファイルから読み込みを行う React コンポーネントを非同期 (`async`) 関数として記述できます。そしてそのデータをインタラクティブなコンポーネントに渡すこともできます。\n\n```js\n// This component runs *only* on the server (or during the build).\nasync function Talks({ confId }) {\n  // 1. You're on the server, so you can talk to your data layer. API endpoint not required.\n  const talks = await db.Talks.findAll({ confId });\n\n  // 2. Add any amount of rendering logic. It won't make your JavaScript bundle larger.\n  const videos = talks.map(talk => talk.video);\n\n  // 3. Pass the data down to the components that will run in the browser.\n  return <SearchableVideoList videos={videos} />;\n}\n```\n\nNext.js の App Router は、[サスペンス (Suspense) を使用したデータフェッチ](/blog/2022/03/29/react-v18#suspense-in-data-frameworks)を統合しています。これにより、React ツリー内で直接、UI の様々な場所に表示されるロード中状態（スケルトンプレースホルダなど）を指定できるようになります。\n\n```js\n<Suspense fallback={<TalksLoading />}>\n  <Talks confId={conf.id} />\n</Suspense>\n```\n\nサーバコンポーネントとサスペンスは、Next.js の機能ではなく React の機能です。しかしフレームワークレベルでこれらを採用するには、合意形成とかなりの実装の手間が必要です。現時点では、Next.js の App Router が最も完全な実装です。React チームはバンドラの開発者と協力して、次世代のフレームワークでこれらの機能を実装しやすくすることを目指しています。\n\n</DeepDive>\n\n## ゼロから構築を始める {/*start-from-scratch*/}\n\nあなたのアプリが既存のフレームワークではうまく対応できない制約を有している場合や、自分自身でフレームワークを構築したい場合、または React アプリの基本を学びたい場合には、ゼロから React プロジェクトを始めるための他の選択肢があります。\n\nゼロから始めることでより柔軟性が得られますが、ルーティング、データフェッチ、その他の一般的な使用パターンにどのツールを使用するかを選択する必要があります。これは、既存のフレームワークを使用する代わりに自分自身でフレームワークを構築するようなものです。[おすすめのフルスタックフレームワーク](#full-stack-frameworks)には、これらの問題に対する組み込みの解決策があります。\n\n独自のソリューションを構築したい場合は、[ゼロからの React アプリ構築](/learn/build-a-react-app-from-scratch)ガイドを参照し、[Vite](https://vite.dev/)、[Parcel](https://parceljs.org/)、または [RSbuild](https://rsbuild.dev/) のようなビルドツールを使って新しい React プロジェクトをセットアップする方法を確認してください。\n\n-----\n\n_このページに掲載されることに興味のあるフレームワークの作者の方は、[こちらからお知らせください](https://github.com/reactjs/react.dev/issues/new?assignees=&labels=type%3A+framework&projects=&template=3-framework.yml&title=%5BFramework%5D%3A+)_。\n"
  },
  {
    "path": "src/content/learn/describing-the-ui.md",
    "content": "---\ntitle: UI の記述\n---\n\n<Intro>\n\nReact は、ユーザインターフェース（UI）を表示するための JavaScript ライブラリです。UI はボタンやテキスト、画像といった小さな要素から構成されています。React ではこれらを、ネストして再利用できる*コンポーネント*にまとめることができます。ウェブサイトであれ携帯電話アプリであれ、画面上のすべてのものはコンポーネントに分解することができます。この章では、React コンポーネントを作成し、カスタマイズし、条件付きで表示する方法について学びます。\n\n</Intro>\n\n<YouWillLearn isChapter={true}>\n\n* [初めてのコンポーネントの書き方](/learn/your-first-component)\n* [コンポーネントファイルを複数に分ける理由とその方法](/learn/importing-and-exporting-components)\n* [JSX を使って JavaScript にマークアップを追加する方法](/learn/writing-markup-with-jsx)\n* [JSX 内で波括弧を使って JavaScript の機能にアクセスする方法](/learn/javascript-in-jsx-with-curly-braces)\n* [コンポーネントを props を使ってカスタマイズする方法](/learn/passing-props-to-a-component)\n* [コンポーネントを条件付きでレンダーする方法](/learn/conditional-rendering)\n* [複数のコンポーネントを同時にレンダーする方法](/learn/rendering-lists)\n* [コンポーネントを純粋に保つことで混乱を避ける方法](/learn/keeping-components-pure)\n* [UI をツリーとして理解することが有用である理由](/learn/understanding-your-ui-as-a-tree)\n\n</YouWillLearn>\n\n## 初めてのコンポーネント {/*your-first-component*/}\n\nReact アプリケーションは*コンポーネント*と呼ばれる独立した UI のパーツで構成されています。React コンポーネントとは、マークアップを添えることができる JavaScript 関数です。コンポーネントは、ボタンのような小さなものであることもあれば、ページ全体といった大きなものであることもあります。以下は、3 つの `Profile` コンポーネントをレンダーする `Gallery` コンポーネントの例です。\n\n<Sandpack>\n\n```js\nfunction Profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/MK3eW3As.jpg\"\n      alt=\"Katherine Johnson\"\n    />\n  );\n}\n\nexport default function Gallery() {\n  return (\n    <section>\n      <h1>Amazing scientists</h1>\n      <Profile />\n      <Profile />\n      <Profile />\n    </section>\n  );\n}\n```\n\n```css\nimg { margin: 0 10px 10px 0; height: 90px; }\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/your-first-component\">\n\n[**初めてのコンポーネント**](/learn/your-first-component)を読んで、React コンポーネントの宣言方法、使用方法について学びましょう。\n\n</LearnMore>\n\n## コンポーネントのインポートとエクスポート {/*importing-and-exporting-components*/}\n\n1 つのファイルに多くのコンポーネントを宣言することもできますが、大きなファイルは取り回しが難しくなります。これを解決するために、コンポーネントを個別のファイルに*エクスポート*し、別のファイルからそのコンポーネントを*インポート*することができます。\n\n\n<Sandpack>\n\n```js src/App.js hidden\nimport Gallery from './Gallery.js';\n\nexport default function App() {\n  return (\n    <Gallery />\n  );\n}\n```\n\n```js src/Gallery.js active\nimport Profile from './Profile.js';\n\nexport default function Gallery() {\n  return (\n    <section>\n      <h1>Amazing scientists</h1>\n      <Profile />\n      <Profile />\n      <Profile />\n    </section>\n  );\n}\n```\n\n```js src/Profile.js\nexport default function Profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/QIrZWGIs.jpg\"\n      alt=\"Alan L. Hart\"\n    />\n  );\n}\n```\n\n```css\nimg { margin: 0 10px 10px 0; }\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/importing-and-exporting-components\">\n\n[**コンポーネントのインポートとエクスポート**](/learn/importing-and-exporting-components)を読んで、コンポーネントを個々の専用ファイルに分割する方法を学びましょう。\n\n</LearnMore>\n\n## JSX でマークアップを記述する {/*writing-markup-with-jsx*/}\n\n各 React コンポーネントは、ブラウザにレンダーされるマークアップを含んだ JavaScript 関数です。React コンポーネントは、マークアップを表現するために JSX という拡張構文を使用します。JSX は HTML によく似ていますが、少し構文が厳密であり、動的な情報を表示することができます。\n\n既存の HTML マークアップを React コンポーネントに貼り付けても、常にうまく機能するわけではありません。\n\n<Sandpack>\n\n```js\nexport default function TodoList() {\n  return (\n    // This doesn't quite work!\n    <h1>Hedy Lamarr's Todos</h1>\n    <img\n      src=\"https://i.imgur.com/yXOvdOSs.jpg\"\n      alt=\"Hedy Lamarr\"\n      class=\"photo\"\n    >\n    <ul>\n      <li>Invent new traffic lights\n      <li>Rehearse a movie scene\n      <li>Improve spectrum technology\n    </ul>\n  );\n}\n```\n\n```css\nimg { height: 90px; }\n```\n\n</Sandpack>\n\nこのような既存の HTML がある場合は、[コンバータ](https://transform.tools/html-to-jsx)を使って修正することができます。\n\n<Sandpack>\n\n```js\nexport default function TodoList() {\n  return (\n    <>\n      <h1>Hedy Lamarr's Todos</h1>\n      <img\n        src=\"https://i.imgur.com/yXOvdOSs.jpg\"\n        alt=\"Hedy Lamarr\"\n        className=\"photo\"\n      />\n      <ul>\n        <li>Invent new traffic lights</li>\n        <li>Rehearse a movie scene</li>\n        <li>Improve spectrum technology</li>\n      </ul>\n    </>\n  );\n}\n```\n\n```css\nimg { height: 90px; }\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/writing-markup-with-jsx\">\n\n[**JSX でマークアップを記述する**](/learn/writing-markup-with-jsx)を読んで、正しい JSX の書き方を学びましょう。\n\n</LearnMore>\n\n## JSX に波括弧で JavaScript を含める {/*javascript-in-jsx-with-curly-braces*/}\n\nJSX を使うことで、JavaScript ファイル内に HTML のようなマークアップを記述し、レンダーのロジックとコンテンツを同じ場所に配置することができます。時には、そのマークアップ内でちょっとした JavaScript ロジックを追加したり、動的なプロパティを参照したりしたいことがあります。このような状況では、JSX 内で波括弧を使い JavaScript への「窓を開ける」ことができます。\n\n<Sandpack>\n\n```js\nconst person = {\n  name: 'Gregorio Y. Zara',\n  theme: {\n    backgroundColor: 'black',\n    color: 'pink'\n  }\n};\n\nexport default function TodoList() {\n  return (\n    <div style={person.theme}>\n      <h1>{person.name}'s Todos</h1>\n      <img\n        className=\"avatar\"\n        src=\"https://i.imgur.com/7vQD0fPs.jpg\"\n        alt=\"Gregorio Y. Zara\"\n      />\n      <ul>\n        <li>Improve the videophone</li>\n        <li>Prepare aeronautics lectures</li>\n        <li>Work on the alcohol-fuelled engine</li>\n      </ul>\n    </div>\n  );\n}\n```\n\n```css\nbody { padding: 0; margin: 0 }\nbody > div > div { padding: 20px; }\n.avatar { border-radius: 50%; height: 90px; }\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/javascript-in-jsx-with-curly-braces\">\n\n[**JSX に波括弧で JavaScript を含める**](/learn/javascript-in-jsx-with-curly-braces)を読んで、JSX 内から JavaScript のデータにアクセスする方法を学びましょう。\n\n</LearnMore>\n\n## コンポーネントに props を渡す {/*passing-props-to-a-component*/}\n\nReact コンポーネントでは、*props* を使ってお互いに情報をやり取りします。親コンポーネントは、子コンポーネントに props を与えることで、情報を渡すことができます。HTML の属性 (attribute) と似ていますが、オブジェクト、配列、関数、そして JSX まで、どのような JavaScript の値でも渡すことができます！\n\n<Sandpack>\n\n```js\nimport { getImageUrl } from './utils.js'\n\nexport default function Profile() {\n  return (\n    <Card>\n      <Avatar\n        size={100}\n        person={{\n          name: 'Katsuko Saruhashi',\n          imageId: 'YfeOqp2'\n        }}\n      />\n    </Card>\n  );\n}\n\nfunction Avatar({ person, size }) {\n  return (\n    <img\n      className=\"avatar\"\n      src={getImageUrl(person)}\n      alt={person.name}\n      width={size}\n      height={size}\n    />\n  );\n}\n\nfunction Card({ children }) {\n  return (\n    <div className=\"card\">\n      {children}\n    </div>\n  );\n}\n\n```\n\n```js src/utils.js\nexport function getImageUrl(person, size = 's') {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    size +\n    '.jpg'\n  );\n}\n```\n\n```css\n.card {\n  width: fit-content;\n  margin: 5px;\n  padding: 5px;\n  font-size: 20px;\n  text-align: center;\n  border: 1px solid #aaa;\n  border-radius: 20px;\n  background: #fff;\n}\n.avatar {\n  margin: 20px;\n  border-radius: 50%;\n}\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/passing-props-to-a-component\">\n\n[**コンポーネントに props を渡す**](/learn/passing-props-to-a-component)を読んで、props の渡し方と読み取り方を学びましょう。\n\n</LearnMore>\n\n## 条件付きレンダー {/*conditional-rendering*/}\n\nコンポーネントは、さまざまな条件によって表示内容を切り替える必要がよくあります。React では、JavaScript の `if` 文、`&&` や `? :` 演算子などの構文を使って、条件付きで JSX をレンダーすることができます。\n\nこの例では、JavaScript の `&&` 演算子を使い、チェックマークを条件付きでレンダーしています。\n\n<Sandpack>\n\n```js\nfunction Item({ name, isPacked }) {\n  return (\n    <li className=\"item\">\n      {name} {isPacked && '✅'}\n    </li>\n  );\n}\n\nexport default function PackingList() {\n  return (\n    <section>\n      <h1>Sally Ride's Packing List</h1>\n      <ul>\n        <Item\n          isPacked={true}\n          name=\"Space suit\"\n        />\n        <Item\n          isPacked={true}\n          name=\"Helmet with a golden leaf\"\n        />\n        <Item\n          isPacked={false}\n          name=\"Photo of Tam\"\n        />\n      </ul>\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/conditional-rendering\">\n\n[**条件付きレンダー**](/learn/conditional-rendering)を読んで、コンテンツを条件付きでレンダーするためのさまざまな方法を学びましょう。\n\n</LearnMore>\n\n## リストのレンダー {/*rendering-lists*/}\n\nデータの集まりから複数のよく似たコンポーネントを表示したいことがよくあります。React で JavaScript の `filter()` や `map()` を使って、データの配列をフィルタリングしたり、コンポーネントの配列に変換したりすることができます。\n\n配列内の各要素には、`key` を指定する必要があります。通常、データベースの ID を `key` として使うことになるでしょう。key は、リストが変更されても各アイテムのリスト内の位置を React が追跡できるようにするために必要です。\n\n<Sandpack>\n\n```js src/App.js\nimport { people } from './data.js';\nimport { getImageUrl } from './utils.js';\n\nexport default function List() {\n  const listItems = people.map(person =>\n    <li key={person.id}>\n      <img\n        src={getImageUrl(person)}\n        alt={person.name}\n      />\n      <p>\n        <b>{person.name}:</b>\n        {' ' + person.profession + ' '}\n        known for {person.accomplishment}\n      </p>\n    </li>\n  );\n  return (\n    <article>\n      <h1>Scientists</h1>\n      <ul>{listItems}</ul>\n    </article>\n  );\n}\n```\n\n```js src/data.js\nexport const people = [{\n  id: 0,\n  name: 'Creola Katherine Johnson',\n  profession: 'mathematician',\n  accomplishment: 'spaceflight calculations',\n  imageId: 'MK3eW3A'\n}, {\n  id: 1,\n  name: 'Mario José Molina-Pasquel Henríquez',\n  profession: 'chemist',\n  accomplishment: 'discovery of Arctic ozone hole',\n  imageId: 'mynHUSa'\n}, {\n  id: 2,\n  name: 'Mohammad Abdus Salam',\n  profession: 'physicist',\n  accomplishment: 'electromagnetism theory',\n  imageId: 'bE7W1ji'\n}, {\n  id: 3,\n  name: 'Percy Lavon Julian',\n  profession: 'chemist',\n  accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',\n  imageId: 'IOjWm71'\n}, {\n  id: 4,\n  name: 'Subrahmanyan Chandrasekhar',\n  profession: 'astrophysicist',\n  accomplishment: 'white dwarf star mass calculations',\n  imageId: 'lrWQx8l'\n}];\n```\n\n```js src/utils.js\nexport function getImageUrl(person) {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    's.jpg'\n  );\n}\n```\n\n```css\nul { list-style-type: none; padding: 0px 10px; }\nli {\n  margin-bottom: 10px;\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  align-items: center;\n}\nimg { width: 100px; height: 100px; border-radius: 50%; }\nh1 { font-size: 22px; }\nh2 { font-size: 20px; }\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/rendering-lists\">\n\n[**リストのレンダー**](/learn/rendering-lists)を読んで、コンポーネントのリストをレンダーする方法と、key の選択方法を学びましょう。\n\n</LearnMore>\n\n## コンポーネントを純粋に保つ {/*keeping-components-pure*/}\n\nいくつかの JavaScript の関数は*純関数*です。純関数には以下の特徴があります。\n\n* **自分の仕事に集中する**。呼び出される前に存在していたオブジェクトや変数を変更しない。\n* **同じ入力には同じ出力**。同じ入力を与えると、純関数は常に同じ結果を返す。\n\nコンポーネントを常に厳密に純関数として書くことで、コードベースが成長するにつれて起きがちな、あらゆる種類の不可解なバグ、予測不可能な挙動を回避することができます。以下は純粋ではないコンポーネントの例です。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [5]}}\nlet guest = 0;\n\nfunction Cup() {\n  // Bad: changing a preexisting variable!\n  guest = guest + 1;\n  return <h2>Tea cup for guest #{guest}</h2>;\n}\n\nexport default function TeaSet() {\n  return (\n    <>\n      <Cup />\n      <Cup />\n      <Cup />\n    </>\n  );\n}\n```\n\n</Sandpack>\n\nこのコンポーネントを純粋にするには、既に存在する変数を書き換えるのではなく、prop を渡すようにすることができます。\n\n<Sandpack>\n\n```js\nfunction Cup({ guest }) {\n  return <h2>Tea cup for guest #{guest}</h2>;\n}\n\nexport default function TeaSet() {\n  return (\n    <>\n      <Cup guest={1} />\n      <Cup guest={2} />\n      <Cup guest={3} />\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/keeping-components-pure\">\n\n[**コンポーネントを純粋に保つ**](/learn/keeping-components-pure)を読んで、予測可能な純関数としてコンポーネントを作成する方法を学びましょう。\n\n</LearnMore>\n\n## UI をツリーとして理解する {/*your-ui-as-a-tree*/}\n\nReact はコンポーネント間あるいはモジュール間の関係性をモデル化するために、ツリー構造を使用します。\n\nReact レンダーツリーとはコンポーネントの親子関係を表現したものです。\n\n<Diagram name=\"generic_render_tree\" height={250} width={500} alt=\"5 つのノードからなるツリー。それぞれのノードはコンポーネントを表している。ルートノードはツリーの最上部にあり 'Root Component' と書かれている。そこから 2 本の矢印が下に伸びており 'Component A' および 'Component C' と書かれたノードを指している。それぞれの矢印には 'renders' と書かれている。'Component A' からは 'renders' と書かれた矢印が 'Component B' と書かれたノードに伸びている。'Component C' からは 'renders' と書かれた矢印が 'Component D' と書かれたノードに伸びている。\">\n\nReact のレンダーツリーの例\n\n</Diagram>\n\nツリーの上側、つまりルートに近いコンポーネントはトップレベルコンポーネントです。子を持たないコンポーネントはリーフ（葉）コンポーネントです。このようなコンポーネントの分類は、データの流れやレンダーパフォーマンスを理解する際に有用です。\n\nアプリを理解する上では、JavaScript のモジュール間の関係性をモデルすることも重要です。このようなものをモジュール依存関係ツリーと呼びます。\n\n<Diagram name=\"generic_dependency_tree\" height={250} width={500} alt=\"5 つのノードからなるツリー。それぞれのノードは JavaScript のモジュールを表している。最上部のノードは 'RootModule.js' と書かれている。そこから 'ModuleA.js'、'ModuleB.js'、'ModuleC.js' へと 3 本の矢印が伸びている。各矢印には 'imports' と書かれている。'ModuleC.js' からは 'imports' と書かれた矢印が 'ModuleD.js' と書かれたノードに伸びている。\">\n\nモジュール依存関係ツリーの例\n\n</Diagram>\n\n依存関係ツリーは、関連する JavaScript コードをすべてバンドルしてクライアントがダウンロード・レンダーできるようにするために、ビルドツールでよく使用されます。バンドルサイズが大きいと、React アプリのユーザ体験は悪化します。モジュール依存関係ツリーを理解することは、そのような問題をデバッグするのに役立ちます。\n\n<LearnMore path=\"/learn/understanding-your-ui-as-a-tree\">\n\n[**UI をツリーとして理解する**](/learn/understanding-your-ui-as-a-tree)を読んで、レンダーツリーやモジュール依存関係ツリーの作り方、そしてそのような考え方がユーザ体験やパフォーマンスを改善する際にどのように役立つのかについて学びましょう。\n\n</LearnMore>\n\n\n## 次のステップ {/*whats-next*/}\n\n[初めてのコンポーネント](/learn/your-first-component)に進んで、この章をページごとに読み進めましょう！\n\nもしくは、すでにこれらのトピックに詳しい場合、[インタラクティビティの追加](/learn/adding-interactivity)について読んでみましょう。\n"
  },
  {
    "path": "src/content/learn/editor-setup.md",
    "content": "---\ntitle: エディタのセットアップ\n---\n\n<Intro>\n\nエディタを適切に設定することで、コードが読みやすく、素早く書けるようになります。さらに、書いている途中でバグを検出するのにも役立ちます！ エディタの設定をするのが初めてである、あるいは現在使用しているエディタをチューンアップしたいという場合、いくつかのおすすめがあります。\n\n</Intro>\n\n<YouWillLearn>\n\n* 最も人気があるエディタの紹介\n* コードを自動フォーマットする方法\n\n</YouWillLearn>\n\n## エディタを選ぶ {/*your-editor*/}\n\n[VS Code](https://code.visualstudio.com/) は、現在最も一般的に使用されているエディタのひとつです。マーケットプレイスには多くの拡張機能があり、GitHub のような人気のサービスとも良く統合されています。以下で説明する多くの機能も、VS Code に拡張機能として追加することができるため、高度にカスタマイズできます。\n\n他に React コミュニティで使用されている人気のテキストエディタとしては以下のようなものがあります。\n\n* [WebStorm](https://www.jetbrains.com/webstorm/) は、JavaScript に特化した統合開発環境です。\n* [Sublime Text](https://www.sublimetext.com/) は、JSX や TypeScript のサポート、[シンタックスハイライト](https://stackoverflow.com/a/70960574/458193)、オートコンプリート機能を組み込みで有しています。\n* [Vim](https://www.vim.org/) は、あらゆる種類のテキストの作成や編集を効率的に行うために作られた、高度にカスタマイズ可能なテキストエディタです。多くの UNIX システムや Apple OS X には \"vi\" として含まれています。\n\n## テキストエディタ機能のお勧め {/*recommended-text-editor-features*/}\n\n一部のエディタは以下の機能を組み込みで有していますが、他のエディタでは拡張機能を追加する必要があるかもしれません。使いたいエディタのサポート状況を確認してください!\n\n### リント {/*linting*/}\n\nコードリンタは、書きながらコード内の問題を見つけて、問題を早期に修正できるようにしてくれます。[ESLint](https://eslint.org/) は、人気の JavaScript 用オープンソースリンタです。\n\n* [React に適した構成の ESLint をインストールする](https://www.npmjs.com/package/eslint-config-react-app)（[Node をインストール](https://nodejs.org/en/download/current/)していることを確認してください！）\n* [公式拡張機能を使用して VS Code に ESLint を統合する](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)\n\n**プロジェクトで [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks) のルールをすべて有効化していることを確認してください**。これは非常に重要であり、とても深刻なバグを早期に検出してくれます。[`eslint-config-react-app`](https://www.npmjs.com/package/eslint-config-react-app) 推奨プリセットは、これらをすでに含んでいます。\n\n### コードフォーマット {/*formatting*/}\n\n他の人とコードを共有するにあたって何よりも避けたいのは、[タブ vs スペース](https://www.google.com/search?q=tabs+vs+spaces)についての議論に巻き込まれてしまうことです！ 幸いなことに、[Prettier](https://prettier.io/) は、あらかじめ設定できるルールに従ってコードを再フォーマットすることで、あなたのコードを綺麗にしてくれます。Prettier を実行すれば、タブはスペースに変換され、インデント、引用符なども設定に従って変更されます。理想的なセットアップでは、Prettier はファイルを保存するたびに実行され、瞬時にこれらの変更を適用してくれます。\n\n[VSCode 用の Prettier 拡張機能](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) を VS Code にインストールするには、次の手順に従ってください。\n\n1. VS Code を起動する。\n2. Quick Open を使用する（Ctrl/Cmd+P を押す）。\n3. `ext install esbenp.prettier-vscode` と打ち込む。\n4. Enter キーを押す。\n\n#### 保存時にフォーマット {/*formatting-on-save*/}\n\n理想的には、毎回の保存時にコードを整形するべきです。VS Code にはそのための設定があります！\n\n1. VS Code で、`CTRL/CMD + SHIFT + P` と押す。\n2. \"settings\" と入力する。\n3. Enter キーを押す。\n4. 検索バーに \"format on save\" と入力する。\n5. \"format on save\" オプションがチェックされているようにする。\n\n> あなたの ESLint のプリセットに、フォーマットに関するルールがある場合、それらが Prettier と競合する可能性があります。[`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier) を使用して、ESLint プリセット内のフォーマットに関するルールをすべて無効にし、ESLint を論理的な誤りの検出*のみ*に限定して利用することをお勧めします。プルリクエストをマージする前にファイルがフォーマットされていることを強制するには、継続的インテグレーション (CI) で [`prettier --check`](https://prettier.io/docs/en/cli.html#--check) を使用してください。\n"
  },
  {
    "path": "src/content/learn/escape-hatches.md",
    "content": "---\ntitle: 避難ハッチ\n---\n\n<Intro>\n\nコンポーネントによっては、React 外のシステムに対して制御や同期を必要とする場合があります。例えば、ブラウザ API を使用して入力フィールドにフォーカスを当てる、React を使用せずに実装されたビデオプレーヤの再生や一時停止を行う、あるいはリモートサーバに接続してメッセージをリッスンする、といったものです。この章では、React の「外側に踏み出して」外部システムに接続するための避難ハッチ (escape hatch) を学びます。アプリケーションのロジックとデータフローの大部分は、これらの機能に依存しないようにすべきです。\n\n</Intro>\n\n<YouWillLearn isChapter={true}>\n\n* [再レンダーせずに情報を「記憶」する方法](/learn/referencing-values-with-refs)\n* [React が管理している DOM 要素にアクセスする方法](/learn/manipulating-the-dom-with-refs)\n* [コンポーネントを外部システムと同期させる方法](/learn/synchronizing-with-effects)\n* [不要なエフェクトをコンポーネントから削除する方法](/learn/you-might-not-need-an-effect)\n* [エフェクトのライフサイクルとコンポーネントのライフサイクルの違い](/learn/lifecycle-of-reactive-effects)\n* [値がエフェクトを再トリガするのを防ぐ方法](/learn/separating-events-from-effects)\n* [エフェクトの再実行頻度を下げる方法](/learn/removing-effect-dependencies)\n* [コンポーネント間でロジックを共有する方法](/learn/reusing-logic-with-custom-hooks)\n\n</YouWillLearn>\n\n## ref で値を参照する {/*referencing-values-with-refs*/}\n\nコンポーネントに情報を「記憶」させたいが、その情報が[新しいレンダーをトリガ](/learn/render-and-commit)しないようにしたい場合、ref を使うことができます。\n\n```js\nconst ref = useRef(0);\n```\n\nstate と同様に、ref は React によって再レンダー間で保持されます。しかし、state はセットするとコンポーネントが再レンダーされます。ref は、変更してもコンポーネントが再レンダーされません！ `ref.current` プロパティを通じて ref の現在の値にアクセスできます。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\n\nexport default function Counter() {\n  let ref = useRef(0);\n\n  function handleClick() {\n    ref.current = ref.current + 1;\n    alert('You clicked ' + ref.current + ' times!');\n  }\n\n  return (\n    <button onClick={handleClick}>\n      Click me!\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\nref は、React が管理しない、コンポーネントの秘密のポケットのようなものです。例えば、[タイムアウト ID](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#return_value)、[DOM 要素](https://developer.mozilla.org/en-US/docs/Web/API/Element)、その他コンポーネントのレンダー出力に影響を与えないオブジェクトを格納するために ref を使用できます。\n\n<LearnMore path=\"/learn/referencing-values-with-refs\">\n\n[**ref で値を参照する**](/learn/referencing-values-with-refs)を読んで、ref を使って情報を記憶する方法を学びましょう。\n\n</LearnMore>\n\n## ref で DOM を操作する {/*manipulating-the-dom-with-refs*/}\n\nReact はレンダー結果に合致するよう自動的に DOM を更新するため、コンポーネントで DOM を操作する必要は通常ほとんどありません。ただし、ノードにフォーカスを当てたり、スクロールさせたり、サイズや位置を測定したりするなどの場合に、React が管理する DOM 要素へのアクセスが必要なことがあります。React にはこれらを行う組み込みの方法が存在しないため、DOM ノードを参照する *ref* が必要になります。例えば、以下のボタンをクリックすると、ref を使用して入力欄にフォーカスが当たります。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\n\nexport default function Form() {\n  const inputRef = useRef(null);\n\n  function handleClick() {\n    inputRef.current.focus();\n  }\n\n  return (\n    <>\n      <input ref={inputRef} />\n      <button onClick={handleClick}>\n        Focus the input\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/manipulating-the-dom-with-refs\">\n\n[**ref で DOM を操作する**](/learn/manipulating-the-dom-with-refs)を読んで、React が管理する DOM 要素にアクセスする方法を学びましょう。\n\n</LearnMore>\n\n## エフェクトを使って同期を行う {/*synchronizing-with-effects*/}\n\n一部のコンポーネントは外部システムと同期を行う必要があります。例えば、React の state に基づいて非 React 製コンポーネントを制御したり、サーバとの接続を確立したり、コンポーネントが画面に表示されたときに分析用のログを送信したりしたいかもしれません。特定のイベントを処理するイベントハンドラとは異なり、*エフェクト (Effect)* を使うことで、レンダー後にコードを実行することができます。これは React 外のシステムとコンポーネントを同期させるために使用します。\n\nPlay/Pause を何度か押して、ビデオプレーヤが `isPlaying` の値と同期していることを確認してみてください。\n\n<Sandpack>\n\n```js\nimport { useState, useRef, useEffect } from 'react';\n\nfunction VideoPlayer({ src, isPlaying }) {\n  const ref = useRef(null);\n\n  useEffect(() => {\n    if (isPlaying) {\n      ref.current.play();\n    } else {\n      ref.current.pause();\n    }\n  }, [isPlaying]);\n\n  return <video ref={ref} src={src} loop playsInline />;\n}\n\nexport default function App() {\n  const [isPlaying, setIsPlaying] = useState(false);\n  return (\n    <>\n      <button onClick={() => setIsPlaying(!isPlaying)}>\n        {isPlaying ? 'Pause' : 'Play'}\n      </button>\n      <VideoPlayer\n        isPlaying={isPlaying}\n        src=\"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4\"\n      />\n    </>\n  );\n}\n```\n\n```css\nbutton { display: block; margin-bottom: 20px; }\nvideo { width: 250px; }\n```\n\n</Sandpack>\n\n多くのエフェクトは自身を「クリーンアップ」します。例えば、チャットサーバへの接続をセットアップするエフェクトは、そのサーバからコンポーネントを切断する方法を React に伝えるために*クリーンアップ関数*を返します。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nexport default function ChatRoom() {\n  useEffect(() => {\n    const connection = createConnection();\n    connection.connect();\n    return () => connection.disconnect();\n  }, []);\n  return <h1>Welcome to the chat!</h1>;\n}\n```\n\n```js src/chat.js\nexport function createConnection() {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected.');\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\n```\n\n</Sandpack>\n\n開発環境では、React はエフェクトの実行とクリーンアップを素早く 1 回余分に実行します。これが `\"✅ Connecting...\"` が 2 回表示される理由です。これにより、クリーンアップ関数の実装を忘れることがないようになっています。\n\n<LearnMore path=\"/learn/synchronizing-with-effects\">\n\n[**エフェクトを使って同期を行う**](/learn/synchronizing-with-effects)を読んで、コンポーネントを外部システムと同期させる方法を学びましょう。\n\n</LearnMore>\n\n## そのエフェクトは不要かも {/*you-might-not-need-an-effect*/}\n\nエフェクトは React のパラダイムからの避難ハッチです。React の外に「踏み出して」、何らかの外部システムと同期させることができるものです。外部システムが関与していない場合（例えば、props や state の変更に合わせてコンポーネントの state を更新したい場合）、エフェクトは必要ありません。不要なエフェクトを削除することで、コードが読みやすくなり、実行速度が向上し、エラーが発生しにくくなります。\n\nエフェクトが不要な場合として一般的なのは次の 2 つのケースです。\n- **レンダーのためのデータ変換にエフェクトは必要ありません。**\n- **ユーザイベントの処理にエフェクトは必要ありません。**\n\n例えば、他の state に基づいてほかの state を調整するのに、エフェクトは必要ありません。\n\n```js {expectedErrors: {'react-compiler': [8]}} {5-9}\nfunction Form() {\n  const [firstName, setFirstName] = useState('Taylor');\n  const [lastName, setLastName] = useState('Swift');\n\n  // 🔴 Avoid: redundant state and unnecessary Effect\n  const [fullName, setFullName] = useState('');\n  useEffect(() => {\n    setFullName(firstName + ' ' + lastName);\n  }, [firstName, lastName]);\n  // ...\n}\n```\n\n代わりに、レンダー時にできるだけ多くを計算するようにします。\n\n```js {4-5}\nfunction Form() {\n  const [firstName, setFirstName] = useState('Taylor');\n  const [lastName, setLastName] = useState('Swift');\n  // ✅ Good: calculated during rendering\n  const fullName = firstName + ' ' + lastName;\n  // ...\n}\n```\n\nただし、外部システムと同期するためにはエフェクトが*必要*です。\n\n<LearnMore path=\"/learn/you-might-not-need-an-effect\">\n\n[**そのエフェクトは不要かも**](/learn/you-might-not-need-an-effect)を読んで、不要なエフェクトを削除する方法を学びましょう。\n\n</LearnMore>\n\n## リアクティブなエフェクトのライフサイクル {/*lifecycle-of-reactive-effects*/}\n\nエフェクトはコンポーネントとは異なるライフサイクルを持ちます。コンポーネントは、マウント、更新、アンマウントを行うことができます。エフェクトは 2 つのことしかできません。同期の開始と、同期の停止です。エフェクトが props や state に依存し、これらが時間と共に変化する場合、このサイクルは繰り返し発生します。\n\n以下のエフェクトは props である `roomId` に依存しています。props は*リアクティブな値* (reactive value)、つまり再レンダー時に変わる可能性がある値です。`roomId` が変更されると、エフェクトが*再同期*（サーバに再接続）されていることに注目してください。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom roomId={roomId} />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\nReact は、エフェクトの依存配列が正しく指定されているかをチェックするためのリンタルールを提供しています。上記の例で `roomId` を依存値のリストに指定し忘れた場合、リンタがそのバグを自動的に見つけてくれます。\n\n<LearnMore path=\"/learn/lifecycle-of-reactive-effects\">\n\n[**リアクティブなエフェクトのライフサイクル**](/learn/lifecycle-of-reactive-effects)を読んで、エフェクトのライフサイクルがコンポーネントのライフサイクルとどのように異なるかを学びましょう。\n\n</LearnMore>\n\n## イベントとエフェクトを切り離す {/*separating-events-from-effects*/}\n\nイベントハンドラは同じインタラクションを再度実行した場合のみ再実行されます。エフェクトはイベントハンドラとは異なり、props や state 変数のようなそれが読み取る値が前回のレンダー時の値と異なる場合に再同期を行います。また、ある値には反応して再実行するが、他の値には反応しないエフェクトなど、両方の動作をミックスさせたい場合もあります。\n\nエフェクト内のすべてのコードは*リアクティブ*です。それが読み取るリアクティブな値が再レンダーにより変更された場合、再度実行されます。例えば、このエフェクトは `roomId` と `theme` のいずれかが変更された場合にチャットに再接続します。\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"toastify-js\": \"1.12.0\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection, sendMessage } from './chat.js';\nimport { showNotification } from './notifications.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId, theme }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.on('connected', () => {\n      showNotification('Connected!', theme);\n    });\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId, theme]);\n\n  return <h1>Welcome to the {roomId} room!</h1>\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Use dark theme\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n        theme={isDark ? 'dark' : 'light'} \n      />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  let connectedCallback;\n  let timeout;\n  return {\n    connect() {\n      timeout = setTimeout(() => {\n        if (connectedCallback) {\n          connectedCallback();\n        }\n      }, 100);\n    },\n    on(event, callback) {\n      if (connectedCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'connected') {\n        throw Error('Only \"connected\" event is supported.');\n      }\n      connectedCallback = callback;\n    },\n    disconnect() {\n      clearTimeout(timeout);\n    }\n  };\n}\n```\n\n```js src/notifications.js\nimport Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\n\nexport function showNotification(message, theme) {\n  Toastify({\n    text: message,\n    duration: 2000,\n    gravity: 'top',\n    position: 'right',\n    style: {\n      background: theme === 'dark' ? 'black' : 'white',\n      color: theme === 'dark' ? 'white' : 'black',\n    },\n  }).showToast();\n}\n```\n\n```css\nlabel { display: block; margin-top: 10px; }\n```\n\n</Sandpack>\n\nこれはあまり良くありません。`roomId` が変更された場合にのみチャットに再接続したいのです。`theme` の切り替えでチャットの再接続が起きるべきではありません！ `theme` を読み取るコードをエフェクトから*エフェクトイベント (Effect Event)* に移動させましょう。\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"toastify-js\": \"1.12.0\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js\nimport { useState, useEffect } from 'react';\nimport { useEffectEvent } from 'react';\nimport { createConnection, sendMessage } from './chat.js';\nimport { showNotification } from './notifications.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId, theme }) {\n  const onConnected = useEffectEvent(() => {\n    showNotification('Connected!', theme);\n  });\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.on('connected', () => {\n      onConnected();\n    });\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n\n  return <h1>Welcome to the {roomId} room!</h1>\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Use dark theme\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n        theme={isDark ? 'dark' : 'light'} \n      />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  let connectedCallback;\n  let timeout;\n  return {\n    connect() {\n      timeout = setTimeout(() => {\n        if (connectedCallback) {\n          connectedCallback();\n        }\n      }, 100);\n    },\n    on(event, callback) {\n      if (connectedCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'connected') {\n        throw Error('Only \"connected\" event is supported.');\n      }\n      connectedCallback = callback;\n    },\n    disconnect() {\n      clearTimeout(timeout);\n    }\n  };\n}\n```\n\n```js src/notifications.js hidden\nimport Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\n\nexport function showNotification(message, theme) {\n  Toastify({\n    text: message,\n    duration: 2000,\n    gravity: 'top',\n    position: 'right',\n    style: {\n      background: theme === 'dark' ? 'black' : 'white',\n      color: theme === 'dark' ? 'white' : 'black',\n    },\n  }).showToast();\n}\n```\n\n```css\nlabel { display: block; margin-top: 10px; }\n```\n\n</Sandpack>\n\nエフェクトイベント内のコードはリアクティブではないため、`theme` を変更してもエフェクトが再接続されることはありません。\n\n<LearnMore path=\"/learn/separating-events-from-effects\">\n\n[**イベントとエフェクトを切り離す**](/learn/separating-events-from-effects)を読んで、ある値の変化がエフェクトの再トリガを引き起こさないようにする方法を学びましょう。\n\n</LearnMore>\n\n## エフェクトから依存値を取り除く {/*removing-effect-dependencies*/}\n\nエフェクトを記述する際、リンタはエフェクトが読み取るすべてのリアクティブな値（props や state など）がエフェクトの依存値のリストに含まれているか確認します。これにより、エフェクトがコンポーネントの最新の props や state と同期された状態を保つことができます。不要な依存値があると、エフェクトが頻繁に実行され過ぎたり、無限ループが発生したりすることがあります。不要な依存値を取り除く方法はケースによって異なります。\n\nたとえば、このエフェクトは入力フィールドを編集するたびに再作成される `options` オブジェクトに依存しています。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  const options = {\n    serverUrl: serverUrl,\n    roomId: roomId\n  };\n\n  useEffect(() => {\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [options]);\n\n  return (\n    <>\n      <h1>Welcome to the {roomId} room!</h1>\n      <input value={message} onChange={e => setMessage(e.target.value)} />\n    </>\n  );\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom roomId={roomId} />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\nチャットでメッセージを入力し始めるたびにチャットが再接続されることは望ましくありません。この問題を解決するために、`options` オブジェクトの作成をエフェクト内に移動し、エフェクトが `roomId` 文字列にのみ依存するようにします：\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const options = {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n\n  return (\n    <>\n      <h1>Welcome to the {roomId} room!</h1>\n      <input value={message} onChange={e => setMessage(e.target.value)} />\n    </>\n  );\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom roomId={roomId} />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n依存値のリストを編集して `options` を削除することから始めなかったことに注意してください。それは間違いです。代わりに、依存関係が*不要*になるよう、周囲のコードを変更したのです。依存値のリストは、あなたのエフェクトのコードが使用しているすべてのリアクティブな値の一覧だと考えてください。そのリストに何を置くかを意識して選ぶのではありません。リストはあなたのコードの説明にすぎません。依存配列を変更するには、コードの方を変更します。\n\n<LearnMore path=\"/learn/removing-effect-dependencies\">\n\n[**エフェクトから依存値を取り除く**](/learn/removing-effect-dependencies)を読んで、エフェクトの再実行頻度を減らす方法を学びましょう。\n\n</LearnMore>\n\n## カスタムフックでロジックを再利用する {/*reusing-logic-with-custom-hooks*/}\n\nReact には `useState`、`useContext`、`useEffect` など複数の組み込みフックが存在します。しかし、データの取得やユーザのオンライン状態の監視、チャットルームへの接続など、より特化した目的のためのフックが欲しいこともあります。これらを行うためには、アプリケーションの要求に合わせて独自のフックを作成することが可能です。\n\n以下の例では、`usePointerPosition` カスタムフックがカーソルの位置を追跡し、`useDelayedValue` カスタムフックが値をある一定のミリ秒だけ「遅らせて」返します。サンドボックスのプレビューエリア上でカーソルを動かすと、カーソルに追従する複数のドットによる軌跡が現れます。\n\n<Sandpack>\n\n```js\nimport { usePointerPosition } from './usePointerPosition.js';\nimport { useDelayedValue } from './useDelayedValue.js';\n\nexport default function Canvas() {\n  const pos1 = usePointerPosition();\n  const pos2 = useDelayedValue(pos1, 100);\n  const pos3 = useDelayedValue(pos2, 200);\n  const pos4 = useDelayedValue(pos3, 100);\n  const pos5 = useDelayedValue(pos4, 50);\n  return (\n    <>\n      <Dot position={pos1} opacity={1} />\n      <Dot position={pos2} opacity={0.8} />\n      <Dot position={pos3} opacity={0.6} />\n      <Dot position={pos4} opacity={0.4} />\n      <Dot position={pos5} opacity={0.2} />\n    </>\n  );\n}\n\nfunction Dot({ position, opacity }) {\n  return (\n    <div style={{\n      position: 'absolute',\n      backgroundColor: 'pink',\n      borderRadius: '50%',\n      opacity,\n      transform: `translate(${position.x}px, ${position.y}px)`,\n      pointerEvents: 'none',\n      left: -20,\n      top: -20,\n      width: 40,\n      height: 40,\n    }} />\n  );\n}\n```\n\n```js src/usePointerPosition.js\nimport { useState, useEffect } from 'react';\n\nexport function usePointerPosition() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n  useEffect(() => {\n    function handleMove(e) {\n      setPosition({ x: e.clientX, y: e.clientY });\n    }\n    window.addEventListener('pointermove', handleMove);\n    return () => window.removeEventListener('pointermove', handleMove);\n  }, []);\n  return position;\n}\n```\n\n```js src/useDelayedValue.js\nimport { useState, useEffect } from 'react';\n\nexport function useDelayedValue(value, delay) {\n  const [delayedValue, setDelayedValue] = useState(value);\n\n  useEffect(() => {\n    setTimeout(() => {\n      setDelayedValue(value);\n    }, delay);\n  }, [value, delay]);\n\n  return delayedValue;\n}\n```\n\n```css\nbody { min-height: 300px; }\n```\n\n</Sandpack>\n\nカスタムフックを作成し、それらを組み合わせ、データを受け渡し、コンポーネント間で再利用することができます。アプリが成長するにつれて、すでに書いたカスタムフックを再利用することで、手作業でエフェクトを書く回数が減っていきます。また、React コミュニティによってメンテナンスされている優れたカスタムフックも多数あります。\n\n<LearnMore path=\"/learn/reusing-logic-with-custom-hooks\">\n\n[**カスタムフックでロジックを再利用する**](/learn/reusing-logic-with-custom-hooks)を読んで、コンポーネント間でロジックを共有する方法について学びましょう。\n\n</LearnMore>\n\n## 次のステップ {/*whats-next*/}\n\n[ref で値を参照する](/learn/referencing-values-with-refs)に進み、この章をページごとに読み進めてください！\n"
  },
  {
    "path": "src/content/learn/extracting-state-logic-into-a-reducer.md",
    "content": "---\ntitle: state ロジックをリデューサに抽出する\n---\n\n<Intro>\n\n多くのイベントハンドラにまたがって state の更新コードが含まれるコンポーネントは、理解が大変になりがちです。このような場合、コンポーネントの外部に、*リデューサ (reducer)* と呼ばれる単一の関数を作成し、すべての state 更新ロジックを集約することができます。\n\n</Intro>\n\n<YouWillLearn>\n\n- リデューサ関数とは何か\n- `useState` から `useReducer` にリファクタリングする方法\n- リデューサを使用するタイミング\n- リデューサを適切に記述する方法\n\n</YouWillLearn>\n\n## リデューサで state ロジックを集約する {/*consolidate-state-logic-with-a-reducer*/}\n\nコンポーネントの複雑さが増すにつれ、コンポーネントの state がどのように更新されるかを一目で確認することが難しくなります。例えば、以下の `TaskApp` コンポーネントは state として `tasks` という配列を保持しており、タスクの追加・削除・編集を行うために 3 つの異なるイベントハンドラが使用されています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\n\nexport default function TaskApp() {\n  const [tasks, setTasks] = useState(initialTasks);\n\n  function handleAddTask(text) {\n    setTasks([\n      ...tasks,\n      {\n        id: nextId++,\n        text: text,\n        done: false,\n      },\n    ]);\n  }\n\n  function handleChangeTask(task) {\n    setTasks(\n      tasks.map((t) => {\n        if (t.id === task.id) {\n          return task;\n        } else {\n          return t;\n        }\n      })\n    );\n  }\n\n  function handleDeleteTask(taskId) {\n    setTasks(tasks.filter((t) => t.id !== taskId));\n  }\n\n  return (\n    <>\n      <h1>Prague itinerary</h1>\n      <AddTask onAddTask={handleAddTask} />\n      <TaskList\n        tasks={tasks}\n        onChangeTask={handleChangeTask}\n        onDeleteTask={handleDeleteTask}\n      />\n    </>\n  );\n}\n\nlet nextId = 3;\nconst initialTasks = [\n  {id: 0, text: 'Visit Kafka Museum', done: true},\n  {id: 1, text: 'Watch a puppet show', done: false},\n  {id: 2, text: 'Lennon Wall pic', done: false},\n];\n```\n\n```js src/AddTask.js hidden\nimport { useState } from 'react';\n\nexport default function AddTask({onAddTask}) {\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={(e) => setText(e.target.value)}\n      />\n      <button\n        onClick={() => {\n          setText('');\n          onAddTask(text);\n        }}>\n        Add\n      </button>\n    </>\n  );\n}\n```\n\n```js src/TaskList.js hidden\nimport { useState } from 'react';\n\nexport default function TaskList({tasks, onChangeTask, onDeleteTask}) {\n  return (\n    <ul>\n      {tasks.map((task) => (\n        <li key={task.id}>\n          <Task task={task} onChange={onChangeTask} onDelete={onDeleteTask} />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({task, onChange, onDelete}) {\n  const [isEditing, setIsEditing] = useState(false);\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={(e) => {\n            onChange({\n              ...task,\n              text: e.target.value,\n            });\n          }}\n        />\n        <button onClick={() => setIsEditing(false)}>Save</button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>Edit</button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={(e) => {\n          onChange({\n            ...task,\n            done: e.target.checked,\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => onDelete(task.id)}>Delete</button>\n    </label>\n  );\n}\n```\n\n```css\nbutton {\n  margin: 5px;\n}\nli {\n  list-style-type: none;\n}\nul,\nli {\n  margin: 0;\n  padding: 0;\n}\n```\n\n</Sandpack>\n\nそれぞれのイベントハンドラは `setTasks` を呼び出して state を更新しています。このコンポーネントが大きくなるにつれ、そこにバラバラに書かれる state ロジックの量も増えていきます。複雑さを減らし、すべてのロジックを 1 つの簡単にアクセスできる場所にまとめるために、コンポーネントの外部にある 1 つの関数、すなわち**リデューサ関数**とよばれるものに、state ロジックを移動させることができます。\n\nリデューサは、state を扱うもう 1 つの方法です。`useState` から `useReducer` への移行は、次の 3 つのステップで行うことができます。\n\n1. state セットをアクションのディスパッチに**置き換える**。\n2. リデューサ関数を**作成**する。\n3. コンポーネントからリデューサを**使用**する。\n\n### ステップ 1：state セットをアクションのディスパッチに置き換える {/*step-1-move-from-setting-state-to-dispatching-actions*/}\n\n現在のイベントハンドラは、state をセットすることで*何をするのか*を指定しています。\n\n```js\nfunction handleAddTask(text) {\n  setTasks([\n    ...tasks,\n    {\n      id: nextId++,\n      text: text,\n      done: false,\n    },\n  ]);\n}\n\nfunction handleChangeTask(task) {\n  setTasks(\n    tasks.map((t) => {\n      if (t.id === task.id) {\n        return task;\n      } else {\n        return t;\n      }\n    })\n  );\n}\n\nfunction handleDeleteTask(taskId) {\n  setTasks(tasks.filter((t) => t.id !== taskId));\n}\n```\n\nstate をセットするロジックをすべて削除します。残るのは以下の 3 つのイベントハンドラです。\n\n- `handleAddTask(text)` は、ユーザが \"Add\" を押したときに呼び出される。\n- `handleChangeTask(task)` は、ユーザがタスクのチェック状態を切り替えたときや \"Save\" を押したときに呼び出される。\n- `handleDeleteTask(taskId)` は、ユーザが \"Delete\" を押したときに呼び出される。\n\nリデューサを使った state 管理は state を直接セットするのとは少し異なります。React に対して state をセットして「何をするか」を指示するのではなく、イベントハンドラから「アクション」をディスパッチすることで「ユーザが何をしたか」を指定します。（state の更新ロジックは別の場所に書きます！）つまりイベントハンドラで「`tasks` をセットする」のではなく、「タスクを追加/変更/削除した」というアクションのディスパッチを行います。これはユーザの意図をより具体的に表現するものです。\n\n```js\nfunction handleAddTask(text) {\n  dispatch({\n    type: 'added',\n    id: nextId++,\n    text: text,\n  });\n}\n\nfunction handleChangeTask(task) {\n  dispatch({\n    type: 'changed',\n    task: task,\n  });\n}\n\nfunction handleDeleteTask(taskId) {\n  dispatch({\n    type: 'deleted',\n    id: taskId,\n  });\n}\n```\n\n`dispatch` に渡すオブジェクトは \"アクション (action)\" と呼ばれます。\n\n```js {3-7}\nfunction handleDeleteTask(taskId) {\n  dispatch(\n    // \"action\" object:\n    {\n      type: 'deleted',\n      id: taskId,\n    }\n  );\n}\n```\n\nアクションは通常の JavaScript オブジェクトです。何を入れるかはあなたが決めることですが、一般的には「*何が起こったか*」に関する最小限の情報が含まれているべきです。（`dispatch` 関数自体は後のステップで追加します。）\n\n<Note>\n\nアクションオブジェクトはどのような形状でも構いません。\n\n一般的な慣習としては、何が起こったかを説明する文字列の `type` を与え、他のフィールドを使って追加情報を渡します。`type` はコンポーネント固有のもので、この例では `'added'` や `'added_task'` といったものがよいでしょう。何が起こったかを説明する名前を選んでください！\n\n```js\ndispatch({\n  // specific to component\n  type: 'what_happened',\n  // other fields go here\n});\n```\n\n</Note>\n\n### ステップ 2：リデューサ関数を作成する {/*step-2-write-a-reducer-function*/}\n\nリデューサ関数が、state のロジックを記述する場所です。現在の state とアクションオブジェクトの 2 つを引数に取り、次の state を返すようにします。\n\n```js\nfunction yourReducer(state, action) {\n  // return next state for React to set\n}\n```\n\nReact が state をリデューサからの返り値にセットします。\n\nこの例では、イベントハンドラからリデューサ関数に state の設定ロジックを移動するために、以下の手順を実施します。\n\n1. 現在の state (`tasks`) を最初の引数として宣言する。\n2. `action` オブジェクトを 2 番目の引数として宣言する。\n3. リデューサから*次の* state を返す（React が state をその値にセットする）。\n\n以下がリデューサ関数に移行した state 設定ロジックの全体像です :\n\n```js\nfunction tasksReducer(tasks, action) {\n  if (action.type === 'added') {\n    return [\n      ...tasks,\n      {\n        id: action.id,\n        text: action.text,\n        done: false,\n      },\n    ];\n  } else if (action.type === 'changed') {\n    return tasks.map((t) => {\n      if (t.id === action.task.id) {\n        return action.task;\n      } else {\n        return t;\n      }\n    });\n  } else if (action.type === 'deleted') {\n    return tasks.filter((t) => t.id !== action.id);\n  } else {\n    throw Error('Unknown action: ' + action.type);\n  }\n}\n```\n\nリデューサ関数は state (`tasks`) を引数として取るため、**コンポーネントの外部で宣言することができます**。これにより、インデントレベルが減り、コードが読みやすくなります。\n\n<Note>\n\n上記のコードでは if/else 文が使用されていますが、リデューサの中では [switch 文](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/switch)を使うことが一般的です。結果は同じですが、switch 文の方が一目でわかりやすくなります。\n\nこれ以降のドキュメントでは、switch 文を以下のように使用します。\n\n```js\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [\n        ...tasks,\n        {\n          id: action.id,\n          text: action.text,\n          done: false,\n        },\n      ];\n    }\n    case 'changed': {\n      return tasks.map((t) => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter((t) => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n```\n\n各 `case` ブロックを `{` と `}` の波括弧で囲むことをお勧めします。これにより、異なる `case` の中で宣言された変数が互いに衝突するのを防ぐことができます。また、`case` は通常 `return` で終わるべきです。`return` を忘れると、コードが次の `case` に「流れて」しまい、誤りが発生することがあります！\n\nもしまだ switch 文に慣れていないのであれば、if/else を使用しても全く問題ありません。\n\n</Note>\n\n<DeepDive>\n\n#### なぜリデューサと呼ばれるのか？ {/*why-are-reducers-called-this-way*/}\n\nリデューサによりコンポーネント内のコード量を「削減 (reduce)」することもできますが、実際にはリデューサは配列で行うことができる [`reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) という操作にちなんで名付けられています。\n\n`reduce()` 操作とは、配列を受け取り、多くの値を 1 つの値に「まとめる」ことができるものです。\n\n```\nconst arr = [1, 2, 3, 4, 5];\nconst sum = arr.reduce(\n  (result, number) => result + number\n); // 1 + 2 + 3 + 4 + 5\n```\n\nここで `reduce` に渡している関数が \"リデューサ\" と呼ばれるものです。これは「ここまでの結果」と「現在の要素」を受け取り、「次の結果」を返す関数です。React のリデューサも同じアイディアを用いています。「ここまでの state」と「アクション」を受け取り、「次の state」を返します。このようにして、経時的に発生する複数のアクションを 1 つの state に「まとめて」いるわけです。\n\n`reduce()` メソッドにリデューサを渡して使用することで、初期 state とアクションの配列から最終 state を計算することもできます。\n\n<Sandpack>\n\n```js src/index.js active\nimport tasksReducer from './tasksReducer.js';\n\nlet initialState = [];\nlet actions = [\n  {type: 'added', id: 1, text: 'Visit Kafka Museum'},\n  {type: 'added', id: 2, text: 'Watch a puppet show'},\n  {type: 'deleted', id: 1},\n  {type: 'added', id: 3, text: 'Lennon Wall pic'},\n];\n\nlet finalState = actions.reduce(tasksReducer, initialState);\n\nconst output = document.getElementById('output');\noutput.textContent = JSON.stringify(finalState, null, 2);\n```\n\n```js src/tasksReducer.js\nexport default function tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [\n        ...tasks,\n        {\n          id: action.id,\n          text: action.text,\n          done: false,\n        },\n      ];\n    }\n    case 'changed': {\n      return tasks.map((t) => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter((t) => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n```\n\n```html public/index.html\n<pre id=\"output\"></pre>\n```\n\n</Sandpack>\n\n自前でこれを行う必要はないでしょうが、React が行っていることもこれと似ています！\n\n</DeepDive>\n\n### ステップ 3：コンポーネントからリデューサを使用する {/*step-3-use-the-reducer-from-your-component*/}\n\n最後に、`tasksReducer` をコンポーネントに接続する必要があります。React から `useReducer` フックをインポートしてください。\n\n```js\nimport { useReducer } from 'react';\n```\n\nそして、以下の `useState` 呼び出しを：\n\n```js\nconst [tasks, setTasks] = useState(initialTasks);\n```\n\nこのように `useReducer` で置き換えます：\n\n```js\nconst [tasks, dispatch] = useReducer(tasksReducer, initialTasks);\n```\n\n`useReducer` フックは、初期 state を受け取り、state 値とそれを更新するための手段（この場合は dispatch 関数）を返す、という点では `useState` に似ています。ただし少し違いもあります。\n\n`useReducer` フックは 2 つの引数を取ります。\n\n1. リデューサ関数\n2. 初期 state\n\nそして次のものを返します。\n\n1. state 値\n2. ディスパッチ関数（ユーザアクションをリデューサに「ディスパッチ」する）\n\nさあ、これですべてが繋がりました！ ここでは、リデューサがコンポーネントファイルの最後に宣言されています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\n\nexport default function TaskApp() {\n  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);\n\n  function handleAddTask(text) {\n    dispatch({\n      type: 'added',\n      id: nextId++,\n      text: text,\n    });\n  }\n\n  function handleChangeTask(task) {\n    dispatch({\n      type: 'changed',\n      task: task,\n    });\n  }\n\n  function handleDeleteTask(taskId) {\n    dispatch({\n      type: 'deleted',\n      id: taskId,\n    });\n  }\n\n  return (\n    <>\n      <h1>Prague itinerary</h1>\n      <AddTask onAddTask={handleAddTask} />\n      <TaskList\n        tasks={tasks}\n        onChangeTask={handleChangeTask}\n        onDeleteTask={handleDeleteTask}\n      />\n    </>\n  );\n}\n\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [\n        ...tasks,\n        {\n          id: action.id,\n          text: action.text,\n          done: false,\n        },\n      ];\n    }\n    case 'changed': {\n      return tasks.map((t) => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter((t) => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nlet nextId = 3;\nconst initialTasks = [\n  {id: 0, text: 'Visit Kafka Museum', done: true},\n  {id: 1, text: 'Watch a puppet show', done: false},\n  {id: 2, text: 'Lennon Wall pic', done: false},\n];\n```\n\n```js src/AddTask.js hidden\nimport { useState } from 'react';\n\nexport default function AddTask({onAddTask}) {\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={(e) => setText(e.target.value)}\n      />\n      <button\n        onClick={() => {\n          setText('');\n          onAddTask(text);\n        }}>\n        Add\n      </button>\n    </>\n  );\n}\n```\n\n```js src/TaskList.js hidden\nimport { useState } from 'react';\n\nexport default function TaskList({tasks, onChangeTask, onDeleteTask}) {\n  return (\n    <ul>\n      {tasks.map((task) => (\n        <li key={task.id}>\n          <Task task={task} onChange={onChangeTask} onDelete={onDeleteTask} />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({task, onChange, onDelete}) {\n  const [isEditing, setIsEditing] = useState(false);\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={(e) => {\n            onChange({\n              ...task,\n              text: e.target.value,\n            });\n          }}\n        />\n        <button onClick={() => setIsEditing(false)}>Save</button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>Edit</button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={(e) => {\n          onChange({\n            ...task,\n            done: e.target.checked,\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => onDelete(task.id)}>Delete</button>\n    </label>\n  );\n}\n```\n\n```css\nbutton {\n  margin: 5px;\n}\nli {\n  list-style-type: none;\n}\nul,\nli {\n  margin: 0;\n  padding: 0;\n}\n```\n\n</Sandpack>\n\n必要であれば、リデューサを別のファイルに移動させることもできます。\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\nimport tasksReducer from './tasksReducer.js';\n\nexport default function TaskApp() {\n  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);\n\n  function handleAddTask(text) {\n    dispatch({\n      type: 'added',\n      id: nextId++,\n      text: text,\n    });\n  }\n\n  function handleChangeTask(task) {\n    dispatch({\n      type: 'changed',\n      task: task,\n    });\n  }\n\n  function handleDeleteTask(taskId) {\n    dispatch({\n      type: 'deleted',\n      id: taskId,\n    });\n  }\n\n  return (\n    <>\n      <h1>Prague itinerary</h1>\n      <AddTask onAddTask={handleAddTask} />\n      <TaskList\n        tasks={tasks}\n        onChangeTask={handleChangeTask}\n        onDeleteTask={handleDeleteTask}\n      />\n    </>\n  );\n}\n\nlet nextId = 3;\nconst initialTasks = [\n  {id: 0, text: 'Visit Kafka Museum', done: true},\n  {id: 1, text: 'Watch a puppet show', done: false},\n  {id: 2, text: 'Lennon Wall pic', done: false},\n];\n```\n\n```js src/tasksReducer.js\nexport default function tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [\n        ...tasks,\n        {\n          id: action.id,\n          text: action.text,\n          done: false,\n        },\n      ];\n    }\n    case 'changed': {\n      return tasks.map((t) => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter((t) => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n```\n\n```js src/AddTask.js hidden\nimport { useState } from 'react';\n\nexport default function AddTask({onAddTask}) {\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={(e) => setText(e.target.value)}\n      />\n      <button\n        onClick={() => {\n          setText('');\n          onAddTask(text);\n        }}>\n        Add\n      </button>\n    </>\n  );\n}\n```\n\n```js src/TaskList.js hidden\nimport { useState } from 'react';\n\nexport default function TaskList({tasks, onChangeTask, onDeleteTask}) {\n  return (\n    <ul>\n      {tasks.map((task) => (\n        <li key={task.id}>\n          <Task task={task} onChange={onChangeTask} onDelete={onDeleteTask} />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({task, onChange, onDelete}) {\n  const [isEditing, setIsEditing] = useState(false);\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={(e) => {\n            onChange({\n              ...task,\n              text: e.target.value,\n            });\n          }}\n        />\n        <button onClick={() => setIsEditing(false)}>Save</button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>Edit</button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={(e) => {\n          onChange({\n            ...task,\n            done: e.target.checked,\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => onDelete(task.id)}>Delete</button>\n    </label>\n  );\n}\n```\n\n```css\nbutton {\n  margin: 5px;\n}\nli {\n  list-style-type: none;\n}\nul,\nli {\n  margin: 0;\n  padding: 0;\n}\n```\n\n</Sandpack>\n\nこのように関心を分離することで、コンポーネントのロジックが読みやすくなります。イベントハンドラはアクションをディスパッチすることで「何が起こったか」を指定し、リデューサ関数がそれらに対する「state の更新方法」を決定します。\n\n## `useState` と `useReducer` の比較 {/*comparing-usestate-and-usereducer*/}\n\nリデューサにもデメリットがないわけではありません。以下のような様々な点で両者には違いがあります。\n\n- **コードサイズ**：一般に、`useState` を使った方が最初に書くコードは少なくなります。`useReducer` の場合、リデューサ関数とアクションをディスパッチするコードを*両方*書く必要があります。ただし、多くのイベントハンドラが同様の方法で state を変更している場合、`useReducer` によりコードを削減できます。\n- **可読性**：シンプルな state 更新の場合は `useState` を読むのは非常に簡単です。しかし、より複雑になると、コンポーネントのコードが肥大化し、見通すことが難しくなります。このような場合、`useReducer` を使うことで、更新ロジックによって書かれる「どう更新するのか」と、イベントハンドラに書かれる「何が起きたのか」とを、きれいに分離することができます。\n- **デバッグ**：`useState` を使っていてバグがある場合、state が*どこで*誤ってセットされたのか、*なぜ*そうなったかを特定するのが難しくなることがあります。`useReducer` を使えば、リデューサにコンソールログを追加することで、すべての state 更新と、それがなぜ起こったか（どの `action` のせいか）を確認できます。それぞれの `action` が正しい場合、リデューサのロジック自体に問題があることが分かります。ただし、`useState` と比べてより多くのコードを調べる必要があります。\n- **テスト**：リデューサはコンポーネントに依存しない純関数です。これは、リデューサをエクスポートし、他のものとは別に単体でテストできることを意味します。一般的には、より現実的な環境でコンポーネントをテストするのがベストですが、複雑な state 更新ロジックがある場合は、特定の初期 state とアクションに対してリデューサが特定の state を返すことをテストすることが役立ちます。\n- **個人の好み**：人によってリデューサが好きだったり、好きではなかったりします。それで構いません。好みの問題です。`useState` と `useReducer` の間を行ったり来たりすることはいつでも可能です。どちらも同等のものです！\n\nバグが頻繁に発生しておりコンポーネントのコードに構造を導入したい場合に、リデューサを利用することをお勧めします。あらゆるコンポーネントにリデューサを使用する必要はありません。自由に組み合わせてください！ 同じコンポーネントで `useState` と `useReducer` を両方使うことも可能です。\n\n## 良いリデューサの書き方 {/*writing-reducers-well*/}\n\nリデューサを書く際には、以下の 2 つのポイントを心に留めておきましょう。\n\n- **リデューサは純粋である必要があります**。[state の更新用関数](/learn/queueing-a-series-of-state-updates)と同様に、リデューサはレンダー中に実行されます！（アクションは次のレンダーまでキューに入れられます。）つまりリデューサは[純粋でなければならない](/learn/keeping-components-pure)ということです。同じ入力に対して常に同じ出力になります。リクエストを送信したり、タイムアウトを設定したり、副作用（コンポーネントの外部に影響を与える操作）を実行したりすべきではありません。リデューサは、[オブジェクト](/learn/updating-objects-in-state)や[配列](/learn/updating-arrays-in-state)をミューテーション（書き換え）せずに更新する必要があります。\n- **各アクションは、複数データの更新を伴う場合であっても単一のユーザ操作を記述するようにします**。たとえば、リデューサで管理されるフォームに \"Reset\" ボタンがあり、そのフォームには 5 つのフィールドがある場合、5 つの別々の `set_field` アクションをディスパッチするよりも、1 つの `reset_form` アクションをディスパッチする方が理にかなっています。リデューサの各アクションを記録している場合、そのログは、どんなユーザ操作やレスポンスがどんな順序で発生したかを再構築できるほど明確でなければなりません。これはデバッグに役立ちます！\n\n## Immer を使用した簡潔なリデューサの記述 {/*writing-concise-reducers-with-immer*/}\n\n通常の state における[オブジェクトの更新](/learn/updating-objects-in-state#write-concise-update-logic-with-immer)や[配列の更新](/learn/updating-arrays-in-state#write-concise-update-logic-with-immer)と同様に、Immer ライブラリを使用してリデューサをより簡潔に記述できます。以下の例では、[`useImmerReducer`](https://github.com/immerjs/use-immer#useimmerreducer) を使って、`push` または `arr[i] =` という代入を使って state の書き換えを行っています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useImmerReducer } from 'use-immer';\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\n\nfunction tasksReducer(draft, action) {\n  switch (action.type) {\n    case 'added': {\n      draft.push({\n        id: action.id,\n        text: action.text,\n        done: false,\n      });\n      break;\n    }\n    case 'changed': {\n      const index = draft.findIndex((t) => t.id === action.task.id);\n      draft[index] = action.task;\n      break;\n    }\n    case 'deleted': {\n      return draft.filter((t) => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nexport default function TaskApp() {\n  const [tasks, dispatch] = useImmerReducer(tasksReducer, initialTasks);\n\n  function handleAddTask(text) {\n    dispatch({\n      type: 'added',\n      id: nextId++,\n      text: text,\n    });\n  }\n\n  function handleChangeTask(task) {\n    dispatch({\n      type: 'changed',\n      task: task,\n    });\n  }\n\n  function handleDeleteTask(taskId) {\n    dispatch({\n      type: 'deleted',\n      id: taskId,\n    });\n  }\n\n  return (\n    <>\n      <h1>Prague itinerary</h1>\n      <AddTask onAddTask={handleAddTask} />\n      <TaskList\n        tasks={tasks}\n        onChangeTask={handleChangeTask}\n        onDeleteTask={handleDeleteTask}\n      />\n    </>\n  );\n}\n\nlet nextId = 3;\nconst initialTasks = [\n  {id: 0, text: 'Visit Kafka Museum', done: true},\n  {id: 1, text: 'Watch a puppet show', done: false},\n  {id: 2, text: 'Lennon Wall pic', done: false},\n];\n```\n\n```js src/AddTask.js hidden\nimport { useState } from 'react';\n\nexport default function AddTask({onAddTask}) {\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={(e) => setText(e.target.value)}\n      />\n      <button\n        onClick={() => {\n          setText('');\n          onAddTask(text);\n        }}>\n        Add\n      </button>\n    </>\n  );\n}\n```\n\n```js src/TaskList.js hidden\nimport { useState } from 'react';\n\nexport default function TaskList({tasks, onChangeTask, onDeleteTask}) {\n  return (\n    <ul>\n      {tasks.map((task) => (\n        <li key={task.id}>\n          <Task task={task} onChange={onChangeTask} onDelete={onDeleteTask} />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({task, onChange, onDelete}) {\n  const [isEditing, setIsEditing] = useState(false);\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={(e) => {\n            onChange({\n              ...task,\n              text: e.target.value,\n            });\n          }}\n        />\n        <button onClick={() => setIsEditing(false)}>Save</button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>Edit</button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={(e) => {\n          onChange({\n            ...task,\n            done: e.target.checked,\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => onDelete(task.id)}>Delete</button>\n    </label>\n  );\n}\n```\n\n```css\nbutton {\n  margin: 5px;\n}\nli {\n  list-style-type: none;\n}\nul,\nli {\n  margin: 0;\n  padding: 0;\n}\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"use-immer\": \"0.5.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\nリデューサは純関数である必要があるため、state を書き換えてはいけません。しかし、Immer は特別な `draft` オブジェクトを提供しており、これを書き換えることは安全です。Immer は内部で、`draft` に加えたミューテーションが適用された state のコピーを作成します。これが、`useImmerReducer` で管理されるリデューサが、最初の引数に書き換えを行えばよく、state を返す必要がない理由です。\n\n<Recap>\n\n- `useState` から `useReducer` に変換するには：\n  1. イベントハンドラからアクションをディスパッチする。\n  2. state とアクションから次の state を返すリデューサ関数を書く。\n  3. `useState` を `useReducer` に置き換える。\n- リデューサにより書くコードの量は少し増えるが、デバッグやテストに有用である。\n- リデューサは純粋である必要がある。\n- 各アクションは、ユーザの操作を 1 つだけ記述する。\n- Immer を使えば、ミューテーション型のスタイルでリデューサを書ける。\n\n</Recap>\n\n<Challenges>\n\n#### イベントハンドラからアクションをディスパッチ {/*dispatch-actions-from-event-handlers*/}\n\n現在、`ContactList.js` と `Chat.js` のイベントハンドラは `// TODO` というコメントになっています。このため入力フィールドに入力しても動作せず、ボタンをクリックしても選択された送信先が変更されません。\n\nこれら 2 つの `// TODO` を、適切なアクションを `dispatch` するコードに置き換えてください。アクションの構造やタイプを確認するには、`messengerReducer.js` 内のリデューサをチェックしてください。リデューサ自体はすでに書かれているので、変更する必要はありません。`ContactList.js` と `Chat.js` でアクションをディスパッチするだけで構いません。\n\n<Hint>\n\nこれらのコンポーネントでは、`dispatch` 関数はすでに props として渡されており利用可能です。対応するアクションオブジェクトでその `dispatch` を呼び出すようにしてください。\n\nアクションオブジェクトの構造を確認するには、リデューサを見て、`action` にどんなフィールドが必要かを確認します。例えば、リデューサの `changed_selection` に対応する case は次のようになっています。\n\n```js\ncase 'changed_selection': {\n  return {\n    ...state,\n    selectedId: action.contactId\n  };\n}\n```\n\nつまりアクションオブジェクトに `type: 'changed_selection'` を持たせる必要があるということです。また、`action.contactId` が使われているので、アクションに `contactId` プロパティを含める必要があるでしょう。\n\n</Hint>\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\nimport { initialState, messengerReducer } from './messengerReducer';\n\nexport default function Messenger() {\n  const [state, dispatch] = useReducer(messengerReducer, initialState);\n  const message = state.message;\n  const contact = contacts.find((c) => c.id === state.selectedId);\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedId={state.selectedId}\n        dispatch={dispatch}\n      />\n      <Chat\n        key={contact.id}\n        message={message}\n        contact={contact}\n        dispatch={dispatch}\n      />\n    </div>\n  );\n}\n\nconst contacts = [\n  {id: 0, name: 'Taylor', email: 'taylor@mail.com'},\n  {id: 1, name: 'Alice', email: 'alice@mail.com'},\n  {id: 2, name: 'Bob', email: 'bob@mail.com'},\n];\n```\n\n```js src/messengerReducer.js\nexport const initialState = {\n  selectedId: 0,\n  message: 'Hello',\n};\n\nexport function messengerReducer(state, action) {\n  switch (action.type) {\n    case 'changed_selection': {\n      return {\n        ...state,\n        selectedId: action.contactId,\n        message: '',\n      };\n    }\n    case 'edited_message': {\n      return {\n        ...state,\n        message: action.message,\n      };\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n```\n\n```js src/ContactList.js\nexport default function ContactList({contacts, selectedId, dispatch}) {\n  return (\n    <section className=\"contact-list\">\n      <ul>\n        {contacts.map((contact) => (\n          <li key={contact.id}>\n            <button\n              onClick={() => {\n                // TODO: dispatch changed_selection\n              }}>\n              {selectedId === contact.id ? <b>{contact.name}</b> : contact.name}\n            </button>\n          </li>\n        ))}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/Chat.js\nimport { useState } from 'react';\n\nexport default function Chat({contact, message, dispatch}) {\n  return (\n    <section className=\"chat\">\n      <textarea\n        value={message}\n        placeholder={'Chat to ' + contact.name}\n        onChange={(e) => {\n          // TODO: dispatch edited_message\n          // (Read the input value from e.target.value)\n        }}\n      />\n      <br />\n      <button>Send to {contact.email}</button>\n    </section>\n  );\n}\n```\n\n```css\n.chat,\n.contact-list {\n  float: left;\n  margin-bottom: 20px;\n}\nul,\nli {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli button {\n  width: 100px;\n  padding: 10px;\n  margin-right: 10px;\n}\ntextarea {\n  height: 150px;\n}\n```\n\n</Sandpack>\n\n<Solution>\n\nリデューサのコードから、アクションは次のような見た目になることが推測できます。\n\n```js\n// When the user presses \"Alice\"\ndispatch({\n  type: 'changed_selection',\n  contactId: 1,\n});\n\n// When user types \"Hello!\"\ndispatch({\n  type: 'edited_message',\n  message: 'Hello!',\n});\n```\n\n対応するメッセージをディスパッチするように更新した例はこちらです。\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\nimport { initialState, messengerReducer } from './messengerReducer';\n\nexport default function Messenger() {\n  const [state, dispatch] = useReducer(messengerReducer, initialState);\n  const message = state.message;\n  const contact = contacts.find((c) => c.id === state.selectedId);\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedId={state.selectedId}\n        dispatch={dispatch}\n      />\n      <Chat\n        key={contact.id}\n        message={message}\n        contact={contact}\n        dispatch={dispatch}\n      />\n    </div>\n  );\n}\n\nconst contacts = [\n  {id: 0, name: 'Taylor', email: 'taylor@mail.com'},\n  {id: 1, name: 'Alice', email: 'alice@mail.com'},\n  {id: 2, name: 'Bob', email: 'bob@mail.com'},\n];\n```\n\n```js src/messengerReducer.js\nexport const initialState = {\n  selectedId: 0,\n  message: 'Hello',\n};\n\nexport function messengerReducer(state, action) {\n  switch (action.type) {\n    case 'changed_selection': {\n      return {\n        ...state,\n        selectedId: action.contactId,\n        message: '',\n      };\n    }\n    case 'edited_message': {\n      return {\n        ...state,\n        message: action.message,\n      };\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n```\n\n```js src/ContactList.js\nexport default function ContactList({contacts, selectedId, dispatch}) {\n  return (\n    <section className=\"contact-list\">\n      <ul>\n        {contacts.map((contact) => (\n          <li key={contact.id}>\n            <button\n              onClick={() => {\n                dispatch({\n                  type: 'changed_selection',\n                  contactId: contact.id,\n                });\n              }}>\n              {selectedId === contact.id ? <b>{contact.name}</b> : contact.name}\n            </button>\n          </li>\n        ))}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/Chat.js\nimport { useState } from 'react';\n\nexport default function Chat({contact, message, dispatch}) {\n  return (\n    <section className=\"chat\">\n      <textarea\n        value={message}\n        placeholder={'Chat to ' + contact.name}\n        onChange={(e) => {\n          dispatch({\n            type: 'edited_message',\n            message: e.target.value,\n          });\n        }}\n      />\n      <br />\n      <button>Send to {contact.email}</button>\n    </section>\n  );\n}\n```\n\n```css\n.chat,\n.contact-list {\n  float: left;\n  margin-bottom: 20px;\n}\nul,\nli {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli button {\n  width: 100px;\n  padding: 10px;\n  margin-right: 10px;\n}\ntextarea {\n  height: 150px;\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n#### メッセージ送信時に入力をクリア {/*clear-the-input-on-sending-a-message*/}\n\n現在、\"Send\" ボタンを押しても何も起こりません。\"Send\" ボタンにイベントハンドラを追加して、以下のことを行ってください。\n\n1. 送信先のメールアドレスとメッセージを含む `alert` を表示する。\n2. メッセージ入力欄をクリアする。\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\nimport { initialState, messengerReducer } from './messengerReducer';\n\nexport default function Messenger() {\n  const [state, dispatch] = useReducer(messengerReducer, initialState);\n  const message = state.message;\n  const contact = contacts.find((c) => c.id === state.selectedId);\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedId={state.selectedId}\n        dispatch={dispatch}\n      />\n      <Chat\n        key={contact.id}\n        message={message}\n        contact={contact}\n        dispatch={dispatch}\n      />\n    </div>\n  );\n}\n\nconst contacts = [\n  {id: 0, name: 'Taylor', email: 'taylor@mail.com'},\n  {id: 1, name: 'Alice', email: 'alice@mail.com'},\n  {id: 2, name: 'Bob', email: 'bob@mail.com'},\n];\n```\n\n```js src/messengerReducer.js\nexport const initialState = {\n  selectedId: 0,\n  message: 'Hello',\n};\n\nexport function messengerReducer(state, action) {\n  switch (action.type) {\n    case 'changed_selection': {\n      return {\n        ...state,\n        selectedId: action.contactId,\n        message: '',\n      };\n    }\n    case 'edited_message': {\n      return {\n        ...state,\n        message: action.message,\n      };\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n```\n\n```js src/ContactList.js\nexport default function ContactList({contacts, selectedId, dispatch}) {\n  return (\n    <section className=\"contact-list\">\n      <ul>\n        {contacts.map((contact) => (\n          <li key={contact.id}>\n            <button\n              onClick={() => {\n                dispatch({\n                  type: 'changed_selection',\n                  contactId: contact.id,\n                });\n              }}>\n              {selectedId === contact.id ? <b>{contact.name}</b> : contact.name}\n            </button>\n          </li>\n        ))}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/Chat.js active\nimport { useState } from 'react';\n\nexport default function Chat({contact, message, dispatch}) {\n  return (\n    <section className=\"chat\">\n      <textarea\n        value={message}\n        placeholder={'Chat to ' + contact.name}\n        onChange={(e) => {\n          dispatch({\n            type: 'edited_message',\n            message: e.target.value,\n          });\n        }}\n      />\n      <br />\n      <button>Send to {contact.email}</button>\n    </section>\n  );\n}\n```\n\n```css\n.chat,\n.contact-list {\n  float: left;\n  margin-bottom: 20px;\n}\nul,\nli {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli button {\n  width: 100px;\n  padding: 10px;\n  margin-right: 10px;\n}\ntextarea {\n  height: 150px;\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n\"Send\" ボタンのイベントハンドラでこれを行う方法は複数あります。1 つのアプローチは、アラートを表示してから、空の `message` で `edited_message` アクションをディスパッチすることです。\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\nimport { initialState, messengerReducer } from './messengerReducer';\n\nexport default function Messenger() {\n  const [state, dispatch] = useReducer(messengerReducer, initialState);\n  const message = state.message;\n  const contact = contacts.find((c) => c.id === state.selectedId);\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedId={state.selectedId}\n        dispatch={dispatch}\n      />\n      <Chat\n        key={contact.id}\n        message={message}\n        contact={contact}\n        dispatch={dispatch}\n      />\n    </div>\n  );\n}\n\nconst contacts = [\n  {id: 0, name: 'Taylor', email: 'taylor@mail.com'},\n  {id: 1, name: 'Alice', email: 'alice@mail.com'},\n  {id: 2, name: 'Bob', email: 'bob@mail.com'},\n];\n```\n\n```js src/messengerReducer.js\nexport const initialState = {\n  selectedId: 0,\n  message: 'Hello',\n};\n\nexport function messengerReducer(state, action) {\n  switch (action.type) {\n    case 'changed_selection': {\n      return {\n        ...state,\n        selectedId: action.contactId,\n        message: '',\n      };\n    }\n    case 'edited_message': {\n      return {\n        ...state,\n        message: action.message,\n      };\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n```\n\n```js src/ContactList.js\nexport default function ContactList({contacts, selectedId, dispatch}) {\n  return (\n    <section className=\"contact-list\">\n      <ul>\n        {contacts.map((contact) => (\n          <li key={contact.id}>\n            <button\n              onClick={() => {\n                dispatch({\n                  type: 'changed_selection',\n                  contactId: contact.id,\n                });\n              }}>\n              {selectedId === contact.id ? <b>{contact.name}</b> : contact.name}\n            </button>\n          </li>\n        ))}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/Chat.js active\nimport { useState } from 'react';\n\nexport default function Chat({contact, message, dispatch}) {\n  return (\n    <section className=\"chat\">\n      <textarea\n        value={message}\n        placeholder={'Chat to ' + contact.name}\n        onChange={(e) => {\n          dispatch({\n            type: 'edited_message',\n            message: e.target.value,\n          });\n        }}\n      />\n      <br />\n      <button\n        onClick={() => {\n          alert(`Sending \"${message}\" to ${contact.email}`);\n          dispatch({\n            type: 'edited_message',\n            message: '',\n          });\n        }}>\n        Send to {contact.email}\n      </button>\n    </section>\n  );\n}\n```\n\n```css\n.chat,\n.contact-list {\n  float: left;\n  margin-bottom: 20px;\n}\nul,\nli {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli button {\n  width: 100px;\n  padding: 10px;\n  margin-right: 10px;\n}\ntextarea {\n  height: 150px;\n}\n```\n\n</Sandpack>\n\nこれは動作し、\"Send\" を押すと入力がクリアされます。\n\nただし、*ユーザーの視点からすると*、メッセージを送るということはフィールドを編集することとは別の操作です。これを反映させるために、代わりに `sent_message` という*新しい*アクションを作成し、リデューサ内で別に処理を行うことができます。\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\nimport { initialState, messengerReducer } from './messengerReducer';\n\nexport default function Messenger() {\n  const [state, dispatch] = useReducer(messengerReducer, initialState);\n  const message = state.message;\n  const contact = contacts.find((c) => c.id === state.selectedId);\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedId={state.selectedId}\n        dispatch={dispatch}\n      />\n      <Chat\n        key={contact.id}\n        message={message}\n        contact={contact}\n        dispatch={dispatch}\n      />\n    </div>\n  );\n}\n\nconst contacts = [\n  {id: 0, name: 'Taylor', email: 'taylor@mail.com'},\n  {id: 1, name: 'Alice', email: 'alice@mail.com'},\n  {id: 2, name: 'Bob', email: 'bob@mail.com'},\n];\n```\n\n```js src/messengerReducer.js active\nexport const initialState = {\n  selectedId: 0,\n  message: 'Hello',\n};\n\nexport function messengerReducer(state, action) {\n  switch (action.type) {\n    case 'changed_selection': {\n      return {\n        ...state,\n        selectedId: action.contactId,\n        message: '',\n      };\n    }\n    case 'edited_message': {\n      return {\n        ...state,\n        message: action.message,\n      };\n    }\n    case 'sent_message': {\n      return {\n        ...state,\n        message: '',\n      };\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n```\n\n```js src/ContactList.js\nexport default function ContactList({contacts, selectedId, dispatch}) {\n  return (\n    <section className=\"contact-list\">\n      <ul>\n        {contacts.map((contact) => (\n          <li key={contact.id}>\n            <button\n              onClick={() => {\n                dispatch({\n                  type: 'changed_selection',\n                  contactId: contact.id,\n                });\n              }}>\n              {selectedId === contact.id ? <b>{contact.name}</b> : contact.name}\n            </button>\n          </li>\n        ))}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/Chat.js active\nimport { useState } from 'react';\n\nexport default function Chat({contact, message, dispatch}) {\n  return (\n    <section className=\"chat\">\n      <textarea\n        value={message}\n        placeholder={'Chat to ' + contact.name}\n        onChange={(e) => {\n          dispatch({\n            type: 'edited_message',\n            message: e.target.value,\n          });\n        }}\n      />\n      <br />\n      <button\n        onClick={() => {\n          alert(`Sending \"${message}\" to ${contact.email}`);\n          dispatch({\n            type: 'sent_message',\n          });\n        }}>\n        Send to {contact.email}\n      </button>\n    </section>\n  );\n}\n```\n\n```css\n.chat,\n.contact-list {\n  float: left;\n  margin-bottom: 20px;\n}\nul,\nli {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli button {\n  width: 100px;\n  padding: 10px;\n  margin-right: 10px;\n}\ntextarea {\n  height: 150px;\n}\n```\n\n</Sandpack>\n\n結果としての振る舞いは同じですが、アクションタイプは理想的には「ユーザが何をしたか」を記述するものであり「state をどのように変更したいか」というものではない、ということに注意してください。これにより、後でより多くの機能を追加することが容易になります。\n\nいずれの解法であっても、リデューサ内に `alert` を**置かない**ことが重要です。リデューサは純関数、つまり、次の state を計算するだけのものであるべきです。メッセージをユーザに表示するといった「何かの実行」を行ってはいけません。メッセージの表示はイベントハンドラで行うべきです。（このような誤りを見つけるために、React は Strict Mode でリデューサを複数回呼び出します。これにより、もしアラートをリデューサに配置してしまった場合、アラートが 2 回表示されます。）\n\n</Solution>\n\n#### タブ切り替え時に入力値を復元 {/*restore-input-values-when-switching-between-tabs*/}\n\nこの例では、異なる送信先間で切り替えるとテキスト入力が常にクリアされます。\n\n```js\ncase 'changed_selection': {\n  return {\n    ...state,\n    selectedId: action.contactId,\n    message: '' // Clears the input\n  };\n```\n\nこれは、複数の送信先間でメッセージの下書きを共有したくないからです。しかし、アプリがそれぞれの連絡先ごとに書きかけのメッセージを「記憶」しておき、連絡先を切り替えたときにそれらを復元する方がより望ましいでしょう。\n\nあなたの仕事は、state の構造を変更して、*連絡先ごとに*別々のメッセージ下書きを記憶するようにすることです。リデューサ、初期 state、およびコンポーネントにいくつか変更を加える必要があります。\n\n<Hint>\n\nstate の構造は次のようにできます。\n\n```js\nexport const initialState = {\n  selectedId: 0,\n  messages: {\n    0: 'Hello, Taylor', // Draft for contactId = 0\n    1: 'Hello, Alice', // Draft for contactId = 1\n  },\n};\n```\n\n`[key]: value` 形式の[計算プロパティ名 (computed property)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names) 構文は、`messages` オブジェクトを更新するのに有用です。\n\n```js\n{\n  ...state.messages,\n  [id]: message\n}\n```\n\n</Hint>\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\nimport { initialState, messengerReducer } from './messengerReducer';\n\nexport default function Messenger() {\n  const [state, dispatch] = useReducer(messengerReducer, initialState);\n  const message = state.message;\n  const contact = contacts.find((c) => c.id === state.selectedId);\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedId={state.selectedId}\n        dispatch={dispatch}\n      />\n      <Chat\n        key={contact.id}\n        message={message}\n        contact={contact}\n        dispatch={dispatch}\n      />\n    </div>\n  );\n}\n\nconst contacts = [\n  {id: 0, name: 'Taylor', email: 'taylor@mail.com'},\n  {id: 1, name: 'Alice', email: 'alice@mail.com'},\n  {id: 2, name: 'Bob', email: 'bob@mail.com'},\n];\n```\n\n```js src/messengerReducer.js\nexport const initialState = {\n  selectedId: 0,\n  message: 'Hello',\n};\n\nexport function messengerReducer(state, action) {\n  switch (action.type) {\n    case 'changed_selection': {\n      return {\n        ...state,\n        selectedId: action.contactId,\n        message: '',\n      };\n    }\n    case 'edited_message': {\n      return {\n        ...state,\n        message: action.message,\n      };\n    }\n    case 'sent_message': {\n      return {\n        ...state,\n        message: '',\n      };\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n```\n\n```js src/ContactList.js\nexport default function ContactList({contacts, selectedId, dispatch}) {\n  return (\n    <section className=\"contact-list\">\n      <ul>\n        {contacts.map((contact) => (\n          <li key={contact.id}>\n            <button\n              onClick={() => {\n                dispatch({\n                  type: 'changed_selection',\n                  contactId: contact.id,\n                });\n              }}>\n              {selectedId === contact.id ? <b>{contact.name}</b> : contact.name}\n            </button>\n          </li>\n        ))}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/Chat.js\nimport { useState } from 'react';\n\nexport default function Chat({contact, message, dispatch}) {\n  return (\n    <section className=\"chat\">\n      <textarea\n        value={message}\n        placeholder={'Chat to ' + contact.name}\n        onChange={(e) => {\n          dispatch({\n            type: 'edited_message',\n            message: e.target.value,\n          });\n        }}\n      />\n      <br />\n      <button\n        onClick={() => {\n          alert(`Sending \"${message}\" to ${contact.email}`);\n          dispatch({\n            type: 'sent_message',\n          });\n        }}>\n        Send to {contact.email}\n      </button>\n    </section>\n  );\n}\n```\n\n```css\n.chat,\n.contact-list {\n  float: left;\n  margin-bottom: 20px;\n}\nul,\nli {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli button {\n  width: 100px;\n  padding: 10px;\n  margin-right: 10px;\n}\ntextarea {\n  height: 150px;\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n連絡先ごとに別々のメッセージ下書きを保存・更新するようにリデューサを書き換える必要があります。\n\n```js\n// When the input is edited\ncase 'edited_message': {\n  return {\n    // Keep other state like selection\n    ...state,\n    messages: {\n      // Keep messages for other contacts\n      ...state.messages,\n      // But change the selected contact's message\n      [state.selectedId]: action.message\n    }\n  };\n}\n```\n\nまた、`Messenger` コンポーネントを変更して、現在選択されている連絡先のメッセージを読み取るようにします。\n\n```js\nconst message = state.messages[state.selectedId];\n```\n\n答えの全体像は以下のとおりです。\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\nimport { initialState, messengerReducer } from './messengerReducer';\n\nexport default function Messenger() {\n  const [state, dispatch] = useReducer(messengerReducer, initialState);\n  const message = state.messages[state.selectedId];\n  const contact = contacts.find((c) => c.id === state.selectedId);\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedId={state.selectedId}\n        dispatch={dispatch}\n      />\n      <Chat\n        key={contact.id}\n        message={message}\n        contact={contact}\n        dispatch={dispatch}\n      />\n    </div>\n  );\n}\n\nconst contacts = [\n  {id: 0, name: 'Taylor', email: 'taylor@mail.com'},\n  {id: 1, name: 'Alice', email: 'alice@mail.com'},\n  {id: 2, name: 'Bob', email: 'bob@mail.com'},\n];\n```\n\n```js src/messengerReducer.js\nexport const initialState = {\n  selectedId: 0,\n  messages: {\n    0: 'Hello, Taylor',\n    1: 'Hello, Alice',\n    2: 'Hello, Bob',\n  },\n};\n\nexport function messengerReducer(state, action) {\n  switch (action.type) {\n    case 'changed_selection': {\n      return {\n        ...state,\n        selectedId: action.contactId,\n      };\n    }\n    case 'edited_message': {\n      return {\n        ...state,\n        messages: {\n          ...state.messages,\n          [state.selectedId]: action.message,\n        },\n      };\n    }\n    case 'sent_message': {\n      return {\n        ...state,\n        messages: {\n          ...state.messages,\n          [state.selectedId]: '',\n        },\n      };\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n```\n\n```js src/ContactList.js\nexport default function ContactList({contacts, selectedId, dispatch}) {\n  return (\n    <section className=\"contact-list\">\n      <ul>\n        {contacts.map((contact) => (\n          <li key={contact.id}>\n            <button\n              onClick={() => {\n                dispatch({\n                  type: 'changed_selection',\n                  contactId: contact.id,\n                });\n              }}>\n              {selectedId === contact.id ? <b>{contact.name}</b> : contact.name}\n            </button>\n          </li>\n        ))}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/Chat.js\nimport { useState } from 'react';\n\nexport default function Chat({contact, message, dispatch}) {\n  return (\n    <section className=\"chat\">\n      <textarea\n        value={message}\n        placeholder={'Chat to ' + contact.name}\n        onChange={(e) => {\n          dispatch({\n            type: 'edited_message',\n            message: e.target.value,\n          });\n        }}\n      />\n      <br />\n      <button\n        onClick={() => {\n          alert(`Sending \"${message}\" to ${contact.email}`);\n          dispatch({\n            type: 'sent_message',\n          });\n        }}>\n        Send to {contact.email}\n      </button>\n    </section>\n  );\n}\n```\n\n```css\n.chat,\n.contact-list {\n  float: left;\n  margin-bottom: 20px;\n}\nul,\nli {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli button {\n  width: 100px;\n  padding: 10px;\n  margin-right: 10px;\n}\ntextarea {\n  height: 150px;\n}\n```\n\n</Sandpack>\n\n特筆すべきは、この異なる動作を実装するために、イベントハンドラを変更する必要がなかったということです。リデューサがない場合、state を更新するすべてのイベントハンドラを変更する必要があります。\n\n</Solution>\n\n#### `useReducer` をゼロから実装 {/*implement-usereducer-from-scratch*/}\n\nこれまでの例では、React から `useReducer` フックをインポートしていました。今回は、*`useReducer` フック自体を実装*してください！ 以下に開始地点となるコードが書かれています。コードは 10 行以内で完結するはずです。\n\n変更を確認するために、入力欄に入力してみるか、連絡先を選択してみてください。\n\n<Hint>\n\n以下が実装の概略をもう少しだけ詳細にしたものです。\n\n```js\nexport function useReducer(reducer, initialState) {\n  const [state, setState] = useState(initialState);\n\n  function dispatch(action) {\n    // ???\n  }\n\n  return [state, dispatch];\n}\n```\n\nリデューサ関数は、現在の state とアクションオブジェクトという 2 つの引数を受け取り、次の state を返すものであることを思い出してください。`dispatch` の実装は何をすべきでしょうか？\n\n</Hint>\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from './MyReact.js';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\nimport { initialState, messengerReducer } from './messengerReducer';\n\nexport default function Messenger() {\n  const [state, dispatch] = useReducer(messengerReducer, initialState);\n  const message = state.messages[state.selectedId];\n  const contact = contacts.find((c) => c.id === state.selectedId);\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedId={state.selectedId}\n        dispatch={dispatch}\n      />\n      <Chat\n        key={contact.id}\n        message={message}\n        contact={contact}\n        dispatch={dispatch}\n      />\n    </div>\n  );\n}\n\nconst contacts = [\n  {id: 0, name: 'Taylor', email: 'taylor@mail.com'},\n  {id: 1, name: 'Alice', email: 'alice@mail.com'},\n  {id: 2, name: 'Bob', email: 'bob@mail.com'},\n];\n```\n\n```js src/messengerReducer.js\nexport const initialState = {\n  selectedId: 0,\n  messages: {\n    0: 'Hello, Taylor',\n    1: 'Hello, Alice',\n    2: 'Hello, Bob',\n  },\n};\n\nexport function messengerReducer(state, action) {\n  switch (action.type) {\n    case 'changed_selection': {\n      return {\n        ...state,\n        selectedId: action.contactId,\n      };\n    }\n    case 'edited_message': {\n      return {\n        ...state,\n        messages: {\n          ...state.messages,\n          [state.selectedId]: action.message,\n        },\n      };\n    }\n    case 'sent_message': {\n      return {\n        ...state,\n        messages: {\n          ...state.messages,\n          [state.selectedId]: '',\n        },\n      };\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n```\n\n```js src/MyReact.js active\nimport { useState } from 'react';\n\nexport function useReducer(reducer, initialState) {\n  const [state, setState] = useState(initialState);\n\n  // ???\n\n  return [state, dispatch];\n}\n```\n\n```js src/ContactList.js hidden\nexport default function ContactList({contacts, selectedId, dispatch}) {\n  return (\n    <section className=\"contact-list\">\n      <ul>\n        {contacts.map((contact) => (\n          <li key={contact.id}>\n            <button\n              onClick={() => {\n                dispatch({\n                  type: 'changed_selection',\n                  contactId: contact.id,\n                });\n              }}>\n              {selectedId === contact.id ? <b>{contact.name}</b> : contact.name}\n            </button>\n          </li>\n        ))}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/Chat.js hidden\nimport { useState } from 'react';\n\nexport default function Chat({contact, message, dispatch}) {\n  return (\n    <section className=\"chat\">\n      <textarea\n        value={message}\n        placeholder={'Chat to ' + contact.name}\n        onChange={(e) => {\n          dispatch({\n            type: 'edited_message',\n            message: e.target.value,\n          });\n        }}\n      />\n      <br />\n      <button\n        onClick={() => {\n          alert(`Sending \"${message}\" to ${contact.email}`);\n          dispatch({\n            type: 'sent_message',\n          });\n        }}>\n        Send to {contact.email}\n      </button>\n    </section>\n  );\n}\n```\n\n```css\n.chat,\n.contact-list {\n  float: left;\n  margin-bottom: 20px;\n}\nul,\nli {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli button {\n  width: 100px;\n  padding: 10px;\n  margin-right: 10px;\n}\ntextarea {\n  height: 150px;\n}\n```\n\n</Sandpack>\n\n<Solution>\n\nアクションがディスパッチされると、現在の state とアクションがリデューサに渡され、結果が次の state として保存されます。コードでは以下のようになります：\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from './MyReact.js';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\nimport { initialState, messengerReducer } from './messengerReducer';\n\nexport default function Messenger() {\n  const [state, dispatch] = useReducer(messengerReducer, initialState);\n  const message = state.messages[state.selectedId];\n  const contact = contacts.find((c) => c.id === state.selectedId);\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedId={state.selectedId}\n        dispatch={dispatch}\n      />\n      <Chat\n        key={contact.id}\n        message={message}\n        contact={contact}\n        dispatch={dispatch}\n      />\n    </div>\n  );\n}\n\nconst contacts = [\n  {id: 0, name: 'Taylor', email: 'taylor@mail.com'},\n  {id: 1, name: 'Alice', email: 'alice@mail.com'},\n  {id: 2, name: 'Bob', email: 'bob@mail.com'},\n];\n```\n\n```js src/messengerReducer.js\nexport const initialState = {\n  selectedId: 0,\n  messages: {\n    0: 'Hello, Taylor',\n    1: 'Hello, Alice',\n    2: 'Hello, Bob',\n  },\n};\n\nexport function messengerReducer(state, action) {\n  switch (action.type) {\n    case 'changed_selection': {\n      return {\n        ...state,\n        selectedId: action.contactId,\n      };\n    }\n    case 'edited_message': {\n      return {\n        ...state,\n        messages: {\n          ...state.messages,\n          [state.selectedId]: action.message,\n        },\n      };\n    }\n    case 'sent_message': {\n      return {\n        ...state,\n        messages: {\n          ...state.messages,\n          [state.selectedId]: '',\n        },\n      };\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n```\n\n```js src/MyReact.js active\nimport { useState } from 'react';\n\nexport function useReducer(reducer, initialState) {\n  const [state, setState] = useState(initialState);\n\n  function dispatch(action) {\n    const nextState = reducer(state, action);\n    setState(nextState);\n  }\n\n  return [state, dispatch];\n}\n```\n\n```js src/ContactList.js hidden\nexport default function ContactList({contacts, selectedId, dispatch}) {\n  return (\n    <section className=\"contact-list\">\n      <ul>\n        {contacts.map((contact) => (\n          <li key={contact.id}>\n            <button\n              onClick={() => {\n                dispatch({\n                  type: 'changed_selection',\n                  contactId: contact.id,\n                });\n              }}>\n              {selectedId === contact.id ? <b>{contact.name}</b> : contact.name}\n            </button>\n          </li>\n        ))}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/Chat.js hidden\nimport { useState } from 'react';\n\nexport default function Chat({contact, message, dispatch}) {\n  return (\n    <section className=\"chat\">\n      <textarea\n        value={message}\n        placeholder={'Chat to ' + contact.name}\n        onChange={(e) => {\n          dispatch({\n            type: 'edited_message',\n            message: e.target.value,\n          });\n        }}\n      />\n      <br />\n      <button\n        onClick={() => {\n          alert(`Sending \"${message}\" to ${contact.email}`);\n          dispatch({\n            type: 'sent_message',\n          });\n        }}>\n        Send to {contact.email}\n      </button>\n    </section>\n  );\n}\n```\n\n```css\n.chat,\n.contact-list {\n  float: left;\n  margin-bottom: 20px;\n}\nul,\nli {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli button {\n  width: 100px;\n  padding: 10px;\n  margin-right: 10px;\n}\ntextarea {\n  height: 150px;\n}\n```\n\n</Sandpack>\n\nこれでほとんどの場合問題ありませんが、より正確な実装は以下のようになります。\n\n```js\nfunction dispatch(action) {\n  setState((s) => reducer(s, action));\n}\n```\n\nこれは、ディスパッチされたアクションは次のレンダーまで[更新用関数と同様に](/learn/queueing-a-series-of-state-updates)キューに入れられるためです。\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/importing-and-exporting-components.md",
    "content": "---\ntitle: コンポーネントのインポートとエクスポート\n---\n\n<Intro>\n\nコンポーネントの魅力は再利用のしやすさにあります。他のコンポーネントを組み合わせて新しいコンポーネントを作ることができるのです。しかし、コンポーネントのネストが増えてくると、それらを別のファイルに分割したくなってきます。これにより、欲しいファイルを簡単に見つけ出し、より多くの場所でコンポーネントを再利用できるようになります。\n\n</Intro>\n\n<YouWillLearn>\n\n* ルートコンポーネントファイルとは何か\n* コンポーネントのインポート・エクスポートの方法\n* デフォルトインポート/エクスポートと名前付きインポート/エクスポートの使い分け\n* 1 つのファイルから複数のコンポーネントをインポート・エクスポートする方法\n* コンポーネントを複数のファイルに分割する方法\n\n</YouWillLearn>\n\n## ルートコンポーネントファイル {/*the-root-component-file*/}\n\n[初めてのコンポーネント](/learn/your-first-component)では、`Profile` コンポーネントと、それをレンダーする `Gallery` コンポーネントを作成しました。\n\n<Sandpack>\n\n```js\nfunction Profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/MK3eW3As.jpg\"\n      alt=\"Katherine Johnson\"\n    />\n  );\n}\n\nexport default function Gallery() {\n  return (\n    <section>\n      <h1>Amazing scientists</h1>\n      <Profile />\n      <Profile />\n      <Profile />\n    </section>\n  );\n}\n```\n\n```css\nimg { margin: 0 10px 10px 0; height: 90px; }\n```\n\n</Sandpack>\n\nこれらのコンポーネントは今のところ、**ルート (root) コンポーネントファイル**（この例では `App.js` という名前）に置かれています。セットアップによっては、ルートコンポーネントは別のファイルに存在するかもしれません。Next.js のようなファイルベースのルーティングがあるフレームワークを使っている場合、ルートコンポーネントはページごとに異なるものになります。\n\n## コンポーネントのエクスポートとインポート {/*exporting-and-importing-a-component*/}\n\nもし将来ランディングページを変更して科学書のリストを表示したくなった場合はどうでしょうか。あるいはプロフィールを別の場所に表示したくなった場合は？ `Gallery` や `Profile` はルートコンポーネントファイル以外の場所に置きたくなるでしょう。これによりコンポーネントはよりモジュール化され、他のファイルから再利用可能になります。コンポーネントの移動は以下の 3 ステップで行えます。\n\n1. コンポーネントを置くための新しい JS ファイルを**作成する**。\n2. [デフォルト](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/export#using_the_default_export)エクスポートあるいは[名前付き](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/export#using_named_exports)エクスポートのいずれかを使い、関数コンポーネントをそのファイルから**エクスポートする**。\n3. そのコンポーネントを、利用する側のファイルで**インポート**する。[デフォルト](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import#importing_defaults)エクスポートされたのか[名前付き](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import#import_a_single_export_from_a_module)エクスポートされたのかに応じて、対応するインポート手法を使う。\n\n以下の例では、`Profile` と `Gallery` の両方が、`App.js` から `Gallery.js` という新しいファイルに移動しています。`App.js` を書きかえて、`Gallery.js` から `Gallery` をインポートするようにできます。\n\n<Sandpack>\n\n```js src/App.js\nimport Gallery from './Gallery.js';\n\nexport default function App() {\n  return (\n    <Gallery />\n  );\n}\n```\n\n```js src/Gallery.js\nfunction Profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/QIrZWGIs.jpg\"\n      alt=\"Alan L. Hart\"\n    />\n  );\n}\n\nexport default function Gallery() {\n  return (\n    <section>\n      <h1>Amazing scientists</h1>\n      <Profile />\n      <Profile />\n      <Profile />\n    </section>\n  );\n}\n```\n\n```css\nimg { margin: 0 10px 10px 0; height: 90px; }\n```\n\n</Sandpack>\n\n元の例がどのようにして 2 つのコンポーネントファイルに分割されたか確認しましょう。\n\n1. `Gallery.js` は：\n     - `Profile` を定義しているが、これは同一ファイル内でしか使われてないのでエクスポートしていない。\n     - **デフォルトエクスポート**として `Gallery` コンポーネントをエクスポートしている。\n2. `App.js` は：\n     - `Gallery.js` から `Gallery` を**デフォルトインポート**している。\n     - ルートの `App` コンポーネントを**デフォルトエクスポート**している。\n\n\n<Note>\n\n`.js` というファイル拡張子が省略された、以下のようなファイルを見ることがあるかもしれません。\n\n```js \nimport Gallery from './Gallery';\n```\n\nReact では `'./Gallery.js'` でも `'./Gallery'` でも動作しますが、前者の方が[ネイティブ ES モジュール](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Modules)の動作により近い方法です。\n\n</Note>\n\n<DeepDive>\n\n#### デフォルトエクスポート vs 名前付きエクスポート {/*default-vs-named-exports*/}\n\nJavaScript には値をエクスポートする主な方法が 2 つあります。デフォルトエクスポートと名前付きエクスポートです。これまで、我々の例ではデフォルトエクスポートのみを使ってきました。しかし同じファイルで両方を使うことも、あるいはどちらか片方だけを使うことも可能です。**ファイルには*デフォルト*エクスポートは 1 つまでしか置けませんが、*名前付き*エクスポートは好きなだけ置くことができます**。\n\n![デフォルトエクスポートと名前付きエクスポート](/images/docs/illustrations/i_import-export.svg)\n\nどのようにコンポーネントをエクスポートするかによって、それをどのようにインポートするのかが決まります。デフォルトエクスポートされたものを名前付きエクスポートのようにインポートしようとするとエラーになります！ 以下の表が参考になるでしょう。\n\n| 構文             | Export 文                                   | Import 文                                        |\n| -----------      | -----------                                | -----------                               |\n| Default  | `export default function Button() {}` | `import Button from './Button.js';`     |\n| Named    | `export function Button() {}`         | `import { Button } from './Button.js';` |\n\n*デフォルト*インポートを書く場合、`import` の後には好きな名前を書くことができます。例えば `import Banana from './Button.js'` と書いたとしても、デフォルトエクスポートされている同じ値を得ることができます。一方で、名前付きエクスポートでは、エクスポート側とインポート側で名前が合致していなければなりません。だからこそ*名前付き*エクスポートと呼ばれるわけですね。\n\n**ファイルがコンポーネントを 1 つだけエクスポートする場合はデフォルトエクスポートが、複数のコンポーネントや値をエクスポートする場合は名前付きエクスポートがよく使われます**。どちらのコーディングスタイルが好みの場合でも、コンポーネントやそれが入るファイルには、常に意味の通った名前を付けるようにしてください。`export default () => {}` のような名前のないコンポーネントは、デバッグが困難になるため推奨されません。\n\n</DeepDive>\n\n## 同じファイルから複数のコンポーネントをエクスポート・インポートする {/*exporting-and-importing-multiple-components-from-the-same-file*/}\n\nギャラリではなく、`Profile` を 1 つだけを表示したい場合はどうでしょう。`Profile` コンポーネントもエクスポートすればいいのです。しかし、`Gallery.js` にはすでにデフォルトエクスポートがあり、デフォルトエクスポートは 2 つ以上存在できません。デフォルトエクスポートを持つ新しいファイルを作成するか、または `Profile` 用の*名前付き*エクスポートを追加することができます。**1 つのファイルはデフォルトエクスポートを 1 つしか持つことができませんが、名前付きエクスポートはたくさんあっても構いません！**\n\n<Note>\n\nデフォルトエクスポートと名前付きエクスポートとで混乱する可能性を減らすために、チームによっては 1 つのスタイル（デフォルトまたは名前付き）に統一したり、1 つのファイルでこれらを混在させないようにしています。これは好みの問題です。自分たちに合った方法を選んでください！\n\n</Note>\n\nまずは `Gallery.js` の `Profile` を名前付きで**エクスポート**します（`default` キーワードは付けない）。\n\n```js\nexport function Profile() {\n  // ...\n}\n```\n\nそして、`Gallery.js` の `Profile` を `App.js` に名前付きで**インポート**します（中括弧を使う）。\n\n```js\nimport { Profile } from './Gallery.js';\n```\n\n最後に `App` コンポーネントで `<Profile />` を**レンダー**します。\n\n```js\nexport default function App() {\n  return <Profile />;\n}\n```\n\nこれで `Gallery.js` には、デフォルトの `Gallery` というエクスポートと、名前付きの `Profile` というエクスポートの 2 つが含まれるようになりました。`App.js` はその両方をインポートしています。この例の `<Profile />` を `<Gallery />` と書きかえたり、元に戻したりしてみてください。\n\n<Sandpack>\n\n```js src/App.js\nimport Gallery from './Gallery.js';\nimport { Profile } from './Gallery.js';\n\nexport default function App() {\n  return (\n    <Profile />\n  );\n}\n```\n\n```js src/Gallery.js\nexport function Profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/QIrZWGIs.jpg\"\n      alt=\"Alan L. Hart\"\n    />\n  );\n}\n\nexport default function Gallery() {\n  return (\n    <section>\n      <h1>Amazing scientists</h1>\n      <Profile />\n      <Profile />\n      <Profile />\n    </section>\n  );\n}\n```\n\n```css\nimg { margin: 0 10px 10px 0; height: 90px; }\n```\n\n</Sandpack>\n\nこれで、デフォルトと名前付きのエクスポートが混在するようになりました。\n\n* `Gallery.js` は：\n  - `Profile` コンポーネントを **`Profile` という名前付きでエクスポートしている**。\n  - `Gallery` コンポーネントを**デフォルトエクスポート**している。\n* `App.js` は：\n  - `Profile` コンポーネントを `Gallery.js` から **`Profile` という名前付きでインポートしている**。\n  - `Gallery` コンポーネントを `Gallery.js` から**デフォルトインポート**している。\n  - ルートの `App` コンポーネントを**デフォルトエクスポート**している。\n\n<Recap>\n\nこのページでは以下のことを学びました。\n\n* ルートコンポーネントファイルとは何か\n* コンポーネントのインポート・エクスポートの方法\n* デフォルトインポート/エクスポートと名前付きインポート/エクスポートの使い分け\n* 1 つのファイルから複数のコンポーネントをインポート・エクスポートする方法\n\n</Recap>\n\n\n\n<Challenges>\n\n#### コンポーネントファイルをさらに分割する {/*split-the-components-further*/}\n\n現在のところ `Gallery.js` は `Profile` と `Gallery` の両方をエクスポートしていますが、これはちょっと混乱の原因になりそうです。\n\n`Profile` コンポーネントを `Profile.js` という別ファイルに移動し、その後で `App` コンポーネントも変更して `<Profile />` と `<Gallery />` を並べてレンダーするようにしてください。\n\n`Profile` をエクスポートするのにデフォルトと名前付きのどちらの手法を使っても構いませんが、`App.js` と `Gallery.js` の両方で対応するインポート構文を使うようにしましょう！ 上記の詳細セクションで挙げたこちらの表を参照しても構いません：\n\n| 構文             | Export 文                                   | Import 文                                        |\n| -----------      | -----------                                | -----------                               |\n| Default  | `export default function Button() {}` | `import Button from './Button.js';`     |\n| Named    | `export function Button() {}`         | `import { Button } from './Button.js';` |\n\n<Hint>\n\nコンポーネントを使っている場所ではインポートするのを忘れないようにしましょう。`Gallery` も `Profile` を使っていますよね。\n\n</Hint>\n\n<Sandpack>\n\n```js src/App.js\nimport Gallery from './Gallery.js';\nimport { Profile } from './Gallery.js';\n\nexport default function App() {\n  return (\n    <div>\n      <Profile />\n    </div>\n  );\n}\n```\n\n```js src/Gallery.js active\n// Move me to Profile.js!\nexport function Profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/QIrZWGIs.jpg\"\n      alt=\"Alan L. Hart\"\n    />\n  );\n}\n\nexport default function Gallery() {\n  return (\n    <section>\n      <h1>Amazing scientists</h1>\n      <Profile />\n      <Profile />\n      <Profile />\n    </section>\n  );\n}\n```\n\n```js src/Profile.js\n```\n\n```css\nimg { margin: 0 10px 10px 0; height: 90px; }\n```\n\n</Sandpack>\n\n片方のエクスポート構文でうまく動かせたら、もう片方の構文でも動くようにしてみましょう。\n\n<Solution>\n\n名前付きエクスポートを使った解法：\n\n<Sandpack>\n\n```js src/App.js\nimport Gallery from './Gallery.js';\nimport { Profile } from './Profile.js';\n\nexport default function App() {\n  return (\n    <div>\n      <Profile />\n      <Gallery />\n    </div>\n  );\n}\n```\n\n```js src/Gallery.js\nimport { Profile } from './Profile.js';\n\nexport default function Gallery() {\n  return (\n    <section>\n      <h1>Amazing scientists</h1>\n      <Profile />\n      <Profile />\n      <Profile />\n    </section>\n  );\n}\n```\n\n```js src/Profile.js\nexport function Profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/QIrZWGIs.jpg\"\n      alt=\"Alan L. Hart\"\n    />\n  );\n}\n```\n\n```css\nimg { margin: 0 10px 10px 0; height: 90px; }\n```\n\n</Sandpack>\n\nデフォルトエクスポートを使った解法：\n\n<Sandpack>\n\n```js src/App.js\nimport Gallery from './Gallery.js';\nimport Profile from './Profile.js';\n\nexport default function App() {\n  return (\n    <div>\n      <Profile />\n      <Gallery />\n    </div>\n  );\n}\n```\n\n```js src/Gallery.js\nimport Profile from './Profile.js';\n\nexport default function Gallery() {\n  return (\n    <section>\n      <h1>Amazing scientists</h1>\n      <Profile />\n      <Profile />\n      <Profile />\n    </section>\n  );\n}\n```\n\n```js src/Profile.js\nexport default function Profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/QIrZWGIs.jpg\"\n      alt=\"Alan L. Hart\"\n    />\n  );\n}\n```\n\n```css\nimg { margin: 0 10px 10px 0; height: 90px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/index.md",
    "content": "---\ntitle: クイックスタート\n---\n\n<Intro>\n\nReact ドキュメントへようこそ！ このページでは、日々の開発で使用する React のコンセプトのうち 80％ の部分を紹介します。\n\n</Intro>\n\n<YouWillLearn>\n\n- コンポーネントの作成とネスト\n- マークアップとスタイルの追加\n- データの表示\n- 条件分岐とリストのレンダー\n- イベントへの応答と画面の更新\n- コンポーネント間でのデータの共有\n\n</YouWillLearn>\n\n## コンポーネントの作成とネスト {/*components*/}\n\nReact アプリは*コンポーネント*で構成されています。コンポーネントとは、独自のロジックと外見を持つ UI（ユーザインターフェース）の部品のことです。コンポーネントは、ボタンのような小さなものである場合も、ページ全体を表す大きなものである場合もあります。\n\nReact におけるコンポーネントとは、マークアップを返す JavaScript 関数です。\n\n```js\nfunction MyButton() {\n  return (\n    <button>I'm a button</button>\n  );\n}\n```\n\n`MyButton` を宣言したら、別のコンポーネントにネストできます。\n\n```js {5}\nexport default function MyApp() {\n  return (\n    <div>\n      <h1>Welcome to my app</h1>\n      <MyButton />\n    </div>\n  );\n}\n```\n\n`<MyButton />` が大文字で始まっていることに注意してください。こうすることで、React のコンポーネントであるということを示しています。React のコンポーネント名は常に大文字で始める必要があり、HTML タグは小文字でなければなりません。\n\n結果を見てみましょう。\n\n<Sandpack>\n\n```js\nfunction MyButton() {\n  return (\n    <button>\n      I'm a button\n    </button>\n  );\n}\n\nexport default function MyApp() {\n  return (\n    <div>\n      <h1>Welcome to my app</h1>\n      <MyButton />\n    </div>\n  );\n}\n```\n\n</Sandpack>\n\n`export default` キーワードは、ファイル内のメインコンポーネントを指定しています。このような JavaScript の構文に関して分からない部分があれば、[MDN](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export) や [javascript.info](https://javascript.info/import-export) に素晴らしいリファレンスがあります。\n\n## JSX でマークアップを書く {/*writing-markup-with-jsx*/}\n\n上で見たマークアップ構文は、*JSX* と呼ばれるものです。使用は任意ですが、その便利さゆえにほとんどの React プロジェクトでは JSX が使用されています。[ローカル開発におすすめのツール](/learn/installation)は、すべて JSX に対応しています。\n\nJSX は HTML より構文が厳格です。`<br />` のようにタグは閉じる必要があります。また、コンポーネントは複数の JSX タグを return することはできません。`<div>...</div>` や空の `<>...</>` ラッパのような共通の親要素で囲む必要があります。\n\n```js {3,6}\nfunction AboutPage() {\n  return (\n    <>\n      <h1>About</h1>\n      <p>Hello there.<br />How do you do?</p>\n    </>\n  );\n}\n```\n\nJSX に変換しないといけない HTML がたくさんある場合は、[オンラインコンバータ](https://transform.tools/html-to-jsx)を使うことができます。\n\n## スタイルの追加 {/*adding-styles*/}\n\nReact では、CSS クラスを `className` で指定します。HTML の [`class`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class) 属性と同じ方法で動作します。\n\n```js\n<img className=\"avatar\" />\n```\n\nそして、別の CSS ファイルに対応する CSS ルールを記述します：\n\n```css\n/* In your CSS */\n.avatar {\n  border-radius: 50%;\n}\n```\n\nReact には CSS ファイルの追加方法に関する規則はありません。最も単純なケースでは、HTML に [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) タグを追加します。ビルドツールやフレームワークを使っている場合は、そちらのドキュメントを参照して、プロジェクトに CSS ファイルを追加する方法を確認してください。\n\n## データの表示 {/*displaying-data*/}\n\nJSX を使うことで、JavaScript 内にマークアップを入れることができます。波括弧を使うことで、逆に JSX の中から JavaScript に「戻る」ことができ、コード内の変数を埋め込んでユーザに表示することができます。たとえば、以下は `user.name` を表示します：\n\n```js {3}\nreturn (\n  <h1>\n    {user.name}\n  </h1>\n);\n```\n\nJSX の属性 (attribute) の部分から JavaScript に「戻る」こともでき、その場合引用符の*代わりに*波括弧を使う必要があります。例えば、`className=\"avatar\"` は CSS クラスとして `\"avatar\"` 文字列を渡すものですが、`src={user.imageUrl}` は JavaScript の `user.imageUrl` 変数の値を読み込み、その値を `src` 属性として渡します：\n\n```js {3,4}\nreturn (\n  <img\n    className=\"avatar\"\n    src={user.imageUrl}\n  />\n);\n```\n\nJSX の波括弧の中にもっと複雑な式を入れることもできます。例えば、[文字列の連結](https://javascript.info/operators#string-concatenation-with-binary)ができます：\n\n<Sandpack>\n\n```js\nconst user = {\n  name: 'Hedy Lamarr',\n  imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',\n  imageSize: 90,\n};\n\nexport default function Profile() {\n  return (\n    <>\n      <h1>{user.name}</h1>\n      <img\n        className=\"avatar\"\n        src={user.imageUrl}\n        alt={'Photo of ' + user.name}\n        style={{\n          width: user.imageSize,\n          height: user.imageSize\n        }}\n      />\n    </>\n  );\n}\n```\n\n```css\n.avatar {\n  border-radius: 50%;\n}\n\n.large {\n  border: 4px solid gold;\n}\n```\n\n</Sandpack>\n\n上記の例では、`style={{}}` は特別な構文ではなく、`style={ }` という JSX の波括弧内にある通常の `{}` オブジェクトです。スタイルが JavaScript 変数に依存する場合は、`style` 属性を使うことができます。\n\n## 条件付きレンダー {/*conditional-rendering*/}\n\nReact には、条件分岐を書くための特別な構文は存在しません。代わりに、通常の JavaScript コードを書くときに使うのと同じ手法を使います。例えば、[`if`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else) ステートメントを使って条件付きで JSX を含めることができます：\n\n```js\nlet content;\nif (isLoggedIn) {\n  content = <AdminPanel />;\n} else {\n  content = <LoginForm />;\n}\nreturn (\n  <div>\n    {content}\n  </div>\n);\n```\n\nコンパクトなコードをお望みの場合は、[条件 `?` 演算子](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator)を使用できます。`if` とは異なり、JSX の中で動作します。\n\n```js\n<div>\n  {isLoggedIn ? (\n    <AdminPanel />\n  ) : (\n    <LoginForm />\n  )}\n</div>\n```\n\n`else` 側の分岐が不要な場合は、短い[論理 `&&` 構文](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND#short-circuit_evaluation)を使用することもできます。\n\n```js\n<div>\n  {isLoggedIn && <AdminPanel />}\n</div>\n```\n\nこれらのアプローチはすべて、属性を条件付きで指定する場合にも機能します。このような JavaScript 構文の一部に慣れていないという場合、最初は常に `if...else` を使用することにしても構いません。\n\n## リストのレンダー {/*rendering-lists*/}\n\nコンポーネントのリストをレンダーする場合は、[`for` ループ](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for) や [配列の `map()` 関数](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) といった JavaScript の機能を使って行います。\n\n例えばこのような商品の配列があるとします：\n\n```js\nconst products = [\n  { title: 'Cabbage', id: 1 },\n  { title: 'Garlic', id: 2 },\n  { title: 'Apple', id: 3 },\n];\n```\n\nコンポーネント内で、`map()` 関数を使って商品の配列を `<li>` 要素の配列に変換します：\n\n```js\nconst listItems = products.map(product =>\n  <li key={product.id}>\n    {product.title}\n  </li>\n);\n\nreturn (\n  <ul>{listItems}</ul>\n);\n```\n\n`<li>` に `key` 属性があることに注意してください。リスト内の各項目には、兄弟の中でそれを一意に識別するための文字列または数値を渡す必要があります。通常、key はデータから来るはずで、データベース上の ID などが該当します。React は、後でアイテムを挿入、削除、並べ替えることがあった際に、何が起こったかを key を使って把握します。\n\n<Sandpack>\n\n```js\nconst products = [\n  { title: 'Cabbage', isFruit: false, id: 1 },\n  { title: 'Garlic', isFruit: false, id: 2 },\n  { title: 'Apple', isFruit: true, id: 3 },\n];\n\nexport default function ShoppingList() {\n  const listItems = products.map(product =>\n    <li\n      key={product.id}\n      style={{\n        color: product.isFruit ? 'magenta' : 'darkgreen'\n      }}\n    >\n      {product.title}\n    </li>\n  );\n\n  return (\n    <ul>{listItems}</ul>\n  );\n}\n```\n\n</Sandpack>\n\n## イベントに応答する {/*responding-to-events*/}\n\nコンポーネントの中で*イベントハンドラ*関数を宣言することで、イベントに応答できます：\n\n```js {2-4,7}\nfunction MyButton() {\n  function handleClick() {\n    alert('You clicked me!');\n  }\n\n  return (\n    <button onClick={handleClick}>\n      Click me\n    </button>\n  );\n}\n```\n\n`onClick={handleClick}` の末尾に括弧がないことに注意してください！ そこでイベントハンドラ関数を*呼び出す*わけではありません。*渡すだけ*です。ユーザがボタンをクリックしたときに、React がイベントハンドラを呼び出します。\n\n## 画面の更新 {/*updating-the-screen*/}\n\nしばしば、コンポーネントに情報を「記憶」させて表示したいことがあります。例えば、ボタンがクリックされた回数を数えて覚えておきたい場合です。これを行うには、コンポーネントに *state* を追加します。\n\nまず、React から [`useState`](/reference/react/useState) をインポートします。\n\n```js\nimport { useState } from 'react';\n```\n\nこれで、コンポーネント内に *state 変数*を宣言できます：\n\n```js\nfunction MyButton() {\n  const [count, setCount] = useState(0);\n  // ...\n```\n\n`useState` からは 2 つのものが得られます。現在の state (`count`) と、それを更新するための関数 (`setCount`) です。名前は何でも構いませんが、慣習的には `[something, setSomething]` のように記述します。\n\nボタンが初めて表示されるとき、`count` は `0` になります。これは `useState()` に `0` を渡したからです。state を変更したいときは、`setCount()` を呼び出し、新しい値を渡します。このボタンをクリックすると、カウンタが増加します：\n\n```js {5}\nfunction MyButton() {\n  const [count, setCount] = useState(0);\n\n  function handleClick() {\n    setCount(count + 1);\n  }\n\n  return (\n    <button onClick={handleClick}>\n      Clicked {count} times\n    </button>\n  );\n}\n```\n\nReact は、再度コンポーネントの関数を呼び出します。今度は `count` が `1` になっています。次の呼び出しでは `2` になっています。次々と増えていきます。\n\n同じコンポーネントを複数の場所でレンダーした場合、それぞれが独自の state を持ちます。それぞれのボタンを個別にクリックしてみてください：\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function MyApp() {\n  return (\n    <div>\n      <h1>Counters that update separately</h1>\n      <MyButton />\n      <MyButton />\n    </div>\n  );\n}\n\nfunction MyButton() {\n  const [count, setCount] = useState(0);\n\n  function handleClick() {\n    setCount(count + 1);\n  }\n\n  return (\n    <button onClick={handleClick}>\n      Clicked {count} times\n    </button>\n  );\n}\n```\n\n```css\nbutton {\n  display: block;\n  margin-bottom: 5px;\n}\n```\n\n</Sandpack>\n\n各ボタンがそれぞれ `count` という state を「記憶」し、他のボタンに影響を与えないことに注意してください。\n\n## フックの使用 {/*using-hooks*/}\n\n`use` で始まる関数は、*フック* (Hook) と呼ばれます。`useState` は React が提供する組み込みのフックです。[API リファレンス](/reference/react)で他の組み込みフックを見ることができます。また、既存のフックを組み合わせて独自のフックを作成することもできます。\n\nフックには通常の関数より多くの制限があります。フックはコンポーネントの*トップレベル*（または他のフック内）でのみ呼び出すことができます。条件分岐やループの中で `useState` を使いたい場合は、新しいコンポーネントを抽出してそこに配置します。\n\n## コンポーネント間でデータを共有する {/*sharing-data-between-components*/}\n\n前述の例では、それぞれの `MyButton` が独立した `count` を持っており、ボタンがクリックされるたびにクリックされたボタンの `count` だけが変更されました。\n\n<DiagramGroup>\n\n<Diagram name=\"sharing_data_child\" height={367} width={407} alt=\"MyApp という名前の親コンポーネントと MyButton という名前の 2 つの子コンポーネントを持つツリーを示す図。どちらの MyButton コンポーネントも、カウントの値は 0。\">\n\n最初、それぞれの `MyButton` の `count` は `0`\n\n</Diagram>\n\n<Diagram name=\"sharing_data_child_clicked\" height={367} width={407} alt=\"前の図と同じだが、1 番目の MyButton コンポーネントのカウントがクリックされ、カウント値が 1 に増えている。2 番目の MyButton コンポーネントの値は 0 のまま。\">\n\n1 番目の `MyButton` が `count` を `1` に更新\n\n</Diagram>\n\n</DiagramGroup>\n\nただし、コンポーネント間で*データを共有し、常に一緒に更新したい*ということもよくあります。\n\n両方の `MyButton` コンポーネントが同じ `count` を表示し、一緒に更新されるようにするには、状態を個々のボタンから「上に」移動して、それらすべてを含む最も近いコンポーネントに入れます。\n\nこの例では、`MyApp` がそれです：\n\n<DiagramGroup>\n\n<Diagram name=\"sharing_data_parent\" height={385} width={410} alt=\"MyApp という名前の親コンポーネントと、MyButton という名前の 2 つの子コンポーネントを持つツリーを示す図。MyApp には値が 0 のカウントが含まれ、それが両方の MyButton コンポーネントに渡される。値は 0。\">\n\n最初、`MyApp` の `count` は `0` で、どちらの子もそれを受け取っている。\n\n</Diagram>\n\n<Diagram name=\"sharing_data_parent_clicked\" height={385} width={410} alt=\"前の図と同じだが、親 MyApp コンポーネントのカウントがクリックによりハイライトされ、値が 1 になっている。子 MyButton コンポーネントも両方ハイライトされ、それぞれの子のカウント値が 1 になっている。値が下に渡されたことを示している。\">\n\nクリック時に `MyApp` が `count` を `1` に更新し、それが両方の子に渡される\n\n</Diagram>\n\n</DiagramGroup>\n\nこうすれば、どちらのボタンをクリックしても、`MyApp` の `count` が更新され、連動して `MyButton` の両方のカウントが更新されるでしょう。以下は、コードでこれを表現する方法です。\n\nまず、`MyButton` から `MyApp` に、*state の移動*を行います。\n\n```js {2-6,18}\nexport default function MyApp() {\n  const [count, setCount] = useState(0);\n\n  function handleClick() {\n    setCount(count + 1);\n  }\n\n  return (\n    <div>\n      <h1>Counters that update separately</h1>\n      <MyButton />\n      <MyButton />\n    </div>\n  );\n}\n\nfunction MyButton() {\n  // ... we're moving code from here ...\n}\n\n```\n\n次に、`MyApp` から各 `MyButton` に *state を渡し*、共有のクリックハンドラも一緒に渡します。以前に `<img>` のような組み込みタグで行ったときと同様、JSX の波括弧を使うことで `MyButton` に情報を渡すことができます。\n\n```js {11-12}\nexport default function MyApp() {\n  const [count, setCount] = useState(0);\n\n  function handleClick() {\n    setCount(count + 1);\n  }\n\n  return (\n    <div>\n      <h1>Counters that update together</h1>\n      <MyButton count={count} onClick={handleClick} />\n      <MyButton count={count} onClick={handleClick} />\n    </div>\n  );\n}\n```\n\nこのように渡される情報は *props* と呼ばれます。`MyApp` コンポーネントは `count` 状態と `handleClick` イベントハンドラを保持しており、それらを*どちらも props として*各ボタンに渡します。\n\n最後に、`MyButton` を変更して、親コンポーネントから渡された props を*読み込む*ようにします。\n\n```js {1,3}\nfunction MyButton({ count, onClick }) {\n  return (\n    <button onClick={onClick}>\n      Clicked {count} times\n    </button>\n  );\n}\n```\n\nボタンをクリックすると、`onClick` ハンドラが発火します。各ボタンの `onClick` プロパティは `MyApp` 内の `handleClick` 関数となっているので、その中のコードが実行されます。そのコードは `setCount(count + 1)` を呼び出し、`count` という state 変数をインクリメントします。新しい `count` の値が各ボタンに props として渡されるため、すべてのボタンに新しい値が表示されます。この手法は「state のリフトアップ（持ち上げ）」と呼ばれています。リフトアップすることで、state をコンポーネント間で共有できました。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function MyApp() {\n  const [count, setCount] = useState(0);\n\n  function handleClick() {\n    setCount(count + 1);\n  }\n\n  return (\n    <div>\n      <h1>Counters that update together</h1>\n      <MyButton count={count} onClick={handleClick} />\n      <MyButton count={count} onClick={handleClick} />\n    </div>\n  );\n}\n\nfunction MyButton({ count, onClick }) {\n  return (\n    <button onClick={onClick}>\n      Clicked {count} times\n    </button>\n  );\n}\n```\n\n```css\nbutton {\n  display: block;\n  margin-bottom: 5px;\n}\n```\n\n</Sandpack>\n\n## 次のステップ {/*next-steps*/}\n\nこれで、React のコードを書く基本が分かったことになります！\n\n[チュートリアル](/learn/tutorial-tic-tac-toe)をチェックして、これらの概念を実践し、React を使った最初のミニアプリを作成しましょう。\n"
  },
  {
    "path": "src/content/learn/installation.md",
    "content": "---\ntitle: インストール\n---\n\n<Intro>\n\nReact は当初より、段階的に導入できるように設計されています。あなたの必要に応じて、React を使う量はどれだけ少なくても、どれだけ多くても構いません。React を少し味見してみたい場合でも、HTML ページにちょっとしたインタラクティブ性を追加したい場合でも、あるいは複雑な React アプリを立ち上げたいという場合でも、このセクションがあなたのスタートをお手伝いします。\n\n</Intro>\n\n## React を試してみる {/*try-react*/}\n\nReact を試すために何かをインストールする必要はありません。このサンドボックスを編集してみてください！\n\n<Sandpack>\n\n```js\nfunction Greeting({ name }) {\n  return <h1>Hello, {name}</h1>;\n}\n\nexport default function App() {\n  return <Greeting name=\"world\" />\n}\n```\n\n</Sandpack>\n\n直接編集しても構いませんし、右上の \"Fork\" ボタンを押して新しいタブで開くこともできます。\n\nReact ドキュメントのほとんどのページには、このようなサンドボックスが含まれています。React のドキュメント外にも、React をサポートするオンラインサンドボックスがたくさんあります。例えば、[CodeSandbox](https://codesandbox.io/s/new)、[StackBlitz](https://stackblitz.com/fork/react) や [CodePen](https://codepen.io/pen?template=QWYVwWN) が挙げられます。\n\nあなたのコンピュータのローカル環境で React を試すには、[この HTML ページをダウンロードしてください](https://gist.githubusercontent.com/gaearon/0275b1e1518599bbeafcde4722e79ed1/raw/db72dcbf3384ee1708c4a07d3be79860db04bff0/example.html)。そしてエディタとブラウザで開いてください！\n\n## React アプリの新規作成 {/*creating-a-react-app*/}\n\n新しい React アプリを立ち上げたい場合は、推奨されるフレームワークを用いて[新しい React アプリを作成](/learn/creating-a-react-app)します。\n\n## ゼロからの React アプリ構築 {/*build-a-react-app-from-scratch*/}\n\nあなたのプロジェクトにフレームワークが適さない場合、自分自身でフレームワークを構築したい場合、あるいは React を基本から学びたい場合は、[ゼロから React アプリを構築する](/learn/build-a-react-app-from-scratch)ことも可能です。\n\n## 既存のプロジェクトに React を追加する {/*add-react-to-an-existing-project*/}\n\n既存のアプリやウェブサイトで React を試してみたい場合は、[既存のプロジェクトに React を追加](/learn/add-react-to-an-existing-project)することもできます。\n\n\n<Note>\n\n#### Create React App を使うべき？ {/*should-i-use-create-react-app*/}\n\nいいえ、Create React App は非推奨となっています。詳細は [Create React App のサポート終了](/blog/2025/02/14/sunsetting-create-react-app)を参照してください。\n\n</Note>\n\n## 次のステップ {/*next-steps*/}\n\n日々遭遇する最も重要な React の概念を紹介する、[クイックスタート](/learn)ガイドへと進んでください。\n\n"
  },
  {
    "path": "src/content/learn/javascript-in-jsx-with-curly-braces.md",
    "content": "---\ntitle: JSX に波括弧で JavaScript を含める\n---\n\n<Intro>\n\nJSX により、JavaScript ファイル内に HTML のようなマークアップを書いて、レンダリングロジックとコンテンツを同じ場所にまとめられるようになります。ときに、そのマークアップの中で JavaScript のロジックを書いたり動的なプロパティを参照したりしたくなることがあります。このような場合、JSX 内で波括弧を使うことで、JavaScript への入り口を開くことができます。\n\n</Intro>\n\n<YouWillLearn>\n\n* 引用符を使って文字列を渡す方法\n* 波括弧を使って JSX 内で JavaScript 変数を参照する方法\n* 波括弧を使って JSX 内で JavaScript 関数を呼び出す方法\n* 波括弧を使って JSX 内でオブジェクトを利用する方法\n\n</YouWillLearn>\n\n## 引用符で文字列を渡す {/*passing-strings-with-quotes*/}\n\nJSX に文字列の属性を渡したい場合、それをシングルクオートかダブルクオートで囲みます。\n\n<Sandpack>\n\n```js\nexport default function Avatar() {\n  return (\n    <img\n      className=\"avatar\"\n      src=\"https://i.imgur.com/7vQD0fPs.jpg\"\n      alt=\"Gregorio Y. Zara\"\n    />\n  );\n}\n```\n\n```css\n.avatar { border-radius: 50%; height: 90px; }\n```\n\n</Sandpack>\n\nこの例では `\"https://i.imgur.com/7vQD0fPs.jpg\"` と `\"Gregorio Y. Zara\"` が文字列として渡されています。\n\nでは `src` や `alt` のテキストを動的に指定したい場合はどうすればいいのでしょう？ **`\"` と `\"` を `{` と `}` に置き換えることで、JavaScript の値を使う**ことができるのです。\n\n<Sandpack>\n\n```js\nexport default function Avatar() {\n  const avatar = 'https://i.imgur.com/7vQD0fPs.jpg';\n  const description = 'Gregorio Y. Zara';\n  return (\n    <img\n      className=\"avatar\"\n      src={avatar}\n      alt={description}\n    />\n  );\n}\n```\n\n```css\n.avatar { border-radius: 50%; height: 90px; }\n```\n\n</Sandpack>\n\n`className=\"avatar\"` は `\"avatar\"` という CSS クラス名を指定して画像を円形にしており、`src={avatar}` は `avatar` という名前の JavaScript 変数を読み出している、という違いに注意してください。波括弧を使うことで、マークアップの中で直接 JavaScript を使えるようになるからです。\n\n## 波括弧は JavaScript 世界への窓口 {/*using-curly-braces-a-window-into-the-javascript-world*/}\n\nJSX とは JavaScript を書く特殊な方法のひとつです。つまりその中で JavaScript が使えるということであり、そのための手段が波括弧 `{}` です。以下の例では、とある科学者の名前を `name` として宣言し、それを `<h1>` 内に波括弧を使って埋め込んでいます。\n\n<Sandpack>\n\n```js\nexport default function TodoList() {\n  const name = 'Gregorio Y. Zara';\n  return (\n    <h1>{name}'s To Do List</h1>\n  );\n}\n```\n\n</Sandpack>\n\n`name` の値を `'Gregorio Y. Zara'` から `'Hedy Lamarr'` にしてみてください。To Do リストのタイトルが変わりましたか？\n\n波括弧の中では `formatDate()` のような関数呼び出しも含む、あらゆる JavaScript の式が動作します。\n\n<Sandpack>\n\n```js\nconst today = new Date();\n\nfunction formatDate(date) {\n  return new Intl.DateTimeFormat(\n    'en-US',\n    { weekday: 'long' }\n  ).format(date);\n}\n\nexport default function TodoList() {\n  return (\n    <h1>To Do List for {formatDate(today)}</h1>\n  );\n}\n```\n\n</Sandpack>\n\n### 波括弧を使える場所 {/*where-to-use-curly-braces*/}\n\nJSX 内部で波括弧を使う方法は 2 つだけです。\n\n1. **テキストとして**、JSX タグの中で直接使う：`<h1>{name}'s To Do List</h1>` は動作しますが `<{tag}>Gregorio Y. Zara's To Do List</{tag}>` は動作しない。\n2. **属性として**、`=` 記号の直後に使う：`src={avatar}` は `avatar` という変数を読み出すが、`src=\"{avatar}\"` と書くと `\"{avatar}\"` という文字列そのものを渡す。\n\n## 「ダブル波括弧」で JSX 内に CSS やその他のオブジェクトを含める {/*using-double-curlies-css-and-other-objects-in-jsx*/}\n\n文字列や数字、その他の JavaScript の式に加えて、オブジェクトを JSX に渡すこともできます。オブジェクトも波括弧を使って `{ name: \"Hedy Lamarr\", inventions: 5 }` のように記述しますね。ですので JS オブジェクトを JSX に渡すときには、オブジェクトを別の波括弧のペアでラップして、`person={{ name: \"Hedy Lamarr\", inventions: 5 }}` のようにする必要があります。\n\nこれは JSX 内でインラインの CSS スタイルを使うときに目にすることがあります。React でインラインスタイルを使わなければいけないわけではありません（大抵の場合は CSS クラスでうまくいきます）。しかしインラインスタイルが必要な場合は、`style` 属性にオブジェクトを渡します。\n\n<Sandpack>\n\n```js\nexport default function TodoList() {\n  return (\n    <ul style={{\n      backgroundColor: 'black',\n      color: 'pink'\n    }}>\n      <li>Improve the videophone</li>\n      <li>Prepare aeronautics lectures</li>\n      <li>Work on the alcohol-fuelled engine</li>\n    </ul>\n  );\n}\n```\n\n```css\nbody { padding: 0; margin: 0 }\nul { padding: 20px 20px 20px 40px; margin: 0; }\n```\n\n</Sandpack>\n\n`backgroundColor` や `color` の値を変更してみてください。\n\n以下のように書けば波括弧の中に書かれた JavaScript のオブジェクトがよく見えてくるでしょう。\n\n```js {2-5}\n<ul style={\n  {\n    backgroundColor: 'black',\n    color: 'pink'\n  }\n}>\n```\n\n次に `{{` と `}}` を見たときには、JSX の波括弧の中に書かれたオブジェクトに過ぎないということを思い出してください！\n\n<Pitfall>\n\nインラインの `style` 属性はキャメルケースで書きます。例えば HTML で `<ul style=\"background-color: black\">` となっていれば、あなたのコンポーネントでは `<ul style={{ backgroundColor: 'black' }}>` になります。\n\n</Pitfall>\n\n## オブジェクトと波括弧でさらにいろいろやってみる {/*more-fun-with-javascript-objects-and-curly-braces*/}\n\n複数の式をひとつのオブジェクト内に移動して、JSX の波括弧内から参照することができます。\n\n<Sandpack>\n\n```js\nconst person = {\n  name: 'Gregorio Y. Zara',\n  theme: {\n    backgroundColor: 'black',\n    color: 'pink'\n  }\n};\n\nexport default function TodoList() {\n  return (\n    <div style={person.theme}>\n      <h1>{person.name}'s Todos</h1>\n      <img\n        className=\"avatar\"\n        src=\"https://i.imgur.com/7vQD0fPs.jpg\"\n        alt=\"Gregorio Y. Zara\"\n      />\n      <ul>\n        <li>Improve the videophone</li>\n        <li>Prepare aeronautics lectures</li>\n        <li>Work on the alcohol-fuelled engine</li>\n      </ul>\n    </div>\n  );\n}\n```\n\n```css\nbody { padding: 0; margin: 0 }\nbody > div > div { padding: 20px; }\n.avatar { border-radius: 50%; height: 90px; }\n```\n\n</Sandpack>\n\nこの例では `person` という JavaScript オブジェクト内に `name` 文字列と `theme` オブジェクトが含まれています。\n\n```js\nconst person = {\n  name: 'Gregorio Y. Zara',\n  theme: {\n    backgroundColor: 'black',\n    color: 'pink'\n  }\n};\n```\n\nコンポーネントは `person` から始めて以下のように書くことでこれらの値を使うことができます。\n\n```js\n<div style={person.theme}>\n  <h1>{person.name}'s Todos</h1>\n```\n\nJSX ではデータやロジックを書くのに JavaScript を使うため、JSX はテンプレート言語としては非常にミニマルです。\n\n<Recap>\n\nこれで JSX についてのほとんどを学びました。\n\n* 引用符内に書かれた JSX 属性は文字列として渡される。\n* 波括弧を使えば JavaScript のロジックや変数をマークアップ内に含めることができる。\n* 波括弧はタグのコンテンツの中で使うか、属性の場合は `=` の直後で使う。\n* `{{` と `}}` は特別な構文ではなく、JSX の波括弧にくっつくように書かれた JavaScript オブジェクトである。\n\n</Recap>\n\n<Challenges>\n\n#### 間違いを修正する {/*fix-the-mistake*/}\n\n以下のコードは `Objects are not valid as a React child` というエラーを出してクラッシュします。\n\n<Sandpack>\n\n```js\nconst person = {\n  name: 'Gregorio Y. Zara',\n  theme: {\n    backgroundColor: 'black',\n    color: 'pink'\n  }\n};\n\nexport default function TodoList() {\n  return (\n    <div style={person.theme}>\n      <h1>{person}'s Todos</h1>\n      <img\n        className=\"avatar\"\n        src=\"https://i.imgur.com/7vQD0fPs.jpg\"\n        alt=\"Gregorio Y. Zara\"\n      />\n      <ul>\n        <li>Improve the videophone</li>\n        <li>Prepare aeronautics lectures</li>\n        <li>Work on the alcohol-fuelled engine</li>\n      </ul>\n    </div>\n  );\n}\n```\n\n```css\nbody { padding: 0; margin: 0 }\nbody > div > div { padding: 20px; }\n.avatar { border-radius: 50%; height: 90px; }\n```\n\n</Sandpack>\n\nどこが問題か分かりますか？\n\n<Hint>波括弧の中に何が書かれているのか見てみましょう。正しいものが入っていますか？</Hint>\n\n<Solution>\n\nこのエラーが起きているのは、マークアップ内で文字列ではなく*オブジェクトそのもの*をレンダーしているからです。つまり `<h1>{person}'s Todos</h1>` が `person` オブジェクト全体をレンダーしようとしてしまっているのです。テキスト内容としてオブジェクトをそのまま含めようとすると、React はそれをどう表示したらいいか分からないため、エラーをスローします。\n\n修正するには、`<h1>{person}'s Todos</h1>` を `<h1>{person.name}'s Todos</h1>` で置き換えてください。\n\n<Sandpack>\n\n```js\nconst person = {\n  name: 'Gregorio Y. Zara',\n  theme: {\n    backgroundColor: 'black',\n    color: 'pink'\n  }\n};\n\nexport default function TodoList() {\n  return (\n    <div style={person.theme}>\n      <h1>{person.name}'s Todos</h1>\n      <img\n        className=\"avatar\"\n        src=\"https://i.imgur.com/7vQD0fPs.jpg\"\n        alt=\"Gregorio Y. Zara\"\n      />\n      <ul>\n        <li>Improve the videophone</li>\n        <li>Prepare aeronautics lectures</li>\n        <li>Work on the alcohol-fuelled engine</li>\n      </ul>\n    </div>\n  );\n}\n```\n\n```css\nbody { padding: 0; margin: 0 }\nbody > div > div { padding: 20px; }\n.avatar { border-radius: 50%; height: 90px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### オブジェクト内にデータを移動する {/*extract-information-into-an-object*/}\n\n画像 URL を `person` オブジェクト内に移動しましょう。\n\n<Sandpack>\n\n```js\nconst person = {\n  name: 'Gregorio Y. Zara',\n  theme: {\n    backgroundColor: 'black',\n    color: 'pink'\n  }\n};\n\nexport default function TodoList() {\n  return (\n    <div style={person.theme}>\n      <h1>{person.name}'s Todos</h1>\n      <img\n        className=\"avatar\"\n        src=\"https://i.imgur.com/7vQD0fPs.jpg\"\n        alt=\"Gregorio Y. Zara\"\n      />\n      <ul>\n        <li>Improve the videophone</li>\n        <li>Prepare aeronautics lectures</li>\n        <li>Work on the alcohol-fuelled engine</li>\n      </ul>\n    </div>\n  );\n}\n```\n\n```css\nbody { padding: 0; margin: 0 }\nbody > div > div { padding: 20px; }\n.avatar { border-radius: 50%; height: 90px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n画像 URL を `person.imageUrl` というプロパティに移動して、それを `<img>` タグから波括弧を使って読み取ります。\n\n<Sandpack>\n\n```js\nconst person = {\n  name: 'Gregorio Y. Zara',\n  imageUrl: \"https://i.imgur.com/7vQD0fPs.jpg\",\n  theme: {\n    backgroundColor: 'black',\n    color: 'pink'\n  }\n};\n\nexport default function TodoList() {\n  return (\n    <div style={person.theme}>\n      <h1>{person.name}'s Todos</h1>\n      <img\n        className=\"avatar\"\n        src={person.imageUrl}\n        alt=\"Gregorio Y. Zara\"\n      />\n      <ul>\n        <li>Improve the videophone</li>\n        <li>Prepare aeronautics lectures</li>\n        <li>Work on the alcohol-fuelled engine</li>\n      </ul>\n    </div>\n  );\n}\n```\n\n```css\nbody { padding: 0; margin: 0 }\nbody > div > div { padding: 20px; }\n.avatar { border-radius: 50%; height: 90px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### JSX 波括弧内に式を書く {/*write-an-expression-inside-jsx-curly-braces*/}\n\n以下のオブジェクトでは画像の URL が、ベース URL・`imageId`・`imageSize`・ファイル拡張子の 4 つに分割されています。\n\n画像 URL を指定するところで、ベース URL（常に `'https://i.imgur.com/'`）、`imageId` (`'7vQD0fP'`)、`imageSize` (`'s'`)、ファイル拡張子（常に `'.jpg'`）の 4 つの値を結合したいと思っています。ですが `<img>` タグが `src` を指定する部分で何かがおかしいようです。\n\n修正できますか？\n\n<Sandpack>\n\n```js\n\nconst baseUrl = 'https://i.imgur.com/';\nconst person = {\n  name: 'Gregorio Y. Zara',\n  imageId: '7vQD0fP',\n  imageSize: 's',\n  theme: {\n    backgroundColor: 'black',\n    color: 'pink'\n  }\n};\n\nexport default function TodoList() {\n  return (\n    <div style={person.theme}>\n      <h1>{person.name}'s Todos</h1>\n      <img\n        className=\"avatar\"\n        src=\"{baseUrl}{person.imageId}{person.imageSize}.jpg\"\n        alt={person.name}\n      />\n      <ul>\n        <li>Improve the videophone</li>\n        <li>Prepare aeronautics lectures</li>\n        <li>Work on the alcohol-fuelled engine</li>\n      </ul>\n    </div>\n  );\n}\n```\n\n```css\nbody { padding: 0; margin: 0 }\nbody > div > div { padding: 20px; }\n.avatar { border-radius: 50%; }\n```\n\n</Sandpack>\n\n修正がうまくいったことを確認するには、`imageSize` の値を `'b'` に書きかえてみてください。編集すると画像のサイズが変わるはずです。\n\n<Solution>\n\n`src={baseUrl + person.imageId + person.imageSize + '.jpg'}` のように書いてください。\n\n1. `{` で JavaScript 式が書けるようになる\n2. `baseUrl + person.imageId + person.imageSize + '.jpg'` が正しい URL を生成する\n3. `}` で JavaScript 式を終了する\n\n<Sandpack>\n\n```js\nconst baseUrl = 'https://i.imgur.com/';\nconst person = {\n  name: 'Gregorio Y. Zara',\n  imageId: '7vQD0fP',\n  imageSize: 's',\n  theme: {\n    backgroundColor: 'black',\n    color: 'pink'\n  }\n};\n\nexport default function TodoList() {\n  return (\n    <div style={person.theme}>\n      <h1>{person.name}'s Todos</h1>\n      <img\n        className=\"avatar\"\n        src={baseUrl + person.imageId + person.imageSize + '.jpg'}\n        alt={person.name}\n      />\n      <ul>\n        <li>Improve the videophone</li>\n        <li>Prepare aeronautics lectures</li>\n        <li>Work on the alcohol-fuelled engine</li>\n      </ul>\n    </div>\n  );\n}\n```\n\n```css\nbody { padding: 0; margin: 0 }\nbody > div > div { padding: 20px; }\n.avatar { border-radius: 50%; }\n```\n\n</Sandpack>\n\nあるいは以下の `getImageUrl` のように、別の関数にこの式を移動することもできます。\n\n<Sandpack>\n\n```js src/App.js\nimport { getImageUrl } from './utils.js'\n\nconst person = {\n  name: 'Gregorio Y. Zara',\n  imageId: '7vQD0fP',\n  imageSize: 's',\n  theme: {\n    backgroundColor: 'black',\n    color: 'pink'\n  }\n};\n\nexport default function TodoList() {\n  return (\n    <div style={person.theme}>\n      <h1>{person.name}'s Todos</h1>\n      <img\n        className=\"avatar\"\n        src={getImageUrl(person)}\n        alt={person.name}\n      />\n      <ul>\n        <li>Improve the videophone</li>\n        <li>Prepare aeronautics lectures</li>\n        <li>Work on the alcohol-fuelled engine</li>\n      </ul>\n    </div>\n  );\n}\n```\n\n```js src/utils.js\nexport function getImageUrl(person) {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    person.imageSize +\n    '.jpg'\n  );\n}\n```\n\n```css\nbody { padding: 0; margin: 0 }\nbody > div > div { padding: 20px; }\n.avatar { border-radius: 50%; }\n```\n\n</Sandpack>\n\n変数と関数を利用することで、マークアップをシンプルに保つことができます！\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/keeping-components-pure.md",
    "content": "---\ntitle: コンポーネントを純粋に保つ\n---\n\n<Intro>\n\nJavaScript 関数の中には、*純関数* (pure function) と呼ばれるものがあります。純関数とは計算だけを行い、他には何もしない関数のことです。コンポーネントを常に厳密に純関数として書くことで、コードベースが成長するにつれて起きがちな、あらゆる種類の不可解なバグ、予測不可能な挙動を回避することができます。ただし、このようなメリットを得るためには、従わなければならないルールがいくつか存在します。\n\n</Intro>\n\n<YouWillLearn>\n\n* 「純粋」であるとは何か、それによりなぜバグが減らせるのか\n* 変更をレンダーの外で行い、コンポーネントを純粋に保つ方法\n* Strict Mode を使用してコンポーネントの間違いを見つける方法\n\n</YouWillLearn>\n\n## 純粋性：コンポーネントとは数式のようなもの {/*purity-components-as-formulas*/}\n\nコンピュータサイエンス（特に関数型プログラミングの世界）では、[純関数 (pure function)](https://wikipedia.org/wiki/Pure_function) とは、以下のような特徴を持つ関数のことを指します。\n\n* **自分の仕事に集中する**。呼び出される前に存在していたオブジェクトや変数を変更しない。\n* **同じ入力には同じ出力**。同じ入力を与えると、純関数は常に同じ結果を返す。\n\n皆さんは純関数の例をひとつ、すでにご存知のはずです。数学における関数です。\n\nこの数式を考えてみてください：<Math><MathI>y</MathI> = 2<MathI>x</MathI></Math>。\n\nもし <Math><MathI>x</MathI> = 2</Math> ならば <Math><MathI>y</MathI> = 4</Math>。常にです。\n\nもし <Math><MathI>x</MathI> = 3</Math> ならば <Math><MathI>y</MathI> = 6</Math>。常にです。\n\nもし <Math><MathI>x</MathI> = 3</Math> ならば、<MathI>y</MathI> が現在時刻や株式市況に影響されてたまに <Math>9</Math> や <Math>–1</Math> や <Math>2.5</Math> になったりはしません。\n\nもし <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math> かつ <Math><MathI>x</MathI> = 3</Math> なら、<MathI>y</MathI> は*どんな場合でも常に* <Math>6</Math> になるのです。\n\nこの式を JavaScript 関数で書くとすると、次のようになります：\n\n```js\nfunction double(number) {\n  return 2 * number;\n}\n```\n\n上記の例では、`double` 関数は**純関数**です。もし `3` を渡すと、`6` を返しますね。常にです。\n\nReact はこのような概念に基づいて設計されています。**React は、あなたが書くすべてのコンポーネントが純関数であると仮定しています**。つまり、あなたが書く React コンポーネントは、与えられた入力が同じであれば、常に同じ JSX を返す必要があります。\n\n<Sandpack>\n\n```js src/App.js\nfunction Recipe({ drinkers }) {\n  return (\n    <ol>    \n      <li>Boil {drinkers} cups of water.</li>\n      <li>Add {drinkers} spoons of tea and {0.5 * drinkers} spoons of spice.</li>\n      <li>Add {0.5 * drinkers} cups of milk to boil and sugar to taste.</li>\n    </ol>\n  );\n}\n\nexport default function App() {\n  return (\n    <section>\n      <h1>Spiced Chai Recipe</h1>\n      <h2>For two</h2>\n      <Recipe drinkers={2} />\n      <h2>For a gathering</h2>\n      <Recipe drinkers={4} />\n    </section>\n  );\n}\n```\n\n</Sandpack>\n\n`drinkers={2}` を `Recipe` に渡すと、`2 cups of water` を含む JSX が返されます。常にです。\n\n`drinkers={4}` を渡すと、`4 cups of water` を含む JSX が返されます。常にです。\n\nそう、まるで数式のように、です。\n\nコンポーネントとはレシピのようなものだと考えることもできるでしょう。調理途中で新しい食材を加えたりせず、レシピに従っておけば、常に同じ料理を得ることができます。その「料理」とは、コンポーネントが React に提供する JSX のことであり、それを React が[表示](/learn/render-and-commit)します。\n\n<Illustration src=\"/images/docs/illustrations/i_puritea-recipe.png\" alt=\"人 x 人分のお茶のレシピ：x cups of water を取り、x spoons of tea と0.5x spoons of spices を加え、0.5x cups of milk を入れる\" />\n\n## 副作用：意図せぬ (?) 付随処理 {/*side-effects-unintended-consequences*/}\n\nReact のレンダープロセスは常に純粋である必要があります。コンポーネントは JSX を*返す*だけであり、レンダー前に存在していたオブジェクトや変数を*書き換え*しないようにしなければなりません。さもなくばコンポーネントは不純 (impure) になってしまいます！\n\n以下は、この規則を守っていないコンポーネントの例です。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [5]}}\nlet guest = 0;\n\nfunction Cup() {\n  // Bad: changing a preexisting variable!\n  guest = guest + 1;\n  return <h2>Tea cup for guest #{guest}</h2>;\n}\n\nexport default function TeaSet() {\n  return (\n    <>\n      <Cup />\n      <Cup />\n      <Cup />\n    </>\n  );\n}\n```\n\n</Sandpack>\n\nこのコンポーネントは、外部で宣言された `guest` 変数を読み書きしています。つまり、**このコンポーネントを複数回呼び出すと、異なる JSX が生成されます！** さらに悪いことに、*ほかの*コンポーネントも `guest` を読み取る場合、それらもレンダーされたタイミングによって異なる JSX を生成することになります！ これでは予測不可能です。\n\n数式 <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math> の例に戻ると、これは <Math><MathI>x</MathI> = 2</Math> であっても <Math><MathI>y</MathI> = 4</Math> であることが保証されない、というようなことです。テストは失敗し、ユーザは当惑し、飛行機も空から墜落しかねません。こんなことをするとなぜ混乱するバグが引き起こされるのか、もうおわかりですね。\n\n[props を使って `guest` を渡す](/learn/passing-props-to-a-component)ように、このコンポーネントを修正できます。\n\n<Sandpack>\n\n```js\nfunction Cup({ guest }) {\n  return <h2>Tea cup for guest #{guest}</h2>;\n}\n\nexport default function TeaSet() {\n  return (\n    <>\n      <Cup guest={1} />\n      <Cup guest={2} />\n      <Cup guest={3} />\n    </>\n  );\n}\n```\n\n</Sandpack>\n\nこれでこのコンポーネントは純粋になります。返す JSX が props である `guest` のみに依存しているからです。\n\n一般に、特定の順序でコンポーネントがレンダーされることを期待してはいけません。<Math><MathI>y</MathI> = 2<MathI>x</MathI></Math> と <Math><MathI>y</MathI> = 5<MathI>x</MathI></Math> のどちらを先に呼ぶかなど問題にしてはいけないのです。これらの数式は互いに無関係に計算されるべきです。同じように、各コンポーネントは「自分のことだけを考える」べきであり、レンダーの最中に他のコンポーネントに依存したり他のコンポーネントと協調したりすることはありません。レンダーとは学校の試験のようなものです。各コンポーネントはそれぞれ、自分の力だけで JSX を計算する必要があるのです！\n\n<DeepDive>\n\n#### StrictMode で純粋でない計算を検出 {/*detecting-impure-calculations-with-strict-mode*/}\n\nまだ全部を使ったことはないかもしれませんが、React には [props](/learn/passing-props-to-a-component)、[state](/learn/state-a-components-memory)、そして[コンテクスト](/learn/passing-data-deeply-with-context)という、レンダー中に読み取ることができる 3 種類の入力値があります。これらの入力値は、常に読み取り専用として扱うようにしてください。\n\nユーザ入力に応じて何かを*変更*したい場合は、変数に書き込む代わりに、[state を設定する](/learn/state-a-components-memory)ことが適切です。要素のレンダー中に既存の変数やオブジェクトを書き換えることは絶対にやってはいけません。\n\nReact には \"Strict Mode\" という機能があり、開発中には各コンポーネント関数を 2 回呼び出します。**関数呼び出しを 2 回行うことで、Strict Mode はこれらのルールに反するコンポーネントを見つけるのに役立ちます。**\n\n元の例では \"Guest #1\"、\"Guest #2\"、\"Guest #3\" と表示される代わりに \"Guest #2\"、\"Guest #4\"、\"Guest #6\" と表示されてしまっていましたね。元の関数が純粋でなかったため、2 回呼び出すと壊れていたわけです。修正された純粋なバージョンは、毎回 2 回呼び出されても問題ありません。**純関数は計算をするだけなので、2 回呼び出しても何も変わりません**。`double(2)` を 2 回呼び出しても返り値が変わることはなく、<Math><MathI>y</MathI> = 2<MathI>x</MathI></Math> を 2 回解いても <MathI>y</MathI> が変わることがないのと全く同じです。入力が同じならば、出力も同じにしてください。常にそうしてください。\n\nStrict Mode は本番環境では影響を与えないため、ユーザが使うアプリを遅くすることはありません。Strict Mode を有効にするには、ルートコンポーネントを `<React.StrictMode>` でラップします。一部のフレームワークでは、これがデフォルトで行われます。\n\n</DeepDive>\n\n### ローカルミューテーション：コンポーネントの小さな秘密 {/*local-mutation-your-components-little-secret*/}\n\n上記の例では、問題はコンポーネントがレンダーの最中に*既存*の変数を変更していた点にありました。このような変更は、少し恐ろしい言い方では **\"ミューテーション（変異; mutation）\"** と呼ばれます。純関数は、関数のスコープ外の変数や、呼び出し前に作成されたオブジェクトをミューテートしません。そういうことをしてしまった関数は「不純」になってしまいます！\n\nしかし、**レンダー中に*その場で*作成した変数やオブジェクトであれば、書き換えることは全く問題ありません**。この例では、`[]` 配列を作成して `cups` 変数に代入し、それに 12 個のカップを `push` しています：\n\n<Sandpack>\n\n```js\nfunction Cup({ guest }) {\n  return <h2>Tea cup for guest #{guest}</h2>;\n}\n\nexport default function TeaGathering() {\n  const cups = [];\n  for (let i = 1; i <= 12; i++) {\n    cups.push(<Cup key={i} guest={i} />);\n  }\n  return cups;\n}\n```\n\n</Sandpack>\n\n`cups` 変数または `[]` 配列が `TeaGathering` 関数の外で作成されたものだった場合、これは大きな問題になることでしょう！ *既存の*オブジェクトを変更してしまうことになるからです。\n\nしかし、`cups` 変数と `[]` 配列は、`TeaGathering` 内で*同一のレンダー中に*作成されたものであるため、問題はありません。`TeaGathering` 以外のコードは、これが起こったことすら知るすべがありません。これは \"ローカルミューテーション (local mutation)\" と呼ばれます。あなたのコンポーネント内のちょっとした秘密のようなものです。\n\n## 副作用を引き起こせる場所 {/*where-you-_can_-cause-side-effects*/}\n\n関数型プログラミングには純粋性が重要であるとはいえ、いつか、どこかの場所で、*何らかのもの*が変化しなければなりません。むしろそれがプログラミングをする意味というものでしょう。これらの変化（スクリーンの更新、アニメーションの開始、データの変更など）は **副作用 (side effect)** と呼ばれます。レンダーの最中には発生しない、「付随的」なものです。\n\nReact では、**副作用は通常、[イベントハンドラ](/learn/responding-to-events)の中に属します**。イベントハンドラは、ボタンがクリックされたといった何らかのアクションが実行されたときに React が実行する関数です。イベントハンドラは、コンポーネントの「内側」で定義されているものではありますが、レンダーの「最中」に実行されるわけではありません！ **つまり、イベントハンドラは純粋である必要はありません。**\n\nいろいろ探してもあなたの副作用を書くのに適切なイベントハンドラがどうしても見つからない場合は、コンポーネントから返された JSX に [`useEffect`](/reference/react/useEffect) 呼び出しを付加することで副作用を付随させることも可能です。これにより React に、その関数をレンダーの後（その時点なら副作用が許されます）で呼ぶように指示できます。**ただしこれは最終手段であるべきです。**\n\n可能な限り、ロジックをレンダーのみで表現してみてください。これだけでどれだけのことができるのか、驚くことでしょう！\n\n<DeepDive>\n\n#### React はなぜ純粋性を重視するのか？ {/*why-does-react-care-about-purity*/}\n\n純関数を書くことには、多少の習慣化と訓練が必要です。しかし、それは素晴らしいチャンスをもたらすものでもあります。\n\n* コンポーネントが異なる環境、例えばサーバ上でも実行できるようになります！ 入力値が同じなら同じ結果を返すので、ひとつのコンポーネントが多数のユーザリクエストを処理できます。\n* 入力値が変化しない場合、[レンダーをスキップ](/reference/react/memo)することでパフォーマンスを向上できます。これが問題ないのは、純関数は常に同じ出力を返すため安全にキャッシュできるからです。\n* 深いコンポーネントツリーのレンダーの途中でデータが変化した場合、React は既に古くなったレンダー処理を最後まで終わらせるような無駄を省き、新しいレンダーを開始できます。純粋性のおかげで、いつ計算を中断しても問題ありません。\n\n我々が開発する React の新たな機能は常に、関数の純粋性を活用しています。データ取得からアニメーション、パフォーマンスの向上に到るまで、React パラダイムの威力はコンポーネントを純関数に保つことによって発揮されるのです。\n\n</DeepDive>\n\n<Recap>\n\n* コンポーネントは純粋である必要がある。すなわち：\n  * **コンポーネントは自分の仕事に集中する**。レンダー前に存在していたオブジェクトや変数を書き換えない。\n  * **入力が同じなら出力も同じ**。同じ入力に対しては、常に同じ JSX を返すようにする。\n* レンダーはいつでも起こる可能性があるため、コンポーネントは相互の呼び出し順に依存してはいけない。\n* コンポーネントがレンダーに使用する入力値を書き換えない。これには props、state、コンテクストが含まれる。画面を更新するためには既存のオブジェクトを書き換えるのではなく、代わりに [state をセットする](/learn/state-a-components-memory)。\n* コンポーネントのロジックはできるだけコンポーネントが返す JSX の中で表現する。何かを「変える」必要がある場合、通常はイベントハンドラで行う。最終手段として `useEffect` を使用する。\n* 純関数を書くことには訓練が必要だが、それにより React パラダイムの威力が発揮される。\n\n</Recap>\n\n\n  \n<Challenges>\n\n#### 壊れた時計を修理 {/*fix-a-broken-clock*/}\n\nこのコンポーネントは、深夜 0 時から朝 6 時までの間は `<h1>` の CSS クラスを `\"night\"` に、その他の時間帯は `\"day\"` に設定しようとしています。ですが失敗してしまっています。このコンポーネントを修正してみてください。\n\nあなたの回答が機能しているかを確認するには、一時的にコンピュータのタイムゾーンを変更することで確認できます。現在の時刻が午前 0 時から 6 時までの場合、時計の色が反転するはずです！\n\n<Hint>\n\nレンダーは計算のみを行います。そこで何かを「行おう」としてはいけません。同じ意味を表現する別の方法はありますか？\n\n</Hint>\n\n<Sandpack>\n\n```js src/Clock.js active\nexport default function Clock({ time }) {\n  const hours = time.getHours();\n  if (hours >= 0 && hours <= 6) {\n    document.getElementById('time').className = 'night';\n  } else {\n    document.getElementById('time').className = 'day';\n  }\n  return (\n    <h1 id=\"time\">\n      {time.toLocaleTimeString()}\n    </h1>\n  );\n}\n```\n\n```js src/App.js hidden\nimport { useState, useEffect } from 'react';\nimport Clock from './Clock.js';\n\nfunction useTime() {\n  const [time, setTime] = useState(() => new Date());\n  useEffect(() => {\n    const id = setInterval(() => {\n      setTime(new Date());\n    }, 1000);\n    return () => clearInterval(id);\n  }, []);\n  return time;\n}\n\nexport default function App() {\n  const time = useTime();\n  return (\n    <Clock time={time} />\n  );\n}\n```\n\n```css\nbody > * {\n  width: 100%;\n  height: 100%;\n}\n.day {\n  background: #fff;\n  color: #222;\n}\n.night {\n  background: #222;\n  color: #fff;\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n`className` を計算してレンダーの出力に含めるようにすれば、このコンポーネントを修正できます。\n\n<Sandpack>\n\n```js src/Clock.js active\nexport default function Clock({ time }) {\n  const hours = time.getHours();\n  let className;\n  if (hours >= 0 && hours <= 6) {\n    className = 'night';\n  } else {\n    className = 'day';\n  }\n  return (\n    <h1 className={className}>\n      {time.toLocaleTimeString()}\n    </h1>\n  );\n}\n```\n\n```js src/App.js hidden\nimport { useState, useEffect } from 'react';\nimport Clock from './Clock.js';\n\nfunction useTime() {\n  const [time, setTime] = useState(() => new Date());\n  useEffect(() => {\n    const id = setInterval(() => {\n      setTime(new Date());\n    }, 1000);\n    return () => clearInterval(id);\n  }, []);\n  return time;\n}\n\nexport default function App() {\n  const time = useTime();\n  return (\n    <Clock time={time} />\n  );\n}\n```\n\n```css\nbody > * {\n  width: 100%;\n  height: 100%;\n}\n.day {\n  background: #fff;\n  color: #222;\n}\n.night {\n  background: #222;\n  color: #fff;\n}\n```\n\n</Sandpack>\n\nこの例では、副作用（DOM の変更）は全く必要ではありませんでした。単に JSX を返すだけで十分です。\n\n</Solution>\n\n#### 壊れたプロフィールを修正する {/*fix-a-broken-profile*/}\n\n異なるデータを持つ 2 つの `Profile` コンポーネントが並んで表示されています。最初のプロフィールを折りたたみ (Collapse) してから展開 (Expand) してみてください。両方のプロフィールが同じ人物を表示することがわかります。これはバグです。\n\nバグの原因を探し、修正してください。\n\n<Hint>\n\nバグがあるコードは `Profile.js` にあります。一番上から一番下まで読み通してください。\n\n</Hint>\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [7]}} src/Profile.js\nimport Panel from './Panel.js';\nimport { getImageUrl } from './utils.js';\n\nlet currentPerson;\n\nexport default function Profile({ person }) {\n  currentPerson = person;\n  return (\n    <Panel>\n      <Header />\n      <Avatar />\n    </Panel>\n  )\n}\n\nfunction Header() {\n  return <h1>{currentPerson.name}</h1>;\n}\n\nfunction Avatar() {\n  return (\n    <img\n      className=\"avatar\"\n      src={getImageUrl(currentPerson)}\n      alt={currentPerson.name}\n      width={50}\n      height={50}\n    />\n  );\n}\n```\n\n```js src/Panel.js hidden\nimport { useState } from 'react';\n\nexport default function Panel({ children }) {\n  const [open, setOpen] = useState(true);\n  return (\n    <section className=\"panel\">\n      <button onClick={() => setOpen(!open)}>\n        {open ? 'Collapse' : 'Expand'}\n      </button>\n      {open && children}\n    </section>\n  );\n}\n```\n\n```js src/App.js\nimport Profile from './Profile.js';\n\nexport default function App() {\n  return (\n    <>\n      <Profile person={{\n        imageId: 'lrWQx8l',\n        name: 'Subrahmanyan Chandrasekhar',\n      }} />\n      <Profile person={{\n        imageId: 'MK3eW3A',\n        name: 'Creola Katherine Johnson',\n      }} />\n    </>\n  )\n}\n```\n\n```js src/utils.js hidden\nexport function getImageUrl(person, size = 's') {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    size +\n    '.jpg'\n  );\n}\n```\n\n```css\n.avatar { margin: 5px; border-radius: 50%; }\n.panel {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n  width: 200px;\n}\nh1 { margin: 5px; font-size: 18px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n問題は、`Profile` コンポーネントが既存の `currentPerson` 変数に書き込み、`Header` と `Avatar` コンポーネントがそれを読み取っていることです。これにより、*これら 3 つのコンポーネントすべて*が不純なものとなり、予測困難となってしまっています。\n\nバグを修正するには、`currentPerson` 変数を削除します。代わりに、`Header` と `Avatar` にすべての情報を props で伝えます。両コンポーネントに props として `person` を追加し、それを渡す必要があります。\n\n<Sandpack>\n\n```js src/Profile.js active\nimport Panel from './Panel.js';\nimport { getImageUrl } from './utils.js';\n\nexport default function Profile({ person }) {\n  return (\n    <Panel>\n      <Header person={person} />\n      <Avatar person={person} />\n    </Panel>\n  )\n}\n\nfunction Header({ person }) {\n  return <h1>{person.name}</h1>;\n}\n\nfunction Avatar({ person }) {\n  return (\n    <img\n      className=\"avatar\"\n      src={getImageUrl(person)}\n      alt={person.name}\n      width={50}\n      height={50}\n    />\n  );\n}\n```\n\n```js src/Panel.js hidden\nimport { useState } from 'react';\n\nexport default function Panel({ children }) {\n  const [open, setOpen] = useState(true);\n  return (\n    <section className=\"panel\">\n      <button onClick={() => setOpen(!open)}>\n        {open ? 'Collapse' : 'Expand'}\n      </button>\n      {open && children}\n    </section>\n  );\n}\n```\n\n```js src/App.js\nimport Profile from './Profile.js';\n\nexport default function App() {\n  return (\n    <>\n      <Profile person={{\n        imageId: 'lrWQx8l',\n        name: 'Subrahmanyan Chandrasekhar',\n      }} />\n      <Profile person={{\n        imageId: 'MK3eW3A',\n        name: 'Creola Katherine Johnson',\n      }} />\n    </>\n  );\n}\n```\n\n```js src/utils.js hidden\nexport function getImageUrl(person, size = 's') {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    size +\n    '.jpg'\n  );\n}\n```\n\n```css\n.avatar { margin: 5px; border-radius: 50%; }\n.panel {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n  width: 200px;\n}\nh1 { margin: 5px; font-size: 18px; }\n```\n\n</Sandpack>\n\nReact ではコンポーネント関数が何らかの特定の順序で実行されることは保証されていないため、変数を設定してコンポーネント間でデータをやりとりすることはできない、ということを覚えておきましょう。すべての通信は props を介して行う必要があります。\n\n</Solution>\n\n#### 壊れたストーリートレイを修正 {/*fix-a-broken-story-tray*/}\n\nあなたの会社の CEO に、オンライン時計アプリに「ストーリー」を追加するよう要求され、ノーと言えない状況です。あなたは `StoryTray` コンポーネントを作成して、受け取った `stories` のリストを表示させ、末尾に \"Create Story\" というプレースホルダを表示することにしました。\n\n\"Create Story\" プレースホルダは、受け取った `stories` 配列にさらにフェイクのストーリーを 1 つ追加することで実装しました。しかし、どういうわけか \"Create Story\" が何度も表示されてしまっています。この問題を修正してください。\n\n<Sandpack>\n\n```js src/StoryTray.js active\nexport default function StoryTray({ stories }) {\n  stories.push({\n    id: 'create',\n    label: 'Create Story'\n  });\n\n  return (\n    <ul>\n      {stories.map(story => (\n        <li key={story.id}>\n          {story.label}\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```js {expectedErrors: {'react-compiler': [16]}} src/App.js hidden\nimport { useState, useEffect } from 'react';\nimport StoryTray from './StoryTray.js';\n\nconst initialStories = [\n  {id: 0, label: \"Ankit's Story\" },\n  {id: 1, label: \"Taylor's Story\" },\n];\n\nexport default function App() {\n  const [stories, setStories] = useState([...initialStories])\n  const time = useTime();\n\n  // HACK: Prevent the memory from growing forever while you read docs.\n  // We're breaking our own rules here.\n  if (stories.length > 100) {\n    stories.length = 100;\n  }\n\n  return (\n    <div\n      style={{\n        width: '100%',\n        height: '100%',\n        textAlign: 'center',\n      }}\n    >\n      <h2>It is {time.toLocaleTimeString()} now.</h2>\n      <StoryTray stories={stories} />\n    </div>\n  );\n}\n\nfunction useTime() {\n  const [time, setTime] = useState(() => new Date());\n  useEffect(() => {\n    const id = setInterval(() => {\n      setTime(new Date());\n    }, 1000);\n    return () => clearInterval(id);\n  }, []);\n  return time;\n}\n```\n\n```css\nul {\n  margin: 0;\n  list-style-type: none;\n}\n\nli {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  float: left;\n  margin: 5px;\n  margin-bottom: 20px;\n  padding: 5px;\n  width: 70px;\n  height: 100px;\n}\n```\n\n```js sandbox.config.json hidden\n{\n  \"hardReloadOnChange\": true\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n時計が更新されるたびに、\"Create Story\" が *2 回*追加されることに気づくと、レンダー中にミューテーションが発生していることがわかるでしょう。Strict Mode は、このような問題をより目立たせるために、コンポーネントを 2 回呼び出します。\n\n問題は `StoryTray` 関数が純粋でないことです。受け取った `stories` 配列（props の一部です）に `push` を呼び出すことで、`StoryTray` がレンダーし始める*前に*作成されたオブジェクトをミューテートしてしまっています。これにより、バグや予測困難な動作につながります。\n\n最も単純な修正方法は、受け取った配列を一切いじらず、\"Create Story\" を別にレンダーすることです。\n\n<Sandpack>\n\n```js src/StoryTray.js active\nexport default function StoryTray({ stories }) {\n  return (\n    <ul>\n      {stories.map(story => (\n        <li key={story.id}>\n          {story.label}\n        </li>\n      ))}\n      <li>Create Story</li>\n    </ul>\n  );\n}\n```\n\n```js {expectedErrors: {'react-compiler': [16]}} src/App.js hidden\nimport { useState, useEffect } from 'react';\nimport StoryTray from './StoryTray.js';\n\nconst initialStories = [\n  {id: 0, label: \"Ankit's Story\" },\n  {id: 1, label: \"Taylor's Story\" },\n];\n\nexport default function App() {\n  const [stories, setStories] = useState([...initialStories])\n  const time = useTime();\n\n  // HACK: Prevent the memory from growing forever while you read docs.\n  // We're breaking our own rules here.\n  if (stories.length > 100) {\n    stories.length = 100;\n  }\n\n  return (\n    <div\n      style={{\n        width: '100%',\n        height: '100%',\n        textAlign: 'center',\n      }}\n    >\n      <h2>It is {time.toLocaleTimeString()} now.</h2>\n      <StoryTray stories={stories} />\n    </div>\n  );\n}\n\nfunction useTime() {\n  const [time, setTime] = useState(() => new Date());\n  useEffect(() => {\n    const id = setInterval(() => {\n      setTime(new Date());\n    }, 1000);\n    return () => clearInterval(id);\n  }, []);\n  return time;\n}\n```\n\n```css\nul {\n  margin: 0;\n  list-style-type: none;\n}\n\nli {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  float: left;\n  margin: 5px;\n  margin-bottom: 20px;\n  padding: 5px;\n  width: 70px;\n  height: 100px;\n}\n```\n\n</Sandpack>\n\nあるいは、アイテムを追加する前に、（すでに存在する配列をコピーすることで）*新しい*配列を生成しても構いません。\n\n<Sandpack>\n\n```js src/StoryTray.js active\nexport default function StoryTray({ stories }) {\n  // Copy the array!\n  const storiesToDisplay = stories.slice();\n\n  // Does not affect the original array:\n  storiesToDisplay.push({\n    id: 'create',\n    label: 'Create Story'\n  });\n\n  return (\n    <ul>\n      {storiesToDisplay.map(story => (\n        <li key={story.id}>\n          {story.label}\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```js {expectedErrors: {'react-compiler': [16]}} src/App.js hidden\nimport { useState, useEffect } from 'react';\nimport StoryTray from './StoryTray.js';\n\nconst initialStories = [\n  {id: 0, label: \"Ankit's Story\" },\n  {id: 1, label: \"Taylor's Story\" },\n];\n\nexport default function App() {\n  const [stories, setStories] = useState([...initialStories])\n  const time = useTime();\n\n  // HACK: Prevent the memory from growing forever while you read docs.\n  // We're breaking our own rules here.\n  if (stories.length > 100) {\n    stories.length = 100;\n  }\n\n  return (\n    <div\n      style={{\n        width: '100%',\n        height: '100%',\n        textAlign: 'center',\n      }}\n    >\n      <h2>It is {time.toLocaleTimeString()} now.</h2>\n      <StoryTray stories={stories} />\n    </div>\n  );\n}\n\nfunction useTime() {\n  const [time, setTime] = useState(() => new Date());\n  useEffect(() => {\n    const id = setInterval(() => {\n      setTime(new Date());\n    }, 1000);\n    return () => clearInterval(id);\n  }, []);\n  return time;\n}\n```\n\n```css\nul {\n  margin: 0;\n  list-style-type: none;\n}\n\nli {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  float: left;\n  margin: 5px;\n  margin-bottom: 20px;\n  padding: 5px;\n  width: 70px;\n  height: 100px;\n}\n```\n\n</Sandpack>\n\nこれにより、あなたのミューテーションはローカルなものとなり、レンダー関数が純粋に保たれます。ただしまだ注意が必要です。たとえば、配列の既存のアイテムを変更したい場合、そのアイテム自体も複製する必要があるでしょう。\n\n配列に対する操作のうちどれが配列の書き換えを伴うもので、どれが伴わないものなのか、覚えておくことが有用です。例えば `push`、`pop`、`reverse`、`sort` は元の配列を書き換えてしまいますが、`slice`、`filter`、`map` は新しい配列を作成します。\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/lifecycle-of-reactive-effects.md",
    "content": "---\ntitle: 'リアクティブなエフェクトのライフサイクル'\n---\n\n<Intro>\n\nエフェクトはコンポーネントとは異なるライフサイクルを持ちます。コンポーネントは、マウント、更新、アンマウントを行うことができます。エフェクトは 2 つのことしかできません。同期の開始と、同期の停止です。エフェクトが props や state に依存し、これらが時間と共に変化する場合、このサイクルは繰り返し発生します。React では、エフェクトの依存配列が適切に指定されているかをチェックするリンタルールが提供されています。これにより、エフェクトが最新の props や state と同期された状態を維持することができます。\n\n</Intro>\n\n<YouWillLearn>\n\n- エフェクトのライフサイクルとコンポーネントのライフサイクルの違い\n- 個々のエフェクトを独立して考える方法\n- いつ、そしてなぜエフェクトを再同期する必要があるのか\n- エフェクトの依存配列を決める方法\n- 値がリアクティブであるとはどのような意味か\n- 空の依存配列の意味\n- React のリンタは、どのようにして依存配列が正しいと判断するのか\n- リンタに同意できない時にどうすれば良いか\n\n</YouWillLearn>\n\n## エフェクトのライフサイクル {/*the-lifecycle-of-an-effect*/}\n\nすべての React コンポーネントは同じライフサイクルを持ちます。\n\n- 画面に追加されたとき、コンポーネントは*マウント*されます。\n- （大抵はインタラクションに応じて）新しい props や state を受け取ったとき、コンポーネントは*更新*されます。\n- 画面から削除されたとき、コンポーネントは*アンマウント*されます。\n\n**これは、コンポーネントの考え方としては良いですが、エフェクトの考え方としては*良くありません***。それぞれのエフェクトは、コンポーネントのライフサイクルから独立させて考えましょう。エフェクトは、現在の props や state に[外部システムをどのように同期させるのか](/learn/synchronizing-with-effects)を記述します。コードが変更されれば、同期の頻度も増減するでしょう。\n\nこの点を説明するために、コンポーネントをチャットサーバに接続するエフェクトを考えてみましょう。\n\n```js\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [roomId]);\n  // ...\n}\n```\n\nエフェクトの本体には、**どのように同期を開始するのか**を記述しています。\n\n```js {2-3}\n    // ...\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n    // ...\n```\n\nエフェクトの返り値として表されるクリーンアップ関数には、**どのように同期を停止するのか**を記述しています。\n\n```js {5}\n    // ...\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n    // ...\n```\n\nこれだけを見ると、React はコンポーネントのマウント時に**同期を開始し**、アンマウント時に**同期を停止する**だけのように見えます。しかし、これで終わりではありません！ コンポーネントがマウントされている間、**同期の開始と停止を繰り返し行わなければならない**場合があるからです。\n\nこの挙動が*いつ*発生し、*どのように*制御することができ、そしてそれが*なぜ*必要なのかを見ていきましょう。\n\n<Note>\n\nエフェクトは、クリーンアップ関数を返さないことがあります。[多くの場合は返すべき](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development)ですが、もし返さなかった場合は、空のクリーンアップ関数が返されたとして扱われます。\n\n</Note>\n\n### なぜ複数回の同期が必要なのか {/*why-synchronization-may-need-to-happen-more-than-once*/}\n\n`ChatRoom` コンポーネントが、ユーザがドロップダウンで選択した `roomId` プロパティを受け取ることを考えましょう。ユーザが最初に、`roomId` として `\"general\"` ルームを選択したとします。あなたのアプリは `\"general\"` ルームを表示します。\n\n```js {3}\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId /* \"general\" */ }) {\n  // ...\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n```\n\nこの UI が表示されたあと、React は**同期を開始するために**エフェクトを実行します。これにより、`\"general\"` ルームに通信が接続されます。\n\n```js {3,4}\nfunction ChatRoom({ roomId /* \"general\" */ }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId); // Connects to the \"general\" room\n    connection.connect();\n    return () => {\n      connection.disconnect(); // Disconnects from the \"general\" room\n    };\n  }, [roomId]);\n  // ...\n```\n\nここまでは順調です。\n\nこの後、ユーザがドロップダウンで違うルーム（例えば `\"travel\"` ルーム）を選択したとします。まず、React は UI を更新します。\n\n```js {1}\nfunction ChatRoom({ roomId /* \"travel\" */ }) {\n  // ...\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n```\n\n次に何が起こるか考えてみましょう。ユーザには、UI 上では `\"travel\"` ルームが選択されているように見えています。しかし、直近に実行されたエフェクトは、未だ `\"general\"` ルームに接続しています。**`roomId` プロパティが変化してしまったため、エフェクトが行なっていたこと（`\"general\"` ルームへの接続）が UI と一致しなくなってしまいました。**\n\nこの時点で、あなたは React に以下の 2 つの処理を実行してほしいはずです。\n\n1. 古い `roomId` での同期を停止する（`\"general\"` ルームとの接続を切断する）\n2. 新しい `roomId` での同期を開始する（`\"travel\"` ルームとの接続を開始する）\n\n**ラッキーなことに、これらの処理を行う方法は既に学んでいます！** エフェクトの本体には「どのように同期を開始するのか」を、クリーンアップ関数には「どのように同期を停止するのか」を記述するのでした。React はエフェクトを、正しい順序、かつ正しい props と state で呼び出します。では、具体的にどうなるのか見てみましょう。\n\n### どのようにしてエフェクトの再同期が行われるのか {/*how-react-re-synchronizes-your-effect*/}\n\n`ChatRoom` コンポーネントが、`roomId` プロパティとして新しい値を受け取ったところを思い出してください。プロパティの値が、`\"general\"` から `\"travel\"` に変化しました。React は、別のルームに再接続するため、エフェクトを再同期する必要があります。\n\nまず React は、**同期を停止する**ために `\"general\"` ルームに接続したあとにエフェクトが返したクリーンアップ関数を呼び出します。`roomId` は `\"general\"` であったので、クリーンアップ関数は `\"general\"` ルームの通信を切断します。\n\n```js {6}\nfunction ChatRoom({ roomId /* \"general\" */ }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId); // Connects to the \"general\" room\n    connection.connect();\n    return () => {\n      connection.disconnect(); // Disconnects from the \"general\" room\n    };\n    // ...\n```\n\n次に React は、レンダー中に提供されたエフェクトを実行します。このとき、`roomId` は `\"travel\"` なので、`\"travel\"` ルームへの**同期が開始されます**。（最終的に、そのエフェクトのクリーンアップ関数が呼び出されるまで、同期が続きます。）\n\n```js {3,4}\nfunction ChatRoom({ roomId /* \"travel\" */ }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId); // Connects to the \"travel\" room\n    connection.connect();\n    // ...\n```\n\nこれで、ユーザが UI で選択したルームに正しく接続することができました。一件落着！\n\n`roomId` が変化しコンポーネントが再レンダーされるたびに、エフェクトは再同期を行います。例として、ユーザが `roomId` を `\"travel\"` から `\"music\"` に変更したとしましょう。React は、クリーンアップ関数を呼び出すことで、再度エフェクトの**同期を停止**（`\"travel\"` ルームを切断）します。次に、新しい `roomId` プロパティの値でエフェクトの本体を実行し、**同期を開始**（`\"music\"` ルームに接続）します。\n\n最後に、ユーザが別の画面に遷移すれば、`ChatRoom` コンポーネントはアンマウントされます。これ以上接続を維持する必要はないので、React は最後にもう一度**同期を停止し**、`\"music\"` ルームとの通信を切断します。\n\n### エフェクトの視点で考える {/*thinking-from-the-effects-perspective*/}\n\n`ChatRoom` コンポーネントの視点で、起こったことを振り返りましょう。\n\n1. `roomId` が `\"general\"` にセットされた状態で `ChatRoom` がマウントされる\n1. `roomId` が `\"travel\"` にセットされた状態で `ChatRoom` が更新される\n1. `roomId` が `\"music\"` にセットされた状態で `ChatRoom` が更新される\n1. `ChatRoom` がアンマウントされる\n\nこの、コンポーネントのライフサイクルの各ポイントで、エフェクトは以下の処理を行いました。\n\n1. エフェクトが `\"general\"` ルームに接続する\n2. エフェクトが `\"general\"` ルームを切断し、`\"travel\"` ルームに接続する\n3. エフェクトが `\"travel\"` ルームを切断し、`\"music\"` ルームに接続する\n4. エフェクトが `\"music\"` ルームを切断する\n\nでは、エフェクトの視点でどのようなことが発生したのか考えましょう。\n\n```js\n  useEffect(() => {\n    // エフェクトが、roomId で指定されたルームに接続する\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      // ...切断されるまで\n      connection.disconnect();\n    };\n  }, [roomId]);\n```\n\nこのコードの構造から、何が発生したのかを、複数に区切られた時間の連続として理解できるかもしれません。\n\n1. エフェクトが、`\"general\"` に接続する（切断されるまで）\n1. エフェクトが、`\"travel\"` に接続する（切断されるまで）\n1. エフェクトが、`\"music\"` に接続する（切断されるまで）\n\n先ほどはコンポーネントの視点で考えていました。コンポーネントの視点から見ると、エフェクトは、\"レンダー直後\" や \"アンマウント直前\" のように特定のタイミングで発生する \"コールバック関数\" や \"ライフサイクル中のイベント\" であると考えたくなります。しかし、このような考え方はすぐにややこしくなるため、避けた方が無難です。\n\n**その代わりに、エフェクトの開始/終了という 1 サイクルのみにフォーカスしてください。コンポーネントがマウント中なのか、更新中なのか、はたまたアンマウント中なのかは問題ではありません。どのように同期を開始し、どのように同期を終了するのか、これを記述すれば良いのです。このことを意識するだけで、開始・終了が何度も繰り返されても、柔軟に対応できるエフェクトとなります。**\n\nもしかすると、JSX を作成するレンダーロジックを書くときのことを思い出したかもしれません。このときも、コンポーネントがマウント中なのか、更新中なのかは意識しませんでした。あなたは画面に表示されるものを記述し、[残りは React がやってくれるのです](/learn/reacting-to-input-with-state)。\n\n### エフェクトが再同期できることを React はどのように確認するのか {/*how-react-verifies-that-your-effect-can-re-synchronize*/}\n\nこちらは、実際に動かして試すことができるサンプルです。\"Open chat\" を押して `ChatRoom` コンポーネントをマウントしてみましょう。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Close chat' : 'Open chat'}\n      </button>\n      {show && <hr />}\n      {show && <ChatRoom roomId={roomId} />}\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\nコンポーネントが初めてマウントされたときに、3 つのログが表示されることに注目してください。\n\n1. `✅ Connecting to \"general\" room at https://localhost:1234...` *(development-only)*\n1. `❌ Disconnected from \"general\" room at https://localhost:1234.` *(development-only)*\n1. `✅ Connecting to \"general\" room at https://localhost:1234...`\n\n最初の 2 つのログは開発時のみ表示されます。開発時には、React は常に各コンポーネントを 1 度再マウントします。\n\n**開発時には、React はエフェクトを即座に再同期させて、エフェクトの再同期が正しく行われることを確認します**。この動作は、ドアの鍵が正しくかかるか確認するために、ドアを 1 度余分に開け閉めしてみることに似ています。React は、[クリーンアップ関数が正しく実装されているか](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development)を確認するために、開発時にエフェクトを 1 回余分に開始・停止します。\n\n実環境でエフェクトの再同期が発生する主な理由は、エフェクトが利用するデータが変更されることです。上記のサンドボックスで、チャットルームの選択を変更してみてください。`roomId` が変更されると、エフェクトが再同期されることがわかります。\n\nしかし、再同期が必要となるより珍しいケースもあります。例えば、チャットが開いている間に、サンドボックス上の `serverUrl` を編集してみてください。コードの編集に応じて、エフェクトが再同期されることがわかります。将来的には、React は再同期に依存する機能を追加するかもしれません。\n\n### React がエフェクトの再同期が必要であることを知る方法 {/*how-react-knows-that-it-needs-to-re-synchronize-the-effect*/}\n\n`roomId` が変更された後に、React はなぜエフェクトを再同期する必要があることを知ったのでしょうか。それは、`roomId` を[依存配列](/learn/synchronizing-with-effects#step-2-specify-the-effect-dependencies)に含めることで、React にそのコードが `roomId` に依存していることを伝えたからです。\n\n```js {1,3,8}\nfunction ChatRoom({ roomId }) { // roomId は時間の経過とともに変化する可能性がある\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId); // このエフェクトは `roomId` を利用している\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [roomId]); // つまり、このエフェクトは roomId に \"依存\" している\n  // ...\n```\n\n仕組みは以下のようになっています。\n\n1. `roomId` はプロパティである。これは、`roomId` が時間の経過とともに変化する可能性があるということです。\n2. エフェクトが `roomId` を利用する。(これは、エフェクトのロジックが、後で変更される可能性のある値に依存していることを意味しています。)\n3. そのため、エフェクトの依存配列に `roomId` を指定する。（こうすることで、`roomId` が変更されたときに再同期することができます。） \n\nコンポーネントが再レンダーされるたびに、React は渡された依存配列を確認します。配列内の値のいずれかが、前回のレンダー時に渡された配列の同じ場所の値と異なる場合、React はエフェクトを再同期します。\n\n例えば、初回のレンダー時に `[\"general\"]` を渡し、次のレンダー時に `[\"travel\"]` を渡したとします。React は `\"general\"` と `\"travel\"` を比較します（[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) で比較されます）。異なる値が存在するため、React はエフェクトを再同期します。一方、コンポーネントが再レンダーされたが `roomId` が変更されていない場合、エフェクトは同じルームに接続されたままになります。\n\n### 1 つのエフェクトは独立した 1 つの同期の処理を表す {/*each-effect-represents-a-separate-synchronization-process*/}\n\n実装済みのエフェクトと同時に実行するというだけの理由で、関係のないロジックを同じエフェクトに追加しないでください。例えば、ユーザがルームを訪れたときにアナリティクスイベントを送信したいとします。既に `roomId` に依存するエフェクトがあるため、そこにアナリティクスの呼び出しを追加したくなるかもしれません。\n\n```js {3}\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    logVisit(roomId);\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [roomId]);\n  // ...\n}\n```\n\nしかし後になってこのエフェクトに、接続の再確立を必要とするような何か別の依存値を追加する必要が出てきたとします。このエフェクトが再同期されると、ルームが変わっていないのに `logVisit(roomId)` が再度呼び出されてしまいます。これは意図したものではありません。訪問の記録は、接続とは**別の処理**です。別のエフェクトとして記述してください。\n\n```js {2-4}\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    logVisit(roomId);\n  }, [roomId]);\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    // ...\n  }, [roomId]);\n  // ...\n}\n```\n\n**コード内の 1 つのエフェクトは、1 つの独立した同期の処理を表すべきです。**\n\n上記の例では、1 つのエフェクトを削除しても、他のエフェクトのロジックは壊れません。これは、それらが異なるものを同期していることを示しており、分割するのが理にかなっているということです。逆に、1 つのロジックを別々のエフェクトに分割してしまうと、コードは一見「きれい」に見えるかもしれませんが、[メンテナンスは困難になるでしょう](/learn/you-might-not-need-an-effect#chains-of-computations)。そのため、コードがきれいに見えるかどうかではなく、処理が独立しているか同じかを考える必要があります。\n\n## エフェクトはリアクティブ (reactive) な値に \"反応\" する {/*effects-react-to-reactive-values*/}\n\n以下の例では、エフェクトが 2 つの変数 (`serverUrl` と `roomId`) を利用していますが、依存配列には `roomId` のみが指定されています。\n\n```js {5,10}\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [roomId]);\n  // ...\n}\n```\n\nなぜ `serverUrl` は依存配列に追加しなくて良いのでしょうか？\n\nこれは、再レンダーが起こっても、決して `serverUrl` が変化することはないからです。どのような理由で何度再レンダーが起こっても、いつも同じ値です。したがって、依存配列に追加しても意味がありません。結局のところ、指定する依存値は、時間によって変化して初めて意味があるのです！\n\n一方、`roomId` は再レンダー時に異なる値になる可能性があります。**コンポーネント内で宣言された props、state、その他の値は、レンダー時に計算され、React のデータフローに含まれるため、*リアクティブ*です。**\n\nもし `serverUrl` が state 変数だった場合、リアクティブとなります。リアクティブな値は、依存配列に含める必要があります。\n\n```js {2,5,10}\nfunction ChatRoom({ roomId }) { // props は時間経過とともに変化し得る\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // state は時間経過とともに変化し得る\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId); // エフェクトは props と state を利用している\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [roomId, serverUrl]); // つまり、エフェクトは props と state に \"依存\" している\n  // ...\n}\n```\n\n`serverUrl` を依存配列に含めることで、`serverUrl` が変更された後に再同期されることを保証します。\n\nチャットルームの選択を変更したり、サーバの URL を編集してみてください。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nfunction ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId, serverUrl]);\n\n  return (\n    <>\n      <label>\n        Server URL:{' '}\n        <input\n          value={serverUrl}\n          onChange={e => setServerUrl(e.target.value)}\n        />\n      </label>\n      <h1>Welcome to the {roomId} room!</h1>\n    </>\n  );\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom roomId={roomId} />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n`roomId` や `serverUrl` のようなリアクティブな値を変更するたびに、エフェクトはチャットサーバに再接続します。\n\n### 依存配列が空のエフェクトは何を意味するのか {/*what-an-effect-with-empty-dependencies-means*/}\n\nもし `serverUrl` や `roomId` をコンポーネント外に移動した場合、どうなるでしょうか？\n\n```js {1,2}\nconst serverUrl = 'https://localhost:1234';\nconst roomId = 'general';\n\nfunction ChatRoom() {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, []); // ✅ All dependencies declared\n  // ...\n}\n```\n\n現在のエフェクトのコードは、リアクティブな値を利用していません。そのため、依存配列は空 (`[]`) になります。\n\n空の依存配列 (`[]`) をコンポーネントの視点から考えると、このエフェクトは、コンポーネントのマウント時にチャットルームに接続し、アンマウント時に切断することを意味しています。（開発時は、ロジックのストレステストのために、[余計に一度再同期される](#how-react-verifies-that-your-effect-can-re-synchronize)ことをお忘れなく）\n\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\nconst roomId = 'general';\n\nfunction ChatRoom() {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, []);\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Close chat' : 'Open chat'}\n      </button>\n      {show && <hr />}\n      {show && <ChatRoom />}\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\nしかし、[エフェクトの視点から考えると](#thinking-from-the-effects-perspective)、マウントとアンマウントについて考える必要は全くありません。重要なのは、エフェクトが同期の開始と停止のときに何を行うかを指定したことです。今回は、リアクティブな依存値はありません。しかし、将来的にユーザに `roomId` や `serverUrl` を変更してもらいたくなった場合（これらの値がリアクティブになるでしょう）でも、エフェクトのコードを変更する必要はありません。依存配列にこれらの値を追加するだけです。\n\n### コンポーネント本体で宣言された変数はすべてリアクティブである {/*all-variables-declared-in-the-component-body-are-reactive*/}\n\nリアクティブな値は、props や state だけではありません。これらの値から導出される値もまた、リアクティブな値となります。props や state が変更されるとコンポーネントは再レンダーされ、導出される値も変化します。これが、コンポーネント本体で宣言された変数で、エフェクトが利用するものは、すべてそのエフェクトの依存配列に含まなければならない理由です。\n\nユーザがドロップダウンでチャットサーバを選択できますが、設定でデフォルトサーバを指定することもできるとしましょう。設定の状態を表す state をすでに[コンテクスト](/learn/scaling-up-with-reducer-and-context)に入れていると仮定し、そのコンテクストから `settings` を読み取ります。そして、props から得られる選択されたサーバと、デフォルトサーバに基づいて `serverUrl` を計算します。\n\n```js {3,5,10}\nfunction ChatRoom({ roomId, selectedServerUrl }) { // roomId はリアクティブ\n  const settings = useContext(SettingsContext); // settings はリアクティブ\n  const serverUrl = selectedServerUrl ?? settings.defaultServerUrl; // serverUrl はリアクティブ\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId); // エフェクトは roomId と serverUrl を利用している\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [roomId, serverUrl]); // そのため、どちらかが変更された場合は再同期が必要！\n  // ...\n}\n```\n\nこの例では、`serverUrl` は props でも state でもありません。レンダー時に計算される通常の変数です。しかし、レンダー時に計算されるがゆえに、再レンダーによって変化する可能性があります。これが、リアクティブな値となる理由です。\n\n**コンポーネント内のすべての値（props、state、コンポーネント本体の変数を含む）はリアクティブです。リアクティブな値は再レンダー時に変更される可能性があるため、エフェクトの依存配列に含める必要があります。**\n\nつまり、エフェクトはコンポーネント本体のすべての値に \"反応 (react)\" するのです。\n\n<DeepDive>\n\n#### グローバルな値やミュータブルな値は依存配列に含めるべき？ {/*can-global-or-mutable-values-be-dependencies*/}\n\nグローバル変数を含むミュータブル（書き換え可能）な値は、リアクティブではありません。\n\n**[`location.pathname`](https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname) のようなミュータブルな値を依存配列に含めることはできません**。なにしろミュータブルなので、React のレンダーデータフローの外部で、いつでも書き換わってしまう可能性があります。外部で変更されても、コンポーネントの再レンダーはトリガされません。したがって、依存配列に含めたとしても、その値が変更されたときにエフェクトの再同期が必要だと React には*伝わりません*。また、レンダー中（依存関係を計算するとき）にミュータブルなデータを読み取ることは、[レンダーの純粋性](/learn/keeping-components-pure)のルールを破っています。代替策として、外部のミュータブルな値の読み取りやリッスンをするには [`useSyncExternalStore`](/learn/you-might-not-need-an-effect#subscribing-to-an-external-store) を利用しましょう。\n\n**[`ref.current`](/reference/react/useRef#reference) のようなミュータブルな値や、この値から導出される値も依存配列に含めることはできません。**`useRef` によって返される ref オブジェクト自体は依存配列に含めることができますが、その `current` プロパティは意図的にミュータブルとなっています。これにより、[再レンダーをトリガせずに値を追跡し続けることができます](/learn/referencing-values-with-refs)。しかし、`current` を変更しても再レンダーはトリガされないため、リアクティブな値とはいえず、その値が変更されたときにエフェクトを再実行することは React には伝わりません。\n\nこのページの後半で学びますが、リンタがこれらの問題を自動でチェックしてくれます。\n\n</DeepDive>\n\n### React はすべてのリアクティブな値が依存配列に含まれることをチェックする {/*react-verifies-that-you-specified-every-reactive-value-as-a-dependency*/}\n\nリンタが [React 向けに設定されている](/learn/editor-setup#linting)場合、エフェクトで利用されているすべてのリアクティブな値が、その依存値として宣言されているかどうかをチェックします。例えば、以下のコードはリンタエラーとなります。なぜなら、`roomId` と `serverUrl` はどちらもリアクティブだからです。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nfunction ChatRoom({ roomId }) { // roomId is reactive\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl is reactive\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, []); // <-- Something's wrong here!\n\n  return (\n    <>\n      <label>\n        Server URL:{' '}\n        <input\n          value={serverUrl}\n          onChange={e => setServerUrl(e.target.value)}\n        />\n      </label>\n      <h1>Welcome to the {roomId} room!</h1>\n    </>\n  );\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom roomId={roomId} />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\nこのエラーは React のエラーのように見えますが、実際にはあなたのコードのバグを指摘してくれています。`roomId` と `serverUrl` は時間経過とともに変化する可能性がありますが、それらが変更されたときにエフェクトを再同期するのを忘れています。ユーザが UI で異なる値を選択しても、最初の `roomId` と `serverUrl` に接続し続けてしまいます。\n\nバグを修正するには、リンタの提案に従って、エフェクトの依存配列に `roomId` と `serverUrl` を追加します。\n\n```js {9}\nfunction ChatRoom({ roomId }) { // roomId is reactive\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl is reactive\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [serverUrl, roomId]); // ✅ All dependencies declared\n  // ...\n}\n```\n\n上記のサンドボックスでこの修正を試してみてください。リンタエラーがなくなり、チャットが適切に再接続されることを確認してください。\n\n<Note>\n\nコンポーネント内で宣言された変数でも、その値が決して変更されないことを React が*知っている*ケースがあります。例えば、`useState` から返される [`set` 関数](/reference/react/useState#setstate) や、[`useRef`](/reference/react/useRef) から返される ref オブジェクトは、*安定*、すなわち、再レンダー時に変更されないことが保証されています。安定な値はリアクティブではないため、依存配列から省略することができます。なお、これらの値は変更されないため、配列に加えてしまっても問題はありません。\n\n</Note>\n\n### 再同期を避けたい場合の方法 {/*what-to-do-when-you-dont-want-to-re-synchronize*/}\n\n前の例では、`roomId` と `serverUrl` を依存配列に追加することで、リンタエラーを修正しました。\n\n**しかし、その代わりに、これらの値がリアクティブではない、つまり再レンダーされても*変更し得ない*ことを、リンタに「証明」することもできます**。例えば、`serverUrl` と `roomId` がレンダーに依存せず、常に同じ値を持つ場合、コンポーネントの外に移動することができます。これで、依存配列に含める必要はなくなりました。\n\n```js {1,2,11}\nconst serverUrl = 'https://localhost:1234'; // serverUrl is not reactive\nconst roomId = 'general'; // roomId is not reactive\n\nfunction ChatRoom() {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, []); // ✅ All dependencies declared\n  // ...\n}\n```\n\nまた、これらの値を*エフェクトの内部*に移動することもできます。レンダー時の計算から外れるため、リアクティブな値とはなりません。\n\n```js {3,4,10}\nfunction ChatRoom() {\n  useEffect(() => {\n    const serverUrl = 'https://localhost:1234'; // serverUrl is not reactive\n    const roomId = 'general'; // roomId is not reactive\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, []); // ✅ All dependencies declared\n  // ...\n}\n```\n\n**エフェクトはリアクティブなコードブロックです**。エフェクトは、その中で読み取っている値が変更されたときに再同期します。イベントハンドラは 1 回のインタラクションにつき 1 回しか実行されませんが、エフェクトは同期が必要なときにいつでも実行されます。\n\n**依存配列を「選ぶ」ことはできません**。依存配列には、エフェクトで読み取るすべての[リアクティブな値](#all-variables-declared-in-the-component-body-are-reactive)を含める必要があります。リンタがこれを強制します。これにより、無限ループや、エフェクトの再同期が頻発してしまうことがありますが、リンタを抑制してこれらの問題を解決としないでください！ 代わりに、以下のことを試してみてください。\n\n* **エフェクトが 1 つの独立した同期の処理を表していることを確認してください**。もし、エフェクトが何も同期していない場合、[エフェクトは不要かもしれません](/learn/you-might-not-need-an-effect)。複数の独立したものを同期している場合は、[分割してください](#each-effect-represents-a-separate-synchronization-process)。\n\n* **props や state に反応せずに、最新の値を読み取り、エフェクトを再同期したい場合**、エフェクトをリアクティブな部分（エフェクト内に残す）と、非リアクティブな部分（いわゆる*エフェクトイベント*に抽出する）に分割することができます。詳しくは、[エフェクトからイベントを分離する](/learn/separating-events-from-effects)を参照してください。\n\n* **オブジェクトや関数を依存配列に含めないようにしてください**。レンダー中に作成したオブジェクトや関数をエフェクトから読み取ると、これらの値は毎回異なるものになります。これにより、エフェクトが毎回再同期されてしまいます。詳しくは、[エフェクトから不要な依存関係を削除する](/learn/removing-effect-dependencies)を参照してください。\n\n<Pitfall>\n\nリンタはあなたの助けになりますが、その力には限界があります。リンタは、依存配列が*間違っている*場合しか検出できず、それぞれのケースを解決する*最善の方法*を提案することはできません。例えば、リンタの提案に従って依存値を追加すると、ループが発生してしまったとしましょう。このような場合でも、リンタを無視すべきではありません。エフェクトの内部（または外部）のコードを変更し、追加した値がリアクティブにならないようにして、依存値になる*必要がない*ようにすべきです。\n\n既存のコードベースがある場合、次のようにリンタを抑制するエフェクトがいくつかあるかもしれません。\n\n```js {3-4}\nuseEffect(() => {\n  // ...\n  // 🔴 Avoid suppressing the linter like this:\n  // eslint-ignore-next-line react-hooks/exhaustive-deps\n}, []);\n```\n\n以降のページ（[こちら](/learn/separating-events-from-effects)と[こちら](/learn/removing-effect-dependencies)）では、ルールを破らずにこれらのコードを修正する方法を学びます。必ず修正する価値があります！\n\n</Pitfall>\n\n<Recap>\n\n- コンポーネントはマウント、更新、アンマウントを行うことができる。\n- それぞれのエフェクトは、周囲のコンポーネントとは別のライフサイクルを持つ。\n- それぞれのエフェクトは、*開始*と*停止*が可能な独立した同期プロセスを表す。\n- エフェクトを読み書きするときは、コンポーネントの視点（どのようにマウント、更新、アンマウントが行われるか）ではなく、それぞれのエフェクトの視点（どのように同期が開始および停止されるか）で考える。\n- コンポーネント本体で宣言された値は \"リアクティブ\" である。\n- リアクティブな値は時間とともに変化する可能性があるため、エフェクトを再同期する必要がある。\n- リンタは、エフェクト内で使用されているすべてのリアクティブな値が依存配列に含まれることを確認する。\n- リンタが検出するエラーは、すべて妥当なものである。ルールを破らずにコードを修正する方法が必ず存在する。\n\n</Recap>\n\n<Challenges>\n\n#### キー入力による再接続を防ぐ {/*fix-reconnecting-on-every-keystroke*/}\n\nこの例では、`ChatRoom` コンポーネントは、マウントされたときにチャットルームに接続し、アンマウントされたときにルームから切断し、別のチャットルームが選択されたときに再接続します。これは正しいので、この動作を維持する必要があります。\n\nしかし、問題があります。下部のメッセージボックスに入力するたびに、`ChatRoom` はチャットに*再接続*してしまいます（コンソールを 1 度クリアしてから入力すると分かりやすいです）。問題を修正し、これを防ぎましょう。\n\n<Hint>\n\nこのエフェクトに依存配列を追加する必要があるかもしれません。どのような依存配列を指定すればよいでしょうか？\n\n</Hint>\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  });\n\n  return (\n    <>\n      <h1>Welcome to the {roomId} room!</h1>\n      <input\n        value={message}\n        onChange={e => setMessage(e.target.value)}\n      />\n    </>\n  );\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom roomId={roomId} />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n<Solution>\n\nこのエフェクトには依存配列がありませんでした。そのため、再レンダーのたびに再同期されてしまいます。まず依存配列を追加しましょう。次に、エフェクトで利用されているリアクティブな値を、すべて依存配列に追加します。例えば、`roomId` は（props なので）リアクティブです。したがって、配列に含める必要があります。これにより、ユーザが別のルームを選択したときに、チャットが再接続されることが保証されます。一方、`serverUrl` はコンポーネントの外で定義されているので、依存配列に含める必要はありません。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n\n  return (\n    <>\n      <h1>Welcome to the {roomId} room!</h1>\n      <input\n        value={message}\n        onChange={e => setMessage(e.target.value)}\n      />\n    </>\n  );\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom roomId={roomId} />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### 同期の有無を切り替える {/*switch-synchronization-on-and-off*/}\n\nこの例では、エフェクトで window の [`pointermove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointermove_event) イベントをリッスンし、画面上のピンクのドットを移動させています。プレビューエリアにカーソルを合わせてみてください（モバイルデバイスの場合は画面をタッチしてください）。ピンクのドットが動きに合わせて移動することがわかります。\n\nチェックボックスもあります。チェックボックスをオンにすると、`canMove` state 変数が切り替わります。しかし、この state 変数はまだコード内で利用されていません。`canMove` が `false` のとき（つまり、チェックボックスがオフのとき）ドットが動かないようにコードを変更してみましょう。また、チェックボックスをオンに戻すと（`canMove` が `true` になり）ドットが再び動き始めるようにしましょう。言い換えると、ドットが動けるかどうかは、チェックボックスがチェックされているかどうかに同期します。\n\n<Hint>\n\nエフェクトは条件分岐の中では利用できません。しかし、エフェクトの中で条件分岐を利用することはできます！\n\n</Hint>\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function App() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n  const [canMove, setCanMove] = useState(true);\n\n  useEffect(() => {\n    function handleMove(e) {\n      setPosition({ x: e.clientX, y: e.clientY });\n    }\n    window.addEventListener('pointermove', handleMove);\n    return () => window.removeEventListener('pointermove', handleMove);\n  }, []);\n\n  return (\n    <>\n      <label>\n        <input type=\"checkbox\"\n          checked={canMove}\n          onChange={e => setCanMove(e.target.checked)} \n        />\n        The dot is allowed to move\n      </label>\n      <hr />\n      <div style={{\n        position: 'absolute',\n        backgroundColor: 'pink',\n        borderRadius: '50%',\n        opacity: 0.6,\n        transform: `translate(${position.x}px, ${position.y}px)`,\n        pointerEvents: 'none',\n        left: -20,\n        top: -20,\n        width: 40,\n        height: 40,\n      }} />\n    </>\n  );\n}\n```\n\n```css\nbody {\n  height: 200px;\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n1 つ目の答えは、`setPosition` を `if (canMove) { ... }` の条件分岐でラップすることです。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function App() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n  const [canMove, setCanMove] = useState(true);\n\n  useEffect(() => {\n    function handleMove(e) {\n      if (canMove) {\n        setPosition({ x: e.clientX, y: e.clientY });\n      }\n    }\n    window.addEventListener('pointermove', handleMove);\n    return () => window.removeEventListener('pointermove', handleMove);\n  }, [canMove]);\n\n  return (\n    <>\n      <label>\n        <input type=\"checkbox\"\n          checked={canMove}\n          onChange={e => setCanMove(e.target.checked)} \n        />\n        The dot is allowed to move\n      </label>\n      <hr />\n      <div style={{\n        position: 'absolute',\n        backgroundColor: 'pink',\n        borderRadius: '50%',\n        opacity: 0.6,\n        transform: `translate(${position.x}px, ${position.y}px)`,\n        pointerEvents: 'none',\n        left: -20,\n        top: -20,\n        width: 40,\n        height: 40,\n      }} />\n    </>\n  );\n}\n```\n\n```css\nbody {\n  height: 200px;\n}\n```\n\n</Sandpack>\n\n2 つ目の答えは、*イベントをリッスンする*ロジック自体を `if (canMove) { ... }` の条件分岐でラップすることです。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function App() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n  const [canMove, setCanMove] = useState(true);\n\n  useEffect(() => {\n    function handleMove(e) {\n      setPosition({ x: e.clientX, y: e.clientY });\n    }\n    if (canMove) {\n      window.addEventListener('pointermove', handleMove);\n      return () => window.removeEventListener('pointermove', handleMove);\n    }\n  }, [canMove]);\n\n  return (\n    <>\n      <label>\n        <input type=\"checkbox\"\n          checked={canMove}\n          onChange={e => setCanMove(e.target.checked)} \n        />\n        The dot is allowed to move\n      </label>\n      <hr />\n      <div style={{\n        position: 'absolute',\n        backgroundColor: 'pink',\n        borderRadius: '50%',\n        opacity: 0.6,\n        transform: `translate(${position.x}px, ${position.y}px)`,\n        pointerEvents: 'none',\n        left: -20,\n        top: -20,\n        width: 40,\n        height: 40,\n      }} />\n    </>\n  );\n}\n```\n\n```css\nbody {\n  height: 200px;\n}\n```\n\n</Sandpack>\n\nどちらの場合でも、`canMove` はエフェクト内で読み取るリアクティブな変数です。そのため、エフェクトの依存配列に `canMove` を指定する必要があります。これにより、エフェクトは、値が変更されるたびに再同期されます。\n\n</Solution>\n\n#### 更新前の値が残るバグを調査 {/*investigate-a-stale-value-bug*/}\n\nこの例では、チェックボックスがオンのときにピンクのドットが動き、オフのときには停止している必要があります。このロジックはすでに実装されています。`handleMove` イベントハンドラは `canMove` state 変数をチェックします。\n\nしかしどういうわけか、`handleMove` 内の `canMove` state 変数は \"古い\" ようです。チェックボックスをオフにしても、常に `true` となっています。なぜこうなるのでしょうか？ コードの間違いを見つけて修正しましょう。\n\n<Hint>\n\nリンタルールが抑制されているのを見つけたら、抑制を解除してください！ これが誤りの多い場所です。\n\n</Hint>\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [16]}}\nimport { useState, useEffect } from 'react';\n\nexport default function App() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n  const [canMove, setCanMove] = useState(true);\n\n  function handleMove(e) {\n    if (canMove) {\n      setPosition({ x: e.clientX, y: e.clientY });\n    }\n  }\n\n  useEffect(() => {\n    window.addEventListener('pointermove', handleMove);\n    return () => window.removeEventListener('pointermove', handleMove);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  return (\n    <>\n      <label>\n        <input type=\"checkbox\"\n          checked={canMove}\n          onChange={e => setCanMove(e.target.checked)} \n        />\n        The dot is allowed to move\n      </label>\n      <hr />\n      <div style={{\n        position: 'absolute',\n        backgroundColor: 'pink',\n        borderRadius: '50%',\n        opacity: 0.6,\n        transform: `translate(${position.x}px, ${position.y}px)`,\n        pointerEvents: 'none',\n        left: -20,\n        top: -20,\n        width: 40,\n        height: 40,\n      }} />\n    </>\n  );\n}\n```\n\n```css\nbody {\n  height: 200px;\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n元のコードの問題点は、依存配列のリンタを抑制していることです。抑制を解除すると、このエフェクトが `handleMove` 関数に依存していることがわかります。これは確かに妥当な指摘です。なぜなら、`handleMove` はコンポーネント本体で宣言されているため、リアクティブな値となるからです。リアクティブな値はすべて依存関係として指定する必要があります。そうしないと、時間の経過とともに値が古くなる可能性があります！\n\n元のコードの作者は、このエフェクトがリアクティブな値に依存していない (`[]`) と言って React に「嘘をついた」のです。そのため、React は `canMove` が変更された後（`handleMove` も変更された後）にエフェクトを再同期しませんでした。React がエフェクトを再同期しなかったため、リスナとしてアタッチされた `handleMove` は、初回レンダー時に作成された `handleMove` 関数のままです。初回レンダー時には `canMove` が `true` であったため、この `handleMove` は常にその値を参照することになります。\n\n**リンタを抑制しないようにすれば、古い値が残る問題は発生しません**。このバグを修正する方法はいくつかありますが、まずはリンタの抑制を解除してください。その後、リントエラーを修正するためにコードを変更しましょう。\n\nエフェクトの依存配列を `[handleMove]` のように変更しても構いませんが、`handleMove` はどうせ毎レンダーで新しく定義される関数なので、依存配列自体をまるごと削除しても良いでしょう。これでエフェクトは再レンダーのたびに再同期の処理を行うようになります。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function App() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n  const [canMove, setCanMove] = useState(true);\n\n  function handleMove(e) {\n    if (canMove) {\n      setPosition({ x: e.clientX, y: e.clientY });\n    }\n  }\n\n  useEffect(() => {\n    window.addEventListener('pointermove', handleMove);\n    return () => window.removeEventListener('pointermove', handleMove);\n  });\n\n  return (\n    <>\n      <label>\n        <input type=\"checkbox\"\n          checked={canMove}\n          onChange={e => setCanMove(e.target.checked)} \n        />\n        The dot is allowed to move\n      </label>\n      <hr />\n      <div style={{\n        position: 'absolute',\n        backgroundColor: 'pink',\n        borderRadius: '50%',\n        opacity: 0.6,\n        transform: `translate(${position.x}px, ${position.y}px)`,\n        pointerEvents: 'none',\n        left: -20,\n        top: -20,\n        width: 40,\n        height: 40,\n      }} />\n    </>\n  );\n}\n```\n\n```css\nbody {\n  height: 200px;\n}\n```\n\n</Sandpack>\n\nこのソリューションは問題なく機能しますが、理想的ではありません。エフェクト内に `console.log('Resubscribing')` を入れてみると、再レンダーのたびにリスナの再登録が発生していることがわかります。再登録は高速に動作するものの、それほど頻繁に行わないようにした方が望ましいでしょう。\n\nより良い修正方法は、`handleMove` 関数をエフェクトの*内部*に移動することです。そうすると、`handleMove` はリアクティブな値ではなくなり、エフェクトは `handleMove` 関数に依存しなくなります。代わりに、エフェクトの内部で利用する `canMove` に依存することになります。これは、あなたが望んでいた動作に一致しています。なぜなら、エフェクトが `canMove` の値と同期するようになるからです。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function App() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n  const [canMove, setCanMove] = useState(true);\n\n  useEffect(() => {\n    function handleMove(e) {\n      if (canMove) {\n        setPosition({ x: e.clientX, y: e.clientY });\n      }\n    }\n\n    window.addEventListener('pointermove', handleMove);\n    return () => window.removeEventListener('pointermove', handleMove);\n  }, [canMove]);\n\n  return (\n    <>\n      <label>\n        <input type=\"checkbox\"\n          checked={canMove}\n          onChange={e => setCanMove(e.target.checked)} \n        />\n        The dot is allowed to move\n      </label>\n      <hr />\n      <div style={{\n        position: 'absolute',\n        backgroundColor: 'pink',\n        borderRadius: '50%',\n        opacity: 0.6,\n        transform: `translate(${position.x}px, ${position.y}px)`,\n        pointerEvents: 'none',\n        left: -20,\n        top: -20,\n        width: 40,\n        height: 40,\n      }} />\n    </>\n  );\n}\n```\n\n```css\nbody {\n  height: 200px;\n}\n```\n\n</Sandpack>\n\nエフェクト本体に `console.log('Resubscribing')` を追加してみてください。チェックボックスを切り替えたとき（つまり `canMove` が変更されたとき）またはコードを編集したときに、リスナの再登録が発生することが分かります。これは、毎レンダーごとに再登録をしていた前のアプローチよりも優れています。\n\nこの種の問題に対するより一般的なアプローチについては、[エフェクトからイベントを分離する](/learn/separating-events-from-effects)を参照してください。\n\n</Solution>\n\n#### 接続の切り替えを修正 {/*fix-a-connection-switch*/}\n\n以下の例では、`chat.js` にあるチャットサービスは 2 つの異なる API を公開しています。`createEncryptedConnection` と `createUnencryptedConnection` です。ルートの `App` コンポーネントでは、暗号化を適用するかどうかをユーザが選択できるようにしています。そして、ユーザが選んだ選択肢に対応する API メソッドを、子の `ChatRoom` コンポーネントに `createConnection` プロパティとして渡しています。\n\n最初は、コンソールログには接続が暗号化されていない旨が表示されています。チェックボックスをオンにしてみてください。何も起こりません。しかし、その後に違うルームを選択すると、チャットが再接続され、*このタイミングで*暗号化も有効になります（コンソールメッセージから確認できます）。これはバグです。*チェックボックスをオンにしたタイミングでも*、チャットが再接続されるように修正してください。\n\n<Hint>\n\nリンタを抑制している部分は常に疑いましょう。バグかもしれません。\n\n</Hint>\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ChatRoom from './ChatRoom.js';\nimport {\n  createEncryptedConnection,\n  createUnencryptedConnection,\n} from './chat.js';\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [isEncrypted, setIsEncrypted] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isEncrypted}\n          onChange={e => setIsEncrypted(e.target.checked)}\n        />\n        Enable encryption\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n        createConnection={isEncrypted ?\n          createEncryptedConnection :\n          createUnencryptedConnection\n        }\n      />\n    </>\n  );\n}\n```\n\n```js {expectedErrors: {'react-compiler': [8]}} src/ChatRoom.js active\nimport { useState, useEffect } from 'react';\n\nexport default function ChatRoom({ roomId, createConnection }) {\n  useEffect(() => {\n    const connection = createConnection(roomId);\n    connection.connect();\n    return () => connection.disconnect();\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [roomId]);\n\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n```\n\n```js src/chat.js\nexport function createEncryptedConnection(roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ 🔐 Connecting to \"' + roomId + '... (encrypted)');\n    },\n    disconnect() {\n      console.log('❌ 🔐 Disconnected from \"' + roomId + '\" room (encrypted)');\n    }\n  };\n}\n\nexport function createUnencryptedConnection(roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '... (unencrypted)');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room (unencrypted)');\n    }\n  };\n}\n```\n\n```css\nlabel { display: block; margin-bottom: 10px; }\n```\n\n</Sandpack>\n\n<Solution>\n\nリンタの抑制を解除すると、リントエラーが表示されます。問題は、`createConnection` が props であるため、リアクティブであることです。時間の経過とともに変化する可能性があります！（実際、変化します。親コンポーネントは、ユーザがチェックボックスをオンにしたときに `createConnection` プロパティに渡す関数を切り替えています。）これが依存配列に含めなければならない理由です。依存配列に追加してバグを修正しましょう。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ChatRoom from './ChatRoom.js';\nimport {\n  createEncryptedConnection,\n  createUnencryptedConnection,\n} from './chat.js';\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [isEncrypted, setIsEncrypted] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isEncrypted}\n          onChange={e => setIsEncrypted(e.target.checked)}\n        />\n        Enable encryption\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n        createConnection={isEncrypted ?\n          createEncryptedConnection :\n          createUnencryptedConnection\n        }\n      />\n    </>\n  );\n}\n```\n\n```js src/ChatRoom.js active\nimport { useState, useEffect } from 'react';\n\nexport default function ChatRoom({ roomId, createConnection }) {\n  useEffect(() => {\n    const connection = createConnection(roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId, createConnection]);\n\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n```\n\n```js src/chat.js\nexport function createEncryptedConnection(roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ 🔐 Connecting to \"' + roomId + '... (encrypted)');\n    },\n    disconnect() {\n      console.log('❌ 🔐 Disconnected from \"' + roomId + '\" room (encrypted)');\n    }\n  };\n}\n\nexport function createUnencryptedConnection(roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '... (unencrypted)');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room (unencrypted)');\n    }\n  };\n}\n```\n\n```css\nlabel { display: block; margin-bottom: 10px; }\n```\n\n</Sandpack>\n\n`createConnection` が依存値であることは正しいです。しかし、このコードは少し壊れやすくなっています。なぜなら、誰かが `App` コンポーネントを編集し、プロパティの値としてインライン関数を渡す可能性があるからです。その場合、`App` コンポーネントが再レンダーされるたびに異なる値が渡され、エフェクトが頻繁に再同期されてしまう可能性があります。これを避けるために、代わりに `isEncrypted` を渡してみましょう。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ChatRoom from './ChatRoom.js';\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [isEncrypted, setIsEncrypted] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isEncrypted}\n          onChange={e => setIsEncrypted(e.target.checked)}\n        />\n        Enable encryption\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n        isEncrypted={isEncrypted}\n      />\n    </>\n  );\n}\n```\n\n```js src/ChatRoom.js active\nimport { useState, useEffect } from 'react';\nimport {\n  createEncryptedConnection,\n  createUnencryptedConnection,\n} from './chat.js';\n\nexport default function ChatRoom({ roomId, isEncrypted }) {\n  useEffect(() => {\n    const createConnection = isEncrypted ?\n      createEncryptedConnection :\n      createUnencryptedConnection;\n    const connection = createConnection(roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId, isEncrypted]);\n\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n```\n\n```js src/chat.js\nexport function createEncryptedConnection(roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ 🔐 Connecting to \"' + roomId + '... (encrypted)');\n    },\n    disconnect() {\n      console.log('❌ 🔐 Disconnected from \"' + roomId + '\" room (encrypted)');\n    }\n  };\n}\n\nexport function createUnencryptedConnection(roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '... (unencrypted)');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room (unencrypted)');\n    }\n  };\n}\n```\n\n```css\nlabel { display: block; margin-bottom: 10px; }\n```\n\n</Sandpack>\n\nこのコードでは、`App` コンポーネントは関数ではなくブール値のプロパティを渡しています。そして、エフェクト内で、どちらの関数を利用するかを決定しています。`createEncryptedConnection` と `createUnencryptedConnection` はどちらもコンポーネント外で宣言されているため、リアクティブではありません。そのため、依存配列に指定する必要はありません。詳しくは、[エフェクトの依存関係を削除する](/learn/removing-effect-dependencies)を参照してください。\n\n</Solution>\n\n#### 連動する選択ボックスの作成 {/*populate-a-chain-of-select-boxes*/}\n\nこの例では、2 つの選択ボックスがあります。1 つ目の選択ボックスでは惑星を選択できます。2 つ目の選択ボックスでは*その惑星上の場所*を選択できますが、まだ機能していません。選択された惑星上の場所の名前を表示するようにしてください。\n\n1 つ目の選択ボックスがどのように動作するか見てみましょう。これは `\"/planets\"` API の呼び出し結果を格納している `planetList` state 変数から作られています。現在選択されている惑星の ID は `planetId` state 変数で保持されています。`placeList` という state 変数に、`\"/planets/\" + planetId + \"/places\"` API の呼び出し結果を格納するには、どこにコードを追加すればよいでしょうか。\n\n正しく実装すると、惑星を選択すると場所のリストが表示されます。惑星を変更すると、場所のリストも入れ替わります。\n\n<Hint>\n\nもし 2 つの独立した同期の処理がある場合は、2 つの別々のエフェクトに分割しましょう。\n\n</Hint>\n\n<Sandpack>\n\n```js src/App.js\nimport { useState, useEffect } from 'react';\nimport { fetchData } from './api.js';\n\nexport default function Page() {\n  const [planetList, setPlanetList] = useState([])\n  const [planetId, setPlanetId] = useState('');\n\n  const [placeList, setPlaceList] = useState([]);\n  const [placeId, setPlaceId] = useState('');\n\n  useEffect(() => {\n    let ignore = false;\n    fetchData('/planets').then(result => {\n      if (!ignore) {\n        console.log('Fetched a list of planets.');\n        setPlanetList(result);\n        setPlanetId(result[0].id); // Select the first planet\n      }\n    });\n    return () => {\n      ignore = true;\n    }\n  }, []);\n\n  return (\n    <>\n      <label>\n        Pick a planet:{' '}\n        <select value={planetId} onChange={e => {\n          setPlanetId(e.target.value);\n        }}>\n          {planetList.map(planet =>\n            <option key={planet.id} value={planet.id}>{planet.name}</option>\n          )}\n        </select>\n      </label>\n      <label>\n        Pick a place:{' '}\n        <select value={placeId} onChange={e => {\n          setPlaceId(e.target.value);\n        }}>\n          {placeList.map(place =>\n            <option key={place.id} value={place.id}>{place.name}</option>\n          )}\n        </select>\n      </label>\n      <hr />\n      <p>You are going to: {placeId || '???'} on {planetId || '???'} </p>\n    </>\n  );\n}\n```\n\n```js src/api.js hidden\nexport function fetchData(url) {\n  if (url === '/planets') {\n    return fetchPlanets();\n  } else if (url.startsWith('/planets/')) {\n    const match = url.match(/^\\/planets\\/([\\w-]+)\\/places(\\/)?$/);\n    if (!match || !match[1] || !match[1].length) {\n      throw Error('Expected URL like \"/planets/earth/places\". Received: \"' + url + '\".');\n    }\n    return fetchPlaces(match[1]);\n  } else throw Error('Expected URL like \"/planets\" or \"/planets/earth/places\". Received: \"' + url + '\".');\n}\n\nasync function fetchPlanets() {\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve([{\n        id: 'earth',\n        name: 'Earth'\n      }, {\n        id: 'venus',\n        name: 'Venus'\n      }, {\n        id: 'mars',\n        name: 'Mars'        \n      }]);\n    }, 1000);\n  });\n}\n\nasync function fetchPlaces(planetId) {\n  if (typeof planetId !== 'string') {\n    throw Error(\n      'fetchPlaces(planetId) expects a string argument. ' +\n      'Instead received: ' + planetId + '.'\n    );\n  }\n  return new Promise(resolve => {\n    setTimeout(() => {\n      if (planetId === 'earth') {\n        resolve([{\n          id: 'laos',\n          name: 'Laos'\n        }, {\n          id: 'spain',\n          name: 'Spain'\n        }, {\n          id: 'vietnam',\n          name: 'Vietnam'        \n        }]);\n      } else if (planetId === 'venus') {\n        resolve([{\n          id: 'aurelia',\n          name: 'Aurelia'\n        }, {\n          id: 'diana-chasma',\n          name: 'Diana Chasma'\n        }, {\n          id: 'kumsong-vallis',\n          name: 'Kŭmsŏng Vallis'        \n        }]);\n      } else if (planetId === 'mars') {\n        resolve([{\n          id: 'aluminum-city',\n          name: 'Aluminum City'\n        }, {\n          id: 'new-new-york',\n          name: 'New New York'\n        }, {\n          id: 'vishniac',\n          name: 'Vishniac'\n        }]);\n      } else throw Error('Unknown planet ID: ' + planetId);\n    }, 1000);\n  });\n}\n```\n\n```css\nlabel { display: block; margin-bottom: 10px; }\n```\n\n</Sandpack>\n\n<Solution>\n\nこのケースでは、2 つの独立した同期の処理があります。\n\n- 最初の選択ボックスは、リモートの惑星リストと同期しています。\n- 2 つ目の選択ボックスは、現在の `planetId` に対応するリモートの場所リストと同期しています。\n\nそのため、それぞれを別々のエフェクトとして記述することが合理的でしょう。以下は、その例です。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState, useEffect } from 'react';\nimport { fetchData } from './api.js';\n\nexport default function Page() {\n  const [planetList, setPlanetList] = useState([])\n  const [planetId, setPlanetId] = useState('');\n\n  const [placeList, setPlaceList] = useState([]);\n  const [placeId, setPlaceId] = useState('');\n\n  useEffect(() => {\n    let ignore = false;\n    fetchData('/planets').then(result => {\n      if (!ignore) {\n        console.log('Fetched a list of planets.');\n        setPlanetList(result);\n        setPlanetId(result[0].id); // Select the first planet\n      }\n    });\n    return () => {\n      ignore = true;\n    }\n  }, []);\n\n  useEffect(() => {\n    if (planetId === '') {\n      // Nothing is selected in the first box yet\n      return;\n    }\n\n    let ignore = false;\n    fetchData('/planets/' + planetId + '/places').then(result => {\n      if (!ignore) {\n        console.log('Fetched a list of places on \"' + planetId + '\".');\n        setPlaceList(result);\n        setPlaceId(result[0].id); // Select the first place\n      }\n    });\n    return () => {\n      ignore = true;\n    }\n  }, [planetId]);\n\n  return (\n    <>\n      <label>\n        Pick a planet:{' '}\n        <select value={planetId} onChange={e => {\n          setPlanetId(e.target.value);\n        }}>\n          {planetList.map(planet =>\n            <option key={planet.id} value={planet.id}>{planet.name}</option>\n          )}\n        </select>\n      </label>\n      <label>\n        Pick a place:{' '}\n        <select value={placeId} onChange={e => {\n          setPlaceId(e.target.value);\n        }}>\n          {placeList.map(place =>\n            <option key={place.id} value={place.id}>{place.name}</option>\n          )}\n        </select>\n      </label>\n      <hr />\n      <p>You are going to: {placeId || '???'} on {planetId || '???'} </p>\n    </>\n  );\n}\n```\n\n```js src/api.js hidden\nexport function fetchData(url) {\n  if (url === '/planets') {\n    return fetchPlanets();\n  } else if (url.startsWith('/planets/')) {\n    const match = url.match(/^\\/planets\\/([\\w-]+)\\/places(\\/)?$/);\n    if (!match || !match[1] || !match[1].length) {\n      throw Error('Expected URL like \"/planets/earth/places\". Received: \"' + url + '\".');\n    }\n    return fetchPlaces(match[1]);\n  } else throw Error('Expected URL like \"/planets\" or \"/planets/earth/places\". Received: \"' + url + '\".');\n}\n\nasync function fetchPlanets() {\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve([{\n        id: 'earth',\n        name: 'Earth'\n      }, {\n        id: 'venus',\n        name: 'Venus'\n      }, {\n        id: 'mars',\n        name: 'Mars'        \n      }]);\n    }, 1000);\n  });\n}\n\nasync function fetchPlaces(planetId) {\n  if (typeof planetId !== 'string') {\n    throw Error(\n      'fetchPlaces(planetId) expects a string argument. ' +\n      'Instead received: ' + planetId + '.'\n    );\n  }\n  return new Promise(resolve => {\n    setTimeout(() => {\n      if (planetId === 'earth') {\n        resolve([{\n          id: 'laos',\n          name: 'Laos'\n        }, {\n          id: 'spain',\n          name: 'Spain'\n        }, {\n          id: 'vietnam',\n          name: 'Vietnam'        \n        }]);\n      } else if (planetId === 'venus') {\n        resolve([{\n          id: 'aurelia',\n          name: 'Aurelia'\n        }, {\n          id: 'diana-chasma',\n          name: 'Diana Chasma'\n        }, {\n          id: 'kumsong-vallis',\n          name: 'Kŭmsŏng Vallis'        \n        }]);\n      } else if (planetId === 'mars') {\n        resolve([{\n          id: 'aluminum-city',\n          name: 'Aluminum City'\n        }, {\n          id: 'new-new-york',\n          name: 'New New York'\n        }, {\n          id: 'vishniac',\n          name: 'Vishniac'\n        }]);\n      } else throw Error('Unknown planet ID: ' + planetId);\n    }, 1000);\n  });\n}\n```\n\n```css\nlabel { display: block; margin-bottom: 10px; }\n```\n\n</Sandpack>\n\nこのコードは少し冗長です。しかし、だからといって 1 つのエフェクトにまとめるのは良い方法ではありません！ もしまとめてしまうと、両方のエフェクトの依存配列を 1 つにまとめる必要があります。そうすると、惑星を変更しただけで、惑星リストが再取得されてしまいます。エフェクトはコードを再利用するためのツールではありません。\n\n代わりに、以下の `useSelectOptions` のように、カスタムフックにロジックを抽出することで、繰り返しを減らすことができます。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { useSelectOptions } from './useSelectOptions.js';\n\nexport default function Page() {\n  const [\n    planetList,\n    planetId,\n    setPlanetId\n  ] = useSelectOptions('/planets');\n\n  const [\n    placeList,\n    placeId,\n    setPlaceId\n  ] = useSelectOptions(planetId ? `/planets/${planetId}/places` : null);\n\n  return (\n    <>\n      <label>\n        Pick a planet:{' '}\n        <select value={planetId} onChange={e => {\n          setPlanetId(e.target.value);\n        }}>\n          {planetList?.map(planet =>\n            <option key={planet.id} value={planet.id}>{planet.name}</option>\n          )}\n        </select>\n      </label>\n      <label>\n        Pick a place:{' '}\n        <select value={placeId} onChange={e => {\n          setPlaceId(e.target.value);\n        }}>\n          {placeList?.map(place =>\n            <option key={place.id} value={place.id}>{place.name}</option>\n          )}\n        </select>\n      </label>\n      <hr />\n      <p>You are going to: {placeId || '...'} on {planetId || '...'} </p>\n    </>\n  );\n}\n```\n\n```js src/useSelectOptions.js\nimport { useState, useEffect } from 'react';\nimport { fetchData } from './api.js';\n\nexport function useSelectOptions(url) {\n  const [list, setList] = useState(null);\n  const [selectedId, setSelectedId] = useState('');\n  useEffect(() => {\n    if (url === null) {\n      return;\n    }\n\n    let ignore = false;\n    fetchData(url).then(result => {\n      if (!ignore) {\n        setList(result);\n        setSelectedId(result[0].id);\n      }\n    });\n    return () => {\n      ignore = true;\n    }\n  }, [url]);\n  return [list, selectedId, setSelectedId];\n}\n```\n\n```js src/api.js hidden\nexport function fetchData(url) {\n  if (url === '/planets') {\n    return fetchPlanets();\n  } else if (url.startsWith('/planets/')) {\n    const match = url.match(/^\\/planets\\/([\\w-]+)\\/places(\\/)?$/);\n    if (!match || !match[1] || !match[1].length) {\n      throw Error('Expected URL like \"/planets/earth/places\". Received: \"' + url + '\".');\n    }\n    return fetchPlaces(match[1]);\n  } else throw Error('Expected URL like \"/planets\" or \"/planets/earth/places\". Received: \"' + url + '\".');\n}\n\nasync function fetchPlanets() {\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve([{\n        id: 'earth',\n        name: 'Earth'\n      }, {\n        id: 'venus',\n        name: 'Venus'\n      }, {\n        id: 'mars',\n        name: 'Mars'        \n      }]);\n    }, 1000);\n  });\n}\n\nasync function fetchPlaces(planetId) {\n  if (typeof planetId !== 'string') {\n    throw Error(\n      'fetchPlaces(planetId) expects a string argument. ' +\n      'Instead received: ' + planetId + '.'\n    );\n  }\n  return new Promise(resolve => {\n    setTimeout(() => {\n      if (planetId === 'earth') {\n        resolve([{\n          id: 'laos',\n          name: 'Laos'\n        }, {\n          id: 'spain',\n          name: 'Spain'\n        }, {\n          id: 'vietnam',\n          name: 'Vietnam'        \n        }]);\n      } else if (planetId === 'venus') {\n        resolve([{\n          id: 'aurelia',\n          name: 'Aurelia'\n        }, {\n          id: 'diana-chasma',\n          name: 'Diana Chasma'\n        }, {\n          id: 'kumsong-vallis',\n          name: 'Kŭmsŏng Vallis'        \n        }]);\n      } else if (planetId === 'mars') {\n        resolve([{\n          id: 'aluminum-city',\n          name: 'Aluminum City'\n        }, {\n          id: 'new-new-york',\n          name: 'New New York'\n        }, {\n          id: 'vishniac',\n          name: 'Vishniac'\n        }]);\n      } else throw Error('Unknown planet ID: ' + planetId);\n    }, 1000);\n  });\n}\n```\n\n```css\nlabel { display: block; margin-bottom: 10px; }\n```\n\n</Sandpack>\n\n動作を確認するには、サンドボックスの `useSelectOptions.js` タブを確認してください。理想的には、アプリケーションに存在するエフェクトのほとんどは、コミュニティから提供されるカスタムフックか、自分で実装したカスタムフックに置き換えられるべきです。カスタムフックは同期ロジックを隠してくれるため、呼び出し元のコンポーネントがエフェクトのことを気にすることがなくなります。アプリケーションを開発していくうちに利用できるフックの選択肢が広がり、最終的にはコンポーネント内でエフェクトを書く必要はほとんどなくなるでしょう。\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/managing-state.md",
    "content": "---\ntitle: state の管理\n---\n\n<Intro>\n\nアプリケーションが成長するにつれて、state の構成方法やコンポーネント間のデータの流れについてより計画的に考えることが重要になります。バグの一般的な原因は、冗長な、あるいは重複した state です。この章では、state を適切に構造化する方法、state 更新ロジックをメンテナンスしやすい状態に保つ方法、そして離れたコンポーネント間で state を共有する方法について学びます。\n\n</Intro>\n\n<YouWillLearn isChapter={true}>\n\n* [UI の変化を state の変化として捉える方法](/learn/reacting-to-input-with-state)\n* [state を適切に構造化する方法](/learn/choosing-the-state-structure)\n* [コンポーネント間で state を共有するために状態を \"リフトアップ\" する方法](/learn/sharing-state-between-components)\n* [state が保持されるかリセットされるかを制御する方法](/learn/preserving-and-resetting-state)\n* [複雑な state ロジックを関数にまとめる方法](/learn/extracting-state-logic-into-a-reducer)\n* [\"props の穴掘り作業\" なしで情報を渡す方法](/learn/passing-data-deeply-with-context)\n* [アプリの成長に合わせて state 管理をスケーリングする方法](/learn/scaling-up-with-reducer-and-context)\n\n</YouWillLearn>\n\n## state を使って入力に反応する {/*reacting-to-input-with-state*/}\n\nReact を使う場合、コードから直接 UI を変更することはありません。例えば「ボタンを無効にする」、「ボタンを有効にする」、「成功メッセージを表示する」といったコマンドを書くことはありません。代わりに、コンポーネントのさまざまな視覚状態（例えば「初期状態」、「入力中状態」、「成功状態」）に対して表示したい UI を記述し、ユーザ入力に応じて state の変更をトリガします。これは、デザイナが UI を考える方法に似ています。\n\n以下は、React を使って構築されたクイズフォームです。`status` という state 変数を使って、送信ボタンを有効にするか無効にするか、成功メッセージを表示するかどうかを決定していることに注目してください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [answer, setAnswer] = useState('');\n  const [error, setError] = useState(null);\n  const [status, setStatus] = useState('typing');\n\n  if (status === 'success') {\n    return <h1>That's right!</h1>\n  }\n\n  async function handleSubmit(e) {\n    e.preventDefault();\n    setStatus('submitting');\n    try {\n      await submitForm(answer);\n      setStatus('success');\n    } catch (err) {\n      setStatus('typing');\n      setError(err);\n    }\n  }\n\n  function handleTextareaChange(e) {\n    setAnswer(e.target.value);\n  }\n\n  return (\n    <>\n      <h2>City quiz</h2>\n      <p>\n        In which city is there a billboard that turns air into drinkable water?\n      </p>\n      <form onSubmit={handleSubmit}>\n        <textarea\n          value={answer}\n          onChange={handleTextareaChange}\n          disabled={status === 'submitting'}\n        />\n        <br />\n        <button disabled={\n          answer.length === 0 ||\n          status === 'submitting'\n        }>\n          Submit\n        </button>\n        {error !== null &&\n          <p className=\"Error\">\n            {error.message}\n          </p>\n        }\n      </form>\n    </>\n  );\n}\n\nfunction submitForm(answer) {\n  // Pretend it's hitting the network.\n  return new Promise((resolve, reject) => {\n    setTimeout(() => {\n      let shouldError = answer.toLowerCase() !== 'lima'\n      if (shouldError) {\n        reject(new Error('Good guess but a wrong answer. Try again!'));\n      } else {\n        resolve();\n      }\n    }, 1500);\n  });\n}\n```\n\n```css\n.Error { color: red; }\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/reacting-to-input-with-state\">\n\n[**state を使って入力に反応する**](/learn/reacting-to-input-with-state)を読んで、state ベースの考え方でユーザ操作を扱う方法を学びましょう。\n\n</LearnMore>\n\n## state 構造の選択 {/*choosing-the-state-structure*/}\n\n快適に変更やデバッグが行えるコンポーネントと、常にバグの種になるコンポーネントの違いは、state をうまく構造化できているかどうかです。最も重要な原則は、state に冗長な情報や重複した情報を持たせないことです。不要な state があると、それを更新するのを忘れてすぐにバグが発生してしまいます！\n\n例えば、このフォームには**冗長な** `fullName` という state 変数があります。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [firstName, setFirstName] = useState('');\n  const [lastName, setLastName] = useState('');\n  const [fullName, setFullName] = useState('');\n\n  function handleFirstNameChange(e) {\n    setFirstName(e.target.value);\n    setFullName(e.target.value + ' ' + lastName);\n  }\n\n  function handleLastNameChange(e) {\n    setLastName(e.target.value);\n    setFullName(firstName + ' ' + e.target.value);\n  }\n\n  return (\n    <>\n      <h2>Let’s check you in</h2>\n      <label>\n        First name:{' '}\n        <input\n          value={firstName}\n          onChange={handleFirstNameChange}\n        />\n      </label>\n      <label>\n        Last name:{' '}\n        <input\n          value={lastName}\n          onChange={handleLastNameChange}\n        />\n      </label>\n      <p>\n        Your ticket will be issued to: <b>{fullName}</b>\n      </p>\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; margin-bottom: 5px; }\n```\n\n</Sandpack>\n\nコンポーネントがレンダーされる際に `fullName` を計算することで、この冗長な state を削除し、コードを簡素化することができます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [firstName, setFirstName] = useState('');\n  const [lastName, setLastName] = useState('');\n\n  const fullName = firstName + ' ' + lastName;\n\n  function handleFirstNameChange(e) {\n    setFirstName(e.target.value);\n  }\n\n  function handleLastNameChange(e) {\n    setLastName(e.target.value);\n  }\n\n  return (\n    <>\n      <h2>Let’s check you in</h2>\n      <label>\n        First name:{' '}\n        <input\n          value={firstName}\n          onChange={handleFirstNameChange}\n        />\n      </label>\n      <label>\n        Last name:{' '}\n        <input\n          value={lastName}\n          onChange={handleLastNameChange}\n        />\n      </label>\n      <p>\n        Your ticket will be issued to: <b>{fullName}</b>\n      </p>\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; margin-bottom: 5px; }\n```\n\n</Sandpack>\n\nこれは小さな変更に見えるかもしれませんが、React アプリの多くのバグはこのようにして修正されます。\n\n<LearnMore path=\"/learn/choosing-the-state-structure\">\n\n[**state 構造の選択**](/learn/choosing-the-state-structure)を読んで、バグを避けるために state の構造をどのように設計するか学びましょう。\n\n</LearnMore>\n\n## コンポーネント間で state を共有する {/*sharing-state-between-components*/}\n\n2 つのコンポーネントの state を常に同時に変更したいという場合があります。これを行うには、両方のコンポーネントから state を削除して最も近い共通の親へ移動し、そこから state を props 経由でコンポーネントへ渡します。これは *state のリフトアップ (lifting state up)* として知られているものであり、React コードを書く際に行う最も一般的な作業のひとつです。\n\nこの例では、一度にアクティブになるべきパネルは 1 つだけです。これを実現するには、各パネルの内部にアクティブかどうかの state を保持する代わりに、親コンポーネントが state を保持し、子コンポーネントの props を指定するようにします。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Accordion() {\n  const [activeIndex, setActiveIndex] = useState(0);\n  return (\n    <>\n      <h2>Almaty, Kazakhstan</h2>\n      <Panel\n        title=\"About\"\n        isActive={activeIndex === 0}\n        onShow={() => setActiveIndex(0)}\n      >\n        With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.\n      </Panel>\n      <Panel\n        title=\"Etymology\"\n        isActive={activeIndex === 1}\n        onShow={() => setActiveIndex(1)}\n      >\n        The name comes from <span lang=\"kk-KZ\">алма</span>, the Kazakh word for \"apple\" and is often translated as \"full of apples\". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang=\"la\">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.\n      </Panel>\n    </>\n  );\n}\n\nfunction Panel({\n  title,\n  children,\n  isActive,\n  onShow\n}) {\n  return (\n    <section className=\"panel\">\n      <h3>{title}</h3>\n      {isActive ? (\n        <p>{children}</p>\n      ) : (\n        <button onClick={onShow}>\n          Show\n        </button>\n      )}\n    </section>\n  );\n}\n```\n\n```css\nh3, p { margin: 5px 0px; }\n.panel {\n  padding: 10px;\n  border: 1px solid #aaa;\n}\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/sharing-state-between-components\">\n\n[**コンポーネント間で state を共有する**](/learn/sharing-state-between-components)を読んで、state をリフトアップしてコンポーネントを同期させる方法を学びましょう。\n\n</LearnMore>\n\n## state の保持とリセット {/*preserving-and-resetting-state*/}\n\nコンポーネントを再レンダーする際、React はツリーのどの部分を保持（および更新）し、どの部分を破棄または最初から再作成するかを決定する必要があります。大抵は React の自動的な挙動でうまくいきます。デフォルトでは、React は以前にレンダーされたコンポーネントツリーと「一致する」部分のツリーを保持します。\n\nただし、場合によってはこれが望ましくないことがあります。このチャットアプリでは、メッセージを入力した後に送信先を切り替えても、入力フィールドがリセットされません。これにより、ユーザが誤って間違った相手にメッセージを送信してしまうかもしれません。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\n\nexport default function Messenger() {\n  const [to, setTo] = useState(contacts[0]);\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedContact={to}\n        onSelect={contact => setTo(contact)}\n      />\n      <Chat contact={to} />\n    </div>\n  )\n}\n\nconst contacts = [\n  { name: 'Taylor', email: 'taylor@mail.com' },\n  { name: 'Alice', email: 'alice@mail.com' },\n  { name: 'Bob', email: 'bob@mail.com' }\n];\n```\n\n```js src/ContactList.js\nexport default function ContactList({\n  selectedContact,\n  contacts,\n  onSelect\n}) {\n  return (\n    <section className=\"contact-list\">\n      <ul>\n        {contacts.map(contact =>\n          <li key={contact.email}>\n            <button onClick={() => {\n              onSelect(contact);\n            }}>\n              {contact.name}\n            </button>\n          </li>\n        )}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/Chat.js\nimport { useState } from 'react';\n\nexport default function Chat({ contact }) {\n  const [text, setText] = useState('');\n  return (\n    <section className=\"chat\">\n      <textarea\n        value={text}\n        placeholder={'Chat to ' + contact.name}\n        onChange={e => setText(e.target.value)}\n      />\n      <br />\n      <button>Send to {contact.email}</button>\n    </section>\n  );\n}\n```\n\n```css\n.chat, .contact-list {\n  float: left;\n  margin-bottom: 20px;\n}\nul, li {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli button {\n  width: 100px;\n  padding: 10px;\n  margin-right: 10px;\n}\ntextarea {\n  height: 150px;\n}\n```\n\n</Sandpack>\n\n例えば `<Chat key={email} />` のように異なる `key` を渡すことでデフォルトの動作を上書きし、コンポーネントの state を*強制的に*リセットすることができます。これにより、送信先が異なる場合は*異なる* `Chat` コンポーネントであり、新しいデータ（および入力フィールドなど）で最初から再作成する必要があると React に伝えることができます。これにより、全く同じコンポーネントをレンダーしても、送信先を切り替えると入力フィールドがリセットされるようになります。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\n\nexport default function Messenger() {\n  const [to, setTo] = useState(contacts[0]);\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedContact={to}\n        onSelect={contact => setTo(contact)}\n      />\n      <Chat key={to.email} contact={to} />\n    </div>\n  )\n}\n\nconst contacts = [\n  { name: 'Taylor', email: 'taylor@mail.com' },\n  { name: 'Alice', email: 'alice@mail.com' },\n  { name: 'Bob', email: 'bob@mail.com' }\n];\n```\n\n```js src/ContactList.js\nexport default function ContactList({\n  selectedContact,\n  contacts,\n  onSelect\n}) {\n  return (\n    <section className=\"contact-list\">\n      <ul>\n        {contacts.map(contact =>\n          <li key={contact.email}>\n            <button onClick={() => {\n              onSelect(contact);\n            }}>\n              {contact.name}\n            </button>\n          </li>\n        )}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/Chat.js\nimport { useState } from 'react';\n\nexport default function Chat({ contact }) {\n  const [text, setText] = useState('');\n  return (\n    <section className=\"chat\">\n      <textarea\n        value={text}\n        placeholder={'Chat to ' + contact.name}\n        onChange={e => setText(e.target.value)}\n      />\n      <br />\n      <button>Send to {contact.email}</button>\n    </section>\n  );\n}\n```\n\n```css\n.chat, .contact-list {\n  float: left;\n  margin-bottom: 20px;\n}\nul, li {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli button {\n  width: 100px;\n  padding: 10px;\n  margin-right: 10px;\n}\ntextarea {\n  height: 150px;\n}\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/preserving-and-resetting-state\">\n\n[**state の保持とリセット**](/learn/preserving-and-resetting-state)を読んで、state の寿命や制御方法について学びましょう。\n\n</LearnMore>\n\n## state ロジックをリデューサに抽出する {/*extracting-state-logic-into-a-reducer*/}\n\n多くのイベントハンドラにまたがって state の更新コードが含まれるコンポーネントは、理解が大変になりがちです。このような場合、コンポーネントの外部に、*リデューサ (reducer)* と呼ばれる単一の関数を作成し、すべての state 更新ロジックを集約することができます。イベントハンドラは、ユーザの「アクション」を指定するだけでよくなるため、簡潔になります。以下ではファイルの最後にあるリデューサ関数が、各アクションに対する state の更新方法を指定しています！\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\n\nexport default function TaskApp() {\n  const [tasks, dispatch] = useReducer(\n    tasksReducer,\n    initialTasks\n  );\n\n  function handleAddTask(text) {\n    dispatch({\n      type: 'added',\n      id: nextId++,\n      text: text,\n    });\n  }\n\n  function handleChangeTask(task) {\n    dispatch({\n      type: 'changed',\n      task: task\n    });\n  }\n\n  function handleDeleteTask(taskId) {\n    dispatch({\n      type: 'deleted',\n      id: taskId\n    });\n  }\n\n  return (\n    <>\n      <h1>Prague itinerary</h1>\n      <AddTask\n        onAddTask={handleAddTask}\n      />\n      <TaskList\n        tasks={tasks}\n        onChangeTask={handleChangeTask}\n        onDeleteTask={handleDeleteTask}\n      />\n    </>\n  );\n}\n\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [...tasks, {\n        id: action.id,\n        text: action.text,\n        done: false\n      }];\n    }\n    case 'changed': {\n      return tasks.map(t => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter(t => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nlet nextId = 3;\nconst initialTasks = [\n  { id: 0, text: 'Visit Kafka Museum', done: true },\n  { id: 1, text: 'Watch a puppet show', done: false },\n  { id: 2, text: 'Lennon Wall pic', done: false }\n];\n```\n\n```js src/AddTask.js hidden\nimport { useState } from 'react';\n\nexport default function AddTask({ onAddTask }) {\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button onClick={() => {\n        setText('');\n        onAddTask(text);\n      }}>Add</button>\n    </>\n  )\n}\n```\n\n```js src/TaskList.js hidden\nimport { useState } from 'react';\n\nexport default function TaskList({\n  tasks,\n  onChangeTask,\n  onDeleteTask\n}) {\n  return (\n    <ul>\n      {tasks.map(task => (\n        <li key={task.id}>\n          <Task\n            task={task}\n            onChange={onChangeTask}\n            onDelete={onDeleteTask}\n          />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ task, onChange, onDelete }) {\n  const [isEditing, setIsEditing] = useState(false);\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={e => {\n            onChange({\n              ...task,\n              text: e.target.value\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={e => {\n          onChange({\n            ...task,\n            done: e.target.checked\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => onDelete(task.id)}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/extracting-state-logic-into-a-reducer\">\n\n[**state ロジックをリデューサに抽出する**](/learn/extracting-state-logic-into-a-reducer)を読んで、リデューサ関数内にロジックを集約する方法を学びましょう。\n\n</LearnMore>\n\n## コンテクストで深くデータを受け渡す {/*passing-data-deeply-with-context*/}\n\n通常、親コンポーネントから子コンポーネントには props を使って情報を渡します。しかし、props を多数の中間コンポーネントを経由して渡さないといけない場合や、アプリ内の多くのコンポーネントが同じ情報を必要とする場合、props の受け渡しは冗長で不便なものとなり得ます。*コンテクスト (Context)* を使用することで、親コンポーネントから props を明示的に渡さずとも、それ以下のツリー内の任意のコンポーネント（深さに関わらず）が情報を受け取れるようにできます。\n\nここでは、`Heading` コンポーネントは、最も近い `Section` に自身のネストレベルを「尋ねる」ことで、見出しレベルを決定しています。各 `Section` は、親の `Section` にレベルを尋ねてそれに 1 を加えることで、自分自身のレベルを把握します。すべての `Section` は、props を渡すことなしに、それ以下のすべてのコンポーネントにコンテクストを通じて情報を提供しています。\n\n<Sandpack>\n\n```js\nimport Heading from './Heading.js';\nimport Section from './Section.js';\n\nexport default function Page() {\n  return (\n    <Section>\n      <Heading>Title</Heading>\n      <Section>\n        <Heading>Heading</Heading>\n        <Heading>Heading</Heading>\n        <Heading>Heading</Heading>\n        <Section>\n          <Heading>Sub-heading</Heading>\n          <Heading>Sub-heading</Heading>\n          <Heading>Sub-heading</Heading>\n          <Section>\n            <Heading>Sub-sub-heading</Heading>\n            <Heading>Sub-sub-heading</Heading>\n            <Heading>Sub-sub-heading</Heading>\n          </Section>\n        </Section>\n      </Section>\n    </Section>\n  );\n}\n```\n\n```js src/Section.js\nimport { useContext } from 'react';\nimport { LevelContext } from './LevelContext.js';\n\nexport default function Section({ children }) {\n  const level = useContext(LevelContext);\n  return (\n    <section className=\"section\">\n      <LevelContext value={level + 1}>\n        {children}\n      </LevelContext>\n    </section>\n  );\n}\n```\n\n```js src/Heading.js\nimport { useContext } from 'react';\nimport { LevelContext } from './LevelContext.js';\n\nexport default function Heading({ children }) {\n  const level = useContext(LevelContext);\n  switch (level) {\n    case 0:\n      throw Error('Heading must be inside a Section!');\n    case 1:\n      return <h1>{children}</h1>;\n    case 2:\n      return <h2>{children}</h2>;\n    case 3:\n      return <h3>{children}</h3>;\n    case 4:\n      return <h4>{children}</h4>;\n    case 5:\n      return <h5>{children}</h5>;\n    case 6:\n      return <h6>{children}</h6>;\n    default:\n      throw Error('Unknown level: ' + level);\n  }\n}\n```\n\n```js src/LevelContext.js\nimport { createContext } from 'react';\n\nexport const LevelContext = createContext(0);\n```\n\n```css\n.section {\n  padding: 10px;\n  margin: 5px;\n  border-radius: 5px;\n  border: 1px solid #aaa;\n}\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/passing-data-deeply-with-context\">\n\n[**コンテクストで深くデータを受け渡す**](/learn/passing-data-deeply-with-context)を読んで、props を渡す代わりにコンテクストを使う方法を学びましょう。\n\n</LearnMore>\n\n## リデューサとコンテクストでスケールアップ {/*scaling-up-with-reducer-and-context*/}\n\nリデューサを使えば、コンポーネントの state 更新ロジックを集約することができます。コンテクストを使えば、他のコンポーネントに深く情報を渡すことができます。そしてリデューサとコンテクストを組み合わせることで、複雑な画面の state 管理ができるようになります。\n\nこのアプローチでは、複雑な state を持つ親コンポーネントが、リデューサを使って state を管理します。ツリー内の任意の深さにある他のコンポーネントは、コンテクストを介してその state を読み取ることができます。また、その state を更新するためのアクションをディスパッチすることもできます。\n\n<Sandpack>\n\n```js src/App.js\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\nimport { TasksProvider } from './TasksContext.js';\n\nexport default function TaskApp() {\n  return (\n    <TasksProvider>\n      <h1>Day off in Kyoto</h1>\n      <AddTask />\n      <TaskList />\n    </TasksProvider>\n  );\n}\n```\n\n```js src/TasksContext.js\nimport { createContext, useContext, useReducer } from 'react';\n\nconst TasksContext = createContext(null);\nconst TasksDispatchContext = createContext(null);\n\nexport function TasksProvider({ children }) {\n  const [tasks, dispatch] = useReducer(\n    tasksReducer,\n    initialTasks\n  );\n\n  return (\n    <TasksContext value={tasks}>\n      <TasksDispatchContext value={dispatch}>\n        {children}\n      </TasksDispatchContext>\n    </TasksContext>\n  );\n}\n\nexport function useTasks() {\n  return useContext(TasksContext);\n}\n\nexport function useTasksDispatch() {\n  return useContext(TasksDispatchContext);\n}\n\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [...tasks, {\n        id: action.id,\n        text: action.text,\n        done: false\n      }];\n    }\n    case 'changed': {\n      return tasks.map(t => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter(t => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nconst initialTasks = [\n  { id: 0, text: 'Philosopher’s Path', done: true },\n  { id: 1, text: 'Visit the temple', done: false },\n  { id: 2, text: 'Drink matcha', done: false }\n];\n```\n\n```js src/AddTask.js\nimport { useState, useContext } from 'react';\nimport { useTasksDispatch } from './TasksContext.js';\n\nexport default function AddTask({ onAddTask }) {\n  const [text, setText] = useState('');\n  const dispatch = useTasksDispatch();\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button onClick={() => {\n        setText('');\n        dispatch({\n          type: 'added',\n          id: nextId++,\n          text: text,\n        });\n      }}>Add</button>\n    </>\n  );\n}\n\nlet nextId = 3;\n```\n\n```js src/TaskList.js\nimport { useState, useContext } from 'react';\nimport { useTasks, useTasksDispatch } from './TasksContext.js';\n\nexport default function TaskList() {\n  const tasks = useTasks();\n  return (\n    <ul>\n      {tasks.map(task => (\n        <li key={task.id}>\n          <Task task={task} />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ task }) {\n  const [isEditing, setIsEditing] = useState(false);\n  const dispatch = useTasksDispatch();\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={e => {\n            dispatch({\n              type: 'changed',\n              task: {\n                ...task,\n                text: e.target.value\n              }\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={e => {\n          dispatch({\n            type: 'changed',\n            task: {\n              ...task,\n              done: e.target.checked\n            }\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => {\n        dispatch({\n          type: 'deleted',\n          id: task.id\n        });\n      }}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\n<LearnMore path=\"/learn/scaling-up-with-reducer-and-context\">\n\n[**リデューサとコンテクストでスケールアップ**](/learn/scaling-up-with-reducer-and-context)を読んで、アプリが成長するに従って state 管理がどのようにスケールしていくか学びましょう。\n\n</LearnMore>\n\n## 次のステップ {/*whats-next*/}\n\n[state を使って入力に反応する](/learn/reacting-to-input-with-state)に進んで、この章をページごとに読み進めましょう！\n\nもしくは、すでにこれらのトピックに詳しい場合、[避難ハッチ](/learn/escape-hatches)について読んでみましょう。\n"
  },
  {
    "path": "src/content/learn/manipulating-the-dom-with-refs.md",
    "content": "---\ntitle: 'ref で DOM を操作する'\n---\n\n<Intro>\n\nReact はレンダー結果に合致するよう自動的に [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) を更新するため、コンポーネントで DOM を操作する必要は通常ほとんどありません。ただし、ノードにフォーカスを当てたり、スクロールさせたり、サイズや位置を測定したりするなどの場合に、React が管理する DOM 要素へのアクセスが必要なことがあります。React にはこれらを行う組み込みの方法が存在しないため、DOM ノードを参照する *ref* が必要になります。\n\n</Intro>\n\n<YouWillLearn>\n\n- React が管理する DOM ノードに `ref` 属性を使ってアクセスする方法\n- `ref` JSX 属性が `useRef` フックとどのように関連しているか\n- 別コンポーネントの DOM ノードにアクセスする方法\n- React が管理する DOM を安全に変更できるのはどのような場合か\n\n</YouWillLearn>\n\n## ノードへの ref の取得 {/*getting-a-ref-to-the-node*/}\n\nReact が管理する DOM ノードにアクセスするには、まず `useRef` フックをインポートします。\n\n```js\nimport { useRef } from 'react';\n```\n\n次に、それを使ってコンポーネント内で ref を宣言します。\n\n```js\nconst myRef = useRef(null);\n```\n\n最後に、参照を得たい DOM ノードに対応する JSX タグの `ref` 属性にこの ref を渡します。\n\n```js\n<div ref={myRef}>\n```\n\n`useRef` フックは、`current` という単一のプロパティを持つオブジェクトを返します。最初は `myRef.current` は `null` になっています。React がこの `<div>` に対応する DOM ノードを作成すると、React はこのノードへの参照を `myRef.current` に入れます。その後、[イベントハンドラ](/learn/responding-to-events)からこの DOM ノードにアクセスし、ノードに定義されている組み込みの[ブラウザ API](https://developer.mozilla.org/docs/Web/API/Element) を使用できるようになります。\n\n```js\n// You can use any browser APIs, for example:\nmyRef.current.scrollIntoView();\n```\n\n### 例：テキスト入力フィールドにフォーカスを当てる {/*example-focusing-a-text-input*/}\n\nこの例では、ボタンをクリックすると入力フィールドにフォーカスが当たります。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\n\nexport default function Form() {\n  const inputRef = useRef(null);\n\n  function handleClick() {\n    inputRef.current.focus();\n  }\n\n  return (\n    <>\n      <input ref={inputRef} />\n      <button onClick={handleClick}>\n        Focus the input\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\nこれを実装するには以下のようにします。\n\n1. `useRef` フックを使って `inputRef` を宣言する。\n2. それを `<input ref={inputRef}>` として渡す。これにより、React に**この `<input>` の DOM ノードを `inputRef.current` に入れる**よう指示している。\n3. `handleClick` 関数内で、`inputRef.current` から入力フィールドの DOM ノードを読み取り、`inputRef.current.focus()` のようにして [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) を呼び出す。\n4. `<button>` の `onClick` に `handleClick` イベントハンドラを渡す。\n\nDOM 操作は ref の最も一般的な使用例ですが、`useRef` フックはほかに、タイマー ID などの React 外部にあるものを格納するためにも使用できます。state と同様に、ref はレンダー間で維持されます。ref は、セットしても再レンダーがトリガされない state 変数のようなものです。ref については、[ref で値を参照する](/learn/referencing-values-with-refs)で読むことができます。\n\n### 例：要素へのスクロール {/*example-scrolling-to-an-element*/}\n\nコンポーネントは複数の ref を持つことができます。この例は、3 つの画像でできたカルーセルです。各ボタンは、対応する DOM ノードに定義されているブラウザの [`scrollIntoView()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView) メソッドを呼び出すことで、画像を中央に表示します。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\n\nexport default function CatFriends() {\n  const firstCatRef = useRef(null);\n  const secondCatRef = useRef(null);\n  const thirdCatRef = useRef(null);\n\n  function handleScrollToFirstCat() {\n    firstCatRef.current.scrollIntoView({\n      behavior: 'smooth',\n      block: 'nearest',\n      inline: 'center'\n    });\n  }\n\n  function handleScrollToSecondCat() {\n    secondCatRef.current.scrollIntoView({\n      behavior: 'smooth',\n      block: 'nearest',\n      inline: 'center'\n    });\n  }\n\n  function handleScrollToThirdCat() {\n    thirdCatRef.current.scrollIntoView({\n      behavior: 'smooth',\n      block: 'nearest',\n      inline: 'center'\n    });\n  }\n\n  return (\n    <>\n      <nav>\n        <button onClick={handleScrollToFirstCat}>\n          Neo\n        </button>\n        <button onClick={handleScrollToSecondCat}>\n          Millie\n        </button>\n        <button onClick={handleScrollToThirdCat}>\n          Bella\n        </button>\n      </nav>\n      <div>\n        <ul>\n          <li>\n            <img\n              src=\"https://placecats.com/neo/300/200\"\n              alt=\"Neo\"\n              ref={firstCatRef}\n            />\n          </li>\n          <li>\n            <img\n              src=\"https://placecats.com/millie/200/200\"\n              alt=\"Millie\"\n              ref={secondCatRef}\n            />\n          </li>\n          <li>\n            <img\n              src=\"https://placecats.com/bella/199/200\"\n              alt=\"Bella\"\n              ref={thirdCatRef}\n            />\n          </li>\n        </ul>\n      </div>\n    </>\n  );\n}\n```\n\n```css\ndiv {\n  width: 100%;\n  overflow: hidden;\n}\n\nnav {\n  text-align: center;\n}\n\nbutton {\n  margin: .25rem;\n}\n\nul,\nli {\n  list-style: none;\n  white-space: nowrap;\n}\n\nli {\n  display: inline;\n  padding: 0.5rem;\n}\n```\n\n</Sandpack>\n\n<DeepDive>\n\n#### ref コールバックを使って ref のリストを管理する {/*how-to-manage-a-list-of-refs-using-a-ref-callback*/}\n\n上記の例では、ref の数は事前に決まっていました。しかし、リスト内の各アイテムに ref が必要で、かつ、いくつ必要かわからない場合もあります。以下のようなコードは**機能しません**。\n\n```js\n<ul>\n  {items.map((item) => {\n    // Doesn't work!\n    const ref = useRef(null);\n    return <li ref={ref} />;\n  })}\n</ul>\n```\n\nこれは、**フックはコンポーネントのトップレベルでのみ呼び出される必要がある**ためです。ループ、条件分岐、または `map()` 呼び出しの中で `useRef` を呼び出すことはできません。\n\nこれを回避する方法のひとつは、親要素への単一の ref を取得し、[`querySelectorAll`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) のような DOM 操作メソッドを使って、個々の子ノードを「見つける」ことです。ただし、これは壊れやすく、DOM 構造が変更されると機能しなくなる可能性があります。\n\n別の解決策は、**`ref` 属性に関数を渡す**ことです。これは、[`ref` コールバック](/reference/react-dom/components/common#ref-callback) と呼ばれます。React は、ref を設定するタイミングで DOM ノードを引数にして ref コールバックを呼び出し、クリアするタイミングではそのコールバックが返したクリーンアップ関数を呼び出します。これにより、独自の配列や [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) を保持し、インデックスや ID のようなもので任意の ref にアクセスできるようになります。\n\nこの例では、このアプローチを用いて、長いリストの任意のノードにスクロールする方法を示しています。\n\n<Sandpack>\n\n```js\nimport { useRef, useState } from \"react\";\n\nexport default function CatFriends() {\n  const itemsRef = useRef(null);\n  const [catList, setCatList] = useState(setupCatList);\n\n  function scrollToCat(cat) {\n    const map = getMap();\n    const node = map.get(cat);\n    node.scrollIntoView({\n      behavior: \"smooth\",\n      block: \"nearest\",\n      inline: \"center\",\n    });\n  }\n\n  function getMap() {\n    if (!itemsRef.current) {\n      // Initialize the Map on first usage.\n      itemsRef.current = new Map();\n    }\n    return itemsRef.current;\n  }\n\n  return (\n    <>\n      <nav>\n        <button onClick={() => scrollToCat(catList[0])}>Neo</button>\n        <button onClick={() => scrollToCat(catList[5])}>Millie</button>\n        <button onClick={() => scrollToCat(catList[8])}>Bella</button>\n      </nav>\n      <div>\n        <ul>\n          {catList.map((cat) => (\n            <li\n              key={cat.id}\n              ref={(node) => {\n                const map = getMap();\n                map.set(cat, node);\n\n                return () => {\n                  map.delete(cat);\n                };\n              }}\n            >\n              <img src={cat.imageUrl} />\n            </li>\n          ))}\n        </ul>\n      </div>\n    </>\n  );\n}\n\nfunction setupCatList() {\n  const catCount = 10;\n  const catList = new Array(catCount)\n  for (let i = 0; i < catCount; i++) {\n    let imageUrl = '';\n    if (i < 5) {\n      imageUrl = \"https://placecats.com/neo/320/240\";\n    } else if (i < 8) {\n      imageUrl = \"https://placecats.com/millie/320/240\";\n    } else {\n      imageUrl = \"https://placecats.com/bella/320/240\";\n    }\n    catList[i] = {\n      id: i,\n      imageUrl,\n    };\n  }\n  return catList;\n}\n\n```\n\n```css\ndiv {\n  width: 100%;\n  overflow: hidden;\n}\n\nnav {\n  text-align: center;\n}\n\nbutton {\n  margin: .25rem;\n}\n\nul,\nli {\n  list-style: none;\n  white-space: nowrap;\n}\n\nli {\n  display: inline;\n  padding: 0.5rem;\n}\n```\n\n</Sandpack>\n\nこの例では、`itemsRef` は単一の DOM ノードを保持していません。代わりに、アイテム ID から DOM ノードへの [Map](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map) を保持しています。（[ref はどんな値でも保持できます！](/learn/referencing-values-with-refs)）すべてのリストアイテムの [`ref` コールバック](/reference/react-dom/components/common#ref-callback) が、Map を更新します。\n\n```js\n<li\n  key={cat.id}\n  ref={node => {\n    const map = getMap();\n    // Add to the Map\n    map.set(cat, node);\n\n    return () => {\n      // Remove from the Map\n      map.delete(cat);\n    };\n  }}\n>\n```\n\nこうしておけば、後で Map から個々の DOM ノードを読み取れるようになります。\n\n<Note>\n\nStrict Mode が有効の場合、ref コールバックは開発環境で 2 回呼び出されます。\n\nコールバック ref で[これがバグの発見にどう役立つのか](/reference/react/StrictMode#fixing-bugs-found-by-re-running-ref-callbacks-in-development)ご覧ください。\n\n</Note>\n\n</DeepDive>\n\n## 別のコンポーネントの DOM ノードにアクセスする {/*accessing-another-components-dom-nodes*/}\n\n<Pitfall>\nref は避難ハッチです。他のコンポーネントの DOM ノードを手作業で書き換えるとコードは壊れやすくなってしまいます。\n</Pitfall>\n\n親コンポーネントからは、[普通の props と全く同じやり方で](/learn/passing-props-to-a-component)子コンポーネントに ref を渡すことができます。\n\n```js {3-4,9}\nimport { useRef } from 'react';\n\nfunction MyInput({ ref }) {\n  return <input ref={ref} />;\n}\n\nfunction MyForm() {\n  const inputRef = useRef(null);\n  return <MyInput ref={inputRef} />\n}\n```\n\n上記の例では、ref が親コンポーネントである `MyForm` で作成されており、それが子コンポーネントである `MyInput` に渡されています。`MyInput` は更にその ref を `<input>` に受け渡しています。`<input>` は[組み込みコンポーネント](/reference/react-dom/components/common)なので、React は ref の `.current` プロパティに `<input>` DOM 要素を代入します。\n\nこれで `MyForm` で作られた `inputRef` は、`MyInput` から返される `<input>` DOM 要素を指し示すようになります。`MyForm` で作成されたクリックハンドラは `inputRef` にアクセスして `focus()` を呼び出し、`<input>` にフォーカスを設定できるようになります。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\n\nfunction MyInput({ ref }) {\n  return <input ref={ref} />;\n}\n\nexport default function MyForm() {\n  const inputRef = useRef(null);\n\n  function handleClick() {\n    inputRef.current.focus();\n  }\n\n  return (\n    <>\n      <MyInput ref={inputRef} />\n      <button onClick={handleClick}>\n        Focus the input\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<DeepDive>\n\n#### 命令型ハンドルで API の一部を公開する {/*exposing-a-subset-of-the-api-with-an-imperative-handle*/}\n\n上記の例では、`MyInput` に渡された ref が本来の DOM 要素である input に受け渡されています。これにより親コンポーネント側からその要素の `focus()` を呼び出すことができます。しかしこれにより、親コンポーネントが他のこと、例えば、CSS スタイルを変更することもできてしまいます。一般的なことではありませんが、公開される機能を制限したいということがあります。それには [`useImperativeHandle`](/reference/react/useImperativeHandle) を使います。\n\n<Sandpack>\n\n```js\nimport { useRef, useImperativeHandle } from \"react\";\n\nfunction MyInput({ ref }) {\n  const realInputRef = useRef(null);\n  useImperativeHandle(ref, () => ({\n    // Only expose focus and nothing else\n    focus() {\n      realInputRef.current.focus();\n    },\n  }));\n  return <input ref={realInputRef} />;\n};\n\nexport default function Form() {\n  const inputRef = useRef(null);\n\n  function handleClick() {\n    inputRef.current.focus();\n  }\n\n  return (\n    <>\n      <MyInput ref={inputRef} />\n      <button onClick={handleClick}>Focus the input</button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\nここでは、`MyInput` 内の `realInputRef` が本物の DOM の input ノードを保持しています。ただし、[`useImperativeHandle`](/reference/react/useImperativeHandle) は、親コンポーネントに対して渡す ref の値として、独自の特別なオブジェクトを使うよう、React に指示します。そのため、`Form` コンポーネント内の `inputRef.current` には `focus` メソッドのみが含まれます。この例での、ref \"handle\" とは DOM ノードではなく、[`useImperativeHandle`](/reference/react/useImperativeHandle) の呼び出し内で作成するカスタムオブジェクトです。\n\n</DeepDive>\n\n## React が ref をアタッチするタイミング {/*when-react-attaches-the-refs*/}\n\nReact では、すべての更新は [2 つのフェーズ](/learn/render-and-commit#step-3-react-commits-changes-to-the-dom)に分けて行われます。\n\n* **レンダー**中に、React はコンポーネントを呼び出して画面に表示される内容を決定する。\n* **コミット**中に、React は DOM に変更を適用する。\n\n一般的に、レンダー中に ref にアクセスすることは[望ましくありません](/learn/referencing-values-with-refs#best-practices-for-refs)。これは、DOM ノードを保持するタイプの ref に対しても当てはまります。最初のレンダー中には、DOM ノードがまだ作成されていないため、`ref.current` は `null` になります。また、更新のレンダー中には、DOM ノードがまだ更新されていないため、それらを読むのは早すぎます。\n\nReact が `ref.current` をセットするのはコミット中です。DOM を更新する前に、React は影響を受ける `ref.current` の値を `null` に設定します。DOM を更新した後すぐに、React はそれらを対応する DOM ノードにセットします。\n\n**通常、ref にアクセスするのはイベントハンドラからです**。ref を使って何かをしたいが、それをするための特定のイベントがないという場合は、エフェクト (Effect) が必要になるかもしれません。これ以降の数ページでは、エフェクトについて説明します。\n\n<DeepDive>\n\n#### flushSync で state 更新を同期的にフラッシュする {/*flushing-state-updates-synchronously-with-flush-sync*/}\n\n新しい todo を追加したら画面をリストの最後の子までスクロールする、以下のようなコードを考えてみましょう。どういうわけか常に、最後に追加されたものの *1 つ前*の todo 項目にスクロールされてしまいます。\n\n<Sandpack>\n\n```js\nimport { useState, useRef } from 'react';\n\nexport default function TodoList() {\n  const listRef = useRef(null);\n  const [text, setText] = useState('');\n  const [todos, setTodos] = useState(\n    initialTodos\n  );\n\n  function handleAdd() {\n    const newTodo = { id: nextId++, text: text };\n    setText('');\n    setTodos([ ...todos, newTodo]);\n    listRef.current.lastChild.scrollIntoView({\n      behavior: 'smooth',\n      block: 'nearest'\n    });\n  }\n\n  return (\n    <>\n      <button onClick={handleAdd}>\n        Add\n      </button>\n      <input\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <ul ref={listRef}>\n        {todos.map(todo => (\n          <li key={todo.id}>{todo.text}</li>\n        ))}\n      </ul>\n    </>\n  );\n}\n\nlet nextId = 0;\nlet initialTodos = [];\nfor (let i = 0; i < 20; i++) {\n  initialTodos.push({\n    id: nextId++,\n    text: 'Todo #' + (i + 1)\n  });\n}\n```\n\n</Sandpack>\n\n問題は、以下の 2 行にあります。\n\n```js\nsetTodos([ ...todos, newTodo]);\nlistRef.current.lastChild.scrollIntoView();\n```\n\nReact では、[state 更新はキューに入ります](/learn/queueing-a-series-of-state-updates)。通常、これは望ましい動作です。しかし、ここでは `setTodos` が DOM をすぐに更新しないため、問題が発生します。リストの最後の要素にスクロールするときに、todo がまだ追加されていないためです。これが、スクロールが常に 1 つのアイテム分「遅れて」いる理由です。\n\nこの問題を解決するために、React に DOM を同期的に更新、あるいは「フラッシュ (flush)」するよう強制することができます。これを行うには、`react-dom` から `flushSync` をインポートし、`flushSync` の呼び出しで **state 更新をラップ**します。\n\n```js\nflushSync(() => {\n  setTodos([ ...todos, newTodo]);\n});\nlistRef.current.lastChild.scrollIntoView();\n```\n\nこれにより React に、`flushSync` でラップされたコードが実行された直後に、DOM を同期的に更新するよう指示します。結果として、スクロールしようとするときには最後の todo 項目がすでに DOM に存在することになります。\n\n<Sandpack>\n\n```js\nimport { useState, useRef } from 'react';\nimport { flushSync } from 'react-dom';\n\nexport default function TodoList() {\n  const listRef = useRef(null);\n  const [text, setText] = useState('');\n  const [todos, setTodos] = useState(\n    initialTodos\n  );\n\n  function handleAdd() {\n    const newTodo = { id: nextId++, text: text };\n    flushSync(() => {\n      setText('');\n      setTodos([ ...todos, newTodo]);\n    });\n    listRef.current.lastChild.scrollIntoView({\n      behavior: 'smooth',\n      block: 'nearest'\n    });\n  }\n\n  return (\n    <>\n      <button onClick={handleAdd}>\n        Add\n      </button>\n      <input\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <ul ref={listRef}>\n        {todos.map(todo => (\n          <li key={todo.id}>{todo.text}</li>\n        ))}\n      </ul>\n    </>\n  );\n}\n\nlet nextId = 0;\nlet initialTodos = [];\nfor (let i = 0; i < 20; i++) {\n  initialTodos.push({\n    id: nextId++,\n    text: 'Todo #' + (i + 1)\n  });\n}\n```\n\n</Sandpack>\n\n</DeepDive>\n\n## ref を使った DOM 操作のベストプラクティス {/*best-practices-for-dom-manipulation-with-refs*/}\n\nref は避難ハッチです。「React の外に踏み出す」必要がある場合にのみ使用してください。よくある例としては、フォーカスの管理、スクロール位置の管理、または React が公開していないブラウザ API の呼び出しなどが含まれます。\n\nフォーカスやスクロールのような非破壊的なアクションに留めておけば、問題は発生しないはずです。ただし、DOM を手動で**書き換え**ようとすると、React が行おうとする変更と競合するリスクがあります。\n\n以下はこの問題を説明するための例です。ウェルカムメッセージと 2 つのボタンが含まれています。最初のボタンは、React で通常行うように、[条件付きレンダー](/learn/conditional-rendering)と [state](/learn/state-a-components-memory) を使用してメッセージの有無を切り替えます。2 番目のボタンは、[`remove()` DOM API](https://developer.mozilla.org/en-US/docs/Web/API/Element/remove) を使用して、React の制御外で DOM から強制的にメッセージを削除します。\n\n\"Toggle with setState\" を数回押してみてください。メッセージが消えたり現れたりします。次に、\"Remove from the DOM\" を押してください。これによりメッセージが強制的に削除されます。最後に、\"Toggle with setState\" を押してください。\n\n<Sandpack>\n\n```js\nimport { useState, useRef } from 'react';\n\nexport default function Counter() {\n  const [show, setShow] = useState(true);\n  const ref = useRef(null);\n\n  return (\n    <div>\n      <button\n        onClick={() => {\n          setShow(!show);\n        }}>\n        Toggle with setState\n      </button>\n      <button\n        onClick={() => {\n          ref.current.remove();\n        }}>\n        Remove from the DOM\n      </button>\n      {show && <p ref={ref}>Hello world</p>}\n    </div>\n  );\n}\n```\n\n```css\np,\nbutton {\n  display: block;\n  margin: 10px;\n}\n```\n\n</Sandpack>\n\nDOM 要素を手動で削除した後、`setState` を使用して再度表示しようとすると、クラッシュが発生します。これは、あなたが DOM を書き換えてしまったので、React はそれを正しく管理し続ける方法がわからなくなってしまったからです。\n\n**React が管理する DOM ノードの変更は避けてください**。React が管理する要素を変更しようとしたり、子要素を追加あるいは削除しようとすると、見た目の一貫性が失われたり、上記のようなクラッシュが発生することがあります。\n\nただし、これがまったくできないというわけでもありません。注意が必要だということです。**React が更新する*理由*がない部分であれば、DOM を安全に変更できます**。例えば、ある `<div>` が JSX では常に空である場合、React はその子要素リストに触れる理由がありません。したがって、そこに要素を手動で追加または削除することは安全です。\n\n<Recap>\n\n- ref は一般的な概念だが、ほとんどの場合、DOM 要素を保持するために使用する。\n- `<div ref={myRef}>` のように渡すことで、React に DOM ノードを `myRef.current` に入れるよう指示する。\n- 通常、フォーカス、スクロール、または DOM 要素の測定などの非破壊的なアクションに ref を使用する。\n- コンポーネントはデフォルトでは内部の DOM ノードを公開しない。props として `ref` を用いることで、DOM ノードの公開を明示的に許可する。\n- React によって管理される DOM ノードの変更を避ける。\n- React によって管理される DOM ノードをどうしても変更する場合は、React が更新する理由のない部分のみ変更する。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### ビデオの再生と一時停止 {/*play-and-pause-the-video*/}\n\nこの例では、ボタンが state 変数をトグルして、再生中状態と一時停止状態の間を切り替えます。ただし、ビデオを実際に再生または一時停止するためには、state をトグルするだけでは十分ではありません。`<video>` DOM 要素に対して [`play()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play) および [`pause()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause) を呼び出す必要もあります。この要素に ref を追加し、ボタンを機能させてください。\n\n<Sandpack>\n\n```js\nimport { useState, useRef } from 'react';\n\nexport default function VideoPlayer() {\n  const [isPlaying, setIsPlaying] = useState(false);\n\n  function handleClick() {\n    const nextIsPlaying = !isPlaying;\n    setIsPlaying(nextIsPlaying);\n  }\n\n  return (\n    <>\n      <button onClick={handleClick}>\n        {isPlaying ? 'Pause' : 'Play'}\n      </button>\n      <video width=\"250\">\n        <source\n          src=\"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4\"\n          type=\"video/mp4\"\n        />\n      </video>\n    </>\n  )\n}\n```\n\n```css\nbutton { display: block; margin-bottom: 20px; }\n```\n\n</Sandpack>\n\nさらなるチャレンジとして、ユーザがビデオを右クリックしてブラウザ組み込みのメディアコントロールを使用して再生を行う場合でも、\"Play\" ボタンをビデオの再生状態と同期させるようにしてください。このためにはビデオの `onPlay` と `onPause` をリッスンする必要があるでしょう。\n\n<Solution>\n\nref を宣言し、`<video>` 要素に配置します。次の state に応じて、イベントハンドラ内で `ref.current.play()` または `ref.current.pause()` を呼び出します。\n\n<Sandpack>\n\n```js\nimport { useState, useRef } from 'react';\n\nexport default function VideoPlayer() {\n  const [isPlaying, setIsPlaying] = useState(false);\n  const ref = useRef(null);\n\n  function handleClick() {\n    const nextIsPlaying = !isPlaying;\n    setIsPlaying(nextIsPlaying);\n\n    if (nextIsPlaying) {\n      ref.current.play();\n    } else {\n      ref.current.pause();\n    }\n  }\n\n  return (\n    <>\n      <button onClick={handleClick}>\n        {isPlaying ? 'Pause' : 'Play'}\n      </button>\n      <video\n        width=\"250\"\n        ref={ref}\n        onPlay={() => setIsPlaying(true)}\n        onPause={() => setIsPlaying(false)}\n      >\n        <source\n          src=\"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4\"\n          type=\"video/mp4\"\n        />\n      </video>\n    </>\n  )\n}\n```\n\n```css\nbutton { display: block; margin-bottom: 20px; }\n```\n\n</Sandpack>\n\n組み込みのブラウザコントロールを扱うためには、`<video>` 要素に `onPlay` および `onPause` ハンドラを追加し、それらから `setIsPlaying` を呼び出します。この方法で、ユーザがブラウザコントロールを使用してビデオを再生する場合でも、state がそれに応じて変更されます。\n\n</Solution>\n\n#### 検索フィールドにフォーカス {/*focus-the-search-field*/}\n\n\"Search\" ボタンをクリックすると、フォーカスがフィールドに移動するようにしてください。\n\n<Sandpack>\n\n```js\nexport default function Page() {\n  return (\n    <>\n      <nav>\n        <button>Search</button>\n      </nav>\n      <input\n        placeholder=\"Looking for something?\"\n      />\n    </>\n  );\n}\n```\n\n```css\nbutton { display: block; margin-bottom: 10px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n入力フィールドに ref を追加し、DOM ノードに対して `focus()` を呼び出してフォーカスを当てます。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\n\nexport default function Page() {\n  const inputRef = useRef(null);\n  return (\n    <>\n      <nav>\n        <button onClick={() => {\n          inputRef.current.focus();\n        }}>\n          Search\n        </button>\n      </nav>\n      <input\n        ref={inputRef}\n        placeholder=\"Looking for something?\"\n      />\n    </>\n  );\n}\n```\n\n```css\nbutton { display: block; margin-bottom: 10px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### 画像カルーセルをスクロールする {/*scrolling-an-image-carousel*/}\n\nこの画像カルーセルには、アクティブな画像を切り替える \"Next\" ボタンがあります。クリックしたときにギャラリを水平方向にスクロールし、アクティブな画像に移動するようにしてください。アクティブな画像に対応する DOM ノードに対して [`scrollIntoView()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView) を呼び出しましょう。\n\n```js\nnode.scrollIntoView({\n  behavior: 'smooth',\n  block: 'nearest',\n  inline: 'center'\n});\n```\n\n<Hint>\n\nこの問題では、すべての画像に対する ref を持つ必要はありません。現在アクティブな画像、またはリスト自体に ref があれば十分です。スクロールする*前に* DOM が更新されるよう、`flushSync` を使用してください。\n\n</Hint>\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function CatFriends() {\n  const [index, setIndex] = useState(0);\n  return (\n    <>\n      <nav>\n        <button onClick={() => {\n          if (index < catList.length - 1) {\n            setIndex(index + 1);\n          } else {\n            setIndex(0);\n          }\n        }}>\n          Next\n        </button>\n      </nav>\n      <div>\n        <ul>\n          {catList.map((cat, i) => (\n            <li key={cat.id}>\n              <img\n                className={\n                  index === i ?\n                    'active' :\n                    ''\n                }\n                src={cat.imageUrl}\n                alt={'Cat #' + cat.id}\n              />\n            </li>\n          ))}\n        </ul>\n      </div>\n    </>\n  );\n}\n\nconst catCount = 10;\nconst catList = new Array(catCount);\nfor (let i = 0; i < catCount; i++) {\n  const bucket = Math.floor(Math.random() * catCount) % 2;\n  let imageUrl = '';\n  switch (bucket) {\n    case 0: {\n      imageUrl = \"https://placecats.com/neo/250/200\";\n      break;\n    }\n    case 1: {\n      imageUrl = \"https://placecats.com/millie/250/200\";\n      break;\n    }\n    case 2:\n    default: {\n      imageUrl = \"https://placecats.com/bella/250/200\";\n      break;\n    }\n  }\n  catList[i] = {\n    id: i,\n    imageUrl,\n  };\n}\n\n```\n\n```css\ndiv {\n  width: 100%;\n  overflow: hidden;\n}\n\nnav {\n  text-align: center;\n}\n\nbutton {\n  margin: .25rem;\n}\n\nul,\nli {\n  list-style: none;\n  white-space: nowrap;\n}\n\nli {\n  display: inline;\n  padding: 0.5rem;\n}\n\nimg {\n  padding: 10px;\n  margin: -10px;\n  transition: background 0.2s linear;\n}\n\n.active {\n  background: rgba(0, 100, 150, 0.4);\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n`selectedRef` を宣言し、現在の画像にのみ条件付きで渡すことができます。\n\n```js\n<li ref={index === i ? selectedRef : null}>\n```\n\n`index === i` の場合、つまり当該画像が選択中の場合、`<li>` は `selectedRef` を受け取ります。React は、`selectedRef.current` が常に正しい DOM ノードを指すようにします。\n\n`flushSync` の呼び出しが必要なのは、スクロールの前に React が強制的に DOM を更新するようにするためです。そうしないと、`selectedRef.current` は常に 1 つ前に選択されていたアイテムを指すことになります。\n\n<Sandpack>\n\n```js\nimport { useRef, useState } from 'react';\nimport { flushSync } from 'react-dom';\n\nexport default function CatFriends() {\n  const selectedRef = useRef(null);\n  const [index, setIndex] = useState(0);\n\n  return (\n    <>\n      <nav>\n        <button onClick={() => {\n          flushSync(() => {\n            if (index < catList.length - 1) {\n              setIndex(index + 1);\n            } else {\n              setIndex(0);\n            }\n          });\n          selectedRef.current.scrollIntoView({\n            behavior: 'smooth',\n            block: 'nearest',\n            inline: 'center'\n          });\n        }}>\n          Next\n        </button>\n      </nav>\n      <div>\n        <ul>\n          {catList.map((cat, i) => (\n            <li\n              key={cat.id}\n              ref={index === i ?\n                selectedRef :\n                null\n              }\n            >\n              <img\n                className={\n                  index === i ?\n                    'active'\n                    : ''\n                }\n                src={cat.imageUrl}\n                alt={'Cat #' + cat.id}\n              />\n            </li>\n          ))}\n        </ul>\n      </div>\n    </>\n  );\n}\n\nconst catCount = 10;\nconst catList = new Array(catCount);\nfor (let i = 0; i < catCount; i++) {\n  const bucket = Math.floor(Math.random() * catCount) % 2;\n  let imageUrl = '';\n  switch (bucket) {\n    case 0: {\n      imageUrl = \"https://placecats.com/neo/250/200\";\n      break;\n    }\n    case 1: {\n      imageUrl = \"https://placecats.com/millie/250/200\";\n      break;\n    }\n    case 2:\n    default: {\n      imageUrl = \"https://placecats.com/bella/250/200\";\n      break;\n    }\n  }\n  catList[i] = {\n    id: i,\n    imageUrl,\n  };\n}\n\n```\n\n```css\ndiv {\n  width: 100%;\n  overflow: hidden;\n}\n\nnav {\n  text-align: center;\n}\n\nbutton {\n  margin: .25rem;\n}\n\nul,\nli {\n  list-style: none;\n  white-space: nowrap;\n}\n\nli {\n  display: inline;\n  padding: 0.5rem;\n}\n\nimg {\n  padding: 10px;\n  margin: -10px;\n  transition: background 0.2s linear;\n}\n\n.active {\n  background: rgba(0, 100, 150, 0.4);\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n#### 別々のコンポーネントで検索フィールドにフォーカス {/*focus-the-search-field-with-separate-components*/}\n\n\"Search\" ボタンをクリックすると、フォーカスがフィールドに移動するようにしてください。しかしそれぞれのコンポーネントは別々のファイルで定義されており、そこから移動させてはいけません。どのようにしてそれらをつなぎ合わればよいでしょう？\n\n<Hint>\n\n`SearchInput` のような独自コンポーネントから DOM ノードを公開するためには、props としての `ref` の受け渡しが必要です。\n\n</Hint>\n\n<Sandpack>\n\n```js src/App.js\nimport SearchButton from './SearchButton.js';\nimport SearchInput from './SearchInput.js';\n\nexport default function Page() {\n  return (\n    <>\n      <nav>\n        <SearchButton />\n      </nav>\n      <SearchInput />\n    </>\n  );\n}\n```\n\n```js src/SearchButton.js\nexport default function SearchButton() {\n  return (\n    <button>\n      Search\n    </button>\n  );\n}\n```\n\n```js src/SearchInput.js\nexport default function SearchInput() {\n  return (\n    <input\n      placeholder=\"Looking for something?\"\n    />\n  );\n}\n```\n\n```css\nbutton { display: block; margin-bottom: 10px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n`SearchButton` の props に `onClick` を追加し、`SearchButton` がそれをブラウザの `<button>` に渡すようにします。また、`<SearchInput>` に ref を渡し、コンポーネントが ref を実際の `<input>` に転送し、セットされるようにします。最後に、クリックハンドラ内で、その ref の中に格納されている DOM ノードに対して `focus` を呼び出します。\n\n<Sandpack>\n\n```js src/App.js\nimport { useRef } from 'react';\nimport SearchButton from './SearchButton.js';\nimport SearchInput from './SearchInput.js';\n\nexport default function Page() {\n  const inputRef = useRef(null);\n  return (\n    <>\n      <nav>\n        <SearchButton onClick={() => {\n          inputRef.current.focus();\n        }} />\n      </nav>\n      <SearchInput ref={inputRef} />\n    </>\n  );\n}\n```\n\n```js src/SearchButton.js\nexport default function SearchButton({ onClick }) {\n  return (\n    <button onClick={onClick}>\n      Search\n    </button>\n  );\n}\n```\n\n```js src/SearchInput.js\nexport default function SearchInput({ ref }) {\n  return (\n    <input\n      ref={ref}\n      placeholder=\"Looking for something?\"\n    />\n  );\n}\n```\n\n```css\nbutton { display: block; margin-bottom: 10px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/passing-data-deeply-with-context.md",
    "content": "---\ntitle: コンテクストで深くデータを受け渡す\n---\n\n<Intro>\n\n通常、親コンポーネントから子コンポーネントには props を使って情報を渡します。しかし、props を多数の中間コンポーネントを経由して渡さないといけない場合や、アプリ内の多くのコンポーネントが同じ情報を必要とする場合、props の受け渡しは冗長で不便なものとなり得ます。*コンテクスト (Context)* を使用することで、親コンポーネントから props を明示的に渡さずとも、それ以下のツリー内の任意のコンポーネントが情報を受け取れるようにできます。\n\n</Intro>\n\n<YouWillLearn>\n\n- \"props の穴掘り作業 (prop drilling)\" とは何か\n- コンテクストを使って props の冗長な受け渡しを解消する方法\n- コンテクストの一般的な用途\n- コンテクストの代替手段\n\n</YouWillLearn>\n\n## props の受け渡しに伴う問題 {/*the-problem-with-passing-props*/}\n\n[props の受け渡し](/learn/passing-props-to-a-component)は、UI ツリー内でデータを取り回してそれを必要とするコンポーネントに届けるための素晴らしい手法です。\n\nしかし、props をツリー内の深い位置にまで渡す必要がある場合や、多くのコンポーネントが同じ props を必要としている場合、props の受け渡しは冗長で不便なものとなり得ます。最も近い共通の祖先要素は、データを必要としているコンポーネントから遠く離れているかもしれず、そのような高い位置まで [state をリフトアップ](/learn/sharing-state-between-components)していくと \"props の穴掘り作業 (prop drilling)\" と呼ばれる状況に陥ることがあります。\n\n<DiagramGroup>\n\n<Diagram name=\"passing_data_lifting_state\" height={160} width={608} captionPosition=\"top\" alt=\"3 コンポーネントからなるツリーを表した図。親には紫でハイライトされた値がある。その値は 2 つの子コンポーネント（紫）に渡されている。\">\n\nstate のリフトアップ\n\n</Diagram>\n<Diagram name=\"passing_data_prop_drilling\" height={430} width={608} captionPosition=\"top\" alt=\"10 ノードからなるツリーの図。各ノードには最大 2 つの子がある。ルートノードには紫でハイライトされた値がある。その値はまず 2 つの子に流れるが、いずれも値を下に流しているだけ。ルートの左の子は、2 つの子（紫）に値を渡している。ルートの右の子は、2 つの子のうち片方（紫）にのみ値を渡している。その子はさらに子に値を渡しているが、その子は値を下に流しているだけであり、さらにその子（紫）が値を受け取っている。\">\n\nprops の穴掘り作業\n\n</Diagram>\n\n</DiagramGroup>\n\nもし props を受け渡すことなしに、データを必要としているツリー内のコンポーネントに直接データを「テレポート」させる方法があれば、素晴らしいと思いませんか？ React のコンテクスト機能を使えば、それが可能です！\n\n## コンテクスト：props 受け渡しの代替手段 {/*context-an-alternative-to-passing-props*/}\n\nコンテクストを使うことで、親コンポーネントが配下のツリー全体にデータを提供できます。コンテクストには多くの用途がありますが、以下に一例を示します。見出しサイズを表す `level` を受け取る `Heading` というコンポーネントを考えてみましょう。\n\n<Sandpack>\n\n```js\nimport Heading from './Heading.js';\nimport Section from './Section.js';\n\nexport default function Page() {\n  return (\n    <Section>\n      <Heading level={1}>Title</Heading>\n      <Heading level={2}>Heading</Heading>\n      <Heading level={3}>Sub-heading</Heading>\n      <Heading level={4}>Sub-sub-heading</Heading>\n      <Heading level={5}>Sub-sub-sub-heading</Heading>\n      <Heading level={6}>Sub-sub-sub-sub-heading</Heading>\n    </Section>\n  );\n}\n```\n\n```js src/Section.js\nexport default function Section({ children }) {\n  return (\n    <section className=\"section\">\n      {children}\n    </section>\n  );\n}\n```\n\n```js src/Heading.js\nexport default function Heading({ level, children }) {\n  switch (level) {\n    case 1:\n      return <h1>{children}</h1>;\n    case 2:\n      return <h2>{children}</h2>;\n    case 3:\n      return <h3>{children}</h3>;\n    case 4:\n      return <h4>{children}</h4>;\n    case 5:\n      return <h5>{children}</h5>;\n    case 6:\n      return <h6>{children}</h6>;\n    default:\n      throw Error('Unknown level: ' + level);\n  }\n}\n```\n\n```css\n.section {\n  padding: 10px;\n  margin: 5px;\n  border-radius: 5px;\n  border: 1px solid #aaa;\n}\n```\n\n</Sandpack>\n\nここで、同じ `Section` 内の複数の見出しは常に同じサイズである必要があるとしましょう。\n\n<Sandpack>\n\n```js\nimport Heading from './Heading.js';\nimport Section from './Section.js';\n\nexport default function Page() {\n  return (\n    <Section>\n      <Heading level={1}>Title</Heading>\n      <Section>\n        <Heading level={2}>Heading</Heading>\n        <Heading level={2}>Heading</Heading>\n        <Heading level={2}>Heading</Heading>\n        <Section>\n          <Heading level={3}>Sub-heading</Heading>\n          <Heading level={3}>Sub-heading</Heading>\n          <Heading level={3}>Sub-heading</Heading>\n          <Section>\n            <Heading level={4}>Sub-sub-heading</Heading>\n            <Heading level={4}>Sub-sub-heading</Heading>\n            <Heading level={4}>Sub-sub-heading</Heading>\n          </Section>\n        </Section>\n      </Section>\n    </Section>\n  );\n}\n```\n\n```js src/Section.js\nexport default function Section({ children }) {\n  return (\n    <section className=\"section\">\n      {children}\n    </section>\n  );\n}\n```\n\n```js src/Heading.js\nexport default function Heading({ level, children }) {\n  switch (level) {\n    case 1:\n      return <h1>{children}</h1>;\n    case 2:\n      return <h2>{children}</h2>;\n    case 3:\n      return <h3>{children}</h3>;\n    case 4:\n      return <h4>{children}</h4>;\n    case 5:\n      return <h5>{children}</h5>;\n    case 6:\n      return <h6>{children}</h6>;\n    default:\n      throw Error('Unknown level: ' + level);\n  }\n}\n```\n\n```css\n.section {\n  padding: 10px;\n  margin: 5px;\n  border-radius: 5px;\n  border: 1px solid #aaa;\n}\n```\n\n</Sandpack>\n\n今のところ、`level` を props としてそれぞれの `<Heading>` に個別に渡しています。\n\n```js\n<Section>\n  <Heading level={3}>About</Heading>\n  <Heading level={3}>Photos</Heading>\n  <Heading level={3}>Videos</Heading>\n</Section>\n```\n\nもし `level` を個々の `<Heading>` から削除して、代わりに `<Section>` コンポーネントに渡すことができれば素敵ですね。これにより同じセクション内にあるすべての見出しが同じサイズになることを強制できそうです。\n\n```js\n<Section level={3}>\n  <Heading>About</Heading>\n  <Heading>Photos</Heading>\n  <Heading>Videos</Heading>\n</Section>\n```\n\nですが `<Heading>` コンポーネントは、最も近い `<Section>` のレベルをどうやって知ることができるのでしょうか？ **これには、ツリー上部のどこかにあるデータを子コンポーネントが「要求する」方法が必要です**。\n\nこれは props だけでは実現できません。ここで登場するのがコンテクストです。以下の 3 つの手順で実現できます。\n\n1. コンテクストを**作成**する。（ここでは見出しレベル用なので `LevelContext` と呼ぶ。）\n2. データを必要とするコンポーネントがそのコンテクストを**使用 (use)** する。（`Heading` が `LevelContext` を使用する。）\n3. データを指定するコンポーネントがそのコンテクストを**提供 (provide)** する。（`Section` が `LevelContext` を提供する。）\n\nコンテクストを使うと、離れた親コンポーネントからであっても、その内部のツリー全体にデータを提供できます。\n\n<DiagramGroup>\n\n<Diagram name=\"passing_data_context_close\" height={160} width={608} captionPosition=\"top\" alt=\"3 コンポーネントからなるツリーを表した図。親にはオレンジ色でハイライトされた値があり、それが 2 つの子（オレンジ）にも投影されている。\" >\n\n近くの子にコンテクストを使用\n\n</Diagram>\n\n<Diagram name=\"passing_data_context_far\" height={430} width={608} captionPosition=\"top\" alt=\"10 ノードからなるツリーの図。各ノードには最大 2 つの子がある。ルート親ノードにはオレンジ色でハイライトされた値がある。値はツリー内の 4 つのリーフノードと 1 つの中間コンポーネントに直接投影され、それらはすべてオレンジ色でハイライトされている。他の中間コンポーネントはハイライトされていない。\">\n\n遠くの子にコンテクストを使用\n\n</Diagram>\n\n</DiagramGroup>\n\n### ステップ 1：コンテクストを作成する {/*step-1-create-the-context*/}\n\nまずはコンテクストを作成する必要があります。複数のコンポーネントから使うことができるように、**ファイルを作ってコンテクストをエクスポート**する必要があります。\n\n<Sandpack>\n\n```js\nimport Heading from './Heading.js';\nimport Section from './Section.js';\n\nexport default function Page() {\n  return (\n    <Section>\n      <Heading level={1}>Title</Heading>\n      <Section>\n        <Heading level={2}>Heading</Heading>\n        <Heading level={2}>Heading</Heading>\n        <Heading level={2}>Heading</Heading>\n        <Section>\n          <Heading level={3}>Sub-heading</Heading>\n          <Heading level={3}>Sub-heading</Heading>\n          <Heading level={3}>Sub-heading</Heading>\n          <Section>\n            <Heading level={4}>Sub-sub-heading</Heading>\n            <Heading level={4}>Sub-sub-heading</Heading>\n            <Heading level={4}>Sub-sub-heading</Heading>\n          </Section>\n        </Section>\n      </Section>\n    </Section>\n  );\n}\n```\n\n```js src/Section.js\nexport default function Section({ children }) {\n  return (\n    <section className=\"section\">\n      {children}\n    </section>\n  );\n}\n```\n\n```js src/Heading.js\nexport default function Heading({ level, children }) {\n  switch (level) {\n    case 1:\n      return <h1>{children}</h1>;\n    case 2:\n      return <h2>{children}</h2>;\n    case 3:\n      return <h3>{children}</h3>;\n    case 4:\n      return <h4>{children}</h4>;\n    case 5:\n      return <h5>{children}</h5>;\n    case 6:\n      return <h6>{children}</h6>;\n    default:\n      throw Error('Unknown level: ' + level);\n  }\n}\n```\n\n```js src/LevelContext.js active\nimport { createContext } from 'react';\n\nexport const LevelContext = createContext(1);\n```\n\n```css\n.section {\n  padding: 10px;\n  margin: 5px;\n  border-radius: 5px;\n  border: 1px solid #aaa;\n}\n```\n\n</Sandpack>\n\n`createContext` の唯一の引数は*デフォルト*値です。ここでは、`1` という値は最大の見出しに対応するレベルを示していますが、任意の型の値（オブジェクトでも）を渡すことができます。デフォルト値の意味は次のステップで分かります。\n\n### ステップ 2：コンテクストを使用する {/*step-2-use-the-context*/}\n\nReact の `useContext` フックと、作ったコンテクストをインポートします。\n\n```js\nimport { useContext } from 'react';\nimport { LevelContext } from './LevelContext.js';\n```\n\n現在、`Heading` コンポーネントは `level` を props から読み取っています。\n\n```js\nexport default function Heading({ level, children }) {\n  // ...\n}\n```\n\n代わりに、props から `level` を削除し、さきほどインポートした `LevelContext` から値を読み取るようにします。\n\n```js {2}\nexport default function Heading({ children }) {\n  const level = useContext(LevelContext);\n  // ...\n}\n```\n\n`useContext` はフックです。`useState` や `useReducer` も同じですが、フックは React コンポーネント内のトップレベルでのみ呼び出すことができます（ループや条件分岐の中では呼び出せません）。**`useContext` は、`Heading` コンポーネントが `LevelContext` を読み取りたいのだということを React に伝えています**。\n\nもう `Heading` コンポーネントの props から `level` がなくなったので、以下のように JSX で `Heading` にレベルを渡す必要はありません。\n\n```js\n<Section>\n  <Heading level={4}>Sub-sub-heading</Heading>\n  <Heading level={4}>Sub-sub-heading</Heading>\n  <Heading level={4}>Sub-sub-heading</Heading>\n</Section>\n```\n\nJSX を更新して、代わりに `Section` が `level` を受け取るようにしましょう。\n\n```jsx\n<Section level={4}>\n  <Heading>Sub-sub-heading</Heading>\n  <Heading>Sub-sub-heading</Heading>\n  <Heading>Sub-sub-heading</Heading>\n</Section>\n```\n\n改めて、動作させたいコードを以下に再掲します。\n\n<Sandpack>\n\n```js\nimport Heading from './Heading.js';\nimport Section from './Section.js';\n\nexport default function Page() {\n  return (\n    <Section level={1}>\n      <Heading>Title</Heading>\n      <Section level={2}>\n        <Heading>Heading</Heading>\n        <Heading>Heading</Heading>\n        <Heading>Heading</Heading>\n        <Section level={3}>\n          <Heading>Sub-heading</Heading>\n          <Heading>Sub-heading</Heading>\n          <Heading>Sub-heading</Heading>\n          <Section level={4}>\n            <Heading>Sub-sub-heading</Heading>\n            <Heading>Sub-sub-heading</Heading>\n            <Heading>Sub-sub-heading</Heading>\n          </Section>\n        </Section>\n      </Section>\n    </Section>\n  );\n}\n```\n\n```js src/Section.js\nexport default function Section({ children }) {\n  return (\n    <section className=\"section\">\n      {children}\n    </section>\n  );\n}\n```\n\n```js src/Heading.js\nimport { useContext } from 'react';\nimport { LevelContext } from './LevelContext.js';\n\nexport default function Heading({ children }) {\n  const level = useContext(LevelContext);\n  switch (level) {\n    case 1:\n      return <h1>{children}</h1>;\n    case 2:\n      return <h2>{children}</h2>;\n    case 3:\n      return <h3>{children}</h3>;\n    case 4:\n      return <h4>{children}</h4>;\n    case 5:\n      return <h5>{children}</h5>;\n    case 6:\n      return <h6>{children}</h6>;\n    default:\n      throw Error('Unknown level: ' + level);\n  }\n}\n```\n\n```js src/LevelContext.js\nimport { createContext } from 'react';\n\nexport const LevelContext = createContext(1);\n```\n\n```css\n.section {\n  padding: 10px;\n  margin: 5px;\n  border-radius: 5px;\n  border: 1px solid #aaa;\n}\n```\n\n</Sandpack>\n\nこの例はまだちゃんと動作していません。すべての見出しが同じサイズになってしまっているのは、**コンテクストの*使用*はしているが、まだコンテクストの*提供*を行っていない**からです。React はどこから値を取得すればいいのか分かりません！\n\nコンテクストが提供されていない場合、React は前のステップで指定したデフォルト値を使用します。この例では、`createContext` の引数に `1` を指定していましたので、`useContext(LevelContext)` は `1` を返し、そのためこれらの見出しは全部 `<h1>` になってしまっています。これを修正するため、各 `Section` がそれぞれコンテクストを提供するようにしましょう。\n\n### ステップ 3：コンテクストを提供する {/*step-3-provide-the-context*/}\n\n現在 `Section` コンポーネントは子要素を直接レンダーしています。\n\n```js\nexport default function Section({ children }) {\n  return (\n    <section className=\"section\">\n      {children}\n    </section>\n  );\n}\n```\n\nこれを**コンテクストプロバイダでラップ**し、`LevelContext` の値を提供します。\n\n```js {1,6,8}\nimport { LevelContext } from './LevelContext.js';\n\nexport default function Section({ level, children }) {\n  return (\n    <section className=\"section\">\n      <LevelContext value={level}>\n        {children}\n      </LevelContext>\n    </section>\n  );\n}\n```\n\nこれにより、「この `<Section>` の下にあるコンポーネントが `LevelContext` の値を要求した場合、この `level` を渡せ」と React に伝えていることになります。コンポーネントは、UI ツリー内の上側で、最も近い `<LevelContext>` の値を使用します。\n\n<Sandpack>\n\n```js\nimport Heading from './Heading.js';\nimport Section from './Section.js';\n\nexport default function Page() {\n  return (\n    <Section level={1}>\n      <Heading>Title</Heading>\n      <Section level={2}>\n        <Heading>Heading</Heading>\n        <Heading>Heading</Heading>\n        <Heading>Heading</Heading>\n        <Section level={3}>\n          <Heading>Sub-heading</Heading>\n          <Heading>Sub-heading</Heading>\n          <Heading>Sub-heading</Heading>\n          <Section level={4}>\n            <Heading>Sub-sub-heading</Heading>\n            <Heading>Sub-sub-heading</Heading>\n            <Heading>Sub-sub-heading</Heading>\n          </Section>\n        </Section>\n      </Section>\n    </Section>\n  );\n}\n```\n\n```js src/Section.js\nimport { LevelContext } from './LevelContext.js';\n\nexport default function Section({ level, children }) {\n  return (\n    <section className=\"section\">\n      <LevelContext value={level}>\n        {children}\n      </LevelContext>\n    </section>\n  );\n}\n```\n\n```js src/Heading.js\nimport { useContext } from 'react';\nimport { LevelContext } from './LevelContext.js';\n\nexport default function Heading({ children }) {\n  const level = useContext(LevelContext);\n  switch (level) {\n    case 1:\n      return <h1>{children}</h1>;\n    case 2:\n      return <h2>{children}</h2>;\n    case 3:\n      return <h3>{children}</h3>;\n    case 4:\n      return <h4>{children}</h4>;\n    case 5:\n      return <h5>{children}</h5>;\n    case 6:\n      return <h6>{children}</h6>;\n    default:\n      throw Error('Unknown level: ' + level);\n  }\n}\n```\n\n```js src/LevelContext.js\nimport { createContext } from 'react';\n\nexport const LevelContext = createContext(1);\n```\n\n```css\n.section {\n  padding: 10px;\n  margin: 5px;\n  border-radius: 5px;\n  border: 1px solid #aaa;\n}\n```\n\n</Sandpack>\n\n元のコードと見た目の結果は同じですが、`level` を props として個々の `Heading` コンポーネントに渡さずに済んでいます！ 代わりに、見出しは最も近い `Section` に値を要求して、自分の見出しレベルを自分で「判断」しているのです。\n\n1. `<Section>` に props として `level` を渡す。\n2. `Section` は子要素を `<LevelContext value={level}>` でラップする。\n3. `Heading` は `useContext(LevelContext)` とすることで、上にある最も近い `LevelContext` の値を要求する。\n\n## 同一コンポーネントでコンテクストを使用しつつ提供 {/*using-and-providing-context-from-the-same-component*/}\n\n今はまだ、セクションごとに `level` を手動で指定する必要があります。\n\n```js\nexport default function Page() {\n  return (\n    <Section level={1}>\n      ...\n      <Section level={2}>\n        ...\n        <Section level={3}>\n          ...\n```\n\nコンテクストによりコンポーネントの上部から情報を読み取ることができるため、各 `Section` は上にある別の `Section` から `level` を読み取りつつ、`level + 1` を下に渡すことができます。以下のようになります。\n\n```js src/Section.js {5,8}\nimport { useContext } from 'react';\nimport { LevelContext } from './LevelContext.js';\n\nexport default function Section({ children }) {\n  const level = useContext(LevelContext);\n  return (\n    <section className=\"section\">\n      <LevelContext value={level + 1}>\n        {children}\n      </LevelContext>\n    </section>\n  );\n}\n```\n\nこの変更により、`<Section>` や `<Heading>` の*どちらにも* props として `level` を渡さずに済むようになりました。\n\n<Sandpack>\n\n```js\nimport Heading from './Heading.js';\nimport Section from './Section.js';\n\nexport default function Page() {\n  return (\n    <Section>\n      <Heading>Title</Heading>\n      <Section>\n        <Heading>Heading</Heading>\n        <Heading>Heading</Heading>\n        <Heading>Heading</Heading>\n        <Section>\n          <Heading>Sub-heading</Heading>\n          <Heading>Sub-heading</Heading>\n          <Heading>Sub-heading</Heading>\n          <Section>\n            <Heading>Sub-sub-heading</Heading>\n            <Heading>Sub-sub-heading</Heading>\n            <Heading>Sub-sub-heading</Heading>\n          </Section>\n        </Section>\n      </Section>\n    </Section>\n  );\n}\n```\n\n```js src/Section.js\nimport { useContext } from 'react';\nimport { LevelContext } from './LevelContext.js';\n\nexport default function Section({ children }) {\n  const level = useContext(LevelContext);\n  return (\n    <section className=\"section\">\n      <LevelContext value={level + 1}>\n        {children}\n      </LevelContext>\n    </section>\n  );\n}\n```\n\n```js src/Heading.js\nimport { useContext } from 'react';\nimport { LevelContext } from './LevelContext.js';\n\nexport default function Heading({ children }) {\n  const level = useContext(LevelContext);\n  switch (level) {\n    case 0:\n      throw Error('Heading must be inside a Section!');\n    case 1:\n      return <h1>{children}</h1>;\n    case 2:\n      return <h2>{children}</h2>;\n    case 3:\n      return <h3>{children}</h3>;\n    case 4:\n      return <h4>{children}</h4>;\n    case 5:\n      return <h5>{children}</h5>;\n    case 6:\n      return <h6>{children}</h6>;\n    default:\n      throw Error('Unknown level: ' + level);\n  }\n}\n```\n\n```js src/LevelContext.js\nimport { createContext } from 'react';\n\nexport const LevelContext = createContext(0);\n```\n\n```css\n.section {\n  padding: 10px;\n  margin: 5px;\n  border-radius: 5px;\n  border: 1px solid #aaa;\n}\n```\n\n</Sandpack>\n\n`Heading` と `Section` の両方が、`LevelContext` を読み取ることで自分がどの「深さ」にいるかを判断しています。また、`Section` はその子要素を `LevelContext` プロバイダ内にラップすることで、その中のすべてのものが「1 段階深い」レベルになるよう指定します。\n\n<Note>\n\n見出しレベルを例として使用したのは、ネストされたコンポーネントでどのようにコンテクストが上書きされていくのか視覚的に説明できるからです。しかしコンテクストは他の多くの用途でも役立ちます。サブツリー全体が必要とする情報（現在のカラーテーマや現在ログイン中のユーザなど）であれば、どんなものでも渡すことができます。\n\n</Note>\n\n## コンテクストは中間コンポーネントを貫通する {/*context-passes-through-intermediate-components*/}\n\nコンテクストを提供するコンポーネントとそれを利用するコンポーネントの間には、好きなだけコンポーネントを挿入することができます。`<div>` などの組み込みコンポーネントでも、自分で構築するコンポーネントでも構いません。\n\nこの例では、同じ `Post` コンポーネント（破線ボーダー）が、2 つの異なるネストレベルでレンダーされています。`Post` の中にある `<Heading>` が、最も近くにある `<Section>` から自動的に見出しレベルを取得できていることに注目してください：\n\n<Sandpack>\n\n```js\nimport Heading from './Heading.js';\nimport Section from './Section.js';\n\nexport default function ProfilePage() {\n  return (\n    <Section>\n      <Heading>My Profile</Heading>\n      <Post\n        title=\"Hello traveller!\"\n        body=\"Read about my adventures.\"\n      />\n      <AllPosts />\n    </Section>\n  );\n}\n\nfunction AllPosts() {\n  return (\n    <Section>\n      <Heading>Posts</Heading>\n      <RecentPosts />\n    </Section>\n  );\n}\n\nfunction RecentPosts() {\n  return (\n    <Section>\n      <Heading>Recent Posts</Heading>\n      <Post\n        title=\"Flavors of Lisbon\"\n        body=\"...those pastéis de nata!\"\n      />\n      <Post\n        title=\"Buenos Aires in the rhythm of tango\"\n        body=\"I loved it!\"\n      />\n    </Section>\n  );\n}\n\nfunction Post({ title, body }) {\n  return (\n    <Section isFancy={true}>\n      <Heading>\n        {title}\n      </Heading>\n      <p><i>{body}</i></p>\n    </Section>\n  );\n}\n```\n\n```js src/Section.js\nimport { useContext } from 'react';\nimport { LevelContext } from './LevelContext.js';\n\nexport default function Section({ children, isFancy }) {\n  const level = useContext(LevelContext);\n  return (\n    <section className={\n      'section ' +\n      (isFancy ? 'fancy' : '')\n    }>\n      <LevelContext value={level + 1}>\n        {children}\n      </LevelContext>\n    </section>\n  );\n}\n```\n\n```js src/Heading.js\nimport { useContext } from 'react';\nimport { LevelContext } from './LevelContext.js';\n\nexport default function Heading({ children }) {\n  const level = useContext(LevelContext);\n  switch (level) {\n    case 0:\n      throw Error('Heading must be inside a Section!');\n    case 1:\n      return <h1>{children}</h1>;\n    case 2:\n      return <h2>{children}</h2>;\n    case 3:\n      return <h3>{children}</h3>;\n    case 4:\n      return <h4>{children}</h4>;\n    case 5:\n      return <h5>{children}</h5>;\n    case 6:\n      return <h6>{children}</h6>;\n    default:\n      throw Error('Unknown level: ' + level);\n  }\n}\n```\n\n```js src/LevelContext.js\nimport { createContext } from 'react';\n\nexport const LevelContext = createContext(0);\n```\n\n```css\n.section {\n  padding: 10px;\n  margin: 5px;\n  border-radius: 5px;\n  border: 1px solid #aaa;\n}\n\n.fancy {\n  border: 4px dashed pink;\n}\n```\n\n</Sandpack>\n\nこれを動作させるために特別なことは何もしていません。`Section` が内部のツリーに対するコンテクストを指定しているため、どこにでも `<Heading>` を挿入することができ、正しいサイズで表示されます。上のサンドボックスで試してみてください！\n\n**コンテクストを使うことで、*どんな場所で*、すなわち*どんな文脈（コンテクスト）で*レンダーされているかに応じて異なる内容を表示できる、「周囲に適応」するコンポーネントを書けるようになります**。\n\nコンテクストの仕組みは、[CSS におけるプロパティの継承](https://developer.mozilla.org/en-US/docs/Web/CSS/inheritance)に似ていると感じるかもしれません。CSS では、ある `<div>` に対して `color: blue` を指定すると、他の DOM ノードが途中で `color: green` などで上書きしない限り、どんなに深くネストされた DOM ノードにもその色が継承されます。同様に React では、上からやってくるコンテクストを上書きする唯一の方法は、別の値を指定したコンテクストプロバイダで子をラップすることです。\n\nCSS では、`color` や `background-color` といった異なるプロパティがお互いを上書きすることはありません。すべての `<div>` の `color` を赤に設定しても、`background-color` には影響しません。同様に、**異なる React コンテクストはお互いを上書きしません**。`createContext()` で作成したそれぞれのコンテクストは、他のものと完全に切り離されており、*その特定のコンテクスト*を使用あるいは提供しているコンポーネント同士だけを結びつけます。1 つのコンポーネントが、異なるコンテクストをいくつも使用しても、あるいは提供しても、問題ありません。\n\n## コンテクストを使う前に {/*before-you-use-context*/}\n\nコンテクストはとても魅力的です！ しかし、これはコンテクストは使いすぎにつながりやすいということでもあります。**いくつかの props を数レベルの深さにわたって受け渡す必要があるというだけでは、その情報をコンテクストに入れるべきとはいえません**。\n\nここで紹介するように、コンテクストを使う前に検討すべきいくつかの代替案があります。\n\n1. **まずは [props を渡す](/learn/passing-props-to-a-component)方法から始めましょう**。ちょっと凝ったコンポーネントであれば、多くの props を多くのコンポーネントを通して受け渡すことは珍しくありません。退屈な仕事に感じるかもしれませんが、どのコンポーネントがどのデータを使っているかが非常に明確になります！ コードをメンテナンスする人は、props を使ってデータの流れが明確に表現されていることに感謝するでしょう。\n2. **コンポーネントを抽出して、[`children` を JSX として渡す](/learn/passing-props-to-a-component#passing-jsx-as-children)方法を検討しましょう**。もし、何らかのデータを、それを必要とせずただ下に流すだけの中間コンポーネントを何層も経由して受け渡ししているような場合、何かコンポーネントを抽出するのを忘れているということかもしれません。たとえば、`<Layout posts={posts} />` のような形で、データを直接使わないビジュアルコンポーネントに `post` のようなデータを渡しているのかもしれません。代わりに、`Layout` は `children` を props として受け取るようにし、`<Layout><Posts posts={posts} /></Layout>` のようにレンダーしてみましょう。これにより、データを指定するコンポーネントとそれを必要とするコンポーネントの間のレイヤ数が減ります。\n\nこれらのアプローチがどちらもうまくいかない場合は、コンテクストを検討してください。\n\n## コンテクストのさまざまな用途 {/*use-cases-for-context*/}\n\n* **テーマ**：例えばダークモードのように、アプリの外見をユーザが変更できる場合は、アプリのトップレベルにコンテクストプロバイダを配置し、外観を変化させる必要があるコンポーネントでそのコンテクストを使用します。\n* **現在のアカウント**：多くのコンポーネントは、現在ログインしているユーザを知る必要があります。それをコンテクストに入れることで、ツリーのどこからでも読み取りが容易になります。一部のアプリでは、複数のアカウントを同時に操作できます（例：別のユーザとしてコメントを残す）。このような場合、別の現在アカウントを指定したプロバイダをネストして、UI の一部をラップすることが有用です。\n* **ルーティング**：ほとんどのルーティングソリューションは、現在のルートを保持するために内部でコンテクストを使用しています。これが、自身がアクティブかどうかをすべてのリンクが「知っている」理由です。独自のルータを構築する場合は自分でもこれを行いたいでしょう。\n* **state 管理**：アプリが大きくなると、アプリのトップ近くに大量の state が集まってくることがあります。下の遠いところにある多くのコンポーネントがその state を変更する必要があるかもしれません。[リデューサとコンテクストを一緒に使用](/learn/scaling-up-with-reducer-and-context)することは一般的であり、これにより大変な手間をかけずに、複雑な state を離れたコンポーネントへ受け渡すことができます。\n  \nコンテクストで扱う値は静的なものとは限りません。次のレンダーで異なる値を渡すと、React はその下でそれを必要しているすべてのコンポーネントを更新します！ これがコンテクストが state と一緒によく使われる理由です。\n\n一般的に、ある情報が、ツリーの様々な部分にある離れたコンポーネントによって必要とされている場合、コンテクストが役立つというサインです。\n\n<Recap>\n\n* コンテクストにより、コンポーネントがそれ以下のツリー全体に情報を提供できる。\n* コンテクストを使うには：\n  1. `export const MyContext = createContext(defaultValue)` を使用して作成およびエクスポートする。\n  2. フックに `useContext(MyContext)` のようにコンテクストを渡せば、どんな深い子コンポーネントからも値が読み取れる。\n  3. コンテクストの値を提供するには子要素を `<MyContext value={...}>` でラップする。\n* コンテクストは中間コンポーネントを貫通する。\n* コンテクストを使えば、「周囲に適応する」コンポーネントが書ける。\n* コンテクストを使用する前に、props を渡すか、`children` として JSX を渡す方法を検討してみる。\n\n</Recap>\n\n<Challenges>\n\n#### props の穴掘り作業をコンテクストで置換 {/*replace-prop-drilling-with-context*/}\n\nこの例では、チェックボックスを切り替えることで、各 `<PlaceImage>` に渡される `imageSize` プロパティが変更されます。チェックボックスの state はトップレベルの `App` コンポーネントで保持されていますが、各 `<PlaceImage>` はそれを認識する必要があります。\n\n現在、`App` は `imageSize` を `List` に渡し、`List` はそれを各 `Place` に渡し、`Place` はそれを `PlaceImage` に渡しています。props から `imageSize` を削除し、代わりにそれを `App` コンポーネントから直接 `PlaceImage` に渡すようにしてください。\n\nコンテクストの宣言は `Context.js` 内で行えます。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { places } from './data.js';\nimport { getImageUrl } from './utils.js';\n\nexport default function App() {\n  const [isLarge, setIsLarge] = useState(false);\n  const imageSize = isLarge ? 150 : 100;\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isLarge}\n          onChange={e => {\n            setIsLarge(e.target.checked);\n          }}\n        />\n        Use large images\n      </label>\n      <hr />\n      <List imageSize={imageSize} />\n    </>\n  )\n}\n\nfunction List({ imageSize }) {\n  const listItems = places.map(place =>\n    <li key={place.id}>\n      <Place\n        place={place}\n        imageSize={imageSize}\n      />\n    </li>\n  );\n  return <ul>{listItems}</ul>;\n}\n\nfunction Place({ place, imageSize }) {\n  return (\n    <>\n      <PlaceImage\n        place={place}\n        imageSize={imageSize}\n      />\n      <p>\n        <b>{place.name}</b>\n        {': ' + place.description}\n      </p>\n    </>\n  );\n}\n\nfunction PlaceImage({ place, imageSize }) {\n  return (\n    <img\n      src={getImageUrl(place)}\n      alt={place.name}\n      width={imageSize}\n      height={imageSize}\n    />\n  );\n}\n```\n\n```js src/Context.js\n\n```\n\n```js src/data.js\nexport const places = [{\n  id: 0,\n  name: 'Bo-Kaap in Cape Town, South Africa',\n  description: 'The tradition of choosing bright colors for houses began in the late 20th century.',\n  imageId: 'K9HVAGH'\n}, {\n  id: 1, \n  name: 'Rainbow Village in Taichung, Taiwan',\n  description: 'To save the houses from demolition, Huang Yung-Fu, a local resident, painted all 1,200 of them in 1924.',\n  imageId: '9EAYZrt'\n}, {\n  id: 2, \n  name: 'Macromural de Pachuca, Mexico',\n  description: 'One of the largest murals in the world covering homes in a hillside neighborhood.',\n  imageId: 'DgXHVwu'\n}, {\n  id: 3, \n  name: 'Selarón Staircase in Rio de Janeiro, Brazil',\n  description: 'This landmark was created by Jorge Selarón, a Chilean-born artist, as a \"tribute to the Brazilian people.\"',\n  imageId: 'aeO3rpI'\n}, {\n  id: 4, \n  name: 'Burano, Italy',\n  description: 'The houses are painted following a specific color system dating back to 16th century.',\n  imageId: 'kxsph5C'\n}, {\n  id: 5, \n  name: 'Chefchaouen, Marocco',\n  description: 'There are a few theories on why the houses are painted blue, including that the color repels mosquitos or that it symbolizes sky and heaven.',\n  imageId: 'rTqKo46'\n}, {\n  id: 6,\n  name: 'Gamcheon Culture Village in Busan, South Korea',\n  description: 'In 2009, the village was converted into a cultural hub by painting the houses and featuring exhibitions and art installations.',\n  imageId: 'ZfQOOzf'\n}];\n```\n\n```js src/utils.js\nexport function getImageUrl(place) {\n  return (\n    'https://i.imgur.com/' +\n    place.imageId +\n    'l.jpg'\n  );\n}\n```\n\n```css\nul { list-style-type: none; padding: 0px 10px; }\nli { \n  margin-bottom: 10px; \n  display: grid; \n  grid-template-columns: auto 1fr;\n  gap: 20px;\n  align-items: center;\n}\n```\n\n</Sandpack>\n\n<Solution>\n\nすべてのコンポーネントの props から `imageSize` を削除します。\n\n`Context.js` で `ImageSizeContext` を作成してエクスポートします。次に、List を `<ImageSizeContext value={imageSize}>` でラップすることで下に値を渡します。`PlaceImage` で `useContext(ImageSizeContext)` を使ってそれを読み取ります。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState, useContext } from 'react';\nimport { places } from './data.js';\nimport { getImageUrl } from './utils.js';\nimport { ImageSizeContext } from './Context.js';\n\nexport default function App() {\n  const [isLarge, setIsLarge] = useState(false);\n  const imageSize = isLarge ? 150 : 100;\n  return (\n    <ImageSizeContext\n      value={imageSize}\n    >\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isLarge}\n          onChange={e => {\n            setIsLarge(e.target.checked);\n          }}\n        />\n        Use large images\n      </label>\n      <hr />\n      <List />\n    </ImageSizeContext>\n  )\n}\n\nfunction List() {\n  const listItems = places.map(place =>\n    <li key={place.id}>\n      <Place place={place} />\n    </li>\n  );\n  return <ul>{listItems}</ul>;\n}\n\nfunction Place({ place }) {\n  return (\n    <>\n      <PlaceImage place={place} />\n      <p>\n        <b>{place.name}</b>\n        {': ' + place.description}\n      </p>\n    </>\n  );\n}\n\nfunction PlaceImage({ place }) {\n  const imageSize = useContext(ImageSizeContext);\n  return (\n    <img\n      src={getImageUrl(place)}\n      alt={place.name}\n      width={imageSize}\n      height={imageSize}\n    />\n  );\n}\n```\n\n```js src/Context.js\nimport { createContext } from 'react';\n\nexport const ImageSizeContext = createContext(500);\n```\n\n```js src/data.js\nexport const places = [{\n  id: 0,\n  name: 'Bo-Kaap in Cape Town, South Africa',\n  description: 'The tradition of choosing bright colors for houses began in the late 20th century.',\n  imageId: 'K9HVAGH'\n}, {\n  id: 1, \n  name: 'Rainbow Village in Taichung, Taiwan',\n  description: 'To save the houses from demolition, Huang Yung-Fu, a local resident, painted all 1,200 of them in 1924.',\n  imageId: '9EAYZrt'\n}, {\n  id: 2, \n  name: 'Macromural de Pachuca, Mexico',\n  description: 'One of the largest murals in the world covering homes in a hillside neighborhood.',\n  imageId: 'DgXHVwu'\n}, {\n  id: 3, \n  name: 'Selarón Staircase in Rio de Janeiro, Brazil',\n  description: 'This landmark was created by Jorge Selarón, a Chilean-born artist, as a \"tribute to the Brazilian people\".',\n  imageId: 'aeO3rpI'\n}, {\n  id: 4, \n  name: 'Burano, Italy',\n  description: 'The houses are painted following a specific color system dating back to 16th century.',\n  imageId: 'kxsph5C'\n}, {\n  id: 5, \n  name: 'Chefchaouen, Marocco',\n  description: 'There are a few theories on why the houses are painted blue, including that the color repels mosquitos or that it symbolizes sky and heaven.',\n  imageId: 'rTqKo46'\n}, {\n  id: 6,\n  name: 'Gamcheon Culture Village in Busan, South Korea',\n  description: 'In 2009, the village was converted into a cultural hub by painting the houses and featuring exhibitions and art installations.',\n  imageId: 'ZfQOOzf'\n}];\n```\n\n```js src/utils.js\nexport function getImageUrl(place) {\n  return (\n    'https://i.imgur.com/' +\n    place.imageId +\n    'l.jpg'\n  );\n}\n```\n\n```css\nul { list-style-type: none; padding: 0px 10px; }\nli { \n  margin-bottom: 10px; \n  display: grid; \n  grid-template-columns: auto 1fr;\n  gap: 20px;\n  align-items: center;\n}\n```\n\n</Sandpack>\n\nこれで中間コンポーネントが `imageSize` を渡す必要がなくなっていることに注意してください。\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/passing-props-to-a-component.md",
    "content": "---\ntitle: コンポーネントに props を渡す\n---\n\n<Intro>\n\nReact コンポーネントは互いにやりとりをする際に *props* というものを使います。親コンポーネントは子コンポーネントに props を渡すことで情報を伝えることができるのです。props は HTML の属性と似ていると思われるかもしれませんが、props ではオブジェクトや配列、関数などのあらゆる JavaScript の値を渡すことができます。\n\n</Intro>\n\n<YouWillLearn>\n\n* コンポーネントに props を渡す方法\n* コンポーネントから props を読み出す方法\n* props のデフォルト値を指定する方法\n* コンポーネントに JSX を渡す方法\n* props は時間とともに変化する\n\n</YouWillLearn>\n\n## お馴染みの props {/*familiar-props*/}\n\nprops とは JSX タグに渡す情報のことです。例えば `className`、`src`、`alt`、`width` や `height` は、`<img>` に渡すことのできる props の例です。\n\n<Sandpack>\n\n```js\nfunction Avatar() {\n  return (\n    <img\n      className=\"avatar\"\n      src=\"https://i.imgur.com/1bX5QH6.jpg\"\n      alt=\"Lin Lanying\"\n      width={100}\n      height={100}\n    />\n  );\n}\n\nexport default function Profile() {\n  return (\n    <Avatar />\n  );\n}\n```\n\n```css\nbody { min-height: 120px; }\n.avatar { margin: 20px; border-radius: 50%; }\n```\n\n</Sandpack>\n\n`<img>` に渡すことができる props の種類は事前に決められています（ReactDOM は [HTML 標準](https://www.w3.org/TR/html52/semantics-embedded-content.html#the-img-element)に準拠しています）。しかし `<Avatar>` のような*あなた独自の*コンポーネントの場合は、任意の props を渡してそれをカスタマイズできます。以下でやり方を説明します。\n\n## コンポーネントに props を渡す {/*passing-props-to-a-component*/}\n\n以下のコードでは、`Profile` コンポーネントは子コンポーネントである `<Avatar>` に何の props も渡していません。\n\n```js\nexport default function Profile() {\n  return (\n    <Avatar />\n  );\n}\n```\n\n以下の 2 ステップで、`Avatar` に props を与えることができます。\n\n### Step 1: 子コンポーネントに props を渡す {/*step-1-pass-props-to-the-child-component*/}\n\nまず、`Avatar` に何か props を渡します。例えば `person`（オブジェクト）と `size`（数値）を渡してみましょう。\n\n```js\nexport default function Profile() {\n  return (\n    <Avatar\n      person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}\n      size={100}\n    />\n  );\n}\n```\n\n<Note>\n\nもし `person=` の後にある二重の波括弧が分からない場合、これが JSX 波括弧の中にある[単なるオブジェクト](/learn/javascript-in-jsx-with-curly-braces#using-double-curlies-css-and-other-objects-in-jsx)であるということを思い出してください。\n\n</Note>\n\nこれでこの props を `Avatar` コンポーネント内から読み出せるようになります。\n\n### Step 2: 子コンポーネントから props を読み出す {/*step-2-read-props-inside-the-child-component*/}\n\nこれらの props を読み出すには、`function Avatar` の直後の `({` と `})` 内で、コンマで区切って `person, size` のように名前を指定します。これにより `Avatar` のコード内で変数と同じようにこれらの props が使えるようになります。\n\n```js\nfunction Avatar({ person, size }) {\n  // person and size are available here\n}\n```\n\n`Avatar` 内に、`person` や `size` を使って何かをレンダーするロジックを書き加えれば完成です。\n\nこれで `Avatar` に様々な props を渡すことで様々に表示を変えられるようになりました。実際に値をいじってみましょう！\n\n<Sandpack>\n\n```js src/App.js\nimport { getImageUrl } from './utils.js';\n\nfunction Avatar({ person, size }) {\n  return (\n    <img\n      className=\"avatar\"\n      src={getImageUrl(person)}\n      alt={person.name}\n      width={size}\n      height={size}\n    />\n  );\n}\n\nexport default function Profile() {\n  return (\n    <div>\n      <Avatar\n        size={100}\n        person={{ \n          name: 'Katsuko Saruhashi', \n          imageId: 'YfeOqp2'\n        }}\n      />\n      <Avatar\n        size={80}\n        person={{\n          name: 'Aklilu Lemma', \n          imageId: 'OKS67lh'\n        }}\n      />\n      <Avatar\n        size={50}\n        person={{ \n          name: 'Lin Lanying',\n          imageId: '1bX5QH6'\n        }}\n      />\n    </div>\n  );\n}\n```\n\n```js src/utils.js\nexport function getImageUrl(person, size = 's') {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    size +\n    '.jpg'\n  );\n}\n```\n\n```css\nbody { min-height: 120px; }\n.avatar { margin: 10px; border-radius: 50%; }\n```\n\n</Sandpack>\n\nprops のおかげで、親と子のコンポーネントを独立して考えることができるようになります。例えば、`Profile` で `person` や `size` を変更するときに `Avatar` 内でどう使われるかを気にしないでよくなります。同様に、`Avatar` がこれらの props をどのように使うのかは、`Profile` を見ずに変更できるようになります。\n\nprops とは自分で調整できるコントローラの「ツマミ」のようなものです。関数における引数と同じ役割を果たしています。むしろ、props があなたのコンポーネントの唯一の引数です！ React コンポーネントは `props` というオブジェクトを唯一の引数として受け取っているのです。\n\n```js\nfunction Avatar(props) {\n  let person = props.person;\n  let size = props.size;\n  // ...\n}\n```\n\n通常は `props` オブジェクト全体を必要とすることはないため、個々の props へと分割代入します。\n\n<Pitfall>\n\nprops を宣言する際は `(` と `)` の中に、**`{` と `}` という波括弧のペアを書き忘れない**ようにしましょう。\n\n```js\nfunction Avatar({ person, size }) {\n  // ...\n}\n```\n\nこの構文は [\"分割代入 (destructuring)\"](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Unpacking_fields_from_objects_passed_as_a_function_parameter) と呼ばれるもので、関数の引数からプロパティを読み出す以下のようなコードと同等です。\n\n```js\nfunction Avatar(props) {\n  let person = props.person;\n  let size = props.size;\n  // ...\n}\n```\n\n</Pitfall>\n\n## props のデフォルト値を指定する {/*specifying-a-default-value-for-a-prop*/}\n\nprops に、値が渡されなかった場合にフォールバックとして使うデフォルト値を指定したい場合、分割代入の中でパラメータ名の直後に `=` とデフォルト値を書くことができます。\n\n```js\nfunction Avatar({ person, size = 100 }) {\n  // ...\n}\n```\n\nこれで、`size` プロパティを指定せずに `<Avatar person={...} />` のようにレンダーされた場合、`size` は `100` にセットされます。\n\nこのデフォルト値は `size` がない場合や `size={undefined}` を渡した場合にのみ使用されます。`size={null}` や `size={0}` を渡した場合にはデフォルト値は**使われません**。\n\n## JSX スプレッド構文で props を転送する {/*forwarding-props-with-the-jsx-spread-syntax*/}\n\nときに、props の受け渡しが冗長な繰り返しになってしまうことがあります。\n\n```js\nfunction Profile({ person, size, isSepia, thickBorder }) {\n  return (\n    <div className=\"card\">\n      <Avatar\n        person={person}\n        size={size}\n        isSepia={isSepia}\n        thickBorder={thickBorder}\n      />\n    </div>\n  );\n}\n```\n\n繰り返しの多いコードが悪いわけではありませんし、その方が読みやすいこともあるでしょう。しかし簡潔であることに価値があることもあります。この `Profile` が `Avatar` に対してやっているように、コンポーネントの中には props をそのまま子コンポーネントに転送するものがあります。`Profile` は props を直接的に使っているわけではありませんので、以下のような「スプレッド」構文を使って短く書く方が理にかなっているかもしれません。\n\n```js\nfunction Profile(props) {\n  return (\n    <div className=\"card\">\n      <Avatar {...props} />\n    </div>\n  );\n}\n```\n\nこれにより、`Profile` に渡された props を、個々の名前を列挙することなくすべて `Avatar` に転送できます。\n\n**スプレッド構文は慎重に使ってください**。この構文をあらゆるコンポーネントで使っているなら、何かが間違っています。多くの場合は、コンポーネントを分割して JSX として children を渡すべきというサインです。今からこれについて述べていきます。\n\n## children として JSX を渡す {/*passing-jsx-as-children*/}\n\nブラウザの組み込みタグをネストすることはよくありますね。\n\n```js\n<div>\n  <img />\n</div>\n```\n\n同様にして独自コンポーネントもネストしたくなることがあります。\n\n```js\n<Card>\n  <Avatar />\n</Card>\n```\n\nこのように JSX タグ内でコンテンツをネストした場合、親側のコンポーネントはその中身を `children` という props として受け取ります。例えば以下の `Card` コンポーネントは、`<Avatar />` がセットされた `children` プロパティを受け取って、ラッパ div 要素の内部にそれをレンダーしています。\n\n<Sandpack>\n\n```js src/App.js\nimport Avatar from './Avatar.js';\n\nfunction Card({ children }) {\n  return (\n    <div className=\"card\">\n      {children}\n    </div>\n  );\n}\n\nexport default function Profile() {\n  return (\n    <Card>\n      <Avatar\n        size={100}\n        person={{ \n          name: 'Katsuko Saruhashi',\n          imageId: 'YfeOqp2'\n        }}\n      />\n    </Card>\n  );\n}\n```\n\n```js src/Avatar.js\nimport { getImageUrl } from './utils.js';\n\nexport default function Avatar({ person, size }) {\n  return (\n    <img\n      className=\"avatar\"\n      src={getImageUrl(person)}\n      alt={person.name}\n      width={size}\n      height={size}\n    />\n  );\n}\n```\n\n```js src/utils.js\nexport function getImageUrl(person, size = 's') {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    size +\n    '.jpg'\n  );\n}\n```\n\n```css\n.card {\n  width: fit-content;\n  margin: 5px;\n  padding: 5px;\n  font-size: 20px;\n  text-align: center;\n  border: 1px solid #aaa;\n  border-radius: 20px;\n  background: #fff;\n}\n.avatar {\n  margin: 20px;\n  border-radius: 50%;\n}\n```\n\n</Sandpack>\n\n`<Card>` 内の `<Avatar>` を何かテキストに置き換えてみて、ネストされているどんなコンテンツでも `Card` コンポーネントは囲んで表示できるということを確かめてください。中に何が表示されるかあらかじめ知っておく必要はありません。このような柔軟なパターンは、様々なところで目にすることになるでしょう。\n\n`children` プロパティを有するコンポーネントには、親に任意の JSX で「埋めて」もらうための「穴」が開いている、と考えることができます。`children` は、パネルやグリッドのような視覚的に何かを囲む要素に使うことができます。\n\n<Illustration src=\"/images/docs/illustrations/i_children-prop.png\" alt='A puzzle-like Card tile with a slot for \"children\" pieces like text and Avatar' />\n\n## props は時間とともに変化する {/*how-props-change-over-time*/}\n\n以下の `Clock` コンポーネントは親コンポーネントから `color` と `time` という 2 つの props を受け取っています。（親コンポーネントのコードは、まだ解説していない [state](/learn/state-a-components-memory) を使っているため省略しています。）\n\n以下の選択ボックスでカラーを変えてみてください。\n\n<Sandpack>\n\n```js src/Clock.js active\nexport default function Clock({ color, time }) {\n  return (\n    <h1 style={{ color: color }}>\n      {time}\n    </h1>\n  );\n}\n```\n\n```js src/App.js hidden\nimport { useState, useEffect } from 'react';\nimport Clock from './Clock.js';\n\nfunction useTime() {\n  const [time, setTime] = useState(() => new Date());\n  useEffect(() => {\n    const id = setInterval(() => {\n      setTime(new Date());\n    }, 1000);\n    return () => clearInterval(id);\n  }, []);\n  return time;\n}\n\nexport default function App() {\n  const time = useTime();\n  const [color, setColor] = useState('lightcoral');\n  return (\n    <div>\n      <p>\n        Pick a color:{' '}\n        <select value={color} onChange={e => setColor(e.target.value)}>\n          <option value=\"lightcoral\">lightcoral</option>\n          <option value=\"midnightblue\">midnightblue</option>\n          <option value=\"rebeccapurple\">rebeccapurple</option>\n        </select>\n      </p>\n      <Clock color={color} time={time.toLocaleTimeString()} />\n    </div>\n  );\n}\n```\n\n</Sandpack>\n\nこの例は、**コンポーネントは時間経過とともに別の props を受け取る可能性がある**ということを示しています。props は常に固定だとは限らないのです！ ここでは `time` プロパティは毎秒変化していますし、`color` プロパティもあなたが別の色を選択するたびに変化します。props とはコンポーネントの最初の時点ではなく、任意の時点でのコンポーネントのデータを反映するものなのです。\n\nしかし、props は[イミュータブル (immutable)](https://en.wikipedia.org/wiki/Immutable_object) です。これは「不変な」という意味のコンピュータサイエンス用語です。コンポーネントの props が（例えばユーザ操作や新しいデータの到着に反応して）変わらないといけない場合、親のコンポーネントに*別の props*、つまり新しいオブジェクトを渡してもらう必要があります！ 古い props は忘れられ、使われていたメモリは JavaScript エンジンがそのうち回収します。\n\n**「props の書き換え」をしようとしてはいけません。**（上記のカラー選択のように）ユーザの入力に反応する必要がある場合は、「state のセット」が必要です。これについては [state：コンポーネントのメモリ](/learn/state-a-components-memory)で学びます。\n\n<Recap>\n\n* props を渡すには HTML で属性を書くのと同様のやり方で JSX 内に書く。\n* props を読み出すには、`function Avatar({ person, size })` のような分割代入構文を使う。\n* `size = 100` のようなデフォルト値を指定でき、これは props がない場合や `undefined` の場合に使われる。\n* `<Avatar {...props} />` のような JSX スプレッド構文ですべての props を転送できるが、多用は禁物！\n* `<Card><Avatar /></Card>` のようなネストされた JSX を書くと `Card` コンポーネントの `children` プロパティになる。\n* props とはある時点での読み取り専用のスナップショットである。レンダー毎に新しいバージョンの props を受け取る。\n* props を書き換えることはできない。インタラクティブ性が必要な場合は state を設定する必要がある。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### コンポーネント抽出 {/*extract-a-component*/}\n\n以下の `Gallery` コンポーネントには 2 名のプロフィール用にとても似たマークアップが含まれてしまっています。ここから `Profile` というコンポーネントを抽出してコードの重複を減らしてください。どんな props を渡すようにするのかは、自分で決めてください。\n\n<Sandpack>\n\n```js src/App.js\nimport { getImageUrl } from './utils.js';\n\nexport default function Gallery() {\n  return (\n    <div>\n      <h1>Notable Scientists</h1>\n      <section className=\"profile\">\n        <h2>Maria Skłodowska-Curie</h2>\n        <img\n          className=\"avatar\"\n          src={getImageUrl('szV5sdG')}\n          alt=\"Maria Skłodowska-Curie\"\n          width={70}\n          height={70}\n        />\n        <ul>\n          <li>\n            <b>Profession: </b> \n            physicist and chemist\n          </li>\n          <li>\n            <b>Awards: 4 </b> \n            (Nobel Prize in Physics, Nobel Prize in Chemistry, Davy Medal, Matteucci Medal)\n          </li>\n          <li>\n            <b>Discovered: </b>\n            polonium (chemical element)\n          </li>\n        </ul>\n      </section>\n      <section className=\"profile\">\n        <h2>Katsuko Saruhashi</h2>\n        <img\n          className=\"avatar\"\n          src={getImageUrl('YfeOqp2')}\n          alt=\"Katsuko Saruhashi\"\n          width={70}\n          height={70}\n        />\n        <ul>\n          <li>\n            <b>Profession: </b> \n            geochemist\n          </li>\n          <li>\n            <b>Awards: 2 </b> \n            (Miyake Prize for geochemistry, Tanaka Prize)\n          </li>\n          <li>\n            <b>Discovered: </b>\n            a method for measuring carbon dioxide in seawater\n          </li>\n        </ul>\n      </section>\n    </div>\n  );\n}\n```\n\n```js src/utils.js\nexport function getImageUrl(imageId, size = 's') {\n  return (\n    'https://i.imgur.com/' +\n    imageId +\n    size +\n    '.jpg'\n  );\n}\n```\n\n```css\n.avatar { margin: 5px; border-radius: 50%; min-height: 70px; }\n.profile {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n}\nh1, h2 { margin: 5px; }\nh1 { margin-bottom: 10px; }\nul { padding: 0px 10px 0px 20px; }\nli { margin: 5px; }\n```\n\n</Sandpack>\n\n<Hint>\n\n一方の科学者のマークアップを取り出すところから始めましょう。もう一方の人物と合わない部分を探し出して、その部分を props で設定できるようにしましょう。\n\n</Hint>\n\n<Solution>\n\n以下の解法では、`Profile` コンポーネントが複数の props を受け取るようにしました。`imageId`（文字列）、`name`（文字列）、`profession`（文字列）、`awards`（文字列の配列）、`discovery`（文字列）、`imageSize`（数値）です。\n\n`imageSize` プロパティにはデフォルト値があるのでコンポーネントに渡す必要がないことに注意してください。\n\n<Sandpack>\n\n```js src/App.js\nimport { getImageUrl } from './utils.js';\n\nfunction Profile({\n  imageId,\n  name,\n  profession,\n  awards,\n  discovery,\n  imageSize = 70\n}) {\n  return (\n    <section className=\"profile\">\n      <h2>{name}</h2>\n      <img\n        className=\"avatar\"\n        src={getImageUrl(imageId)}\n        alt={name}\n        width={imageSize}\n        height={imageSize}\n      />\n      <ul>\n        <li><b>Profession:</b> {profession}</li>\n        <li>\n          <b>Awards: {awards.length} </b>\n          ({awards.join(', ')})\n        </li>\n        <li>\n          <b>Discovered: </b>\n          {discovery}\n        </li>\n      </ul>\n    </section>\n  );\n}\n\nexport default function Gallery() {\n  return (\n    <div>\n      <h1>Notable Scientists</h1>\n      <Profile\n        imageId=\"szV5sdG\"\n        name=\"Maria Skłodowska-Curie\"\n        profession=\"physicist and chemist\"\n        discovery=\"polonium (chemical element)\"\n        awards={[\n          'Nobel Prize in Physics',\n          'Nobel Prize in Chemistry',\n          'Davy Medal',\n          'Matteucci Medal'\n        ]}\n      />\n      <Profile\n        imageId='YfeOqp2'\n        name='Katsuko Saruhashi'\n        profession='geochemist'\n        discovery=\"a method for measuring carbon dioxide in seawater\"\n        awards={[\n          'Miyake Prize for geochemistry',\n          'Tanaka Prize'\n        ]}\n      />\n    </div>\n  );\n}\n```\n\n```js src/utils.js\nexport function getImageUrl(imageId, size = 's') {\n  return (\n    'https://i.imgur.com/' +\n    imageId +\n    size +\n    '.jpg'\n  );\n}\n```\n\n```css\n.avatar { margin: 5px; border-radius: 50%; min-height: 70px; }\n.profile {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n}\nh1, h2 { margin: 5px; }\nh1 { margin-bottom: 10px; }\nul { padding: 0px 10px 0px 20px; }\nli { margin: 5px; }\n```\n\n</Sandpack>\n\n`awards` を配列として渡せば、`awardCount` のような props を別に使う必要がないことに注意してください。配列を渡すことで `awards.length` を使って受賞数がわかります。props にはあらゆる値を渡すことができ、配列も渡せるということを思い出してください！\n\n別の解法として、このページの上の方の例と同様に、ある人物の情報を単一のオブジェクトにグループ化して、そのオブジェクトを単一の値として props 経由で渡すようにすることもできます。\n\n<Sandpack>\n\n```js src/App.js\nimport { getImageUrl } from './utils.js';\n\nfunction Profile({ person, imageSize = 70 }) {\n  const imageSrc = getImageUrl(person)\n\n  return (\n    <section className=\"profile\">\n      <h2>{person.name}</h2>\n      <img\n        className=\"avatar\"\n        src={imageSrc}\n        alt={person.name}\n        width={imageSize}\n        height={imageSize}\n      />\n      <ul>\n        <li>\n          <b>Profession:</b> {person.profession}\n        </li>\n        <li>\n          <b>Awards: {person.awards.length} </b>\n          ({person.awards.join(', ')})\n        </li>\n        <li>\n          <b>Discovered: </b>\n          {person.discovery}\n        </li>\n      </ul>\n    </section>\n  )\n}\n\nexport default function Gallery() {\n  return (\n    <div>\n      <h1>Notable Scientists</h1>\n      <Profile person={{\n        imageId: 'szV5sdG',\n        name: 'Maria Skłodowska-Curie',\n        profession: 'physicist and chemist',\n        discovery: 'polonium (chemical element)',\n        awards: [\n          'Nobel Prize in Physics',\n          'Nobel Prize in Chemistry',\n          'Davy Medal',\n          'Matteucci Medal'\n        ],\n      }} />\n      <Profile person={{\n        imageId: 'YfeOqp2',\n        name: 'Katsuko Saruhashi',\n        profession: 'geochemist',\n        discovery: 'a method for measuring carbon dioxide in seawater',\n        awards: [\n          'Miyake Prize for geochemistry',\n          'Tanaka Prize'\n        ],\n      }} />\n    </div>\n  );\n}\n```\n\n```js src/utils.js\nexport function getImageUrl(person, size = 's') {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    size +\n    '.jpg'\n  );\n}\n```\n\n```css\n.avatar { margin: 5px; border-radius: 50%; min-height: 70px; }\n.profile {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n}\nh1, h2 { margin: 5px; }\nh1 { margin-bottom: 10px; }\nul { padding: 0px 10px 0px 20px; }\nli { margin: 5px; }\n```\n\n</Sandpack>\n\n複数の JSX 属性ではなく JavaScript のプロパティを使って記述しているので構文はちょっと変わっていますが、これらの例はほぼ同等であり、どちらのアプローチを使っても構いません。\n\n</Solution>\n\n#### props に基づく画像サイズ変更 {/*adjust-the-image-size-based-on-a-prop*/}\n\n以下の例では `Avatar` は数値型の `size` プロパティを受け取り、これが `<img>` の幅と高さを決定しています。この例では `size` は `40` に設定されています。しかしこの画像を新しいタブで開いてみると、画像自体はもっと大きい（`160` ピクセル）ことがわかります。実際の画像自体のサイズは、リクエストするサムネイルのサイズによって決定されます。\n\n`Avatar` コンポーネントを書き換えて、`size` プロパティに基づいて最も近い画像サイズをリクエストするようにしてください。具体的には、`size` が `90` 未満のときは `getImageUrl` 関数に `'b'` (\"big\") ではなく `'s'` (\"small\") を渡すようにします。書き換えがうまくいったことを確認するには、アバターを `size` を書き換えてレンダーし、画像を新しいタブで開くようにします。\n\n<Sandpack>\n\n```js src/App.js\nimport { getImageUrl } from './utils.js';\n\nfunction Avatar({ person, size }) {\n  return (\n    <img\n      className=\"avatar\"\n      src={getImageUrl(person, 'b')}\n      alt={person.name}\n      width={size}\n      height={size}\n    />\n  );\n}\n\nexport default function Profile() {\n  return (\n    <Avatar\n      size={40}\n      person={{ \n        name: 'Gregorio Y. Zara', \n        imageId: '7vQD0fP'\n      }}\n    />\n  );\n}\n```\n\n```js src/utils.js\nexport function getImageUrl(person, size) {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    size +\n    '.jpg'\n  );\n}\n```\n\n```css\n.avatar { margin: 20px; border-radius: 50%; }\n```\n\n</Sandpack>\n\n<Solution>\n\n以下が解法の一例です。\n\n<Sandpack>\n\n```js src/App.js\nimport { getImageUrl } from './utils.js';\n\nfunction Avatar({ person, size }) {\n  let thumbnailSize = 's';\n  if (size > 90) {\n    thumbnailSize = 'b';\n  }\n  return (\n    <img\n      className=\"avatar\"\n      src={getImageUrl(person, thumbnailSize)}\n      alt={person.name}\n      width={size}\n      height={size}\n    />\n  );\n}\n\nexport default function Profile() {\n  return (\n    <>\n      <Avatar\n        size={40}\n        person={{ \n          name: 'Gregorio Y. Zara', \n          imageId: '7vQD0fP'\n        }}\n      />\n      <Avatar\n        size={120}\n        person={{ \n          name: 'Gregorio Y. Zara', \n          imageId: '7vQD0fP'\n        }}\n      />\n    </>\n  );\n}\n```\n\n```js src/utils.js\nexport function getImageUrl(person, size) {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    size +\n    '.jpg'\n  );\n}\n```\n\n```css\n.avatar { margin: 20px; border-radius: 50%; }\n```\n\n</Sandpack>\n\n[`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) を考慮に入れて、高 DPI の画面でよりシャープな画像を表示するようにすることもできます。\n\n<Sandpack>\n\n```js src/App.js\nimport { getImageUrl } from './utils.js';\n\nconst ratio = window.devicePixelRatio;\n\nfunction Avatar({ person, size }) {\n  let thumbnailSize = 's';\n  if (size * ratio > 90) {\n    thumbnailSize = 'b';\n  }\n  return (\n    <img\n      className=\"avatar\"\n      src={getImageUrl(person, thumbnailSize)}\n      alt={person.name}\n      width={size}\n      height={size}\n    />\n  );\n}\n\nexport default function Profile() {\n  return (\n    <>\n      <Avatar\n        size={40}\n        person={{ \n          name: 'Gregorio Y. Zara', \n          imageId: '7vQD0fP'\n        }}\n      />\n      <Avatar\n        size={70}\n        person={{ \n          name: 'Gregorio Y. Zara', \n          imageId: '7vQD0fP'\n        }}\n      />\n      <Avatar\n        size={120}\n        person={{ \n          name: 'Gregorio Y. Zara', \n          imageId: '7vQD0fP'\n        }}\n      />\n    </>\n  );\n}\n```\n\n```js src/utils.js\nexport function getImageUrl(person, size) {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    size +\n    '.jpg'\n  );\n}\n```\n\n```css\n.avatar { margin: 20px; border-radius: 50%; }\n```\n\n</Sandpack>\n\nprops を使うことでこのようなロジックを `Avatar` 内にカプセル化（そして必要なら後で修正）し、画像がどのようにリクエストされサイズ変更されているのかを考えなくとも、誰でも `<Avatar>` が使えるようにできます。\n\n</Solution>\n\n#### `children` に JSX を渡す {/*passing-jsx-in-a-children-prop*/}\n\n以下のマークアップから `Card` コンポーネントを抽出し、props である `children` を使って、異なる JSX を渡せるようにしてください。\n\n<Sandpack>\n\n```js\nexport default function Profile() {\n  return (\n    <div>\n      <div className=\"card\">\n        <div className=\"card-content\">\n          <h1>Photo</h1>\n          <img\n            className=\"avatar\"\n            src=\"https://i.imgur.com/OKS67lhm.jpg\"\n            alt=\"Aklilu Lemma\"\n            width={70}\n            height={70}\n          />\n        </div>\n      </div>\n      <div className=\"card\">\n        <div className=\"card-content\">\n          <h1>About</h1>\n          <p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>\n        </div>\n      </div>\n    </div>\n  );\n}\n```\n\n```css\n.card {\n  width: fit-content;\n  margin: 20px;\n  padding: 20px;\n  border: 1px solid #aaa;\n  border-radius: 20px;\n  background: #fff;\n}\n.card-content {\n  text-align: center;\n}\n.avatar {\n  margin: 10px;\n  border-radius: 50%;\n}\nh1 {\n  margin: 5px;\n  padding: 0;\n  font-size: 24px;\n}\n```\n\n</Sandpack>\n\n<Hint>\n\nコンポーネントのタグ内に書かれたあらゆる JSX は、そのコンポーネントに `children` として渡されます。\n\n</Hint>\n\n<Solution>\n\n以下のようにすれば両方の場所で `Card` コンポーネントを使えるようになります。\n\n<Sandpack>\n\n```js\nfunction Card({ children }) {\n  return (\n    <div className=\"card\">\n      <div className=\"card-content\">\n        {children}\n      </div>\n    </div>\n  );\n}\n\nexport default function Profile() {\n  return (\n    <div>\n      <Card>\n        <h1>Photo</h1>\n        <img\n          className=\"avatar\"\n          src=\"https://i.imgur.com/OKS67lhm.jpg\"\n          alt=\"Aklilu Lemma\"\n          width={100}\n          height={100}\n        />\n      </Card>\n      <Card>\n        <h1>About</h1>\n        <p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>\n      </Card>\n    </div>\n  );\n}\n```\n\n```css\n.card {\n  width: fit-content;\n  margin: 20px;\n  padding: 20px;\n  border: 1px solid #aaa;\n  border-radius: 20px;\n  background: #fff;\n}\n.card-content {\n  text-align: center;\n}\n.avatar {\n  margin: 10px;\n  border-radius: 50%;\n}\nh1 {\n  margin: 5px;\n  padding: 0;\n  font-size: 24px;\n}\n```\n\n</Sandpack>\n\n`Card` に常にタイトルがあるようにしたい場合、`title` を独立した props にすることもできます。\n\n<Sandpack>\n\n```js\nfunction Card({ children, title }) {\n  return (\n    <div className=\"card\">\n      <div className=\"card-content\">\n        <h1>{title}</h1>\n        {children}\n      </div>\n    </div>\n  );\n}\n\nexport default function Profile() {\n  return (\n    <div>\n      <Card title=\"Photo\">\n        <img\n          className=\"avatar\"\n          src=\"https://i.imgur.com/OKS67lhm.jpg\"\n          alt=\"Aklilu Lemma\"\n          width={100}\n          height={100}\n        />\n      </Card>\n      <Card title=\"About\">\n        <p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>\n      </Card>\n    </div>\n  );\n}\n```\n\n```css\n.card {\n  width: fit-content;\n  margin: 20px;\n  padding: 20px;\n  border: 1px solid #aaa;\n  border-radius: 20px;\n  background: #fff;\n}\n.card-content {\n  text-align: center;\n}\n.avatar {\n  margin: 10px;\n  border-radius: 50%;\n}\nh1 {\n  margin: 5px;\n  padding: 0;\n  font-size: 24px;\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/preserving-and-resetting-state.md",
    "content": "---\ntitle: state の保持とリセット\n---\n\n<Intro>\n\nstate は複数のコンポーネント間で独立しています。React は UI ツリー内の各コンポーネントの位置に基づいて、どの state がどのコンポーネントに属するか管理します。再レンダーをまたいでどのようなときに state を保持し、どのようなときにリセットするのか、制御することができます。\n\n</Intro>\n\n<YouWillLearn>\n\n* React が state の保持とリセットを行うタイミング\n* React にコンポーネントの state のリセットを強制する方法\n* key とタイプが state の保持にどのように影響するか\n\n</YouWillLearn>\n\n## state はレンダーツリー内の位置に結びついている {/*state-is-tied-to-a-position-in-the-tree*/}\n\nReact はあなたの UI のコンポーネント構造を[レンダーツリー](learn/understanding-your-ui-as-a-tree#the-render-tree)としてビルドします。\n\nコンポーネントに state を与えると、その state はそのコンポーネントの内部で「生存」しているように思えるかもしれません。しかし、実際には state は React の中に保持されています。React は、「レンダーツリー内でそのコンポーネントがどの位置にあるか」に基づいて、保持している各 state を正しいコンポーネントに関連付けます。\n\n以下のコードには `<Counter />` JSX タグは 1 つしかありませんが、それが 2 つの異なる位置にレンダーされています。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function App() {\n  const counter = <Counter />;\n  return (\n    <div>\n      {counter}\n      {counter}\n    </div>\n  );\n}\n\nfunction Counter() {\n  const [score, setScore] = useState(0);\n  const [hover, setHover] = useState(false);\n\n  let className = 'counter';\n  if (hover) {\n    className += ' hover';\n  }\n\n  return (\n    <div\n      className={className}\n      onPointerEnter={() => setHover(true)}\n      onPointerLeave={() => setHover(false)}\n    >\n      <h1>{score}</h1>\n      <button onClick={() => setScore(score + 1)}>\n        Add one\n      </button>\n    </div>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  clear: both;\n}\n\n.counter {\n  width: 100px;\n  text-align: center;\n  border: 1px solid gray;\n  border-radius: 4px;\n  padding: 20px;\n  margin: 0 20px 20px 0;\n  float: left;\n}\n\n.hover {\n  background: #ffffd8;\n}\n```\n\n</Sandpack>\n\nツリーとしては、これは以下のように見えます。\n\n<DiagramGroup>\n\n<Diagram name=\"preserving_state_tree\" height={248} width={395} alt=\"React コンポーネントツリーを表す図。ルートノードは 'div' であり、2 つの子を持つ。子ノードはいずれも 'Counter' であり、値が 0 の 'count' を state として持っている。\">\n\nReact ツリー\n\n</Diagram>\n\n</DiagramGroup>\n\n**これらはツリー内の別々の位置にレンダーされているため、2 つの別々のカウンタとして動作します**。React を使用する上でこのような位置のことについて考える必要はめったにありませんが、React がどのように機能するかを理解することは有用です。\n\nReact では、画面上の各コンポーネントは完全に独立した state を持ちます。例えば、`Counter` コンポーネントを 2 つ横に並べてレンダーすると、それぞれが別個に、独立した `score` および `hover` という state を持つことになります。\n\n両方のカウンタをクリックしてみて、互いに影響していないことを確かめてください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function App() {\n  return (\n    <div>\n      <Counter />\n      <Counter />\n    </div>\n  );\n}\n\nfunction Counter() {\n  const [score, setScore] = useState(0);\n  const [hover, setHover] = useState(false);\n\n  let className = 'counter';\n  if (hover) {\n    className += ' hover';\n  }\n\n  return (\n    <div\n      className={className}\n      onPointerEnter={() => setHover(true)}\n      onPointerLeave={() => setHover(false)}\n    >\n      <h1>{score}</h1>\n      <button onClick={() => setScore(score + 1)}>\n        Add one\n      </button>\n    </div>\n  );\n}\n```\n\n```css\n.counter {\n  width: 100px;\n  text-align: center;\n  border: 1px solid gray;\n  border-radius: 4px;\n  padding: 20px;\n  margin: 0 20px 20px 0;\n  float: left;\n}\n\n.hover {\n  background: #ffffd8;\n}\n```\n\n</Sandpack>\n\nご覧のように、カウンタのうち 1 つが更新されると、そのコンポーネントの state だけが更新されます。\n\n\n<DiagramGroup>\n\n<Diagram name=\"preserving_state_increment\" height={248} width={441} alt=\"React コンポーネントツリーを表す図。ルートノードは 'div' であり、2 つの子を持つ。左の子は 'Counter' で、値が 0 の 'count' を state として持つ。右の子は 'Counter' で、値が 1 の 'count' を state として持つ。右の子の state バブルは黄色でハイライトされており、その値が更新されたことを示している。\">\n\nstate の更新\n\n</Diagram>\n\n</DiagramGroup>\n\n\nReact は、同じコンポーネントをツリー内の同じ位置でレンダーしている限り、その state を保持し続けます。これを確認するため、両方のカウンタを増加させてから、\"Render the second counter\" のチェックボックスのチェックを外して 2 つ目のコンポーネントを削除し、再びチェックを入れて元に戻してみてください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function App() {\n  const [showB, setShowB] = useState(true);\n  return (\n    <div>\n      <Counter />\n      {showB && <Counter />} \n      <label>\n        <input\n          type=\"checkbox\"\n          checked={showB}\n          onChange={e => {\n            setShowB(e.target.checked)\n          }}\n        />\n        Render the second counter\n      </label>\n    </div>\n  );\n}\n\nfunction Counter() {\n  const [score, setScore] = useState(0);\n  const [hover, setHover] = useState(false);\n\n  let className = 'counter';\n  if (hover) {\n    className += ' hover';\n  }\n\n  return (\n    <div\n      className={className}\n      onPointerEnter={() => setHover(true)}\n      onPointerLeave={() => setHover(false)}\n    >\n      <h1>{score}</h1>\n      <button onClick={() => setScore(score + 1)}>\n        Add one\n      </button>\n    </div>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  clear: both;\n}\n\n.counter {\n  width: 100px;\n  text-align: center;\n  border: 1px solid gray;\n  border-radius: 4px;\n  padding: 20px;\n  margin: 0 20px 20px 0;\n  float: left;\n}\n\n.hover {\n  background: #ffffd8;\n}\n```\n\n</Sandpack>\n\n2 つ目のカウンタのレンダーをやめた瞬間、その state は完全に消えてしまいます。これは、React がコンポーネントを削除する際にその state も破棄するからです。\n\n<DiagramGroup>\n\n<Diagram name=\"preserving_state_remove_component\" height={253} width={422} alt=\"React コンポーネントツリーを表す図。ルートノードは 'div' であり、2 つの子を持つ。左の子は 'Counter' で、値が 0 の 'count' を state として持つ。右の子はパッと消えたようになっており、コンポーネントがツリーから削除されたことを示している。\">\n\nコンポーネントの削除\n\n</Diagram>\n\n</DiagramGroup>\n\n\"Render the second counter\" にチェックを入れると、2 つ目の `Counter` とその state が初期化され (`score = 0`)、DOM に追加されます。\n\n<DiagramGroup>\n\n<Diagram name=\"preserving_state_add_component\" height={258} width={500} alt=\"React コンポーネントツリーを表す図。ルートノードは 'div' であり、2 つの子を持つ。左の子は 'Counter' で、値が 0 の 'count' state を持つ。右の子も 'Counter' であり、値が 0 の 'count' state を持つ。右の子は全体が黄色くハイライトされており、今まさにツリーに追加されたことを示している。\">\n\nコンポーネントを追加する\n\n</Diagram>\n\n</DiagramGroup>\n\n**React は、UI ツリーの中でコンポーネントが当該位置にレンダーされ続けている間は、そのコンポーネントの state を維持します**。もし削除されたり、同じ位置に別のコンポーネントがレンダーされたりすると、React は state を破棄します。\n\n## 同じ位置の同じコンポーネントは state が保持される {/*same-component-at-the-same-position-preserves-state*/}\n\nこの例のコードには、`<Counter />` タグが 2 つあります。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function App() {\n  const [isFancy, setIsFancy] = useState(false);\n  return (\n    <div>\n      {isFancy ? (\n        <Counter isFancy={true} /> \n      ) : (\n        <Counter isFancy={false} /> \n      )}\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isFancy}\n          onChange={e => {\n            setIsFancy(e.target.checked)\n          }}\n        />\n        Use fancy styling\n      </label>\n    </div>\n  );\n}\n\nfunction Counter({ isFancy }) {\n  const [score, setScore] = useState(0);\n  const [hover, setHover] = useState(false);\n\n  let className = 'counter';\n  if (hover) {\n    className += ' hover';\n  }\n  if (isFancy) {\n    className += ' fancy';\n  }\n\n  return (\n    <div\n      className={className}\n      onPointerEnter={() => setHover(true)}\n      onPointerLeave={() => setHover(false)}\n    >\n      <h1>{score}</h1>\n      <button onClick={() => setScore(score + 1)}>\n        Add one\n      </button>\n    </div>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  clear: both;\n}\n\n.counter {\n  width: 100px;\n  text-align: center;\n  border: 1px solid gray;\n  border-radius: 4px;\n  padding: 20px;\n  margin: 0 20px 20px 0;\n  float: left;\n}\n\n.fancy {\n  border: 5px solid gold;\n  color: #ff6767;\n}\n\n.hover {\n  background: #ffffd8;\n}\n```\n\n</Sandpack>\n\nチェックボックスにチェックを入れたり消したりしても、カウンタの state はリセットされません。`isFancy` が `true` であろうと `false` であろうと、ルートの `App` コンポーネントが返す `div` の最初の子は常に `<Counter />` だからです。\n\n<DiagramGroup>\n\n<Diagram name=\"preserving_state_same_component\" height={461} width={600} alt=\"矢印で遷移している 2 つのセクションで構成される図。各セクションには、'App' とラベルの付いた親と、isFancy というラベルの付いた state ボックスが表示されている。このコンポーネントには 'div' とラベル付けされた 1 つの子があり、その下には isFancy と書かれたボックス（紫）があり下に props として渡されることを示している。どちらのセクションでも最後には 'count' = 3 という state ボックスを持つ 'Counter' がある。図の左側のセクションでは何も強調表示されておらず、isFancy の親状態の値は false。図の右側のセクションでは、isFancy の親の状態値が true に変更されて黄色でハイライトされており、下の props バブルも同様に true に変更されてハイライトされている。\">\n\n`App` の state を更新しても、`Counter` は同じ位置にあるためリセットされない\n\n</Diagram>\n\n</DiagramGroup>\n\n\n同じコンポーネントが同じ位置にあるので、React の観点からは同じカウンタだというわけです。\n\n<Pitfall>\n\n**React にとって重要なのは JSX マークアップの位置ではなく UI ツリー内の位置である**ということを覚えておいてください。このコンポーネントからは、`if` の内側と外側で、2 つの `return` 文から 2 つの異なる `<Counter />` JSX タグが返されています。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function App() {\n  const [isFancy, setIsFancy] = useState(false);\n  if (isFancy) {\n    return (\n      <div>\n        <Counter isFancy={true} />\n        <label>\n          <input\n            type=\"checkbox\"\n            checked={isFancy}\n            onChange={e => {\n              setIsFancy(e.target.checked)\n            }}\n          />\n          Use fancy styling\n        </label>\n      </div>\n    );\n  }\n  return (\n    <div>\n      <Counter isFancy={false} />\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isFancy}\n          onChange={e => {\n            setIsFancy(e.target.checked)\n          }}\n        />\n        Use fancy styling\n      </label>\n    </div>\n  );\n}\n\nfunction Counter({ isFancy }) {\n  const [score, setScore] = useState(0);\n  const [hover, setHover] = useState(false);\n\n  let className = 'counter';\n  if (hover) {\n    className += ' hover';\n  }\n  if (isFancy) {\n    className += ' fancy';\n  }\n\n  return (\n    <div\n      className={className}\n      onPointerEnter={() => setHover(true)}\n      onPointerLeave={() => setHover(false)}\n    >\n      <h1>{score}</h1>\n      <button onClick={() => setScore(score + 1)}>\n        Add one\n      </button>\n    </div>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  clear: both;\n}\n\n.counter {\n  width: 100px;\n  text-align: center;\n  border: 1px solid gray;\n  border-radius: 4px;\n  padding: 20px;\n  margin: 0 20px 20px 0;\n  float: left;\n}\n\n.fancy {\n  border: 5px solid gold;\n  color: #ff6767;\n}\n\n.hover {\n  background: #ffffd8;\n}\n```\n\n</Sandpack>\n\nチェックボックスにチェックを入れると state がリセットされると思われるかもしれませんが、そうはなりません。これは、**これらの両方の `<Counter />` タグが同じ位置でレンダーされている**ためです。あなたの関数内で条件分岐がどのように書かれているか、React には分かりません。React に「見える」のは、返されるツリーだけです。\n\nどちらの場合も、`App` コンポーネントは、`<Counter />` を最初の子として持つ `<div>` を返します。React にとって、これら 2 つのカウンタは、「ルートの最初の子の最初の子」という、同じ「住所」を持っています。これが、あなたがどのようにロジックを構築しているかに関係なく、前回のレンダーと次のレンダーの間で React がコンポーネントを対応付ける方法なのです。\n\n</Pitfall>\n\n## 同じ位置の異なるコンポーネントは state をリセットする {/*different-components-at-the-same-position-reset-state*/}\n\nこの例では、チェックボックスにチェックを入れると、`<Counter>` が `<p>` に置き換わります。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function App() {\n  const [isPaused, setIsPaused] = useState(false);\n  return (\n    <div>\n      {isPaused ? (\n        <p>See you later!</p> \n      ) : (\n        <Counter /> \n      )}\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isPaused}\n          onChange={e => {\n            setIsPaused(e.target.checked)\n          }}\n        />\n        Take a break\n      </label>\n    </div>\n  );\n}\n\nfunction Counter() {\n  const [score, setScore] = useState(0);\n  const [hover, setHover] = useState(false);\n\n  let className = 'counter';\n  if (hover) {\n    className += ' hover';\n  }\n\n  return (\n    <div\n      className={className}\n      onPointerEnter={() => setHover(true)}\n      onPointerLeave={() => setHover(false)}\n    >\n      <h1>{score}</h1>\n      <button onClick={() => setScore(score + 1)}>\n        Add one\n      </button>\n    </div>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  clear: both;\n}\n\n.counter {\n  width: 100px;\n  text-align: center;\n  border: 1px solid gray;\n  border-radius: 4px;\n  padding: 20px;\n  margin: 0 20px 20px 0;\n  float: left;\n}\n\n.hover {\n  background: #ffffd8;\n}\n```\n\n</Sandpack>\n\nここでは、同じ位置で異なる種類のコンポーネントを切り替えています。最初は `<div>` の最初の子は `Counter` でした。それを `p` と入れ替えると、React は UI ツリーから `Counter` を削除し、その state を破棄します。\n\n<DiagramGroup>\n\n<Diagram name=\"preserving_state_diff_pt1\" height={290} width={753} alt=\"矢印で遷移する 3 セクションから構成される図。最初のセクションには React コンポーネントとして 'div' とその唯一の子である 'Counter' があり、カウンタには値が 3 になった 'count' state がある。中央のセクションにも親の 'div' があるが、子が消失している。最後のセクションにも 'div' があるが、今度は 'p' と書かれた新しい子ができており、黄色でハイライトされている。\">\n\n`Counter` が `p` に変わると、`Counter` は削除され、`p` が追加される\n\n</Diagram>\n\n</DiagramGroup>\n\n<DiagramGroup>\n\n<Diagram name=\"preserving_state_diff_pt2\" height={290} width={753} alt=\"矢印で遷移する 3 セクションから構成される図。最初のセクションには React コンポーネントとして 'div' とその子である 'p' がある。中央のセクションにも親の 'div' があるが、子が消失している。最後のセクションにも 'div' があるが、今度は 'Count' と書かれた新しい子ができており黄色でハイライトされている。その 'count' state は 0 となっている。\">\n\n戻すときは、`p` が削除され、`Counter` が追加される\n\n</Diagram>\n\n</DiagramGroup>\n\nまた、**同じ位置で異なるコンポーネントをレンダーすると、そのサブツリー全体の state がリセットされます**。これがどのように動作するかを確認するために、以下でカウンタを増やしてからチェックボックスにチェックを入れてみてください：\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function App() {\n  const [isFancy, setIsFancy] = useState(false);\n  return (\n    <div>\n      {isFancy ? (\n        <div>\n          <Counter isFancy={true} /> \n        </div>\n      ) : (\n        <section>\n          <Counter isFancy={false} />\n        </section>\n      )}\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isFancy}\n          onChange={e => {\n            setIsFancy(e.target.checked)\n          }}\n        />\n        Use fancy styling\n      </label>\n    </div>\n  );\n}\n\nfunction Counter({ isFancy }) {\n  const [score, setScore] = useState(0);\n  const [hover, setHover] = useState(false);\n\n  let className = 'counter';\n  if (hover) {\n    className += ' hover';\n  }\n  if (isFancy) {\n    className += ' fancy';\n  }\n\n  return (\n    <div\n      className={className}\n      onPointerEnter={() => setHover(true)}\n      onPointerLeave={() => setHover(false)}\n    >\n      <h1>{score}</h1>\n      <button onClick={() => setScore(score + 1)}>\n        Add one\n      </button>\n    </div>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  clear: both;\n}\n\n.counter {\n  width: 100px;\n  text-align: center;\n  border: 1px solid gray;\n  border-radius: 4px;\n  padding: 20px;\n  margin: 0 20px 20px 0;\n  float: left;\n}\n\n.fancy {\n  border: 5px solid gold;\n  color: #ff6767;\n}\n\n.hover {\n  background: #ffffd8;\n}\n```\n\n</Sandpack>\n\nチェックボックスをクリックするとカウンタの state がリセットされます。`Counter` をレンダーしていることは同じでも、`<div>` の最初の子が `section` から `div` に変わっています。子側の `section` が DOM から削除されたとき、その下のツリー全体（`Counter` とその state を含む）も破棄されたのです。\n\n<DiagramGroup>\n\n<Diagram name=\"preserving_state_diff_same_pt1\" height={350} width={794} alt=\"矢印で遷移する 3 セクションから構成される図。最初のセクションには React コンポーネントとして 'div' とその子である 'section' がある。さらにその子に 'Counter' があり、'count' の state ボックスは 3 となっている。中央のセクションにも親の 'div' があるが、子が消失している。最後のセクションにも 'div' があり、今度は別の 'div' が新しい子となってハイライトされている。さらにその子に 'Counter' があって黄色でハイライトされており、その 'count' の state ボックスは 0 となっている。\">\n\n`section` が `div` に変わると、`section` は削除され、新しい `div` が追加される\n\n</Diagram>\n\n</DiagramGroup>\n\n<DiagramGroup>\n\n<Diagram name=\"preserving_state_diff_same_pt2\" height={350} width={794} alt=\"矢印で遷移する 3 セクションから構成される図。最初のセクションには React コンポーネントとして 'div' とその子である別の 'div' がある。さらにその子に 'Counter' があり、'count' の state ボックスは 0 となっている。中央のセクションにも親の 'div' があるが、子が消失している。最後のセクションにも 'div' があり、今度は 'section' が新しい子となってハイライトされている。さらにその子に 'Counter' があって黄色でハイライトされており、その 'count' の state ボックスは 0 となっている。\">\n\n戻すときは `div` は削除され、新しい `section` が追加される\n\n</Diagram>\n\n</DiagramGroup>\n\n覚えておくべきルールとして、**再レンダー間で state を維持したい場合、ツリーの構造はレンダー間で「合致」する必要があります**。構造が異なる場合、React がツリーからコンポーネントを削除するときに state も破棄されてしまいます。\n\n<Pitfall>\n\nこれがコンポーネント関数の定義をネストしてはいけない理由でもあります。\n\n以下では、`MyTextField` コンポーネント関数が `MyComponent` の*内部*で定義されています。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [7]}}\nimport { useState } from 'react';\n\nexport default function MyComponent() {\n  const [counter, setCounter] = useState(0);\n\n  function MyTextField() {\n    const [text, setText] = useState('');\n\n    return (\n      <input\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n    );\n  }\n\n  return (\n    <>\n      <MyTextField />\n      <button onClick={() => {\n        setCounter(counter + 1)\n      }}>Clicked {counter} times</button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n\nボタンをクリックするたびに、入力フィールドの state が消えてしまいます！ これは、`MyComponent` がレンダーされるたびに*異なる* `MyTextField` 関数が作成されているためです。同じ位置に*異なる*コンポーネントをレンダーしているので、React はそれより下のすべての state をリセットします。これはバグやパフォーマンスの問題につながります。この問題を避けるために、**常にコンポーネント関数はトップレベルで宣言し、定義をネストしない**ようにしてください。\n\n</Pitfall>\n\n## 同じ位置で state をリセット {/*resetting-state-at-the-same-position*/}\n\nデフォルトでは、React はコンポーネントが同じ位置にある間、その state を保持します。通常、これがまさにあなたが望むものであり、デフォルト動作として妥当です。しかし時には、コンポーネントの state をリセットしたい場合があります。以下の、2 人のプレーヤに交替でスコアを記録させるアプリを考えてみましょう。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Scoreboard() {\n  const [isPlayerA, setIsPlayerA] = useState(true);\n  return (\n    <div>\n      {isPlayerA ? (\n        <Counter person=\"Taylor\" />\n      ) : (\n        <Counter person=\"Sarah\" />\n      )}\n      <button onClick={() => {\n        setIsPlayerA(!isPlayerA);\n      }}>\n        Next player!\n      </button>\n    </div>\n  );\n}\n\nfunction Counter({ person }) {\n  const [score, setScore] = useState(0);\n  const [hover, setHover] = useState(false);\n\n  let className = 'counter';\n  if (hover) {\n    className += ' hover';\n  }\n\n  return (\n    <div\n      className={className}\n      onPointerEnter={() => setHover(true)}\n      onPointerLeave={() => setHover(false)}\n    >\n      <h1>{person}'s score: {score}</h1>\n      <button onClick={() => setScore(score + 1)}>\n        Add one\n      </button>\n    </div>\n  );\n}\n```\n\n```css\nh1 {\n  font-size: 18px;\n}\n\n.counter {\n  width: 100px;\n  text-align: center;\n  border: 1px solid gray;\n  border-radius: 4px;\n  padding: 20px;\n  margin: 0 20px 20px 0;\n}\n\n.hover {\n  background: #ffffd8;\n}\n```\n\n</Sandpack>\n\n現在のところ、プレーヤを変更してもスコアが保持されています。2 つの `Counter` は同じ位置に表示されるため、React は `person` という props が変更されただけの*同一の* `Counter` であると認識します。\n\nしかし、概念的には、このアプリでは、この 2 つのカウンタは別物であるべきです。UI 上の同じ場所に表示されているにせよ、一方は Taylor のカウンタで、もう一方は Sarah のカウンタなのです。\n\nこれらを切り替えるときに state をリセットする方法は、2 つあります。\n\n1. コンポーネントを異なる位置でレンダーする\n2. `key` を使って各コンポーネントに明示的な識別子を付与する\n\n\n### オプション 1：異なる位置でコンポーネントをレンダー {/*option-1-rendering-a-component-in-different-positions*/}\n\nこれら 2 つの `Counter` を互いに独立させたい場合、レンダーを 2 つの別の位置で行うことで可能です。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Scoreboard() {\n  const [isPlayerA, setIsPlayerA] = useState(true);\n  return (\n    <div>\n      {isPlayerA &&\n        <Counter person=\"Taylor\" />\n      }\n      {!isPlayerA &&\n        <Counter person=\"Sarah\" />\n      }\n      <button onClick={() => {\n        setIsPlayerA(!isPlayerA);\n      }}>\n        Next player!\n      </button>\n    </div>\n  );\n}\n\nfunction Counter({ person }) {\n  const [score, setScore] = useState(0);\n  const [hover, setHover] = useState(false);\n\n  let className = 'counter';\n  if (hover) {\n    className += ' hover';\n  }\n\n  return (\n    <div\n      className={className}\n      onPointerEnter={() => setHover(true)}\n      onPointerLeave={() => setHover(false)}\n    >\n      <h1>{person}'s score: {score}</h1>\n      <button onClick={() => setScore(score + 1)}>\n        Add one\n      </button>\n    </div>\n  );\n}\n```\n\n```css\nh1 {\n  font-size: 18px;\n}\n\n.counter {\n  width: 100px;\n  text-align: center;\n  border: 1px solid gray;\n  border-radius: 4px;\n  padding: 20px;\n  margin: 0 20px 20px 0;\n}\n\n.hover {\n  background: #ffffd8;\n}\n```\n\n</Sandpack>\n\n* 最初 `isPlayerA` は `true` です。そのため、1 番目の位置に対して `Counter` の state が保持され、2 番目は空です。\n* \"Next player\" ボタンをクリックすると、最初の位置がクリアされ、2 番目の位置に `Counter` が入ります。\n\n<DiagramGroup>\n\n<Diagram name=\"preserving_state_diff_position_p1\" height={375} width={504} alt=\"React コンポーネントツリーを表す図。親は 'Scoreboard' という名前であり isPlayerA という state ボックスの値は 'true' である。唯一の子は左側に配置される 'Counter' であり、'count' という state ボックスの値は 0 である。左の子供全体が黄色でハイライトされており、追加されたことを示している。\">\n\n初期 state\n\n</Diagram>\n\n<Diagram name=\"preserving_state_diff_position_p2\" height={375} width={504} alt=\"React コンポーネントツリーを表す図。親は 'Scoreboard' という名前であり isPlayerA という state ボックスの値は 'false' である。このボックスは黄色でハイライトされており、変更があったことを示している。左の子は消失しており、一方で右に別の子が追加されており、黄色でハイライトされている。新しい子は 'Counter' であり、'count' という state ボックスの値は 0 である。\">\n\n\"Next\" をクリック\n\n</Diagram>\n\n<Diagram name=\"preserving_state_diff_position_p3\" height={375} width={504} alt=\"React コンポーネントツリーを表す図。親は 'Scoreboard' という名前であり isPlayerA という state ボックスの値は 'true' である。このボックスは黄色でハイライトされており、変更があったことを示している。左に子が追加されており、黄色でハイライトされている。新しい子は 'Counter' であり、'count' という state ボックスの値は 0 である。右の子は消失している。\">\n\n再び \"Next\" をクリック\n\n</Diagram>\n\n</DiagramGroup>\n\n`Counter` の state は DOM から削除されるたびに破棄されます。これがボタンをクリックするたびにカウントがリセットされる理由です。\n\nこの解決策は、同じ場所でレンダーされる独立したコンポーネントが数個しかない場合には便利です。この例では 2 つしかないので、両方を JSX 内で別々にレンダーしても大変ではありません。\n\n### オプション 2：key で state をリセットする {/*option-2-resetting-state-with-a-key*/}\n\nコンポーネントの state をリセットする一般的な方法がもうひとつあります。\n\n[リストをレンダー](/learn/rendering-lists#keeping-list-items-in-order-with-key)する際に `key` を使ったのを覚えているでしょうか。key はリストのためだけのものではありません！ どんなコンポーネントでも React がそれを識別するために使用できるのです。デフォルトでは、React は親コンポーネント内での順序（「1 番目のカウンタ」「2 番目のカウンタ」）を使ってコンポーネントを区別します。しかし、`key` を使うことで、カウンタが単なる *1 番目の*カウンタや *2 番目の*カウンタではなく特定のカウンタ、例えば *Taylor の*カウンタである、と React に伝えることができます。このようにして、React は *Taylor の*カウンタがツリーのどこにあっても識別できるようになるのです。\n\nこの例では、2 つの `<Counter />` は JSX の同じ場所にあるにもかかわらず、state を共有していません：\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Scoreboard() {\n  const [isPlayerA, setIsPlayerA] = useState(true);\n  return (\n    <div>\n      {isPlayerA ? (\n        <Counter key=\"Taylor\" person=\"Taylor\" />\n      ) : (\n        <Counter key=\"Sarah\" person=\"Sarah\" />\n      )}\n      <button onClick={() => {\n        setIsPlayerA(!isPlayerA);\n      }}>\n        Next player!\n      </button>\n    </div>\n  );\n}\n\nfunction Counter({ person }) {\n  const [score, setScore] = useState(0);\n  const [hover, setHover] = useState(false);\n\n  let className = 'counter';\n  if (hover) {\n    className += ' hover';\n  }\n\n  return (\n    <div\n      className={className}\n      onPointerEnter={() => setHover(true)}\n      onPointerLeave={() => setHover(false)}\n    >\n      <h1>{person}'s score: {score}</h1>\n      <button onClick={() => setScore(score + 1)}>\n        Add one\n      </button>\n    </div>\n  );\n}\n```\n\n```css\nh1 {\n  font-size: 18px;\n}\n\n.counter {\n  width: 100px;\n  text-align: center;\n  border: 1px solid gray;\n  border-radius: 4px;\n  padding: 20px;\n  margin: 0 20px 20px 0;\n}\n\n.hover {\n  background: #ffffd8;\n}\n```\n\n</Sandpack>\n\nTaylor と Sarah を切り替えたときに state が保持されなくなりました。**異なる `key` を指定した**からです。\n\n```js\n{isPlayerA ? (\n  <Counter key=\"Taylor\" person=\"Taylor\" />\n) : (\n  <Counter key=\"Sarah\" person=\"Sarah\" />\n)}\n```\n\n`key` を指定することで、親要素内の順序ではなく、`key` 自体を位置に関する情報として React に使用させることができます。これにより、JSX で同じ位置にレンダーしても、React はそれらを異なるカウンタとして認識するため、state が共有されてしまうことはありません。カウンタが画面に表示されるたびに、新しい state が作成されます。カウンタが削除されるたびに、その state は破棄されます。切り替えるたびに、何度でも state がリセットされます。\n\n<Note>\n\nkey はグローバルに一意である必要はなく、*親要素内での*位置を指定しているだけであることを覚えておきましょう。\n\n</Note>\n\n### key でフォームをリセットする {/*resetting-a-form-with-a-key*/}\n\nkey を使って state をリセットすることは、フォームを扱う際に特に有用です。\n\nこのチャットアプリでは、`<Chat>` コンポーネントがテキスト入力フィールド用の state を持っています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\n\nexport default function Messenger() {\n  const [to, setTo] = useState(contacts[0]);\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedContact={to}\n        onSelect={contact => setTo(contact)}\n      />\n      <Chat contact={to} />\n    </div>\n  )\n}\n\nconst contacts = [\n  { id: 0, name: 'Taylor', email: 'taylor@mail.com' },\n  { id: 1, name: 'Alice', email: 'alice@mail.com' },\n  { id: 2, name: 'Bob', email: 'bob@mail.com' }\n];\n```\n\n```js src/ContactList.js\nexport default function ContactList({\n  selectedContact,\n  contacts,\n  onSelect\n}) {\n  return (\n    <section className=\"contact-list\">\n      <ul>\n        {contacts.map(contact =>\n          <li key={contact.id}>\n            <button onClick={() => {\n              onSelect(contact);\n            }}>\n              {contact.name}\n            </button>\n          </li>\n        )}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/Chat.js\nimport { useState } from 'react';\n\nexport default function Chat({ contact }) {\n  const [text, setText] = useState('');\n  return (\n    <section className=\"chat\">\n      <textarea\n        value={text}\n        placeholder={'Chat to ' + contact.name}\n        onChange={e => setText(e.target.value)}\n      />\n      <br />\n      <button>Send to {contact.email}</button>\n    </section>\n  );\n}\n```\n\n```css\n.chat, .contact-list {\n  float: left;\n  margin-bottom: 20px;\n}\nul, li {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli button {\n  width: 100px;\n  padding: 10px;\n  margin-right: 10px;\n}\ntextarea {\n  height: 150px;\n}\n```\n\n</Sandpack>\n\n入力フィールドに何か入力してから、\"Alice\" または \"Bob\" をクリックして別の送信先を選択してみてください。`<Chat>` がツリーの同じ位置にレンダーされているため、入力した state が保持されたままになっていることが分かります。\n\n**多くのアプリではこれが望ましい動作でしょうが、チャットアプリでは違います！** ミスクリックのせいで既に入力したメッセージを誤った相手に送ってしまうことは避けたいでしょう。これを修正するために、`key` を追加します。\n\n```js\n<Chat key={to.id} contact={to} />\n```\n\nこれにより、異なる送信先を選択したときに、`Chat` コンポーネントが、その下のツリーにあるあらゆる state も含めて最初から再作成されるようになります。React は DOM 要素についても再利用するのではなく再作成します。\n\nこれで、送信先を切り替えると常にテキストフィールドがクリアされるようになります。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport Chat from './Chat.js';\nimport ContactList from './ContactList.js';\n\nexport default function Messenger() {\n  const [to, setTo] = useState(contacts[0]);\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedContact={to}\n        onSelect={contact => setTo(contact)}\n      />\n      <Chat key={to.id} contact={to} />\n    </div>\n  )\n}\n\nconst contacts = [\n  { id: 0, name: 'Taylor', email: 'taylor@mail.com' },\n  { id: 1, name: 'Alice', email: 'alice@mail.com' },\n  { id: 2, name: 'Bob', email: 'bob@mail.com' }\n];\n```\n\n```js src/ContactList.js\nexport default function ContactList({\n  selectedContact,\n  contacts,\n  onSelect\n}) {\n  return (\n    <section className=\"contact-list\">\n      <ul>\n        {contacts.map(contact =>\n          <li key={contact.id}>\n            <button onClick={() => {\n              onSelect(contact);\n            }}>\n              {contact.name}\n            </button>\n          </li>\n        )}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/Chat.js\nimport { useState } from 'react';\n\nexport default function Chat({ contact }) {\n  const [text, setText] = useState('');\n  return (\n    <section className=\"chat\">\n      <textarea\n        value={text}\n        placeholder={'Chat to ' + contact.name}\n        onChange={e => setText(e.target.value)}\n      />\n      <br />\n      <button>Send to {contact.email}</button>\n    </section>\n  );\n}\n```\n\n```css\n.chat, .contact-list {\n  float: left;\n  margin-bottom: 20px;\n}\nul, li {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli button {\n  width: 100px;\n  padding: 10px;\n  margin-right: 10px;\n}\ntextarea {\n  height: 150px;\n}\n```\n\n</Sandpack>\n\n<DeepDive>\n\n#### 削除されたコンポーネントの state の保持 {/*preserving-state-for-removed-components*/}\n\n実際のチャットアプリでは、ユーザが以前の送信先を再度選択したときに入力欄の state を復元できるようにしたいでしょう。表示されなくなったコンポーネントの state を「生かしておく」方法はいくつかあります。\n\n- 現在のチャットだけでなく*すべて*のチャットをレンダーしておき、CSS で残りのすべてのチャットを非表示にすることができます。チャットはツリーから削除されないため、ローカル state も保持されます。この解決策はシンプルな UI では適していますが、非表示のツリーが大きく DOM ノードがたくさん含まれている場合は非常に遅くなります。\n- state を[リフトアップする](/learn/sharing-state-between-components)ことで、各送信先に対応する書きかけのメッセージを親コンポーネントで保持することができます。この方法では、重要な情報を保持しているのは親の方なので、子コンポーネントが削除されても問題ありません。これが最も一般的な解決策です。\n- また、React の state に加えて別の情報源を利用することもできます。例えば、ユーザがページを誤って閉じたとしてもメッセージの下書きが保持されるようにしたいでしょう。これを実装するために、`Chat` コンポーネントが [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) から読み込んで state を初期化し、下書きを保存するようにできます。\n\nどの戦略を選ぶ場合でも、*Alice との*チャットと *Bob との*チャットは概念的には異なるものなので、現在の送信先に基づいて `<Chat>` ツリーに `key` を付与することは妥当です。\n\n</DeepDive>\n\n<Recap>\n\n- React は、同じコンポーネントが同じ位置でレンダーされている限り、state を保持する。\n- state は JSX タグに保持されるのではない。JSX を置くツリー内の位置に関連付けられている。\n- 異なる key を与えることで、サブツリーの state をリセットするよう強制することができる。\n- コンポーネント定義をネストさせてはいけない。さもないと state がリセットされてしまう。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### 入力テキストの消失を修正 {/*fix-disappearing-input-text*/}\n\nこの例では、ボタンを押すとメッセージが表示されます。が、ボタンを押すことでどういうわけか入力欄がリセットされてしまいます。なぜこれが起こるのでしょうか？ ボタンを押しても入力テキストがリセットされないように修正してください。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nexport default function App() {\n  const [showHint, setShowHint] = useState(false);\n  if (showHint) {\n    return (\n      <div>\n        <p><i>Hint: Your favorite city?</i></p>\n        <Form />\n        <button onClick={() => {\n          setShowHint(false);\n        }}>Hide hint</button>\n      </div>\n    );\n  }\n  return (\n    <div>\n      <Form />\n      <button onClick={() => {\n        setShowHint(true);\n      }}>Show hint</button>\n    </div>\n  );\n}\n\nfunction Form() {\n  const [text, setText] = useState('');\n  return (\n    <textarea\n      value={text}\n      onChange={e => setText(e.target.value)}\n    />\n  );\n}\n```\n\n```css\ntextarea { display: block; margin: 10px 0; }\n```\n\n</Sandpack>\n\n<Solution>\n\n問題は、`Form` が異なる位置にレンダーされていることです。`if` 側の分岐では、`<div>` の 2 番目の子としてレンダーされますが、`else` 側の分岐では、1 番目の子としてレンダーされます。そのため各位置でのコンポーネントのタイプが変わってしまいます。1 番目の位置では `p` と `Form` の間で切り替わり、2 番目の位置では `Form` と `button` の間で切り替わっているわけです。React は、コンポーネントタイプが変更されるたびに state をリセットします。\n\n最も簡単な解決策は、2 つの分岐を統合して `Form` が常に同じ位置でレンダーされるようにすることです。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nexport default function App() {\n  const [showHint, setShowHint] = useState(false);\n  return (\n    <div>\n      {showHint &&\n        <p><i>Hint: Your favorite city?</i></p>\n      }\n      <Form />\n      {showHint ? (\n        <button onClick={() => {\n          setShowHint(false);\n        }}>Hide hint</button>\n      ) : (\n        <button onClick={() => {\n          setShowHint(true);\n        }}>Show hint</button>\n      )}\n    </div>\n  );\n}\n\nfunction Form() {\n  const [text, setText] = useState('');\n  return (\n    <textarea\n      value={text}\n      onChange={e => setText(e.target.value)}\n    />\n  );\n}\n```\n\n```css\ntextarea { display: block; margin: 10px 0; }\n```\n\n</Sandpack>\n\n\n厳密に言えば、`else` 側の分岐の `<Form />` の前に `if` 側の分岐の構造に対応する `null` を追加する、ということも可能です。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nexport default function App() {\n  const [showHint, setShowHint] = useState(false);\n  if (showHint) {\n    return (\n      <div>\n        <p><i>Hint: Your favorite city?</i></p>\n        <Form />\n        <button onClick={() => {\n          setShowHint(false);\n        }}>Hide hint</button>\n      </div>\n    );\n  }\n  return (\n    <div>\n      {null}\n      <Form />\n      <button onClick={() => {\n        setShowHint(true);\n      }}>Show hint</button>\n    </div>\n  );\n}\n\nfunction Form() {\n  const [text, setText] = useState('');\n  return (\n    <textarea\n      value={text}\n      onChange={e => setText(e.target.value)}\n    />\n  );\n}\n```\n\n```css\ntextarea { display: block; margin: 10px 0; }\n```\n\n</Sandpack>\n\nこれで `Form` は常に 2 番目の子になり、同じ位置にあるため state が保持されます。ただし、このアプローチは分かりづらく、誰かが `null` を削除してしまう危険性があるため、注意が必要です。\n\n</Solution>\n\n#### 2 つのフィールドを入れ替え {/*swap-two-form-fields*/}\n\nこれは姓と名を入力できるフォームです。また、どちらのフィールドが最初に表示されるかを制御するチェックボックスもあります。チェックボックスにチェックを入れると \"Last name\" フィールドが \"First name\" フィールドの前に表示されるようになります。\n\nほぼ問題なく動作していますが、バグがあります。\"First name\" 欄に何か入力してからチェックボックスにチェックを入れると、テキストがいまや \"Last name\" になった 1 番目の入力フィールドに残ってしまいます。順序を逆にしたときに入力テキスト*も*移動するように修正してください。\n\n<Hint>\n\nこのフィールドでは、親要素内の位置を使うだけでは十分ではないようです。再レンダー間で React に state の照合を行わせる方法がありませんでしたか？\n\n</Hint>\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nexport default function App() {\n  const [reverse, setReverse] = useState(false);\n  let checkbox = (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={reverse}\n        onChange={e => setReverse(e.target.checked)}\n      />\n      Reverse order\n    </label>\n  );\n  if (reverse) {\n    return (\n      <>\n        <Field label=\"Last name\" /> \n        <Field label=\"First name\" />\n        {checkbox}\n      </>\n    );\n  } else {\n    return (\n      <>\n        <Field label=\"First name\" /> \n        <Field label=\"Last name\" />\n        {checkbox}\n      </>\n    );    \n  }\n}\n\nfunction Field({ label }) {\n  const [text, setText] = useState('');\n  return (\n    <label>\n      {label}:{' '}\n      <input\n        type=\"text\"\n        value={text}\n        placeholder={label}\n        onChange={e => setText(e.target.value)}\n      />\n    </label>\n  );\n}\n```\n\n```css\nlabel { display: block; margin: 10px 0; }\n```\n\n</Sandpack>\n\n<Solution>\n\n`if` および `else` の両方の分岐の `<Field>` コンポーネントに `key` を指定します。これにより、親要素内での順序が変わっても、どちらの `<Field>` に正しく state を「マッチ」させればいいのか、React が理解できるようになります。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nexport default function App() {\n  const [reverse, setReverse] = useState(false);\n  let checkbox = (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={reverse}\n        onChange={e => setReverse(e.target.checked)}\n      />\n      Reverse order\n    </label>\n  );\n  if (reverse) {\n    return (\n      <>\n        <Field key=\"lastName\" label=\"Last name\" /> \n        <Field key=\"firstName\" label=\"First name\" />\n        {checkbox}\n      </>\n    );\n  } else {\n    return (\n      <>\n        <Field key=\"firstName\" label=\"First name\" /> \n        <Field key=\"lastName\" label=\"Last name\" />\n        {checkbox}\n      </>\n    );    \n  }\n}\n\nfunction Field({ label }) {\n  const [text, setText] = useState('');\n  return (\n    <label>\n      {label}:{' '}\n      <input\n        type=\"text\"\n        value={text}\n        placeholder={label}\n        onChange={e => setText(e.target.value)}\n      />\n    </label>\n  );\n}\n```\n\n```css\nlabel { display: block; margin: 10px 0; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### 詳細フォームをリセット {/*reset-a-detail-form*/}\n\nこれは、編集可能な連絡先リストです。選択された連絡先の詳細情報を編集した後に、\"Save\" ボタンを押して更新するか、\"Reset\" ボタンを押して編集を元に戻すことができます。\n\n異なる連絡先（例えば Alice）を選択すると、state は更新されるのに、フォームには前の連絡先の詳細が表示されたままになっています。選択されている連絡先が変更されたときに、正しくフォームがリセットされるように修正してください。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ContactList from './ContactList.js';\nimport EditContact from './EditContact.js';\n\nexport default function ContactManager() {\n  const [\n    contacts,\n    setContacts\n  ] = useState(initialContacts);\n  const [\n    selectedId,\n    setSelectedId\n  ] = useState(0);\n  const selectedContact = contacts.find(c =>\n    c.id === selectedId\n  );\n\n  function handleSave(updatedData) {\n    const nextContacts = contacts.map(c => {\n      if (c.id === updatedData.id) {\n        return updatedData;\n      } else {\n        return c;\n      }\n    });\n    setContacts(nextContacts);\n  }\n\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedId={selectedId}\n        onSelect={id => setSelectedId(id)}\n      />\n      <hr />\n      <EditContact\n        initialData={selectedContact}\n        onSave={handleSave}\n      />\n    </div>\n  )\n}\n\nconst initialContacts = [\n  { id: 0, name: 'Taylor', email: 'taylor@mail.com' },\n  { id: 1, name: 'Alice', email: 'alice@mail.com' },\n  { id: 2, name: 'Bob', email: 'bob@mail.com' }\n];\n```\n\n```js src/ContactList.js\nexport default function ContactList({\n  contacts,\n  selectedId,\n  onSelect\n}) {\n  return (\n    <section>\n      <ul>\n        {contacts.map(contact =>\n          <li key={contact.id}>\n            <button onClick={() => {\n              onSelect(contact.id);\n            }}>\n              {contact.id === selectedId ?\n                <b>{contact.name}</b> :\n                contact.name\n              }\n            </button>\n          </li>\n        )}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/EditContact.js\nimport { useState } from 'react';\n\nexport default function EditContact({ initialData, onSave }) {\n  const [name, setName] = useState(initialData.name);\n  const [email, setEmail] = useState(initialData.email);\n  return (\n    <section>\n      <label>\n        Name:{' '}\n        <input\n          type=\"text\"\n          value={name}\n          onChange={e => setName(e.target.value)}\n        />\n      </label>\n      <label>\n        Email:{' '}\n        <input\n          type=\"email\"\n          value={email}\n          onChange={e => setEmail(e.target.value)}\n        />\n      </label>\n      <button onClick={() => {\n        const updatedData = {\n          id: initialData.id,\n          name: name,\n          email: email\n        };\n        onSave(updatedData);\n      }}>\n        Save\n      </button>\n      <button onClick={() => {\n        setName(initialData.name);\n        setEmail(initialData.email);\n      }}>\n        Reset\n      </button>\n    </section>\n  );\n}\n```\n\n```css\nul, li {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli { display: inline-block; }\nli button {\n  padding: 10px;\n}\nlabel {\n  display: block;\n  margin: 10px 0;\n}\nbutton {\n  margin-right: 10px;\n  margin-bottom: 10px;\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n`EditContact` コンポーネントに `key={selectedId}` を指定します。これにより、連絡先を切り替えたときにフォームがリセットされるようになります。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ContactList from './ContactList.js';\nimport EditContact from './EditContact.js';\n\nexport default function ContactManager() {\n  const [\n    contacts,\n    setContacts\n  ] = useState(initialContacts);\n  const [\n    selectedId,\n    setSelectedId\n  ] = useState(0);\n  const selectedContact = contacts.find(c =>\n    c.id === selectedId\n  );\n\n  function handleSave(updatedData) {\n    const nextContacts = contacts.map(c => {\n      if (c.id === updatedData.id) {\n        return updatedData;\n      } else {\n        return c;\n      }\n    });\n    setContacts(nextContacts);\n  }\n\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedId={selectedId}\n        onSelect={id => setSelectedId(id)}\n      />\n      <hr />\n      <EditContact\n        key={selectedId}\n        initialData={selectedContact}\n        onSave={handleSave}\n      />\n    </div>\n  )\n}\n\nconst initialContacts = [\n  { id: 0, name: 'Taylor', email: 'taylor@mail.com' },\n  { id: 1, name: 'Alice', email: 'alice@mail.com' },\n  { id: 2, name: 'Bob', email: 'bob@mail.com' }\n];\n```\n\n```js src/ContactList.js\nexport default function ContactList({\n  contacts,\n  selectedId,\n  onSelect\n}) {\n  return (\n    <section>\n      <ul>\n        {contacts.map(contact =>\n          <li key={contact.id}>\n            <button onClick={() => {\n              onSelect(contact.id);\n            }}>\n              {contact.id === selectedId ?\n                <b>{contact.name}</b> :\n                contact.name\n              }\n            </button>\n          </li>\n        )}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/EditContact.js\nimport { useState } from 'react';\n\nexport default function EditContact({ initialData, onSave }) {\n  const [name, setName] = useState(initialData.name);\n  const [email, setEmail] = useState(initialData.email);\n  return (\n    <section>\n      <label>\n        Name:{' '}\n        <input\n          type=\"text\"\n          value={name}\n          onChange={e => setName(e.target.value)}\n        />\n      </label>\n      <label>\n        Email:{' '}\n        <input\n          type=\"email\"\n          value={email}\n          onChange={e => setEmail(e.target.value)}\n        />\n      </label>\n      <button onClick={() => {\n        const updatedData = {\n          id: initialData.id,\n          name: name,\n          email: email\n        };\n        onSave(updatedData);\n      }}>\n        Save\n      </button>\n      <button onClick={() => {\n        setName(initialData.name);\n        setEmail(initialData.email);\n      }}>\n        Reset\n      </button>\n    </section>\n  );\n}\n```\n\n```css\nul, li {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli { display: inline-block; }\nli button {\n  padding: 10px;\n}\nlabel {\n  display: block;\n  margin: 10px 0;\n}\nbutton {\n  margin-right: 10px;\n  margin-bottom: 10px;\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n#### 読み込み中に画像をクリア {/*clear-an-image-while-its-loading*/}\n\n\"Next\" ボタンを押すとブラウザが次の画像を読み込み始めます。ただし、同一の `<img>` タグを使って画像を表示しているため、このままでは次の画像が読み込まれるまで前の画像が表示されたままになります。テキストが常に画像と一致することが重要である場合、これは望ましくないかもしれません。\"Next\" を押した瞬間に前の画像がクリアされるように変更してください。\n\n<Hint>\n\nReact に DOM を再利用させず再作成させる方法がありませんでしたか？\n\n</Hint>\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Gallery() {\n  const [index, setIndex] = useState(0);\n  const hasNext = index < images.length - 1;\n\n  function handleClick() {\n    if (hasNext) {\n      setIndex(index + 1);\n    } else {\n      setIndex(0);\n    }\n  }\n\n  let image = images[index];\n  return (\n    <>\n      <button onClick={handleClick}>\n        Next\n      </button>\n      <h3>\n        Image {index + 1} of {images.length}\n      </h3>\n      <img src={image.src} />\n      <p>\n        {image.place}\n      </p>\n    </>\n  );\n}\n\nlet images = [{\n  place: 'Penang, Malaysia',\n  src: 'https://i.imgur.com/FJeJR8M.jpg'\n}, {\n  place: 'Lisbon, Portugal',\n  src: 'https://i.imgur.com/dB2LRbj.jpg'\n}, {\n  place: 'Bilbao, Spain',\n  src: 'https://i.imgur.com/z08o2TS.jpg'\n}, {\n  place: 'Valparaíso, Chile',\n  src: 'https://i.imgur.com/Y3utgTi.jpg'\n}, {\n  place: 'Schwyz, Switzerland',\n  src: 'https://i.imgur.com/JBbMpWY.jpg'\n}, {\n  place: 'Prague, Czechia',\n  src: 'https://i.imgur.com/QwUKKmF.jpg'\n}, {\n  place: 'Ljubljana, Slovenia',\n  src: 'https://i.imgur.com/3aIiwfm.jpg'\n}];\n```\n\n```css\nimg { width: 150px; height: 150px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n`<img>` タグに `key` を渡しましょう。`key` が変更されると、React は `<img>` の DOM ノードを再作成します。こうすると画像が読み込まれるたびに瞬間的に画面が白くなってしまうため、アプリ内のあらゆる画像でこれが望ましいわけではありません。しかし画像が常にテキストと対応していることを保証したい場合は、この方法が適しています。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Gallery() {\n  const [index, setIndex] = useState(0);\n  const hasNext = index < images.length - 1;\n\n  function handleClick() {\n    if (hasNext) {\n      setIndex(index + 1);\n    } else {\n      setIndex(0);\n    }\n  }\n\n  let image = images[index];\n  return (\n    <>\n      <button onClick={handleClick}>\n        Next\n      </button>\n      <h3>\n        Image {index + 1} of {images.length}\n      </h3>\n      <img key={image.src} src={image.src} />\n      <p>\n        {image.place}\n      </p>\n    </>\n  );\n}\n\nlet images = [{\n  place: 'Penang, Malaysia',\n  src: 'https://i.imgur.com/FJeJR8M.jpg'\n}, {\n  place: 'Lisbon, Portugal',\n  src: 'https://i.imgur.com/dB2LRbj.jpg'\n}, {\n  place: 'Bilbao, Spain',\n  src: 'https://i.imgur.com/z08o2TS.jpg'\n}, {\n  place: 'Valparaíso, Chile',\n  src: 'https://i.imgur.com/Y3utgTi.jpg'\n}, {\n  place: 'Schwyz, Switzerland',\n  src: 'https://i.imgur.com/JBbMpWY.jpg'\n}, {\n  place: 'Prague, Czechia',\n  src: 'https://i.imgur.com/QwUKKmF.jpg'\n}, {\n  place: 'Ljubljana, Slovenia',\n  src: 'https://i.imgur.com/3aIiwfm.jpg'\n}];\n```\n\n```css\nimg { width: 150px; height: 150px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### リスト内の state 位置ズレを修正 {/*fix-misplaced-state-in-the-list*/}\n\nこのリストでは各 `Contact` は、\"Show email\" ボタンが押されたかどうかを保持する state を持っています。Alice の \"Show email\" を押してから、\"Show in reverse order\" のチェックボックスにチェックを入れてください。すると Taylor のメール欄が展開されてしまい、一方で最下部に移動した Alice のメール欄は閉じられてしまいます。\n\n選択中の並び順と関係なく、展開中かどうかの state がそれぞれの連絡先に関連付けられるよう、修正してください。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport Contact from './Contact.js';\n\nexport default function ContactList() {\n  const [reverse, setReverse] = useState(false);\n\n  const displayedContacts = [...contacts];\n  if (reverse) {\n    displayedContacts.reverse();\n  }\n\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={reverse}\n          onChange={e => {\n            setReverse(e.target.checked)\n          }}\n        />{' '}\n        Show in reverse order\n      </label>\n      <ul>\n        {displayedContacts.map((contact, i) =>\n          <li key={i}>\n            <Contact contact={contact} />\n          </li>\n        )}\n      </ul>\n    </>\n  );\n}\n\nconst contacts = [\n  { id: 0, name: 'Alice', email: 'alice@mail.com' },\n  { id: 1, name: 'Bob', email: 'bob@mail.com' },\n  { id: 2, name: 'Taylor', email: 'taylor@mail.com' }\n];\n```\n\n```js src/Contact.js\nimport { useState } from 'react';\n\nexport default function Contact({ contact }) {\n  const [expanded, setExpanded] = useState(false);\n  return (\n    <>\n      <p><b>{contact.name}</b></p>\n      {expanded &&\n        <p><i>{contact.email}</i></p>\n      }\n      <button onClick={() => {\n        setExpanded(!expanded);\n      }}>\n        {expanded ? 'Hide' : 'Show'} email\n      </button>\n    </>\n  );\n}\n```\n\n```css\nul, li {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli {\n  margin-bottom: 20px;\n}\nlabel {\n  display: block;\n  margin: 10px 0;\n}\nbutton {\n  margin-right: 10px;\n  margin-bottom: 10px;\n}\n```\n\n</Sandpack>\n\n<Solution>\n\nこの例の問題は `key` としてインデックスを使用してしまっていたことです。\n\n```js\n{displayedContacts.map((contact, i) =>\n  <li key={i}>\n```\n\nしかし state は*それぞれ特定の連絡先*に関連づける必要があるはずです。\n\n連絡先の ID を `key` として使用することで、問題が解決します：\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport Contact from './Contact.js';\n\nexport default function ContactList() {\n  const [reverse, setReverse] = useState(false);\n\n  const displayedContacts = [...contacts];\n  if (reverse) {\n    displayedContacts.reverse();\n  }\n\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={reverse}\n          onChange={e => {\n            setReverse(e.target.checked)\n          }}\n        />{' '}\n        Show in reverse order\n      </label>\n      <ul>\n        {displayedContacts.map(contact =>\n          <li key={contact.id}>\n            <Contact contact={contact} />\n          </li>\n        )}\n      </ul>\n    </>\n  );\n}\n\nconst contacts = [\n  { id: 0, name: 'Alice', email: 'alice@mail.com' },\n  { id: 1, name: 'Bob', email: 'bob@mail.com' },\n  { id: 2, name: 'Taylor', email: 'taylor@mail.com' }\n];\n```\n\n```js src/Contact.js\nimport { useState } from 'react';\n\nexport default function Contact({ contact }) {\n  const [expanded, setExpanded] = useState(false);\n  return (\n    <>\n      <p><b>{contact.name}</b></p>\n      {expanded &&\n        <p><i>{contact.email}</i></p>\n      }\n      <button onClick={() => {\n        setExpanded(!expanded);\n      }}>\n        {expanded ? 'Hide' : 'Show'} email\n      </button>\n    </>\n  );\n}\n```\n\n```css\nul, li {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli {\n  margin-bottom: 20px;\n}\nlabel {\n  display: block;\n  margin: 10px 0;\n}\nbutton {\n  margin-right: 10px;\n  margin-bottom: 10px;\n}\n```\n\n</Sandpack>\n\nstate はツリー内の位置と関連付けられています。`key` を使うことで、表示順に依存しない、名前付きの位置情報を指定することができます。\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/queueing-a-series-of-state-updates.md",
    "content": "---\ntitle: 一連の state の更新をキューに入れる\n---\n\n<Intro>\n\nstate 変数をセットすることで、新しいレンダーがキューに予約されます。しかし、次のレンダーをキューに入れる前に、state の値に対して複数の操作を行いたい場合があります。そのためには、React が state の更新をバッチ処理（batching, 一括処理）する方法についての理解が役に立ちます。\n\n</Intro>\n\n<YouWillLearn>\n\n* 「バッチ処理」とは何か、React が複数の state 更新を処理する際にどのように使用されるのか\n* 同じ state 変数に対し連続して複数の更新を適用する方法\n\n</YouWillLearn>\n\n## React は state 更新をまとめて処理する {/*react-batches-state-updates*/}\n\n以下で \"+3\" ボタンをクリックした場合、`setNumber(number + 1)` を 3 回呼び出しているので、カウンタが 3 回インクリメントされると思うかもしれません。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [number, setNumber] = useState(0);\n\n  return (\n    <>\n      <h1>{number}</h1>\n      <button onClick={() => {\n        setNumber(number + 1);\n        setNumber(number + 1);\n        setNumber(number + 1);\n      }}>+3</button>\n    </>\n  )\n}\n```\n\n```css\nbutton { display: inline-block; margin: 10px; font-size: 20px; }\nh1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }\n```\n\n</Sandpack>\n\nしかし、前のセクションで説明したように、[個々のレンダー内の state 値は固定です](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time)。従って `setNumber(1)` を何度呼び出しても、最初のレンダー内ではイベントハンドラ内の `number` の値は常に `0` です。\n\n```js\nsetNumber(0 + 1);\nsetNumber(0 + 1);\nsetNumber(0 + 1);\n```\n\nしかしながら、ここにもう 1 つ別の要素が関わってきます。**イベントハンドラ内のすべてのコードが実行されるまで、React は state の更新処理を待機します**。このため、再レンダーはこれらの `setNumber()` 呼び出しがすべて終わった後で行われます。\n\nレストランで注文を取るウェイターの話を思い出すかもしれません。ウェイターは最初の料理の注文を聞いた瞬間にキッチンにかけこむわけではありません！ 代わりに、客の注文を最後まで聞き、訂正がある場合はそれも聞き取り、さらにはテーブルの他の客からの注文もまとめて受け取るはずです。\n\n<Illustration src=\"/images/docs/illustrations/i_react-batching.png\"  alt=\"レストランで何度も注文をしている客と、それを聞き取っているウェイター役の React。客が何度も setState() したとしても、ウェイターは最後のものだけを最終的な注文として聞き取って用紙に書き込む。\" />\n\nこれにより、複数の state 変数（複数のコンポーネントからの場合も含む）の更新を、[再レンダー](/learn/render-and-commit#re-renders-when-state-updates)の過剰なトリガなしに行うことができます。これは、イベントハンドラおよびその中のコードがすべて完了した*後*まで、UI は更新されないということでもあります。このような動作は**バッチ処理**（バッチング）とも呼ばれ、これにより React アプリの動作が格段に素早くなります。またこれは、変数のうち一部のみが更新された「中途半端な」レンダー結果に混乱させられずに済むということでもあります。\n\n**React は、クリックのような意図的なイベントが*複数回*発生した場合、それらにまたがったバッチ処理までは行いません**。各クリックは別々に処理されます。React は一般的に安全と判断される場合にのみバッチ処理を行いますので、安心してください。たとえば、最初のボタンクリックでフォームを無効にしたのであれば、2 度目のクリックでフォームが再び送信されてしまわないことが保証されます。\n\n## 次のレンダー前に同じ state を複数回更新する {/*updating-the-same-state-multiple-times-before-the-next-render*/}\n\n一般的なユースケースではありませんが、次のレンダー前に同じ state 変数を複数回更新する場合、`setNumber(number + 1)` のようにして*次の state 値*を渡すのではなく、代わりに `setNumber(n => n + 1)` のようなキュー内のひとつ前の state に基づいて次の state を計算する*関数*を渡すことができます。これは、state の値を単に置き換えるのではなく、代わりに React に「その state の値に対してこのようにせよ」と伝えるための手段です。\n\nこのカウンタをインクリメントしてみてください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [number, setNumber] = useState(0);\n\n  return (\n    <>\n      <h1>{number}</h1>\n      <button onClick={() => {\n        setNumber(n => n + 1);\n        setNumber(n => n + 1);\n        setNumber(n => n + 1);\n      }}>+3</button>\n    </>\n  )\n}\n```\n\n```css\nbutton { display: inline-block; margin: 10px; font-size: 20px; }\nh1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }\n```\n\n</Sandpack>\n\nここで、`n => n + 1` は**更新用関数 (updater function)** と呼ばれます。これを state のセッタに渡すと：\n\n1. React はこの関数をキューに入れて、イベントハンドラ内の他のコードがすべて実行された後に処理されるようにします。\n2. 次のレンダー中に、React はキューを処理し、最後に更新された state を返します。\n\n```js\nsetNumber(n => n + 1);\nsetNumber(n => n + 1);\nsetNumber(n => n + 1);\n```\n\n以下に、イベントハンドラを実行するときに、React はこれらのコードをどのように処理するかを示します。\n\n1. `setNumber(n => n + 1)`: `n => n + 1` は関数。React はこれをキューに追加する。\n1. `setNumber(n => n + 1)`: `n => n + 1` は関数。React はこれをキューに追加する。\n1. `setNumber(n => n + 1)`: `n => n + 1` は関数。React はこれをキューに追加する。\n\n次のレンダー中に `useState` が呼び出されると、React はこのキューを処理します。前回 `number` という state の値は `0` だったので、それがひとつ目の更新用関数の引数 `n` に渡されます。React はひとつ前の更新用関数の返り値を取得し、それを次の更新用関数の `n` に渡し、というように続いていきます：\n\n| キュー内の更新処理 | `n` | 返り値 |\n|--------------|---------|-----|\n| `n => n + 1` | `0` | `0 + 1 = 1` |\n| `n => n + 1` | `1` | `1 + 1 = 2` |\n| `n => n + 1` | `2` | `2 + 1 = 3` |\n\nReact は `3` を最終結果として保存し、`useState` から返します。\n\nこのような理由で、上記の例で \"+3\" をクリックしたときに、値が正しく 3 ずつ増加するのです。\n### state を置き換えた後に更新するとどうなるか {/*what-happens-if-you-update-state-after-replacing-it*/}\n\nでは、このイベントハンドラはどうでしょうか？ 次回のレンダーで `number` の値はどうなっていると思いますか？\n\n```js\n<button onClick={() => {\n  setNumber(number + 5);\n  setNumber(n => n + 1);\n}}>\n```\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [number, setNumber] = useState(0);\n\n  return (\n    <>\n      <h1>{number}</h1>\n      <button onClick={() => {\n        setNumber(number + 5);\n        setNumber(n => n + 1);\n      }}>Increase the number</button>\n    </>\n  )\n}\n```\n\n```css\nbutton { display: inline-block; margin: 10px; font-size: 20px; }\nh1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }\n```\n\n</Sandpack>\n\nこのイベントハンドラでは、React に次のように指示しています。\n\n1. `setNumber(number + 5)`: `number` は `0` なので、`setNumber(0 + 5)`。React はキューに *\"`5` に置き換えよ\"* という命令を追加する。\n2. `setNumber(n => n + 1)`: `n => n + 1` は更新用関数。React は*その関数*をキューに追加する。\n\n次のレンダー時、React は state 更新キューを処理します。\n\n| キュー内の更新処理 | `n` | 返り値 |\n|--------------|---------|-----|\n| \"`5` に置き換えよ\" | `0` (未使用) | `5` |\n| `n => n + 1` | `5` | `5 + 1 = 6` |\n\nReact は `6` を最終結果として保存し、`useState` から返します。\n\n<Note>\n\nお気づきかもしれませんが、`setState(5)` とは、実際には `n` の使用されない `setState(n => 5)` と同じように動作します！\n\n</Note>\n\n### state を更新した後に置き換えるとどうなるか {/*what-happens-if-you-replace-state-after-updating-it*/}\n\nもうひとつ別の例を試してみましょう。次回のレンダーで `number` は何になると思いますか？\n\n```js\n<button onClick={() => {\n  setNumber(number + 5);\n  setNumber(n => n + 1);\n  setNumber(42);\n}}>\n```\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [number, setNumber] = useState(0);\n\n  return (\n    <>\n      <h1>{number}</h1>\n      <button onClick={() => {\n        setNumber(number + 5);\n        setNumber(n => n + 1);\n        setNumber(42);\n      }}>Increase the number</button>\n    </>\n  )\n}\n```\n\n```css\nbutton { display: inline-block; margin: 10px; font-size: 20px; }\nh1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }\n```\n\n</Sandpack>\n\nこのイベントハンドラを実行する際、React は以下の順番でコードを処理します。\n\n1. `setNumber(number + 5)`: `number` は `0` なので `setNumber(0 + 5)`。React はキューに *\"`5` に置き換えよ\"* という命令を追加する。\n2. `setNumber(n => n + 1)`: `n => n + 1` は更新用関数。React は*その関数*をキューに追加する。\n3. `setNumber(42)`: React はキューに *\"`42` に置き換えよ\"* という命令を追加する。\n\n次のレンダー中に、React は state 更新キューを処理します。\n\n|   キュー内の更新処理       | `n` | 返り値 |\n|--------------|---------|-----|\n| \"`5` に置き換えよ\" | `0` （未使用） | `5` |\n| `n => n + 1` | `5` | `5 + 1 = 6` |\n| \"`42` に置き換えよ\" | `6` （未使用） | `42` |\n\nというわけで、React は最終結果として `42` を保存し、`useState` から返します。\n\nまとめると、`setNumber` という state セッタに渡すものを、以下のように考えることができます。\n\n* **更新用関数**（例：`n => n + 1`）の場合、それがキューに追加されます。\n* **それ以外の値**（例：数値 `5`）の場合、ここまでのキューの内容を無視する \"`5` に置き換えよ\" のような命令を追加します。\n\nイベントハンドラが完了した後、React は再レンダーをトリガします。再レンダー中に React はキューを処理します。更新用関数はレンダー中に実行されるため、**更新用関数は[純関数](/learn/keeping-components-pure)である必要があり**、結果だけを*返す*ようにする必要があります。その中で state をセットしたり、他の副作用を実行したりしないでください。Strict Mode では、React は各更新用関数を 2 回実行します（ただし 2 つ目の結果は破棄されます）が、これによって間違いを見つけやすくなります。\n\n### 命名規則 {/*naming-conventions*/}\n\n対応する state 変数の頭文字を使って更新用関数の引数の名前を付けることが一般的です。\n\n```js\nsetEnabled(e => !e);\nsetLastName(ln => ln.reverse());\nsetFriendCount(fc => fc * 2);\n```\n\nもっと長いコードが好きな場合、別の一般的な慣習としては、`setEnabled(enabled => !enabled)` のように完全な state 変数名を繰り返すか、`setEnabled(prevEnabled => !prevEnabled)` のようなプレフィックスを使用することがあります。\n\n<Recap>\n\n* state をセットしても既存のレンダーの変数は変更されず、代わりに新しいレンダーが要求される。\n* React は、イベントハンドラが完了してから state の更新を処理する。これをバッチ処理と呼ぶ。\n* 1 つのイベントで複数回 state を更新したい場合 `setNumber(n => n + 1)` という形の更新用関数を使用できる。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### リクエストカウンタの修正 {/*fix-a-request-counter*/}\n\nあなたは、ユーザが美術品に対して複数の注文処理を同時並行で行える、アートマーケットアプリの開発をしています。ユーザが \"Buy\" ボタンを押すたびに、\"Pending\"（処理中）カウンタが 1 つずつ増えるようにする必要があります。3 秒後に \"Pending\" カウンタが 1 減り、\"Completed\" カウンタが 1 増える必要があります。\n\nしかし、\"Pending\" カウンタは意図した通りに動作していません。\"Buy\" を押すと、\"Pending\" が `-1` に減少します（あり得ない！）。また、2 回素早くクリックすると、両方のカウンタが予測不可能な挙動を示します。\n\nなぜこれが起こるのでしょうか？ 両方のカウンタを修正してください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function RequestTracker() {\n  const [pending, setPending] = useState(0);\n  const [completed, setCompleted] = useState(0);\n\n  async function handleClick() {\n    setPending(pending + 1);\n    await delay(3000);\n    setPending(pending - 1);\n    setCompleted(completed + 1);\n  }\n\n  return (\n    <>\n      <h3>\n        Pending: {pending}\n      </h3>\n      <h3>\n        Completed: {completed}\n      </h3>\n      <button onClick={handleClick}>\n        Buy     \n      </button>\n    </>\n  );\n}\n\nfunction delay(ms) {\n  return new Promise(resolve => {\n    setTimeout(resolve, ms);\n  });\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n`handleClick` イベントハンドラ内では、`pending` と `completed` の値はクリックイベントが起きた時点での値に対応しています。最初のレンダーでは、`pending` は `0` だったため、`setPending(pending - 1)` は `setPending(-1)` となり、これは間違いです。カウンタを*インクリメント*または*デクリメント*したいので、クリック時に決まる具体的な値をセットするのではなく、代わりに更新用関数を渡すことができます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function RequestTracker() {\n  const [pending, setPending] = useState(0);\n  const [completed, setCompleted] = useState(0);\n\n  async function handleClick() {\n    setPending(p => p + 1);\n    await delay(3000);\n    setPending(p => p - 1);\n    setCompleted(c => c + 1);\n  }\n\n  return (\n    <>\n      <h3>\n        Pending: {pending}\n      </h3>\n      <h3>\n        Completed: {completed}\n      </h3>\n      <button onClick={handleClick}>\n        Buy     \n      </button>\n    </>\n  );\n}\n\nfunction delay(ms) {\n  return new Promise(resolve => {\n    setTimeout(resolve, ms);\n  });\n}\n```\n\n</Sandpack>\n\nこれにより、カウンタをインクリメントまたはデクリメントする際に、クリック時の state ではなく、最新の state に対して行われることが保証されます。\n\n</Solution>\n\n#### state キューの独自実装 {/*implement-the-state-queue-yourself*/}\n\nこのチャレンジ問題では、React のごく一部をゼロから再実装します！ それほど難しくありません。\n\nサンドボックスプレビューをスクロールしてください。**4 つのテストケース**が表示されていることに注意してください。それらはこのページの先ほどの例に対応しています。あなたの仕事は、`getFinalState` 関数を実装して、それぞれのケースに対して正しい結果を返すことです。正しく実装すると、すべてのテストが通るはずです。\n\n2 つの引数を受け取ることになります。`baseState` は初期 state（例えば `0`）であり、`queue` は数値（例えば `5`）または更新用関数（例えば `n => n + 1`）のいずれかが、キューに入れられた順番で入っている配列です。\n\nあなたの仕事は、このページ内の表で見たような処理を行って、最終的な state を返すことです！\n\n<Hint>\n\n難しいと感じたら、次のコード構造から始めてください：\n\n```js\nexport function getFinalState(baseState, queue) {\n  let finalState = baseState;\n\n  for (let update of queue) {\n    if (typeof update === 'function') {\n      // TODO: apply the updater function\n    } else {\n      // TODO: replace the state\n    }\n  }\n\n  return finalState;\n}\n```\n\n足りない行を埋めてください！\n\n</Hint>\n\n<Sandpack>\n\n```js src/processQueue.js active\nexport function getFinalState(baseState, queue) {\n  let finalState = baseState;\n\n  // TODO: do something with the queue...\n\n  return finalState;\n}\n```\n\n```js src/App.js\nimport { getFinalState } from './processQueue.js';\n\nfunction increment(n) {\n  return n + 1;\n}\nincrement.toString = () => 'n => n+1';\n\nexport default function App() {\n  return (\n    <>\n      <TestCase\n        baseState={0}\n        queue={[1, 1, 1]}\n        expected={1}\n      />\n      <hr />\n      <TestCase\n        baseState={0}\n        queue={[\n          increment,\n          increment,\n          increment\n        ]}\n        expected={3}\n      />\n      <hr />\n      <TestCase\n        baseState={0}\n        queue={[\n          5,\n          increment,\n        ]}\n        expected={6}\n      />\n      <hr />\n      <TestCase\n        baseState={0}\n        queue={[\n          5,\n          increment,\n          42,\n        ]}\n        expected={42}\n      />\n    </>\n  );\n}\n\nfunction TestCase({\n  baseState,\n  queue,\n  expected\n}) {\n  const actual = getFinalState(baseState, queue);\n  return (\n    <>\n      <p>Base state: <b>{baseState}</b></p>\n      <p>Queue: <b>[{queue.join(', ')}]</b></p>\n      <p>Expected result: <b>{expected}</b></p>\n      <p style={{\n        color: actual === expected ?\n          'green' :\n          'red'\n      }}>\n        Your result: <b>{actual}</b>\n        {' '}\n        ({actual === expected ?\n          'correct' :\n          'wrong'\n        })\n      </p>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n以下が、このページで説明してきたような形で React が最終 state を計算するために使用しているアルゴリズムそのものです：\n\n<Sandpack>\n\n```js src/processQueue.js active\nexport function getFinalState(baseState, queue) {\n  let finalState = baseState;\n\n  for (let update of queue) {\n    if (typeof update === 'function') {\n      // Apply the updater function.\n      finalState = update(finalState);\n    } else {\n      // Replace the next state.\n      finalState = update;\n    }\n  }\n\n  return finalState;\n}\n```\n\n```js src/App.js\nimport { getFinalState } from './processQueue.js';\n\nfunction increment(n) {\n  return n + 1;\n}\nincrement.toString = () => 'n => n+1';\n\nexport default function App() {\n  return (\n    <>\n      <TestCase\n        baseState={0}\n        queue={[1, 1, 1]}\n        expected={1}\n      />\n      <hr />\n      <TestCase\n        baseState={0}\n        queue={[\n          increment,\n          increment,\n          increment\n        ]}\n        expected={3}\n      />\n      <hr />\n      <TestCase\n        baseState={0}\n        queue={[\n          5,\n          increment,\n        ]}\n        expected={6}\n      />\n      <hr />\n      <TestCase\n        baseState={0}\n        queue={[\n          5,\n          increment,\n          42,\n        ]}\n        expected={42}\n      />\n    </>\n  );\n}\n\nfunction TestCase({\n  baseState,\n  queue,\n  expected\n}) {\n  const actual = getFinalState(baseState, queue);\n  return (\n    <>\n      <p>Base state: <b>{baseState}</b></p>\n      <p>Queue: <b>[{queue.join(', ')}]</b></p>\n      <p>Expected result: <b>{expected}</b></p>\n      <p style={{\n        color: actual === expected ?\n          'green' :\n          'red'\n      }}>\n        Your result: <b>{actual}</b>\n        {' '}\n        ({actual === expected ?\n          'correct' :\n          'wrong'\n        })\n      </p>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\nこれで、React のこの部分がどのように動作するのかが分かりましたね！\n\n</Solution>\n\n</Challenges>"
  },
  {
    "path": "src/content/learn/react-compiler/debugging.md",
    "content": "---\ntitle: デバッグとトラブルシューティング\n---\n\n<Intro>\nこのガイドでは、React Compiler を使用する際に発生する問題を特定し、修正する方法を説明します。コンパイル問題のデバッグ方法と一般的な問題の解決方法を学びましょう。\n</Intro>\n\n<YouWillLearn>\n\n* コンパイラエラーとランタイムエラーの違い\n* コンパイルが失敗する一般的なパターン\n* デバッグ手順\n\n</YouWillLearn>\n\n## コンパイラの動作を理解する {/*understanding-compiler-behavior*/}\n\nReact Compiler は [React のルール](/reference/rules)に従うコードを処理するように設計されています。ルールに違反している可能性のあるコードに遭遇した場合、アプリケーションの動作を変更するリスクを冒すのではなく、最適化を安全にスキップします。\n\n### コンパイラエラーとランタイムエラー {/*compiler-errors-vs-runtime-issues*/}\n\n**コンパイラエラー** はビルド時に発生し、コードのコンパイルができなくなってしまうものです。コンパイラは問題のあるコードをエラーを発生させずにスキップするように設計されているため、このようなエラーが発生することは稀です。\n\n**ランタイムエラー** は、コンパイル後のコードが想定と異なる動作をした場合に発生します。React Compiler で問題に遭遇した場合、ほとんどの場合はランタイムエラーです。これは通常、コードがコンパイラが検出できない微妙な形で React のルールに違反し、コンパイラがスキップすべきコンポーネントを誤ってコンパイルした場合に発生します。\n\nランタイムエラーのデバッグにおいては、影響を受けているコンポーネント内にある、ESLint では検出されない React のルール違反の特定に注力してください。コンパイラはコードがこれらのルールに従うことを前提としています。ランタイムエラーが発生するのは、検出できない方法でルール違反が起きている場合なのです。\n\n\n## 一般的な違反パターン {/*common-breaking-patterns*/}\n\nReact Compiler がアプリケーションの不具合を引き起こす代表的なパターンの 1 つは、コードの正しさがメモ化の存在に依存している場合です。つまり、アプリケーションが正常に動作するために、特定の値がメモ化されることに依存している状態です。コンパイラは、あなたの手動のアプローチとは異なる方法でメモ化を行う可能性があるため、エフェクトの過剰な実行や無限ループ、更新の欠落といった予期せぬ動作を引き起こす可能性があります。\n\nこれが発生する一般的なシナリオは以下のようなものです。\n\n- **参照同一性に依存したエフェクト** - エフェクトが、複数のレンダー間でオブジェクトや配列が同じ参照を保つことに依存している場合\n- **参照の同一性を仮定した依存配列** - 依存値が不安定で、エフェクトの過剰発火や無限ループを引き起こしている場合\n- **参照同一性のチェックを条件にしたロジック** - キャッシュや最適化のために参照同一性のチェックを行っている場合\n\n## デバッグ手順 {/*debugging-workflow*/}\n\n問題に遭遇した場合は、以下の手順に従ってください。\n\n### ビルド時のコンパイラエラー {/*compiler-build-errors*/}\n\nコンパイラエラーでビルドが予期せず失敗した場合、これはコンパイラのバグである可能性が高いです。以下の情報を添えて [facebook/react](https://github.com/facebook/react/issues) リポジトリに報告してください。\n- エラーメッセージ\n- エラーを引き起こしたコード\n- React とコンパイラのバージョン\n\n### ランタイムエラー {/*runtime-issues*/}\n\nランタイム動作の問題については以下の手順に従ってください。\n\n### 1. コンパイルを一時的に無効化 {/*temporarily-disable-compilation*/}\n\n問題がコンパイラによるものかを切り分けるために `\"use no memo\"` を使用します。\n\n```js\nfunction ProblematicComponent() {\n  \"use no memo\"; // Skip compilation for this component\n  // ... rest of component\n}\n```\n\nこれで問題が解決する場合は、React のルール違反が原因である可能性が高いです。\n\n問題のあるコンポーネントから手動のメモ化（useMemo、useCallback、memo）を削除し、メモ化なしでアプリケーションが正しく動作することを確認することもできます。メモ化をすべて外しても不具合が残る場合は、React のルール違反の修正が必要です。\n\n### 2. 段階的な問題の修正 {/*fix-issues-step-by-step*/}\n\n1. 根本原因を特定する（大抵は動作がメモ化の存在に依存していることが原因）\n2. 修正のたびにテストを実施する\n3. 修正したら `\"use no memo\"` を削除する\n4. React DevTools でコンポーネントに ✨ バッジが表示されることを確認する\n\n## コンパイラバグの報告 {/*reporting-compiler-bugs*/}\n\nコンパイラバグを発見したと思われる場合は以下のようにしてください。\n\n1. **React のルール違反ではないことを確認する** - ESLint でチェックする\n2. **最小限の再現方法を特定する** - 小さな例で問題を切り分ける\n3. **コンパイラを無効化した状態でテストする** - 問題がコンパイル時にのみ発生するかを確認する\n4. **[issue](https://github.com/facebook/react/issues/new?template=compiler_bug_report.yml) を提出する**：\n   - React とコンパイラのバージョン\n   - 最小限の再現コード\n   - 期待される動作と実際の動作\n   - エラーメッセージ\n\n## 次のステップ {/*next-steps*/}\n\n- 問題を防ぐために [React のルール](/reference/rules)を確認する\n- 段階的な展開戦略については[段階的導入ガイド](/learn/react-compiler/incremental-adoption)を確認する"
  },
  {
    "path": "src/content/learn/react-compiler/incremental-adoption.md",
    "content": "---\ntitle: 段階的な導入\n---\n\n<Intro>\nReact Compiler は段階的に導入でき、まずコードベースの特定の箇所で試すことができます。このガイドでは、既存のプロジェクトでコンパイラを徐々に展開する方法を説明します。\n</Intro>\n\n<YouWillLearn>\n\n* 段階的な導入が推奨される理由\n* ディレクトリ単位で導入するための Babel の overrides の使い方\n* 明示的にコンパイルを有効化する \"use memo\" ディレクティブの使い方\n* コンポーネントを除外する \"use no memo\" ディレクティブの使い方\n* ランタイムのゲーティングによる機能フラグの運用\n* 導入状況のモニタリング方法\n\n</YouWillLearn>\n\n## なぜ段階的な導入が推奨されるのか？ {/*why-incremental-adoption*/}\n\nReact Compiler はコードベース全体を自動的に最適化するように設計されていますが、一度にすべてを導入する必要はありません。段階的な導入により、展開プロセスをコントロールでき、アプリの一部でコンパイラをテストしてから残りの部分に拡大できます。\n\n小さく始めることで、コンパイラの最適化に対する信頼を築けます。コンパイルされたコードでアプリが正しく動作することを確認し、パフォーマンスの改善を測定しつつ、コードベースに特有のエッジケースを特定できます。このアプローチは、安定性が重要な本番アプリケーションで特に価値があります。\n\n段階的な導入により、コンパイラが見つける可能性のある React のルール違反に対処することも容易になります。コードベース全体の違反を一度に修正するのではなく、コンパイラのカバレッジを拡張しながら体系的に対処できます。これにより、移行作業が管理しやすくなり、バグが混入するリスクを減らします。\n\nコードのどの部分がコンパイルされるかをコントロールすることで、コンパイラの最適化の実際の影響を測定する A/B テストを実行することもできます。このデータは、コンパイラを全体へ適用するか否かを意思決定したり、コンパイラの価値をチームに示したりするための情報として役立ちます。\n\n## 段階的な導入のアプローチ {/*approaches-to-incremental-adoption*/}\n\nReact Compiler を段階的に導入する主なアプローチは 3 つあります。\n\n1. **Babel overrides** - 特定のディレクトリにコンパイラを適用\n2. **\"use memo\" によるオプトイン** - 明示的にオプトインしたコンポーネントのみをコンパイル\n3. **ランタイムゲーティング** - フィーチャーフラグでコンパイルをコントロール\n\nどのアプローチを使っても、全体への展開前にアプリケーションの特定の部分のみでコンパイラのテストが可能です。\n\n## Babel Overrides によるディレクトリベースの導入 {/*directory-based-adoption*/}\n\nBabel の `overrides` オプションにより、コードベースの異なる部分に異なるプラグインを適用できます。これは、ディレクトリごとに React Compiler を徐々に導入するのに理想的な方法です。\n\n### 基本的な設定 {/*basic-configuration*/}\n\n特定のディレクトリにコンパイラを適用することから始めます。\n\n```js\n// babel.config.js\nmodule.exports = {\n  plugins: [\n    // Global plugins that apply to all files\n  ],\n  overrides: [\n    {\n      test: './src/modern/**/*.{js,jsx,ts,tsx}',\n      plugins: [\n        'babel-plugin-react-compiler'\n      ]\n    }\n  ]\n};\n```\n\n### カバレッジの拡張 {/*expanding-coverage*/}\n\n自信が出てきたら、より多くのディレクトリを追加します。\n\n```js\n// babel.config.js\nmodule.exports = {\n  plugins: [\n    // Global plugins\n  ],\n  overrides: [\n    {\n      test: ['./src/modern/**/*.{js,jsx,ts,tsx}', './src/features/**/*.{js,jsx,ts,tsx}'],\n      plugins: [\n        'babel-plugin-react-compiler'\n      ]\n    },\n    {\n      test: './src/legacy/**/*.{js,jsx,ts,tsx}',\n      plugins: [\n        // Different plugins for legacy code\n      ]\n    }\n  ]\n};\n```\n\n### コンパイラオプション {/*with-compiler-options*/}\n\nオーバーライドごとにコンパイラオプションを設定することもできます。\n\n```js\n// babel.config.js\nmodule.exports = {\n  plugins: [],\n  overrides: [\n    {\n      test: './src/experimental/**/*.{js,jsx,ts,tsx}',\n      plugins: [\n        ['babel-plugin-react-compiler', {\n          // options ...\n        }]\n      ]\n    },\n    {\n      test: './src/production/**/*.{js,jsx,ts,tsx}',\n      plugins: [\n        ['babel-plugin-react-compiler', {\n          // options ...\n        }]\n      ]\n    }\n  ]\n};\n```\n\n\n## \"use memo\" によるオプトインモード {/*opt-in-mode-with-use-memo*/}\n\nより厳格な制御を行うため、`compilationMode: 'annotation'` を使用して、`\"use memo\"` ディレクティブで明示的にオプトインしたコンポーネントとフックのみをコンパイルできます。\n\n<Note>\nこのアプローチにより、個々のコンポーネントとフックに対する細かいコントロールが可能になります。ディレクトリ全体に影響を与えることなく、特定のコンポーネントでコンパイラをテストしたい場合に有用です。\n</Note>\n\n### アノテーションモードの設定 {/*annotation-mode-configuration*/}\n\n```js\n// babel.config.js\nmodule.exports = {\n  plugins: [\n    ['babel-plugin-react-compiler', {\n      compilationMode: 'annotation',\n    }],\n  ],\n};\n```\n\n### ディレクティブの使用 {/*using-the-directive*/}\n\nコンパイルしたい関数の先頭に `\"use memo\"` を追加します。\n\n```js\nfunction TodoList({ todos }) {\n  \"use memo\"; // Opt this component into compilation\n\n  const sortedTodos = todos.slice().sort();\n\n  return (\n    <ul>\n      {sortedTodos.map(todo => (\n        <TodoItem key={todo.id} todo={todo} />\n      ))}\n    </ul>\n  );\n}\n\nfunction useSortedData(data) {\n  \"use memo\"; // Opt this hook into compilation\n\n  return data.slice().sort();\n}\n```\n\n`compilationMode: 'annotation'` を指定する際は、以下を行う必要があります。\n- 最適化したいすべてのコンポーネントに `\"use memo\"` を追加\n- すべてのカスタムフックに `\"use memo\"` を追加\n- 新しいコンポーネントに追加することを忘れない\n\nこれにより、コンパイラの影響を評価しながら、どのコンポーネントをコンパイルするかを正確にコントロールできます。\n\n## ゲーティング機能によるフィーチャーフラグ制御 {/*runtime-feature-flags-with-gating*/}\n\n`gating` オプションにより、フィーチャーフラグを使用してランタイムでコンパイルをコントロールできます。これは A/B テストを実行したり、ユーザセグメントに基づいてコンパイラを徐々に展開したりするのに有用です。\n\n### ゲーティングの仕組み {/*how-gating-works*/}\n\nコンパイラは最適化されたコードをランタイムチェックでラップします。ゲートが `true` を返す場合、最適化されたバージョンが実行されます。そうでなければ、元のコードが実行されます。\n\n### ゲーティングの設定 {/*gating-configuration*/}\n\n```js\n// babel.config.js\nmodule.exports = {\n  plugins: [\n    ['babel-plugin-react-compiler', {\n      gating: {\n        source: 'ReactCompilerFeatureFlags',\n        importSpecifierName: 'isCompilerEnabled',\n      },\n    }],\n  ],\n};\n```\n\n### フィーチャーフラグの実装 {/*implementing-the-feature-flag*/}\n\nゲーティング関数をエクスポートするモジュールを作成します。\n\n```js\n// ReactCompilerFeatureFlags.js\nexport function isCompilerEnabled() {\n  // Use your feature flag system\n  return getFeatureFlag('react-compiler-enabled');\n}\n```\n\n## 導入時のトラブルシューティング {/*troubleshooting-adoption*/}\n\n導入中に問題が発生した場合は、以下のようにしてください。\n\n1. `\"use no memo\"` を使用して問題のあるコンポーネントを一時的に除外\n2. 一般的な問題については[デバッグガイド](/learn/react-compiler/debugging)を確認\n3. ESLint プラグインによって特定された React のルール違反を修正\n4. より段階的な導入のために `compilationMode: 'annotation'` の使用を検討\n\n## 次のステップ {/*next-steps*/}\n\n- さまざまなオプションについて[設定ガイド](/reference/react-compiler/configuration)を確認する\n- [デバッグテクニック](/learn/react-compiler/debugging)について学ぶ\n- すべてのコンパイラオプションについては [API リファレンス](/reference/react-compiler/configuration)を確認する"
  },
  {
    "path": "src/content/learn/react-compiler/index.md",
    "content": "---\ntitle: React Compiler\n---\n\n## はじめに {/*introduction*/}\n\n[React Compiler の機能](/learn/react-compiler/introduction)について知り、React アプリケーションを自動的に最適化して `useMemo`、`useCallback`、`React.memo` による手動メモ化を不要にしてくれる仕組みについて理解しましょう。\n\n## インストール {/*installation*/}\n\n[React Compiler のインストール](/learn/react-compiler/installation)を行い、ビルドツールと組み合わせるための設定方法を学びましょう。\n\n\n## 段階的な導入 {/*incremental-adoption*/}\n\n全面的に有効にする準備がまだできていない場合に、既存のコードベースで [React Compiler を段階的に導入する戦略](/learn/react-compiler/incremental-adoption)を学びましょう。\n\n## デバッグとトラブルシューティング {/*debugging-and-troubleshooting*/}\n\n期待通りに動作しない場合は[デバッグガイド](/learn/react-compiler/debugging)を参考に、コンパイラエラーとランタイムエラーの違いやよくある失敗パターンを理解しつつ、体系的なデバッグ作業を行いましょう。\n\n## 設定とリファレンス {/*configuration-and-reference*/}\n\n詳細な設定オプションと API リファレンスについては以下をご覧ください：\n\n- [設定オプション](/reference/react-compiler/configuration) - React バージョンの互換性を含むすべてのコンパイラ設定オプション\n- [ディレクティブ](/reference/react-compiler/directives) - 関数レベルのコンパイル制御\n- [ライブラリのコンパイル](/reference/react-compiler/compiling-libraries) - 事前コンパイルされたライブラリの配布\n\n## 追加情報 {/*additional-resources*/}\n\nこれらのドキュメントに加えて、コンパイラに関する追加情報や議論については [React Compiler Working Group](https://github.com/reactwg/react-compiler) を確認することをお勧めします。\n\n"
  },
  {
    "path": "src/content/learn/react-compiler/installation.md",
    "content": "---\ntitle: インストール\n---\n\n<Intro>\nこのガイドでは、React アプリケーションに React Compiler をインストールし、設定する方法を説明します。\n</Intro>\n\n<YouWillLearn>\n\n* React Compiler のインストール方法\n* さまざまなビルドツールでの基本的な設定\n* セットアップが正常に動作しているかの確認方法\n\n</YouWillLearn>\n\n## 前提条件 {/*prerequisites*/}\n\nReact Compiler は React 19 で最適に動作するよう設計されていますが、React 17 および 18 もサポートしています。詳細については [React バージョン互換性](/reference/react-compiler/target)をご覧ください。\n\n## インストール {/*installation*/}\n\nReact Compiler を `devDependency` としてインストールします。\n\n<TerminalBlock>\nnpm install -D babel-plugin-react-compiler@latest\n</TerminalBlock>\n\nYarn を使用する場合：\n\n<TerminalBlock>\nyarn add -D babel-plugin-react-compiler@latest\n</TerminalBlock>\n\npnpm を使用する場合：\n\n<TerminalBlock>\npnpm install -D babel-plugin-react-compiler@latest\n</TerminalBlock>\n\n## 基本的なセットアップ {/*basic-setup*/}\n\nReact Compiler は、デフォルトで設定なしで動作するように設計されています。ただし、特別な状況で設定が必要な場合（例えば、React 19 未満のバージョンを対象とする場合）は、[コンパイラオプションリファレンス](/reference/react-compiler/configuration)を参照してください。\n\nセットアッププロセスは使用するビルドツールによって異なります。React Compiler には、ビルドパイプラインと統合して動作する Babel プラグインが含まれています。\n\n<Pitfall>\nReact Compiler は Babel プラグインパイプライン内で**最初に**実行される必要があります。コンパイラが適切な解析を行うためにはオリジナルのソース情報が必要なため、他の変換より前に処理する必要があるのです。\n</Pitfall>\n\n### Babel {/*babel*/}\n\n`babel.config.js` を作成または更新します。\n\n```js {3}\nmodule.exports = {\n  plugins: [\n    'babel-plugin-react-compiler', // must run first!\n    // ... other plugins\n  ],\n  // ... other config\n};\n```\n\n### Vite {/*vite*/}\n\nVite を使用している場合は、プラグインを vite-plugin-react に追加できます。\n\n```js {3,9}\n// vite.config.js\nimport { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\nexport default defineConfig({\n  plugins: [\n    react({\n      babel: {\n        plugins: ['babel-plugin-react-compiler'],\n      },\n    }),\n  ],\n});\n```\n\nまたは、Vite 用の Babel プラグインを別に使用したい場合は以下のようにします。\n\n<TerminalBlock>\nnpm install -D vite-plugin-babel\n</TerminalBlock>\n\n```js {2,11}\n// vite.config.js\nimport babel from 'vite-plugin-babel';\nimport { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\nexport default defineConfig({\n  plugins: [\n    react(),\n    babel({\n      babelConfig: {\n        plugins: ['babel-plugin-react-compiler'],\n      },\n    }),\n  ],\n});\n```\n\n### Next.js {/*usage-with-nextjs*/}\n\n詳細については [Next.js ドキュメント](https://nextjs.org/docs/app/api-reference/next-config-js/reactCompiler)を参照してください。\n\n### React Router {/*usage-with-react-router*/}\n`vite-plugin-babel` をインストールし、コンパイラの Babel プラグインを追加します。\n\n<TerminalBlock>\nnpm install vite-plugin-babel\n</TerminalBlock>\n\n```js {3-4,16}\n// vite.config.js\nimport { defineConfig } from \"vite\";\nimport babel from \"vite-plugin-babel\";\nimport { reactRouter } from \"@react-router/dev/vite\";\n\nconst ReactCompilerConfig = { /* ... */ };\n\nexport default defineConfig({\n  plugins: [\n    reactRouter(),\n    babel({\n      filter: /\\.[jt]sx?$/,\n      babelConfig: {\n        presets: [\"@babel/preset-typescript\"], // if you use TypeScript\n        plugins: [\n          [\"babel-plugin-react-compiler\", ReactCompilerConfig],\n        ],\n      },\n    }),\n  ],\n});\n```\n\n### Webpack {/*usage-with-webpack*/}\n\nコミュニティ製の webpack ローダーが[こちら](https://github.com/SukkaW/react-compiler-webpack)で利用できます。\n\n### Expo {/*usage-with-expo*/}\n\nExpo アプリで React Compiler を有効にして使用する方法については、[Expo のドキュメント](https://docs.expo.dev/guides/react-compiler/)を参照してください。\n\n### Metro (React Native) {/*usage-with-react-native-metro*/}\n\nReact Native は Metro 経由で Babel を使用するため、インストール手順については [Babel での使用](#babel)セクションを参照してください。\n\n### Rspack {/*usage-with-rspack*/}\n\nRspack アプリで React Compiler を有効にして使用する方法については、[Rspack のドキュメント](https://rspack.dev/guide/tech/react#react-compiler)を参照してください。\n\n### Rsbuild {/*usage-with-rsbuild*/}\n\nRsbuild アプリで React Compiler を有効にして使用する方法については、[Rsbuild のドキュメント](https://rsbuild.dev/guide/framework/react#react-compiler)を参照してください。\n\n\n## ESLint 統合 {/*eslint-integration*/}\n\nReact Compiler には、最適化できないコードを特定するのに役立つ ESLint ルールが含まれています。ESLint ルールがエラーを報告する場合、コンパイラによるそのコンポーネントやフックの最適化がスキップされるという意味です。これは安全です。コンパイラはコードベースの他の部分の最適化を続けるので、すべての違反をすぐに修正する必要はありません。自分のペースで対処し、最適化されるコンポーネントの数を徐々に増やしていってください。\n\nESLint プラグインをインストールします。\n\n<TerminalBlock>\nnpm install -D eslint-plugin-react-hooks@latest\n</TerminalBlock>\n\n`eslint-plugin-react-hooks` をまだ設定していない場合は、[readme のインストール手順](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/README.md#installation)に従ってください。コンパイラのルールは `recommended-latest` プリセットで利用できます。\n\nESLint ルールは以下を行います。\n- [React のルール](/reference/rules)の違反の特定\n- 最適化できないコンポーネントの表示\n- 問題の修正に役立つエラーメッセージの提供\n\n## セットアップの確認 {/*verify-your-setup*/}\n\nインストール後、React Compiler が正常に動作していることを確認します。\n\n### React DevTools による確認 {/*check-react-devtools*/}\n\nReact Compiler によって最適化されたコンポーネントは、React DevTools で \"Memo ✨\" バッジが表示されます。\n\n1. [React Developer Tools](/learn/react-developer-tools) ブラウザ拡張機能をインストール\n2. 開発モードでアプリを開く\n3. React DevTools を開く\n4. コンポーネント名の横にある ✨ 絵文字を探す\n\nコンパイラが動作している場合\n- コンポーネントは React DevTools で \"Memo ✨\" バッジを表示\n- 高コストな計算が自動的にメモ化される\n- 手動の `useMemo` は不要\n\n### ビルド出力の確認 {/*check-build-output*/}\n\nまた、ビルド出力を確認することでもコンパイラが動作していることを確認できます。コンパイルされたコードには、コンパイラが自動的に追加する自動メモ化ロジックが含まれています。\n\n```js\nimport { c as _c } from \"react/compiler-runtime\";\nexport default function MyApp() {\n  const $ = _c(1);\n  let t0;\n  if ($[0] === Symbol.for(\"react.memo_cache_sentinel\")) {\n    t0 = <div>Hello World</div>;\n    $[0] = t0;\n  } else {\n    t0 = $[0];\n  }\n  return t0;\n}\n\n```\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### 特定のコンポーネントの除外 {/*opting-out-specific-components*/}\n\nコンポーネントがコンパイル後に問題を引き起こしている場合、`\"use no memo\"` ディレクティブを使用して一時的に除外できます。\n\n```js\nfunction ProblematicComponent() {\n  \"use no memo\";\n  // Component code here\n}\n```\n\nこれにより、コンパイラはこの特定のコンポーネントの最適化をスキップします。根本的な問題を修正し、解決したらディレクティブを削除してください。\n\nトラブルシューティングの詳細については、[デバッグガイド](/learn/react-compiler/debugging)を参照してください。\n\n## 次のステップ {/*next-steps*/}\n\nReact Compiler がインストールされたので、以下について詳しく学びましょう。\n\n- React 17 と 18 の [React バージョン互換性](/reference/react-compiler/target)\n- コンパイラをカスタマイズする[設定オプション](/reference/react-compiler/configuration)\n- 既存のコードベースでの[段階的な導入戦略](/learn/react-compiler/incremental-adoption)\n- 問題のトラブルシューティングのための[デバッグテクニック](/learn/react-compiler/debugging)\n- React ライブラリをコンパイルするための[ライブラリコンパイルガイド](/reference/react-compiler/compiling-libraries)"
  },
  {
    "path": "src/content/learn/react-compiler/introduction.md",
    "content": "---\ntitle: はじめに\n---\n\n<Intro>\nReact Compiler は、React アプリを自動的に最適化する新しいビルド時ツールです。プレーンな JavaScript で動作し、[React のルール](/reference/rules)を理解しているため、コードを書き直すことなく使用できます。\n</Intro>\n\n<YouWillLearn>\n\n* React Compiler の機能\n* React Compiler の導入方法\n* 段階的な導入戦略\n* 問題が発生した際のデバッグとトラブルシューティング\n* React ライブラリでのコンパイラの使用方法\n\n</YouWillLearn>\n\n## React Compiler の機能 {/*what-does-react-compiler-do*/}\n\nReact Compiler は、ビルド時に React アプリケーションを自動的に最適化します。React は最適化なしでも大抵は十分に高速ですが、アプリの応答性を保つために、コンポーネントや値を手動でメモ化する必要がある場合があります。このようなメモ化は面倒で、間違いやすく、メンテナンスすべきコード量を増加させます。React Compiler はこの最適化を自動的に行って認知負荷を軽減することで、開発者が機能開発に集中できるようにしてくれます。\n\n### React Compiler を使用しない場合 {/*before-react-compiler*/}\n\nコンパイラを使用しない場合、再レンダーを最適化するためにコンポーネントや値を手作業でメモ化する必要があります。\n\n```js\nimport { useMemo, useCallback, memo } from 'react';\n\nconst ExpensiveComponent = memo(function ExpensiveComponent({ data, onClick }) {\n  const processedData = useMemo(() => {\n    return expensiveProcessing(data);\n  }, [data]);\n\n  const handleClick = useCallback((item) => {\n    onClick(item.id);\n  }, [onClick]);\n\n  return (\n    <div>\n      {processedData.map(item => (\n        <Item key={item.id} onClick={() => handleClick(item)} />\n      ))}\n    </div>\n  );\n});\n```\n\n\n<Note>\n\nこの手動でのメモ化には、メモ化を破壊してしまう気づきづらいバグがあります。\n\n```js [[2, 1, \"() => handleClick(item)\"]]\n<Item key={item.id} onClick={() => handleClick(item)} />\n```\n\n`handleClick` は `useCallback` でラップされていますが、アロー関数 `() => handleClick(item)` はコンポーネントがレンダーされるたびに新しい関数を作成します。つまり `Item` は常に新しい `onClick` プロパティを受け取っていることになり、メモ化が動作しなくなります。\n\nReact Compiler は、アロー関数の使用の有無にかかわらず、これを正しく最適化でき、`props.onClick` が変更されたときにのみ `Item` が再レンダーされることを保証します。\n\n</Note>\n\n### React Compiler を使用する場合 {/*after-react-compiler*/}\n\nReact Compiler を使うことで、手動によるメモ化なしで同じコードを書くことができます。\n\n```js\nfunction ExpensiveComponent({ data, onClick }) {\n  const processedData = expensiveProcessing(data);\n\n  const handleClick = (item) => {\n    onClick(item.id);\n  };\n\n  return (\n    <div>\n      {processedData.map(item => (\n        <Item key={item.id} onClick={() => handleClick(item)} />\n      ))}\n    </div>\n  );\n}\n```\n\n*[React Compiler Playground でこの例を確認](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAMygOzgFwJYSYAEAogB4AOCmYeAbggMIQC2Fh1OAFMEQCYBDHAIA0RQowA2eOAGsiAXwCURYAB1iROITA4iFGBERgwCPgBEhAogF4iCStVoMACoeO1MAcy6DhSgG4NDSItHT0ACwFMPkkmaTlbIi48HAQWFRsAPlUQ0PFMKRlZFLSWADo8PkC8hSDMPJgEHFhiLjzQgB4+eiyO-OADIwQTM0thcpYBClL02xz2zXz8zoBJMqJZBABPG2BU9Mq+BQKiuT2uTJyomLizkoOMk4B6PqX8pSUFfs7nnro3qEapgFCAFEA)*\n\nReact Compiler は最適なメモ化を自動で適用し、アプリが必要なときだけ再レンダーされるようにします。\n\n<DeepDive>\n#### React Compiler はどのようなメモ化を行うのか？ {/*what-kind-of-memoization-does-react-compiler-add*/}\n\nReact Compiler の自動メモ化は主に**更新パフォーマンスの向上**（既存コンポーネントの再レンダー）に焦点を当てており、主に以下の 2 つのユースケースに重点を置いています。\n\n1. **コンポーネントの連鎖的な再レンダーのスキップ**\n    * `<Parent />` の再レンダーにより、`<Parent />` のみが変更されたにも関わらず、そのコンポーネントツリー内の多くのコンポーネントが再レンダーされる\n1. **React の外で行われる高コストな計算のスキップ**\n    * 例えば、コンポーネントやフック内で `expensivelyProcessAReallyLargeArrayOfObjects()` を呼び出す場合\n\n#### 再レンダーの最適化 {/*optimizing-re-renders*/}\n\nReact では、UI を現在の状態（具体的には props、state、context）の関数として表現できます。現在の実装では、コンポーネントの状態が変更されると、`useMemo()`、`useCallback()`、`React.memo()` による何らかのメモ化を適用していない限り、React はそのコンポーネントに加えて*そのすべての子要素*を再レンダーします。例えば、以下の例では、`<FriendList>` の状態が変更されるたびに `<MessageButton>` が再レンダーされます。\n\n```javascript\nfunction FriendList({ friends }) {\n  const onlineCount = useFriendOnlineCount();\n  if (friends.length === 0) {\n    return <NoFriends />;\n  }\n  return (\n    <div>\n      <span>{onlineCount} online</span>\n      {friends.map((friend) => (\n        <FriendListCard key={friend.id} friend={friend} />\n      ))}\n      <MessageButton />\n    </div>\n  );\n}\n```\n[*React Compiler Playground でこの例を確認*](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAMygOzgFwJYSYAEAYjHgpgCYAyeYOAFMEWuZVWEQL4CURwADrEicQgyKEANnkwIAwtEw4iAXiJQwCMhWoB5TDLmKsTXgG5hRInjRFGbXZwB0UygHMcACzWr1ABn4hEWsYBBxYYgAeADkIHQ4uAHoAPksRbisiMIiYYkYs6yiqPAA3FMLrIiiwAAcAQ0wU4GlZBSUcbklDNqikusaKkKrgR0TnAFt62sYHdmp+VRT7SqrqhOo6Bnl6mCoiAGsEAE9VUfmqZzwqLrHqM7ubolTVol5eTOGigFkEMDB6u4EAAhKA4HCEZ5DNZ9ErlLIWYTcEDcIA)\n\nReact Compiler はメモ化と同等の処理を自動的に適用し、状態が変更されてもアプリの関連部分のみが再レンダーされることを保証します。これは細粒度のリアクティビティ (fine-grained reactivity) と呼ばれることもあります。上記の例では、React Compiler は `friends` が変更されても個々の `<FriendListCard />` の返り値を再利用できると判断し、この JSX の再作成 *と* カウントの変更による `<MessageButton>` の再レンダーを回避できます。\n\n#### 高コストな計算もメモ化される {/*expensive-calculations-also-get-memoized*/}\n\nReact Compiler は、レンダー中に使用される高コストな計算も自動的にメモ化できます。\n\n```js\n// **Not** memoized by React Compiler, since this is not a component or hook\nfunction expensivelyProcessAReallyLargeArrayOfObjects() { /* ... */ }\n\n// Memoized by React Compiler since this is a component\nfunction TableContainer({ items }) {\n  // This function call would be memoized:\n  const data = expensivelyProcessAReallyLargeArrayOfObjects(items);\n  // ...\n}\n```\n[*React Compiler Playground でこの例を確認*](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAejQAgFTYHIQAuumAtgqRAJYBeCAJpgEYCemASggIZyGYDCEUgAcqAGwQwANJjBUAdokyEAFlTCZ1meUUxdMcIcIjyE8vhBiYVECAGsAOvIBmURYSonMCAB7CzcgBuCGIsAAowEIhgYACCnFxioQAyXDAA5gixMDBcLADyzvlMAFYIvGAAFACUmMCYaNiYAHStOFgAvk5OGJgAshTUdIysHNy8AkbikrIKSqpaWvqGIiZmhE6u7p7ymAAqXEwSguZcCpKV9VSEFBodtcBOmAYmYHz0XIT6ALzefgFUYKhCJRBAxeLcJIsVIZLI5PKFYplCqVa63aoAbm6u0wMAQhFguwAPPRAQA+YAfL4dIloUmBMlODogDpAA)\n\nただし、`expensivelyProcessAReallyLargeArrayOfObjects` が本当に高コストな関数である場合は、React 外で独自のメモ化を実装することを検討することをお勧めします。理由は以下の通りです。\n\n- React Compiler は React コンポーネントとフックのみをメモ化し、すべての関数をメモ化するわけではない\n- React Compiler のメモ化は複数のコンポーネントやフック間で共有されない\n\nそのため、`expensivelyProcessAReallyLargeArrayOfObjects` が多くの異なるコンポーネントで使用される場合、まったく同じ入力が渡されたとしても、その高コストな計算が繰り返し実行されてしまいます。コードをより複雑にする前に、[プロファイリング](reference/react/useMemo#how-to-tell-if-a-calculation-is-expensive)を行って、本当にそれほど高コストかどうかを確認することをお勧めします。\n</DeepDive>\n\n## コンパイラを試すべきか？ {/*should-i-try-out-the-compiler*/}\n\nすべての方に React Compiler の使用を開始することをお勧めします。コンパイラは現在は React の任意機能ですが、将来的には一部の機能を完全に動作させるためにコンパイラが必要になる可能性があります。\n\n### 安全に使用できるか？ {/*is-it-safe-to-use*/}\n\nReact Compiler は現在安定版であり、本番環境で広範囲にテストされています。Meta などの企業で本番環境で使用されていますが、あなたのアプリケーションでコンパイラを導入できるかどうかは、コードベースの健全性と [React のルール](/reference/rules)をどの程度遵守しているかに依存します。\n\n## どのビルドツールがサポートされているか？ {/*what-build-tools-are-supported*/}\n\nReact Compiler は[複数のビルドツール](/learn/react-compiler/installation)で利用できます。具体的には Babel、Vite、Metro、Rsbuild などが含まれます。\n\nReact Compiler は本質的に、コアコンパイラを軽量な Babel プラグインでラップしたものです。コアコンパイラは Babel 自体から分離して設計されています。コンパイラの最初の安定版は主に Babel プラグインとなりますが、swc と [oxc](https://github.com/oxc-project/oxc/issues/10048) チームと協力して、React Compiler のファーストクラスサポートを構築しており、将来的にはビルドパイプラインに Babel を再追加する必要がなくなる予定です。\n\nNext.js を使用しているユーザは、[v15.3.1](https://github.com/vercel/next.js/releases/tag/v15.3.1) 以降のバージョンを利用することで、SWC 経由での React Compiler の利用を有効化できます。\n\n## useMemo、useCallback、React.memo をどう扱うべきか？ {/*what-should-i-do-about-usememo-usecallback-and-reactmemo*/}\n\nデフォルトでは、React Compiler はコードの分析結果とヒューリスティックに基づいてコードをメモ化します。ほとんどの場合このメモ化は、あなたが書くであろうものと同等か、それ以上に正確です。\n\nただし、場合によっては開発者がメモ化をより細かく制御する必要があるかもしれません。`useMemo` と `useCallback` フックは、React Compiler と併用して、どの値をメモ化するかを制御するための避難ハッチ (escape hatch) として使用し続けることができます。一般的なユースケースは、メモ化された値がエフェクトの依存値として使用される場合で、依存値が実質的に変化しないならエフェクトが繰り返し発火しないようにする、というものです。\n\n新しいコードにおいては、メモ化をコンパイラに任せ、詳細な制御が必要な場合に `useMemo`/`useCallback` を使用することをお勧めします。\n\n既存のコードの場合は、既存のメモ化をそのまま残すか（削除するとコンパイル出力が変わる可能性があります）、メモ化を削除する前に慎重にテストすることをお勧めします。\n\n## React Compiler を試す {/*try-react-compiler*/}\n\nこのセクションでは、React Compiler の始め方と、プロジェクトで効果的に使用するための情報を提供します。\n\n* **[インストール](/learn/react-compiler/installation)** - React Compiler をインストールし、ビルドツール用に設定する\n* **[React バージョン互換性](/reference/react-compiler/target)** - React 17、18、19 のサポート\n* **[設定](/reference/react-compiler/configuration)** - 特定のニーズに合わせてコンパイラをカスタマイズする\n* **[段階的な導入](/learn/react-compiler/incremental-adoption)** - 既存のコードベースでコンパイラを段階的に展開する戦略\n* **[デバッグとトラブルシューティング](/learn/react-compiler/debugging)** - コンパイラ使用時の問題の特定と修正\n* **[ライブラリのコンパイル](/reference/react-compiler/compiling-libraries)** - コンパイルされたコードを配布するためのベストプラクティス\n* **[API リファレンス](/reference/react-compiler/configuration)** - すべての設定オプションの詳細ドキュメント\n\n## 追加情報 {/*additional-resources*/}\n\nこれらのドキュメントに加えて、コンパイラに関する追加情報や議論については [React Compiler Working Group](https://github.com/reactwg/react-compiler) を確認することをお勧めします。\n"
  },
  {
    "path": "src/content/learn/react-developer-tools.md",
    "content": "---\ntitle: React Developer Tools\n---\n\n<Intro>\n\nReact Developer Tools を使うことで、React の[コンポーネント](/learn/your-first-component)を調査し、[props](/learn/passing-props-to-a-component) や [state](/learn/state-a-components-memory) を編集し、パフォーマンスの問題を特定できます。\n\n</Intro>\n\n<YouWillLearn>\n\n* React Developer Tools をインストールする方法\n\n</YouWillLearn>\n\n## ブラウザ拡張機能 {/*browser-extension*/}\n\nReact を使ったウェブサイトをデバッグする最も簡単な方法は、React Developer Tools というブラウザ拡張機能をインストールすることです。これは複数の人気のブラウザで利用可能です：\n\n* [**Chrome** 用にインストール](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en)\n* [**Firefox** 用にインストール](https://addons.mozilla.org/en-US/firefox/addon/react-devtools/)\n* [**Edge** 用にインストール](https://microsoftedge.microsoft.com/addons/detail/react-developer-tools/gpphkfbcpidddadnkolkpfckpihlkkil)\n\nこれで、**React で構築されたウェブサイト**を訪れると、_Components_ と _Profiler_ パネルが表示されるようになります。\n\n![React Developer Tools エクステンション](/images/docs/react-devtools-extension.png)\n\n### Safari および他のブラウザ {/*safari-and-other-browsers*/}\n他のブラウザ（例えば Safari）の場合、[`react-devtools`](https://www.npmjs.com/package/react-devtools) の npm パッケージをインストールします：\n```bash\n# Yarn\nyarn global add react-devtools\n\n# Npm\nnpm install -g react-devtools\n```\n\n次に、ターミナルから開発者ツールを開きます：\n```bash\nreact-devtools\n```\n\nそして、ウェブサイトの `<head>` の先頭に以下の `<script>` タグを追加して、ウェブサイトを接続します：\n```html {3}\n<html>\n  <head>\n    <script src=\"http://localhost:8097\"></script>\n```\n\nここでブラウザでウェブサイトをリロードし、開発者ツールで表示できるようにしてください。\n\n![スタンドアロン版 React Developer Tools](/images/docs/react-devtools-standalone.png)\n\n## モバイル (React Native) {/*mobile-react-native*/}\n\n[React Native](https://reactnative.dev/) で作成するアプリの調査を行う場合は、React Developer Tools と密に統合された組み込みデバッガである [React Native DevTools](https://reactnative.dev/docs/react-native-devtools) を使用できます。要素のハイライトや選択を含むすべての機能が、ブラウザ版の機能拡張と同様に動作します。\n\n[React Native のデバッグについてさらに読む](https://reactnative.dev/docs/debugging)\n\n> 0.76 より前のバージョンの React Native の場合は、上記の [Safari および他のブラウザ](#safari-and-other-browsers)のガイドに従ってスタンドアロン版の React DevTools を使用してください。"
  },
  {
    "path": "src/content/learn/reacting-to-input-with-state.md",
    "content": "---\ntitle: state を使って入力に反応する\n---\n\n<Intro>\n\nReact は UI を操作するための宣言的な方法を提供します。UI の個々の部分を直接操作するのではなく、コンポーネントが取りうる異なる状態を記述し、ユーザの入力に応じてそれらの状態を切り替えます。これは、デザイナが UI について考える方法に似ています。\n\n</Intro>\n\n<YouWillLearn>\n\n* 宣言型 UI プログラミングと命令型 UI プログラミングの違い\n* コンポーネントが持ちうる様々な視覚状態を列挙する方法\n* 異なる視覚状態間の変更をコードからトリガする方法\n\n</YouWillLearn>\n\n## 宣言型 UI と命令型 UI の比較 {/*how-declarative-ui-compares-to-imperative*/}\n\nインタラクティブな UI を設計する際、ユーザのアクションに応じて UI がどのように*変化する*かを考えることが多いでしょう。たとえば、ユーザが回答を送信できるフォームを考えてみましょう。\n\n* フォームに何かを入力すると、\"Submit\" ボタンが**有効になる**。\n* \"Submit\" ボタンを押すと、フォームとボタンが**無効になり**、スピナが**表示される**。\n* ネットワークリクエストが成功すると、フォームは**非表示になり**、お礼メッセージが**表示される**。\n* ネットワークリクエストに失敗した場合、エラーメッセージが**表示され**、フォームが再び**有効になる**。\n\n**命令型プログラミング**では、上記の変化がそのまま UI とのインタラクションの実装法に対応します。起こったことに応じて UI を操作するための命令そのものを書かなければならないのです。別の考え方をしてみましょう：車の中で隣に乗っている人に、曲がるたびに行き先を指示することを想像してみてください。\n\n<Illustration src=\"/images/docs/illustrations/i_imperative-ui-programming.png\"  alt=\"In a car driven by an anxious-looking person representing JavaScript, a passenger orders the driver to execute a sequence of complicated turn by turn navigations.\" />\n\n運転手はあなたがどこに行きたいのか知らず、ただあなたの指示に従うだけです。（そして、あなたの方向指示が間違っていたら、間違った場所に着いてしまいます！）これは*命令型*と呼ばれます。なぜなら、スピナからボタンに至るまで、個々の要素に対して直接命令し、コンピュータに*どのように* UI を更新するのか指示しているからです。\n\n以下の命令型 UI プログラミングの例では、フォームは React を*使わずに*作成されています。ブラウザの [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) だけを利用しています。\n\n<Sandpack>\n\n```js src/index.js active\nasync function handleFormSubmit(e) {\n  e.preventDefault();\n  disable(textarea);\n  disable(button);\n  show(loadingMessage);\n  hide(errorMessage);\n  try {\n    await submitForm(textarea.value);\n    show(successMessage);\n    hide(form);\n  } catch (err) {\n    show(errorMessage);\n    errorMessage.textContent = err.message;\n  } finally {\n    hide(loadingMessage);\n    enable(textarea);\n    enable(button);\n  }\n}\n\nfunction handleTextareaChange() {\n  if (textarea.value.length === 0) {\n    disable(button);\n  } else {\n    enable(button);\n  }\n}\n\nfunction hide(el) {\n  el.style.display = 'none';\n}\n\nfunction show(el) {\n  el.style.display = '';\n}\n\nfunction enable(el) {\n  el.disabled = false;\n}\n\nfunction disable(el) {\n  el.disabled = true;\n}\n\nfunction submitForm(answer) {\n  // Pretend it's hitting the network.\n  return new Promise((resolve, reject) => {\n    setTimeout(() => {\n      if (answer.toLowerCase() === 'istanbul') {\n        resolve();\n      } else {\n        reject(new Error('Good guess but a wrong answer. Try again!'));\n      }\n    }, 1500);\n  });\n}\n\nlet form = document.getElementById('form');\nlet textarea = document.getElementById('textarea');\nlet button = document.getElementById('button');\nlet loadingMessage = document.getElementById('loading');\nlet errorMessage = document.getElementById('error');\nlet successMessage = document.getElementById('success');\nform.onsubmit = handleFormSubmit;\ntextarea.oninput = handleTextareaChange;\n```\n\n```js sandbox.config.json hidden\n{\n  \"hardReloadOnChange\": true\n}\n```\n\n```html public/index.html\n<form id=\"form\">\n  <h2>City quiz</h2>\n  <p>\n    What city is located on two continents?\n  </p>\n  <textarea id=\"textarea\"></textarea>\n  <br />\n  <button id=\"button\" disabled>Submit</button>\n  <p id=\"loading\" style=\"display: none\">Loading...</p>\n  <p id=\"error\" style=\"display: none; color: red;\"></p>\n</form>\n<h1 id=\"success\" style=\"display: none\">That's right!</h1>\n\n<style>\n* { box-sizing: border-box; }\nbody { font-family: sans-serif; margin: 20px; padding: 0; }\n</style>\n```\n\n</Sandpack>\n\nUI を命令的に操作することは、小さなサンプルではうまくいくかもしれませんが、より複雑なシステムでは指数関数的に難しくなります。例えばこのような様々なフォームでいっぱいのページを更新することを想像してみてください。新しい UI 要素や新しい操作方法を追加する場合、既存のすべてのコードを注意深くチェックして、バグ（例えば、何かを表示または非表示にすることを忘れていないか）を確認する必要があります。\n\nReact はこの問題を解決するために作られました。\n\nReact では、あなたが UI を直接操作することはありません。つまり、コンポーネントの有効化、無効化、表示、非表示を直接行うことはありません。代わりに、**表示したいものを宣言する**ことで、React が UI を更新する方法を考えてくれるのです。タクシーに乗ったとき、どこで曲がるかを正確に伝えるのではなく、どこに行きたいかを運転手に伝えることを思い浮かべてください。運転手はあなたをそこに連れていくのが仕事ですし、あなたが考えもしなかった近道も知っているかもしれません！\n\n<Illustration src=\"/images/docs/illustrations/i_declarative-ui-programming.png\" alt=\"In a car driven by React, a passenger asks to be taken to a specific place on the map. React figures out how to do that.\" />\n\n## UI を宣言的に考える {/*thinking-about-ui-declaratively*/}\n\n上記では、フォームを命令的に実装する方法を見てきました。React 的な思考法をより理解するために、以下でこの UI を React で再実装する方法を確認していきます。\n\n1. コンポーネントの様々な視覚状態を**特定**する\n2. それらの状態変更を引き起こすトリガを**決定**する\n3. `useState` を使用してメモリ上に state を**表現**する\n4. 必要不可欠でない state 変数をすべて**削除**する\n5. イベントハンドラを**接続**して state を設定する\n\n### Step 1: コンポーネントの様々な視覚状態を特定する {/*step-1-identify-your-components-different-visual-states*/}\n\nコンピュータサイエンス用語で、複数の「状態」間を行き来する仕組みである[「ステートマシン」](https://en.wikipedia.org/wiki/Finite-state_machine) というものを聞いたことがあるかもしれません。あるいはデザイナと一緒に仕事をしていて、さまざまな「視覚状態」のモックアップを見たことがあるかもしれません。React はデザインとコンピュータサイエンスの交点に位置しているため、これら両方のアイデアがインスピレーションの源になります。\n\nまず、ユーザが目にする可能性のある UI の様々な「状態」をすべて可視化する必要があります。\n\n* **Empty**：フォームには無効な \"Submit\" ボタンがある。\n* **Typing**：フォームには有効な \"Submit\" ボタンがある。\n* **Submitting**：フォームは完全に無効化される。スピナが表示される。\n* **Success**：フォームの代わりにお礼のメッセージが表示される。\n* **Error**：Typing 状態と同様だがエラーメッセージも表示される。\n\nデザイナのように、ロジックを追加する前に様々な状態の「モックアップ」を作成することをお勧めします。例えば、フォームの表示部分だけのモックを以下に示します。このモックはデフォルト値が `'empty'` の `status` という props によって制御されます。\n\n<Sandpack>\n\n```js\nexport default function Form({\n  status = 'empty'\n}) {\n  if (status === 'success') {\n    return <h1>That's right!</h1>\n  }\n  return (\n    <>\n      <h2>City quiz</h2>\n      <p>\n        In which city is there a billboard that turns air into drinkable water?\n      </p>\n      <form>\n        <textarea />\n        <br />\n        <button>\n          Submit\n        </button>\n      </form>\n    </>\n  )\n}\n```\n\n</Sandpack>\n\nその props の名前は何でも構いません。命名は重要ではありません。`status = 'empty'` を `status = 'success'` に編集して、正解というメッセージが表示されるのを確認してみてください。モックアップを使うことで、ロジックを結びつける前に、各 UI の状態を素早く確認することができます。上記のコンポーネントにもう少し肉付けしたプロトタイプを以下に示しますが、依然 `status` プロパティによって「制御」されています。\n\n<Sandpack>\n\n```js\nexport default function Form({\n  // Try 'submitting', 'error', 'success':\n  status = 'empty'\n}) {\n  if (status === 'success') {\n    return <h1>That's right!</h1>\n  }\n  return (\n    <>\n      <h2>City quiz</h2>\n      <p>\n        In which city is there a billboard that turns air into drinkable water?\n      </p>\n      <form>\n        <textarea disabled={\n          status === 'submitting'\n        } />\n        <br />\n        <button disabled={\n          status === 'empty' ||\n          status === 'submitting'\n        }>\n          Submit\n        </button>\n        {status === 'error' &&\n          <p className=\"Error\">\n            Good guess but a wrong answer. Try again!\n          </p>\n        }\n      </form>\n      </>\n  );\n}\n```\n\n```css\n.Error { color: red; }\n```\n\n</Sandpack>\n\n<DeepDive>\n\n#### 多くの視覚状態を一度に表示する {/*displaying-many-visual-states-at-once*/}\n\nコンポーネントが多くの視覚状態を持つ場合、それらをすべて 1 つのページに表示することが便利な場合があります。\n\n<Sandpack>\n\n```js src/App.js active\nimport Form from './Form.js';\n\nlet statuses = [\n  'empty',\n  'typing',\n  'submitting',\n  'success',\n  'error',\n];\n\nexport default function App() {\n  return (\n    <>\n      {statuses.map(status => (\n        <section key={status}>\n          <h4>Form ({status}):</h4>\n          <Form status={status} />\n        </section>\n      ))}\n    </>\n  );\n}\n```\n\n```js src/Form.js\nexport default function Form({ status }) {\n  if (status === 'success') {\n    return <h1>That's right!</h1>\n  }\n  return (\n    <form>\n      <textarea disabled={\n        status === 'submitting'\n      } />\n      <br />\n      <button disabled={\n        status === 'empty' ||\n        status === 'submitting'\n      }>\n        Submit\n      </button>\n      {status === 'error' &&\n        <p className=\"Error\">\n          Good guess but a wrong answer. Try again!\n        </p>\n      }\n    </form>\n  );\n}\n```\n\n```css\nsection { border-bottom: 1px solid #aaa; padding: 20px; }\nh4 { color: #222; }\nbody { margin: 0; }\n.Error { color: red; }\n```\n\n</Sandpack>\n\nこのようなページはしばしば \"living styleguide\" あるいは \"storybook\" と呼ばれます。\n\n</DeepDive>\n\n### Step 2: それらの状態変更を引き起こすトリガを決定する {/*step-2-determine-what-triggers-those-state-changes*/}\n\n以下の 2 種類の入力に応答して、状態の更新をトリガすることができます。\n\n* **人間からの入力**：例えばボタンをクリックする、フィールドに入力する、リンクをナビゲートするなど。\n* **コンピュータからの入力**：例えばネットワークからのレスポンスが到着する、タイムアウトが起きる、画像が読み込まれるなど。\n\n<IllustrationBlock>\n  <Illustration caption=\"人間からの入力\" alt=\"A finger.\" src=\"/images/docs/illustrations/i_inputs1.png\" />\n  <Illustration caption=\"コンピュータからの入力\" alt=\"Ones and zeroes.\" src=\"/images/docs/illustrations/i_inputs2.png\" />\n</IllustrationBlock>\n\nいずれの場合も、**UI を更新するためには [state 変数](/learn/state-a-components-memory#anatomy-of-usestate)を設定する必要があります**。今回開発するフォームでは、いくつかの異なる入力に反応して状態を変更する必要があります。\n\n* **テキスト入力フィールドの編集**（人間）により、テキストボックスが空かどうかによって、*Empty* 状態と *Typing* 状態を切り替える。\n* **送信ボタンのクリック**（人間）により、*Submitting* 状態に切り替える。\n* **ネットワーク応答の成功**（コンピュータ）により、*Success* 状態に切り替える。\n* **ネットワーク応答の失敗**（コンピュータ）により、対応するエラーメッセージと共に *Error* 状態に切り替える。\n\n<Note>\n\n人間からの入力は、しばしば[イベントハンドラ](/learn/responding-to-events)を必要とすることに注意してください！\n\n</Note>\n\nこのフローを視覚化するために、各状態を丸で囲んで紙に描き、2 つの状態間の変化を矢印として描くことを試してみてください。このようにして多くのフローを描き出すことで、実装のはるか前にバグを減らすことができます。\n\n<DiagramGroup>\n\n<Diagram name=\"responding_to_input_flow\" height={350} width={688} alt=\"Flow chart moving left to right with 5 nodes. The first node labeled 'empty' has one edge labeled 'start typing' connected to a node labeled 'typing'. That node has one edge labeled 'press submit' connected to a node labeled 'submitting', which has two edges. The left edge is labeled 'network error' connecting to a node labeled 'error'. The right edge is labeled 'network success' connecting to a node labeled 'success'.\">\n\nForm states\n\n</Diagram>\n\n</DiagramGroup>\n\n### Step 3: `useState` を使用してメモリ上に state を表現する {/*step-3-represent-the-state-in-memory-with-usestate*/}\n\n次に、[`useState`](/reference/react/useState) を使用してコンポーネントの視覚状態をメモリ内で表現する必要があります。シンプルさが鍵です。各 state は「動くパーツ」であり、**可能な限り「動くパーツ」を少なくすることが望ましいです**。複雑さが増すとバグも増えます！\n\nまず*絶対に必要な* state から始めます。例えば、入力中の回答である `answer` を保存する必要があり、最後に起きたエラー（あれば）を保存するために `error` が必要です。\n\n```js\nconst [answer, setAnswer] = useState('');\nconst [error, setError] = useState(null);\n```\n\nそして、どの視覚状態を表示させるかを表す state 変数が必要になります。通常、メモリ上でそれを表現する方法は 1 つではないので、実験してみる必要があります。\n\nもし、すぐにベストな方法が思い浮かばない場合は、まず、考えられるすべての視覚状態を*確実*にカバーできる十分な数の state を追加することから始めてください。\n\n```js\nconst [isEmpty, setIsEmpty] = useState(true);\nconst [isTyping, setIsTyping] = useState(false);\nconst [isSubmitting, setIsSubmitting] = useState(false);\nconst [isSuccess, setIsSuccess] = useState(false);\nconst [isError, setIsError] = useState(false);\n```\n\n最初のアイデアがベストでない可能性もありますが、それはそれで OK です。state のリファクタリングはプロセスの一部です！\n\n### Step 4: 必要不可欠でない state 変数をすべて削除する {/*step-4-remove-any-non-essential-state-variables*/}\n\nstate の内容に重複がないようにし、本当に必要なものだけを管理するようにしたいです。state の構造をリファクタリングすることに少し時間をかけることで、コンポーネントが理解しやすくなり、重複が減り、想定外の意味を持つことがなくなります。目標は、**メモリ上の state がユーザに見せたい有効な UI を表現しないという状況を防ぐことです**。（例えば、エラーメッセージを表示すると同時に入力を無効化するようなことはあってはいけません。ユーザがエラーを修正できなくなってしまいます！）\n\n自身の state 変数に関して以下のように自問してみるとよいでしょう。\n\n* **この state で矛盾は生じないか？** 例えば、`isTyping` と `isSubmitting` の両方が `true` となることはありえません。矛盾がある state とは通常、state の制約が十分でないことを意味します。2 つのブール値の組み合わせは 4 通りありますが、有効な状態に対応するのは 3 つだけです。このような「ありえない」state を削除するためには、これらをまとめて、`typing`、`submitting`、または `success` の 3 つの値のうちどれかでなければならない `status` という 1 つの state にすればよいでしょう。\n* **同じ情報が別の state 変数から入手できないか？** もうひとつの矛盾の原因は、`isEmpty` と `isTyping` が同時に `true` にならないことです。これらを別々の state 変数にすることで、同期がとれなくなり、バグが発生する危険性があります。幸い、`isEmpty` を削除して、代わりに `answer.length === 0` をチェックすることができます。\n* **別の state 変数の逆を取って同じ情報を得られないか？** `isError` は不要です。なぜなら代わりに `error !== null` をチェックできるからです。\n\nこの削減後、3 つ（7 つから減りました！）の*必須* state 変数が残ります。\n\n```js\nconst [answer, setAnswer] = useState('');\nconst [error, setError] = useState(null);\nconst [status, setStatus] = useState('typing'); // 'typing', 'submitting', or 'success'\n```\n\n機能を壊さずにどれかを外すことはできないので、必要不可欠なものであることがわかります。\n\n<DeepDive>\n\n#### リデューサを用いて「ありえない」state を解消する {/*eliminating-impossible-states-with-a-reducer*/}\n\nこの 3 つの変数は、このフォームの状態を十分に表現しています。しかし、あまり意味をなさない中途半端な状態がまだ存在します。例えば、`status` が `success` の場合、`error` が null 以外になることは意味をなしません。state をより正確にモデル化するには、[リデューサに抽出することができます](/learn/extracting-state-logic-into-a-reducer)。リデューサを使えば、複数の state 変数を 1 つのオブジェクトに統一し、関連するロジックをすべて統合することができます！\n\n</DeepDive>\n\n### Step 5: イベントハンドラを接続して state を設定する {/*step-5-connect-the-event-handlers-to-set-state*/}\n\n最後に、state を更新するイベントハンドラを作成します。以下に、すべてのイベントハンドラが接続された最終的なフォームを示します。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [answer, setAnswer] = useState('');\n  const [error, setError] = useState(null);\n  const [status, setStatus] = useState('typing');\n\n  if (status === 'success') {\n    return <h1>That's right!</h1>\n  }\n\n  async function handleSubmit(e) {\n    e.preventDefault();\n    setStatus('submitting');\n    try {\n      await submitForm(answer);\n      setStatus('success');\n    } catch (err) {\n      setStatus('typing');\n      setError(err);\n    }\n  }\n\n  function handleTextareaChange(e) {\n    setAnswer(e.target.value);\n  }\n\n  return (\n    <>\n      <h2>City quiz</h2>\n      <p>\n        In which city is there a billboard that turns air into drinkable water?\n      </p>\n      <form onSubmit={handleSubmit}>\n        <textarea\n          value={answer}\n          onChange={handleTextareaChange}\n          disabled={status === 'submitting'}\n        />\n        <br />\n        <button disabled={\n          answer.length === 0 ||\n          status === 'submitting'\n        }>\n          Submit\n        </button>\n        {error !== null &&\n          <p className=\"Error\">\n            {error.message}\n          </p>\n        }\n      </form>\n    </>\n  );\n}\n\nfunction submitForm(answer) {\n  // Pretend it's hitting the network.\n  return new Promise((resolve, reject) => {\n    setTimeout(() => {\n      let shouldError = answer.toLowerCase() !== 'lima'\n      if (shouldError) {\n        reject(new Error('Good guess but a wrong answer. Try again!'));\n      } else {\n        resolve();\n      }\n    }, 1500);\n  });\n}\n```\n\n```css\n.Error { color: red; }\n```\n\n</Sandpack>\n\nこのコードは、元の命令型の例よりも長くなっていますが、はるかに壊れにくくなっています。すべてのインタラクションを state 変化として表現することで、既存の state を壊すことなく、後から新しい視覚状態を導入することができます。また、インタラクション自体のロジックを変更することなく、各 state で表示されるべきものを変更することができます。\n\n<Recap>\n\n* 宣言型プログラミングとは、UI を細かく管理する（命令型）のではなく、視覚状態ごとに UI を記述することを意味する。\n* コンポーネントを開発するとき：\n  1. コンポーネントの視覚状態をすべて特定する。\n  2. 状態を変更するための人間およびコンピュータのトリガを決定する。\n  3. `useState` で state をモデル化する。\n  4. バグや矛盾を避けるため、不必要な state を削除する。\n  5. state を設定するためのイベントハンドラを接続する。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### CSS クラスの追加・削除 {/*add-and-remove-a-css-class*/}\n\n画像をクリックすると、外側の `<div>` から  `background--active` CSS クラスが削除され、`<img>` に `picture--active` クラスが追加されるようにしてください。もう一度背景をクリックすると、元の CSS クラスに戻るようにします。\n\n視覚的には、画像の上をクリックすると、紫色の背景が消え、画像の境界線がハイライトされると考えてください。画像の外側をクリックすると、背景がハイライトされますが、画像の境界線のハイライトは削除されます。\n\n<Sandpack>\n\n```js\nexport default function Picture() {\n  return (\n    <div className=\"background background--active\">\n      <img\n        className=\"picture\"\n        alt=\"Rainbow houses in Kampung Pelangi, Indonesia\"\n        src=\"https://i.imgur.com/5qwVYb1.jpeg\"\n      />\n    </div>\n  );\n}\n```\n\n```css\nbody { margin: 0; padding: 0; height: 250px; }\n\n.background {\n  width: 100vw;\n  height: 100vh;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background: #eee;\n}\n\n.background--active {\n  background: #a6b5ff;\n}\n\n.picture {\n  width: 200px;\n  height: 200px;\n  border-radius: 10px;\n  border: 5px solid transparent;\n}\n\n.picture--active {\n  border: 5px solid #a6b5ff;\n}\n```\n\n</Sandpack>\n\n<Solution>\n\nこのコンポーネントは、画像がアクティブなときと、画像が非アクティブなときの 2 つの視覚状態を持ちます。\n\n* 画像がアクティブの場合、CSS クラスは `background` と `picture picture--active` となります。\n* 画像が非アクティブの場合、CSS クラスは `background background--active` と `picture` になります。\n\n画像がアクティブかどうかを記憶するためには、単一のブール型の state 変数があれば十分です。本来の作業は CSS クラスを削除または追加することでした。しかし、React では UI 要素を*操作*するのではなく、何を見たいのかを*記述*する必要があります。そのため、現在の state に基づいて両方の CSS クラスを計算する必要があります。また、画像のクリックが背景のクリックとしても処理されてしまわないように、[イベントの伝播を停止](/learn/responding-to-events#stopping-propagation)する必要があります。\n\n画像をクリックしたりその外側をクリックしたりして、このバージョンが動作することを確認してください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Picture() {\n  const [isActive, setIsActive] = useState(false);\n\n  let backgroundClassName = 'background';\n  let pictureClassName = 'picture';\n  if (isActive) {\n    pictureClassName += ' picture--active';\n  } else {\n    backgroundClassName += ' background--active';\n  }\n\n  return (\n    <div\n      className={backgroundClassName}\n      onClick={() => setIsActive(false)}\n    >\n      <img\n        onClick={e => {\n          e.stopPropagation();\n          setIsActive(true);\n        }}\n        className={pictureClassName}\n        alt=\"Rainbow houses in Kampung Pelangi, Indonesia\"\n        src=\"https://i.imgur.com/5qwVYb1.jpeg\"\n      />\n    </div>\n  );\n}\n```\n\n```css\nbody { margin: 0; padding: 0; height: 250px; }\n\n.background {\n  width: 100vw;\n  height: 100vh;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background: #eee;\n}\n\n.background--active {\n  background: #a6b5ff;\n}\n\n.picture {\n  width: 200px;\n  height: 200px;\n  border-radius: 10px;\n  border: 5px solid transparent;\n}\n\n.picture--active {\n  border: 5px solid #a6b5ff;\n}\n```\n\n</Sandpack>\n\nあるいは、2 つの別々の JSX の塊を返すこともできます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Picture() {\n  const [isActive, setIsActive] = useState(false);\n  if (isActive) {\n    return (\n      <div\n        className=\"background\"\n        onClick={() => setIsActive(false)}\n      >\n        <img\n          className=\"picture picture--active\"\n          alt=\"Rainbow houses in Kampung Pelangi, Indonesia\"\n          src=\"https://i.imgur.com/5qwVYb1.jpeg\"\n          onClick={e => e.stopPropagation()}\n        />\n      </div>\n    );\n  }\n  return (\n    <div className=\"background background--active\">\n      <img\n        className=\"picture\"\n        alt=\"Rainbow houses in Kampung Pelangi, Indonesia\"\n        src=\"https://i.imgur.com/5qwVYb1.jpeg\"\n        onClick={() => setIsActive(true)}\n      />\n    </div>\n  );\n}\n```\n\n```css\nbody { margin: 0; padding: 0; height: 250px; }\n\n.background {\n  width: 100vw;\n  height: 100vh;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background: #eee;\n}\n\n.background--active {\n  background: #a6b5ff;\n}\n\n.picture {\n  width: 200px;\n  height: 200px;\n  border-radius: 10px;\n  border: 5px solid transparent;\n}\n\n.picture--active {\n  border: 5px solid #a6b5ff;\n}\n```\n\n</Sandpack>\n\n異なる 2 つの JSX の塊が同じツリーを記述する場合、それらのネスト（最初の `<div>` → 最初の `<img>`）は一致する必要があることに留意してください。そうでなければ、`isActive` を切り替えると、下のツリー全体が再作成され、[state がリセット](/learn/preserving-and-resetting-state)されてしまいます。このため、同じような JSX ツリーが両方のケースで返される場合は、1 つの JSX として記述する方が良いでしょう。\n\n</Solution>\n\n#### プロフィールエディタ {/*profile-editor*/}\n\n以下は、プレーンな JavaScript と DOM で実装した小さなフォームです。このフォームをいじってみて、その動作を理解してください。\n\n<Sandpack>\n\n```js src/index.js active\nfunction handleFormSubmit(e) {\n  e.preventDefault();\n  if (editButton.textContent === 'Edit Profile') {\n    editButton.textContent = 'Save Profile';\n    hide(firstNameText);\n    hide(lastNameText);\n    show(firstNameInput);\n    show(lastNameInput);\n  } else {\n    editButton.textContent = 'Edit Profile';\n    hide(firstNameInput);\n    hide(lastNameInput);\n    show(firstNameText);\n    show(lastNameText);\n  }\n}\n\nfunction handleFirstNameChange() {\n  firstNameText.textContent = firstNameInput.value;\n  helloText.textContent = (\n    'Hello ' +\n    firstNameInput.value + ' ' +\n    lastNameInput.value + '!'\n  );\n}\n\nfunction handleLastNameChange() {\n  lastNameText.textContent = lastNameInput.value;\n  helloText.textContent = (\n    'Hello ' +\n    firstNameInput.value + ' ' +\n    lastNameInput.value + '!'\n  );\n}\n\nfunction hide(el) {\n  el.style.display = 'none';\n}\n\nfunction show(el) {\n  el.style.display = '';\n}\n\nlet form = document.getElementById('form');\nlet editButton = document.getElementById('editButton');\nlet firstNameInput = document.getElementById('firstNameInput');\nlet firstNameText = document.getElementById('firstNameText');\nlet lastNameInput = document.getElementById('lastNameInput');\nlet lastNameText = document.getElementById('lastNameText');\nlet helloText = document.getElementById('helloText');\nform.onsubmit = handleFormSubmit;\nfirstNameInput.oninput = handleFirstNameChange;\nlastNameInput.oninput = handleLastNameChange;\n```\n\n```js sandbox.config.json hidden\n{\n  \"hardReloadOnChange\": true\n}\n```\n\n```html public/index.html\n<form id=\"form\">\n  <label>\n    First name:\n    <b id=\"firstNameText\">Jane</b>\n    <input\n      id=\"firstNameInput\"\n      value=\"Jane\"\n      style=\"display: none\">\n  </label>\n  <label>\n    Last name:\n    <b id=\"lastNameText\">Jacobs</b>\n    <input\n      id=\"lastNameInput\"\n      value=\"Jacobs\"\n      style=\"display: none\">\n  </label>\n  <button type=\"submit\" id=\"editButton\">Edit Profile</button>\n  <p><i id=\"helloText\">Hello, Jane Jacobs!</i></p>\n</form>\n\n<style>\n* { box-sizing: border-box; }\nbody { font-family: sans-serif; margin: 20px; padding: 0; }\nlabel { display: block; margin-bottom: 20px; }\n</style>\n```\n\n</Sandpack>\n\nこのフォームは、編集モードでは入力フィールド、閲覧モードでは入力結果のみが表示される、というように 2 つのモードを切り替えて動作します。ボタンのラベルは、モードによって \"Edit\" と \"Save\" が切り替わります。入力内容を変更すると、下部のウェルカムメッセージがリアルタイムで更新されます。\n\nあなたのタスクは、これを以下のサンドボックス内で React で再実装することです。作業しやすいようにマークアップはすでに JSX に変換されていますが、元のコードと同様に入力フィールドの表示と非表示を行う必要があります。\n\nまた、下部のテキストもちゃんと更新されるようにしてください！\n\n<Sandpack>\n\n```js\nexport default function EditProfile() {\n  return (\n    <form>\n      <label>\n        First name:{' '}\n        <b>Jane</b>\n        <input />\n      </label>\n      <label>\n        Last name:{' '}\n        <b>Jacobs</b>\n        <input />\n      </label>\n      <button type=\"submit\">\n        Edit Profile\n      </button>\n      <p><i>Hello, Jane Jacobs!</i></p>\n    </form>\n  );\n}\n```\n\n```css\nlabel { display: block; margin-bottom: 20px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n入力値を保持するために `firstName` と `lastName` の 2 つの state 変数が必要になります。また、入力フィールドを表示するかどうかを管理する `isEditing` state 変数も必要になります。`fullName` 変数は必要*ありません*。なぜなら、フルネームは常に `firstName` と `lastName` から計算できるからです。\n\n最後に、[条件付きレンダー](/learn/conditional-rendering)を使用して、`isEditing` に応じて入力フィールドを表示したり非表示にしたりする必要があります。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function EditProfile() {\n  const [isEditing, setIsEditing] = useState(false);\n  const [firstName, setFirstName] = useState('Jane');\n  const [lastName, setLastName] = useState('Jacobs');\n\n  return (\n    <form onSubmit={e => {\n      e.preventDefault();\n      setIsEditing(!isEditing);\n    }}>\n      <label>\n        First name:{' '}\n        {isEditing ? (\n          <input\n            value={firstName}\n            onChange={e => {\n              setFirstName(e.target.value)\n            }}\n          />\n        ) : (\n          <b>{firstName}</b>\n        )}\n      </label>\n      <label>\n        Last name:{' '}\n        {isEditing ? (\n          <input\n            value={lastName}\n            onChange={e => {\n              setLastName(e.target.value)\n            }}\n          />\n        ) : (\n          <b>{lastName}</b>\n        )}\n      </label>\n      <button type=\"submit\">\n        {isEditing ? 'Save' : 'Edit'} Profile\n      </button>\n      <p><i>Hello, {firstName} {lastName}!</i></p>\n    </form>\n  );\n}\n```\n\n```css\nlabel { display: block; margin-bottom: 20px; }\n```\n\n</Sandpack>\n\nこの解決策と元の命令型のコードを比較してみてください。どう違うでしょうか？\n\n</Solution>\n\n#### React を使わない命令型コードのリファクタリング {/*refactor-the-imperative-solution-without-react*/}\n\nこちらはひとつ前のチャレンジで、React を使わずに命令的に記述されたオリジナルのサンドボックスです。\n\n<Sandpack>\n\n```js src/index.js active\nfunction handleFormSubmit(e) {\n  e.preventDefault();\n  if (editButton.textContent === 'Edit Profile') {\n    editButton.textContent = 'Save Profile';\n    hide(firstNameText);\n    hide(lastNameText);\n    show(firstNameInput);\n    show(lastNameInput);\n  } else {\n    editButton.textContent = 'Edit Profile';\n    hide(firstNameInput);\n    hide(lastNameInput);\n    show(firstNameText);\n    show(lastNameText);\n  }\n}\n\nfunction handleFirstNameChange() {\n  firstNameText.textContent = firstNameInput.value;\n  helloText.textContent = (\n    'Hello ' +\n    firstNameInput.value + ' ' +\n    lastNameInput.value + '!'\n  );\n}\n\nfunction handleLastNameChange() {\n  lastNameText.textContent = lastNameInput.value;\n  helloText.textContent = (\n    'Hello ' +\n    firstNameInput.value + ' ' +\n    lastNameInput.value + '!'\n  );\n}\n\nfunction hide(el) {\n  el.style.display = 'none';\n}\n\nfunction show(el) {\n  el.style.display = '';\n}\n\nlet form = document.getElementById('form');\nlet editButton = document.getElementById('editButton');\nlet firstNameInput = document.getElementById('firstNameInput');\nlet firstNameText = document.getElementById('firstNameText');\nlet lastNameInput = document.getElementById('lastNameInput');\nlet lastNameText = document.getElementById('lastNameText');\nlet helloText = document.getElementById('helloText');\nform.onsubmit = handleFormSubmit;\nfirstNameInput.oninput = handleFirstNameChange;\nlastNameInput.oninput = handleLastNameChange;\n```\n\n```js sandbox.config.json hidden\n{\n  \"hardReloadOnChange\": true\n}\n```\n\n```html public/index.html\n<form id=\"form\">\n  <label>\n    First name:\n    <b id=\"firstNameText\">Jane</b>\n    <input\n      id=\"firstNameInput\"\n      value=\"Jane\"\n      style=\"display: none\">\n  </label>\n  <label>\n    Last name:\n    <b id=\"lastNameText\">Jacobs</b>\n    <input\n      id=\"lastNameInput\"\n      value=\"Jacobs\"\n      style=\"display: none\">\n  </label>\n  <button type=\"submit\" id=\"editButton\">Edit Profile</button>\n  <p><i id=\"helloText\">Hello, Jane Jacobs!</i></p>\n</form>\n\n<style>\n* { box-sizing: border-box; }\nbody { font-family: sans-serif; margin: 20px; padding: 0; }\nlabel { display: block; margin-bottom: 20px; }\n</style>\n```\n\n</Sandpack>\n\nReact が存在しなかったと想像してください。このコードをリファクタリングして、ロジックを壊れにくくし、React のバージョンに近づけることはできるでしょうか？ React のように state が明示的であれば、どのように見えるでしょうか？\n\n何から手をつければいいのか悩んでいる人は、下のスタブですでにほとんどの構造ができています。ここから始める場合は、`updateDOM` 関数で足りないロジックを埋めてください。（必要に応じて元のコードを参照してください。）\n\n<Sandpack>\n\n```js src/index.js active\nlet firstName = 'Jane';\nlet lastName = 'Jacobs';\nlet isEditing = false;\n\nfunction handleFormSubmit(e) {\n  e.preventDefault();\n  setIsEditing(!isEditing);\n}\n\nfunction handleFirstNameChange(e) {\n  setFirstName(e.target.value);\n}\n\nfunction handleLastNameChange(e) {\n  setLastName(e.target.value);\n}\n\nfunction setFirstName(value) {\n  firstName = value;\n  updateDOM();\n}\n\nfunction setLastName(value) {\n  lastName = value;\n  updateDOM();\n}\n\nfunction setIsEditing(value) {\n  isEditing = value;\n  updateDOM();\n}\n\nfunction updateDOM() {\n  if (isEditing) {\n    editButton.textContent = 'Save Profile';\n    // TODO: show inputs, hide content\n  } else {\n    editButton.textContent = 'Edit Profile';\n    // TODO: hide inputs, show content\n  }\n  // TODO: update text labels\n}\n\nfunction hide(el) {\n  el.style.display = 'none';\n}\n\nfunction show(el) {\n  el.style.display = '';\n}\n\nlet form = document.getElementById('form');\nlet editButton = document.getElementById('editButton');\nlet firstNameInput = document.getElementById('firstNameInput');\nlet firstNameText = document.getElementById('firstNameText');\nlet lastNameInput = document.getElementById('lastNameInput');\nlet lastNameText = document.getElementById('lastNameText');\nlet helloText = document.getElementById('helloText');\nform.onsubmit = handleFormSubmit;\nfirstNameInput.oninput = handleFirstNameChange;\nlastNameInput.oninput = handleLastNameChange;\n```\n\n```js sandbox.config.json hidden\n{\n  \"hardReloadOnChange\": true\n}\n```\n\n```html public/index.html\n<form id=\"form\">\n  <label>\n    First name:\n    <b id=\"firstNameText\">Jane</b>\n    <input\n      id=\"firstNameInput\"\n      value=\"Jane\"\n      style=\"display: none\">\n  </label>\n  <label>\n    Last name:\n    <b id=\"lastNameText\">Jacobs</b>\n    <input\n      id=\"lastNameInput\"\n      value=\"Jacobs\"\n      style=\"display: none\">\n  </label>\n  <button type=\"submit\" id=\"editButton\">Edit Profile</button>\n  <p><i id=\"helloText\">Hello, Jane Jacobs!</i></p>\n</form>\n\n<style>\n* { box-sizing: border-box; }\nbody { font-family: sans-serif; margin: 20px; padding: 0; }\nlabel { display: block; margin-bottom: 20px; }\n</style>\n```\n\n</Sandpack>\n\n<Solution>\n\n足りないロジックは、入力フィールドとコンテンツの表示の切り替え、ラベルの更新などでした。\n\n<Sandpack>\n\n```js src/index.js active\nlet firstName = 'Jane';\nlet lastName = 'Jacobs';\nlet isEditing = false;\n\nfunction handleFormSubmit(e) {\n  e.preventDefault();\n  setIsEditing(!isEditing);\n}\n\nfunction handleFirstNameChange(e) {\n  setFirstName(e.target.value);\n}\n\nfunction handleLastNameChange(e) {\n  setLastName(e.target.value);\n}\n\nfunction setFirstName(value) {\n  firstName = value;\n  updateDOM();\n}\n\nfunction setLastName(value) {\n  lastName = value;\n  updateDOM();\n}\n\nfunction setIsEditing(value) {\n  isEditing = value;\n  updateDOM();\n}\n\nfunction updateDOM() {\n  if (isEditing) {\n    editButton.textContent = 'Save Profile';\n    hide(firstNameText);\n    hide(lastNameText);\n    show(firstNameInput);\n    show(lastNameInput);\n  } else {\n    editButton.textContent = 'Edit Profile';\n    hide(firstNameInput);\n    hide(lastNameInput);\n    show(firstNameText);\n    show(lastNameText);\n  }\n  firstNameText.textContent = firstName;\n  lastNameText.textContent = lastName;\n  helloText.textContent = (\n    'Hello ' +\n    firstName + ' ' +\n    lastName + '!'\n  );\n}\n\nfunction hide(el) {\n  el.style.display = 'none';\n}\n\nfunction show(el) {\n  el.style.display = '';\n}\n\nlet form = document.getElementById('form');\nlet editButton = document.getElementById('editButton');\nlet firstNameInput = document.getElementById('firstNameInput');\nlet firstNameText = document.getElementById('firstNameText');\nlet lastNameInput = document.getElementById('lastNameInput');\nlet lastNameText = document.getElementById('lastNameText');\nlet helloText = document.getElementById('helloText');\nform.onsubmit = handleFormSubmit;\nfirstNameInput.oninput = handleFirstNameChange;\nlastNameInput.oninput = handleLastNameChange;\n```\n\n```js sandbox.config.json hidden\n{\n  \"hardReloadOnChange\": true\n}\n```\n\n```html public/index.html\n<form id=\"form\">\n  <label>\n    First name:\n    <b id=\"firstNameText\">Jane</b>\n    <input\n      id=\"firstNameInput\"\n      value=\"Jane\"\n      style=\"display: none\">\n  </label>\n  <label>\n    Last name:\n    <b id=\"lastNameText\">Jacobs</b>\n    <input\n      id=\"lastNameInput\"\n      value=\"Jacobs\"\n      style=\"display: none\">\n  </label>\n  <button type=\"submit\" id=\"editButton\">Edit Profile</button>\n  <p><i id=\"helloText\">Hello, Jane Jacobs!</i></p>\n</form>\n\n<style>\n* { box-sizing: border-box; }\nbody { font-family: sans-serif; margin: 20px; padding: 0; }\nlabel { display: block; margin-bottom: 20px; }\n</style>\n```\n\n</Sandpack>\n\nあなたが書いた `updateDOM` 関数は、state を設定したときに React が水面下で何をするのかを示しています。（ただし、React は前回設定したときから変化していないプロパティについては DOM に触れないようにもしています。)\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/referencing-values-with-refs.md",
    "content": "---\ntitle: ref で値を参照する\n---\n\n<Intro>\n\nコンポーネントに情報を「記憶」させたいが、その情報が[新しいレンダーをトリガ](/learn/render-and-commit)しないようにしたい場合、*ref* を使うことができます。\n\n</Intro>\n\n<YouWillLearn>\n\n- コンポーネントに ref を追加する方法\n- ref の値を更新する方法\n- ref と state の違い\n- ref を安全に使う方法\n\n</YouWillLearn>\n\n## コンポーネントに ref を追加する {/*adding-a-ref-to-your-component*/}\n\nコンポーネントに ref を追加するには、React から `useRef` フックをインポートします。\n\n```js\nimport { useRef } from 'react';\n```\n\nコンポーネント内で、`useRef` フックを呼び出し、唯一の引数として参照したい初期値を渡します。例えば、値 `0` を参照する ref は以下のようになります。\n\n```js\nconst ref = useRef(0);\n```\n\n`useRef` は以下のようなオブジェクトを返します。\n\n```js\n{ \n  current: 0 // The value you passed to useRef\n}\n```\n\n<Illustration src=\"/images/docs/illustrations/i_ref.png\" alt=\"'current' と書かれた矢印が 'ref' と書かれたポケットに詰め込まれている。\" />\n\nref の現在の値には、`ref.current` プロパティを通じてアクセスできます。この値は意図的にミュータブル、つまり読み書きが可能となっています。これは、React が管理しない、コンポーネントの秘密のポケットのようなものです。（そしてこれが、ref が React の一方向データフローからの「避難ハッチ (escape hatch)」である理由です。詳細は以下で説明します！）\n\nこの例では、ボタンがクリックされるたびに `ref.current` をインクリメントします。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\n\nexport default function Counter() {\n  let ref = useRef(0);\n\n  function handleClick() {\n    ref.current = ref.current + 1;\n    alert('You clicked ' + ref.current + ' times!');\n  }\n\n  return (\n    <button onClick={handleClick}>\n      Click me!\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\nこの ref は数値を参照していますが、[state](/learn/state-a-components-memory) と同様に、文字列、オブジェクト、関数など、何でも扱うことができます。ただし、state とは異なり、ref は `current` プロパティを読み書きできるだけのプレーンな JavaScript オブジェクトです。\n\n**インクリメントごとにコンポーネントが再レンダーされない**ことに注意してください。state と同様に、ref は React によって再レンダー間で保持されます。ただし、state はセットするとコンポーネントが再レンダーされます。ref を変更しても再レンダーは起きません！\n\n## 例：ストップウォッチの作成 {/*example-building-a-stopwatch*/}\n\nref と state を 1 つのコンポーネントで組み合わせることができます。例えば、ユーザがボタンを押すことで開始または停止できるストップウォッチを作成しましょう。ユーザが \"Start\" を押してからどれだけの時間が経過したかを表示するためには、\"Start\" ボタンが押された時刻と現在時刻を管理する必要があります。**これらの情報はレンダーに使用されるものなので、state に保持します**。\n\n```js\nconst [startTime, setStartTime] = useState(null);\nconst [now, setNow] = useState(null);\n```\n\nユーザが \"Start\" を押すと、[`setInterval`](https://developer.mozilla.org/docs/Web/API/setInterval) を使って 10 ミリ秒ごとに時間を更新します。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Stopwatch() {\n  const [startTime, setStartTime] = useState(null);\n  const [now, setNow] = useState(null);\n\n  function handleStart() {\n    // Start counting.\n    setStartTime(Date.now());\n    setNow(Date.now());\n\n    setInterval(() => {\n      // Update the current time every 10ms.\n      setNow(Date.now());\n    }, 10);\n  }\n\n  let secondsPassed = 0;\n  if (startTime != null && now != null) {\n    secondsPassed = (now - startTime) / 1000;\n  }\n\n  return (\n    <>\n      <h1>Time passed: {secondsPassed.toFixed(3)}</h1>\n      <button onClick={handleStart}>\n        Start\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n\"Stop\" ボタンが押されると、既存のインターバルをキャンセルして `now` という state 変数の更新を停止する必要があります。これは [`clearInterval`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval) を呼び出すことで実現できますが、ユーザが以前 Start を押した際の `setInterval` 呼び出しで返された、インターバル ID を指定する必要があります。インターバル ID は、どこかに保持しておく必要があります。**インターバル ID はレンダーには使用されないため、ref に保持します**。\n\n<Sandpack>\n\n```js\nimport { useState, useRef } from 'react';\n\nexport default function Stopwatch() {\n  const [startTime, setStartTime] = useState(null);\n  const [now, setNow] = useState(null);\n  const intervalRef = useRef(null);\n\n  function handleStart() {\n    setStartTime(Date.now());\n    setNow(Date.now());\n\n    clearInterval(intervalRef.current);\n    intervalRef.current = setInterval(() => {\n      setNow(Date.now());\n    }, 10);\n  }\n\n  function handleStop() {\n    clearInterval(intervalRef.current);\n  }\n\n  let secondsPassed = 0;\n  if (startTime != null && now != null) {\n    secondsPassed = (now - startTime) / 1000;\n  }\n\n  return (\n    <>\n      <h1>Time passed: {secondsPassed.toFixed(3)}</h1>\n      <button onClick={handleStart}>\n        Start\n      </button>\n      <button onClick={handleStop}>\n        Stop\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n情報がレンダー時に使用される場合は、state に保持します。情報がイベントハンドラ内でのみ必要で、変更しても再レンダーが必要ない場合は、ref を使用する方が効率的です。\n\n## ref と state の違い {/*differences-between-refs-and-state*/}\n\nref の方が state よりも「制限が緩い」と感じるかもしれません。例えば、state セッタ関数を使わずに変更できるわけですから。しかし、ほとんどの場合、state を使用することになります。ref は頻繁には必要としない「避難ハッチ」です。state と ref の比較は以下の通りです。\n\n| ref                                                                                   | state                                                                                                                     |\n| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |\n| `useRef(initialValue)` は `{ current: initialValue }` を返す                          | `useState(initialValue)` は state 変数の現在の値と state セッタ関数を返す（`[value, setValue]`）                          |\n| 変更しても再レンダーがトリガされない                                                  | 変更すると再レンダーがトリガされる                                                                                        |\n| ミュータブル - レンダープロセス外で `current` の値を変更・更新できる                  | \"イミュータブル\" - state 変数を変更するためには、再レンダーをキューに入れるために state セッタ関数を使用する              |\n| レンダー中に `current` の値を読み取る（または書き込む）べきではない                   | いつでも state を読み取ることができる。ただし、各レンダーには独自の state の[スナップショット](/learn/state-as-a-snapshot) があり変更されない |\n\nここに、state を使って実装されたカウンタボタンがあります。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [count, setCount] = useState(0);\n\n  function handleClick() {\n    setCount(count + 1);\n  }\n\n  return (\n    <button onClick={handleClick}>\n      You clicked {count} times\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\n`count` 値は表示されるものなので、state を使うのが適切です。カウンタの値が `setCount()` でセットされると、React はコンポーネントを再レンダーし、画面が新しいカウントを反映するように更新されます。\n\nもしこれを ref で実装しようとしても、React はコンポーネントを再レンダーしないため、カウントの変更は一切反映されません！ ボタンをクリックしても**テキストが更新されない**ことがわかります。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [13]}}\nimport { useRef } from 'react';\n\nexport default function Counter() {\n  let countRef = useRef(0);\n\n  function handleClick() {\n    // This doesn't re-render the component!\n    countRef.current = countRef.current + 1;\n  }\n\n  return (\n    <button onClick={handleClick}>\n      You clicked {countRef.current} times\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\nこれが、レンダー中に `ref.current` を読み込むと信頼性の低いコードになる理由です。それが必要な場合は、代わりに state を使用してください。\n\n<DeepDive>\n\n#### useRef の内部動作 {/*how-does-use-ref-work-inside*/}\n\n`useState` と `useRef` は両方とも React によって提供される機能ですが、本質的には `useRef` は `useState` *をベースに*実装されているものです。React の内部では、`useRef` が以下のように実装されていると考えることができます。\n\n```js\n// Inside of React\nfunction useRef(initialValue) {\n  const [ref, unused] = useState({ current: initialValue });\n  return ref;\n}\n```\n\n最初のレンダー中に、`useRef` は `{ current: initialValue }` を返します。このオブジェクトは React によって保持されるため、次のレンダー時には同じオブジェクトが返されます。この例で、state のセッタは使われていないことに注意してください。`useRef` は常に同じオブジェクトを返す必要があるのですからセッタは不要です！\n\nReact が `useRef` を組み込み機能として提供しているのは、これが現実的によくある使用法だからです。しかし、ref をセッタのない通常の state 変数と考えることができます。オブジェクト指向プログラミングに慣れている場合、ref はインスタンスフィールドに似ていると感じるかもしれませんが、`this.something` の代わりに `somethingRef.current` と書きます。\n\n</DeepDive>\n\n## ref を使うタイミング {/*when-to-use-refs*/}\n\n通常、ref を使用するのは、コンポーネントが React の外に「踏み出して」、外部 API（多くの場合はコンポーネントの外観に影響を与えないブラウザ API）と通信する必要がある場合です。以下は、そのような稀な状況の例です。\n\n- [タイムアウト ID](https://developer.mozilla.org/docs/Web/API/setTimeout) の保存。\n- [DOM 要素](https://developer.mozilla.org/docs/Web/API/Element)の保存と操作。これについては[次のページ](/learn/manipulating-the-dom-with-refs)で取り上げます。\n- JSX を計算するために必要ではないその他のオブジェクトの保存。\n\nコンポーネントが値を保存する必要があるがそれがレンダーロジックに影響しないという場合は、ref を選択してください。\n\n## ref のベストプラクティス {/*best-practices-for-refs*/}\n\n以下の原則に従うことで、コンポーネントがより予測可能になります。\n\n- **ref を避難ハッチ (escape hatch) として扱う**。ref が有用なのは、外部システムやブラウザ API と連携する場合です。アプリケーションのロジックやデータフローの多くが ref に依存しているような場合は、アプローチを見直すことを検討してください。\n- **レンダー中に `ref.current` を読み書きしない**。レンダー中に情報が必要な場合は、代わりに [state](/learn/state-a-components-memory) を使用してください。React は `ref.current` が書き換わったタイミングを把握しないため、レンダー中にただそれを読み込むだけでも、コンポーネントの挙動が予測しづらくなってしまいます。（唯一の例外は `if (!ref.current) ref.current = new Thing()` のような、最初のレンダー中に一度だけ ref をセットするコードです。）\n\nReact の state の制約は ref には適用されません。例えば、state は[各レンダーのスナップショット](/learn/state-as-a-snapshot)のように振る舞い、[同期的に更新されません](/learn/queueing-a-series-of-state-updates)。しかし、ref の現在値を書き換えると、すぐに変更されます。\n\n```js\nref.current = 5;\nconsole.log(ref.current); // 5\n```\n\nこれは、**ref 自体は通常の JavaScript オブジェクト**に過ぎず、現にそのように振る舞うからです。\n\nまた、ref を使っている場合は、[ミューテーションを避ける](/learn/updating-objects-in-state)ことを考慮する必要もありません。書き換えようとしているオブジェクトがレンダーに使われない限り、React は ref やその内容に対してあなたが何を行っても気にしません。\n\n## ref と DOM {/*refs-and-the-dom*/}\n\nref は任意の値を参照として保持できます。ただし、ref の最も一般的な使用例は、DOM 要素にアクセスすることです。例えば、プログラムで入力にフォーカスを当てたい場合に便利です。`<div ref={myRef}>` のようにして JSX の `ref` 属性に ref を渡すと、React は対応する DOM 要素を `myRef.current` に入れます。その要素が DOM から削除されると、React は `myRef.current` を `null` にセットします。これについては、[ref で DOM を操作する](/learn/manipulating-the-dom-with-refs)で詳しく説明しています。\n\n<Recap>\n\n- ref は、レンダーに使用されない値を保持するための避難ハッチである。これは頻繁には必要ない。\n- ref は、`current` という単一のプロパティを持つプレーンな JavaScript オブジェクトであり、読み取りや書き込みができる。\n- `useRef` フックを呼び出すことで、React に ref を渡してもらう。\n- state と同様に、ref はコンポーネントの再レンダー間で情報を保持することができる。\n- state とは異なり、ref の `current` 値をセットしても再レンダーはトリガされない。\n- レンダー中に `ref.current` を読み書きしてはならない。それをするとコンポーネントが予測困難になる。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### 壊れたチャット入力欄を修正 {/*fix-a-broken-chat-input*/}\n\nメッセージを入力して \"Send\" をクリックしてください。\"Sent!\" アラートが表示されるまでに 3 秒の遅延があることに気付くでしょう。この遅延中に \"Undo\" ボタンが表示されます。それをクリックしてください。この \"Undo\" ボタンは、`handleSend` 中で保存されたタイムアウト ID に対して [`clearTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout) を呼び出すことで、\"Sent!\" メッセージが表示されないようにするはずのものです。しかし、\"Undo\" をクリックしても \"Sent!\" メッセージが表示されてしまいます。動作しない理由を探し、修正してください。\n\n<Hint>\n\nすべてのレンダーはコンポーネントのコードを最初から実行する（変数も初期化する）ため、`let timeoutID` のような通常の変数は、再レンダー間で「生き残る」ことはありません。タイムアウト ID を別の場所に保持する必要はないでしょうか？\n\n</Hint>\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [10]}}\nimport { useState } from 'react';\n\nexport default function Chat() {\n  const [text, setText] = useState('');\n  const [isSending, setIsSending] = useState(false);\n  let timeoutID = null;\n\n  function handleSend() {\n    setIsSending(true);\n    timeoutID = setTimeout(() => {\n      alert('Sent!');\n      setIsSending(false);\n    }, 3000);\n  }\n\n  function handleUndo() {\n    setIsSending(false);\n    clearTimeout(timeoutID);\n  }\n\n  return (\n    <>\n      <input\n        disabled={isSending}\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button\n        disabled={isSending}\n        onClick={handleSend}>\n        {isSending ? 'Sending...' : 'Send'}\n      </button>\n      {isSending &&\n        <button onClick={handleUndo}>\n          Undo\n        </button>\n      }\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution>\n\nコンポーネントが（state のセットなどにより）再レンダーされるたびに、すべてのローカル変数は初期化されます。これが、`timeoutID` のようなローカル変数にタイムアウト ID を保存しても将来別のイベントハンドラがそれを「見える」ことを期待できない理由です。代わりに、レンダー間で React が保持する ref に保存しましょう。\n\n<Sandpack>\n\n```js\nimport { useState, useRef } from 'react';\n\nexport default function Chat() {\n  const [text, setText] = useState('');\n  const [isSending, setIsSending] = useState(false);\n  const timeoutRef = useRef(null);\n\n  function handleSend() {\n    setIsSending(true);\n    timeoutRef.current = setTimeout(() => {\n      alert('Sent!');\n      setIsSending(false);\n    }, 3000);\n  }\n\n  function handleUndo() {\n    setIsSending(false);\n    clearTimeout(timeoutRef.current);\n  }\n\n  return (\n    <>\n      <input\n        disabled={isSending}\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button\n        disabled={isSending}\n        onClick={handleSend}>\n        {isSending ? 'Sending...' : 'Send'}\n      </button>\n      {isSending &&\n        <button onClick={handleUndo}>\n          Undo\n        </button>\n      }\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n\n#### 再レンダーに失敗するコンポーネントを修正 {/*fix-a-component-failing-to-re-render*/}\n\nこのボタンは、\"On\" と \"Off\" を表示するトグルボタンのはずです。しかし、常に \"Off\" が表示されます。このコードの何が問題なのでしょうか？ 修正してください。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [10]}}\nimport { useRef } from 'react';\n\nexport default function Toggle() {\n  const isOnRef = useRef(false);\n\n  return (\n    <button onClick={() => {\n      isOnRef.current = !isOnRef.current;\n    }}>\n      {isOnRef.current ? 'On' : 'Off'}\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution>\n\nこの例では、ref の現在値がレンダー出力の計算に使われています：`{isOnRef.current ? 'On' : 'Off'}`。つまりこの情報は ref にあるべきではなく、代わりに state に入れるべきだということです。修正するには ref を削除し、代わりに state を使用します。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Toggle() {\n  const [isOn, setIsOn] = useState(false);\n\n  return (\n    <button onClick={() => {\n      setIsOn(!isOn);\n    }}>\n      {isOn ? 'On' : 'Off'}\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n#### デバウンスの修正 {/*fix-debouncing*/}\n\nこの例では、すべてのボタンクリックハンドラが [\"デバウンス (debounce)\"](https://kettanaito.com/blog/debounce-vs-throttle) されています。この意味を確認するために、ボタンのうちの 1 つを押してみてください。メッセージが 1 秒後に表示されることに気付くでしょう。メッセージを待っている間にボタンを押すと、タイマがリセットされます。ですので、同じボタンを素早く何度もクリックし続けると、メッセージはクリックを*やめた* 1 秒後まで表示されません。デバウンスにより、ユーザが「操作をやめる」まであるアクションを遅らせることができます。\n\nこの例は動作していますが、意図した通りではありません。ボタンが独立していないのです。問題を確認するために、ボタンのうちの 1 つをクリックし、すぐに別のボタンをクリックしてみてください。遅延の後、両方のボタンのメッセージが表示されることを期待するでしょう。しかし、最後のボタンのメッセージだけが表示され、最初のボタンのメッセージは失われてしまいます。\n\nボタンがお互いに干渉しているのはなぜでしょうか？ 問題を見つけて修正してください。\n\n<Hint>\n\n最後のタイムアウト ID 変数が、すべての `DebouncedButton` コンポーネント間で共有されています。これが、あるボタンをクリックすると、別のボタンのタイムアウトがリセットされる理由です。各ボタンに別々のタイムアウト ID を格納できますか？\n\n</Hint>\n\n<Sandpack>\n\n```js\nlet timeoutID;\n\nfunction DebouncedButton({ onClick, children }) {\n  return (\n    <button onClick={() => {\n      clearTimeout(timeoutID);\n      timeoutID = setTimeout(() => {\n        onClick();\n      }, 1000);\n    }}>\n      {children}\n    </button>\n  );\n}\n\nexport default function Dashboard() {\n  return (\n    <>\n      <DebouncedButton\n        onClick={() => alert('Spaceship launched!')}\n      >\n        Launch the spaceship\n      </DebouncedButton>\n      <DebouncedButton\n        onClick={() => alert('Soup boiled!')}\n      >\n        Boil the soup\n      </DebouncedButton>\n      <DebouncedButton\n        onClick={() => alert('Lullaby sung!')}\n      >\n        Sing a lullaby\n      </DebouncedButton>\n    </>\n  )\n}\n```\n\n```css\nbutton { display: block; margin: 10px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n`timeoutID` のような変数は、すべてのコンポーネント間で共有されています。これが、2 つ目のボタンをクリックすると、最初のボタンの待機中のタイムアウトがリセットされてしまう理由です。これを修正するために、タイムアウトを ref に保持することができます。各ボタンは独自の ref を取得するため、互いに競合しません。2 つのボタンを素早くクリックすると、両方のメッセージが表示されることを確認してください。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\n\nfunction DebouncedButton({ onClick, children }) {\n  const timeoutRef = useRef(null);\n  return (\n    <button onClick={() => {\n      clearTimeout(timeoutRef.current);\n      timeoutRef.current = setTimeout(() => {\n        onClick();\n      }, 1000);\n    }}>\n      {children}\n    </button>\n  );\n}\n\nexport default function Dashboard() {\n  return (\n    <>\n      <DebouncedButton\n        onClick={() => alert('Spaceship launched!')}\n      >\n        Launch the spaceship\n      </DebouncedButton>\n      <DebouncedButton\n        onClick={() => alert('Soup boiled!')}\n      >\n        Boil the soup\n      </DebouncedButton>\n      <DebouncedButton\n        onClick={() => alert('Lullaby sung!')}\n      >\n        Sing a lullaby\n      </DebouncedButton>\n    </>\n  )\n}\n```\n\n```css\nbutton { display: block; margin: 10px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### 最新の state を読む {/*read-the-latest-state*/}\n\nこの例では、\"Send\" ボタンを押した後、メッセージが表示されるまでにちいさな遅延があります。\"hello\" と入力して Send ボタンを押してから、すぐに入力欄を編集してみてください。編集したにもかかわらず、アラートには \"hello\"（ボタンがクリックされた[時点](/learn/state-as-a-snapshot#state-over-time)の state 値）が表示されます。\n\n通常、これがアプリで望ましい動作です。ただしまれに、非同期コードで state の*最新*バージョンを読み取りたい場合があります。クリック時のテキストではなく、*現在*の入力テキストをアラートに表示する方法を考えてみてください。\n\n<Sandpack>\n\n```js\nimport { useState, useRef } from 'react';\n\nexport default function Chat() {\n  const [text, setText] = useState('');\n\n  function handleSend() {\n    setTimeout(() => {\n      alert('Sending: ' + text);\n    }, 3000);\n  }\n\n  return (\n    <>\n      <input\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button\n        onClick={handleSend}>\n        Send\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution>\n\nstate は[スナップショットのように](/learn/state-as-a-snapshot)動作するため、タイムアウトのような非同期の操作から最新の state を読み取ることはできません。ただし、最新の入力テキストを ref に保持しておくことができます。ref は書き換え可能であるため、いつでも `current` プロパティを読み取ることができます。現在のテキストもレンダーに使用されるため、この例では、state 変数（レンダー用）*と* ref（タイムアウトで読み取るため）*の両方*が必要です。現在の ref 値は手動で更新する必要があります。\n\n<Sandpack>\n\n```js\nimport { useState, useRef } from 'react';\n\nexport default function Chat() {\n  const [text, setText] = useState('');\n  const textRef = useRef(text);\n\n  function handleChange(e) {\n    setText(e.target.value);\n    textRef.current = e.target.value;\n  }\n\n  function handleSend() {\n    setTimeout(() => {\n      alert('Sending: ' + textRef.current);\n    }, 3000);\n  }\n\n  return (\n    <>\n      <input\n        value={text}\n        onChange={handleChange}\n      />\n      <button\n        onClick={handleSend}>\n        Send\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/removing-effect-dependencies.md",
    "content": "---\ntitle: 'エフェクトから依存値を取り除く'\n---\n\n<Intro>\n\nエフェクトを記述する際、リンタはエフェクトが読み取るすべてのリアクティブな値（props や state など）がエフェクトの依存値のリストに含まれているか確認します。これにより、エフェクトがコンポーネントの最新の props や state と同期された状態を保つことができます。不要な依存値があると、エフェクトが頻繁に実行され過ぎたり、無限ループが発生したりすることがあります。このガイドでは、エフェクトから必要のない依存値を見つけ、取り除く方法を説明します。\n\n</Intro>\n\n<YouWillLearn>\n\n- エフェクトの依存値に伴う無限ループを修正する方法\n- 依存値を削除したい場合に行うこと\n- エフェクト内で「反応」させずに値を読み取る方法\n- オブジェクト型や関数型の依存値を避ける理由とその方法\n- 依存配列のリンタを抑制する危険性と、代わりに行うべきこと\n\n</YouWillLearn>\n\n## 依存配列はコードに合わせるべき {/*dependencies-should-match-the-code*/}\n\nエフェクトを記述する際は、それに何をさせたいのであれ、[開始方法および停止方法](/learn/lifecycle-of-reactive-effects#the-lifecycle-of-an-effect)を指定することになります。\n\n```js {5-7}\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  \t// ...\n}\n```\n\nここでエフェクトの依存配列を空 (`[]`) にした場合、リンタは正しい依存配列を提案します。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, []); // <-- Fix the mistake here!\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom roomId={roomId} />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\nリンタの指示に従って、依存値を記入しましょう。\n\n```js {6}\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]); // ✅ All dependencies declared\n  // ...\n}\n```\n\n[エフェクトはリアクティブな値に「反応」します](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values)。`roomId` はリアクティブな値である（なぜなら再レンダー時に変更される可能性がある）ため、リンタはそれを依存値として指定していることを確認します。`roomId` として異なる値が与えられた場合、React はエフェクトを再同期します。これにより、チャットが選択中のルームへの接続を維持し、ドロップダウンに「反応」することが保証されます。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom roomId={roomId} />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n### 依存値を削除したければ依存値でないことを示す {/*to-remove-a-dependency-prove-that-its-not-a-dependency*/}\n\nエフェクトの依存配列は自分で「選ぶ」たぐいのものではないことに注意してください。エフェクトのコードで使用されるすべての<CodeStep step={2}>リアクティブな値</CodeStep>は、依存値のリスト内で宣言されなければなりません。依存配列は、その周囲にあるコードによって決定されます。\n\n```js [[2, 3, \"roomId\"], [2, 5, \"roomId\"], [2, 8, \"roomId\"]]\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) { // This is a reactive value\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId); // This Effect reads that reactive value\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]); // ✅ So you must specify that reactive value as a dependency of your Effect\n  // ...\n}\n```\n\n[リアクティブな値](/learn/lifecycle-of-reactive-effects#all-variables-declared-in-the-component-body-are-reactive)には props や、コンポーネント内に直接宣言されたすべての変数や関数が含まれます。`roomId` はリアクティブな値であるため、依存値のリストから取り除くことはできません。リンタはそれを許可しません。\n\n```js {8}\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, []); // 🔴 React Hook useEffect has a missing dependency: 'roomId'\n  // ...\n}\n```\n\nそして、リンタは正しいのです！ `roomId` は時間とともに変化する可能性があるので、コードにバグが発生する可能性があります。\n\n**依存値を削除するには、それが依存値である*必要がない*ことをリンタに「証明」します**。例えば、`roomId` をコンポーネントの外に移動すれば、リアクティブではなく再レンダー時に変更されない、ということを証明できます。\n\n```js {2,9}\nconst serverUrl = 'https://localhost:1234';\nconst roomId = 'music'; // Not a reactive value anymore\n\nfunction ChatRoom() {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, []); // ✅ All dependencies declared\n  // ...\n}\n```\n\nこれでもう `roomId` はリアクティブな値ではなくなった（再レンダー時に変更されない）ため、依存値にする必要はなくなります。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\nconst roomId = 'music';\n\nexport default function ChatRoom() {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, []);\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\nこれで、[依存配列を空 (`[]`) にする](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means)ことができるようになります。エフェクトはリアクティブな値にもはや*依存していない*ので、コンポーネントの props や state が変更されたときに再実行する必要は*確かにない*のです。\n\n### 依存配列を変更したければコードを変更する {/*to-change-the-dependencies-change-the-code*/}\n\nワークフローにパターンがあることに気付いたかもしれません。\n\n1. まず、エフェクトのコードやリアクティブな値の宣言部分を**変更**してみる。\n2. 次にリンタに指摘されたとおり、**変更したコードに合わせるように**依存配列を調整する。\n3. その依存配列に満足できない場合は、**最初のステップに戻る**（コードを再度変更する）。\n\n最後の部分が重要です。**依存配列を変更したい場合は、まず周囲のコードを変更してください**。依存配列は、[エフェクトのコードで使用されるすべてのリアクティブな値のリスト](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency)と考えることができます。あなたがリストに何を載せるか*選ぶ*のではありません。リストはあなたのコードの*説明書き*に過ぎません。依存値のリストを変更したくなったら、コードの方を変更してください。\n\nこれは方程式を解くような感覚かもしれません。目標（例えば、ある依存値を削除すること）から始めて、その目標に合ったコードを「見つける」必要があります。方程式を解くことが楽しいと思わない人もいるでしょうし、エフェクトを書く場合でも同じでしょう！ 幸い、以下に試すことができる一般的なレシピのリストがあります。\n\n<Pitfall>\n\n既存のコードベースがある場合、次のようにリンタを黙らせているエフェクトがあるかもしれません。\n\n```js {3-4}\nuseEffect(() => {\n  // ...\n  // 🔴 Avoid suppressing the linter like this:\n  // eslint-ignore-next-line react-hooks/exhaustive-deps\n}, []);\n```\n\n**依存配列がコードと一致しない場合、バグが発生するリスクが非常に高くなります**。リンタを止めることで、エフェクトが依存する値について React に「嘘」をついていることになります。\n\n代わりに、以下にある手法を使用してください。\n\n</Pitfall>\n\n<DeepDive>\n\n#### 依存値のリンタを止めてしまうことがなぜ危険なのか？ {/*why-is-suppressing-the-dependency-linter-so-dangerous*/}\n\nこのリンタを止めてしまうと、見つけたり修正したりするのが難しい、非常に分かりづらいバグの原因になります。一例を示しましょう。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [14]}}\nimport { useState, useEffect } from 'react';\n\nexport default function Timer() {\n  const [count, setCount] = useState(0);\n  const [increment, setIncrement] = useState(1);\n\n  function onTick() {\n\tsetCount(count + increment);\n  }\n\n  useEffect(() => {\n    const id = setInterval(onTick, 1000);\n    return () => clearInterval(id);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  return (\n    <>\n      <h1>\n        Counter: {count}\n        <button onClick={() => setCount(0)}>Reset</button>\n      </h1>\n      <hr />\n      <p>\n        Every second, increment by:\n        <button disabled={increment === 0} onClick={() => {\n          setIncrement(i => i - 1);\n        }}>–</button>\n        <b>{increment}</b>\n        <button onClick={() => {\n          setIncrement(i => i + 1);\n        }}>+</button>\n      </p>\n    </>\n  );\n}\n```\n\n```css\nbutton { margin: 10px; }\n```\n\n</Sandpack>\n\nたとえば、エフェクトを「マウント時にのみ」実行したいとします。あなたは[空の (`[]`) 依存配列](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means)を使えばよいとどこかで読んだので、リンタを無視して強制的に `[]` を依存配列として指定することにしました。\n\nこのカウンタは毎秒、2 つのボタンにより指定される数だけインクリメントするはずです。しかし、このエフェクトは何にも依存していないと React に「嘘」をついたため、React は初期レンダー時の `onTick` 関数を永遠に使用し続けます。[そのレンダー中](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time)には `count` は `0` で、`increment` は `1` でした。従って、そのレンダー時に作られた `onTick` は毎秒 `setCount(0 + 1)` を呼び出し、常に `1` を表示することになります。このようなバグは、複数のコンポーネントにまたがっている場合、修正がより困難になります。\n\nリンタを無視するよりも良い解決策は常に存在します！ このコードを修正するには、`onTick` を依存値のリストに追加する必要があります。（インターバルが一度だけ設定されるように、[`onTick` をエフェクトイベント (Effect Event) にします。](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events)）\n\n**依存配列に関するリントエラーはコンパイルエラーとして扱うことをお勧めします。リンタを抑制しなければ、このようなバグには決して遭遇せずに済みます**。このページの残りの部分で、このようなケースや他のケースで代わりに行える対応策を説明します。\n\n</DeepDive>\n\n## 不要な依存値を取り除く {/*removing-unnecessary-dependencies*/}\n\nエフェクトの依存配列をコードに合わせて調整するたびに、そのリストの中身を見るようにしてください。依存値のどれかが変更されたときにエフェクトを再実行することは、理にかなっていますか？ 時々、答えは「いいえ」でしょう。\n\n* エフェクトの*異なる部分*を異なる条件で再実行したい。\n* 依存値の*最新の値*を読み取りたいだけで、その変化に「反応」したいわけではない。\n* 依存値の型がオブジェクトや関数であるために、*意図せずに*頻繁に変更される。\n\n適切な解決策を見つけるためには、あなたのエフェクトに関するいくつかの質問に答える必要があります。それらを見ていきましょう。\n\n### コードをイベントハンドラに移動すべきでは？ {/*should-this-code-move-to-an-event-handler*/}\n\nまず考える必要があるのは、そのコードがそもそもエフェクトであるべきかどうかです。\n\nフォームを想像してください。送信時に、`submitted` という state 変数を `true` に設定します。POST リクエストを送信してから通知を表示する必要があります。`submitted` が `true` になることに「反応」するエフェクトの中に、以下のようなロジックを入れました。\n\n```js {6-8}\nfunction Form() {\n  const [submitted, setSubmitted] = useState(false);\n\n  useEffect(() => {\n    if (submitted) {\n      // 🔴 Avoid: Event-specific logic inside an Effect\n      post('/api/register');\n      showNotification('Successfully registered!');\n    }\n  }, [submitted]);\n\n  function handleSubmit() {\n    setSubmitted(true);\n  }\n\n  // ...\n}\n```\n\n後で、現在のテーマに応じて通知メッセージにスタイルを適用するために、現在のテーマを読み取ることにしました。`theme` はコンポーネント本体で宣言されているためリアクティブな値であり、依存値として追加することになります。\n\n```js {3,9,11}\nfunction Form() {\n  const [submitted, setSubmitted] = useState(false);\n  const theme = useContext(ThemeContext);\n\n  useEffect(() => {\n    if (submitted) {\n      // 🔴 Avoid: Event-specific logic inside an Effect\n      post('/api/register');\n      showNotification('Successfully registered!', theme);\n    }\n  }, [submitted, theme]); // ✅ All dependencies declared\n\n  function handleSubmit() {\n    setSubmitted(true);\n  }  \n\n  // ...\n}\n```\n\nしかしこれによりバグを発生させてしまいました。まずフォームを送信してから、ダークテーマとライトテーマを切り替えるところを想像してください。`theme` が変更されることによりエフェクトが再実行されるため、同じ通知が再度表示されます！\n\n**ここでの問題は、そもそもこれがエフェクトであるべきではなかったということです**。この POST リクエストを送信して通知を表示することは、*フォームの送信*という特定のユーザ操作に対応しています。特定のユーザ操作に対応してコードを実行する場合は、そのロジックを対応するイベントハンドラに直接配置してください。\n\n```js {6-7}\nfunction Form() {\n  const theme = useContext(ThemeContext);\n\n  function handleSubmit() {\n    // ✅ Good: Event-specific logic is called from event handlers\n    post('/api/register');\n    showNotification('Successfully registered!', theme);\n  }  \n\n  // ...\n}\n```\n\nコードがイベントハンドラにあるため、リアクティブではなくなります。つまり、ユーザがフォームを送信したときにのみ実行されます。詳しくは、[イベントハンドラとエフェクトの選択](/learn/separating-events-from-effects#reactive-values-and-reactive-logic)や[不要なエフェクトの削除](/learn/you-might-not-need-an-effect)を参照してください。\n\n### エフェクトが複数の互いに無関係なことを行っていないか？ {/*is-your-effect-doing-several-unrelated-things*/}\n\n次に自分に問うべき質問は、エフェクトが複数の関連性のないことを行っていないかどうかです。\n\n例えば、ユーザに都市とエリアを選択させる配送フォームを作成しているとします。選択された `country` に応じてサーバから `cities` のリストを取得し、ドロップダウンで表示します。\n\n```js\nfunction ShippingForm({ country }) {\n  const [cities, setCities] = useState(null);\n  const [city, setCity] = useState(null);\n\n  useEffect(() => {\n    let ignore = false;\n    fetch(`/api/cities?country=${country}`)\n      .then(response => response.json())\n      .then(json => {\n        if (!ignore) {\n          setCities(json);\n        }\n      });\n    return () => {\n      ignore = true;\n    };\n  }, [country]); // ✅ All dependencies declared\n\n  // ...\n```\n\nこれは、[エフェクトでデータを取得する](/learn/you-might-not-need-an-effect#fetching-data)良い例です。`country` プロパティに応じて、`cities` という state をネットワークと同期させています。データ取得は `ShippingForm` が表示されるとすぐに、かつ（どんなユーザ操作が原因であれ）`country` が変更されるたびに行う必要があるので、イベントハンドラで行うことはできません。\n\nさて、現在選択されている `city` に対応する `areas` を取得するための、2 つ目のセレクトボックスを追加しようとしているとしましょう。同じエフェクトの中に、エリアの一覧を取得するための 2 つ目の `fetch` コールを追加するところから取りかかってしまうかもしれません。\n\n```js {15-24,28}\nfunction ShippingForm({ country }) {\n  const [cities, setCities] = useState(null);\n  const [city, setCity] = useState(null);\n  const [areas, setAreas] = useState(null);\n\n  useEffect(() => {\n    let ignore = false;\n    fetch(`/api/cities?country=${country}`)\n      .then(response => response.json())\n      .then(json => {\n        if (!ignore) {\n          setCities(json);\n        }\n      });\n    // 🔴 Avoid: A single Effect synchronizes two independent processes\n    if (city) {\n      fetch(`/api/areas?city=${city}`)\n        .then(response => response.json())\n        .then(json => {\n          if (!ignore) {\n            setAreas(json);\n          }\n        });\n    }\n    return () => {\n      ignore = true;\n    };\n  }, [country, city]); // ✅ All dependencies declared\n\n  // ...\n```\n\nしかし、エフェクトが `city` という state 変数も使用するようになったため、依存値のリストに `city` を追加する必要がありました。それが問題を引き起こします。ユーザが別の都市を選択するとエフェクトが再実行され、`fetchCities(country)` が呼び出されます。その結果、都市のリストを何度も不必要に取得することになります。\n\n**このコードの問題は、2 つの互いに関連性のないことに関して同期を行っていることです**。\n\n1. props である `country` に基づいて、`cities` state をネットワークと同期させたい。\n1. state である `city` に基づいて、`areas` state をネットワークと同期させたい。\n\nロジックを 2 つのエフェクトに分割して、それぞれが同期する必要がある値にのみ反応するようにします。\n\n```js {19-33}\nfunction ShippingForm({ country }) {\n  const [cities, setCities] = useState(null);\n  useEffect(() => {\n    let ignore = false;\n    fetch(`/api/cities?country=${country}`)\n      .then(response => response.json())\n      .then(json => {\n        if (!ignore) {\n          setCities(json);\n        }\n      });\n    return () => {\n      ignore = true;\n    };\n  }, [country]); // ✅ All dependencies declared\n\n  const [city, setCity] = useState(null);\n  const [areas, setAreas] = useState(null);\n  useEffect(() => {\n    if (city) {\n      let ignore = false;\n      fetch(`/api/areas?city=${city}`)\n        .then(response => response.json())\n        .then(json => {\n          if (!ignore) {\n            setAreas(json);\n          }\n        });\n      return () => {\n        ignore = true;\n      };\n    }\n  }, [city]); // ✅ All dependencies declared\n\n  // ...\n```\n\nこれで、1 番目のエフェクトは `country` が変更された場合にのみ再実行され、2 番目のエフェクトは `city` が変更された場合にのみ再実行されます。目的別に分けたことで、2 つの異なるものが、2 つの別々のエフェクトによって同期されるようになりました。2 つのエフェクトが 2 つの別の依存配列を有しているので、互いを意図せずにトリガしてしまうことはありません。\n\n最終的なコードは元のコードよりも長くなりますが、これらのエフェクトを分割することは正当です。[各エフェクトは独立した同期プロセスを表すべきです](/learn/lifecycle-of-reactive-effects#each-effect-represents-a-separate-synchronization-process)。この例では、一方のエフェクトを削除しても、もう一方のエフェクトのロジックが壊れることはありません。つまりそれらは*異なるものを同期している*ということであり、分割することは良いことです。コードの重複が気になる場合は、[カスタムフックに繰り返しのロジックを抽出](/learn/reusing-logic-with-custom-hooks#when-to-use-custom-hooks)することで改善できます。\n\n### state の読み取りは次の state を計算するためか？ {/*are-you-reading-some-state-to-calculate-the-next-state*/}\n\n以下のエフェクトは、新しいメッセージが届くたびに、新しい配列を作って `messages` という state にセットしています。\n\n```js {2,6-8}\nfunction ChatRoom({ roomId }) {\n  const [messages, setMessages] = useState([]);\n  useEffect(() => {\n    const connection = createConnection();\n    connection.connect();\n    connection.on('message', (receivedMessage) => {\n      setMessages([...messages, receivedMessage]);\n    });\n    // ...\n```\n\n既存のすべてのメッセージから始まる[新たな配列を作成する](/learn/updating-arrays-in-state)部分で `messages` 変数を使っており、新しいメッセージを最後に追加しています。しかし、`messages` はエフェクトによって読み取られるリアクティブな値であるため、依存値でなければなりません。\n\n```js {7,10}\nfunction ChatRoom({ roomId }) {\n  const [messages, setMessages] = useState([]);\n  useEffect(() => {\n    const connection = createConnection();\n    connection.connect();\n    connection.on('message', (receivedMessage) => {\n      setMessages([...messages, receivedMessage]);\n    });\n    return () => connection.disconnect();\n  }, [roomId, messages]); // ✅ All dependencies declared\n  // ...\n```\n\nそして `messages` を依存値にすることで問題が発生してしまいます。\n\nメッセージを受信するたびに、`setMessages()` は受信したメッセージを含む新しい `messages` 配列でコンポーネントを再レンダーさせます。しかしこのエフェクトは `messages` に依存するようになったため、これによってエフェクトの再同期も発生します。そのため新しいメッセージが届くたびにチャットが再接続されます。これはユーザにとって望ましくありません！\n\n問題を解決するには、エフェクト内で `messages` を読み取らないようにします。代わりに、`setMessages` に[更新用関数](/reference/react/useState#updating-state-based-on-the-previous-state)を渡します。\n\n```js {7,10}\nfunction ChatRoom({ roomId }) {\n  const [messages, setMessages] = useState([]);\n  useEffect(() => {\n    const connection = createConnection();\n    connection.connect();\n    connection.on('message', (receivedMessage) => {\n      setMessages(msgs => [...msgs, receivedMessage]);\n    });\n    return () => connection.disconnect();\n  }, [roomId]); // ✅ All dependencies declared\n  // ...\n```\n\n**エフェクトが `messages` 変数を一切読み取らなくなっていることに注目してください**。`msgs => [...msgs, receivedMessage]` のような更新用関数を渡すだけで構いません。React は[更新用関数をキューに入れ](/learn/queueing-a-series-of-state-updates)、次のレンダー時に `msgs` 引数に値を渡します。ですのでエフェクト自体はもう `messages` に依存する必要がなくなっています。この修正により、チャットメッセージを受信してもチャットが再接続されることはなくなります。\n\n### 変更に「反応」せず値を読み出したいだけか？ {/*do-you-want-to-read-a-value-without-reacting-to-its-changes*/}\n\n`isMuted` が `true` でない場合に限り、ユーザが新しいメッセージを受信したときに音を再生したいとします。\n\n```js {3,10-12}\nfunction ChatRoom({ roomId }) {\n  const [messages, setMessages] = useState([]);\n  const [isMuted, setIsMuted] = useState(false);\n\n  useEffect(() => {\n    const connection = createConnection();\n    connection.connect();\n    connection.on('message', (receivedMessage) => {\n      setMessages(msgs => [...msgs, receivedMessage]);\n      if (!isMuted) {\n        playSound();\n      }\n    });\n    // ...\n```\n\nエフェクトが `isMuted` をコード内で使用するようになったので、依存配列に追加する必要があります。\n\n```js {10,15}\nfunction ChatRoom({ roomId }) {\n  const [messages, setMessages] = useState([]);\n  const [isMuted, setIsMuted] = useState(false);\n\n  useEffect(() => {\n    const connection = createConnection();\n    connection.connect();\n    connection.on('message', (receivedMessage) => {\n      setMessages(msgs => [...msgs, receivedMessage]);\n      if (!isMuted) {\n        playSound();\n      }\n    });\n    return () => connection.disconnect();\n  }, [roomId, isMuted]); // ✅ All dependencies declared\n  // ...\n```\n\n問題は、（ユーザが \"Muted\" トグルボタンを押すなどで）`isMuted` が変更されるたびに、エフェクトが再同期され、チャットに再接続されることです。これは望ましいユーザ体験ではありません！（この例では、単にリンタを無効にしてもうまくいきません。そうすると `isMuted` が古い値のまま「固定」されてしまいます。）\n\nこの問題を解決するためには、リアクティブではないロジックをエフェクトの外部に取り出す必要があります。`isMuted` の変更に対してこのエフェクトを「反応」させたくありません。そこで[リアクティブではないロジックを、エフェクトイベントに移動します](/learn/separating-events-from-effects#declaring-an-effect-event)。\n\n```js {1,7-12,18,21}\nimport { useState, useEffect, useEffectEvent } from 'react';\n\nfunction ChatRoom({ roomId }) {\n  const [messages, setMessages] = useState([]);\n  const [isMuted, setIsMuted] = useState(false);\n\n  const onMessage = useEffectEvent(receivedMessage => {\n    setMessages(msgs => [...msgs, receivedMessage]);\n    if (!isMuted) {\n      playSound();\n    }\n  });\n\n  useEffect(() => {\n    const connection = createConnection();\n    connection.connect();\n    connection.on('message', (receivedMessage) => {\n      onMessage(receivedMessage);\n    });\n    return () => connection.disconnect();\n  }, [roomId]); // ✅ All dependencies declared\n  // ...\n```\n\nエフェクトイベントを使うことで、エフェクトをリアクティブな部分（`roomId` のようなリアクティブな値とその変更に「反応」する部分）と、リアクティブではない部分（`onMessage` が `isMuted` を読むような、最新の値だけを読みとる部分）に分割できます。**`isMuted` はエフェクトイベント内で読みとられるようになったので、エフェクトの依存値である必要がなくなります**。その結果、「ミュート」設定をオン・オフしてもチャットが再接続されなくなり、一件落着となります！\n\n#### props から来るイベントハンドラをラップ {/*wrapping-an-event-handler-from-the-props*/}\n\n似た問題は、コンポーネントがイベントハンドラを props として受け取る場合にも発生することがあります。\n\n```js {1,8,11}\nfunction ChatRoom({ roomId, onReceiveMessage }) {\n  const [messages, setMessages] = useState([]);\n\n  useEffect(() => {\n    const connection = createConnection();\n    connection.connect();\n    connection.on('message', (receivedMessage) => {\n      onReceiveMessage(receivedMessage);\n    });\n    return () => connection.disconnect();\n  }, [roomId, onReceiveMessage]); // ✅ All dependencies declared\n  // ...\n```\n\nここで親コンポーネントが毎回のレンダーで*異なる* `onReceiveMessage` 関数を渡してくる場合を考えましょう。\n\n```js {3-5}\n<ChatRoom\n  roomId={roomId}\n  onReceiveMessage={receivedMessage => {\n    // ...\n  }}\n/>\n```\n\n`onReceiveMessage` は依存値なので、親の再レンダー後に毎回エフェクトが再同期されることになります。これにより毎回チャットが再接続されてしまいます。これを解消するために、エフェクトイベントでこの関数の呼び出しをラップします。\n\n```js {4-6,12,15}\nfunction ChatRoom({ roomId, onReceiveMessage }) {\n  const [messages, setMessages] = useState([]);\n\n  const onMessage = useEffectEvent(receivedMessage => {\n    onReceiveMessage(receivedMessage);\n  });\n\n  useEffect(() => {\n    const connection = createConnection();\n    connection.connect();\n    connection.on('message', (receivedMessage) => {\n      onMessage(receivedMessage);\n    });\n    return () => connection.disconnect();\n  }, [roomId]); // ✅ All dependencies declared\n  // ...\n```\n\nエフェクトイベントはリアクティブではないため、依存値として指定する必要がなくなります。結果的に、親コンポーネントが毎回の再レンダー時に異なる関数を渡してきた場合でも、チャットが再接続されることはなくなります。\n\n#### リアクティブなコードと非リアクティブなコードの分離 {/*separating-reactive-and-non-reactive-code*/}\n\nこの例では、`roomId` が変更されるたびに訪問をログに記録したいとします。ログには現在の `notificationCount` の値を含めたいものの、`notificationCount` の変更によってログの記録を発生させたくはありません。\n\n今回も解決策は、非リアクティブなコードをエフェクトイベントに分離することです。\n\n```js {2-4,7}\nfunction Chat({ roomId, notificationCount }) {\n  const onVisit = useEffectEvent(visitedRoomId => {\n    logVisit(visitedRoomId, notificationCount);\n  });\n\n  useEffect(() => {\n    onVisit(roomId);\n  }, [roomId]); // ✅ All dependencies declared\n  // ...\n}\n```\n\n`roomId` に対してロジックをリアクティブにしたいので、エフェクト内で `roomId` を読み取るようにします。一方で `notificationCount` の変更によって余分な訪問ログを記録したくないので、`notificationCount` の値はエフェクトイベント内で読み取ります。[エフェクトイベントを使用してエフェクトから最新の props と state を読む方法について詳しく学ぶ。](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events)\n\n### リアクティブな値が意図せず変更されていないか？ {/*does-some-reactive-value-change-unintentionally*/}\n\n場合によっては、特定の値に対してエフェクトが「リアクティブ」で*あってはほしい*が、ユーザ視点からの実質的な変化がないのに値が頻繁に変わりすぎてしまう、ということがあります。例えば、コンポーネントの本体で `options` オブジェクトを作成し、そのオブジェクトをエフェクト内で読み取っているとしましょう。\n\n```js {3-6,9}\nfunction ChatRoom({ roomId }) {\n  // ...\n  const options = {\n    serverUrl: serverUrl,\n    roomId: roomId\n  };\n\n  useEffect(() => {\n    const connection = createConnection(options);\n    connection.connect();\n    // ...\n```\n\nこのオブジェクトはコンポーネントの本体内で宣言されているため、[リアクティブな値](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values)です。エフェクト内でこのようなリアクティブな値を読み取る場合は依存値として宣言する必要があります。これにより、エフェクトがその変更に「反応」するようになります。\n\n```js {3,6}\n  // ...\n  useEffect(() => {\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [options]); // ✅ All dependencies declared\n  // ...\n```\n\n依存値として宣言することは重要です！ これにより、例えば `roomId` が変更された場合に、エフェクトが新しい `options` でチャットに再接続することが保証されます。ただし上記のコードには問題もあります。これを確認するため、以下のサンドボックスの入力欄に入力して、コンソールで何が起こるかを見てみましょう。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [10]}}\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  // Temporarily disable the linter to demonstrate the problem\n  // eslint-disable-next-line react-hooks/exhaustive-deps\n  const options = {\n    serverUrl: serverUrl,\n    roomId: roomId\n  };\n\n  useEffect(() => {\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [options]);\n\n  return (\n    <>\n      <h1>Welcome to the {roomId} room!</h1>\n      <input value={message} onChange={e => setMessage(e.target.value)} />\n    </>\n  );\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom roomId={roomId} />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n上記のサンドボックスでは、入力欄は `message` という state 変数のみを更新しています。ユーザ視点からすると、これがチャットの接続に影響を与えるべきではありません。しかし、`message` を更新するたびに、コンポーネントは再レンダーされます。コンポーネントは再レンダーされるたびに、その内部のコードが最初から再実行されます。\n\n`ChatRoom` コンポーネントの再レンダーごとに、新しい `options` オブジェクトがゼロから再作成されます。React は、`options` オブジェクトが前回のレンダー時に作成された `options` オブジェクトとは*異なるオブジェクト*であると認識します。従って、（`options` に依存する）エフェクトの再同期が発生し、タイピングによりチャットの再接続が発生してしまいます。\n\n**この問題はオブジェクトと関数にのみ影響します。JavaScript では、新しく作成されたオブジェクトや関数は、他のすべてのオブジェクトや関数とは異なると見なされます。中身が同じであっても関係ありません！**\n\n```js {7-8}\n// During the first render\nconst options1 = { serverUrl: 'https://localhost:1234', roomId: 'music' };\n\n// During the next render\nconst options2 = { serverUrl: 'https://localhost:1234', roomId: 'music' };\n\n// These are two different objects!\nconsole.log(Object.is(options1, options2)); // false\n```\n\n**オブジェクト型や関数型の依存値は、エフェクトが必要以上に再同期される原因となります**。\n\nしたがって、エフェクトの依存値としてのオブジェクトや関数は、可能な限り避けるべきです。代わりに、それらをコンポーネントの外側やエフェクトの内側に移動させるか、あるいはそれらからプリミティブな値を抽出するよう試みてください。\n\n#### 静的なオブジェクトや関数をコンポーネント外に移動 {/*move-static-objects-and-functions-outside-your-component*/}\n\nオブジェクトが props や state に依存しない場合、そのオブジェクトをコンポーネントの外に移動できます。\n\n```js {1-4,13}\nconst options = {\n  serverUrl: 'https://localhost:1234',\n  roomId: 'music'\n};\n\nfunction ChatRoom() {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, []); // ✅ All dependencies declared\n  // ...\n```\n\nこれにより、それがリアクティブでないことをリンタに対して*証明*できます。再レンダーの結果として変更されることがないため、依存値にする必要がありません。これにより、`ChatRoom` を再レンダーしても、エフェクトが再同期されることはなくなります。\n\nこれは関数でも同様です。\n\n```js {1-6,12}\nfunction createOptions() {\n  return {\n    serverUrl: 'https://localhost:1234',\n    roomId: 'music'\n  };\n}\n\nfunction ChatRoom() {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const options = createOptions();\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, []); // ✅ All dependencies declared\n  // ...\n```\n\n`createOptions` はコンポーネントの外で宣言されているため、リアクティブな値ではありません。したがって、エフェクトの依存値に指定する必要はありませんし、この関数がエフェクトの再同期を発生させることも決してありません。\n\n#### リアクティブなオブジェクトや関数をエフェクト内部に移動 {/*move-dynamic-objects-and-functions-inside-your-effect*/}\n\nオブジェクトが再レンダーの結果として変更される可能性があるリアクティブな値（例：props としての `roomId`）に依存している場合、コンポーネントの*外側*に引っ張り出すことはできません。しかしそれを作成するコードをエフェクトのコード*内部*に移動させることは可能です。\n\n```js {7-10,11,14}\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const options = {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]); // ✅ All dependencies declared\n  // ...\n```\n\n`options` はエフェクトの内部で宣言されているため、エフェクトの依存値ではなくなります。代わりにエフェクトで使用される唯一のリアクティブな値は `roomId` となります。`roomId` はオブジェクトや関数ではないため、*意図せず*変わってしまうことはありません。JavaScript では、数値や文字列は内容によって比較されます。\n\n```js {7-8}\n// During the first render\nconst roomId1 = 'music';\n\n// During the next render\nconst roomId2 = 'music';\n\n// These two strings are the same!\nconsole.log(Object.is(roomId1, roomId2)); // true\n```\n\nこの修正により、入力欄を編集してもチャットの再接続は起こらなくなります。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const options = {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n\n  return (\n    <>\n      <h1>Welcome to the {roomId} room!</h1>\n      <input value={message} onChange={e => setMessage(e.target.value)} />\n    </>\n  );\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom roomId={roomId} />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\nただし `roomId` ドロップダウンを変更すると、期待通り再接続が発生します。\n\nこれは関数の場合でも同様です。\n\n```js {7-12,14}\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    function createOptions() {\n      return {\n        serverUrl: serverUrl,\n        roomId: roomId\n      };\n    }\n\n    const options = createOptions();\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]); // ✅ All dependencies declared\n  // ...\n```\n\nエフェクト内でロジックの一部をグループ化するための独自の関数を作成できます。エフェクトの*内部*で宣言している限り、それらはリアクティブな値ではないので、エフェクトの依存値にする必要はありません。\n\n#### オブジェクトからプリミティブ値を読み取る {/*read-primitive-values-from-objects*/}\n\nprops からオブジェクトを受け取ることがあります。\n\n```js {1,5,8}\nfunction ChatRoom({ options }) {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [options]); // ✅ All dependencies declared\n  // ...\n```\n\nしかし、親コンポーネントがオブジェクト作成をレンダー中に行っているかもしれないという心配があります。\n\n```js {3-6}\n<ChatRoom\n  roomId={roomId}\n  options={{\n    serverUrl: serverUrl,\n    roomId: roomId\n  }}\n/>\n```\n\nこれにより、親コンポーネントの再レンダーのたびに、エフェクトによる再接続が発生してしまいます。これを修正するには、エフェクトの*外側*でオブジェクトから情報を読み取っておき、オブジェクトや関数自体を依存値として持たせないようにします。\n\n```js {4,7-8,12}\nfunction ChatRoom({ options }) {\n  const [message, setMessage] = useState('');\n\n  const { roomId, serverUrl } = options;\n  useEffect(() => {\n    const connection = createConnection({\n      roomId: roomId,\n      serverUrl: serverUrl\n    });\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId, serverUrl]); // ✅ All dependencies declared\n  // ...\n```\n\nロジックは少し繰り返しになります（エフェクト外でオブジェクトから値を読み取り、エフェクト内で同じ値を持つオブジェクトを作成している）。しかし、エフェクトが*実際に*依存している情報が何なのかが、非常に明確になります。親コンポーネントが誤ってオブジェクトを再作成している場合でも、チャットの再接続は起こりません。ですが `options.roomId` や `options.serverUrl` が実際に異なる場合は、チャットが再接続されます。\n\n#### 関数からプリミティブ値を計算する {/*calculate-primitive-values-from-functions*/}\n\n同じアプローチは関数にも適用できます。例えば、親コンポーネントが関数を渡してくる場合を考えてみましょう。\n\n```js {3-8}\n<ChatRoom\n  roomId={roomId}\n  getOptions={() => {\n    return {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n  }}\n/>\n```\n\nこれを依存値にしない（再レンダー時の再接続を防ぐ）ために、エフェクトの外側でそれを呼び出します。これによりエフェクト内で読み取ることができる、オブジェクトではない `roomId` と `serverUrl` の値が得られます。\n\n```js {1,4}\nfunction ChatRoom({ getOptions }) {\n  const [message, setMessage] = useState('');\n\n  const { roomId, serverUrl } = getOptions();\n  useEffect(() => {\n    const connection = createConnection({\n      roomId: roomId,\n      serverUrl: serverUrl\n    });\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId, serverUrl]); // ✅ All dependencies declared\n  // ...\n```\n\nこれは、レンダー中に呼び出しても安全な[純粋](/learn/keeping-components-pure)な関数に対してのみ機能します。関数がイベントハンドラであり、その変更がエフェクトの再同期を引き起こさないようにしたい場合は、[エフェクトイベントにラップしてください](#do-you-want-to-read-a-value-without-reacting-to-its-changes)。\n\n<Recap>\n\n- 依存配列は常にコードと一致する必要がある。\n- 依存配列が気に入らない場合、編集する必要があるのはコードの方である。\n- リンタを抑制すると非常にわかりにくいバグが発生するため、いかなる場合も避けるべきである。\n- 依存値を削除するには、リンタにそれが不要であることを「証明」する必要がある。\n- 特定のユーザ操作に応答してコードを実行する必要がある場合は、そのコードをイベントハンドラに移動する。\n- エフェクトの異なる部分が異なる理由で再実行される必要がある場合は、複数のエフェクトに分割する。\n- 前の state に基づいて state を更新したい場合は、更新用関数を渡す。\n- 最新の値を読み取りたいがそれに「反応」したくない場合、エフェクトからエフェクトイベントを抽出する。\n- JavaScript では、オブジェクトや関数は、異なるタイミングで作成された場合、異なる値だと見なされる。\n- オブジェクト型や関数型の依存値はなるべく避けるようにする。コンポーネントの外側かエフェクトの内側に移動させる。\n\n</Recap>\n\n<Challenges>\n\n#### インターバルがリセットされる問題を修正 {/*fix-a-resetting-interval*/}\n\nこのエフェクトは 1 秒ごとに発火するインターバルをセットアップしています。しかし何かがおかしいことに気付きました。インターバルが発火するたびに破棄され、再作成されているようです。インターバルが何度も再作成されないようにコードを修正してください。\n\n<Hint>\n\nこのエフェクトのコードは `count` に依存しているようです。この依存値の必要性をなくす方法はないでしょうか？ `count` の値に依存せず、前回の値に基づいて state を更新する方法があるはずです。\n\n</Hint>\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function Timer() {\n  const [count, setCount] = useState(0);\n\n  useEffect(() => {\n    console.log('✅ Creating an interval');\n    const id = setInterval(() => {\n      console.log('⏰ Interval tick');\n      setCount(count + 1);\n    }, 1000);\n    return () => {\n      console.log('❌ Clearing an interval');\n      clearInterval(id);\n    };\n  }, [count]);\n\n  return <h1>Counter: {count}</h1>\n}\n```\n\n</Sandpack>\n\n<Solution>\n\nエフェクトの中で `count` の state を `count + 1` に更新したいと考えています。しかし、これによりエフェクトが `count` に依存することになります。`count` は毎回更新されるため、インターバルも毎回再作成されることになります。\n\nこれを解決するには、[更新用関数](/reference/react/useState#updating-state-based-on-the-previous-state)を使い、`setCount(count + 1)` ではなく `setCount(c => c + 1)` と書くようにします。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function Timer() {\n  const [count, setCount] = useState(0);\n\n  useEffect(() => {\n    console.log('✅ Creating an interval');\n    const id = setInterval(() => {\n      console.log('⏰ Interval tick');\n      setCount(c => c + 1);\n    }, 1000);\n    return () => {\n      console.log('❌ Clearing an interval');\n      clearInterval(id);\n    };\n  }, []);\n\n  return <h1>Counter: {count}</h1>\n}\n```\n\n</Sandpack>\n\nエフェクトの中で `count` を読み取る代わりに、`c => c + 1` という指示（「この数値をインクリメントせよ」）を React に渡します。React は次のレンダー時にそれを適用します。エフェクトの中で `count` の値を読み取る必要がなくなったので、エフェクトの依存配列を空 (`[]`) に保つことができます。これにより、エフェクトが毎秒インターバルを再作成するのを防ぐことができます。\n\n</Solution>\n\n#### アニメーションの再トリガを修正 {/*fix-a-retriggering-animation*/}\n\nこの例では、\"Show\" を押すとウェルカムメッセージがフェードインします。アニメーションには 1 秒かかります。\"Remove\" を押すと、ウェルカムメッセージがすぐに消えます。フェードインアニメーションのロジックは、`animation.js` ファイル内でプレーンな JavaScript [アニメーションループ](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)を使って実装されています。このロジックは変更の必要がありませんのでサードパーティのライブラリのように扱ってください。エフェクトは DOM ノードに対して `FadeInAnimation` インスタンスを作成し、アニメーションを制御するために `start(duration)` または `stop()` を呼び出します。`duration` はスライダで制御されます。スライダを調整して、アニメーションがどのように変化するかを確認してください。\n\nこのコードはすでに動作していますが、変更したい点があります。現在、`duration` state 変数を制御するスライダを動かすと、アニメーションが再トリガされてしまっています。`duration` 変数に対してエフェクトが「反応」しないよう、動作を変更してください。\"Show\" ボタンを押したときに、エフェクトはスライダで指定する現在の `duration` を使用する必要があります。しかしスライダの操作それ自体でアニメーションが再トリガされてはいけません。\n\n<Hint>\n\nエフェクト内にリアクティブにしてはいけないコードはありませんか？ エフェクトの外に非リアクティブなコードを移動するにはどうすればいいでしょうか？\n\n</Hint>\n\n<Sandpack>\n\n```js\nimport { useState, useEffect, useRef } from 'react';\nimport { useEffectEvent } from 'react';\nimport { FadeInAnimation } from './animation.js';\n\nfunction Welcome({ duration }) {\n  const ref = useRef(null);\n\n  useEffect(() => {\n    const animation = new FadeInAnimation(ref.current);\n    animation.start(duration);\n    return () => {\n      animation.stop();\n    };\n  }, [duration]);\n\n  return (\n    <h1\n      ref={ref}\n      style={{\n        opacity: 0,\n        color: 'white',\n        padding: 50,\n        textAlign: 'center',\n        fontSize: 50,\n        backgroundImage: 'radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%)'\n      }}\n    >\n      Welcome\n    </h1>\n  );\n}\n\nexport default function App() {\n  const [duration, setDuration] = useState(1000);\n  const [show, setShow] = useState(false);\n\n  return (\n    <>\n      <label>\n        <input\n          type=\"range\"\n          min=\"100\"\n          max=\"3000\"\n          value={duration}\n          onChange={e => setDuration(Number(e.target.value))}\n        />\n        <br />\n        Fade in duration: {duration} ms\n      </label>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Remove' : 'Show'}\n      </button>\n      <hr />\n      {show && <Welcome duration={duration} />}\n    </>\n  );\n}\n```\n\n```js src/animation.js\nexport class FadeInAnimation {\n  constructor(node) {\n    this.node = node;\n  }\n  start(duration) {\n    this.duration = duration;\n    if (this.duration === 0) {\n      // Jump to end immediately\n      this.onProgress(1);\n    } else {\n      this.onProgress(0);\n      // Start animating\n      this.startTime = performance.now();\n      this.frameId = requestAnimationFrame(() => this.onFrame());\n    }\n  }\n  onFrame() {\n    const timePassed = performance.now() - this.startTime;\n    const progress = Math.min(timePassed / this.duration, 1);\n    this.onProgress(progress);\n    if (progress < 1) {\n      // We still have more frames to paint\n      this.frameId = requestAnimationFrame(() => this.onFrame());\n    }\n  }\n  onProgress(progress) {\n    this.node.style.opacity = progress;\n  }\n  stop() {\n    cancelAnimationFrame(this.frameId);\n    this.startTime = null;\n    this.frameId = null;\n    this.duration = 0;\n  }\n}\n```\n\n```css\nlabel, button { display: block; margin-bottom: 20px; }\nhtml, body { min-height: 300px; }\n```\n\n</Sandpack>\n\n<Solution>\n\nエフェクトは `duration` の最新の値を読み取る必要がありますが、`duration` の変更に対して「反応」させたくありません。アニメーション開始時に `duration` を使用しますが、アニメーションがリアクティブに開始されるわけではありません。非リアクティブなコードをエフェクトイベントに抽出し、エフェクトからその関数を呼び出してください。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect, useRef } from 'react';\nimport { FadeInAnimation } from './animation.js';\nimport { useEffectEvent } from 'react';\n\nfunction Welcome({ duration }) {\n  const ref = useRef(null);\n\n  const onAppear = useEffectEvent(animation => {\n    animation.start(duration);\n  });\n\n  useEffect(() => {\n    const animation = new FadeInAnimation(ref.current);\n    onAppear(animation);\n    return () => {\n      animation.stop();\n    };\n  }, []);\n\n  return (\n    <h1\n      ref={ref}\n      style={{\n        opacity: 0,\n        color: 'white',\n        padding: 50,\n        textAlign: 'center',\n        fontSize: 50,\n        backgroundImage: 'radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%)'\n      }}\n    >\n      Welcome\n    </h1>\n  );\n}\n\nexport default function App() {\n  const [duration, setDuration] = useState(1000);\n  const [show, setShow] = useState(false);\n\n  return (\n    <>\n      <label>\n        <input\n          type=\"range\"\n          min=\"100\"\n          max=\"3000\"\n          value={duration}\n          onChange={e => setDuration(Number(e.target.value))}\n        />\n        <br />\n        Fade in duration: {duration} ms\n      </label>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Remove' : 'Show'}\n      </button>\n      <hr />\n      {show && <Welcome duration={duration} />}\n    </>\n  );\n}\n```\n\n```js src/animation.js\nexport class FadeInAnimation {\n  constructor(node) {\n    this.node = node;\n  }\n  start(duration) {\n    this.duration = duration;\n    this.onProgress(0);\n    this.startTime = performance.now();\n    this.frameId = requestAnimationFrame(() => this.onFrame());\n  }\n  onFrame() {\n    const timePassed = performance.now() - this.startTime;\n    const progress = Math.min(timePassed / this.duration, 1);\n    this.onProgress(progress);\n    if (progress < 1) {\n      // We still have more frames to paint\n      this.frameId = requestAnimationFrame(() => this.onFrame());\n    }\n  }\n  onProgress(progress) {\n    this.node.style.opacity = progress;\n  }\n  stop() {\n    cancelAnimationFrame(this.frameId);\n    this.startTime = null;\n    this.frameId = null;\n    this.duration = 0;\n  }\n}\n```\n\n```css\nlabel, button { display: block; margin-bottom: 20px; }\nhtml, body { min-height: 300px; }\n```\n\n</Sandpack>\n\n`onAppear` のようなエフェクトイベントはリアクティブではないため、アニメーションを再トリガすることなく `duration` を内部で読み取ることができます。\n\n</Solution>\n\n#### チャットの再接続を修正 {/*fix-a-reconnecting-chat*/}\n\nこの例では、\"Toggle theme\" を押すたびにチャットの再接続が発生します。なぜこれが起こるのでしょうか？ サーバの URL を編集したり、別のチャットルームを選択したりしたときにのみチャットが再接続されるよう、修正してください。\n\n`chat.js` は外部のサードパーティライブラリとして扱ってください。API を確認するために参照しても構いませんが、編集はしないでください。\n\n<Hint>\n\nこれを修正する方法は複数ありますが、最終的には依存値としてオブジェクトを使わないようにする必要があります。\n\n</Hint>\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ChatRoom from './ChatRoom.js';\n\nexport default function App() {\n  const [isDark, setIsDark] = useState(false);\n  const [roomId, setRoomId] = useState('general');\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  const options = {\n    serverUrl: serverUrl,\n    roomId: roomId\n  };\n\n  return (\n    <div className={isDark ? 'dark' : 'light'}>\n      <button onClick={() => setIsDark(!isDark)}>\n        Toggle theme\n      </button>\n      <label>\n        Server URL:{' '}\n        <input\n          value={serverUrl}\n          onChange={e => setServerUrl(e.target.value)}\n        />\n      </label>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom options={options} />\n    </div>\n  );\n}\n```\n\n```js src/ChatRoom.js active\nimport { useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nexport default function ChatRoom({ options }) {\n  useEffect(() => {\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [options]);\n\n  return <h1>Welcome to the {options.roomId} room!</h1>;\n}\n```\n\n```js src/chat.js\nexport function createConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  if (typeof serverUrl !== 'string') {\n    throw Error('Expected serverUrl to be a string. Received: ' + serverUrl);\n  }\n  if (typeof roomId !== 'string') {\n    throw Error('Expected roomId to be a string. Received: ' + roomId);\n  }\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\nlabel, button { display: block; margin-bottom: 5px; }\n.dark { background: #222; color: #eee; }\n```\n\n</Sandpack>\n\n<Solution>\n\nエフェクトが再実行されるのは、`options` オブジェクトに依存しているためです。オブジェクトはうっかり再作成されることがあるため、可能な場合は常に、エフェクトの依存値としては使用しないようにしましょう。\n\n最も影響範囲の小さい修正方法は、エフェクトの外で `roomId` と `serverUrl` を読み取り、エフェクトをこれらプリミティブな値（意図せず変更されることがない）に依存させることです。エフェクト内でオブジェクトを作成し、それを `createConnection` に渡します。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ChatRoom from './ChatRoom.js';\n\nexport default function App() {\n  const [isDark, setIsDark] = useState(false);\n  const [roomId, setRoomId] = useState('general');\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  const options = {\n    serverUrl: serverUrl,\n    roomId: roomId\n  };\n\n  return (\n    <div className={isDark ? 'dark' : 'light'}>\n      <button onClick={() => setIsDark(!isDark)}>\n        Toggle theme\n      </button>\n      <label>\n        Server URL:{' '}\n        <input\n          value={serverUrl}\n          onChange={e => setServerUrl(e.target.value)}\n        />\n      </label>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom options={options} />\n    </div>\n  );\n}\n```\n\n```js src/ChatRoom.js active\nimport { useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nexport default function ChatRoom({ options }) {\n  const { roomId, serverUrl } = options;\n  useEffect(() => {\n    const connection = createConnection({\n      roomId: roomId,\n      serverUrl: serverUrl\n    });\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId, serverUrl]);\n\n  return <h1>Welcome to the {options.roomId} room!</h1>;\n}\n```\n\n```js src/chat.js\nexport function createConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  if (typeof serverUrl !== 'string') {\n    throw Error('Expected serverUrl to be a string. Received: ' + serverUrl);\n  }\n  if (typeof roomId !== 'string') {\n    throw Error('Expected roomId to be a string. Received: ' + roomId);\n  }\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\nlabel, button { display: block; margin-bottom: 5px; }\n.dark { background: #222; color: #eee; }\n```\n\n</Sandpack>\n\nさらに良い方法は、オブジェクト型の props である `options` を、より具体的な `roomId` と `serverUrl` に置き換えることです。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ChatRoom from './ChatRoom.js';\n\nexport default function App() {\n  const [isDark, setIsDark] = useState(false);\n  const [roomId, setRoomId] = useState('general');\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  return (\n    <div className={isDark ? 'dark' : 'light'}>\n      <button onClick={() => setIsDark(!isDark)}>\n        Toggle theme\n      </button>\n      <label>\n        Server URL:{' '}\n        <input\n          value={serverUrl}\n          onChange={e => setServerUrl(e.target.value)}\n        />\n      </label>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n        serverUrl={serverUrl}\n      />\n    </div>\n  );\n}\n```\n\n```js src/ChatRoom.js active\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nexport default function ChatRoom({ roomId, serverUrl }) {\n  useEffect(() => {\n    const connection = createConnection({\n      roomId: roomId,\n      serverUrl: serverUrl\n    });\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId, serverUrl]);\n\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n```\n\n```js src/chat.js\nexport function createConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  if (typeof serverUrl !== 'string') {\n    throw Error('Expected serverUrl to be a string. Received: ' + serverUrl);\n  }\n  if (typeof roomId !== 'string') {\n    throw Error('Expected roomId to be a string. Received: ' + roomId);\n  }\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\nlabel, button { display: block; margin-bottom: 5px; }\n.dark { background: #222; color: #eee; }\n```\n\n</Sandpack>\n\n可能な限り props をプリミティブ値にすることで、後でコンポーネントの最適化がやりやすくなります。\n\n</Solution>\n\n#### 別のチャット再接続問題を修正 {/*fix-a-reconnecting-chat-again*/}\n\nこの例ではチャットに接続する際に暗号化 (encryption) の有無を切り替えられます。チェックボックスを切り替えて、暗号化がオンの場合とオフの場合でコンソールに表示されるメッセージが異なることを確認してください。ルームを変更してみてください。次に、テーマを切り替えてみてください。チャットルームに接続している間、数秒ごとに新しいメッセージが届きます。受信したメッセージの色が選択したテーマに一致していることを確認してください。\n\nこの例では、テーマを変更するたびにチャットが再接続されてしまっています。これを修正してください。修正後は、テーマを変更してもチャットが再接続されず、暗号化設定を切り替えたりルームを変更したりした場合にのみ再接続されるようにしてください。\n\n`chat.js` のコードは変更しないでください。振る舞いを変えない限り、それ以外のコードは変更しても構いません。例えば、どの props を渡すか変えてみると有用かもしれません。\n\n<Hint>\n\nprops として `onMessage` と `createConnection` という 2 つの関数を渡しています。どちらも `App` が再レンダーされるたびに最初から作成されます。これらは毎回新しい値と見なされるため、それが原因でエフェクトが再トリガされてしまっています。\n\nこれらの関数のうち一方は、イベントハンドラです。新しいイベントハンドラに「反応」せずに、エフェクトでイベントハンドラを呼び出す方法を知っていますか？ それが役立ちそうです！\n\nもう一方の関数は、ある state をインポートされた API メソッドに渡すためだけに存在します。この関数は本当に必要ですか？ 本質的に渡されている情報は何ですか？ `App.js` から `ChatRoom.js` にいくつかのインポートを移動する必要があるかもしれません。\n\n</Hint>\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"toastify-js\": \"1.12.0\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js src/App.js\nimport { useState } from 'react';\nimport ChatRoom from './ChatRoom.js';\nimport {\n  createEncryptedConnection,\n  createUnencryptedConnection,\n} from './chat.js';\nimport { showNotification } from './notifications.js';\n\nexport default function App() {\n  const [isDark, setIsDark] = useState(false);\n  const [roomId, setRoomId] = useState('general');\n  const [isEncrypted, setIsEncrypted] = useState(false);\n\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Use dark theme\n      </label>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isEncrypted}\n          onChange={e => setIsEncrypted(e.target.checked)}\n        />\n        Enable encryption\n      </label>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n        onMessage={msg => {\n          showNotification('New message: ' + msg, isDark ? 'dark' : 'light');\n        }}\n        createConnection={() => {\n          const options = {\n            serverUrl: 'https://localhost:1234',\n            roomId: roomId\n          };\n          if (isEncrypted) {\n            return createEncryptedConnection(options);\n          } else {\n            return createUnencryptedConnection(options);\n          }\n        }}\n      />\n    </>\n  );\n}\n```\n\n```js src/ChatRoom.js active\nimport { useState, useEffect } from 'react';\nimport { useEffectEvent } from 'react';\n\nexport default function ChatRoom({ roomId, createConnection, onMessage }) {\n  useEffect(() => {\n    const connection = createConnection();\n    connection.on('message', (msg) => onMessage(msg));\n    connection.connect();\n    return () => connection.disconnect();\n  }, [createConnection, onMessage]);\n\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n```\n\n```js src/chat.js\nexport function createEncryptedConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  if (typeof serverUrl !== 'string') {\n    throw Error('Expected serverUrl to be a string. Received: ' + serverUrl);\n  }\n  if (typeof roomId !== 'string') {\n    throw Error('Expected roomId to be a string. Received: ' + roomId);\n  }\n  let intervalId;\n  let messageCallback;\n  return {\n    connect() {\n      console.log('✅ 🔐 Connecting to \"' + roomId + '\" room... (encrypted)');\n      clearInterval(intervalId);\n      intervalId = setInterval(() => {\n        if (messageCallback) {\n          if (Math.random() > 0.5) {\n            messageCallback('hey')\n          } else {\n            messageCallback('lol');\n          }\n        }\n      }, 3000);\n    },\n    disconnect() {\n      clearInterval(intervalId);\n      messageCallback = null;\n      console.log('❌ 🔐 Disconnected from \"' + roomId + '\" room (encrypted)');\n    },\n    on(event, callback) {\n      if (messageCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'message') {\n        throw Error('Only \"message\" event is supported.');\n      }\n      messageCallback = callback;\n    },\n  };\n}\n\nexport function createUnencryptedConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  if (typeof serverUrl !== 'string') {\n    throw Error('Expected serverUrl to be a string. Received: ' + serverUrl);\n  }\n  if (typeof roomId !== 'string') {\n    throw Error('Expected roomId to be a string. Received: ' + roomId);\n  }\n  let intervalId;\n  let messageCallback;\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room (unencrypted)...');\n      clearInterval(intervalId);\n      intervalId = setInterval(() => {\n        if (messageCallback) {\n          if (Math.random() > 0.5) {\n            messageCallback('hey')\n          } else {\n            messageCallback('lol');\n          }\n        }\n      }, 3000);\n    },\n    disconnect() {\n      clearInterval(intervalId);\n      messageCallback = null;\n      console.log('❌ Disconnected from \"' + roomId + '\" room (unencrypted)');\n    },\n    on(event, callback) {\n      if (messageCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'message') {\n        throw Error('Only \"message\" event is supported.');\n      }\n      messageCallback = callback;\n    },\n  };\n}\n```\n\n```js src/notifications.js\nimport Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\n\nexport function showNotification(message, theme) {\n  Toastify({\n    text: message,\n    duration: 2000,\n    gravity: 'top',\n    position: 'right',\n    style: {\n      background: theme === 'dark' ? 'black' : 'white',\n      color: theme === 'dark' ? 'white' : 'black',\n    },\n  }).showToast();\n}\n```\n\n```css\nlabel, button { display: block; margin-bottom: 5px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n正しい解決法は複数ありますが、ここでは可能な解決策のうち 1 つを示します。\n\n元の例では、テーマを切り替えると、異なる `onMessage` と `createConnection` 関数が作成され、渡されていました。エフェクトがこれらの関数に依存していたため、テーマを切り替えるたびにチャットの再接続が起きていました。\n\n`onMessage` の問題を修正するには、エフェクトイベントにラップする必要があります。\n\n```js {1,2,6}\nexport default function ChatRoom({ roomId, createConnection, onMessage }) {\n  const onReceiveMessage = useEffectEvent(onMessage);\n\n  useEffect(() => {\n    const connection = createConnection();\n    connection.on('message', (msg) => onReceiveMessage(msg));\n    // ...\n```\n\nprops である `onMessage` とは異なり、`onReceiveMessage` エフェクトイベントはリアクティブではありません。したがってエフェクトの依存値にする必要はありません。この結果、`onMessage` が変わってもチャットの再接続が引き起こされることがなくなります。\n\n`createConnection` はリアクティブで*あるべき* ですので、同じことをやってはいけません。暗号化接続と非暗号化接続を切り替えたり、現在のルームを切り替えたりする場合、エフェクトが再トリガされることは*望ましいこと*です。しかし、`createConnection` は関数であるため、読み取り情報が*本当に*変更されたかどうかを確認することはできません。これを解決するために、`App` コンポーネントから `createConnection` を渡す代わりに、生の `roomId` と `isEncrypted` の値を渡すようにします。\n\n```js {2-3}\n      <ChatRoom\n        roomId={roomId}\n        isEncrypted={isEncrypted}\n        onMessage={msg => {\n          showNotification('New message: ' + msg, isDark ? 'dark' : 'light');\n        }}\n      />\n```\n\nこれで、`createConnection` 関数を `App` から渡すのではなく、エフェクトの*内部*に移動できます。\n\n```js {1-4,6,10-20}\nimport {\n  createEncryptedConnection,\n  createUnencryptedConnection,\n} from './chat.js';\n\nexport default function ChatRoom({ roomId, isEncrypted, onMessage }) {\n  const onReceiveMessage = useEffectEvent(onMessage);\n\n  useEffect(() => {\n    function createConnection() {\n      const options = {\n        serverUrl: 'https://localhost:1234',\n        roomId: roomId\n      };\n      if (isEncrypted) {\n        return createEncryptedConnection(options);\n      } else {\n        return createUnencryptedConnection(options);\n      }\n    }\n    // ...\n```\n\nこれらの変更の後、エフェクトはもはや関数型の値に依存しなくなります。\n\n```js {1,8,10,21}\nexport default function ChatRoom({ roomId, isEncrypted, onMessage }) { // Reactive values\n  const onReceiveMessage = useEffectEvent(onMessage); // Not reactive\n\n  useEffect(() => {\n    function createConnection() {\n      const options = {\n        serverUrl: 'https://localhost:1234',\n        roomId: roomId // Reading a reactive value\n      };\n      if (isEncrypted) { // Reading a reactive value\n        return createEncryptedConnection(options);\n      } else {\n        return createUnencryptedConnection(options);\n      }\n    }\n\n    const connection = createConnection();\n    connection.on('message', (msg) => onReceiveMessage(msg));\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId, isEncrypted]); // ✅ All dependencies declared\n```\n\n結果として、チャットは意味のあるもの（`roomId` や `isEncrypted`）が変更されたときにのみ再接続されるようになります。\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"toastify-js\": \"1.12.0\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js src/App.js\nimport { useState } from 'react';\nimport ChatRoom from './ChatRoom.js';\n\nimport { showNotification } from './notifications.js';\n\nexport default function App() {\n  const [isDark, setIsDark] = useState(false);\n  const [roomId, setRoomId] = useState('general');\n  const [isEncrypted, setIsEncrypted] = useState(false);\n\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Use dark theme\n      </label>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isEncrypted}\n          onChange={e => setIsEncrypted(e.target.checked)}\n        />\n        Enable encryption\n      </label>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n        isEncrypted={isEncrypted}\n        onMessage={msg => {\n          showNotification('New message: ' + msg, isDark ? 'dark' : 'light');\n        }}\n      />\n    </>\n  );\n}\n```\n\n```js src/ChatRoom.js active\nimport { useState, useEffect } from 'react';\nimport { useEffectEvent } from 'react';\nimport {\n  createEncryptedConnection,\n  createUnencryptedConnection,\n} from './chat.js';\n\nexport default function ChatRoom({ roomId, isEncrypted, onMessage }) {\n  const onReceiveMessage = useEffectEvent(onMessage);\n\n  useEffect(() => {\n    function createConnection() {\n      const options = {\n        serverUrl: 'https://localhost:1234',\n        roomId: roomId\n      };\n      if (isEncrypted) {\n        return createEncryptedConnection(options);\n      } else {\n        return createUnencryptedConnection(options);\n      }\n    }\n\n    const connection = createConnection();\n    connection.on('message', (msg) => onReceiveMessage(msg));\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId, isEncrypted]);\n\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n```\n\n```js src/chat.js\nexport function createEncryptedConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  if (typeof serverUrl !== 'string') {\n    throw Error('Expected serverUrl to be a string. Received: ' + serverUrl);\n  }\n  if (typeof roomId !== 'string') {\n    throw Error('Expected roomId to be a string. Received: ' + roomId);\n  }\n  let intervalId;\n  let messageCallback;\n  return {\n    connect() {\n      console.log('✅ 🔐 Connecting to \"' + roomId + '\" room... (encrypted)');\n      clearInterval(intervalId);\n      intervalId = setInterval(() => {\n        if (messageCallback) {\n          if (Math.random() > 0.5) {\n            messageCallback('hey')\n          } else {\n            messageCallback('lol');\n          }\n        }\n      }, 3000);\n    },\n    disconnect() {\n      clearInterval(intervalId);\n      messageCallback = null;\n      console.log('❌ 🔐 Disconnected from \"' + roomId + '\" room (encrypted)');\n    },\n    on(event, callback) {\n      if (messageCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'message') {\n        throw Error('Only \"message\" event is supported.');\n      }\n      messageCallback = callback;\n    },\n  };\n}\n\nexport function createUnencryptedConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  if (typeof serverUrl !== 'string') {\n    throw Error('Expected serverUrl to be a string. Received: ' + serverUrl);\n  }\n  if (typeof roomId !== 'string') {\n    throw Error('Expected roomId to be a string. Received: ' + roomId);\n  }\n  let intervalId;\n  let messageCallback;\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room (unencrypted)...');\n      clearInterval(intervalId);\n      intervalId = setInterval(() => {\n        if (messageCallback) {\n          if (Math.random() > 0.5) {\n            messageCallback('hey')\n          } else {\n            messageCallback('lol');\n          }\n        }\n      }, 3000);\n    },\n    disconnect() {\n      clearInterval(intervalId);\n      messageCallback = null;\n      console.log('❌ Disconnected from \"' + roomId + '\" room (unencrypted)');\n    },\n    on(event, callback) {\n      if (messageCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'message') {\n        throw Error('Only \"message\" event is supported.');\n      }\n      messageCallback = callback;\n    },\n  };\n}\n```\n\n```js src/notifications.js\nimport Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\n\nexport function showNotification(message, theme) {\n  Toastify({\n    text: message,\n    duration: 2000,\n    gravity: 'top',\n    position: 'right',\n    style: {\n      background: theme === 'dark' ? 'black' : 'white',\n      color: theme === 'dark' ? 'white' : 'black',\n    },\n  }).showToast();\n}\n```\n\n```css\nlabel, button { display: block; margin-bottom: 5px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/render-and-commit.md",
    "content": "---\ntitle: レンダーとコミット\n---\n\n<Intro>\n\nコンポーネントは、画面上に表示される前に React によってレンダーされる必要があります。このプロセスが踏む段階を理解すると、コードがどのように実行されるのか考える際や、コードの振る舞いを説明する際に役立ちます。\n\n</Intro>\n\n<YouWillLearn>\n\n* React での「レンダー」の意味\n* いつ、なぜ React はコンポーネントをレンダーするのか\n* 画面上にコンポーネントが表示されるステップ\n* レンダーしたからといって DOM が更新されるとは限らない理由\n\n</YouWillLearn>\n\nコンポーネントが料理人として厨房に立ち、食材を調理して美味しい料理を作っている様子をイメージしてみてください。このシナリオにおいて React はウェイターです。お客様の注文を伝えて、できた料理をお客様に渡します。この UI の「注文」と「提供」のプロセスは、次の 3 つのステップからなります：\n\n1. レンダーの**トリガ**（お客様の注文を厨房に伝える）\n2. コンポーネントの**レンダー**（厨房で注文の品を料理する）\n3. DOM への**コミット**（テーブルに注文の品を提供する）\n\n<IllustrationBlock sequential>\n  <Illustration caption=\"トリガ\" alt=\"レストランのウェイター役の React が、ユーザから注文を聞き取って、コンポーネントの厨房に渡している。\" src=\"/images/docs/illustrations/i_render-and-commit1.png\" />\n  <Illustration caption=\"レンダー\" alt=\"Card シェフが React に出来立ての Card コンポーネントを渡している。\" src=\"/images/docs/illustrations/i_render-and-commit2.png\" />\n  <Illustration caption=\"コミット\" alt=\"React がユーザの座っているテーブルに Card を提供している。\" src=\"/images/docs/illustrations/i_render-and-commit3.png\" />\n</IllustrationBlock>\n\n## ステップ 1：レンダーのトリガ {/*step-1-trigger-a-render*/}\n\nコンポーネントがレンダーされる理由には 2 つあります。\n\n1. コンポーネントの**初回レンダー**。\n2. コンポーネント（またはその祖先のいずれか）の **state の更新**。\n\n### 初回レンダー {/*initial-render*/}\n\nアプリが開始するときには、初回のレンダーをトリガする必要があります。フレームワークやサンドボックスは、しばしばこのコードを隠蔽しますが、自力で行う場合には、ターゲットとなる DOM ノードに対して [`createRoot`](/reference/react-dom/client/createRoot) を呼び出し、作成されたルートの `render` メソッドを、コンポーネントに対して呼び出します。\n\n<Sandpack>\n\n```js src/index.js active\nimport Image from './Image.js';\nimport { createRoot } from 'react-dom/client';\n\nconst root = createRoot(document.getElementById('root'))\nroot.render(<Image />);\n```\n\n```js src/Image.js\nexport default function Image() {\n  return (\n    <img\n      src=\"https://i.imgur.com/ZF6s192.jpg\"\n      alt=\"'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals\"\n    />\n  );\n}\n```\n\n</Sandpack>\n\n`root.render()` の呼び出しをコメントアウトして、コンポーネントが消えるのを確認してみてください！\n\n### state 更新後の再レンダー {/*re-renders-when-state-updates*/}\n\nコンポーネントが最初にレンダーされた後、[`set` 関数](/reference/react/useState#setstate)を使って state を更新することで、さらなるレンダーをトリガすることができます。コンポーネントの state を更新すると、自動的にレンダーがキューイングされます。（これは、レストランの客が最初の注文の後に、喉の渇きや空腹の状態に応じてお茶やデザートなどいろいろなものを注文するようなものだと考えることができます。）\n\n<IllustrationBlock sequential>\n  <Illustration caption=\"state の更新が...\" alt=\"レストランのウェイター役の React が Card UI を提供したところ。矢印頭の顧客は、黒ではなく、ピンクのカードが欲しいのだと注文している。\" src=\"/images/docs/illustrations/i_rerender1.png\" />\n  <Illustration caption=\"... トリガになって ...\" alt=\"React はコンポーネントの厨房に戻り、ピンクの Card が必要だと Card シェフに伝える。\" src=\"/images/docs/illustrations/i_rerender2.png\" />\n  <Illustration caption=\"... レンダーされる！\" alt=\"Card シェフはピンクのカードを React に渡している。\" src=\"/images/docs/illustrations/i_rerender3.png\" />\n</IllustrationBlock>\n\n## ステップ 2：React がコンポーネントをレンダー {/*step-2-react-renders-your-components*/}\n\nあなたがレンダーをトリガした後、React はコンポーネントを呼び出して画面に表示する内容を把握します。**「レンダー」とは、React がコンポーネントを呼び出すことです。**\n\n* **初回レンダー時**、React はルート (root) コンポーネントを呼び出します。\n* **次回以降のレンダー**では、state の更新によってレンダーがトリガされた関数コンポーネントを、React が呼び出します。\n\nこのプロセスは再帰的に発生します。更新されたコンポーネントが他のコンポーネントを返す場合、次に*その*コンポーネントを React がレンダーし、そのコンポーネントも何かコンポーネントを返す場合、*その*コンポーネントも次にレンダーし、といった具合に続きます。このプロセスは、ネストされたコンポーネントがなくなり、React が画面に表示されるべき内容を知り尽くすまで続きます。\n\n次の例では、React は `Gallery()` を呼び出した後、`Image()` を何度も呼び出します。\n\n<Sandpack>\n\n```js src/Gallery.js active\nexport default function Gallery() {\n  return (\n    <section>\n      <h1>Inspiring Sculptures</h1>\n      <Image />\n      <Image />\n      <Image />\n    </section>\n  );\n}\n\nfunction Image() {\n  return (\n    <img\n      src=\"https://i.imgur.com/ZF6s192.jpg\"\n      alt=\"'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals\"\n    />\n  );\n}\n```\n\n```js src/index.js\nimport Gallery from './Gallery.js';\nimport { createRoot } from 'react-dom/client';\n\nconst root = createRoot(document.getElementById('root'))\nroot.render(<Gallery />);\n```\n\n```css\nimg { margin: 0 10px 10px 0; }\n```\n\n</Sandpack>\n\n* **初回レンダー時には**、React は `<section>`、`<h1>`、および 3 つの `<img>` タグの [DOM ノードを作成](https://developer.mozilla.org/docs/Web/API/Document/createElement)します。\n* **再レンダー時には**、React は前回のレンダーからどの部分が変わったのか、あるいは変わらなかったのかを計算します。次のステップであるコミットフェーズまでこの情報は使われません。\n\n<Pitfall>\n\nレンダーは常に[純粋な計算](/learn/keeping-components-pure)であるべきです。\n\n* **同じ入力には同じ出力**。同じ入力が与えられた場合、コンポーネントは常に同じ JSX を返す必要がある。（トマトサラダを注文した人がオニオンサラダを受け取ってはいけない！）\n* **自分の仕事に専念する**。レンダー前に存在したオブジェクトや変数を変更しない。（ある注文が他の誰かの注文を変更してはいけない。）\n\nこれを守らなかった場合、コードベースが複雑になるにつれて、ややこしいバグや予測不能な挙動に遭遇することになります。\"Strict Mode\" で開発している場合、React は各コンポーネントの関数を 2 回呼び出すことで、純粋でない関数が引き起こす間違いに気づきやすくしてくれます。\n\n</Pitfall>\n\n<DeepDive>\n\n#### パフォーマンスの最適化 {/*optimizing-performance*/}\n\n更新されたコンポーネントがツリー内で非常に高い位置にある場合、その内部にネストされたすべてのコンポーネントを再レンダーするというデフォルトの挙動は、パフォーマンスにとって理想的ではありません。パフォーマンスの問題に遭遇した場合、[パフォーマンス](https://reactjs.org/docs/optimizing-performance.html)セクションで述べられているいくつかのオプトインによる解決方法があります。**早まった最適化をしてしまってはいけません！**\n\n</DeepDive>\n\n## ステップ 3：React が DOM への変更をコミットする {/*step-3-react-commits-changes-to-the-dom*/}\n\nあなたのコンポーネントをレンダー（関数として呼び出し）した後、React は DOM を変更します。\n\n* **初回レンダー時には**、React は [`appendChild()`](https://developer.mozilla.org/docs/Web/API/Node/appendChild) DOM API を使用して、作成したすべての DOM ノードを画面に表示します。\n* **再レンダー時には**、React は最新のレンダー出力に合わせて DOM を変更するため、必要な最小限の操作（レンダー中に計算されたもの！）を適用します。\n\n**React はレンダー間で違いがあった場合にのみ DOM ノードを変更します**。例えば、以下のコンポーネントは親から渡された異なる props で毎秒再レンダーされます。実際に試してみてください。`<input>` にテキストを追加して `value` を更新することができますが、コンポーネントが再レンダーされる瞬間もテキストが消えることはありません：\n\n<Sandpack>\n\n```js src/Clock.js active\nexport default function Clock({ time }) {\n  return (\n    <>\n      <h1>{time}</h1>\n      <input />\n    </>\n  );\n}\n```\n\n```js src/App.js hidden\nimport { useState, useEffect } from 'react';\nimport Clock from './Clock.js';\n\nfunction useTime() {\n  const [time, setTime] = useState(() => new Date());\n  useEffect(() => {\n    const id = setInterval(() => {\n      setTime(new Date());\n    }, 1000);\n    return () => clearInterval(id);\n  }, []);\n  return time;\n}\n\nexport default function App() {\n  const time = useTime();\n  return (\n    <Clock time={time.toLocaleTimeString()} />\n  );\n}\n```\n\n</Sandpack>\n\nこれが上手く動作するのは、最終ステップで React によって更新されるのが、新しい `time` の値で更新される `<h1>` の中身だけだからです。`<input>` は JSX 内で前回と同じ場所にあるので、React は `<input>` やその `value` に触れません！\n## エピローグ：ブラウザのペイント {/*epilogue-browser-paint*/}\n\nレンダーが完了し、React が DOM を更新した後、ブラウザは画面を再描画します。このプロセスは「ブラウザレンダリング」として知られていますが、我々は、混乱を避けるために、ドキュメント全体を通して「ペイント」と呼ぶことにします。\n\n<Illustration alt=\"ブラウザが「カード要素と静物画」をペイントしている\" src=\"/images/docs/illustrations/i_browser-paint.png\" />\n\n<Recap>\n\n* React アプリでの画面更新は、以下の 3 つのステップで行われる：\n  1. トリガ\n  2. レンダー\n  3. コミット\n* Strict Mode を使って、コンポーネントの間違いを発見できる。\n* レンダー結果が前回と同一である場合、React は DOM を触らない。\n\n</Recap>\n\n"
  },
  {
    "path": "src/content/learn/rendering-lists.md",
    "content": "---\ntitle: リストのレンダー\n---\n\n<Intro>\n\nデータの集まりから、似たようなコンポーネントを複数表示させたいことがあります。データの配列を操作するには [JavaScript の配列メソッド](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array#)が使えます。このページでは [`filter()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) や [`map()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map) を React で使用して、データの配列をフィルタリングしたり、コンポーネントの配列に変換したりする方法を見ていきましょう。\n\n</Intro>\n\n<YouWillLearn>\n\n* JavaScript の `map()` を使用して、配列からコンポーネントをレンダーする方法\n* JavaScript の `filter()` を使用して、特定のコンポーネントのみをレンダーする方法\n* React での key の使用方法と、その必要性\n\n</YouWillLearn>\n\n## 配列からデータをレンダー {/*rendering-data-from-arrays*/}\n\n以下のようなコンテンツのリストがあるとしましょう。\n\n```js\n<ul>\n  <li>Creola Katherine Johnson: mathematician</li>\n  <li>Mario José Molina-Pasquel Henríquez: chemist</li>\n  <li>Mohammad Abdus Salam: physicist</li>\n  <li>Percy Lavon Julian: chemist</li>\n  <li>Subrahmanyan Chandrasekhar: astrophysicist</li>\n</ul>\n```\n\nこれらのリスト項目は、その中身、すなわちデータのみが異なっています。インターフェースを構築する際にはよく、コメント一覧やプロフィール画像のギャラリなどのように、異なるデータを使用して同じコンポーネントの複数のインスタンスを表示する必要があります。このような場合、JavaScript のオブジェクトや配列にそのデータを保存し、[`map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) や [`filter()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) などのメソッドを使ってコンポーネントのリストをレンダーすることができます。\n\n以下は、配列からアイテムのリストを生成する方法を示す簡単な例です。\n\n1. データを配列に**移動**します。\n\n```js\nconst people = [\n  'Creola Katherine Johnson: mathematician',\n  'Mario José Molina-Pasquel Henríquez: chemist',\n  'Mohammad Abdus Salam: physicist',\n  'Percy Lavon Julian: chemist',\n  'Subrahmanyan Chandrasekhar: astrophysicist'\n];\n```\n\n2. `people` 内のメンバを `listItems` という新しい JSX の配列に**マップ**します。\n\n```js\nconst listItems = people.map(person => <li>{person}</li>);\n```\n\n3. コンポーネントから `listItems` を `<ul>` で囲んで返します。\n\n```js\nreturn <ul>{listItems}</ul>;\n```\n\n以下が結果です。\n\n<Sandpack>\n\n```js\nconst people = [\n  'Creola Katherine Johnson: mathematician',\n  'Mario José Molina-Pasquel Henríquez: chemist',\n  'Mohammad Abdus Salam: physicist',\n  'Percy Lavon Julian: chemist',\n  'Subrahmanyan Chandrasekhar: astrophysicist'\n];\n\nexport default function List() {\n  const listItems = people.map(person =>\n    <li>{person}</li>\n  );\n  return <ul>{listItems}</ul>;\n}\n```\n\n```css\nli { margin-bottom: 10px; }\n```\n\n</Sandpack>\n\n上のサンドボックスには、以下のようなコンソールエラーが表示されています。\n\n<ConsoleBlock level=\"error\">\n\nWarning: Each child in a list should have a unique \"key\" prop.\n\n</ConsoleBlock>\n\nこのエラーを修正する方法についてはこのページの後半で説明します。その前に、このデータに構造を追加しましょう。\n\n## アイテムの配列をフィルタする {/*filtering-arrays-of-items*/}\n\nこのデータにさらに構造を加えてみました。\n\n```js\nconst people = [{\n  id: 0,\n  name: 'Creola Katherine Johnson',\n  profession: 'mathematician',\n}, {\n  id: 1,\n  name: 'Mario José Molina-Pasquel Henríquez',\n  profession: 'chemist',\n}, {\n  id: 2,\n  name: 'Mohammad Abdus Salam',\n  profession: 'physicist',\n}, {\n  id: 3,\n  name: 'Percy Lavon Julian',\n  profession: 'chemist',  \n}, {\n  id: 4,\n  name: 'Subrahmanyan Chandrasekhar',\n  profession: 'astrophysicist',\n}];\n```\n\nここで、職業 (profession) が `'chemist'` の人だけを表示したいとしましょう。JavaScript の `filter()` メソッドを使用すれば、そのような職業の人だけを返すことができます。このメソッドは、要素の配列を受け取り、個々の要素を「テスト」（`true` または `false` を返す関数）にかけ、そして、テストを通過した要素（テスト関数が `true` を返したもの）のみを含む配列を返します。\n\n`profession` が `'chemist'` となっている要素のみが必要でした。そのための「テスト」関数は、`(person) => person.profession === 'chemist'` のようになります。使い方の全体像は以下のようになります。\n\n1. `people` に対して `filter()` を呼び出し、`person.profession === 'chemist'` を使ってフィルタした、職業が \"chemist\" である人物のみの新しい配列を**作成**します。\n\n```js\nconst chemists = people.filter(person =>\n  person.profession === 'chemist'\n);\n```\n\n2. 次に `chemists` に対して **map** を適用します。\n\n```js {1,13}\nconst listItems = chemists.map(person =>\n  <li>\n     <img\n       src={getImageUrl(person)}\n       alt={person.name}\n     />\n     <p>\n       <b>{person.name}:</b>\n       {' ' + person.profession + ' '}\n       known for {person.accomplishment}\n     </p>\n  </li>\n);\n```\n\n3. 最後に、コンポーネントから `listItems` を**返し**ます。\n\n```js\nreturn <ul>{listItems}</ul>;\n```\n\n<Sandpack>\n\n```js src/App.js\nimport { people } from './data.js';\nimport { getImageUrl } from './utils.js';\n\nexport default function List() {\n  const chemists = people.filter(person =>\n    person.profession === 'chemist'\n  );\n  const listItems = chemists.map(person =>\n    <li>\n      <img\n        src={getImageUrl(person)}\n        alt={person.name}\n      />\n      <p>\n        <b>{person.name}:</b>\n        {' ' + person.profession + ' '}\n        known for {person.accomplishment}\n      </p>\n    </li>\n  );\n  return <ul>{listItems}</ul>;\n}\n```\n\n```js src/data.js\nexport const people = [{\n  id: 0,\n  name: 'Creola Katherine Johnson',\n  profession: 'mathematician',\n  accomplishment: 'spaceflight calculations',\n  imageId: 'MK3eW3A'\n}, {\n  id: 1,\n  name: 'Mario José Molina-Pasquel Henríquez',\n  profession: 'chemist',\n  accomplishment: 'discovery of Arctic ozone hole',\n  imageId: 'mynHUSa'\n}, {\n  id: 2,\n  name: 'Mohammad Abdus Salam',\n  profession: 'physicist',\n  accomplishment: 'electromagnetism theory',\n  imageId: 'bE7W1ji'\n}, {\n  id: 3,\n  name: 'Percy Lavon Julian',\n  profession: 'chemist',\n  accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',\n  imageId: 'IOjWm71'\n}, {\n  id: 4,\n  name: 'Subrahmanyan Chandrasekhar',\n  profession: 'astrophysicist',\n  accomplishment: 'white dwarf star mass calculations',\n  imageId: 'lrWQx8l'\n}];\n```\n\n```js src/utils.js\nexport function getImageUrl(person) {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    's.jpg'\n  );\n}\n```\n\n```css\nul { list-style-type: none; padding: 0px 10px; }\nli { \n  margin-bottom: 10px; \n  display: grid; \n  grid-template-columns: auto 1fr;\n  gap: 20px;\n  align-items: center;\n}\nimg { width: 100px; height: 100px; border-radius: 50%; }\n```\n\n</Sandpack>\n\n<Pitfall>\n\nアロー関数は `=>` の直後の式を自動的に返しますので、`return` 文を直接書く必要はありません。\n\n```js\nconst listItems = chemists.map(person =>\n  <li>...</li> // Implicit return!\n);\n```\n\nただし、もし `=>` の次に `{` が続く場合は、**必ず `return` 文を明示的に書く必要があります**。\n\n```js\nconst listItems = chemists.map(person => { // Curly brace\n  return <li>...</li>;\n});\n```\n\n`=> {` を含むアロー関数は [\"ブロック形式の関数本体\"](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#function_body) を持つものとして扱われます。これにより複数行のコードが書けるようになりますが、`return` 文を自分で書かなければなりません。書き忘れた場合は何も返されません！\n\n</Pitfall>\n\n## `key` によるリストアイテムの順序の保持 {/*keeping-list-items-in-order-with-key*/}\n\n上記のすべてのサンドボックスで、コンソールにエラーが表示されていることに注目しましょう：\n\n<ConsoleBlock level=\"error\">\n\nWarning: Each child in a list should have a unique \"key\" prop.\n\n</ConsoleBlock>\n\n配列の各アイテムには、`key` を渡す必要があります。配列内の他のアイテムと区別できるようにするための一意な文字列ないし数値のことです。\n\n```js\n<li key={person.id}>...</li>\n```\n\n<Note>\n\n`map()` 内で直接 JSX 要素を使用する場合、必ず key が必要です！\n\n</Note>\n\nkey は、配列のどの要素がどのコンポーネントに対応するのかを React が判断し、後で正しく更新するために必要です。これが重要となるのは、配列の要素が移動（ソートなどによって）した場合、挿入された場合、あるいは削除された場合です。適切に `key` を選ぶことで、React は何が起こったか推測し、DOM ツリーに正しい更新を反映させることができます。\n\nkey は動的に生成するのではなく、元データに含めるべきです。\n\n<Sandpack>\n\n```js src/App.js\nimport { people } from './data.js';\nimport { getImageUrl } from './utils.js';\n\nexport default function List() {\n  const listItems = people.map(person =>\n    <li key={person.id}>\n      <img\n        src={getImageUrl(person)}\n        alt={person.name}\n      />\n      <p>\n        <b>{person.name}</b>\n          {' ' + person.profession + ' '}\n          known for {person.accomplishment}\n      </p>\n    </li>\n  );\n  return <ul>{listItems}</ul>;\n}\n```\n\n```js src/data.js active\nexport const people = [{\n  id: 0, // Used in JSX as a key\n  name: 'Creola Katherine Johnson',\n  profession: 'mathematician',\n  accomplishment: 'spaceflight calculations',\n  imageId: 'MK3eW3A'\n}, {\n  id: 1, // Used in JSX as a key\n  name: 'Mario José Molina-Pasquel Henríquez',\n  profession: 'chemist',\n  accomplishment: 'discovery of Arctic ozone hole',\n  imageId: 'mynHUSa'\n}, {\n  id: 2, // Used in JSX as a key\n  name: 'Mohammad Abdus Salam',\n  profession: 'physicist',\n  accomplishment: 'electromagnetism theory',\n  imageId: 'bE7W1ji'\n}, {\n  id: 3, // Used in JSX as a key\n  name: 'Percy Lavon Julian',\n  profession: 'chemist',\n  accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',\n  imageId: 'IOjWm71'\n}, {\n  id: 4, // Used in JSX as a key\n  name: 'Subrahmanyan Chandrasekhar',\n  profession: 'astrophysicist',\n  accomplishment: 'white dwarf star mass calculations',\n  imageId: 'lrWQx8l'\n}];\n```\n\n```js src/utils.js\nexport function getImageUrl(person) {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    's.jpg'\n  );\n}\n```\n\n```css\nul { list-style-type: none; padding: 0px 10px; }\nli { \n  margin-bottom: 10px; \n  display: grid; \n  grid-template-columns: auto 1fr;\n  gap: 20px;\n  align-items: center;\n}\nimg { width: 100px; height: 100px; border-radius: 50%; }\n```\n\n</Sandpack>\n\n<DeepDive>\n\n#### リストアイテムごとに複数の DOM ノードを表示する {/*displaying-several-dom-nodes-for-each-list-item*/}\n\n各アイテムが 1 つの DOM ノードではなく、複数の DOM ノードをレンダーする必要がある場合はどうするのでしょうか？\n\n短い [`<>...</>` フラグメント](/reference/react/Fragment)構文では `key` を渡せないため、これらを 1 つの `<div>` にグループ化するか、やや長くて[より明示的な `<Fragment>` 構文](/reference/react/Fragment#rendering-a-list-of-fragments)を使用する必要があります。\n\n```js\nimport { Fragment } from 'react';\n\n// ...\n\nconst listItems = people.map(person =>\n  <Fragment key={person.id}>\n    <h1>{person.name}</h1>\n    <p>{person.bio}</p>\n  </Fragment>\n);\n```\n\nフラグメントは DOM から消え去るため、これにより `<h1>`、`<p>`、`<h1>`、`<p>` というように続くフラットなリストが生成されます。\n\n</DeepDive>\n\n### `key` をどこから得るのか {/*where-to-get-your-key*/}\n\nデータソースの種類によって key を得る方法は異なります。\n\n* **データベースからのデータ：** データがデータベースから来る場合、データベースのキーや ID は必然的に一意ですので、それを利用できます。\n* **ローカルで生成されたデータ：** データがローカルで生成されて保持される場合（例：ノートを取るアプリにおけるノート）は、アイテムを作成する際に、インクリメンタルなカウンタや [`crypto.randomUUID()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID)、または [`uuid`](https://www.npmjs.com/package/uuid) などのパッケージを使用します。\n\n### `key` のルール {/*rules-of-keys*/}\n\n* **キーは兄弟間で一意でなければなりません**。ただし、*異なる*配列に対応する JSX ノードには同じキーを使用することができます。\n* **キーは変更してはいけません**。さもないと `key` の目的が台無しになります。レンダーの最中に key を生成してはいけません。\n\n### なぜ React は `key` を必要とするのか {/*why-does-react-need-keys*/}\n\nデスクトップ上のファイルに名前がない場合を想像してください。代わりに、最初のファイル、2 番目のファイルといったように、順番によってそれらを区別する必要があるとしましょう。そのうち番号に慣れるかもしれませんが、ファイルを削除した途端に混乱してしまいますね。2 番目のファイルが 1 番目のファイルになり、3 番目のファイルが 2 番目のファイルになり、という具合です。\n\nフォルダ内のファイル名と JSX の key の目的は似ています。兄弟間で項目を一意に識別できるようにするのです。適切に選択された key は、配列内の位置よりも多くの情報を提供します。並べ替えによって*位置*が変更されたとしても、`key` のおかげで React はその項目が存在する限り、それを一意に識別できるのです。\n\n<Pitfall>\n\nアイテムのインデックスを `key` として使用したくなるかもしれません。実際、`key` を指定しなかった場合、React はデフォルトでインデックスを使用します。しかし、アイテムが挿入されたり削除されたり、配列を並び替えたりすると、レンダーするアイテムの順序も変わります。インデックスをキーとして利用すると、微妙かつややこしいバグの原因となります。\n\n同様に、`key={Math.random()}` などとしてキーをその場で生成してはいけません。こうするとキーがレンダーごとに一切合致しなくなり、コンポーネントと DOM が毎回再作成されるようになります。これは遅くなるのみならず、リストアイテム内のユーザによる入力値も失われてしまいます。代わりに、データに紐づいた安定した ID を使用してください。\n\nコンポーネントは `key` を props として受け取らないということに注意してください。React 自体がヒントとして使用するだけです。コンポーネントが ID を必要とする場合は、別の props として渡す必要があります：`<Profile key={id} userId={id} />`。\n\n</Pitfall>\n\n<Recap>\n\nこのページでは以下のことを学びました。\n\n* コンポーネントからデータを配列やオブジェクトといったデータ構造に移動する方法。\n* JavaScript の `map()` を使用して類似したコンポーネントの集まりを作成する方法。\n* JavaScript の `filter()` を使用してフィルタリングされたアイテムの配列を作成する方法。\n* コレクション内の各コンポーネントに `key` を設定して、位置やデータが変更された場合でも React がそれぞれを追跡できるようにする方法と、それが必要な理由。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### リストを 2 つに分割 {/*splitting-a-list-in-two*/}\n\nこの例では、すべての人物の一覧を表示しています。\n\nそれを、**化学者 (chemist)** と **その他の人** の 2 つの別々の一覧に変更してください。前に述べたように、`person.profession === 'chemist'` であるかどうかを確認することで、その人が化学者であるかどうかを決定できます。\n\n<Sandpack>\n\n```js src/App.js\nimport { people } from './data.js';\nimport { getImageUrl } from './utils.js';\n\nexport default function List() {\n  const listItems = people.map(person =>\n    <li key={person.id}>\n      <img\n        src={getImageUrl(person)}\n        alt={person.name}\n      />\n      <p>\n        <b>{person.name}:</b>\n        {' ' + person.profession + ' '}\n        known for {person.accomplishment}\n      </p>\n    </li>\n  );\n  return (\n    <article>\n      <h1>Scientists</h1>\n      <ul>{listItems}</ul>\n    </article>\n  );\n}\n```\n\n```js src/data.js\nexport const people = [{\n  id: 0,\n  name: 'Creola Katherine Johnson',\n  profession: 'mathematician',\n  accomplishment: 'spaceflight calculations',\n  imageId: 'MK3eW3A'\n}, {\n  id: 1,\n  name: 'Mario José Molina-Pasquel Henríquez',\n  profession: 'chemist',\n  accomplishment: 'discovery of Arctic ozone hole',\n  imageId: 'mynHUSa'\n}, {\n  id: 2,\n  name: 'Mohammad Abdus Salam',\n  profession: 'physicist',\n  accomplishment: 'electromagnetism theory',\n  imageId: 'bE7W1ji'\n}, {\n  id: 3,\n  name: 'Percy Lavon Julian',\n  profession: 'chemist',\n  accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',\n  imageId: 'IOjWm71'\n}, {\n  id: 4,\n  name: 'Subrahmanyan Chandrasekhar',\n  profession: 'astrophysicist',\n  accomplishment: 'white dwarf star mass calculations',\n  imageId: 'lrWQx8l'\n}];\n```\n\n```js src/utils.js\nexport function getImageUrl(person) {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    's.jpg'\n  );\n}\n```\n\n```css\nul { list-style-type: none; padding: 0px 10px; }\nli {\n  margin-bottom: 10px;\n  display: grid;\n  grid-template-columns: auto 1fr;\n  gap: 20px;\n  align-items: center;\n}\nimg { width: 100px; height: 100px; border-radius: 50%; }\n```\n\n</Sandpack>\n\n<Solution>\n\n`filter()` を 2 回使用して、2 つの別々の配列を作成し、その両方に `map` を適用します。\n\n<Sandpack>\n\n```js src/App.js\nimport { people } from './data.js';\nimport { getImageUrl } from './utils.js';\n\nexport default function List() {\n  const chemists = people.filter(person =>\n    person.profession === 'chemist'\n  );\n  const everyoneElse = people.filter(person =>\n    person.profession !== 'chemist'\n  );\n  return (\n    <article>\n      <h1>Scientists</h1>\n      <h2>Chemists</h2>\n      <ul>\n        {chemists.map(person =>\n          <li key={person.id}>\n            <img\n              src={getImageUrl(person)}\n              alt={person.name}\n            />\n            <p>\n              <b>{person.name}:</b>\n              {' ' + person.profession + ' '}\n              known for {person.accomplishment}\n            </p>\n          </li>\n        )}\n      </ul>\n      <h2>Everyone Else</h2>\n      <ul>\n        {everyoneElse.map(person =>\n          <li key={person.id}>\n            <img\n              src={getImageUrl(person)}\n              alt={person.name}\n            />\n            <p>\n              <b>{person.name}:</b>\n              {' ' + person.profession + ' '}\n              known for {person.accomplishment}\n            </p>\n          </li>\n        )}\n      </ul>\n    </article>\n  );\n}\n```\n\n```js src/data.js\nexport const people = [{\n  id: 0,\n  name: 'Creola Katherine Johnson',\n  profession: 'mathematician',\n  accomplishment: 'spaceflight calculations',\n  imageId: 'MK3eW3A'\n}, {\n  id: 1,\n  name: 'Mario José Molina-Pasquel Henríquez',\n  profession: 'chemist',\n  accomplishment: 'discovery of Arctic ozone hole',\n  imageId: 'mynHUSa'\n}, {\n  id: 2,\n  name: 'Mohammad Abdus Salam',\n  profession: 'physicist',\n  accomplishment: 'electromagnetism theory',\n  imageId: 'bE7W1ji'\n}, {\n  id: 3,\n  name: 'Percy Lavon Julian',\n  profession: 'chemist',\n  accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',\n  imageId: 'IOjWm71'\n}, {\n  id: 4,\n  name: 'Subrahmanyan Chandrasekhar',\n  profession: 'astrophysicist',\n  accomplishment: 'white dwarf star mass calculations',\n  imageId: 'lrWQx8l'\n}];\n```\n\n```js src/utils.js\nexport function getImageUrl(person) {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    's.jpg'\n  );\n}\n```\n\n```css\nul { list-style-type: none; padding: 0px 10px; }\nli {\n  margin-bottom: 10px;\n  display: grid;\n  grid-template-columns: auto 1fr;\n  gap: 20px;\n  align-items: center;\n}\nimg { width: 100px; height: 100px; border-radius: 50%; }\n```\n\n</Sandpack>\n\nこの解答では、`map` コールは親の `<ul>` 要素の中に直接インラインで配置されていますが、このための変数を導入する方が読みやすいと思った場合は、そうしても構いません。\n\nレンダーされた 2 つのリストの間にはまだ少し重複部分があります。さらに進んで、反復部分を `<ListSection>` コンポーネントに抽出できます。\n\n<Sandpack>\n\n```js src/App.js\nimport { people } from './data.js';\nimport { getImageUrl } from './utils.js';\n\nfunction ListSection({ title, people }) {\n  return (\n    <>\n      <h2>{title}</h2>\n      <ul>\n        {people.map(person =>\n          <li key={person.id}>\n            <img\n              src={getImageUrl(person)}\n              alt={person.name}\n            />\n            <p>\n              <b>{person.name}:</b>\n              {' ' + person.profession + ' '}\n              known for {person.accomplishment}\n            </p>\n          </li>\n        )}\n      </ul>\n    </>\n  );\n}\n\nexport default function List() {\n  const chemists = people.filter(person =>\n    person.profession === 'chemist'\n  );\n  const everyoneElse = people.filter(person =>\n    person.profession !== 'chemist'\n  );\n  return (\n    <article>\n      <h1>Scientists</h1>\n      <ListSection\n        title=\"Chemists\"\n        people={chemists}\n      />\n      <ListSection\n        title=\"Everyone Else\"\n        people={everyoneElse}\n      />\n    </article>\n  );\n}\n```\n\n```js src/data.js\nexport const people = [{\n  id: 0,\n  name: 'Creola Katherine Johnson',\n  profession: 'mathematician',\n  accomplishment: 'spaceflight calculations',\n  imageId: 'MK3eW3A'\n}, {\n  id: 1,\n  name: 'Mario José Molina-Pasquel Henríquez',\n  profession: 'chemist',\n  accomplishment: 'discovery of Arctic ozone hole',\n  imageId: 'mynHUSa'\n}, {\n  id: 2,\n  name: 'Mohammad Abdus Salam',\n  profession: 'physicist',\n  accomplishment: 'electromagnetism theory',\n  imageId: 'bE7W1ji'\n}, {\n  id: 3,\n  name: 'Percy Lavon Julian',\n  profession: 'chemist',\n  accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',\n  imageId: 'IOjWm71'\n}, {\n  id: 4,\n  name: 'Subrahmanyan Chandrasekhar',\n  profession: 'astrophysicist',\n  accomplishment: 'white dwarf star mass calculations',\n  imageId: 'lrWQx8l'\n}];\n```\n\n```js src/utils.js\nexport function getImageUrl(person) {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    's.jpg'\n  );\n}\n```\n\n```css\nul { list-style-type: none; padding: 0px 10px; }\nli {\n  margin-bottom: 10px;\n  display: grid;\n  grid-template-columns: auto 1fr;\n  gap: 20px;\n  align-items: center;\n}\nimg { width: 100px; height: 100px; border-radius: 50%; }\n```\n\n</Sandpack>\n\n注意深い方は、2 つの `filter` コールで各人物の職業を 2 回確認していることに気づくかもしれません。プロパティのチェックは非常に高速ですので、この例では問題ありません。あなたのロジックがそれ以上に重たい場合は、`filter` コールを手動のループに置き換え、各人物を一度だけチェックしながらループ内で手動で 2 つの配列を構築することができます。\n\n実際、`people` が一切変わらないのであれば、このコードをコンポーネントの外に移動しても構いません。React の観点からは、最終的に JSX ノードの配列が生成されていればよいのです。どのようにしてその配列を生成しているかは問題ではありません。\n\n<Sandpack>\n\n```js src/App.js\nimport { people } from './data.js';\nimport { getImageUrl } from './utils.js';\n\nlet chemists = [];\nlet everyoneElse = [];\npeople.forEach(person => {\n  if (person.profession === 'chemist') {\n    chemists.push(person);\n  } else {\n    everyoneElse.push(person);\n  }\n});\n\nfunction ListSection({ title, people }) {\n  return (\n    <>\n      <h2>{title}</h2>\n      <ul>\n        {people.map(person =>\n          <li key={person.id}>\n            <img\n              src={getImageUrl(person)}\n              alt={person.name}\n            />\n            <p>\n              <b>{person.name}:</b>\n              {' ' + person.profession + ' '}\n              known for {person.accomplishment}\n            </p>\n          </li>\n        )}\n      </ul>\n    </>\n  );\n}\n\nexport default function List() {\n  return (\n    <article>\n      <h1>Scientists</h1>\n      <ListSection\n        title=\"Chemists\"\n        people={chemists}\n      />\n      <ListSection\n        title=\"Everyone Else\"\n        people={everyoneElse}\n      />\n    </article>\n  );\n}\n```\n\n```js src/data.js\nexport const people = [{\n  id: 0,\n  name: 'Creola Katherine Johnson',\n  profession: 'mathematician',\n  accomplishment: 'spaceflight calculations',\n  imageId: 'MK3eW3A'\n}, {\n  id: 1,\n  name: 'Mario José Molina-Pasquel Henríquez',\n  profession: 'chemist',\n  accomplishment: 'discovery of Arctic ozone hole',\n  imageId: 'mynHUSa'\n}, {\n  id: 2,\n  name: 'Mohammad Abdus Salam',\n  profession: 'physicist',\n  accomplishment: 'electromagnetism theory',\n  imageId: 'bE7W1ji'\n}, {\n  id: 3,\n  name: 'Percy Lavon Julian',\n  profession: 'chemist',\n  accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',\n  imageId: 'IOjWm71'\n}, {\n  id: 4,\n  name: 'Subrahmanyan Chandrasekhar',\n  profession: 'astrophysicist',\n  accomplishment: 'white dwarf star mass calculations',\n  imageId: 'lrWQx8l'\n}];\n```\n\n```js src/utils.js\nexport function getImageUrl(person) {\n  return (\n    'https://i.imgur.com/' +\n    person.imageId +\n    's.jpg'\n  );\n}\n```\n\n```css\nul { list-style-type: none; padding: 0px 10px; }\nli {\n  margin-bottom: 10px;\n  display: grid;\n  grid-template-columns: auto 1fr;\n  gap: 20px;\n  align-items: center;\n}\nimg { width: 100px; height: 100px; border-radius: 50%; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### 同一コンポーネント内のネストしたリスト {/*nested-lists-in-one-component*/}\n\n以下の配列から、レシピのリストを作成してください！ 配列内の各レシピについて、名前を `<h2>` で表示し、その材料を `<ul>` を使って表示してください。\n\n<Hint>\n\nこれには 2 つの異なる `map` コールをネストする必要があります。\n\n</Hint>\n\n<Sandpack>\n\n```js src/App.js\nimport { recipes } from './data.js';\n\nexport default function RecipeList() {\n  return (\n    <div>\n      <h1>Recipes</h1>\n    </div>\n  );\n}\n```\n\n```js src/data.js\nexport const recipes = [{\n  id: 'greek-salad',\n  name: 'Greek Salad',\n  ingredients: ['tomatoes', 'cucumber', 'onion', 'olives', 'feta']\n}, {\n  id: 'hawaiian-pizza',\n  name: 'Hawaiian Pizza',\n  ingredients: ['pizza crust', 'pizza sauce', 'mozzarella', 'ham', 'pineapple']\n}, {\n  id: 'hummus',\n  name: 'Hummus',\n  ingredients: ['chickpeas', 'olive oil', 'garlic cloves', 'lemon', 'tahini']\n}];\n```\n\n</Sandpack>\n\n<Solution>\n\n以下が解決法のひとつです。\n\n<Sandpack>\n\n```js src/App.js\nimport { recipes } from './data.js';\n\nexport default function RecipeList() {\n  return (\n    <div>\n      <h1>Recipes</h1>\n      {recipes.map(recipe =>\n        <div key={recipe.id}>\n          <h2>{recipe.name}</h2>\n          <ul>\n            {recipe.ingredients.map(ingredient =>\n              <li key={ingredient}>\n                {ingredient}\n              </li>\n            )}\n          </ul>\n        </div>\n      )}\n    </div>\n  );\n}\n```\n\n```js src/data.js\nexport const recipes = [{\n  id: 'greek-salad',\n  name: 'Greek Salad',\n  ingredients: ['tomatoes', 'cucumber', 'onion', 'olives', 'feta']\n}, {\n  id: 'hawaiian-pizza',\n  name: 'Hawaiian Pizza',\n  ingredients: ['pizza crust', 'pizza sauce', 'mozzarella', 'ham', 'pineapple']\n}, {\n  id: 'hummus',\n  name: 'Hummus',\n  ingredients: ['chickpeas', 'olive oil', 'garlic cloves', 'lemon', 'tahini']\n}];\n```\n\n</Sandpack>\n\n`recipes` の各要素にはすでに `id` フィールドが含まれているので、外側のループではこれを `key` として使用します。一方で材料をループするために使用できる ID はありません。しかし、同じレシピに同じ材料が 2 度表示されることはないと考えるのが妥当ですので、材料の名前を `key` として使用することができます。あるいは、データ構造を変更して ID を追加するか、インデックスを `key` として使用することもできます（ただし、材料を安全に並べ替えることはできなくなります）。\n\n</Solution>\n\n#### リスト要素のコンポーネントを抽出 {/*extracting-a-list-item-component*/}\n\nこの `RecipeList` コンポーネントは、2 つのネストした `map` 呼び出しを含んでいます。これを単純化するために、`id`, `name`, `ingredients` という props を受けとる `Recipe` コンポーネントを抽出してください。外側の `key` はどこに置きますか、またその理由は？\n\n<Sandpack>\n\n```js src/App.js\nimport { recipes } from './data.js';\n\nexport default function RecipeList() {\n  return (\n    <div>\n      <h1>Recipes</h1>\n      {recipes.map(recipe =>\n        <div key={recipe.id}>\n          <h2>{recipe.name}</h2>\n          <ul>\n            {recipe.ingredients.map(ingredient =>\n              <li key={ingredient}>\n                {ingredient}\n              </li>\n            )}\n          </ul>\n        </div>\n      )}\n    </div>\n  );\n}\n```\n\n```js src/data.js\nexport const recipes = [{\n  id: 'greek-salad',\n  name: 'Greek Salad',\n  ingredients: ['tomatoes', 'cucumber', 'onion', 'olives', 'feta']\n}, {\n  id: 'hawaiian-pizza',\n  name: 'Hawaiian Pizza',\n  ingredients: ['pizza crust', 'pizza sauce', 'mozzarella', 'ham', 'pineapple']\n}, {\n  id: 'hummus',\n  name: 'Hummus',\n  ingredients: ['chickpeas', 'olive oil', 'garlic cloves', 'lemon', 'tahini']\n}];\n```\n\n</Sandpack>\n\n<Solution>\n\n外側の `map` から新しい `Recipe` コンポーネントに JSX をコピーペーストして、その JSX を返すことができます。そして、`recipe.name` を `name` に、`recipe.id` を `id` に変更し、`Recipe` に props として渡します。\n\n<Sandpack>\n\n```js\nimport { recipes } from './data.js';\n\nfunction Recipe({ id, name, ingredients }) {\n  return (\n    <div>\n      <h2>{name}</h2>\n      <ul>\n        {ingredients.map(ingredient =>\n          <li key={ingredient}>\n            {ingredient}\n          </li>\n        )}\n      </ul>\n    </div>\n  );\n}\n\nexport default function RecipeList() {\n  return (\n    <div>\n      <h1>Recipes</h1>\n      {recipes.map(recipe =>\n        <Recipe {...recipe} key={recipe.id} />\n      )}\n    </div>\n  );\n}\n```\n\n```js src/data.js\nexport const recipes = [{\n  id: 'greek-salad',\n  name: 'Greek Salad',\n  ingredients: ['tomatoes', 'cucumber', 'onion', 'olives', 'feta']\n}, {\n  id: 'hawaiian-pizza',\n  name: 'Hawaiian Pizza',\n  ingredients: ['pizza crust', 'pizza sauce', 'mozzarella', 'ham', 'pineapple']\n}, {\n  id: 'hummus',\n  name: 'Hummus',\n  ingredients: ['chickpeas', 'olive oil', 'garlic cloves', 'lemon', 'tahini']\n}];\n```\n\n</Sandpack>\n\nここで `<Recipe {...recipe} key={recipe.id} />` というのは「`recipe` オブジェクトのすべてのプロパティを `Recipe` コンポーネントの props として渡せ」という意味のショートカット構文です。`<Recipe id={recipe.id} name={recipe.name} ingredients={recipe.ingredients} key={recipe.id} />` のように、個々の props を明示的に書いても構いません。\n\n**`key` は `<Recipe>` から返される `<div>` 内ではなく、`<Recipe>` 自体に指定する必要があることに注意してください**。これは、`key` を直接必要としているのはそれを囲んでいる配列の文脈の方だからです。これまでは `<div>` の配列があったので個々の div に `key` が必要でしたが、今存在するのは `<Recipe>` の配列です。別の言い方をすると、コンポーネントを抽出する場合は、コピーペーストする JSX の外に `key` を残すことを忘れないようにしましょう。\n\n</Solution>\n\n#### セパレータ付きリスト {/*list-with-a-separator*/}\n\nこの例では、立花北枝の有名な俳句を、各行を `<p>` タグで囲みつつ表示しています。あなたの仕事は、各段落の間に `<hr />` という区切り線を挿入することです。結果はこのような形にしてください。\n\n```js\n<article>\n  <p>I write, erase, rewrite</p>\n  <hr />\n  <p>Erase again, and then</p>\n  <hr />\n  <p>A poppy blooms.</p>\n</article>\n```\n\n俳句は 3 行しかありませんが、あなたの答えはどのような行数であっても動作するようにしてください。`<hr />` 要素は `<p>` 要素の*間*にのみ現れ、最初や最後には現れないことに注意してください！\n\n<Sandpack>\n\n```js\nconst poem = {\n  lines: [\n    'I write, erase, rewrite',\n    'Erase again, and then',\n    'A poppy blooms.'\n  ]\n};\n\nexport default function Poem() {\n  return (\n    <article>\n      {poem.lines.map((line, index) =>\n        <p key={index}>\n          {line}\n        </p>\n      )}\n    </article>\n  );\n}\n```\n\n```css\nbody {\n  text-align: center;\n}\np {\n  font-family: Georgia, serif;\n  font-size: 20px;\n  font-style: italic;\n}\nhr {\n  margin: 0 120px 0 120px;\n  border: 1px dashed #45c3d8;\n}\n```\n\n</Sandpack>\n\n（俳句の行が途中で入れ替わることは決してないので、今回は特例としてインデックスを key として利用して構いません。）\n\n<Hint>\n\n`map` を手動のループに置き換えるか、フラグメントを使う必要があります。\n\n</Hint>\n\n<Solution>\n\n手動でループを書いて、`<hr />` と `<p>...</p>` を出力用の配列に順番に入れていくという方法がとれます。\n\n<Sandpack>\n\n```js\nconst poem = {\n  lines: [\n    'I write, erase, rewrite',\n    'Erase again, and then',\n    'A poppy blooms.'\n  ]\n};\n\nexport default function Poem() {\n  let output = [];\n\n  // Fill the output array\n  poem.lines.forEach((line, i) => {\n    output.push(\n      <hr key={i + '-separator'} />\n    );\n    output.push(\n      <p key={i + '-text'}>\n        {line}\n      </p>\n    );\n  });\n  // Remove the first <hr />\n  output.shift();\n\n  return (\n    <article>\n      {output}\n    </article>\n  );\n}\n```\n\n```css\nbody {\n  text-align: center;\n}\np {\n  font-family: Georgia, serif;\n  font-size: 20px;\n  font-style: italic;\n}\nhr {\n  margin: 0 120px 0 120px;\n  border: 1px dashed #45c3d8;\n}\n```\n\n</Sandpack>\n\n各セパレータと段落は同じ配列に入るので、元の行インデックスを `key` として使用することはできなくなります。ですが `key={i + '-text'}` のように後ろに文字を付加することで、一意なキーを与えることができます。\n\nあるいは、`<hr />` と `<p>...</p>` を含むフラグメントの配列をレンダーすることもできます。しかし、`<>...</>` という省略記法は key を渡すことをサポートしていないため、明示的に `<Fragment>` と記述する必要があります：\n\n<Sandpack>\n\n```js\nimport { Fragment } from 'react';\n\nconst poem = {\n  lines: [\n    'I write, erase, rewrite',\n    'Erase again, and then',\n    'A poppy blooms.'\n  ]\n};\n\nexport default function Poem() {\n  return (\n    <article>\n      {poem.lines.map((line, i) =>\n        <Fragment key={i}>\n          {i > 0 && <hr />}\n          <p>{line}</p>\n        </Fragment>\n      )}\n    </article>\n  );\n}\n```\n\n```css\nbody {\n  text-align: center;\n}\np {\n  font-family: Georgia, serif;\n  font-size: 20px;\n  font-style: italic;\n}\nhr {\n  margin: 0 120px 0 120px;\n  border: 1px dashed #45c3d8;\n}\n```\n\n</Sandpack>\n\nフラグメント（通常は `<> </>` のように書かれる）によって、余分な `<div>` を増やさずに JSX ノードをグループ化できるということを覚えておきましょう！\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/responding-to-events.md",
    "content": "---\ntitle: イベントへの応答\n---\n\n<Intro>\n\nReact では、JSX に*イベントハンドラ*を追加できます。イベントハンドラとはあなた自身で書く関数であり、クリック、ホバー、フォーム入力へのフォーカスといったユーザインタラクションに応答してトリガされます。\n\n</Intro>\n\n<YouWillLearn>\n\n* イベントハンドラを記述するさまざまな方法\n* 親コンポーネントからイベント処理ロジックを渡す方法\n* イベントの伝播のしかたとそれを停止する方法\n\n</YouWillLearn>\n\n## イベントハンドラの追加 {/*adding-event-handlers*/}\n\nイベントハンドラを追加するには、まず関数を定義し、適切な JSX タグに [props の一部として渡します](/learn/passing-props-to-a-component)。例として、以下にまだ何もしないボタンを示します。\n\n<Sandpack>\n\n```js\nexport default function Button() {\n  return (\n    <button>\n      I don't do anything\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\nユーザがクリックするとメッセージが表示されるようにするには、以下の 3 つの手順を実行します。\n\n1. `Button` コンポーネントの*内部*で `handleClick` という関数を宣言します。\n2. その関数内にロジックを実装します（ここでは `alert` を使ってメッセージを表示）。\n3. `<button>` の JSX に `onClick={handleClick}` を追加します。\n\n<Sandpack>\n\n```js\nexport default function Button() {\n  function handleClick() {\n    alert('You clicked me!');\n  }\n\n  return (\n    <button onClick={handleClick}>\n      Click me\n    </button>\n  );\n}\n```\n\n```css\nbutton { margin-right: 10px; }\n```\n\n</Sandpack>\n\n`handleClick` 関数を定義し、それを `<button>` に [props の一部として渡しました](/learn/passing-props-to-a-component)。この `handleClick` が**イベントハンドラ**です。イベントハンドラ関数は：\n\n* 通常、コンポーネントの*内部*で定義されます。\n* イベント名の先頭に `handle` が付いた名前にします。\n\n慣習的に、イベントハンドラは `handle` の後ろにイベントの名称をつなげた名前にすることが一般的です。`onClick={handleClick}`、`onMouseEnter={handleMouseEnter}` などがよく見られます。\n\nまた、JSX の中でイベントハンドラをインラインで定義することもできます。\n\n```jsx\n<button onClick={function handleClick() {\n  alert('You clicked me!');\n}}>\n```\n\nまたは、より簡潔にアロー関数を使って記述することもできます。\n\n```jsx\n<button onClick={() => {\n  alert('You clicked me!');\n}}>\n```\n\nこれらのスタイルはすべて同等です。インラインのイベントハンドラは、短い関数の場合に便利です。\n\n<Pitfall>\n\nイベントハンドラに渡す関数は、渡すべきなのであって、呼び出すべきではありません。例えば：\n\n| 関数を渡す（正しい）               | 関数を呼び出す（誤り）               |\n| -------------------------------- | ---------------------------------- |\n| `<button onClick={handleClick}>` | `<button onClick={handleClick()}>` |\n\n微妙に違いますね。最初の例では、`handleClick` 関数が `onClick` イベントハンドラとして渡されています。これは、React にこの関数を覚えておいて、ユーザがボタンをクリックしたときにのみコールするように指示します。\n\n2 つ目の例では、`handleClick()` の末尾に `()` があるため、クリックを必要とせず[レンダー](/learn/render-and-commit)の際に*すぐに*関数を実行します。これは、[JSX の `{` と `}`](/learn/javascript-in-jsx-with-curly-braces)の中の JavaScript はすぐに実行されるためです。\n\nインラインでコードを書くときにも、以下のように同様の落とし穴が別の形で現れます。\n\n| 関数を渡す（正しい）                      | 関数を呼び出す（間違い）            |\n| --------------------------------------- | --------------------------------- |\n| `<button onClick={() => alert('...')}>` | `<button onClick={alert('...')}>` |\n\n\nこのようなインラインコードを渡すと、クリックしたときではなく、コンポーネントがレンダーされるたびに実行されます。\n\n```jsx\n// This alert fires when the component renders, not when clicked!\n<button onClick={alert('You clicked me!')}>\n```\n\nイベントハンドラをインラインで定義したい場合は、以下のように無名関数でラップしてください。\n\n```jsx\n<button onClick={() => alert('You clicked me!')}>\n```\n\nこれで、レンダーごとに中のコードが実行されるのではなく、後で呼び出されるべき関数を作成したことになります。\n\nどちらの場合であっても、渡したいのは関数です。\n\n* `<button onClick={handleClick}>` は `handleClick` 関数を渡します。\n* `<button onClick={() => alert('...')}>` は、`() => alert('...')` という関数を渡します。\n\n[アロー関数についての詳細はこちら](https://javascript.info/arrow-functions-basics)。\n\n</Pitfall>\n\n### イベントハンドラでの props の読み取り {/*reading-props-in-event-handlers*/}\n\nイベントハンドラはコンポーネント内に宣言されているため、コンポーネントの props にアクセスできます。以下は、クリックされたときに `message` プロパティの内容をアラートで表示するボタンです。\n\n<Sandpack>\n\n```js\nfunction AlertButton({ message, children }) {\n  return (\n    <button onClick={() => alert(message)}>\n      {children}\n    </button>\n  );\n}\n\nexport default function Toolbar() {\n  return (\n    <div>\n      <AlertButton message=\"Playing!\">\n        Play Movie\n      </AlertButton>\n      <AlertButton message=\"Uploading!\">\n        Upload Image\n      </AlertButton>\n    </div>\n  );\n}\n```\n\n```css\nbutton { margin-right: 10px; }\n```\n\n</Sandpack>\n\nこれにより、これらの 2 つのボタンは異なるメッセージを表示できます。渡されるメッセージを変更してみてください。\n\n### イベントハンドラを props として渡す {/*passing-event-handlers-as-props*/}\n\nよくあるケースとして、親コンポーネントが子のイベントハンドラを指定したい場合があります。ボタンを考えてみましょう：`Button` というコンポーネントには、使用する場所によって、動画を再生する、画像をアップロードするなど、異なる関数を実行させたいことでしょう。\n\nこれを行うには、以下のようにして、コンポーネントが親から受け取った props の一部をイベントハンドラとして渡します：\n\n<Sandpack>\n\n```js\nfunction Button({ onClick, children }) {\n  return (\n    <button onClick={onClick}>\n      {children}\n    </button>\n  );\n}\n\nfunction PlayButton({ movieName }) {\n  function handlePlayClick() {\n    alert(`Playing ${movieName}!`);\n  }\n\n  return (\n    <Button onClick={handlePlayClick}>\n      Play \"{movieName}\"\n    </Button>\n  );\n}\n\nfunction UploadButton() {\n  return (\n    <Button onClick={() => alert('Uploading!')}>\n      Upload Image\n    </Button>\n  );\n}\n\nexport default function Toolbar() {\n  return (\n    <div>\n      <PlayButton movieName=\"Kiki's Delivery Service\" />\n      <UploadButton />\n    </div>\n  );\n}\n```\n\n```css\nbutton { margin-right: 10px; }\n```\n\n</Sandpack>\n\nこの例では、`Toolbar` コンポーネントは `PlayButton` と `UploadButton` をレンダーしています：\n\n- `PlayButton` は `handlePlayClick` を内部 `Button` の `onClick` プロパティとして渡しています。\n- `UploadButton` は `() => alert('Uploading!')` を内部 `Button` の `onClick` プロパティとして渡しています。\n\n最後に、`Button` コンポーネントは props として `onClick` を受け取ります。それを `onClick={onClick}` としてブラウザの組み込み `<button>` に直接渡しています。これにより、クリック時に React は渡された関数を呼び出すようになります。\n\n[デザインシステム](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969)を使用している場合、ボタンなどのコンポーネントはスタイリングを含んでいるが振る舞いは指定されないことが一般的です。代わりに、`PlayButton` や `UploadButton` のようなコンポーネントがイベントハンドラを渡します。\n\n### イベントハンドラの props の命名 {/*naming-event-handler-props*/}\n\n`<button>` や `<div>` のような組み込みコンポーネントは、`onClick` のような[ブラウザのイベント名](/reference/react-dom/components/common#common-props)のみをサポートしています。ただし、独自のコンポーネントを作成する場合、イベントハンドラとなる props を、好きなように命名できます。\n\n慣習として、イベントハンドラのプロップは `on` で始まり、次に大文字の文字が続くようにします。\n\nたとえば、`Button` コンポーネントの props である `onClick` は `onSmash` と命名することも可能です：\n\n<Sandpack>\n\n```js\nfunction Button({ onSmash, children }) {\n  return (\n    <button onClick={onSmash}>\n      {children}\n    </button>\n  );\n}\n\nexport default function App() {\n  return (\n    <div>\n      <Button onSmash={() => alert('Playing!')}>\n        Play Movie\n      </Button>\n      <Button onSmash={() => alert('Uploading!')}>\n        Upload Image\n      </Button>\n    </div>\n  );\n}\n```\n\n```css\nbutton { margin-right: 10px; }\n```\n\n</Sandpack>\n\nこの例の `<button onClick={onSmash}>` を見ると分かるように、ブラウザの `<button>`（小文字）は常に props として `onClick` が必要ですが、カスタム `Button` コンポーネントが受け取る props の名前はあなた次第です！\n\nコンポーネントが複数種類のインタラクションをサポートする場合、イベントハンドラの props をアプリ固有の概念に基づいて命名することができます。例えば、この `Toolbar` コンポーネントは `onPlayMovie` と `onUploadImage` というイベントハンドラを受け取ります：\n\n<Sandpack>\n\n```js\nexport default function App() {\n  return (\n    <Toolbar\n      onPlayMovie={() => alert('Playing!')}\n      onUploadImage={() => alert('Uploading!')}\n    />\n  );\n}\n\nfunction Toolbar({ onPlayMovie, onUploadImage }) {\n  return (\n    <div>\n      <Button onClick={onPlayMovie}>\n        Play Movie\n      </Button>\n      <Button onClick={onUploadImage}>\n        Upload Image\n      </Button>\n    </div>\n  );\n}\n\nfunction Button({ onClick, children }) {\n  return (\n    <button onClick={onClick}>\n      {children}\n    </button>\n  );\n}\n```\n\n```css\nbutton { margin-right: 10px; }\n```\n\n</Sandpack>\n\n`Toolbar` が `onPlayMovie` や `onUploadImage` を*どう扱う*のかを、`App` コンポーネントが知る必要がないことに注意してください。それは `Toolbar` の実装の詳細です。ここでは、`Toolbar` はそれらを `Button` の `onClick` ハンドラとして渡していますが、後でキーボードショートカットでもそれらをトリガするようにすることができます。`onPlayMovie` のようなアプリ固有のインタラクションに基づいて props を名付けることで、後でどのように使用されるかを変更できるという柔軟性が得られます。\n  \n<Note>\n\nイベントハンドラは適切な HTML タグに設定するようにしてください。例えば、クリックを処理するためには、`<div onClick={handleClick}>` ではなく [`<button onClick={handleClick}>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) を使用します。本物のブラウザの `<button>` を使用することで、キーボードナビゲーションのようなブラウザ組み込みの振る舞いが有効になります。ブラウザデフォルトのボタンのスタイルが気に入らず、リンクや他の UI 要素のような見た目にしたい場合、CSS を使うことで実現できます。[アクセシブルなマークアップについて学ぶ](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML)。\n  \n</Note>\n\n## イベント伝播 {/*event-propagation*/}\n\nイベントハンドラは、コンポーネントが持っている可能性のあるどの子が由来であってもイベントをキャッチします。このことをイベントがツリーを \"バブリング (bubble)\" または \"伝播 (propagate)\" する、と表現します。イベントは発生した場所から始まり、ツリーを上に向かって進んでいきます。\n\nこの `<div>` には 2 つのボタンが含まれています。`<div>` も各ボタンも、それぞれ `onClick` ハンドラを持っていますね。ボタンをクリックしたとき、どのハンドラが実行されると思いますか？\n\n<Sandpack>\n\n```js\nexport default function Toolbar() {\n  return (\n    <div className=\"Toolbar\" onClick={() => {\n      alert('You clicked on the toolbar!');\n    }}>\n      <button onClick={() => alert('Playing!')}>\n        Play Movie\n      </button>\n      <button onClick={() => alert('Uploading!')}>\n        Upload Image\n      </button>\n    </div>\n  );\n}\n```\n\n```css\n.Toolbar {\n  background: #aaa;\n  padding: 5px;\n}\nbutton { margin: 5px; }\n```\n\n</Sandpack>\n\nどちらのボタンをクリックしても、最初にそれ自体の `onClick` が実行され、その後で親である `<div>` の `onClick` が実行されます。つまりメッセージが 2 つ表示されます。ツールバー自体をクリックすると、親の `<div>` の `onClick` のみが実行されます。\n\n<Pitfall>\n\nReact では `onScroll` 以外のすべてのイベントが伝播します。`onScroll` は、それをアタッチした JSX タグでのみ機能します。\n\n</Pitfall>\n\n### 伝播の停止 {/*stopping-propagation*/}\n\nイベントハンドラは、**イベントオブジェクト**を唯一の引数として受け取ります。慣習的に、それは \"event\" を意味する `e` と呼ばれています。このオブジェクトを使用して、イベントに関する情報を読み取ることができます。\n\nこのイベントオブジェクトを使い、伝播を止めることもできます。イベントが親コンポーネントに伝わらないようにしたい場合、以下の `Button` コンポーネントのようにして `e.stopPropagation()` を呼び出す必要があります：\n\n<Sandpack>\n\n```js\nfunction Button({ onClick, children }) {\n  return (\n    <button onClick={e => {\n      e.stopPropagation();\n      onClick();\n    }}>\n      {children}\n    </button>\n  );\n}\n\nexport default function Toolbar() {\n  return (\n    <div className=\"Toolbar\" onClick={() => {\n      alert('You clicked on the toolbar!');\n    }}>\n      <Button onClick={() => alert('Playing!')}>\n        Play Movie\n      </Button>\n      <Button onClick={() => alert('Uploading!')}>\n        Upload Image\n      </Button>\n    </div>\n  );\n}\n```\n\n```css\n.Toolbar {\n  background: #aaa;\n  padding: 5px;\n}\nbutton { margin: 5px; }\n```\n\n</Sandpack>\n\nボタンをクリックすると以下のことが起こります。\n\n1. React が `<button>` に渡された `onClick` ハンドラを呼び出す。\n2. そのハンドラは `Button` で定義されており、次のことを行う。\n   * `e.stopPropagation()` を呼び出し、イベントがさらにバブリングされるのを防ぐ。\n   * `Toolbar` コンポーネントから渡された props である `onClick` 関数を呼び出す。\n3. その関数は `Toolbar` コンポーネントで定義されており、そのボタン固有のアラートを表示する。\n4. 伝播が停止されたため、親の `<div>` の `onClick` ハンドラは*実行されない*。\n\n`e.stopPropagation()` の結果、ボタンをクリックすると、アラートが 2 つ（`<button>` と親のツールバーの `<div>` から）ではなく、1 つだけ（`<button>` のみから）表示されるようになります。ボタンをクリックすることと、周囲のツールバーの余白をクリックすることは別物なので、この UI では伝播を止めることが理にかなっています。\n\n<DeepDive>\n\n#### キャプチャフェーズのイベント {/*capture-phase-events*/}\n\nまれに、*伝播が停止されても*子要素のすべてのイベントをキャッチしたいという場合があります。たとえば、伝播のロジックに関係なく、すべてのクリックを分析のため記録したい場合などです。これを行うには、イベント名の末尾に `Capture` を追加します。\n\n```js\n<div onClickCapture={() => { /* this runs first */ }}>\n  <button onClick={e => e.stopPropagation()} />\n  <button onClick={e => e.stopPropagation()} />\n</div>\n```\n\nすべてのイベントは 3 つのフェーズで伝播します。\n\n1. 下方向に移動し、すべての `onClickCapture` ハンドラを呼び出す。\n2. クリックされた要素自体の `onClick` ハンドラを実行する。\n3. 上方向に移動し、すべての `onClick` ハンドラを呼び出す。\n\nキャプチャイベントはルータや分析のようなコードで役立ちますが、アプリケーションコードで使用することはほとんどありません。\n\n</DeepDive>\n\n### 伝播の代わりにハンドラを渡す {/*passing-handlers-as-alternative-to-propagation*/}\n\nこのクリックハンドラは、親から渡された `onClick` を呼び出す*前に*、1 行コードを実行していることに注目してください。\n\n```js {4,5}\nfunction Button({ onClick, children }) {\n  return (\n    <button onClick={e => {\n      e.stopPropagation();\n      onClick();\n    }}>\n      {children}\n    </button>\n  );\n}\n```\n\n親の `onClick` イベントハンドラを呼び出す前に、このハンドラにさらにコードを追加することもできるでしょう。このパターンは、伝播の*代替手段*になります。子コンポーネントにイベントを処理させつつ、親コンポーネントが追加の動作を指定できるようになるのです。伝播とは異なり、これは自動的に起こることではありません。しかし、このパターンの利点は、あるイベントが発生した結果として実行されるコードのすべての繋がりをはっきりと追跡できることです。\n\nイベント伝播に依存していて、どのハンドラがどのような理由で実行されているのかを追跡することが困難な場合は、代わりにこのアプローチを試してください。\n\n### デフォルト動作を防ぐ {/*preventing-default-behavior*/}\n\nブラウザのイベントには、デフォルトの動作が関連付けられているものがあります。例えば、`<form>` の submit イベントは、その中のボタンがクリックされると、デフォルトではページ全体がリロードされます。\n\n<Sandpack>\n\n```js\nexport default function Signup() {\n  return (\n    <form onSubmit={() => alert('Submitting!')}>\n      <input />\n      <button>Send</button>\n    </form>\n  );\n}\n```\n\n```css\nbutton { margin-left: 5px; }\n```\n\n</Sandpack>\n\nイベントオブジェクトの `e.preventDefault()` を呼び出して、これを防ぐことができます。\n\n<Sandpack>\n\n```js\nexport default function Signup() {\n  return (\n    <form onSubmit={e => {\n      e.preventDefault();\n      alert('Submitting!');\n    }}>\n      <input />\n      <button>Send</button>\n    </form>\n  );\n}\n```\n\n```css\nbutton { margin-left: 5px; }\n```\n\n</Sandpack>\n\n`e.stopPropagation()` と `e.preventDefault()` を混同しないでください。どちらも有用ですが、両者は無関係です。\n\n* [`e.stopPropagation()`](https://developer.mozilla.org/docs/Web/API/Event/stopPropagation) は、ツリーの上側にあるタグにアタッチされたイベントハンドラが発火しないようにします。\n* [`e.preventDefault()`](https://developer.mozilla.org/docs/Web/API/Event/preventDefault) は、数は少ないですがイベントがブラウザデフォルトの動作を持っていた場合に、それを抑制します。\n\n## イベントハンドラは副作用を持っていても構わない？ {/*can-event-handlers-have-side-effects*/}\n\nもちろんです！ イベントハンドラは副作用のための最適な場所です。\n\nレンダー関数とは異なり、イベントハンドラは[純関数 (pure function)](/learn/keeping-components-pure) である必要はないので、何かを*変更*するのに最適な場所です。たとえば、入力値をタイプに応じて変更する、ボタンの押下に応じてリストを変更する、などです。ただし、情報を変更するためには、まずそれを格納する方法が必要です。React では、これは [state、コンポーネントのメモリ](/learn/state-a-components-memory)を使用して行います。次のページでそのすべてを学びます。\n\n<Recap>\n\n* イベントは、`<button>` などの要素に関数を props として渡すことで処理できます。\n* イベントハンドラは渡す必要があります。**呼び出してはいけません！** `onClick={handleClick}` とするのであって、`onClick={handleClick()}` としてはいけません。\n* イベントハンドラ関数は別途定義することも、インラインで定義することもできます。\n* イベントハンドラはコンポーネント内に定義されているため、props にアクセスできます。\n* 親でイベントハンドラを宣言し、子に props として渡すことができます。\n* イベントハンドラ props を定義する際にアプリケーション固有の名前をつけることができます。\n* イベントは上方向に伝播します。これを防ぐには、最初の引数を使って `e.stopPropagation()` を呼び出します。\n* イベントは、望ましくないデフォルトのブラウザ動作を持つことがあります。これを防ぐには、`e.preventDefault()` を呼ぶ必要があります。\n* 子コンポーネントでイベントハンドラを定義して props から受け取ったハンドラを明示的に呼び出すことは、伝播の代替手段として良い方法です。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### イベントハンドラを修正 {/*fix-an-event-handler*/}\n\nこのボタンをクリックすると、ページの背景が白と黒の間で切り替わることになっています。ただし、クリックしても何も起こりません。問題を修正してください（`handleClick` 内部のロジックについては心配無用です。そこは問題ありません）。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [5, 7]}}\nexport default function LightSwitch() {\n  function handleClick() {\n    let bodyStyle = document.body.style;\n    if (bodyStyle.backgroundColor === 'black') {\n      bodyStyle.backgroundColor = 'white';\n    } else {\n      bodyStyle.backgroundColor = 'black';\n    }\n  }\n\n  return (\n    <button onClick={handleClick()}>\n      Toggle the lights\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n問題は、`<button onClick={handleClick()}>` がレンダー時に `handleClick` 関数を*呼び出す*のではなく、*渡す*べきだということです。`()` の呼び出しを削除して `<button onClick={handleClick}>` にすることで、問題が修正されます。\n\n<Sandpack>\n\n```js\nexport default function LightSwitch() {\n  function handleClick() {\n    let bodyStyle = document.body.style;\n    if (bodyStyle.backgroundColor === 'black') {\n      bodyStyle.backgroundColor = 'white';\n    } else {\n      bodyStyle.backgroundColor = 'black';\n    }\n  }\n\n  return (\n    <button onClick={handleClick}>\n      Toggle the lights\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\nまたは、別の関数にラップして、`<button onClick={() => handleClick()}>` のようにすることもできます。\n\n<Sandpack>\n\n```js\nexport default function LightSwitch() {\n  function handleClick() {\n    let bodyStyle = document.body.style;\n    if (bodyStyle.backgroundColor === 'black') {\n      bodyStyle.backgroundColor = 'white';\n    } else {\n      bodyStyle.backgroundColor = 'black';\n    }\n  }\n\n  return (\n    <button onClick={() => handleClick()}>\n      Toggle the lights\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n#### イベントを接続 {/*wire-up-the-events*/}\n\nこの `ColorSwitch` コンポーネントは、ボタンをレンダーしています。このボタンはページの色を変更することになっています。親から props として受け取っている `onChangeColor` イベントハンドラをボタンに接続して、ボタンをクリックすると色が変わるようにしてください。\n\nまた、ボタンをクリックすると、ページのクリックカウンタも増加していることに注意してください。この親コンポーネントを書いた同僚は、`onChangeColor` 関数にはカウンタを増やすコードなどないと言い張っています。ではほかに何かが起こっているのでしょうか？ ボタンをクリックしても*色が変わるだけ*で、カウンタは*増えない*ように、修正してください。\n\n<Sandpack>\n\n```js src/ColorSwitch.js active\nexport default function ColorSwitch({\n  onChangeColor\n}) {\n  return (\n    <button>\n      Change color\n    </button>\n  );\n}\n```\n\n```js src/App.js hidden\nimport { useState } from 'react';\nimport ColorSwitch from './ColorSwitch.js';\n\nexport default function App() {\n  const [clicks, setClicks] = useState(0);\n\n  function handleClickOutside() {\n    setClicks(c => c + 1);\n  }\n\n  function getRandomLightColor() {\n    let r = 150 + Math.round(100 * Math.random());\n    let g = 150 + Math.round(100 * Math.random());\n    let b = 150 + Math.round(100 * Math.random());\n    return `rgb(${r}, ${g}, ${b})`;\n  }\n\n  function handleChangeColor() {\n    let bodyStyle = document.body.style;\n    bodyStyle.backgroundColor = getRandomLightColor();\n  }\n\n  return (\n    <div style={{ width: '100%', height: '100%' }} onClick={handleClickOutside}>\n      <ColorSwitch onChangeColor={handleChangeColor} />\n      <br />\n      <br />\n      <h2>Clicks on the page: {clicks}</h2>\n    </div>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution>\n\nまずはイベントハンドラを追加する必要があります。`<button onClick={onChangeColor}>` のようになります。\n\nただし、これによりカウンタが増加する問題が発生します。同僚が主張している通り `onChangeColor` がこれをやっているのでないのであれば、このイベントが上に伝播して、どこかのハンドラがそれを行っているのでしょう。この問題を解決するには、伝播を停止する必要があります。ただし `onChangeColor` を呼び出すことを忘れないでください。\n\n<Sandpack>\n\n```js src/ColorSwitch.js active\nexport default function ColorSwitch({\n  onChangeColor\n}) {\n  return (\n    <button onClick={e => {\n      e.stopPropagation();\n      onChangeColor();\n    }}>\n      Change color\n    </button>\n  );\n}\n```\n\n```js src/App.js hidden\nimport { useState } from 'react';\nimport ColorSwitch from './ColorSwitch.js';\n\nexport default function App() {\n  const [clicks, setClicks] = useState(0);\n\n  function handleClickOutside() {\n    setClicks(c => c + 1);\n  }\n\n  function getRandomLightColor() {\n    let r = 150 + Math.round(100 * Math.random());\n    let g = 150 + Math.round(100 * Math.random());\n    let b = 150 + Math.round(100 * Math.random());\n    return `rgb(${r}, ${g}, ${b})`;\n  }\n\n  function handleChangeColor() {\n    let bodyStyle = document.body.style;\n    bodyStyle.backgroundColor = getRandomLightColor();\n  }\n\n  return (\n    <div style={{ width: '100%', height: '100%' }} onClick={handleClickOutside}>\n      <ColorSwitch onChangeColor={handleChangeColor} />\n      <br />\n      <br />\n      <h2>Clicks on the page: {clicks}</h2>\n    </div>\n  );\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/reusing-logic-with-custom-hooks.md",
    "content": "---\ntitle: 'カスタムフックでロジックを再利用する'\n---\n\n<Intro>\n\nReact には `useState`、`useContext`、`useEffect` など複数の組み込みフックが存在します。しかし、データの取得やユーザのオンライン状態の監視、チャットルームへの接続など、より特化した目的のためのフックが欲しいこともあります。React にこれらのフックはありませんが、アプリケーションの要求に合わせて独自のフックを作成することが可能です。\n\n</Intro>\n\n<YouWillLearn>\n\n- カスタムフックとは何で、どのように自分で作成するのか\n- コンポーネント間でロジックを再利用する方法\n- カスタムフックの命名や構成の方法\n- カスタムフックを抽出するタイミングと理由\n\n</YouWillLearn>\n\n## カスタムフック：コンポーネント間でのロジック共有 {/*custom-hooks-sharing-logic-between-components*/}\n\nネットワークに大きく依存するアプリを開発していると想像してください（ほとんどのアプリがそうですが）。アプリの使用中にユーザのネットワーク接続が急に切断された場合に、ユーザに警告を表示したいとします。どのようにすればよいでしょうか？ コンポーネントには以下の 2 つが必要になるようです。\n\n1. ネットワークがオンラインかどうかを保持する state。\n2. グローバルの [`online`](https://developer.mozilla.org/en-US/docs/Web/API/Window/online_event) および [`offline`](https://developer.mozilla.org/en-US/docs/Web/API/Window/offline_event) イベントにリスナを登録し、上記の state を更新するエフェクト。\n\nこれにより、コンポーネントはネットワークの状態と[同期](/learn/synchronizing-with-effects)するようになります。まずは以下のようなコードができるでしょう。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function StatusBar() {\n  const [isOnline, setIsOnline] = useState(true);\n  useEffect(() => {\n    function handleOnline() {\n      setIsOnline(true);\n    }\n    function handleOffline() {\n      setIsOnline(false);\n    }\n    window.addEventListener('online', handleOnline);\n    window.addEventListener('offline', handleOffline);\n    return () => {\n      window.removeEventListener('online', handleOnline);\n      window.removeEventListener('offline', handleOffline);\n    };\n  }, []);\n\n  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;\n}\n```\n\n</Sandpack>\n\nネットワークをオン・オフしてみて、この `StatusBar` が操作に反応してどのように更新されるか観察してみてください。\n\nさて、別のコンポーネント*でも*同じロジックを使用したくなったところを想像してください。ネットワークがオフの間は \"Save\" の代わりに \"Reconnecting...\" と表示されて無効になるような保存ボタンを実装したいとします。\n\nまず、`isOnline` state とエフェクトを、`SaveButton` にコピー・ペーストしてみましょう。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function SaveButton() {\n  const [isOnline, setIsOnline] = useState(true);\n  useEffect(() => {\n    function handleOnline() {\n      setIsOnline(true);\n    }\n    function handleOffline() {\n      setIsOnline(false);\n    }\n    window.addEventListener('online', handleOnline);\n    window.addEventListener('offline', handleOffline);\n    return () => {\n      window.removeEventListener('online', handleOnline);\n      window.removeEventListener('offline', handleOffline);\n    };\n  }, []);\n\n  function handleSaveClick() {\n    console.log('✅ Progress saved');\n  }\n\n  return (\n    <button disabled={!isOnline} onClick={handleSaveClick}>\n      {isOnline ? 'Save progress' : 'Reconnecting...'}\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\nネットワークをオフにするとボタンの外観が変わることを確認してください。\n\nこれらの 2 つのコンポーネントはうまく動作していますが、それらの間でロジックが重複しているのは残念な感じがします。*視覚的な外観*は異なるにせよ、ロジックはそれらの間で再利用したいと思うことでしょう。\n\n### コンポーネントから独自のカスタムフックを抽出する {/*extracting-your-own-custom-hook-from-a-component*/}\n\n[`useState`](/reference/react/useState) や [`useEffect`](/reference/react/useEffect) と同様に、組み込みの `useOnlineStatus` というフックがあるところを、ちょっと想像してみてください。それがあれば、これらのコンポーネントを簡略化し、両者で重複しているコードを取り除けるでしょう。\n\n```js {2,7}\nfunction StatusBar() {\n  const isOnline = useOnlineStatus();\n  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;\n}\n\nfunction SaveButton() {\n  const isOnline = useOnlineStatus();\n\n  function handleSaveClick() {\n    console.log('✅ Progress saved');\n  }\n\n  return (\n    <button disabled={!isOnline} onClick={handleSaveClick}>\n      {isOnline ? 'Save progress' : 'Reconnecting...'}\n    </button>\n  );\n}\n```\n\nこのような組み込みのフックは存在しませんが、自分で書くことは可能です。`useOnlineStatus` という関数を宣言して、先ほど作成したコンポーネントから、重複しているコードをすべて移動しましょう。\n\n```js {2-16}\nfunction useOnlineStatus() {\n  const [isOnline, setIsOnline] = useState(true);\n  useEffect(() => {\n    function handleOnline() {\n      setIsOnline(true);\n    }\n    function handleOffline() {\n      setIsOnline(false);\n    }\n    window.addEventListener('online', handleOnline);\n    window.addEventListener('offline', handleOffline);\n    return () => {\n      window.removeEventListener('online', handleOnline);\n      window.removeEventListener('offline', handleOffline);\n    };\n  }, []);\n  return isOnline;\n}\n```\n\n関数の最後で `isOnline` を返します。これにより、コンポーネント側でその値を読み取ることができるようになります。\n\n<Sandpack>\n\n```js\nimport { useOnlineStatus } from './useOnlineStatus.js';\n\nfunction StatusBar() {\n  const isOnline = useOnlineStatus();\n  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;\n}\n\nfunction SaveButton() {\n  const isOnline = useOnlineStatus();\n\n  function handleSaveClick() {\n    console.log('✅ Progress saved');\n  }\n\n  return (\n    <button disabled={!isOnline} onClick={handleSaveClick}>\n      {isOnline ? 'Save progress' : 'Reconnecting...'}\n    </button>\n  );\n}\n\nexport default function App() {\n  return (\n    <>\n      <SaveButton />\n      <StatusBar />\n    </>\n  );\n}\n```\n\n```js src/useOnlineStatus.js\nimport { useState, useEffect } from 'react';\n\nexport function useOnlineStatus() {\n  const [isOnline, setIsOnline] = useState(true);\n  useEffect(() => {\n    function handleOnline() {\n      setIsOnline(true);\n    }\n    function handleOffline() {\n      setIsOnline(false);\n    }\n    window.addEventListener('online', handleOnline);\n    window.addEventListener('offline', handleOffline);\n    return () => {\n      window.removeEventListener('online', handleOnline);\n      window.removeEventListener('offline', handleOffline);\n    };\n  }, []);\n  return isOnline;\n}\n```\n\n</Sandpack>\n\nネットワークのオン・オフを切り替えることで、両方のコンポーネントが更新されることを確認してください。\n\nこれで、コンポーネント間のロジックの重複が減りました。**さらに重要なのは、コンポーネント内のコードが、「オンラインステータスを使用 (use) する」という、*何をしたいのか*の記述になっているということです。*どのようにして実現するのか*（ブラウザのイベントに登録する）ではありません**。\n\nロジックをカスタムフックに抽出することで、外部システムやブラウザ API とのやり取りに関する面倒な詳細を隠蔽することができます。あなたのコンポーネントのコードは、実装方法ではなく意図を表現するようになるのです。\n\n### フックの名前は常に `use` で始める {/*hook-names-always-start-with-use*/}\n\nReact アプリケーションはコンポーネントから構築されます。コンポーネントは、組み込みのものやカスタムのものなど、フックから構築されます。他の人が作成したカスタムフックをよく使うことになりますが、時には自分で書くこともあるでしょう！\n\n以下の命名規則に従う必要があります。\n\n1. **React コンポーネントの名前は大文字で始まる必要があります**。例えば、`StatusBar` や `SaveButton` などです。React コンポーネントは、JSX のような、React が表示方法を知っているものを返す必要もあります。\n2. **フックの名前は `use` で始めて大文字を続ける必要があります**。例えば、[`useState`](/reference/react/useState)（組み込みのもの）や `useOnlineStatus`（上述のようなカスタムのもの）などです。フックは任意の値を返すことができます。\n\nこの慣習により、コンポーネントを見るだけで、その中の state、エフェクト、その他の React 機能がどこに「隠れている」可能性があるか、常に把握できることが保証されます。例えば、コンポーネント内で `getColor()` 関数の呼び出しを見た場合、名前が `use` で始まっていないので React の state が内部に含まれている可能性はありません。しかし `useOnlineStatus()` のような関数呼び出しは、内部で他のフックを呼び出している可能性が高いです！\n\n<Note>\n\nリンタが [React 用に設定されている場合](/learn/editor-setup#linting)、この命名規約が強制されます。上のサンドボックスにスクロールして、`useOnlineStatus` を `getOnlineStatus` に変更してみてください。リンタは `useState` や `useEffect` をその中で呼び出すことを許さなくなります。フックやコンポーネントだけが他のフックを呼び出すことができます！\n\n</Note>\n\n<DeepDive>\n\n#### レンダー中に呼び出されるすべての関数を use プレフィックスで始めるべきか？ {/*should-all-functions-called-during-rendering-start-with-the-use-prefix*/}\n\nいいえ。フックを*呼び出さない*関数は、フックである必要はありません。\n\n関数がフックを呼び出さない場合は、`use` プレフィックスを避けてください。代わりに、`use` プレフィックス*なし*の通常の関数として記述してください。例えば、以下の `useSorted` はフックを呼び出さないので、代わりに `getSorted` という名前にしましょう。\n\n```js\n// 🔴 Avoid: A Hook that doesn't use Hooks\nfunction useSorted(items) {\n  return items.slice().sort();\n}\n\n// ✅ Good: A regular function that doesn't use Hooks\nfunction getSorted(items) {\n  return items.slice().sort();\n}\n```\n\nこれにより、コードはこの通常の関数を、条件分岐内を含むどんな場所からでも呼び出すことができます。\n\n```js\nfunction List({ items, shouldSort }) {\n  let displayedItems = items;\n  if (shouldSort) {\n    // ✅ It's ok to call getSorted() conditionally because it's not a Hook\n    displayedItems = getSorted(items);\n  }\n  // ...\n}\n```\n\n関数の内部で 1 つ以上のフックを使用している場合は、`use` プレフィックスを付ける（つまりフックにする）必要があります。\n\n```js\n// ✅ Good: A Hook that uses other Hooks\nfunction useAuth() {\n  return useContext(Auth);\n}\n```\n\n厳密には、これは React によって強制されているわけではありません。原理上は、他のフックを呼び出さないフックを作成することは可能です。混乱を招き余計な制限が加わるため、このようなパターンは避けるのが賢明です。ただし、まれにこれが役立つ場合もあります。例えば、関数が現在はフックを使用していない場合でも、将来的にフック呼び出しを追加する予定があるかもしれません。その場合、`use` プレフィックスを使って名前を付けておくことは理にかなっているでしょう。\n\n```js {3-4}\n// ✅ Good: A Hook that will likely use some other Hooks later\nfunction useAuth() {\n  // TODO: Replace with this line when authentication is implemented:\n  // return useContext(Auth);\n  return TEST_USER;\n}\n```\n\nこうすれば、コンポーネントはこのコードを条件分岐内で呼び出すことができなくなります。中でフック呼び出しを実際に追加したときに、このことが重要になります。現在も将来も内部でフックを使用する予定がない場合は、フックにしないでください。\n\n</DeepDive>\n\n### カスタムフックは state 自体ではなく、state を使うロジックを共有する {/*custom-hooks-let-you-share-stateful-logic-not-state-itself*/}\n\n前の例では、ネットワークをオンまたはオフに切り替えると、両方のコンポーネントが同時に更新されました。しかし、`isOnline` という単一の state 変数がそれらの間で共有されていると考えるのは間違いです。こちらのコードを見てください。\n\n```js {2,7}\nfunction StatusBar() {\n  const isOnline = useOnlineStatus();\n  // ...\n}\n\nfunction SaveButton() {\n  const isOnline = useOnlineStatus();\n  // ...\n}\n```\n\nこれは、重複を抽出する前と同じ方法で動作します。\n\n```js {2-5,10-13}\nfunction StatusBar() {\n  const [isOnline, setIsOnline] = useState(true);\n  useEffect(() => {\n    // ...\n  }, []);\n  // ...\n}\n\nfunction SaveButton() {\n  const [isOnline, setIsOnline] = useState(true);\n  useEffect(() => {\n    // ...\n  }, []);\n  // ...\n}\n```\n\nこれらは、完全に独立した state 変数とエフェクトです！ 同時に同じ値になっているのは、たまたま（ネットワークがオンかどうかという）同一の外部の値と同期させたからです。\n\nより分かりやすく説明するために、別の例を考えてみましょう。この `Form` コンポーネントを考えてみてください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [firstName, setFirstName] = useState('Mary');\n  const [lastName, setLastName] = useState('Poppins');\n\n  function handleFirstNameChange(e) {\n    setFirstName(e.target.value);\n  }\n\n  function handleLastNameChange(e) {\n    setLastName(e.target.value);\n  }\n\n  return (\n    <>\n      <label>\n        First name:\n        <input value={firstName} onChange={handleFirstNameChange} />\n      </label>\n      <label>\n        Last name:\n        <input value={lastName} onChange={handleLastNameChange} />\n      </label>\n      <p><b>Good morning, {firstName} {lastName}.</b></p>\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 10px; }\n```\n\n</Sandpack>\n\n各フォームフィールドに対応して、繰り返しのロジックがあります。\n\n1. state 変数（`firstName` と `lastName`）。\n1. change ハンドラ（`handleFirstNameChange` と `handleLastNameChange`）。\n1. 対応する入力フィールドに `value` と `onChange` 属性を指定するための JSX。\n\nこの繰り返しのロジックを `useFormInput` というカスタムフックに抽出することができます。\n\n<Sandpack>\n\n```js\nimport { useFormInput } from './useFormInput.js';\n\nexport default function Form() {\n  const firstNameProps = useFormInput('Mary');\n  const lastNameProps = useFormInput('Poppins');\n\n  return (\n    <>\n      <label>\n        First name:\n        <input {...firstNameProps} />\n      </label>\n      <label>\n        Last name:\n        <input {...lastNameProps} />\n      </label>\n      <p><b>Good morning, {firstNameProps.value} {lastNameProps.value}.</b></p>\n    </>\n  );\n}\n```\n\n```js src/useFormInput.js active\nimport { useState } from 'react';\n\nexport function useFormInput(initialValue) {\n  const [value, setValue] = useState(initialValue);\n\n  function handleChange(e) {\n    setValue(e.target.value);\n  }\n\n  const inputProps = {\n    value: value,\n    onChange: handleChange\n  };\n\n  return inputProps;\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 10px; }\n```\n\n</Sandpack>\n\nコード内で宣言されているのは `value` という *1 つの* state 変数だけであることに注目してください。\n\nしかし `Form` コンポーネントは `useFormInput` を *2 回* 呼び出しています。\n\n```js\nfunction Form() {\n  const firstNameProps = useFormInput('Mary');\n  const lastNameProps = useFormInput('Poppins');\n  // ...\n```\n\nこれが、2 つの別々の state 変数を宣言するのと同じように動作する理由です！\n\n**カスタムフックは、*state 自体*ではなく、*state を扱うロジック*を共有できるようにするためのものです。フックの呼び出しは、同じフックの他の場所からの呼び出しとは完全に独立しています**。これが、上記の 2 つのサンドボックスが完全に同等である理由です。よろしければ、スクロールして上に戻って見比べてみてください。カスタムフックを抽出する前と後で、挙動は全く同一です。\n\n複数のコンポーネント間で state 自体を共有する必要がある場合は、[リフトアップして下に渡す](/learn/sharing-state-between-components)ようにしてください。\n\n## フック間でリアクティブな値を渡す {/*passing-reactive-values-between-hooks*/}\n\nカスタムフック内のコードは、コンポーネントの再レンダーごとに実行されます。そのため、コンポーネントと同様に、カスタムフックは[純粋である必要があります](/learn/keeping-components-pure)。カスタムフックのコードは、コンポーネントの本体の一部だと考えてください！\n\nカスタムフックはコンポーネントと一緒に再レンダーされるため、常に最新の props と state を受け取ります。どういうことか理解するために、以下のチャットルームの例を考えてみましょう。サーバの URL やチャットルームを変更してみてください。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ChatRoom from './ChatRoom.js';\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n      />\n    </>\n  );\n}\n```\n\n```js src/ChatRoom.js active\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\nimport { showNotification } from './notifications.js';\n\nexport default function ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useEffect(() => {\n    const options = {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n    const connection = createConnection(options);\n    connection.on('message', (msg) => {\n      showNotification('New message: ' + msg);\n    });\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId, serverUrl]);\n\n  return (\n    <>\n      <label>\n        Server URL:\n        <input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />\n      </label>\n      <h1>Welcome to the {roomId} room!</h1>\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  if (typeof serverUrl !== 'string') {\n    throw Error('Expected serverUrl to be a string. Received: ' + serverUrl);\n  }\n  if (typeof roomId !== 'string') {\n    throw Error('Expected roomId to be a string. Received: ' + roomId);\n  }\n  let intervalId;\n  let messageCallback;\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n      clearInterval(intervalId);\n      intervalId = setInterval(() => {\n        if (messageCallback) {\n          if (Math.random() > 0.5) {\n            messageCallback('hey')\n          } else {\n            messageCallback('lol');\n          }\n        }\n      }, 3000);\n    },\n    disconnect() {\n      clearInterval(intervalId);\n      messageCallback = null;\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl + '');\n    },\n    on(event, callback) {\n      if (messageCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'message') {\n        throw Error('Only \"message\" event is supported.');\n      }\n      messageCallback = callback;\n    },\n  };\n}\n```\n\n```js src/notifications.js\nimport Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\n\nexport function showNotification(message, theme = 'dark') {\n  Toastify({\n    text: message,\n    duration: 2000,\n    gravity: 'top',\n    position: 'right',\n    style: {\n      background: theme === 'dark' ? 'black' : 'white',\n      color: theme === 'dark' ? 'white' : 'black',\n    },\n  }).showToast();\n}\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"toastify-js\": \"1.12.0\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n`serverUrl` や `roomId` を変更すると、エフェクトは[変更に「反応」して](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values)再同期されます。コンソールのメッセージを見ると、エフェクトの依存配列に変更があるたびにチャットが再接続されていることがわかります。\n\n次に、エフェクトのコードをカスタムフックに移動します。\n\n```js {2-13}\nexport function useChatRoom({ serverUrl, roomId }) {\n  useEffect(() => {\n    const options = {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n    const connection = createConnection(options);\n    connection.connect();\n    connection.on('message', (msg) => {\n      showNotification('New message: ' + msg);\n    });\n    return () => connection.disconnect();\n  }, [roomId, serverUrl]);\n}\n```\n\nこれにより、`ChatRoom` コンポーネントは単にカスタムフックを呼び出せばよく、内部でどのように動作するかを気にしなくてもよくなります。\n\n```js {4-7}\nexport default function ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useChatRoom({\n    roomId: roomId,\n    serverUrl: serverUrl\n  });\n\n  return (\n    <>\n      <label>\n        Server URL:\n        <input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />\n      </label>\n      <h1>Welcome to the {roomId} room!</h1>\n    </>\n  );\n}\n```\n\nこれはずっとシンプルに見えます！（しかしやっていることは同じです。）\n\n依然として props や state の変更に対しロジックが*反応している*ことに注意してください。サーバ URL やルームを編集してみてください。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ChatRoom from './ChatRoom.js';\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n      />\n    </>\n  );\n}\n```\n\n```js src/ChatRoom.js active\nimport { useState } from 'react';\nimport { useChatRoom } from './useChatRoom.js';\n\nexport default function ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useChatRoom({\n    roomId: roomId,\n    serverUrl: serverUrl\n  });\n\n  return (\n    <>\n      <label>\n        Server URL:\n        <input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />\n      </label>\n      <h1>Welcome to the {roomId} room!</h1>\n    </>\n  );\n}\n```\n\n```js src/useChatRoom.js\nimport { useEffect } from 'react';\nimport { createConnection } from './chat.js';\nimport { showNotification } from './notifications.js';\n\nexport function useChatRoom({ serverUrl, roomId }) {\n  useEffect(() => {\n    const options = {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n    const connection = createConnection(options);\n    connection.connect();\n    connection.on('message', (msg) => {\n      showNotification('New message: ' + msg);\n    });\n    return () => connection.disconnect();\n  }, [roomId, serverUrl]);\n}\n```\n\n```js src/chat.js\nexport function createConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  if (typeof serverUrl !== 'string') {\n    throw Error('Expected serverUrl to be a string. Received: ' + serverUrl);\n  }\n  if (typeof roomId !== 'string') {\n    throw Error('Expected roomId to be a string. Received: ' + roomId);\n  }\n  let intervalId;\n  let messageCallback;\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n      clearInterval(intervalId);\n      intervalId = setInterval(() => {\n        if (messageCallback) {\n          if (Math.random() > 0.5) {\n            messageCallback('hey')\n          } else {\n            messageCallback('lol');\n          }\n        }\n      }, 3000);\n    },\n    disconnect() {\n      clearInterval(intervalId);\n      messageCallback = null;\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl + '');\n    },\n    on(event, callback) {\n      if (messageCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'message') {\n        throw Error('Only \"message\" event is supported.');\n      }\n      messageCallback = callback;\n    },\n  };\n}\n```\n\n```js src/notifications.js\nimport Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\n\nexport function showNotification(message, theme = 'dark') {\n  Toastify({\n    text: message,\n    duration: 2000,\n    gravity: 'top',\n    position: 'right',\n    style: {\n      background: theme === 'dark' ? 'black' : 'white',\n      color: theme === 'dark' ? 'white' : 'black',\n    },\n  }).showToast();\n}\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"toastify-js\": \"1.12.0\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\nここでは、あるフックの返り値を取得して…\n\n```js {2}\nexport default function ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useChatRoom({\n    roomId: roomId,\n    serverUrl: serverUrl\n  });\n  // ...\n```\n\n…それを別のフックに入力として渡しています。\n\n```js {6}\nexport default function ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useChatRoom({\n    roomId: roomId,\n    serverUrl: serverUrl\n  });\n  // ...\n```\n\n`ChatRoom` コンポーネントが再レンダーされるたびに、あなたのフックには最新の `roomId` と `serverUrl` が渡されます。したがって、再レンダー後にこれらの値が異なる場合にはエフェクトがチャットの再接続を行います。（オーディオやビデオ処理ソフトウェアを使ったことがある場合、このようなフックのチェーンは、視覚エフェクトやオーディオエフェクトのチェーンに似ていると感じるかもしれません。`useState` の出力が `useChatRoom` の入力に \"フィードイン\" しています。）\n\n### カスタムフックにイベントハンドラを渡す {/*passing-event-handlers-to-custom-hooks*/}\n\n`useChatRoom` がより多くのコンポーネントで使用されるようになると、コンポーネント側でその動作をカスタマイズしたくなってくるでしょう。例えば現在のところ、メッセージが届いたときの処理ロジックはフック内にハードコードされています。\n\n```js {9-11}\nexport function useChatRoom({ serverUrl, roomId }) {\n  useEffect(() => {\n    const options = {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n    const connection = createConnection(options);\n    connection.connect();\n    connection.on('message', (msg) => {\n      showNotification('New message: ' + msg);\n    });\n    return () => connection.disconnect();\n  }, [roomId, serverUrl]);\n}\n```\n\nこのロジックをコンポーネント側に戻したいとしましょう。\n\n```js {7-9}\nexport default function ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useChatRoom({\n    roomId: roomId,\n    serverUrl: serverUrl,\n    onReceiveMessage(msg) {\n      showNotification('New message: ' + msg);\n    }\n  });\n  // ...\n```\n\nこれを実現するために、カスタムフックを変更して、`onReceiveMessage` を名前付きオプションの 1 つとして受け取るようにします。\n\n```js {1,10,13}\nexport function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {\n  useEffect(() => {\n    const options = {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n    const connection = createConnection(options);\n    connection.connect();\n    connection.on('message', (msg) => {\n      onReceiveMessage(msg);\n    });\n    return () => connection.disconnect();\n  }, [roomId, serverUrl, onReceiveMessage]); // ✅ All dependencies declared\n}\n```\n\nこれで動作しますが、カスタムフックがイベントハンドラを受け取る場合、改善できることがもう 1 つあります。\n\n`onReceiveMessage` を依存値として追加すると、コンポーネントが再レンダーされるたびにチャットが再接続されてしまうため、あまり望ましくありません。[このイベントハンドラをエフェクトイベント (Effect Event) にラップして、依存配列から取り除きます](/learn/removing-effect-dependencies#wrapping-an-event-handler-from-the-props)。\n\n```js {1,4,5,15,18}\nimport { useEffect, useEffectEvent } from 'react';\n// ...\n\nexport function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {\n  const onMessage = useEffectEvent(onReceiveMessage);\n\n  useEffect(() => {\n    const options = {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n    const connection = createConnection(options);\n    connection.connect();\n    connection.on('message', (msg) => {\n      onMessage(msg);\n    });\n    return () => connection.disconnect();\n  }, [roomId, serverUrl]); // ✅ All dependencies declared\n}\n```\n\nこれで、`ChatRoom` コンポーネントが再レンダーされるたびにチャットが再接続されることはなくなります。以下が、イベントハンドラをカスタムフックに渡す完全なデモです。試してみてください。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ChatRoom from './ChatRoom.js';\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n      />\n    </>\n  );\n}\n```\n\n```js src/ChatRoom.js active\nimport { useState } from 'react';\nimport { useChatRoom } from './useChatRoom.js';\nimport { showNotification } from './notifications.js';\n\nexport default function ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useChatRoom({\n    roomId: roomId,\n    serverUrl: serverUrl,\n    onReceiveMessage(msg) {\n      showNotification('New message: ' + msg);\n    }\n  });\n\n  return (\n    <>\n      <label>\n        Server URL:\n        <input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />\n      </label>\n      <h1>Welcome to the {roomId} room!</h1>\n    </>\n  );\n}\n```\n\n```js src/useChatRoom.js\nimport { useEffect } from 'react';\nimport { useEffectEvent } from 'react';\nimport { createConnection } from './chat.js';\n\nexport function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {\n  const onMessage = useEffectEvent(onReceiveMessage);\n\n  useEffect(() => {\n    const options = {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n    const connection = createConnection(options);\n    connection.connect();\n    connection.on('message', (msg) => {\n      onMessage(msg);\n    });\n    return () => connection.disconnect();\n  }, [roomId, serverUrl]);\n}\n```\n\n```js src/chat.js\nexport function createConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  if (typeof serverUrl !== 'string') {\n    throw Error('Expected serverUrl to be a string. Received: ' + serverUrl);\n  }\n  if (typeof roomId !== 'string') {\n    throw Error('Expected roomId to be a string. Received: ' + roomId);\n  }\n  let intervalId;\n  let messageCallback;\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n      clearInterval(intervalId);\n      intervalId = setInterval(() => {\n        if (messageCallback) {\n          if (Math.random() > 0.5) {\n            messageCallback('hey')\n          } else {\n            messageCallback('lol');\n          }\n        }\n      }, 3000);\n    },\n    disconnect() {\n      clearInterval(intervalId);\n      messageCallback = null;\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl + '');\n    },\n    on(event, callback) {\n      if (messageCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'message') {\n        throw Error('Only \"message\" event is supported.');\n      }\n      messageCallback = callback;\n    },\n  };\n}\n```\n\n```js src/notifications.js\nimport Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\n\nexport function showNotification(message, theme = 'dark') {\n  Toastify({\n    text: message,\n    duration: 2000,\n    gravity: 'top',\n    position: 'right',\n    style: {\n      background: theme === 'dark' ? 'black' : 'white',\n      color: theme === 'dark' ? 'white' : 'black',\n    },\n  }).showToast();\n}\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"toastify-js\": \"1.12.0\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n`useChatRoom` を使うために*内部の動作*を知らなくても良くなったことに着目してください。他のコンポーネントに追加したり、他のオプションを渡したりしても、同じように動作します。これがカスタムフックの威力です。\n\n## カスタムフックを使うタイミング {/*when-to-use-custom-hooks*/}\n\nあらゆる小さなコードの重複に対してカスタムフックを抽出する必要はありません。多少の重複は問題ありません。例えば、先ほどのように 1 回の `useState` 呼び出しをラップするだけの `useFormInput` フックを抽出することは、おそらく不要でしょう。\n\nただし、エフェクトを書くときは常に、更にそのエフェクトをカスタムフックにラップすることでより分かりやすくならないか、検討するようにしてください。[エフェクトは頻繁には必要とされないものです](/learn/you-might-not-need-an-effect)。エフェクトを書くということは、外部システムと同期するために「React の外に踏み出す」必要がある、もしくは React に組み込みの API がない何かを行う必要があるということです。カスタムフックにラップすることで、あなたの意図とデータの流れを正確に表現することができます。\n\n例えば、都市のリストを表示するドロップダウンと、そこで選択中の都市内にある地区のリストを表示する別のドロップダウンがある、`ShippingForm` というコンポーネントを考えてみましょう。まずは次のようなコードを書くことになるでしょう。\n\n```js {3-16,20-35}\nfunction ShippingForm({ country }) {\n  const [cities, setCities] = useState(null);\n  // This Effect fetches cities for a country\n  useEffect(() => {\n    let ignore = false;\n    fetch(`/api/cities?country=${country}`)\n      .then(response => response.json())\n      .then(json => {\n        if (!ignore) {\n          setCities(json);\n        }\n      });\n    return () => {\n      ignore = true;\n    };\n  }, [country]);\n\n  const [city, setCity] = useState(null);\n  const [areas, setAreas] = useState(null);\n  // This Effect fetches areas for the selected city\n  useEffect(() => {\n    if (city) {\n      let ignore = false;\n      fetch(`/api/areas?city=${city}`)\n        .then(response => response.json())\n        .then(json => {\n          if (!ignore) {\n            setAreas(json);\n          }\n        });\n      return () => {\n        ignore = true;\n      };\n    }\n  }, [city]);\n\n  // ...\n```\n\nこのコードはかなりの繰り返しになっていますが、[これらのエフェクトを互いに独立させておくことは正当です](/learn/removing-effect-dependencies#is-your-effect-doing-several-unrelated-things)。これらは 2 つの異なるものを同期しているので、1 つのエフェクトに統合すべきではありません。代わりに、これらに共通のロジックを独自の `useData` フックとして抽出することで、上記の `ShippingForm` コンポーネントを簡略化することができます。\n\n```js {2-18}\nfunction useData(url) {\n  const [data, setData] = useState(null);\n  useEffect(() => {\n    if (url) {\n      let ignore = false;\n      fetch(url)\n        .then(response => response.json())\n        .then(json => {\n          if (!ignore) {\n            setData(json);\n          }\n        });\n      return () => {\n        ignore = true;\n      };\n    }\n  }, [url]);\n  return data;\n}\n```\n\nこれで、`ShippingForm` コンポーネントの両方のエフェクトを `useData` の呼び出しに置き換えることができます。\n\n```js {2,4}\nfunction ShippingForm({ country }) {\n  const cities = useData(`/api/cities?country=${country}`);\n  const [city, setCity] = useState(null);\n  const areas = useData(city ? `/api/areas?city=${city}` : null);\n  // ...\n```\n\nカスタムフックに抽出することで、データの流れが明示的になります。`url` を入力し `data` を出力しているということです。`useData` の中にエフェクトを「隠す」ことで、`ShippingForm` コンポーネントで作業中の誰かが[不要な依存値](/learn/removing-effect-dependencies)を追加してしまうことを防げます。時間が経つにつれて、アプリのほとんどのエフェクトはカスタムフックに書かれるようになるでしょう。\n\n<DeepDive>\n\n#### カスタムフックは具体的かつ高レベルなユースケースに対して使う {/*keep-your-custom-hooks-focused-on-concrete-high-level-use-cases*/}\n\nまず、カスタムフックの名前を選ぶところから始めましょう。明確な名前を選ぶことが難しいと感じる場合、エフェクトがコンポーネントの他のロジックとあまりにも密接に関連しており、まだ抽出する準備ができていないということかもしれません。\n\n理想的には、カスタムフックの名前は、コードをあまり書かない人でも、何をするのか、何を受け取るのか、何を返すのかを推測できるほどに明確であるべきです。\n\n* ✅ `useData(url)`\n* ✅ `useImpressionLog(eventName, extraData)`\n* ✅ `useChatRoom(options)`\n\n外部システムと同期する場合、カスタムフックの名前は、そのシステム固有の専門用語を使用したより技術的なものになるかもしれません。そのシステムに精通している人にとって明確である限り、問題はありません。\n\n* ✅ `useMediaQuery(query)`\n* ✅ `useSocket(url)`\n* ✅ `useIntersectionObserver(ref, options)`\n\n**カスタムフックは具体的かつ高レベルのユースケースに対して使うようにしてください**。`useEffect` API 自体の代替物ないし便利なラッパとして機能させるための、カスタム「ライフサイクル」フックを作ったり使ったりしないようにしてください。\n\n* 🔴 `useMount(fn)`\n* 🔴 `useEffectOnce(fn)`\n* 🔴 `useUpdateEffect(fn)`\n\n例えば、この `useMount` フックは、あるコードが「マウント時」にのみ実行されるようにしようとしています。\n\n```js {4-5,14-15}\nfunction ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  // 🔴 Avoid: using custom \"lifecycle\" Hooks\n  useMount(() => {\n    const connection = createConnection({ roomId, serverUrl });\n    connection.connect();\n\n    post('/analytics/event', { eventName: 'visit_chat' });\n  });\n  // ...\n}\n\n// 🔴 Avoid: creating custom \"lifecycle\" Hooks\nfunction useMount(fn) {\n  useEffect(() => {\n    fn();\n  }, []); // 🔴 React Hook useEffect has a missing dependency: 'fn'\n}\n```\n\n**`useMount` のようなカスタム「ライフサイクル」フックは、React のパラダイムと適合しません**。例えば、このコードサンプルには間違いがあります（`roomId` や `serverUrl` の変更に「反応」しません）が、リンタは `useEffect` の直接的な呼び出しのみをチェックするため、これに対して警告を出してくれません。あなたのフックのことは知らないからです。\n\nエフェクトを書く場合は、まず React の API を直接使ってください。\n\n```js\nfunction ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  // ✅ Good: two raw Effects separated by purpose\n\n  useEffect(() => {\n    const connection = createConnection({ serverUrl, roomId });\n    connection.connect();\n    return () => connection.disconnect();\n  }, [serverUrl, roomId]);\n\n  useEffect(() => {\n    post('/analytics/event', { eventName: 'visit_chat', roomId });\n  }, [roomId]);\n\n  // ...\n}\n```\n\nその後、様々な高レベルのユースケースに対してカスタムフックを抽出するようにします（必須ではありません）。\n\n```js\nfunction ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  // ✅ Great: custom Hooks named after their purpose\n  useChatRoom({ serverUrl, roomId });\n  useImpressionLog('visit_chat', { roomId });\n  // ...\n}\n```\n\n**良いカスタムフックとは、動作を制約することで呼び出し側のコードをより宣言的にするものです**。例えば、`useChatRoom(options)` はチャットルームへの接続のみを行い、`useImpressionLog(eventName, extraData)` はアナリティクスに表示ログを送信することのみを行います。あなたのカスタムフックの API がユースケースを制約しない非常に抽象的なものである場合、長期的には解決される問題よりも多くの問題を引き起こす可能性が高いでしょう。\n\n</DeepDive>\n\n### カスタムフックはより良いパターンへの移行を支援する {/*custom-hooks-help-you-migrate-to-better-patterns*/}\n\nエフェクトは [\"避難ハッチ\"](/learn/escape-hatches) です。「React の外に踏み出す」必要があり、当該ユースケースに対してより良い組み込みのソリューションがない場合に使用するものです。長期的な React チームの目標は、より具体的な問題に対してより具体的なソリューションを提供することで、アプリ内のエフェクトの数を最小限に減らすことです。エフェクトをカスタムフックにラップしておくことで、これらのソリューションが利用可能になったときにコードのアップグレードが容易になります。\n\nこちらの例に戻りましょう。\n\n<Sandpack>\n\n```js\nimport { useOnlineStatus } from './useOnlineStatus.js';\n\nfunction StatusBar() {\n  const isOnline = useOnlineStatus();\n  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;\n}\n\nfunction SaveButton() {\n  const isOnline = useOnlineStatus();\n\n  function handleSaveClick() {\n    console.log('✅ Progress saved');\n  }\n\n  return (\n    <button disabled={!isOnline} onClick={handleSaveClick}>\n      {isOnline ? 'Save progress' : 'Reconnecting...'}\n    </button>\n  );\n}\n\nexport default function App() {\n  return (\n    <>\n      <SaveButton />\n      <StatusBar />\n    </>\n  );\n}\n```\n\n```js src/useOnlineStatus.js active\nimport { useState, useEffect } from 'react';\n\nexport function useOnlineStatus() {\n  const [isOnline, setIsOnline] = useState(true);\n  useEffect(() => {\n    function handleOnline() {\n      setIsOnline(true);\n    }\n    function handleOffline() {\n      setIsOnline(false);\n    }\n    window.addEventListener('online', handleOnline);\n    window.addEventListener('offline', handleOffline);\n    return () => {\n      window.removeEventListener('online', handleOnline);\n      window.removeEventListener('offline', handleOffline);\n    };\n  }, []);\n  return isOnline;\n}\n```\n\n</Sandpack>\n\n上記の例では、`useOnlineStatus` は [`useState`](/reference/react/useState) と [`useEffect`](/reference/react/useEffect) のペアで実装されています。しかし、これは最善のソリューションではありません。考慮されていないエッジケースがいくつかあります。例えば、コンポーネントがマウントされたとき `isOnline` は `true` であると仮定していますが、ネットワークがすでにオフラインになっていた場合、これは誤りです。ブラウザの [`navigator.onLine`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine) API を使ってそれをチェックすることはできますが、それを直接使うと、サーバで初期 HTML を生成する際には動作しません。要するに、このコードには改善の余地があるということです。\n\nReact には、これらの問題をすべて解決してくれる専用の API である [`useSyncExternalStore`](/reference/react/useSyncExternalStore) が含まれています。以下は、この新しい API を活用して書き直された `useOnlineStatus` フックです。\n\n<Sandpack>\n\n```js\nimport { useOnlineStatus } from './useOnlineStatus.js';\n\nfunction StatusBar() {\n  const isOnline = useOnlineStatus();\n  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;\n}\n\nfunction SaveButton() {\n  const isOnline = useOnlineStatus();\n\n  function handleSaveClick() {\n    console.log('✅ Progress saved');\n  }\n\n  return (\n    <button disabled={!isOnline} onClick={handleSaveClick}>\n      {isOnline ? 'Save progress' : 'Reconnecting...'}\n    </button>\n  );\n}\n\nexport default function App() {\n  return (\n    <>\n      <SaveButton />\n      <StatusBar />\n    </>\n  );\n}\n```\n\n```js src/useOnlineStatus.js active\nimport { useSyncExternalStore } from 'react';\n\nfunction subscribe(callback) {\n  window.addEventListener('online', callback);\n  window.addEventListener('offline', callback);\n  return () => {\n    window.removeEventListener('online', callback);\n    window.removeEventListener('offline', callback);\n  };\n}\n\nexport function useOnlineStatus() {\n  return useSyncExternalStore(\n    subscribe,\n    () => navigator.onLine, // How to get the value on the client\n    () => true // How to get the value on the server\n  );\n}\n\n```\n\n</Sandpack>\n\n**どのコンポーネントも変更することなしに**この移行ができたことに注目してください。\n\n```js {2,7}\nfunction StatusBar() {\n  const isOnline = useOnlineStatus();\n  // ...\n}\n\nfunction SaveButton() {\n  const isOnline = useOnlineStatus();\n  // ...\n}\n```\n\nこれが、カスタムフックにエフェクトをラップすることが有益であるもうひとつの理由です。\n\n1. エフェクトに出入りするデータの流れが非常に明確になる。\n2. コンポーネントがエフェクトの実装そのものではなく、意図にフォーカスできるようになる。\n3. React が新しい機能を追加したときに、コンポーネントを変更せずにエフェクトを削除できるようになる。\n\n[デザインシステム](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969)と同様に、アプリのコンポーネントから共通の定型コードをカスタムフックに抽出することは役立つでしょう。これにより、コンポーネントのコードは意図を表現するようになり、生のエフェクトを頻繁に書くことを避けることができるようになります。React コミュニティでは多くの優れたカスタムフックがメンテナンスされています。\n\n<DeepDive>\n\n#### 将来 React はデータフェッチのための組み込みソリューションを提供するか？ {/*will-react-provide-any-built-in-solution-for-data-fetching*/}\n\n現在では [`use`](/reference/react/use#streaming-data-from-server-to-client) API に[プロミス](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)を渡すことで、レンダー時にデータの読み込みが可能となっています。\n\n```js {1,4,11}\nimport { use, Suspense } from \"react\";\n\nfunction Message({ messagePromise }) {\n  const messageContent = use(messagePromise);\n  return <p>Here is the message: {messageContent}</p>;\n}\n\nexport function MessageContainer({ messagePromise }) {\n  return (\n    <Suspense fallback={<p>⌛Downloading message...</p>}>\n      <Message messagePromise={messagePromise} />\n    </Suspense>\n  );\n}\n```\n\n詳細はまだ検討中ですが、将来的にはデータフェッチを以下のように書けるようになるはずです。\n\n```js {1,4,6}\nimport { use } from 'react';\n\nfunction ShippingForm({ country }) {\n  const cities = use(fetch(`/api/cities?country=${country}`));\n  const [city, setCity] = useState(null);\n  const areas = city ? use(fetch(`/api/areas?city=${city}`)) : null;\n  // ...\n```\n\nアプリで上記のような `useData` のようなカスタムフックを使用しておくことで、最終的に推奨されるアプローチに移行する際に、コンポーネントごとに手動で生のエフェクトを書く場合よりも変更が少なくて済みます。ただし、古いアプローチでも問題なく動作するので、生のエフェクトを書くことに満足している場合は、それを続けることもできます。\n\n</DeepDive>\n\n### やり方は 1 つではない {/*there-is-more-than-one-way-to-do-it*/}\n\nブラウザの [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) API を使って、*ゼロから*フェードインアニメーションを実装したいとしましょう。アニメーション用のループを設定するエフェクトから始めることになるでしょう。アニメーションの各フレームでは、[ref で保持している](/learn/manipulating-the-dom-with-refs) DOM ノードの不透明度を `1` になるまで更新していきます。最初のコードは次のようになるでしょう。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect, useRef } from 'react';\n\nfunction Welcome() {\n  const ref = useRef(null);\n\n  useEffect(() => {\n    const duration = 1000;\n    const node = ref.current;\n\n    let startTime = performance.now();\n    let frameId = null;\n\n    function onFrame(now) {\n      const timePassed = now - startTime;\n      const progress = Math.min(timePassed / duration, 1);\n      onProgress(progress);\n      if (progress < 1) {\n        // We still have more frames to paint\n        frameId = requestAnimationFrame(onFrame);\n      }\n    }\n\n    function onProgress(progress) {\n      node.style.opacity = progress;\n    }\n\n    function start() {\n      onProgress(0);\n      startTime = performance.now();\n      frameId = requestAnimationFrame(onFrame);\n    }\n\n    function stop() {\n      cancelAnimationFrame(frameId);\n      startTime = null;\n      frameId = null;\n    }\n\n    start();\n    return () => stop();\n  }, []);\n\n  return (\n    <h1 className=\"welcome\" ref={ref}>\n      Welcome\n    </h1>\n  );\n}\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Remove' : 'Show'}\n      </button>\n      <hr />\n      {show && <Welcome />}\n    </>\n  );\n}\n```\n\n```css\nlabel, button { display: block; margin-bottom: 20px; }\nhtml, body { min-height: 300px; }\n.welcome {\n  opacity: 0;\n  color: white;\n  padding: 50px;\n  text-align: center;\n  font-size: 50px;\n  background-image: radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%);\n}\n```\n\n</Sandpack>\n\nこのコンポーネントをより読みやすくするために、`useFadeIn` カスタムフックにロジックを抽出することができます。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect, useRef } from 'react';\nimport { useFadeIn } from './useFadeIn.js';\n\nfunction Welcome() {\n  const ref = useRef(null);\n\n  useFadeIn(ref, 1000);\n\n  return (\n    <h1 className=\"welcome\" ref={ref}>\n      Welcome\n    </h1>\n  );\n}\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Remove' : 'Show'}\n      </button>\n      <hr />\n      {show && <Welcome />}\n    </>\n  );\n}\n```\n\n```js src/useFadeIn.js\nimport { useEffect } from 'react';\n\nexport function useFadeIn(ref, duration) {\n  useEffect(() => {\n    const node = ref.current;\n\n    let startTime = performance.now();\n    let frameId = null;\n\n    function onFrame(now) {\n      const timePassed = now - startTime;\n      const progress = Math.min(timePassed / duration, 1);\n      onProgress(progress);\n      if (progress < 1) {\n        // We still have more frames to paint\n        frameId = requestAnimationFrame(onFrame);\n      }\n    }\n\n    function onProgress(progress) {\n      node.style.opacity = progress;\n    }\n\n    function start() {\n      onProgress(0);\n      startTime = performance.now();\n      frameId = requestAnimationFrame(onFrame);\n    }\n\n    function stop() {\n      cancelAnimationFrame(frameId);\n      startTime = null;\n      frameId = null;\n    }\n\n    start();\n    return () => stop();\n  }, [ref, duration]);\n}\n```\n\n```css\nlabel, button { display: block; margin-bottom: 20px; }\nhtml, body { min-height: 300px; }\n.welcome {\n  opacity: 0;\n  color: white;\n  padding: 50px;\n  text-align: center;\n  font-size: 50px;\n  background-image: radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%);\n}\n```\n\n</Sandpack>\n\nこの `useFadeIn` はこのままでも構いませんが、さらにリファクタリングすることも可能です。例えば、アニメーションループの設定ロジックを `useFadeIn` の外のカスタム `useAnimationLoop` フックへと抽出することができます。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect, useRef } from 'react';\nimport { useFadeIn } from './useFadeIn.js';\n\nfunction Welcome() {\n  const ref = useRef(null);\n\n  useFadeIn(ref, 1000);\n\n  return (\n    <h1 className=\"welcome\" ref={ref}>\n      Welcome\n    </h1>\n  );\n}\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Remove' : 'Show'}\n      </button>\n      <hr />\n      {show && <Welcome />}\n    </>\n  );\n}\n```\n\n```js src/useFadeIn.js active\nimport { useState, useEffect } from 'react';\nimport { useEffectEvent } from 'react';\n\nexport function useFadeIn(ref, duration) {\n  const [isRunning, setIsRunning] = useState(true);\n\n  useAnimationLoop(isRunning, (timePassed) => {\n    const progress = Math.min(timePassed / duration, 1);\n    ref.current.style.opacity = progress;\n    if (progress === 1) {\n      setIsRunning(false);\n    }\n  });\n}\n\nfunction useAnimationLoop(isRunning, drawFrame) {\n  const onFrame = useEffectEvent(drawFrame);\n\n  useEffect(() => {\n    if (!isRunning) {\n      return;\n    }\n\n    const startTime = performance.now();\n    let frameId = null;\n\n    function tick(now) {\n      const timePassed = now - startTime;\n      onFrame(timePassed);\n      frameId = requestAnimationFrame(tick);\n    }\n\n    tick();\n    return () => cancelAnimationFrame(frameId);\n  }, [isRunning]);\n}\n```\n\n```css\nlabel, button { display: block; margin-bottom: 20px; }\nhtml, body { min-height: 300px; }\n.welcome {\n  opacity: 0;\n  color: white;\n  padding: 50px;\n  text-align: center;\n  font-size: 50px;\n  background-image: radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%);\n}\n```\n\n</Sandpack>\n\nただし、これは*必須*ではありませんでした。通常の関数と同様、コードのどこに分割線を引いていくのかは、最終的にあなたが決めることです。また、まったく異なるアプローチを取ることもできます。エフェクト内にロジックを保持する代わりに、命令型のロジックのほとんどを JavaScript の[クラス](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)内に移動することもできるでしょう。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect, useRef } from 'react';\nimport { useFadeIn } from './useFadeIn.js';\n\nfunction Welcome() {\n  const ref = useRef(null);\n\n  useFadeIn(ref, 1000);\n\n  return (\n    <h1 className=\"welcome\" ref={ref}>\n      Welcome\n    </h1>\n  );\n}\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Remove' : 'Show'}\n      </button>\n      <hr />\n      {show && <Welcome />}\n    </>\n  );\n}\n```\n\n```js src/useFadeIn.js active\nimport { useState, useEffect } from 'react';\nimport { FadeInAnimation } from './animation.js';\n\nexport function useFadeIn(ref, duration) {\n  useEffect(() => {\n    const animation = new FadeInAnimation(ref.current);\n    animation.start(duration);\n    return () => {\n      animation.stop();\n    };\n  }, [ref, duration]);\n}\n```\n\n```js src/animation.js\nexport class FadeInAnimation {\n  constructor(node) {\n    this.node = node;\n  }\n  start(duration) {\n    this.duration = duration;\n    this.onProgress(0);\n    this.startTime = performance.now();\n    this.frameId = requestAnimationFrame(() => this.onFrame());\n  }\n  onFrame() {\n    const timePassed = performance.now() - this.startTime;\n    const progress = Math.min(timePassed / this.duration, 1);\n    this.onProgress(progress);\n    if (progress === 1) {\n      this.stop();\n    } else {\n      // We still have more frames to paint\n      this.frameId = requestAnimationFrame(() => this.onFrame());\n    }\n  }\n  onProgress(progress) {\n    this.node.style.opacity = progress;\n  }\n  stop() {\n    cancelAnimationFrame(this.frameId);\n    this.startTime = null;\n    this.frameId = null;\n    this.duration = 0;\n  }\n}\n```\n\n```css\nlabel, button { display: block; margin-bottom: 20px; }\nhtml, body { min-height: 300px; }\n.welcome {\n  opacity: 0;\n  color: white;\n  padding: 50px;\n  text-align: center;\n  font-size: 50px;\n  background-image: radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%);\n}\n```\n\n</Sandpack>\n\nエフェクトとは React を外部システムに接続することができるものです。エフェクト間で多くの調整が必要になればなるほど（例えば、複数のアニメーションを連動させるなど）、上記のサンドボックスのようにエフェクトやフックからロジックを*完全に*抽出してしまうことがより意味を持つようになります。そうすればその抽出したコード*こそが*「外部システム」となります。これにより、その React 外に移動したシステムにメッセージを送るだけでよくなるため、エフェクトはシンプルに保たれるでしょう。\n\nなお上記の例では、フェードインのロジックを JavaScript で記述する必要があると仮定していました。ただしこの特定のケースに関して言えば、このフェードインアニメーションは単純な [CSS アニメーション](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations)で実装する方がずっと簡単で効率的です。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect, useRef } from 'react';\nimport './welcome.css';\n\nfunction Welcome() {\n  return (\n    <h1 className=\"welcome\">\n      Welcome\n    </h1>\n  );\n}\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Remove' : 'Show'}\n      </button>\n      <hr />\n      {show && <Welcome />}\n    </>\n  );\n}\n```\n\n```css src/styles.css\nlabel, button { display: block; margin-bottom: 20px; }\nhtml, body { min-height: 300px; }\n```\n\n```css src/welcome.css active\n.welcome {\n  color: white;\n  padding: 50px;\n  text-align: center;\n  font-size: 50px;\n  background-image: radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%);\n\n  animation: fadeIn 1000ms;\n}\n\n@keyframes fadeIn {\n  0% { opacity: 0; }\n  100% { opacity: 1; }\n}\n\n```\n\n</Sandpack>\n\nときには、そもそもフック自体が不要ということです！\n\n<Recap>\n\n- カスタムフックを使ってコンポーネント間でロジックを共有できる。\n- カスタムフックの名前は `use` で始めて大文字を続ける必要がある。\n- カスタムフックは state 自体ではなく、state を使うロジックを共有する。\n- あるフックから別のフックにリアクティブな値を渡すことができ、それらは最新の状態に保たれる。\n- すべてのフックはコンポーネントが再レンダーされるたびに再実行される。\n- カスタムフックのコードは、コンポーネントコードと同様に純粋である必要がある。\n- カスタムフックが受け取るイベントハンドラはエフェクトイベントにラップする。\n- `useMount` のようなカスタムフックを作成してはいけない。常に目的は具体的なものにする。\n- コードの境界をどこにどのように置くかはあなたが決定する。\n\n</Recap>\n\n<Challenges>\n\n#### `useCounter` フックを抽出 {/*extract-a-usecounter-hook*/}\n\nこのコンポーネントは、state 変数とエフェクトを使い、1 秒ごとに増加する数値を表示しています。このロジックを `useCounter` というカスタムフックに抽出してください。目標は、`Counter` コンポーネントの実装が以下のようになることです。\n\n```js\nexport default function Counter() {\n  const count = useCounter();\n  return <h1>Seconds passed: {count}</h1>;\n}\n```\n\nカスタムフックを `useCounter.js` に記述して、`App.js` ファイルにインポートする必要があります。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function Counter() {\n  const [count, setCount] = useState(0);\n  useEffect(() => {\n    const id = setInterval(() => {\n      setCount(c => c + 1);\n    }, 1000);\n    return () => clearInterval(id);\n  }, []);\n  return <h1>Seconds passed: {count}</h1>;\n}\n```\n\n```js src/useCounter.js\n// Write your custom Hook in this file!\n```\n\n</Sandpack>\n\n<Solution>\n\n以下のようなコードになるでしょう。\n\n<Sandpack>\n\n```js\nimport { useCounter } from './useCounter.js';\n\nexport default function Counter() {\n  const count = useCounter();\n  return <h1>Seconds passed: {count}</h1>;\n}\n```\n\n```js src/useCounter.js\nimport { useState, useEffect } from 'react';\n\nexport function useCounter() {\n  const [count, setCount] = useState(0);\n  useEffect(() => {\n    const id = setInterval(() => {\n      setCount(c => c + 1);\n    }, 1000);\n    return () => clearInterval(id);\n  }, []);\n  return count;\n}\n```\n\n</Sandpack>\n\n`App.js` は `useState` や `useEffect` をインポートする必要がなくなったことに注意してください。\n\n</Solution>\n\n#### カウンタの遅延を設定可能に {/*make-the-counter-delay-configurable*/}\n\nこの例にはスライダで制御されている `delay` という state 変数がありますが、その値は使用されていません。`delay` の値をカスタム `useCounter` フックに渡すとともに、`useCounter` フックの中身を変更して、ハードコードされた `1000` ms の代わりに渡された `delay` を使用するようにしてください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { useCounter } from './useCounter.js';\n\nexport default function Counter() {\n  const [delay, setDelay] = useState(1000);\n  const count = useCounter();\n  return (\n    <>\n      <label>\n        Tick duration: {delay} ms\n        <br />\n        <input\n          type=\"range\"\n          value={delay}\n          min=\"10\"\n          max=\"2000\"\n          onChange={e => setDelay(Number(e.target.value))}\n        />\n      </label>\n      <hr />\n      <h1>Ticks: {count}</h1>\n    </>\n  );\n}\n```\n\n```js src/useCounter.js\nimport { useState, useEffect } from 'react';\n\nexport function useCounter() {\n  const [count, setCount] = useState(0);\n  useEffect(() => {\n    const id = setInterval(() => {\n      setCount(c => c + 1);\n    }, 1000);\n    return () => clearInterval(id);\n  }, []);\n  return count;\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n`useCounter(delay)` のようにして `delay` をフックに渡します。次にフック内で、ハードコードされた `1000` の値の代わりに `delay` を使用します。`delay` をエフェクトの依存配列に追加する必要があります。これにより、`delay` を変更することでインターバルが確実にリセットされるようになります。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { useCounter } from './useCounter.js';\n\nexport default function Counter() {\n  const [delay, setDelay] = useState(1000);\n  const count = useCounter(delay);\n  return (\n    <>\n      <label>\n        Tick duration: {delay} ms\n        <br />\n        <input\n          type=\"range\"\n          value={delay}\n          min=\"10\"\n          max=\"2000\"\n          onChange={e => setDelay(Number(e.target.value))}\n        />\n      </label>\n      <hr />\n      <h1>Ticks: {count}</h1>\n    </>\n  );\n}\n```\n\n```js src/useCounter.js\nimport { useState, useEffect } from 'react';\n\nexport function useCounter(delay) {\n  const [count, setCount] = useState(0);\n  useEffect(() => {\n    const id = setInterval(() => {\n      setCount(c => c + 1);\n    }, delay);\n    return () => clearInterval(id);\n  }, [delay]);\n  return count;\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n#### `useCounter` から `useInterval` を抽出 {/*extract-useinterval-out-of-usecounter*/}\n\n現在あなたの `useCounter` フックは 2 つのことを行っています。インターバルの設定と、各インターバルごとに state 変数をインクリメントすることです。インターバルを設定するロジックを、`useInterval` という別のフックに分割してください。フックは `onTick` コールバックと `delay` の 2 つの引数を受け取る必要があります。この変更後、`useCounter` の実装は次のようになります。\n\n```js\nexport function useCounter(delay) {\n  const [count, setCount] = useState(0);\n  useInterval(() => {\n    setCount(c => c + 1);\n  }, delay);\n  return count;\n}\n```\n\n`useInterval.js` ファイルに `useInterval` を記述し、`useCounter.js` ファイルにインポートするようにします。\n\n<Sandpack>\n\n```js\nimport { useCounter } from './useCounter.js';\n\nexport default function Counter() {\n  const count = useCounter(1000);\n  return <h1>Seconds passed: {count}</h1>;\n}\n```\n\n```js src/useCounter.js\nimport { useState, useEffect } from 'react';\n\nexport function useCounter(delay) {\n  const [count, setCount] = useState(0);\n  useEffect(() => {\n    const id = setInterval(() => {\n      setCount(c => c + 1);\n    }, delay);\n    return () => clearInterval(id);\n  }, [delay]);\n  return count;\n}\n```\n\n```js src/useInterval.js\n// Write your Hook here!\n```\n\n</Sandpack>\n\n<Solution>\n\n`useInterval` 内のロジックは、インターバルの設定とクリアを行う必要があります。それ以外のことは何もする必要はありません。\n\n<Sandpack>\n\n```js\nimport { useCounter } from './useCounter.js';\n\nexport default function Counter() {\n  const count = useCounter(1000);\n  return <h1>Seconds passed: {count}</h1>;\n}\n```\n\n```js src/useCounter.js\nimport { useState } from 'react';\nimport { useInterval } from './useInterval.js';\n\nexport function useCounter(delay) {\n  const [count, setCount] = useState(0);\n  useInterval(() => {\n    setCount(c => c + 1);\n  }, delay);\n  return count;\n}\n```\n\n```js src/useInterval.js active\nimport { useEffect } from 'react';\n\nexport function useInterval(onTick, delay) {\n  useEffect(() => {\n    const id = setInterval(onTick, delay);\n    return () => clearInterval(id);\n  }, [onTick, delay]);\n}\n```\n\n</Sandpack>\n\n実はこの答えにはまだちょっとした問題がありますが、それは次のチャレンジ問題で修正します。\n\n</Solution>\n\n#### インターバルがリセットされる問題を修正 {/*fix-a-resetting-interval*/}\n\n以下の例には *2 つの*別々のインターバルがあります。\n\n`App` コンポーネントは `useCounter` を呼び出しており、それは内部でカウンタを毎秒更新するために `useInterval` を呼び出しています。さらにこの `App` コンポーネントは、ページの背景色を 2 秒ごとにランダムに更新するために別の `useInterval` を呼び出しています。\n\nしかし何らかの理由で、ページの背景を更新するコールバックが実行されません。`useInterval` 内に以下のようにログを追加してみてください：\n\n```js {2,5}\n  useEffect(() => {\n    console.log('✅ Setting up an interval with delay ', delay)\n    const id = setInterval(onTick, delay);\n    return () => {\n      console.log('❌ Clearing an interval with delay ', delay)\n      clearInterval(id);\n    };\n  }, [onTick, delay]);\n```\n\n表示されるログは期待通りのものですか？ エフェクトが不必要に再同期されるように見えるとして、どの依存値が原因で起こっているか分かるでしょうか？ エフェクトからその依存値を[削除する](/learn/removing-effect-dependencies)方法はありますか？\n\n問題を修正すると、ページの背景が 2 秒ごとに更新されるようになるはずです。\n\n<Hint>\n\n`useInterval` フックは引数としてイベントリスナを受け取っているようです。このイベントリスナをエフェクトの依存値にせずとも済むよう、これをラップする方法はなかったでしょうか？\n\n</Hint>\n\n<Sandpack>\n\n```js\nimport { useCounter } from './useCounter.js';\nimport { useInterval } from './useInterval.js';\n\nexport default function Counter() {\n  const count = useCounter(1000);\n\n  useInterval(() => {\n    const randomColor = `hsla(${Math.random() * 360}, 100%, 50%, 0.2)`;\n    document.body.style.backgroundColor = randomColor;\n  }, 2000);\n\n  return <h1>Seconds passed: {count}</h1>;\n}\n```\n\n```js src/useCounter.js\nimport { useState } from 'react';\nimport { useInterval } from './useInterval.js';\n\nexport function useCounter(delay) {\n  const [count, setCount] = useState(0);\n  useInterval(() => {\n    setCount(c => c + 1);\n  }, delay);\n  return count;\n}\n```\n\n```js src/useInterval.js\nimport { useEffect } from 'react';\nimport { useEffectEvent } from 'react';\n\nexport function useInterval(onTick, delay) {\n  useEffect(() => {\n    const id = setInterval(onTick, delay);\n    return () => {\n      clearInterval(id);\n    };\n  }, [onTick, delay]);\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n[上記で行ったように](/learn/reusing-logic-with-custom-hooks#passing-event-handlers-to-custom-hooks)、`useInterval` の中で、tick コールバックをエフェクトイベント内にラップします。\n\nこれにより、エフェクトの依存配列から `onTick` をなくすことができます。エフェクトがコンポーネントの再レンダーごとに再同期されることはなくなり、ページ背景色変更のためのインターバルが発火する前に毎秒リセットされてしまうこともなくなります。\n\nこの変更により、両方のインターバルが期待通りに動作し、互いに干渉しないようになります。\n\n<Sandpack>\n\n\n```js\nimport { useCounter } from './useCounter.js';\nimport { useInterval } from './useInterval.js';\n\nexport default function Counter() {\n  const count = useCounter(1000);\n\n  useInterval(() => {\n    const randomColor = `hsla(${Math.random() * 360}, 100%, 50%, 0.2)`;\n    document.body.style.backgroundColor = randomColor;\n  }, 2000);\n\n  return <h1>Seconds passed: {count}</h1>;\n}\n```\n\n```js src/useCounter.js\nimport { useState } from 'react';\nimport { useInterval } from './useInterval.js';\n\nexport function useCounter(delay) {\n  const [count, setCount] = useState(0);\n  useInterval(() => {\n    setCount(c => c + 1);\n  }, delay);\n  return count;\n}\n```\n\n```js src/useInterval.js active\nimport { useEffect } from 'react';\nimport { useEffectEvent } from 'react';\n\nexport function useInterval(callback, delay) {\n  const onTick = useEffectEvent(callback);\n  useEffect(() => {\n    const id = setInterval(onTick, delay);\n    return () => clearInterval(id);\n  }, [delay]);\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n#### タイムシフト効果の実装 {/*implement-a-staggering-movement*/}\n\nこの例では、`usePointerPosition()` フックが現在のポインタ位置を追跡しています。プレビューエリア上でカーソルや指を動かしてみて、赤い点が動きに追従するのを確認してください。その座標は `pos1` 変数に保存されています。\n\n実は、5 つ (!) の赤い点が別々にレンダーされています。すべて同じ位置に表示されているので全部を見ることはできません。あなたのタスクはこれを修正することです。代わりに実装したいのは「時間をずらした」動きです。各ドットが前のドットの経路を「追いかける」必要があります。たとえば、カーソルを素早く移動すると、最初のドットはすぐにそれに追従し、2 つ目のドットは最初のドットに少し遅れて追従し、3 つ目のドットは 2 つ目のドットに追従する、といった具合です。\n\n`useDelayedValue` カスタムフックを実装してください。現在の実装は渡された `value` をそのまま返しています。代わりに、`delay` ミリ秒前の値を返すようにしたいです。これを実現するためには state とエフェクトが必要になるかもしれません。\n\n`useDelayedValue` を実装した後、点が別の点を追いかけるように動くのが見えるはずです。\n\n<Hint>\n\nカスタムフック内で `delayedValue` を state 変数として保持する必要があります。`value` が変更されたときに、エフェクトを実行したいです。このエフェクトは、`delay` が経過した後に `delayedValue` を更新する必要があります。`setTimeout` を呼び出すといいかもしれません。\n\nこのエフェクトにクリーンアップは必要ですか？ それはなぜですか？\n\n</Hint>\n\n<Sandpack>\n\n```js\nimport { usePointerPosition } from './usePointerPosition.js';\n\nfunction useDelayedValue(value, delay) {\n  // TODO: Implement this Hook\n  return value;\n}\n\nexport default function Canvas() {\n  const pos1 = usePointerPosition();\n  const pos2 = useDelayedValue(pos1, 100);\n  const pos3 = useDelayedValue(pos2, 200);\n  const pos4 = useDelayedValue(pos3, 100);\n  const pos5 = useDelayedValue(pos3, 50);\n  return (\n    <>\n      <Dot position={pos1} opacity={1} />\n      <Dot position={pos2} opacity={0.8} />\n      <Dot position={pos3} opacity={0.6} />\n      <Dot position={pos4} opacity={0.4} />\n      <Dot position={pos5} opacity={0.2} />\n    </>\n  );\n}\n\nfunction Dot({ position, opacity }) {\n  return (\n    <div style={{\n      position: 'absolute',\n      backgroundColor: 'pink',\n      borderRadius: '50%',\n      opacity,\n      transform: `translate(${position.x}px, ${position.y}px)`,\n      pointerEvents: 'none',\n      left: -20,\n      top: -20,\n      width: 40,\n      height: 40,\n    }} />\n  );\n}\n```\n\n```js src/usePointerPosition.js\nimport { useState, useEffect } from 'react';\n\nexport function usePointerPosition() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n  useEffect(() => {\n    function handleMove(e) {\n      setPosition({ x: e.clientX, y: e.clientY });\n    }\n    window.addEventListener('pointermove', handleMove);\n    return () => window.removeEventListener('pointermove', handleMove);\n  }, []);\n  return position;\n}\n```\n\n```css\nbody { min-height: 300px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n以下が動作するバージョンです。state 変数として `delayedValue` を保持するようにします。`value` が更新されると、エフェクトが `delayedValue` を更新するタイムアウトをセットします。これにより、`delayedValue` は常に実際の `value` の後に「遅れて」更新されるようになります。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { usePointerPosition } from './usePointerPosition.js';\n\nfunction useDelayedValue(value, delay) {\n  const [delayedValue, setDelayedValue] = useState(value);\n\n  useEffect(() => {\n    setTimeout(() => {\n      setDelayedValue(value);\n    }, delay);\n  }, [value, delay]);\n\n  return delayedValue;\n}\n\nexport default function Canvas() {\n  const pos1 = usePointerPosition();\n  const pos2 = useDelayedValue(pos1, 100);\n  const pos3 = useDelayedValue(pos2, 200);\n  const pos4 = useDelayedValue(pos3, 100);\n  const pos5 = useDelayedValue(pos3, 50);\n  return (\n    <>\n      <Dot position={pos1} opacity={1} />\n      <Dot position={pos2} opacity={0.8} />\n      <Dot position={pos3} opacity={0.6} />\n      <Dot position={pos4} opacity={0.4} />\n      <Dot position={pos5} opacity={0.2} />\n    </>\n  );\n}\n\nfunction Dot({ position, opacity }) {\n  return (\n    <div style={{\n      position: 'absolute',\n      backgroundColor: 'pink',\n      borderRadius: '50%',\n      opacity,\n      transform: `translate(${position.x}px, ${position.y}px)`,\n      pointerEvents: 'none',\n      left: -20,\n      top: -20,\n      width: 40,\n      height: 40,\n    }} />\n  );\n}\n```\n\n```js src/usePointerPosition.js\nimport { useState, useEffect } from 'react';\n\nexport function usePointerPosition() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n  useEffect(() => {\n    function handleMove(e) {\n      setPosition({ x: e.clientX, y: e.clientY });\n    }\n    window.addEventListener('pointermove', handleMove);\n    return () => window.removeEventListener('pointermove', handleMove);\n  }, []);\n  return position;\n}\n```\n\n```css\nbody { min-height: 300px; }\n```\n\n</Sandpack>\n\nこのエフェクトには*クリーンアップが必要ない*ことに注意してください。クリーンアップ関数で `clearTimeout` を呼び出すと、`value` が変更されるたびにすでにスケジュールされているタイムアウトがリセットされてしまいます。連続的な動きを保つためには、すべてのタイムアウトが発火する必要があります。\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/scaling-up-with-reducer-and-context.md",
    "content": "---\ntitle: リデューサとコンテクストでスケールアップ\n---\n\n<Intro>\n\nリデューサを使えば、コンポーネントの state 更新ロジックを集約することができます。コンテクストを使えば、他のコンポーネントに深く情報を渡すことができます。そしてリデューサとコンテクストを組み合わせることで、複雑な画面の state 管理ができるようになります。\n\n</Intro>\n\n<YouWillLearn>\n\n* リデューサとコンテクストを組み合わせる方法\n* state とディスパッチ関数を props を介して渡すことを避ける方法\n* コンテクストと state ロジックを別のファイルに保持する方法\n\n</YouWillLearn>\n\n## リデューサとコンテクストの組み合わせ {/*combining-a-reducer-with-context*/}\n\n[リデューサの導入記事](/learn/extracting-state-logic-into-a-reducer)で紹介した以下の例では、state はリデューサによって管理されています。リデューサ関数はファイルの下部で宣言されており、そこに state の更新ロジックがすべて含まれています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\n\nexport default function TaskApp() {\n  const [tasks, dispatch] = useReducer(\n    tasksReducer,\n    initialTasks\n  );\n\n  function handleAddTask(text) {\n    dispatch({\n      type: 'added',\n      id: nextId++,\n      text: text,\n    });\n  }\n\n  function handleChangeTask(task) {\n    dispatch({\n      type: 'changed',\n      task: task\n    });\n  }\n\n  function handleDeleteTask(taskId) {\n    dispatch({\n      type: 'deleted',\n      id: taskId\n    });\n  }\n\n  return (\n    <>\n      <h1>Day off in Kyoto</h1>\n      <AddTask\n        onAddTask={handleAddTask}\n      />\n      <TaskList\n        tasks={tasks}\n        onChangeTask={handleChangeTask}\n        onDeleteTask={handleDeleteTask}\n      />\n    </>\n  );\n}\n\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [...tasks, {\n        id: action.id,\n        text: action.text,\n        done: false\n      }];\n    }\n    case 'changed': {\n      return tasks.map(t => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter(t => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nlet nextId = 3;\nconst initialTasks = [\n  { id: 0, text: 'Philosopher’s Path', done: true },\n  { id: 1, text: 'Visit the temple', done: false },\n  { id: 2, text: 'Drink matcha', done: false }\n];\n```\n\n```js src/AddTask.js\nimport { useState } from 'react';\n\nexport default function AddTask({ onAddTask }) {\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button onClick={() => {\n        setText('');\n        onAddTask(text);\n      }}>Add</button>\n    </>\n  )\n}\n```\n\n```js src/TaskList.js\nimport { useState } from 'react';\n\nexport default function TaskList({\n  tasks,\n  onChangeTask,\n  onDeleteTask\n}) {\n  return (\n    <ul>\n      {tasks.map(task => (\n        <li key={task.id}>\n          <Task\n            task={task}\n            onChange={onChangeTask}\n            onDelete={onDeleteTask}\n          />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ task, onChange, onDelete }) {\n  const [isEditing, setIsEditing] = useState(false);\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={e => {\n            onChange({\n              ...task,\n              text: e.target.value\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={e => {\n          onChange({\n            ...task,\n            done: e.target.checked\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => onDelete(task.id)}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\nリデューサを使うことで、イベントハンドラを短く簡潔に保てます。しかしアプリが大きくなるにつれ、別の困難が発生することがあります。**現在 `tasks` state と `dispatch` 関数は、トップレベルの `TaskApp` コンポーネントでしか使えません**。他のコンポーネントがタスクリストの読み込みや変更をできるようにするには、現在の state や変更用のイベントハンドラを props として明示的に[下に渡していく](/learn/passing-props-to-a-component)必要があります。\n\n例えば `TaskApp` はタスクリストとイベントハンドラを `TaskList` に渡していますし：\n\n```js\n<TaskList\n  tasks={tasks}\n  onChangeTask={handleChangeTask}\n  onDeleteTask={handleDeleteTask}\n/>\n```\n\n`TaskList` もイベントハンドラを `Task` に渡しています：\n\n```js\n<Task\n  task={task}\n  onChange={onChangeTask}\n  onDelete={onDeleteTask}\n/>\n```\n\nこのような小さなサンプルではこれはうまく機能しますが、間に数十、数百といったコンポーネントがある場合、すべての state や関数をこのように渡していくのは非常に面倒です！\n\nというわけで、`tasks` state と `dispatch` 関数は、props 経由で渡すのではなく、[コンテクストに入れる](/learn/passing-data-deeply-with-context)方が望ましい場合があります。**こうすることで `TaskApp` ツリーの下部にある任意のコンポーネントが、\"props の穴掘り作業 (prop drilling)\" を繰り返さずともタスクのリストを読み取り、アクションをディスパッチすることができるようになります**。\n\n以下がリデューサをコンテクストと組み合わせる方法です。\n\n1. コンテクストを**作成する**。\n2. state と dispatch をコンテクストに**入れる**。\n3. ツリー内の任意の場所でコンテクストを**使用する**。\n\n### ステップ 1：コンテクストを作成する {/*step-1-create-the-context*/}\n\n`useReducer` フックは、現在の `tasks` と、それを更新するための `dispatch` 関数を返します。\n\n```js\nconst [tasks, dispatch] = useReducer(tasksReducer, initialTasks);\n```\n\nこれらをツリーに渡すために、2 つの異なる[コンテクストを作成](/learn/passing-data-deeply-with-context#step-2-use-the-context)しましょう。\n\n- `TasksContext` は、現在のタスクのリストを提供 (provide) する。\n- `TasksDispatchContext` は、コンポーネントがアクションをディスパッチするための関数を提供する。\n\n別のファイルを作ってエクスポートすることで、これらを他のファイルからインポートできるようにします。\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\n\nexport default function TaskApp() {\n  const [tasks, dispatch] = useReducer(\n    tasksReducer,\n    initialTasks\n  );\n\n  function handleAddTask(text) {\n    dispatch({\n      type: 'added',\n      id: nextId++,\n      text: text,\n    });\n  }\n\n  function handleChangeTask(task) {\n    dispatch({\n      type: 'changed',\n      task: task\n    });\n  }\n\n  function handleDeleteTask(taskId) {\n    dispatch({\n      type: 'deleted',\n      id: taskId\n    });\n  }\n\n  return (\n    <>\n      <h1>Day off in Kyoto</h1>\n      <AddTask\n        onAddTask={handleAddTask}\n      />\n      <TaskList\n        tasks={tasks}\n        onChangeTask={handleChangeTask}\n        onDeleteTask={handleDeleteTask}\n      />\n    </>\n  );\n}\n\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [...tasks, {\n        id: action.id,\n        text: action.text,\n        done: false\n      }];\n    }\n    case 'changed': {\n      return tasks.map(t => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter(t => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nlet nextId = 3;\nconst initialTasks = [\n  { id: 0, text: 'Philosopher’s Path', done: true },\n  { id: 1, text: 'Visit the temple', done: false },\n  { id: 2, text: 'Drink matcha', done: false }\n];\n```\n\n```js src/TasksContext.js active\nimport { createContext } from 'react';\n\nexport const TasksContext = createContext(null);\nexport const TasksDispatchContext = createContext(null);\n```\n\n```js src/AddTask.js\nimport { useState } from 'react';\n\nexport default function AddTask({ onAddTask }) {\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button onClick={() => {\n        setText('');\n        onAddTask(text);\n      }}>Add</button>\n    </>\n  )\n}\n```\n\n```js src/TaskList.js\nimport { useState } from 'react';\n\nexport default function TaskList({\n  tasks,\n  onChangeTask,\n  onDeleteTask\n}) {\n  return (\n    <ul>\n      {tasks.map(task => (\n        <li key={task.id}>\n          <Task\n            task={task}\n            onChange={onChangeTask}\n            onDelete={onDeleteTask}\n          />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ task, onChange, onDelete }) {\n  const [isEditing, setIsEditing] = useState(false);\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={e => {\n            onChange({\n              ...task,\n              text: e.target.value\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={e => {\n          onChange({\n            ...task,\n            done: e.target.checked\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => onDelete(task.id)}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\nここでは、両方のコンテクストにデフォルト値として `null` を渡しています。実際の値は `TaskApp` コンポーネントが提供します。\n\n### ステップ 2：state と dispatch をコンテクストに入れる {/*step-2-put-state-and-dispatch-into-context*/}\n\n`TaskApp` コンポーネントで両方のコンテクストをインポートできます。`useReducer()` の返り値として `tasks` と `dispatch` を取得し、それらを下位のツリー全体に[提供します](/learn/passing-data-deeply-with-context#step-3-provide-the-context)：\n\n```js {4,7-8}\nimport { TasksContext, TasksDispatchContext } from './TasksContext.js';\n\nexport default function TaskApp() {\n  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);\n  // ...\n  return (\n    <TasksContext value={tasks}>\n      <TasksDispatchContext value={dispatch}>\n        ...\n      </TasksDispatchContext>\n    </TasksContext>\n  );\n}\n```\n\n今のところ、プロパティ経由とコンテクスト経由の両方で情報を渡しています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\nimport { TasksContext, TasksDispatchContext } from './TasksContext.js';\n\nexport default function TaskApp() {\n  const [tasks, dispatch] = useReducer(\n    tasksReducer,\n    initialTasks\n  );\n\n  function handleAddTask(text) {\n    dispatch({\n      type: 'added',\n      id: nextId++,\n      text: text,\n    });\n  }\n\n  function handleChangeTask(task) {\n    dispatch({\n      type: 'changed',\n      task: task\n    });\n  }\n\n  function handleDeleteTask(taskId) {\n    dispatch({\n      type: 'deleted',\n      id: taskId\n    });\n  }\n\n  return (\n    <TasksContext value={tasks}>\n      <TasksDispatchContext value={dispatch}>\n        <h1>Day off in Kyoto</h1>\n        <AddTask\n          onAddTask={handleAddTask}\n        />\n        <TaskList\n          tasks={tasks}\n          onChangeTask={handleChangeTask}\n          onDeleteTask={handleDeleteTask}\n        />\n      </TasksDispatchContext>\n    </TasksContext>\n  );\n}\n\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [...tasks, {\n        id: action.id,\n        text: action.text,\n        done: false\n      }];\n    }\n    case 'changed': {\n      return tasks.map(t => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter(t => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nlet nextId = 3;\nconst initialTasks = [\n  { id: 0, text: 'Philosopher’s Path', done: true },\n  { id: 1, text: 'Visit the temple', done: false },\n  { id: 2, text: 'Drink matcha', done: false }\n];\n```\n\n```js src/TasksContext.js\nimport { createContext } from 'react';\n\nexport const TasksContext = createContext(null);\nexport const TasksDispatchContext = createContext(null);\n```\n\n```js src/AddTask.js\nimport { useState } from 'react';\n\nexport default function AddTask({ onAddTask }) {\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button onClick={() => {\n        setText('');\n        onAddTask(text);\n      }}>Add</button>\n    </>\n  )\n}\n```\n\n```js src/TaskList.js\nimport { useState } from 'react';\n\nexport default function TaskList({\n  tasks,\n  onChangeTask,\n  onDeleteTask\n}) {\n  return (\n    <ul>\n      {tasks.map(task => (\n        <li key={task.id}>\n          <Task\n            task={task}\n            onChange={onChangeTask}\n            onDelete={onDeleteTask}\n          />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ task, onChange, onDelete }) {\n  const [isEditing, setIsEditing] = useState(false);\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={e => {\n            onChange({\n              ...task,\n              text: e.target.value\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={e => {\n          onChange({\n            ...task,\n            done: e.target.checked\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => onDelete(task.id)}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\n次のステップで、props による受け渡しを削除します。\n\n### ステップ 3：ツリー内の任意の場所でコンテクストを使う {/*step-3-use-context-anywhere-in-the-tree*/}\n\n現在すでに、タスクのリストやイベントハンドラを props 経由でツリーに渡す必要はなくなっています。\n\n```js {4-5}\n<TasksContext value={tasks}>\n  <TasksDispatchContext value={dispatch}>\n    <h1>Day off in Kyoto</h1>\n    <AddTask />\n    <TaskList />\n  </TasksDispatchContext>\n</TasksContext>\n```\n\nタスクのリストを必要とするコンポーネントは、代わりに `TasksContext` から読み込むことができます。\n\n```js {2}\nexport default function TaskList() {\n  const tasks = useContext(TasksContext);\n  // ...\n```\n\nタスクリストを更新したい場合は、任意のコンポーネントがコンテクストから `dispatch` 関数を読み取り、それを呼び出します。\n\n```js {3,9-13}\nexport default function AddTask() {\n  const [text, setText] = useState('');\n  const dispatch = useContext(TasksDispatchContext);\n  // ...\n  return (\n    // ...\n    <button onClick={() => {\n      setText('');\n      dispatch({\n        type: 'added',\n        id: nextId++,\n        text: text,\n      });\n    }}>Add</button>\n    // ...\n```\n\n**`TaskApp` コンポーネントはイベントハンドラを一切下に渡しておらず、`TaskList` も `Task` コンポーネントに一切イベントハンドラを渡していません**。各コンポーネントが必要なコンテクストを読み込みます。\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\nimport { TasksContext, TasksDispatchContext } from './TasksContext.js';\n\nexport default function TaskApp() {\n  const [tasks, dispatch] = useReducer(\n    tasksReducer,\n    initialTasks\n  );\n\n  return (\n    <TasksContext value={tasks}>\n      <TasksDispatchContext value={dispatch}>\n        <h1>Day off in Kyoto</h1>\n        <AddTask />\n        <TaskList />\n      </TasksDispatchContext>\n    </TasksContext>\n  );\n}\n\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [...tasks, {\n        id: action.id,\n        text: action.text,\n        done: false\n      }];\n    }\n    case 'changed': {\n      return tasks.map(t => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter(t => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nconst initialTasks = [\n  { id: 0, text: 'Philosopher’s Path', done: true },\n  { id: 1, text: 'Visit the temple', done: false },\n  { id: 2, text: 'Drink matcha', done: false }\n];\n```\n\n```js src/TasksContext.js\nimport { createContext } from 'react';\n\nexport const TasksContext = createContext(null);\nexport const TasksDispatchContext = createContext(null);\n```\n\n```js src/AddTask.js\nimport { useState, useContext } from 'react';\nimport { TasksDispatchContext } from './TasksContext.js';\n\nexport default function AddTask() {\n  const [text, setText] = useState('');\n  const dispatch = useContext(TasksDispatchContext);\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button onClick={() => {\n        setText('');\n        dispatch({\n          type: 'added',\n          id: nextId++,\n          text: text,\n        }); \n      }}>Add</button>\n    </>\n  );\n}\n\nlet nextId = 3;\n```\n\n```js src/TaskList.js active\nimport { useState, useContext } from 'react';\nimport { TasksContext, TasksDispatchContext } from './TasksContext.js';\n\nexport default function TaskList() {\n  const tasks = useContext(TasksContext);\n  return (\n    <ul>\n      {tasks.map(task => (\n        <li key={task.id}>\n          <Task task={task} />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ task }) {\n  const [isEditing, setIsEditing] = useState(false);\n  const dispatch = useContext(TasksDispatchContext);\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={e => {\n            dispatch({\n              type: 'changed',\n              task: {\n                ...task,\n                text: e.target.value\n              }\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={e => {\n          dispatch({\n            type: 'changed',\n            task: {\n              ...task,\n              done: e.target.checked\n            }\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => {\n        dispatch({\n          type: 'deleted',\n          id: task.id\n        });\n      }}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\n**state は引き続きトップレベルの `TaskApp` コンポーネントに「存在」しており、`useReducer` で管理されています**。しかし今や、`tasks` と `dispatch` は、コンテクストをインポートして使うという形で、下位のツリー全体で利用可能になっているのです。\n\n## すべての繋ぎ込みコードを 1 つのファイルに移動 {/*moving-all-wiring-into-a-single-file*/}\n\n必ずしも必要な作業ではありませんが、リデューサとコンテクストの両方を 1 つのファイルに移動することで、コンポーネントをさらに整理することもできます。現在 `TasksContext.js` には 2 つのコンテクスト宣言のみが含まれています。\n\n```js\nimport { createContext } from 'react';\n\nexport const TasksContext = createContext(null);\nexport const TasksDispatchContext = createContext(null);\n```\n\nこのファイルの中身を増やしていきましょう！ リデューサを同じファイルに移動します。次に、同ファイルで新しく `TasksProvider` というコンポーネントを宣言します。このコンポーネントは、すべての要素を繋ぎ合わせるためのものです。\n\n1. リデューサを使って state を管理する。\n2. 下位のコンポーネントに両方のコンテクストを提供する。\n3. JSX を渡すことができるように、[`children` を prop として受け取る](/learn/passing-props-to-a-component#passing-jsx-as-children)ようにする。\n\n```js\nexport function TasksProvider({ children }) {\n  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);\n\n  return (\n    <TasksContext value={tasks}>\n      <TasksDispatchContext value={dispatch}>\n        {children}\n      </TasksDispatchContext>\n    </TasksContext>\n  );\n}\n```\n\n**これにより `TaskApp` コンポーネントから、あらゆる複雑性と繋ぎ込みコードが消え去ります**。\n\n<Sandpack>\n\n```js src/App.js\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\nimport { TasksProvider } from './TasksContext.js';\n\nexport default function TaskApp() {\n  return (\n    <TasksProvider>\n      <h1>Day off in Kyoto</h1>\n      <AddTask />\n      <TaskList />\n    </TasksProvider>\n  );\n}\n```\n\n```js src/TasksContext.js\nimport { createContext, useReducer } from 'react';\n\nexport const TasksContext = createContext(null);\nexport const TasksDispatchContext = createContext(null);\n\nexport function TasksProvider({ children }) {\n  const [tasks, dispatch] = useReducer(\n    tasksReducer,\n    initialTasks\n  );\n\n  return (\n    <TasksContext value={tasks}>\n      <TasksDispatchContext value={dispatch}>\n        {children}\n      </TasksDispatchContext>\n    </TasksContext>\n  );\n}\n\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [...tasks, {\n        id: action.id,\n        text: action.text,\n        done: false\n      }];\n    }\n    case 'changed': {\n      return tasks.map(t => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter(t => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nconst initialTasks = [\n  { id: 0, text: 'Philosopher’s Path', done: true },\n  { id: 1, text: 'Visit the temple', done: false },\n  { id: 2, text: 'Drink matcha', done: false }\n];\n```\n\n```js src/AddTask.js\nimport { useState, useContext } from 'react';\nimport { TasksDispatchContext } from './TasksContext.js';\n\nexport default function AddTask() {\n  const [text, setText] = useState('');\n  const dispatch = useContext(TasksDispatchContext);\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button onClick={() => {\n        setText('');\n        dispatch({\n          type: 'added',\n          id: nextId++,\n          text: text,\n        }); \n      }}>Add</button>\n    </>\n  );\n}\n\nlet nextId = 3;\n```\n\n```js src/TaskList.js\nimport { useState, useContext } from 'react';\nimport { TasksContext, TasksDispatchContext } from './TasksContext.js';\n\nexport default function TaskList() {\n  const tasks = useContext(TasksContext);\n  return (\n    <ul>\n      {tasks.map(task => (\n        <li key={task.id}>\n          <Task task={task} />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ task }) {\n  const [isEditing, setIsEditing] = useState(false);\n  const dispatch = useContext(TasksDispatchContext);\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={e => {\n            dispatch({\n              type: 'changed',\n              task: {\n                ...task,\n                text: e.target.value\n              }\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={e => {\n          dispatch({\n            type: 'changed',\n            task: {\n              ...task,\n              done: e.target.checked\n            }\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => {\n        dispatch({\n          type: 'deleted',\n          id: task.id\n        });\n      }}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\nさらに、`TasksContext.js` から、コンテクストを*使用する*ための以下のような関数をエクスポートすることもできます。\n\n```js\nexport function useTasks() {\n  return useContext(TasksContext);\n}\n\nexport function useTasksDispatch() {\n  return useContext(TasksDispatchContext);\n}\n```\n\nコンポーネントがコンテクストを読む必要がある場合、これらの関数を使うことができます。\n\n```js\nconst tasks = useTasks();\nconst dispatch = useTasksDispatch();\n```\n\n動作は何ら変わりませんが、後でこれらのコンテクストをさらに分割したり、これらの関数にいくつかのロジックを追加したりすることができます。**これで、すべてのコンテクストやリデューサの繋ぎ込みコードが `TasksContext.js` にあることになります。これにより、コンポーネントはクリーンで整頓された状態に保たれ、どこからデータを取得するのかではなく何を表示するのかに集中できるようになります**。\n\n<Sandpack>\n\n```js src/App.js\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\nimport { TasksProvider } from './TasksContext.js';\n\nexport default function TaskApp() {\n  return (\n    <TasksProvider>\n      <h1>Day off in Kyoto</h1>\n      <AddTask />\n      <TaskList />\n    </TasksProvider>\n  );\n}\n```\n\n```js src/TasksContext.js\nimport { createContext, useContext, useReducer } from 'react';\n\nconst TasksContext = createContext(null);\n\nconst TasksDispatchContext = createContext(null);\n\nexport function TasksProvider({ children }) {\n  const [tasks, dispatch] = useReducer(\n    tasksReducer,\n    initialTasks\n  );\n\n  return (\n    <TasksContext value={tasks}>\n      <TasksDispatchContext value={dispatch}>\n        {children}\n      </TasksDispatchContext>\n    </TasksContext>\n  );\n}\n\nexport function useTasks() {\n  return useContext(TasksContext);\n}\n\nexport function useTasksDispatch() {\n  return useContext(TasksDispatchContext);\n}\n\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [...tasks, {\n        id: action.id,\n        text: action.text,\n        done: false\n      }];\n    }\n    case 'changed': {\n      return tasks.map(t => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter(t => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nconst initialTasks = [\n  { id: 0, text: 'Philosopher’s Path', done: true },\n  { id: 1, text: 'Visit the temple', done: false },\n  { id: 2, text: 'Drink matcha', done: false }\n];\n```\n\n```js src/AddTask.js\nimport { useState } from 'react';\nimport { useTasksDispatch } from './TasksContext.js';\n\nexport default function AddTask() {\n  const [text, setText] = useState('');\n  const dispatch = useTasksDispatch();\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button onClick={() => {\n        setText('');\n        dispatch({\n          type: 'added',\n          id: nextId++,\n          text: text,\n        }); \n      }}>Add</button>\n    </>\n  );\n}\n\nlet nextId = 3;\n```\n\n```js src/TaskList.js active\nimport { useState } from 'react';\nimport { useTasks, useTasksDispatch } from './TasksContext.js';\n\nexport default function TaskList() {\n  const tasks = useTasks();\n  return (\n    <ul>\n      {tasks.map(task => (\n        <li key={task.id}>\n          <Task task={task} />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ task }) {\n  const [isEditing, setIsEditing] = useState(false);\n  const dispatch = useTasksDispatch();\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={e => {\n            dispatch({\n              type: 'changed',\n              task: {\n                ...task,\n                text: e.target.value\n              }\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={e => {\n          dispatch({\n            type: 'changed',\n            task: {\n              ...task,\n              done: e.target.checked\n            }\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => {\n        dispatch({\n          type: 'deleted',\n          id: task.id\n        });\n      }}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\n`TasksProvider` はタスクの処理方法を知っている画面要素の一部であり、`useTasks` はツリー内の任意のコンポーネントからタスクを読み出す方法であり、`useTasksDispatch` はそれを更新する方法である、というように考えることができます。\n\n<Note>\n\n`useTasks` や `useTasksDispatch` のような関数は、[*カスタムフック*](/learn/reusing-logic-with-custom-hooks)と呼ばれます。関数名が `use` で始まる場合、その関数はカスタムフックと見なされます。これにより `useContext` のような他のフックを内部で使用できます。\n\n</Note>\n\nアプリが成長するにつれ、このようなコンテクストとリデューサのペアをたくさん作ることになるかもしれません。この強力な手法により、ツリーの深くでデータにアクセスしたい場合でも、あまり手間をかけずにアプリをスケールさせ、[state をリフトアップ](/learn/sharing-state-between-components)することが可能です。\n\n<Recap>\n\n- リデューサとコンテクストを組み合わせることで、任意のコンポーネントが上位の state を読み取り、更新できるようになる。\n- 下位のコンポーネントに state とディスパッチ関数を提供するには以下の手順に従う。\n  1. state 用とディスパッチ関数用の 2 つのコンテクストを作成する。\n  2. リデューサを使うコンポーネントから両方のコンテクストを提供する。\n  3. それらを読む必要があるコンポーネントからコンテクストを使用する。\n- すべての繋ぎ込みコードを 1 つのファイルに移動することで、コンポーネントをさらに整理することができる。\n  - コンテクストを提供する `TasksProvider` のようなコンポーネントをエクスポートする。\n  - コンテクストから情報を読むためのカスタムフックである `useTasks` や `useTasksDispatch` をエクスポートすることもできる。\n- アプリ内で、このようなコンテクストとリデューサのペアを多く作ることができる。\n\n</Recap>\n"
  },
  {
    "path": "src/content/learn/separating-events-from-effects.md",
    "content": "---\ntitle: 'エフェクトからイベントを分離する'\n---\n\n<Intro>\n\nイベントハンドラは、ユーザが同じ操作を繰り返した場合にのみ再実行されます。エフェクトはイベントハンドラとは異なり、props や state 変数のようなそれが読み取る値が前回のレンダー時と異なる場合に「再同期」を行います。また両方の動作をミックスさせて、ある値には反応して再実行されるが他の値には反応しないというエフェクトが欲しくなる場合もあります。このページでは、その方法を説明します。\n\n</Intro>\n\n<YouWillLearn>\n\n- イベントハンドラとエフェクトのどちらを選ぶか\n- エフェクトがリアクティブで、イベントハンドラがリアクティブでない理由\n- エフェクトのコードの一部をリアクティブにしたくない場合の対処法\n- エフェクトイベントとは何か、そしてエフェクトからエフェクトイベントを分離する方法\n- エフェクトイベントを使用してエフェクトから最新の props と state を読み取る方法\n\n</YouWillLearn>\n\n## イベントハンドラとエフェクトのどちらを選ぶか {/*choosing-between-event-handlers-and-effects*/}\n\nまず、イベントハンドラとエフェクトの違いについておさらいしておきましょう。\n\nチャットルームのコンポーネントを実装している場合を想像してください。要件は次のようなものです。\n\n1. コンポーネントは選択中のチャットルームに自動的に接続する。\n2. \"Send\" ボタンをクリックすると、チャットにメッセージが送信される。\n\nこのためのコードはすでに実装されているが、それをどこに置くか迷っているとしましょう。イベントハンドラを使うべきでしょうか、エフェクトを使うべきでしょうか。このような質問に答える必要がある場合は常に、[*なぜ*そのコードを実行する必要があるのか](/learn/synchronizing-with-effects#what-are-effects-and-how-are-they-different-from-events)を考えるようにしてください。\n\n### イベントハンドラは具体的なユーザ操作に反応して実行される {/*event-handlers-run-in-response-to-specific-interactions*/}\n\nユーザの立場からすると、メッセージの送信とは、\"Send\" という特定のボタンがクリックされたから起こるべきものです。それ以外のタイミングや理由でメッセージが送信されてしまうとユーザは怒ることでしょう。これが、メッセージの送信はイベントハンドラで行うべき理由です。イベントハンドラを使うことで、特定のユーザ操作を処理できます。\n\n```js {4-6}\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n  // ...\n  function handleSendClick() {\n    sendMessage(message);\n  }\n  // ...\n  return (\n    <>\n      <input value={message} onChange={e => setMessage(e.target.value)} />\n      <button onClick={handleSendClick}>Send</button>\n    </>\n  );\n}\n```\n\nイベントハンドラを使うことにより、ユーザがボタンを押したときに*だけ* `sendMessage(message)` が実行される、ということが保証されるのです。\n\n### エフェクトは同期が必要なときに常に実行される {/*effects-run-whenever-synchronization-is-needed*/}\n\nコンポーネントはチャットルームへの接続を維持する必要がある、という要件もあるのでした。そのためのコードはどこに記述すべきでしょうか？\n\nそのコードを実行する*理由*は、何か特定のユーザ操作ではありません。ユーザがチャットルーム画面に移動した理由や方法は問題ではありません。ユーザがチャットルームの画面を見てそれを操作できるようになった以上、そのコンポーネントは選択されたチャットサーバへの接続を維持する必要があります。チャットルームコンポーネントがアプリの初期画面であって、ユーザは何の操作も行っていないという場合でも、やはり接続は必要です。したがってエフェクトを使用するべきです。\n\n```js {3-9}\nfunction ChatRoom({ roomId }) {\n  // ...\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [roomId]);\n  // ...\n}\n```\n\nこのコードにより、ユーザが行った特定の操作とは*関係なく*、現在選択されているチャットサーバへの接続が常にアクティブであると確信することができます。ユーザがアプリを開いただけの場合でも、別のルームを選んだ場合でも、他の画面に移動して戻ってきた場合でも、このエフェクトによりコンポーネントが現在選択されているルームと同期し、[必要なとき常に再接続を行う](/learn/lifecycle-of-reactive-effects#why-synchronization-may-need-to-happen-more-than-once)ことが保証されます。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection, sendMessage } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n\n  function handleSendClick() {\n    sendMessage(message);\n  }\n\n  return (\n    <>\n      <h1>Welcome to the {roomId} room!</h1>\n      <input value={message} onChange={e => setMessage(e.target.value)} />\n      <button onClick={handleSendClick}>Send</button>\n    </>\n  );\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Close chat' : 'Open chat'}\n      </button>\n      {show && <hr />}\n      {show && <ChatRoom roomId={roomId} />}\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function sendMessage(message) {\n  console.log('🔵 You sent: ' + message);\n}\n\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput, select { margin-right: 20px; }\n```\n\n</Sandpack>\n\n## リアクティブな値とリアクティブなロジック {/*reactive-values-and-reactive-logic*/}\n\n直感的にはイベントハンドラとは、例えばボタンをクリックするなど、常に「手動」でトリガされるものだと言えるでしょう。一方、エフェクトは「自動」であり、同期を保つために必要なだけ実行・再実行されます。\n\nしかし、もっと正確な考え方があります。\n\nコンポーネントの本体部分で宣言された props、state および変数のことを<CodeStep step={2}>リアクティブな値</CodeStep> (reactive value) と呼びます。この例では、`serverUrl` はリアクティブな値ではありませんが、`roomId` と `message` はリアクティブな値です。これらはレンダーのデータフローに関わる値です。\n\n```js [[2, 3, \"roomId\"], [2, 4, \"message\"]]\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  // ...\n}\n```\n\nこのようなリアクティブな値は、再レンダー時に変化する可能性があります。例えばユーザは `message` を編集したり、ドロップダウンで別の `roomId` を選択したりするかもしれません。イベントハンドラとエフェクトは、それぞれ異なる方法で値の変化に対応します。\n\n- **イベントハンドラ内のロジックはリアクティブではない**。ユーザが同じ操作（クリックなど）を再度行わない限り、再度実行されることはない。イベントハンドラは値の変化に「反応」することなく、リアクティブな値を読み取ることができる。\n- **エフェクト内のロジックはリアクティブである**。エフェクトがリアクティブな値を読み取る場合、[依存配列としてそれを指定する必要がある](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values)。その後再レンダーによって値が変化した場合、React は新しい値でエフェクトのロジックを再実行する。\n\nこの違いを理解するため、先ほどの例をもう一度見てみましょう。\n\n### イベントハンドラ内のロジックはリアクティブではない {/*logic-inside-event-handlers-is-not-reactive*/}\n\nこのコードをご覧ください。このロジックはリアクティブであるべきでしょうか、そうではないでしょうか？\n\n```js [[2, 2, \"message\"]]\n    // ...\n    sendMessage(message);\n    // ...\n```\n\nユーザの観点からは、**`message` が変化することがメッセージを送りたいという意味になるわけでは*ありません***。あくまでユーザが入力の最中だということでしかありません。つまり、メッセージ送信のロジックは、リアクティブであってはならないということです。<CodeStep step={2}>リアクティブな値</CodeStep>が変わったからと言って、再び実行されるべきではありません。したがって、これはイベントハンドラの中にあるべきです。\n\n```js {2}\n  function handleSendClick() {\n    sendMessage(message);\n  }\n```\n\nイベントハンドラはリアクティブではないので、`sendMessage(message)` はユーザが送信ボタンをクリックしたときのみ実行されます。\n\n### エフェクト内のロジックはリアクティブである {/*logic-inside-effects-is-reactive*/}\n\nではこちらの行に戻りましょう。\n\n```js [[2, 2, \"roomId\"]]\n    // ...\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    // ...\n```\n\nユーザの観点からは、**`roomId` が変化することは、別の部屋に接続したいことを意味します**。つまり、ルームに接続するためのロジックもリアクティブであるべきだ、ということです。コードが<CodeStep step={2}>リアクティブな値</CodeStep>の変化に「キャッチアップ」するようにし、値が変われば再度実行されるように*したい*のです。したがってこれはエフェクトの中にあるべきです。\n\n```js {2-3}\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect()\n    };\n  }, [roomId]);\n```\n\nエフェクトはリアクティブなので、`createConnection(serverUrl, roomId)` と `connection.connect()` は、`roomId` の値が変わるごとに実行されます。エフェクトにより、チャットの接続が選択中のルームに同期された状態が維持されます。\n\n## エフェクトから非リアクティブなロジックを分離する {/*extracting-non-reactive-logic-out-of-effects*/}\n\nリアクティブなロジックと非リアクティブなロジックを混在させたい場合、少し厄介なことになります。\n\n例えば、ユーザがチャットに接続したときに通知を表示したいとします。正しい色で通知を表示することができるよう、props から現在のテーマ（ダークまたはライト）を読み取ることにしましょう。\n\n```js {1,4-6}\nfunction ChatRoom({ roomId, theme }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.on('connected', () => {\n      showNotification('Connected!', theme);\n    });\n    connection.connect();\n    // ...\n```\n\nしかし、`theme` は（再レンダーの結果として変化する可能性があるので）リアクティブな値であり、そして[エフェクトが読み取るすべてのリアクティブな値は依存値として宣言する必要があります](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency)。というわけで `theme` はエフェクトの依存配列として指定しないといけません。\n\n```js {5,11}\nfunction ChatRoom({ roomId, theme }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.on('connected', () => {\n      showNotification('Connected!', theme);\n    });\n    connection.connect();\n    return () => {\n      connection.disconnect()\n    };\n  }, [roomId, theme]); // ✅ All dependencies declared\n  // ...\n```\n\n以下の例をいじってみてください。ユーザエクスペリエンスの問題点が分かりますか？\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"toastify-js\": \"1.12.0\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection, sendMessage } from './chat.js';\nimport { showNotification } from './notifications.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId, theme }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.on('connected', () => {\n      showNotification('Connected!', theme);\n    });\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId, theme]);\n\n  return <h1>Welcome to the {roomId} room!</h1>\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Use dark theme\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n        theme={isDark ? 'dark' : 'light'}\n      />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  let connectedCallback;\n  let timeout;\n  return {\n    connect() {\n      timeout = setTimeout(() => {\n        if (connectedCallback) {\n          connectedCallback();\n        }\n      }, 100);\n    },\n    on(event, callback) {\n      if (connectedCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'connected') {\n        throw Error('Only \"connected\" event is supported.');\n      }\n      connectedCallback = callback;\n    },\n    disconnect() {\n      clearTimeout(timeout);\n    }\n  };\n}\n```\n\n```js src/notifications.js\nimport Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\n\nexport function showNotification(message, theme) {\n  Toastify({\n    text: message,\n    duration: 2000,\n    gravity: 'top',\n    position: 'right',\n    style: {\n      background: theme === 'dark' ? 'black' : 'white',\n      color: theme === 'dark' ? 'white' : 'black',\n    },\n  }).showToast();\n}\n```\n\n```css\nlabel { display: block; margin-top: 10px; }\n```\n\n</Sandpack>\n\n`roomId` が変わるとチャットが再接続され、それは期待通りの動作です。しかし、`theme` も依存値であるため、ダークテーマとライトテーマを切り替えることでも毎回チャットが再接続されてしまっています。これはあまり良くありません！\n\nつまり、以下の行は（リアクティブである）エフェクトの中にあるにもかかわらず、リアクティブであってほしくないということです。\n\n```js\n      // ...\n      showNotification('Connected!', theme);\n      // ...\n```\n\nこの非リアクティブなロジックを、周囲にあるリアクティブなエフェクトのコードから分離する方法が必要です。\n\n### エフェクトイベントの宣言 {/*declaring-an-effect-event*/}\n\n[`useEffectEvent`](/reference/react/useEffectEvent) という特別なフックを使うことで、エフェクトからこの非リアクティブなロジックを分離することができます。\n\n```js {1,4-6}\nimport { useEffect, useEffectEvent } from 'react';\n\nfunction ChatRoom({ roomId, theme }) {\n  const onConnected = useEffectEvent(() => {\n    showNotification('Connected!', theme);\n  });\n  // ...\n```\n\nここで、`onConnected` は*エフェクトイベント (Effect Event)* と呼ばれるものです。これはエフェクトロジックの一部でありながら、むしろイベントハンドラに近い動作をします。この中のロジックはリアクティブではなく、常に props と state の最新の値を「見る」ことができます。\n\nこれで、エフェクトの内部から `onConnected` エフェクトイベントを呼び出せるようになります。\n\n```js {2-4,9,13}\nfunction ChatRoom({ roomId, theme }) {\n  const onConnected = useEffectEvent(() => {\n    showNotification('Connected!', theme);\n  });\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.on('connected', () => {\n      onConnected();\n    });\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]); // ✅ All dependencies declared\n  // ...\n```\n\nこれで問題は解決しました。なお、`theme` はエフェクト内で使われなくなったので、依存配列から*削除*しなくてはなりません。また**エフェクトイベント自体はリアクティブではなく**、依存配列から除外すべきなので、`onConnected` を依存配列に*加える*必要もありません。\n\n新しい動作が期待通りであることを確認してください。\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"toastify-js\": \"1.12.0\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js\nimport { useState, useEffect } from 'react';\nimport { useEffectEvent } from 'react';\nimport { createConnection, sendMessage } from './chat.js';\nimport { showNotification } from './notifications.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId, theme }) {\n  const onConnected = useEffectEvent(() => {\n    showNotification('Connected!', theme);\n  });\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.on('connected', () => {\n      onConnected();\n    });\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n\n  return <h1>Welcome to the {roomId} room!</h1>\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Use dark theme\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n        theme={isDark ? 'dark' : 'light'}\n      />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  let connectedCallback;\n  let timeout;\n  return {\n    connect() {\n      timeout = setTimeout(() => {\n        if (connectedCallback) {\n          connectedCallback();\n        }\n      }, 100);\n    },\n    on(event, callback) {\n      if (connectedCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'connected') {\n        throw Error('Only \"connected\" event is supported.');\n      }\n      connectedCallback = callback;\n    },\n    disconnect() {\n      clearTimeout(timeout);\n    }\n  };\n}\n```\n\n```js src/notifications.js hidden\nimport Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\n\nexport function showNotification(message, theme) {\n  Toastify({\n    text: message,\n    duration: 2000,\n    gravity: 'top',\n    position: 'right',\n    style: {\n      background: theme === 'dark' ? 'black' : 'white',\n      color: theme === 'dark' ? 'white' : 'black',\n    },\n  }).showToast();\n}\n```\n\n```css\nlabel { display: block; margin-top: 10px; }\n```\n\n</Sandpack>\n\nエフェクトイベントは、イベントハンドラと非常に似たものだと考えることができます。主な違いは、イベントハンドラがユーザの操作に反応して実行されるのに対し、エフェクトイベントはエフェクトからトリガされることです。エフェクトイベントを使うことで、リアクティブであるエフェクトと、リアクティブであってはならないコードとの間の「繋がりを断ち切る」ことができます。\n\n### エフェクトイベントで最新の props や state を読み取る {/*reading-latest-props-and-state-with-effect-events*/}\n\n依存値に関するリンタを抑制したくなるようなパターンの多くは、エフェクトイベントによって回避可能です。\n\n例えば、ページへの訪問をログに記録するエフェクトがあるとしましょう。\n\n```js\nfunction Page() {\n  useEffect(() => {\n    logVisit();\n  }, []);\n  // ...\n}\n```\n\n後になって、サイトに複数のページを追加することになりました。`Page` コンポーネントは現在のパスを表す `url` を props として受け取るようになります。この `url` を `logVisit` コールに渡そうと思うのですが、そこで依存値リンタが文句を言ってきます。\n\n```js {1,3}\nfunction Page({ url }) {\n  useEffect(() => {\n    logVisit(url);\n  }, []); // 🔴 React Hook useEffect has a missing dependency: 'url'\n  // ...\n}\n```\n\nコードに何をさせたいのか考えてみましょう。それぞれの URL は別々のページを表しているので、*やりたいこと*はそれぞれの URL に対して別々に訪問ログを記録することです。言い換えれば、この `logVisit` の呼び出しは、`url` に関して確かに*リアクティブであるべき*ですね。したがってこの場合、依存値リンタが言う通り、`url` を依存配列に追加することは理にかなっています。\n\n```js {4}\nfunction Page({ url }) {\n  useEffect(() => {\n    logVisit(url);\n  }, [url]); // ✅ All dependencies declared\n  // ...\n}\n```\n\nところがここで、個々のページ訪問ログに、ショッピングカート内にある商品の数も含めたくなったとしましょう。\n\n```js {2-3,6}\nfunction Page({ url }) {\n  const { items } = useContext(ShoppingCartContext);\n  const numberOfItems = items.length;\n\n  useEffect(() => {\n    logVisit(url, numberOfItems);\n  }, [url]); // 🔴 React Hook useEffect has a missing dependency: 'numberOfItems'\n  // ...\n}\n```\n\nエフェクト内で `numberOfItems` を使用したので、リンタは依存値としてそれを追加するように言ってきます。しかし `numberOfItems` に対しては、`logVisit` の呼び出しがリアクティブになることは*望ましくありません*。ユーザがショッピングカートに何かを入れて、`numberOfItems` が変化しても、それはユーザが再びページを訪れたことを*意味しません*。つまり、*ページを訪れた*ということは、ある意味で「イベント」なのです。ある特定の瞬間に起こることです。\n\nこのコードを 2 つに分割しましょう。\n\n```js {5-7,10}\nfunction Page({ url }) {\n  const { items } = useContext(ShoppingCartContext);\n  const numberOfItems = items.length;\n\n  const onVisit = useEffectEvent(visitedUrl => {\n    logVisit(visitedUrl, numberOfItems);\n  });\n\n  useEffect(() => {\n    onVisit(url);\n  }, [url]); // ✅ All dependencies declared\n  // ...\n}\n```\n\nここで、`onVisit` はエフェクトイベントです。この中のコードはリアクティブではありません。このため、`numberOfItems`（または他のリアクティブな値！）を使用しても、変更時に周囲のコードが再実行される心配はありません。\n\n一方、エフェクトそのものはリアクティブなままです。エフェクトの中のコードは props である `url` を使用しているため、異なる `url` で再レンダーが起きるたびにエフェクトが再実行されます。次にそれが `onVisit` エフェクトイベントを呼び出します。\n\nその結果、`url` に変化があるごとに `logVisit` が呼び出され、常に最新の `numberOfItems` が読み取れることになります。一方で `numberOfItems` だけが変化してもコードの再実行は起きません。\n\n<Note>\n\n`onVisit()` は引数なしで呼び出して、関数内から直に `url` を読み取ればいいのでは、と疑問に思うかもしれません。\n\n```js {2,6}\n  const onVisit = useEffectEvent(() => {\n    logVisit(url, numberOfItems);\n  });\n\n  useEffect(() => {\n    onVisit();\n  }, [url]);\n```\n\nこれでも動作はするのですが、この `url` は明示的にエフェクトイベントに渡す方がいいでしょう。**エフェクトイベントの引数として `url` を渡すことにより、異なる `url` のページを訪問することがユーザの視点から見ると別の「イベント」を構成しているのだ、という意図を表現できます**。`visitedUrl` は、起こった「イベント」の*一部*なのです。\n\n```js {1-2,6}\n  const onVisit = useEffectEvent(visitedUrl => {\n    logVisit(visitedUrl, numberOfItems);\n  });\n\n  useEffect(() => {\n    onVisit(url);\n  }, [url]);\n```\n\nエフェクトイベントが `visitedUrl` を明示的に「要求」しているので、エフェクト側の依存配列から誤って `url` を削除することができなくなりました。もし依存配列から `url` を削除してしまうと（別々のページへの訪問が 1 つとしてカウントされてしまう）、リンタはそれについて警告を発します。`onVisit` は `url` に対してはリアクティブであってほしいのですから、`url` はイベント内で読み込む（そうするとリアクティブでなくなってしまう）のではなく、*エフェクトから*渡すようにしましょう。\n\nこれは、エフェクトの中に非同期のロジックがある場合に特に重要になります。\n\n```js {6,8}\n  const onVisit = useEffectEvent(visitedUrl => {\n    logVisit(visitedUrl, numberOfItems);\n  });\n\n  useEffect(() => {\n    setTimeout(() => {\n      onVisit(url);\n    }, 5000); // Delay logging visits\n  }, [url]);\n```\n\nこの場合、`onVisit` 内で `url` を読み取ると、（既に別物に変わっている可能性がある）*最新*の `url` を読み取ってしまいますが、`visitedUrl` はこのエフェクト（およびこの `onVisit` コール）が実行される大元のきっかけとなった `url` に対応することになります。\n\n</Note>\n\n<DeepDive>\n\n#### 代わりに依存値リンタを止めても大丈夫？ {/*is-it-okay-to-suppress-the-dependency-linter-instead*/}\n\n既存のコードベースで、以下のようにリントルールが抑制されているのを見かけることがあるかもしれません。\n\n```js {expectedErrors: {'react-compiler': [8]}} {7-9}\nfunction Page({ url }) {\n  const { items } = useContext(ShoppingCartContext);\n  const numberOfItems = items.length;\n\n  useEffect(() => {\n    logVisit(url, numberOfItems);\n    // 🔴 Avoid suppressing the linter like this:\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [url]);\n  // ...\n}\n```\n\n**決してリンタを抑制しない**ことをお勧めします。\n\nこのルールを止めてしまうことの最大の欠点は、新たにコードにリアクティブな依存値を追加してそれにエフェクトが「反応する」必要がある場合でも、もはや React が警告を表示できなくなってしまうことです。先ほどの例でも、`url` を依存配列に追加し忘れずに済んだのは、そうするよう React が教えてくれていたからでしたね。リンタを無効化してしまうと、今後そのエフェクトを編集する際に、そのようなリマインダを受け取ることができなくなります。これはバグにつながります。\n\n以下は、リンタを無効化することで発生する紛らわしいバグの一例です。この例では、`handleMove` 関数は、ドットがカーソルに従うべきかどうかを決定するために、`canMove` という state 変数の現在値を読み取ろうとしています。しかし `handleMove` の内部では `canMove` は常に `true` となります。\n\nなぜかわかりますか？\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [16]}}\nimport { useState, useEffect } from 'react';\n\nexport default function App() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n  const [canMove, setCanMove] = useState(true);\n\n  function handleMove(e) {\n    if (canMove) {\n      setPosition({ x: e.clientX, y: e.clientY });\n    }\n  }\n\n  useEffect(() => {\n    window.addEventListener('pointermove', handleMove);\n    return () => window.removeEventListener('pointermove', handleMove);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  return (\n    <>\n      <label>\n        <input type=\"checkbox\"\n          checked={canMove}\n          onChange={e => setCanMove(e.target.checked)}\n        />\n        The dot is allowed to move\n      </label>\n      <hr />\n      <div style={{\n        position: 'absolute',\n        backgroundColor: 'pink',\n        borderRadius: '50%',\n        opacity: 0.6,\n        transform: `translate(${position.x}px, ${position.y}px)`,\n        pointerEvents: 'none',\n        left: -20,\n        top: -20,\n        width: 40,\n        height: 40,\n      }} />\n    </>\n  );\n}\n```\n\n```css\nbody {\n  height: 200px;\n}\n```\n\n</Sandpack>\n\n\nこのコードの問題は、依存値リンタを無効化してしまっていることです。無効化のコメントを外すと、このエフェクトの依存値として `handleMove` 関数を含める必要があることがわかります。これは理にかなっています。なぜなら `handleMove` はコンポーネント本体の内部で宣言されているのでリアクティブな値だからです。すべてのリアクティブな値は依存値として指定されなければなりませんし、さもなくば時間の経過とともに古くなってしまう可能性があります！\n\n元のコードを書いた人は、React に対して「このエフェクトはどのリアクティブな値にも依存しない (`[]`)」と「嘘」をついています。だから React は `canMove`（とそれを使う `handleMove`）が変化したのにエフェクトを再同期しなかったのです。React はエフェクトを再同期しなかったため、リスナとしてアタッチされる `handleMove` は、初回レンダー時に作成された `handleMove` 関数のままとなります。初回レンダー時に `canMove` は `true` であったため、同時に作られた `handleMove` からも永遠にその値が見え続けることになります。\n\n**リンタを決して抑制しないようにすれば、値が古くなることに関する問題が発生することはありません**。\n\n`useEffectEvent` を使えば、リンタに「嘘」をつく必要はなく、期待通りにコードが動きます。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { useEffectEvent } from 'react';\n\nexport default function App() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n  const [canMove, setCanMove] = useState(true);\n\n  const onMove = useEffectEvent(e => {\n    if (canMove) {\n      setPosition({ x: e.clientX, y: e.clientY });\n    }\n  });\n\n  useEffect(() => {\n    window.addEventListener('pointermove', onMove);\n    return () => window.removeEventListener('pointermove', onMove);\n  }, []);\n\n  return (\n    <>\n      <label>\n        <input type=\"checkbox\"\n          checked={canMove}\n          onChange={e => setCanMove(e.target.checked)}\n        />\n        The dot is allowed to move\n      </label>\n      <hr />\n      <div style={{\n        position: 'absolute',\n        backgroundColor: 'pink',\n        borderRadius: '50%',\n        opacity: 0.6,\n        transform: `translate(${position.x}px, ${position.y}px)`,\n        pointerEvents: 'none',\n        left: -20,\n        top: -20,\n        width: 40,\n        height: 40,\n      }} />\n    </>\n  );\n}\n```\n\n```css\nbody {\n  height: 200px;\n}\n```\n\n</Sandpack>\n\nだからといって `useEffectEvent` が*常に*正しい解決策だというわけではありません。コード中の、リアクティブにしたくない行にだけ適用するようにしてください。上記のサンドボックスでは、エフェクトコードが `canMove` に関してはリアクティブであってほしくなかったため、エフェクトイベントとして抜き出すことが理にかなっていたのです。\n\nリンタを無効化しないで済む他の方法については、[エフェクトから依存値を取り除く](/learn/removing-effect-dependencies)を参照してください。\n\n</DeepDive>\n\n### エフェクトイベントに関する制限事項 {/*limitations-of-effect-events*/}\n\nエフェクトイベントは、使い方が非常に限定されています。\n\n* **エフェクトの内部からしか呼び出すことができない**。\n* **他のコンポーネントやフックに渡してはいけない**。\n\n例えば、次のようにエフェクトイベントを宣言して渡してはいけません。\n\n```js {4-6,8}\nfunction Timer() {\n  const [count, setCount] = useState(0);\n\n  const onTick = useEffectEvent(() => {\n    setCount(count + 1);\n  });\n\n  useTimer(onTick, 1000); // 🔴 Avoid: Passing Effect Events\n\n  return <h1>{count}</h1>\n}\n\nfunction useTimer(callback, delay) {\n  useEffect(() => {\n    const id = setInterval(() => {\n      callback();\n    }, delay);\n    return () => {\n      clearInterval(id);\n    };\n  }, [delay, callback]); // Need to specify \"callback\" in dependencies\n}\n```\n\n上記の代わりに、エフェクトイベントは常に、それを使用するエフェクトのすぐ隣で宣言してください。\n\n```js {10-12,16,21}\nfunction Timer() {\n  const [count, setCount] = useState(0);\n  useTimer(() => {\n    setCount(count + 1);\n  }, 1000);\n  return <h1>{count}</h1>\n}\n\nfunction useTimer(callback, delay) {\n  const onTick = useEffectEvent(() => {\n    callback();\n  });\n\n  useEffect(() => {\n    const id = setInterval(() => {\n      onTick(); // ✅ Good: Only called locally inside an Effect\n    }, delay);\n    return () => {\n      clearInterval(id);\n    };\n  }, [delay]); // No need to specify \"onTick\" (an Effect Event) as a dependency\n}\n```\n\nエフェクトイベントとは、エフェクトコードを構成する「パーツ」のうちの非リアクティブな部分です。それを使用するエフェクトの隣に置くようにしましょう。\n\n<Recap>\n\n- イベントハンドラは、特定のユーザ操作に応答して実行される。\n- エフェクトは、同期が必要になるたびに実行される。\n- イベントハンドラ内のロジックは、リアクティブではない。\n- エフェクト内のロジックは、リアクティブである。\n- エフェクト内の非リアクティブなロジックをエフェクトイベントに移動することができる。\n- エフェクトイベントを呼び出せるのはエフェクトの内部だけである。\n- エフェクトイベントを他のコンポーネントやフックに渡してはいけない。\n\n</Recap>\n\n<Challenges>\n\n#### 更新されない変数を修正 {/*fix-a-variable-that-doesnt-update*/}\n\nこの `Timer` コンポーネントは、1 秒ごとに値が増加する `count` という state 変数を保持しています。値をいくつ増加させるのかは、`increment` という state 変数に格納されます。プラスボタンとマイナスボタンで `increment` 変数を制御できます。\n\nしかし、プラスボタンを何度クリックしても、カウンタは 1 秒ごとに 1 つずつ増えていきます。このコードの何が問題なのでしょうか？ なぜエフェクトのコード内部では `increment` が常に 1 になっているのでしょうか？ 間違いを見つけて修正しましょう。\n\n<Hint>\n\nこのコードを直すには、ルールを守ればいいのです。\n\n</Hint>\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [14]}}\nimport { useState, useEffect } from 'react';\n\nexport default function Timer() {\n  const [count, setCount] = useState(0);\n  const [increment, setIncrement] = useState(1);\n\n  useEffect(() => {\n    const id = setInterval(() => {\n      setCount(c => c + increment);\n    }, 1000);\n    return () => {\n      clearInterval(id);\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  return (\n    <>\n      <h1>\n        Counter: {count}\n        <button onClick={() => setCount(0)}>Reset</button>\n      </h1>\n      <hr />\n      <p>\n        Every second, increment by:\n        <button disabled={increment === 0} onClick={() => {\n          setIncrement(i => i - 1);\n        }}>–</button>\n        <b>{increment}</b>\n        <button onClick={() => {\n          setIncrement(i => i + 1);\n        }}>+</button>\n      </p>\n    </>\n  );\n}\n```\n\n```css\nbutton { margin: 10px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n例によって、エフェクトのバグを探すときは、リンタを無効化している箇所を探すことから始めてください。\n\n無効化コメントを削除すると、React はこのエフェクトのコードが `increment` に依存していることを教えてくれます。このエフェクトはリアクティブな値に依存していない (`[]`) と主張することで React に「嘘をついた」のです。依存配列に `increment` を追加しましょう。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function Timer() {\n  const [count, setCount] = useState(0);\n  const [increment, setIncrement] = useState(1);\n\n  useEffect(() => {\n    const id = setInterval(() => {\n      setCount(c => c + increment);\n    }, 1000);\n    return () => {\n      clearInterval(id);\n    };\n  }, [increment]);\n\n  return (\n    <>\n      <h1>\n        Counter: {count}\n        <button onClick={() => setCount(0)}>Reset</button>\n      </h1>\n      <hr />\n      <p>\n        Every second, increment by:\n        <button disabled={increment === 0} onClick={() => {\n          setIncrement(i => i - 1);\n        }}>–</button>\n        <b>{increment}</b>\n        <button onClick={() => {\n          setIncrement(i => i + 1);\n        }}>+</button>\n      </p>\n    </>\n  );\n}\n```\n\n```css\nbutton { margin: 10px; }\n```\n\n</Sandpack>\n\nこれで `increment` が変わると、React はエフェクトを再同期し、インターバルも再スタートされます。\n\n</Solution>\n\n#### カウンタのフリーズを修正 {/*fix-a-freezing-counter*/}\n\nこの `Timer` コンポーネントは、1 秒ごとに値が増加する `count` という state 変数を保持しています。値をいくつ増加させるのかは `increment` という state 変数に格納され、プラスとマイナスのボタンでコントロールすることができます。例えば、プラスボタンを 9 回押してみると、1 秒ごとにカウントが 1 ずつではなく 10 ずつ増えていくことがわかります。\n\nこのユーザインターフェースには小さな問題があります。プラス・マイナスボタンを毎秒 1 回以上の速さで押し続けると、タイマーが一時停止してしまうのです。最後にどちらかのボタンを押してから 1 秒が経過すると、タイマーが再開します。この原因を突き止め、タイマーが止まらず*毎秒*動作するように修正しましょう。\n\n<Hint>\n\nタイマーを設定するエフェクトが `increment` 値に「反応」してしまっているようです。`setCount` を呼び出すために現在の `increment` 値を使用している行は、本当にリアクティブである必要があるのでしょうか？\n\n</Hint>\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { useEffectEvent } from 'react';\n\nexport default function Timer() {\n  const [count, setCount] = useState(0);\n  const [increment, setIncrement] = useState(1);\n\n  useEffect(() => {\n    const id = setInterval(() => {\n      setCount(c => c + increment);\n    }, 1000);\n    return () => {\n      clearInterval(id);\n    };\n  }, [increment]);\n\n  return (\n    <>\n      <h1>\n        Counter: {count}\n        <button onClick={() => setCount(0)}>Reset</button>\n      </h1>\n      <hr />\n      <p>\n        Every second, increment by:\n        <button disabled={increment === 0} onClick={() => {\n          setIncrement(i => i - 1);\n        }}>–</button>\n        <b>{increment}</b>\n        <button onClick={() => {\n          setIncrement(i => i + 1);\n        }}>+</button>\n      </p>\n    </>\n  );\n}\n```\n\n```css\nbutton { margin: 10px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n問題はエフェクト内のコードが `increment` state 変数を使用していることです。この変数はエフェクトの依存値になっているので、`increment` を変更するたびにエフェクトが再同期し、インターバルがクリアされることになります。発火する前に毎回インターバルをクリアし続けると、タイマーが停止したように見えてしまいます。\n\nこの問題を解決するには、エフェクトから `onTick` エフェクトイベントを分離します。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { useEffectEvent } from 'react';\n\nexport default function Timer() {\n  const [count, setCount] = useState(0);\n  const [increment, setIncrement] = useState(1);\n\n  const onTick = useEffectEvent(() => {\n    setCount(c => c + increment);\n  });\n\n  useEffect(() => {\n    const id = setInterval(() => {\n      onTick();\n    }, 1000);\n    return () => {\n      clearInterval(id);\n    };\n  }, []);\n\n  return (\n    <>\n      <h1>\n        Counter: {count}\n        <button onClick={() => setCount(0)}>Reset</button>\n      </h1>\n      <hr />\n      <p>\n        Every second, increment by:\n        <button disabled={increment === 0} onClick={() => {\n          setIncrement(i => i - 1);\n        }}>–</button>\n        <b>{increment}</b>\n        <button onClick={() => {\n          setIncrement(i => i + 1);\n        }}>+</button>\n      </p>\n    </>\n  );\n}\n```\n\n\n```css\nbutton { margin: 10px; }\n```\n\n</Sandpack>\n\n`onTick` はエフェクトイベントなので、その中のコードはリアクティブではありません。`increment` を変更しても、エフェクトはトリガされません。\n\n</Solution>\n\n#### 遅延を調整できない問題を修正 {/*fix-a-non-adjustable-delay*/}\n\nこの例では、インターバルの遅延をカスタマイズすることができます。この値は `delay` という state 変数に格納され、2 つのボタンによって更新できます。しかし、`delay` が 1000 ミリ秒（つまり 1 秒）になるまで \"+ 100 ms\" ボタンを押しても、タイマーは非常に速く（100 ミリ秒ごとに）増えていることに気づくでしょう。`delay` の変更が無視されているようです。バグを特定し、修正してください。\n\n<Hint>\n\nエフェクトイベントの中のコードはリアクティブではありません。`setInterval` 呼び出しを*実際に*再実行しないといけないケースがあるのでは？\n\n</Hint>\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { useEffectEvent } from 'react';\n\nexport default function Timer() {\n  const [count, setCount] = useState(0);\n  const [increment, setIncrement] = useState(1);\n  const [delay, setDelay] = useState(100);\n\n  const onTick = useEffectEvent(() => {\n    setCount(c => c + increment);\n  });\n\n  const onMount = useEffectEvent(() => {\n    return setInterval(() => {\n      onTick();\n    }, delay);\n  });\n\n  useEffect(() => {\n    const id = onMount();\n    return () => {\n      clearInterval(id);\n    }\n  }, []);\n\n  return (\n    <>\n      <h1>\n        Counter: {count}\n        <button onClick={() => setCount(0)}>Reset</button>\n      </h1>\n      <hr />\n      <p>\n        Increment by:\n        <button disabled={increment === 0} onClick={() => {\n          setIncrement(i => i - 1);\n        }}>–</button>\n        <b>{increment}</b>\n        <button onClick={() => {\n          setIncrement(i => i + 1);\n        }}>+</button>\n      </p>\n      <p>\n        Increment delay:\n        <button disabled={delay === 100} onClick={() => {\n          setDelay(d => d - 100);\n        }}>–100 ms</button>\n        <b>{delay} ms</b>\n        <button onClick={() => {\n          setDelay(d => d + 100);\n        }}>+100 ms</button>\n      </p>\n    </>\n  );\n}\n```\n\n\n```css\nbutton { margin: 10px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n上記の例の問題点は、コードが実際に何をすべきかを考えずに `onMount` というエフェクトイベントを抽出してしまったことです。エフェクトイベントを抽出するのは、コードの一部を非リアクティブにしたいという特別な理由があるときだけです。しかし、`setInterval` の呼び出しは `delay` state 変数に対してリアクティブであるべきです。`delay` が変更された場合、インターバルを最初から設定する必要があります！ このコードを修正するには、すべてのリアクティブなコードをエフェクトの内部に戻します。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { useEffectEvent } from 'react';\n\nexport default function Timer() {\n  const [count, setCount] = useState(0);\n  const [increment, setIncrement] = useState(1);\n  const [delay, setDelay] = useState(100);\n\n  const onTick = useEffectEvent(() => {\n    setCount(c => c + increment);\n  });\n\n  useEffect(() => {\n    const id = setInterval(() => {\n      onTick();\n    }, delay);\n    return () => {\n      clearInterval(id);\n    }\n  }, [delay]);\n\n  return (\n    <>\n      <h1>\n        Counter: {count}\n        <button onClick={() => setCount(0)}>Reset</button>\n      </h1>\n      <hr />\n      <p>\n        Increment by:\n        <button disabled={increment === 0} onClick={() => {\n          setIncrement(i => i - 1);\n        }}>–</button>\n        <b>{increment}</b>\n        <button onClick={() => {\n          setIncrement(i => i + 1);\n        }}>+</button>\n      </p>\n      <p>\n        Increment delay:\n        <button disabled={delay === 100} onClick={() => {\n          setDelay(d => d - 100);\n        }}>–100 ms</button>\n        <b>{delay} ms</b>\n        <button onClick={() => {\n          setDelay(d => d + 100);\n        }}>+100 ms</button>\n      </p>\n    </>\n  );\n}\n```\n\n```css\nbutton { margin: 10px; }\n```\n\n</Sandpack>\n\n一般的に、`onMount` のような、コードの*目的*ではなく*タイミング*に焦点を当てた名前の関数は疑ってかかるべきです。最初は「分かりやすい」と感じるかもしれませんが、実際にはあなたの意図を分かりづらくします。経験則から言うと、エフェクトイベントは*ユーザ*の視点から起こることに対応する必要があります。例えば、`onMessage`、`onTick`、`onVisit`、`onConnected` は、良いエフェクトイベント名です。これらのイベントの中のコードは、おそらくリアクティブである必要はないでしょう。一方、`onMount`、`onUpdate`、`onUnmount`、`onAfterRender` は名前は過度に汎用的なものであり、リアクティブにすべきコードを誤って入れてしまうことがあります。このため、エフェクトイベントの名前は、コードがいつ実行されるかではなく、*ユーザの視点から何が起こったのか*を基準にして付けるべきなのです。\n\n</Solution>\n\n#### 遅延表示型の通知を修正 {/*fix-a-delayed-notification*/}\n\nチャットルームに参加すると、このコンポーネントは通知を表示します。しかし、このコンポーネントはすぐに通知を表示するわけではありません。その代わり、ユーザが UI を見て回る機会があるように、通知を意図的に 2 秒遅らせて表示します。\n\nほぼ機能していますが、バグがあります。ドロップダウンを \"general\" から \"travel\"、そして \"music\" へと素早く切り替えてみてください。十分な速さで行うと通知が 2 つ表示されますが（これ自体は期待通り）、どちらも \"Welcome to music\" と表示されてしまっています。\n\n\"general\" から \"travel\"、そして \"music\" に素早く切り替えると、2 つの通知が表示され、1 つ目は \"Welcome to travel\"、2 つ目は \"Welcome to music\" となるように修正してください。（追加の課題として、ちゃんと 2 個の通知が正しい部屋を表示するようにした後で、後者の通知のみが表示されるようにコードを修正してみてください。）\n\n<Hint>\n\nエフェクトはどのルームに接続したかを知っています。エフェクトイベントに渡したい情報がありませんか？\n\n</Hint>\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"toastify-js\": \"1.12.0\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js\nimport { useState, useEffect } from 'react';\nimport { useEffectEvent } from 'react';\nimport { createConnection, sendMessage } from './chat.js';\nimport { showNotification } from './notifications.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId, theme }) {\n  const onConnected = useEffectEvent(() => {\n    showNotification('Welcome to ' + roomId, theme);\n  });\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.on('connected', () => {\n      setTimeout(() => {\n        onConnected();\n      }, 2000);\n    });\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n\n  return <h1>Welcome to the {roomId} room!</h1>\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Use dark theme\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n        theme={isDark ? 'dark' : 'light'}\n      />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  let connectedCallback;\n  let timeout;\n  return {\n    connect() {\n      timeout = setTimeout(() => {\n        if (connectedCallback) {\n          connectedCallback();\n        }\n      }, 100);\n    },\n    on(event, callback) {\n      if (connectedCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'connected') {\n        throw Error('Only \"connected\" event is supported.');\n      }\n      connectedCallback = callback;\n    },\n    disconnect() {\n      clearTimeout(timeout);\n    }\n  };\n}\n```\n\n```js src/notifications.js hidden\nimport Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\n\nexport function showNotification(message, theme) {\n  Toastify({\n    text: message,\n    duration: 2000,\n    gravity: 'top',\n    position: 'right',\n    style: {\n      background: theme === 'dark' ? 'black' : 'white',\n      color: theme === 'dark' ? 'white' : 'black',\n    },\n  }).showToast();\n}\n```\n\n```css\nlabel { display: block; margin-top: 10px; }\n```\n\n</Sandpack>\n\n<Solution>\n\nエフェクトイベントの内部では、`roomId` は*エフェクトイベントが呼び出された時点*の値です。\n\n今回のエフェクトイベントは、2 秒間の遅延を伴って呼び出されます。travel ルームから music ルームに素早く切り替える場合、travel ルームの通知が表示される頃には、`roomId` は既に `\"music\"` になっています。そのため、両方の通知で \"Welcome to music\" と表示されてしまったのです。\n\nこの問題を解決するには、エフェクトイベントの中で*最新の* `roomId` を読み込むのではなく、以下の `connectedRoomId` のように、エフェクトイベントのパラメータとして指定するようにします。そして、`onConnected(roomId)` のように呼び出すことで、エフェクトから `roomId` を渡すようにします。\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"toastify-js\": \"1.12.0\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js\nimport { useState, useEffect } from 'react';\nimport { useEffectEvent } from 'react';\nimport { createConnection, sendMessage } from './chat.js';\nimport { showNotification } from './notifications.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId, theme }) {\n  const onConnected = useEffectEvent(connectedRoomId => {\n    showNotification('Welcome to ' + connectedRoomId, theme);\n  });\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.on('connected', () => {\n      setTimeout(() => {\n        onConnected(roomId);\n      }, 2000);\n    });\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n\n  return <h1>Welcome to the {roomId} room!</h1>\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Use dark theme\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n        theme={isDark ? 'dark' : 'light'}\n      />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  let connectedCallback;\n  let timeout;\n  return {\n    connect() {\n      timeout = setTimeout(() => {\n        if (connectedCallback) {\n          connectedCallback();\n        }\n      }, 100);\n    },\n    on(event, callback) {\n      if (connectedCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'connected') {\n        throw Error('Only \"connected\" event is supported.');\n      }\n      connectedCallback = callback;\n    },\n    disconnect() {\n      clearTimeout(timeout);\n    }\n  };\n}\n```\n\n```js src/notifications.js hidden\nimport Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\n\nexport function showNotification(message, theme) {\n  Toastify({\n    text: message,\n    duration: 2000,\n    gravity: 'top',\n    position: 'right',\n    style: {\n      background: theme === 'dark' ? 'black' : 'white',\n      color: theme === 'dark' ? 'white' : 'black',\n    },\n  }).showToast();\n}\n```\n\n```css\nlabel { display: block; margin-top: 10px; }\n```\n\n</Sandpack>\n\n`roomId` が `\"travel\"` に設定された（つまり `\"travel\"` ルームに接続したときの）エフェクトは、`\"travel\"` の通知を表示します。`roomId` が `\"music\"` に設定された（つまり `\"music\"` ルームに接続したときの）エフェクトは、`\"music\"` に対する通知を表示します。つまり、`connectedRoomId` はエフェクト（リアクティブなもの）に由来し、一方で `theme` は常に最新の値を使用するのです。\n\n追加の課題を解決するには、通知のタイムアウト ID を保存し、エフェクトのクリーンアップ関数でクリアするようにしてください。\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"toastify-js\": \"1.12.0\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js\nimport { useState, useEffect } from 'react';\nimport { useEffectEvent } from 'react';\nimport { createConnection, sendMessage } from './chat.js';\nimport { showNotification } from './notifications.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId, theme }) {\n  const onConnected = useEffectEvent(connectedRoomId => {\n    showNotification('Welcome to ' + connectedRoomId, theme);\n  });\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    let notificationTimeoutId;\n    connection.on('connected', () => {\n      notificationTimeoutId = setTimeout(() => {\n        onConnected(roomId);\n      }, 2000);\n    });\n    connection.connect();\n    return () => {\n      connection.disconnect();\n      if (notificationTimeoutId !== undefined) {\n        clearTimeout(notificationTimeoutId);\n      }\n    };\n  }, [roomId]);\n\n  return <h1>Welcome to the {roomId} room!</h1>\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Use dark theme\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n        theme={isDark ? 'dark' : 'light'}\n      />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  let connectedCallback;\n  let timeout;\n  return {\n    connect() {\n      timeout = setTimeout(() => {\n        if (connectedCallback) {\n          connectedCallback();\n        }\n      }, 100);\n    },\n    on(event, callback) {\n      if (connectedCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'connected') {\n        throw Error('Only \"connected\" event is supported.');\n      }\n      connectedCallback = callback;\n    },\n    disconnect() {\n      clearTimeout(timeout);\n    }\n  };\n}\n```\n\n```js src/notifications.js hidden\nimport Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\n\nexport function showNotification(message, theme) {\n  Toastify({\n    text: message,\n    duration: 2000,\n    gravity: 'top',\n    position: 'right',\n    style: {\n      background: theme === 'dark' ? 'black' : 'white',\n      color: theme === 'dark' ? 'white' : 'black',\n    },\n  }).showToast();\n}\n```\n\n```css\nlabel { display: block; margin-top: 10px; }\n```\n\n</Sandpack>\n\nこれによりルームを変更した際に、すでにスケジュール済み（だが未表示）の通知がキャンセルされるようになります。\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/setup.md",
    "content": "---\ntitle: セットアップ\n---\n<Intro>\n\nReact はエディタ、TypeScript、ブラウザ拡張機能、コンパイラといったツールと連携します。このセクションでは環境セットアップの方法を説明します。\n\n</Intro>\n\n## エディタのセットアップ {/*editor-setup*/}\n\n[推奨エディタ](/learn/editor-setup)を確認し、React での作業に適したセットアップ方法を学びましょう。\n\n## TypeScript の使用 {/*using-typescript*/}\n\nTypeScript は JavaScript のコードベースに型定義を追加する一般的な方法です。[React プロジェクトに TypeScript を組み込む方法を学びましょう](/learn/typescript)。\n\n## React Developer Tools {/*react-developer-tools*/}\n\nReact Developer Tools は React コンポーネントの調査を行い、props や state を編集し、パフォーマンスの問題を特定できるブラウザ拡張機能です。インストール方法は[こちら](learn/react-developer-tools)で確認できます。\n\n## React Compiler {/*react-compiler*/}\n\nReact Compiler は React アプリを自動的に最適化するツールです。[詳細はこちら](/learn/react-compiler)。\n\n## 次のステップ {/*next-steps*/}\n\n[クイックスタート](/learn)ガイドに進んで、日常的に遭遇する最も重要な React の概念についてのツアーを始めましょう。"
  },
  {
    "path": "src/content/learn/sharing-state-between-components.md",
    "content": "---\ntitle: コンポーネント間で state を共有する\n---\n\n<Intro>\n\n2 つのコンポーネントの state を常に同時に変更したいという場合があります。これを行うには、両方のコンポーネントから state を削除して最も近い共通の親へ移動し、そこから state を props 経由でコンポーネントへ渡します。これは *state のリフトアップ (lifting state up)* として知られているものであり、React コードを書く際に行う最も一般的な作業のひとつです。\n\n</Intro>\n\n<YouWillLearn>\n\n- コンポーネント間で state を共有する方法\n- 制御された (controlled) コンポーネントと非制御 (uncontrolled) コンポーネントとは何か\n\n</YouWillLearn>\n\n## state のリフトアップの例 {/*lifting-state-up-by-example*/}\n\n以下の例では、親の `Accordion` コンポーネントが 2 つの別々の `Panel` をレンダーしています。\n\n* `Accordion`\n  - `Panel`\n  - `Panel`\n\n各 `Panel` コンポーネントには、内容を表示中かどうかを決定するブール型の `isActive` という state があります。\n\n両方のパネルで \"Show\" ボタンを押してみてください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nfunction Panel({ title, children }) {\n  const [isActive, setIsActive] = useState(false);\n  return (\n    <section className=\"panel\">\n      <h3>{title}</h3>\n      {isActive ? (\n        <p>{children}</p>\n      ) : (\n        <button onClick={() => setIsActive(true)}>\n          Show\n        </button>\n      )}\n    </section>\n  );\n}\n\nexport default function Accordion() {\n  return (\n    <>\n      <h2>Almaty, Kazakhstan</h2>\n      <Panel title=\"About\">\n        With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.\n      </Panel>\n      <Panel title=\"Etymology\">\n        The name comes from <span lang=\"kk-KZ\">алма</span>, the Kazakh word for \"apple\" and is often translated as \"full of apples\". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang=\"la\">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.\n      </Panel>\n    </>\n  );\n}\n```\n\n```css\nh3, p { margin: 5px 0px; }\n.panel {\n  padding: 10px;\n  border: 1px solid #aaa;\n}\n```\n\n</Sandpack>\n\n片方のパネルのボタンを押しても、もう片方のパネルには影響しません。2 つのパネルは独立していますね。\n\n<DiagramGroup>\n\n<Diagram name=\"sharing_state_child\" height={367} width={477} alt=\"Accordion というラベルが付いた親と、Panel というラベルが付いた 2 つの子からなる 3 つのコンポーネントのツリーを示す図。両方の Panel コンポーネントには、false の値を持った isActive が含まれている。\">\n\n最初は、各 `Panel` の `isActive` state は `false` なので、どちらも折りたたまれている\n\n</Diagram>\n\n<Diagram name=\"sharing_state_child_clicked\" height={367} width={480} alt=\"前の図と同様で、最初の子の Panel コンポーネントの isActive がクリックによりハイライトされ、値が true に設定されている。2 つ目の Panel コンポーネントは false のまま。\">\n\nどちらかの `Panel` のボタンをクリックすると、その `Panel` のみ `isActive` の state が更新される\n\n</Diagram>\n\n</DiagramGroup>\n\n**ですが今回はこれを変更し、一度に 1 つのパネルだけが展開されるようにしたいとしましょう**。この設計では、2 番目のパネルを展開すると 1 番目のパネルが折りたたまれます。どのようにして実現すればよいでしょうか？\n\nこれら 2 つのパネルを協調して動作させるためには、以下の 3 ステップで、親のコンポーネントに \"state をリフトアップ\" する必要があります。\n\n1. 子コンポーネントから state を**削除**する。\n2. 共通の親からハードコードされた**データを渡す**。\n3. 共通の親に state を**追加**し、イベントハンドラと一緒に下に渡す。\n\nこれにより、`Accordion` コンポーネントが両方の `Panel` の調整役となり、一度に一方だけを展開できるようになります。\n\n### ステップ 1：子コンポーネントから state を削除する {/*step-1-remove-state-from-the-child-components*/}\n\n`Panel` の `isActive` の制御権を親コンポーネントに与えることになります。つまり、親コンポーネントが `isActive` を `Panel` に props として渡すということです。まずは `Panel` コンポーネントから**以下の行を削除**してください。\n\n```js\nconst [isActive, setIsActive] = useState(false);\n```\n\n代わりに、`isActive` を `Panel` の props のリストに追加します。\n\n```js\nfunction Panel({ title, children, isActive }) {\n```\n\nこれで、`Panel` の親コンポーネントは `isActive` を [props として渡すことで](/learn/passing-props-to-a-component)*制御*できます。逆に、`Panel` コンポーネントは `isActive` の値を自身で*制御できなく*なりました。制御が親コンポーネントに移ったのです！\n\n### ステップ 2：共通の親からハードコードされたデータを渡す {/*step-2-pass-hardcoded-data-from-the-common-parent*/}\n\nstate をリフトアップするためには、協調動作させたい*すべての*子コンポーネントの、最も近い共通の親コンポーネントを特定する必要があります。\n\n* `Accordion` *（最も近い共通の親）*\n  - `Panel`\n  - `Panel`\n\nこの例では `Accordion` コンポーネントが該当します。両方のパネルの上にあり、それらの props を制御できるので、現在アクティブなパネルに関する \"信頼できる情報源 (source of truth)\" となります。`Accordion` コンポーネントからハードコードされた `isActive` の値（例えば、`true`）を両方のパネルに渡しましょう。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Accordion() {\n  return (\n    <>\n      <h2>Almaty, Kazakhstan</h2>\n      <Panel title=\"About\" isActive={true}>\n        With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.\n      </Panel>\n      <Panel title=\"Etymology\" isActive={true}>\n        The name comes from <span lang=\"kk-KZ\">алма</span>, the Kazakh word for \"apple\" and is often translated as \"full of apples\". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang=\"la\">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.\n      </Panel>\n    </>\n  );\n}\n\nfunction Panel({ title, children, isActive }) {\n  return (\n    <section className=\"panel\">\n      <h3>{title}</h3>\n      {isActive ? (\n        <p>{children}</p>\n      ) : (\n        <button onClick={() => setIsActive(true)}>\n          Show\n        </button>\n      )}\n    </section>\n  );\n}\n```\n\n```css\nh3, p { margin: 5px 0px; }\n.panel {\n  padding: 10px;\n  border: 1px solid #aaa;\n}\n```\n\n</Sandpack>\n\n`Accordion` コンポーネントにハードコードされた `isActive` の値を編集してみて、画面上で起きる結果を確認してください。\n\n### ステップ 3：共通の親に state を追加する {/*step-3-add-state-to-the-common-parent*/}\n\nstate をリフトアップすることで、state として格納するデータの意味が変わることがあります。\n\n今回の場合、一度に 1 つのパネルだけがアクティブであるべきです。つまり、共通の親コンポーネントである `Accordion` は、どのパネルがアクティブなのかを管理する必要があります。state 変数としては、`boolean` 値の代わりに、アクティブな `Panel` のインデックスを表す数値を使うことができます。\n\n```js\nconst [activeIndex, setActiveIndex] = useState(0);\n```\n\n`activeIndex` が `0` のときは 1 番目のパネルが、`1` のときは 2 番目のパネルがアクティブになります。\n\nどちらの `Panel` の \"Show\" ボタンがクリックされた場合でも、`Accordion` のアクティブインデックスを変更する必要があります。`activeIndex` という state は `Accordion` 内に定義されるものであるため、`Panel` からそれを直接セットすることはできません。`Accordion` コンポーネントは、[props として `onShow` イベントハンドラを下に渡すこと](/learn/responding-to-events#passing-event-handlers-as-props)で、`Panel` コンポーネントがアコーディオンの state を変更できるように*明示的に許可*する必要があります：\n\n```js\n<>\n  <Panel\n    isActive={activeIndex === 0}\n    onShow={() => setActiveIndex(0)}\n  >\n    ...\n  </Panel>\n  <Panel\n    isActive={activeIndex === 1}\n    onShow={() => setActiveIndex(1)}\n  >\n    ...\n  </Panel>\n</>\n```\n\nそして `Panel` 内の `<button>` は、クリックイベントハンドラとして props である `onShow` を使用します。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Accordion() {\n  const [activeIndex, setActiveIndex] = useState(0);\n  return (\n    <>\n      <h2>Almaty, Kazakhstan</h2>\n      <Panel\n        title=\"About\"\n        isActive={activeIndex === 0}\n        onShow={() => setActiveIndex(0)}\n      >\n        With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.\n      </Panel>\n      <Panel\n        title=\"Etymology\"\n        isActive={activeIndex === 1}\n        onShow={() => setActiveIndex(1)}\n      >\n        The name comes from <span lang=\"kk-KZ\">алма</span>, the Kazakh word for \"apple\" and is often translated as \"full of apples\". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang=\"la\">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.\n      </Panel>\n    </>\n  );\n}\n\nfunction Panel({\n  title,\n  children,\n  isActive,\n  onShow\n}) {\n  return (\n    <section className=\"panel\">\n      <h3>{title}</h3>\n      {isActive ? (\n        <p>{children}</p>\n      ) : (\n        <button onClick={onShow}>\n          Show\n        </button>\n      )}\n    </section>\n  );\n}\n```\n\n```css\nh3, p { margin: 5px 0px; }\n.panel {\n  padding: 10px;\n  border: 1px solid #aaa;\n}\n```\n\n</Sandpack>\n\nこれで state のリフトアップが完了です！ state を共通の親コンポーネントに移動させることで、2 つのパネルを協調動作させられるようになりました。「表示中」フラグを 2 つ使う代わりにアクティブインデックスを使用することで、一度にアクティブなパネルが 1 つだけであることが保証されました。また、イベントハンドラを子に渡すことで、子に親の state を変更させることができました。\n\n<DiagramGroup>\n\n<Diagram name=\"sharing_state_parent\" height={385} width={487} alt=\"Accordion というラベルの親コンポーネントと、Panel というラベルの 2 つの子コンポーネントからなる 3 つのコンポーネントのツリーを示す図。最初 Accordion の activeIndex が 0 なので、最初の Panel が isActive = true を受け取る。\" >\n\n最初、`Accordion` の `activeIndex` は `0` なので、最初の `Panel` が `isActive = true` を受け取る。\n\n</Diagram>\n\n<Diagram name=\"sharing_state_parent_clicked\" height={385} width={521} alt=\"前と同じ図だが、クリックによりハイライトされた親の Accordion コンポーネントの activeIndex が 1 に変わっている。両方の子の Panel コンポーネントもハイライトされており、isActive の値が逆転して渡されている。最初の Panel には false、2 番目の Panel には true。\">\n\n`Accordion` の `activeIndex` state が `1` に変更されると、2 番目の `Panel` が `isActive = true` を受け取る。\n\n</Diagram>\n\n</DiagramGroup>\n\n<DeepDive>\n\n#### 制御されたコンポーネントと非制御コンポーネント {/*controlled-and-uncontrolled-components*/}\n\n一般的に、ローカル state を持つコンポーネントを \"非制御 (uncontrolled)\" であると呼びます。例えば、`isActive` という state 変数を持つ元の `Panel` コンポーネントは、パネルがアクティブかどうかに関して親が影響を与えることができないため、非制御コンポーネントです。\n\n対照的に、重要な情報がローカル state ではなく props によって駆動されるとき、コンポーネントは \"制御された (controlled)\" ものと呼ばれることがあります。これにより、親コンポーネントがその振る舞いを完全に指定することができます。`isActive` を props として持つ最終的な `Panel` コンポーネントは、`Accordion` コンポーネントによって制御されていることになります。\n\n非制御コンポーネントは、設定が少なくて済むので親コンポーネントの中に入れて使用することが簡単にできます。しかし、それらを協調動作させたい場合に柔軟性がありません。制御されたコンポーネントはとても柔軟ですが、親コンポーネントが props で完全に設定してあげる必要があります。\n\n実際には、\"制御された\"、\"非制御\" は技術用語として厳密なものではありません。各コンポーネントは通常、ローカルな state と props の両方を、混在して持つものです。しかし、コンポーネントがどう設計されるか、どんな機能を持つかについて話す際には、このような考え方が役に立つでしょう。\n\nコンポーネントを書くときには、その中のどの情報を（props で）制御し、どの情報を（state を使うことで）制御しないのかを検討してください。しかし後で考えを変えてリファクタリングすることはいつでも可能です。\n\n</DeepDive>\n\n## 各 state の信頼できる唯一の情報源 {/*a-single-source-of-truth-for-each-state*/}\n\nReact アプリケーションでは、多くのコンポーネントが自身の state を保持します。一部の state は、入力フィールドのような末梢コンポーネント（ツリーの最下部のコンポーネント）に近いところに存在します。一部の state はアプリの上部に近いところに存在することでしょう。例えば、クライアントサイドルーティングライブラリも、React の state に現在のルートを格納し、props を介して下に渡すことで実装されることが一般的です。\n\n**それぞれの state について、それを「所有」するコンポーネントを選択してください**。この原則は、[\"信頼できる唯一の情報源 (single source of truth)\"](https://en.wikipedia.org/wiki/Single_source_of_truth) としても知られています。これは、すべての state が一箇所にまとまっているという意味ではありません。*それぞれの* state について、その情報を保持する*特定の*コンポーネントが存在すべきという意味です。コンポーネント間で共有される state は複製する代わりに、共通の親に*リフトアップ*して、それを必要とする子に*渡す*ようにしてください。\n\nあなたのアプリは作業を進めるうちに変化していきます。まだそれぞれの state がどこに存在すべきか分からない間は、state を下に移動させたり、上に戻したりすることがよくあります。これは開発プロセスの一環です！\n\nもう少し多くのコンポーネントが登場する例で実践的に感覚を理解したい場合は、[React の流儀](/learn/thinking-in-react)を読んでみましょう。\n\n<Recap>\n\n* 2 つのコンポーネントを協調動作させたい場合は、state を共通の親に移動する。\n* 次に、その共通の親から props 経由で情報を下に渡す。\n* 最後に、子が親の state を変更できるよう、イベントハンドラを下に渡す。\n* コンポーネントを「制御された」（props によって駆動される）か「非制御」（state によって駆動される）かという観点で考えることが有用である。\n\n</Recap>\n\n<Challenges>\n\n#### 入力欄の同期 {/*synced-inputs*/}\n\n以下の 2 つの入力欄は独立しています。同期して動作するようにしましょう。片方の入力欄を編集すると、他方の入力欄も同じテキストに更新されるようにしてください。\n\n<Hint>\n\n親コンポーネントに state をリフトアップする必要があります。\n\n</Hint>\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function SyncedInputs() {\n  return (\n    <>\n      <Input label=\"First input\" />\n      <Input label=\"Second input\" />\n    </>\n  );\n}\n\nfunction Input({ label }) {\n  const [text, setText] = useState('');\n\n  function handleChange(e) {\n    setText(e.target.value);\n  }\n\n  return (\n    <label>\n      {label}\n      {' '}\n      <input\n        value={text}\n        onChange={handleChange}\n      />\n    </label>\n  );\n}\n```\n\n```css\ninput { margin: 5px; }\nlabel { display: block; }\n```\n\n</Sandpack>\n\n<Solution>\n\n`text` という state 変数と `handleChange` ハンドラを親コンポーネントに移動させます。次に、それらを両方の `Input` コンポーネントに props 経由で渡します。これにより、2 つの入力欄の同期が保たれます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function SyncedInputs() {\n  const [text, setText] = useState('');\n\n  function handleChange(e) {\n    setText(e.target.value);\n  }\n\n  return (\n    <>\n      <Input\n        label=\"First input\"\n        value={text}\n        onChange={handleChange}\n      />\n      <Input\n        label=\"Second input\"\n        value={text}\n        onChange={handleChange}\n      />\n    </>\n  );\n}\n\nfunction Input({ label, value, onChange }) {\n  return (\n    <label>\n      {label}\n      {' '}\n      <input\n        value={value}\n        onChange={onChange}\n      />\n    </label>\n  );\n}\n```\n\n```css\ninput { margin: 5px; }\nlabel { display: block; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### リストのフィルタリング {/*filtering-a-list*/}\n\n以下の例では、`SearchBar` は、テキスト入力を制御する `query` という独自の state を保持しています。親である `FilterableList` コンポーネントは項目の `List` を表示していますが、検索クエリが考慮されていません。\n\n`filterItems(foods, query)` 関数を使って、検索クエリに従ってリストの絞り込みを行ってください。変更をテストするには、入力欄に \"s\" と入力すると、\"Sushi\"、\"Shish kebab\"、\"Dim sum\" のようにリストが絞り込まれることを確認します。\n\n`filterItems` は既に実装されておりインポートされているので、自分で書く必要はありません！\n\n<Hint>\n\n`query` の state と `handleChange` ハンドラを `SearchBar` から削除し、`FilterableList` に移動します。その後、`query` および `onChange` を props として `SearchBar` に渡します。\n\n</Hint>\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { foods, filterItems } from './data.js';\n\nexport default function FilterableList() {\n  return (\n    <>\n      <SearchBar />\n      <hr />\n      <List items={foods} />\n    </>\n  );\n}\n\nfunction SearchBar() {\n  const [query, setQuery] = useState('');\n\n  function handleChange(e) {\n    setQuery(e.target.value);\n  }\n\n  return (\n    <label>\n      Search:{' '}\n      <input\n        value={query}\n        onChange={handleChange}\n      />\n    </label>\n  );\n}\n\nfunction List({ items }) {\n  return (\n    <table>\n      <tbody>\n        {items.map(food => (\n          <tr key={food.id}>\n            <td>{food.name}</td>\n            <td>{food.description}</td>\n          </tr>\n        ))}\n      </tbody>\n    </table>\n  );\n}\n```\n\n```js src/data.js\nexport function filterItems(items, query) {\n  query = query.toLowerCase();\n  return items.filter(item =>\n    item.name.split(' ').some(word =>\n      word.toLowerCase().startsWith(query)\n    )\n  );\n}\n\nexport const foods = [{\n  id: 0,\n  name: 'Sushi',\n  description: 'Sushi is a traditional Japanese dish of prepared vinegared rice'\n}, {\n  id: 1,\n  name: 'Dal',\n  description: 'The most common way of preparing dal is in the form of a soup to which onions, tomatoes and various spices may be added'\n}, {\n  id: 2,\n  name: 'Pierogi',\n  description: 'Pierogi are filled dumplings made by wrapping unleavened dough around a savoury or sweet filling and cooking in boiling water'\n}, {\n  id: 3,\n  name: 'Shish kebab',\n  description: 'Shish kebab is a popular meal of skewered and grilled cubes of meat.'\n}, {\n  id: 4,\n  name: 'Dim sum',\n  description: 'Dim sum is a large range of small dishes that Cantonese people traditionally enjoy in restaurants for breakfast and lunch'\n}];\n```\n\n</Sandpack>\n\n<Solution>\n\n`query` の state を `FilterableList` コンポーネントにリフトアップします。フィルタリング済のリストを取得するために、`filterItems(foods, query)` を呼び出し、それを `List` に渡します。これで、query の入力を変更するとリストに反映されるようになります。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { foods, filterItems } from './data.js';\n\nexport default function FilterableList() {\n  const [query, setQuery] = useState('');\n  const results = filterItems(foods, query);\n\n  function handleChange(e) {\n    setQuery(e.target.value);\n  }\n\n  return (\n    <>\n      <SearchBar\n        query={query}\n        onChange={handleChange}\n      />\n      <hr />\n      <List items={results} />\n    </>\n  );\n}\n\nfunction SearchBar({ query, onChange }) {\n  return (\n    <label>\n      Search:{' '}\n      <input\n        value={query}\n        onChange={onChange}\n      />\n    </label>\n  );\n}\n\nfunction List({ items }) {\n  return (\n    <table>\n      <tbody> \n        {items.map(food => (\n          <tr key={food.id}>\n            <td>{food.name}</td>\n            <td>{food.description}</td>\n          </tr>\n        ))}\n      </tbody>\n    </table>\n  );\n}\n```\n\n```js src/data.js\nexport function filterItems(items, query) {\n  query = query.toLowerCase();\n  return items.filter(item =>\n    item.name.split(' ').some(word =>\n      word.toLowerCase().startsWith(query)\n    )\n  );\n}\n\nexport const foods = [{\n  id: 0,\n  name: 'Sushi',\n  description: 'Sushi is a traditional Japanese dish of prepared vinegared rice'\n}, {\n  id: 1,\n  name: 'Dal',\n  description: 'The most common way of preparing dal is in the form of a soup to which onions, tomatoes and various spices may be added'\n}, {\n  id: 2,\n  name: 'Pierogi',\n  description: 'Pierogi are filled dumplings made by wrapping unleavened dough around a savoury or sweet filling and cooking in boiling water'\n}, {\n  id: 3,\n  name: 'Shish kebab',\n  description: 'Shish kebab is a popular meal of skewered and grilled cubes of meat.'\n}, {\n  id: 4,\n  name: 'Dim sum',\n  description: 'Dim sum is a large range of small dishes that Cantonese people traditionally enjoy in restaurants for breakfast and lunch'\n}];\n```\n\n</Sandpack>\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/state-a-components-memory.md",
    "content": "---\ntitle: \"state：コンポーネントのメモリ\"\n---\n\n<Intro>\n\nコンポーネントによっては、ユーザ操作の結果として画面上の表示内容を変更する必要があります。フォーム上でタイプすると入力欄が更新される、画像カルーセルで「次」をクリックすると表示される画像が変わる、「購入」をクリックすると買い物かごに商品が入る、といったものです。コンポーネントは、現在の入力値、現在の画像、ショッピングカートの状態といったものを「覚えておく」必要があります。React では、このようなコンポーネント固有のメモリのことを *state* と呼びます。\n\n</Intro>\n\n<YouWillLearn>\n\n* [`useState`](/reference/react/useState) を使って state 変数を追加する方法\n* `useState` フックが返す 2 つの値\n* 複数の state 変数を追加する方法\n* state がローカルと呼ばれる理由\n\n</YouWillLearn>\n\n## 通常の変数ではうまくいかない例 {/*when-a-regular-variable-isnt-enough*/}\n\n以下は、彫刻の画像をレンダーするコンポーネントです。\"Next\" ボタンをクリックすると、`index` が `1`、`2` のように変わりながら次の彫刻が表示されて欲しいのですが、これは**正しく動作しません**（試してみてください）。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [7]}}\nimport { sculptureList } from './data.js';\n\nexport default function Gallery() {\n  let index = 0;\n\n  function handleClick() {\n    index = index + 1;\n  }\n\n  let sculpture = sculptureList[index];\n  return (\n    <>\n      <button onClick={handleClick}>\n        Next\n      </button>\n      <h2>\n        <i>{sculpture.name} </i> \n        by {sculpture.artist}\n      </h2>\n      <h3>  \n        ({index + 1} of {sculptureList.length})\n      </h3>\n      <img \n        src={sculpture.url} \n        alt={sculpture.alt}\n      />\n      <p>\n        {sculpture.description}\n      </p>\n    </>\n  );\n}\n```\n\n```js src/data.js\nexport const sculptureList = [{\n  name: 'Homenaje a la Neurocirugía',\n  artist: 'Marta Colvin Andrade',\n  description: 'Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.',\n  url: 'https://i.imgur.com/Mx7dA2Y.jpg',\n  alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.'  \n}, {\n  name: 'Floralis Genérica',\n  artist: 'Eduardo Catalano',\n  description: 'This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.',\n  url: 'https://i.imgur.com/ZF6s192m.jpg',\n  alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.'\n}, {\n  name: 'Eternal Presence',\n  artist: 'John Woodrow Wilson',\n  description: 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n  url: 'https://i.imgur.com/aTtVpES.jpg',\n  alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.'\n}, {\n  name: 'Moai',\n  artist: 'Unknown Artist',\n  description: 'Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.',\n  url: 'https://i.imgur.com/RCwLEoQm.jpg',\n  alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.'\n}, {\n  name: 'Blue Nana',\n  artist: 'Niki de Saint Phalle',\n  description: 'The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.',\n  url: 'https://i.imgur.com/Sd1AgUOm.jpg',\n  alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.'\n}, {\n  name: 'Ultimate Form',\n  artist: 'Barbara Hepworth',\n  description: 'This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.',\n  url: 'https://i.imgur.com/2heNQDcm.jpg',\n  alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.'\n}, {\n  name: 'Cavaliere',\n  artist: 'Lamidi Olonade Fakeye',\n  description: \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n  url: 'https://i.imgur.com/wIdGuZwm.png',\n  alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.'\n}, {\n  name: 'Big Bellies',\n  artist: 'Alina Szapocznikow',\n  description: \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n  url: 'https://i.imgur.com/AlHTAdDm.jpg',\n  alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.'\n}, {\n  name: 'Terracotta Army',\n  artist: 'Unknown Artist',\n  description: 'The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.',\n  url: 'https://i.imgur.com/HMFmH6m.jpg',\n  alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.'\n}, {\n  name: 'Lunar Landscape',\n  artist: 'Louise Nevelson',\n  description: 'Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism’s geometric abstraction of space and form.',\n  url: 'https://i.imgur.com/rN7hY6om.jpg',\n  alt: 'A black matte sculpture where the individual elements are initially indistinguishable.'\n}, {\n  name: 'Aureole',\n  artist: 'Ranjani Shettar',\n  description: 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n  url: 'https://i.imgur.com/okTpbHhm.jpg',\n  alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.'\n}, {\n  name: 'Hippos',\n  artist: 'Taipei Zoo',\n  description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.',\n  url: 'https://i.imgur.com/6o5Vuyu.jpg',\n  alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.'\n}];\n```\n\n```css\nh2 { margin-top: 10px; margin-bottom: 0; }\nh3 {\n  margin-top: 5px;\n  font-weight: normal;\n  font-size: 100%;\n}\nimg { width: 120px; height: 120px; }\nbutton {\n  display: block;\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n```\n\n</Sandpack>\n\n`handleClick` イベントハンドラは、ローカル変数 `index` を更新しています。しかし、以下の 2 つの理由により、目に見える変化が起きません。\n\n1. **ローカル変数はレンダー間で保持されません**。React がこのコンポーネントを次にレンダーするときは、まっさらな状態からレンダーします。過去にローカル変数を変更したことは考慮されません。\n2. **ローカル変数の変更は、レンダーをトリガしません**。新しいデータでコンポーネントを再度レンダーする必要があることに React は気づきません。\n\nコンポーネントを新しいデータで更新するためには、次の 2 つのことが必要です。\n\n1. レンダー間でデータを**保持**する。\n2. 新しいデータでコンポーネントをレンダー（つまり再レンダー）するよう React に**伝える**。\n\n[`useState`](/reference/react/useState) フックは、これら 2 つの機能を提供します。\n\n1. レンダー間でデータを保持する **state 変数**。\n2. 変数を更新し、React がコンポーネントを再度レンダーするようにトリガする **state  セッタ関数**。\n\n## state 変数の追加 {/*adding-a-state-variable*/}\n\nstate 変数を追加するには、ファイルの先頭で React から `useState` をインポートします：\n\n```js\nimport { useState } from 'react';\n```\n\n次に、この行を：\n\n```js\nlet index = 0;\n```\n\n以下のように置き換えます：\n\n```js\nconst [index, setIndex] = useState(0);\n```\n\n`index` は state 変数であり、`setIndex` はセッタ関数です。\n\n> ここでの `[` と `]` という構文は[配列の分割代入](https://javascript.info/destructuring-assignment)と呼ばれるもので、配列から個々の値を読み取ることができます。`useState` から返される配列は常に正確に 2 個の要素を持っています。\n\nこれらは `handleClick` の中で以下のように動作します：\n\n```js\nfunction handleClick() {\n  setIndex(index + 1);\n}\n```\n\nこれで、\"Next\" ボタンをクリックすると、現在の彫刻が切り替わるようになります：\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { sculptureList } from './data.js';\n\nexport default function Gallery() {\n  const [index, setIndex] = useState(0);\n\n  function handleClick() {\n    setIndex(index + 1);\n  }\n\n  let sculpture = sculptureList[index];\n  return (\n    <>\n      <button onClick={handleClick}>\n        Next\n      </button>\n      <h2>\n        <i>{sculpture.name} </i> \n        by {sculpture.artist}\n      </h2>\n      <h3>  \n        ({index + 1} of {sculptureList.length})\n      </h3>\n      <img \n        src={sculpture.url} \n        alt={sculpture.alt}\n      />\n      <p>\n        {sculpture.description}\n      </p>\n    </>\n  );\n}\n```\n\n```js src/data.js\nexport const sculptureList = [{\n  name: 'Homenaje a la Neurocirugía',\n  artist: 'Marta Colvin Andrade',\n  description: 'Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.',\n  url: 'https://i.imgur.com/Mx7dA2Y.jpg',\n  alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.'  \n}, {\n  name: 'Floralis Genérica',\n  artist: 'Eduardo Catalano',\n  description: 'This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.',\n  url: 'https://i.imgur.com/ZF6s192m.jpg',\n  alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.'\n}, {\n  name: 'Eternal Presence',\n  artist: 'John Woodrow Wilson',\n  description: 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n  url: 'https://i.imgur.com/aTtVpES.jpg',\n  alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.'\n}, {\n  name: 'Moai',\n  artist: 'Unknown Artist',\n  description: 'Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.',\n  url: 'https://i.imgur.com/RCwLEoQm.jpg',\n  alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.'\n}, {\n  name: 'Blue Nana',\n  artist: 'Niki de Saint Phalle',\n  description: 'The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.',\n  url: 'https://i.imgur.com/Sd1AgUOm.jpg',\n  alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.'\n}, {\n  name: 'Ultimate Form',\n  artist: 'Barbara Hepworth',\n  description: 'This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.',\n  url: 'https://i.imgur.com/2heNQDcm.jpg',\n  alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.'\n}, {\n  name: 'Cavaliere',\n  artist: 'Lamidi Olonade Fakeye',\n  description: \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n  url: 'https://i.imgur.com/wIdGuZwm.png',\n  alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.'\n}, {\n  name: 'Big Bellies',\n  artist: 'Alina Szapocznikow',\n  description: \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n  url: 'https://i.imgur.com/AlHTAdDm.jpg',\n  alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.'\n}, {\n  name: 'Terracotta Army',\n  artist: 'Unknown Artist',\n  description: 'The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.',\n  url: 'https://i.imgur.com/HMFmH6m.jpg',\n  alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.'\n}, {\n  name: 'Lunar Landscape',\n  artist: 'Louise Nevelson',\n  description: 'Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism’s geometric abstraction of space and form.',\n  url: 'https://i.imgur.com/rN7hY6om.jpg',\n  alt: 'A black matte sculpture where the individual elements are initially indistinguishable.'\n}, {\n  name: 'Aureole',\n  artist: 'Ranjani Shettar',\n  description: 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n  url: 'https://i.imgur.com/okTpbHhm.jpg',\n  alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.'\n}, {\n  name: 'Hippos',\n  artist: 'Taipei Zoo',\n  description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.',\n  url: 'https://i.imgur.com/6o5Vuyu.jpg',\n  alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.'\n}];\n```\n\n```css\nh2 { margin-top: 10px; margin-bottom: 0; }\nh3 {\n margin-top: 5px;\n font-weight: normal;\n font-size: 100%;\n}\nimg { width: 120px; height: 120px; }\nbutton {\n  display: block;\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n```\n\n</Sandpack>\n\n### はじめてのフック {/*meet-your-first-hook*/}\n\nReact では、`useState` やその他の `use` で始まる関数はフック (Hook) と呼ばれます。\n\n*フック*は、React が[レンダー](/learn/render-and-commit#step-1-trigger-a-render)されている間のみ利用可能な特別な関数です（この点については、次ページで詳しく説明します）。フックを使うことで、さまざまな React の機能に「接続 (hook into)」して使用することができます。\n\nstate はそれらの機能のうちのひとつですが、他のフックについても後で紹介します。\n\n<Pitfall>\n\n**`use` で始まるフックは、コンポーネントのトップレベルまたは[カスタムフック](/learn/reusing-logic-with-custom-hooks)内でのみ呼び出すことができます**。条件分岐、ループ、ネストされた関数の中でフックを呼び出すことはできません。フックは関数ですが、コンポーネントの要求に関する無条件の宣言を行うものだ、と捉えることが有用です。ファイルの先頭でモジュールを \"import\" するのと同様に、コンポーネントの先頭で React の機能を \"use\" するのです。\n\n</Pitfall>\n\n### `useState` の構造 {/*anatomy-of-usestate*/}\n\n[`useState`](/reference/react/useState) を呼び出すということは、このコンポーネントに何かを覚えさせるよう React に指示を出すということです：\n\n```js\nconst [index, setIndex] = useState(0);\n```\n\nこの場合、React には `index` を覚えてもらいます。\n\n<Note>\n\n慣習的に、このペアは `const [something, setSomething]` のように命名します。自由に名前を付けることもできますが、慣習に従うことでプロジェクト間で理解しやすくなります。\n\n</Note>\n\n`useState` に渡す唯一の引数は、state 変数の**初期値**です。この例では `useState(0)` とすることで `index` の初期値を `0` に設定しています。\n\nコンポーネントがレンダーされるたびに、`useState` は以下の 2 つの値を含む配列を返します。\n\n1. 保存した値を保持している **state 変数** (`index`)。\n2. state 変数を更新し、React にコンポーネントの再レンダーをトリガする **state セッタ関数** (`setIndex`)。\n\n以下は、これが実際にどのように動作するかを示しています。\n\n```js\nconst [index, setIndex] = useState(0);\n```\n\n1. **コンポーネントが初めてレンダーされる**。`useState` に `index` の初期値として `0` を渡したので、`[0, setIndex]` を返す。React は `0` が最新の state 値であることを覚える。\n2. **state を更新する**。ユーザがボタンをクリックすると、`setIndex(index + 1)` が呼び出される。現在 `index` は `0` なので、`setIndex(1)` になる。これにより、React は `index` が `1` になったことを覚え、再レンダーがトリガされる。\n3. **コンポーネントの 2 回目のレンダー**。React は再び `useState(0)` というコードに出会うが、React は `index` を `1` にセットしたことを*覚えている*ので、代わりに `[1, setIndex]` を返す。\n4. 以降も続く。\n\n## コンポーネントで複数の state 変数を使う {/*giving-a-component-multiple-state-variables*/}\n\n1 つのコンポーネントは、いくつでも好きな型の state 変数を持つことができます。このコンポーネントは、数値型の `index` と、\"Show details\" をクリックすると切り替わるブーリアン型の `showMore` という、2 つの state 変数を持っています。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { sculptureList } from './data.js';\n\nexport default function Gallery() {\n  const [index, setIndex] = useState(0);\n  const [showMore, setShowMore] = useState(false);\n\n  function handleNextClick() {\n    setIndex(index + 1);\n  }\n\n  function handleMoreClick() {\n    setShowMore(!showMore);\n  }\n\n  let sculpture = sculptureList[index];\n  return (\n    <>\n      <button onClick={handleNextClick}>\n        Next\n      </button>\n      <h2>\n        <i>{sculpture.name} </i> \n        by {sculpture.artist}\n      </h2>\n      <h3>  \n        ({index + 1} of {sculptureList.length})\n      </h3>\n      <button onClick={handleMoreClick}>\n        {showMore ? 'Hide' : 'Show'} details\n      </button>\n      {showMore && <p>{sculpture.description}</p>}\n      <img \n        src={sculpture.url} \n        alt={sculpture.alt}\n      />\n    </>\n  );\n}\n```\n\n```js src/data.js\nexport const sculptureList = [{\n  name: 'Homenaje a la Neurocirugía',\n  artist: 'Marta Colvin Andrade',\n  description: 'Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.',\n  url: 'https://i.imgur.com/Mx7dA2Y.jpg',\n  alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.'  \n}, {\n  name: 'Floralis Genérica',\n  artist: 'Eduardo Catalano',\n  description: 'This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.',\n  url: 'https://i.imgur.com/ZF6s192m.jpg',\n  alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.'\n}, {\n  name: 'Eternal Presence',\n  artist: 'John Woodrow Wilson',\n  description: 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n  url: 'https://i.imgur.com/aTtVpES.jpg',\n  alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.'\n}, {\n  name: 'Moai',\n  artist: 'Unknown Artist',\n  description: 'Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.',\n  url: 'https://i.imgur.com/RCwLEoQm.jpg',\n  alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.'\n}, {\n  name: 'Blue Nana',\n  artist: 'Niki de Saint Phalle',\n  description: 'The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.',\n  url: 'https://i.imgur.com/Sd1AgUOm.jpg',\n  alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.'\n}, {\n  name: 'Ultimate Form',\n  artist: 'Barbara Hepworth',\n  description: 'This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.',\n  url: 'https://i.imgur.com/2heNQDcm.jpg',\n  alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.'\n}, {\n  name: 'Cavaliere',\n  artist: 'Lamidi Olonade Fakeye',\n  description: \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n  url: 'https://i.imgur.com/wIdGuZwm.png',\n  alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.'\n}, {\n  name: 'Big Bellies',\n  artist: 'Alina Szapocznikow',\n  description: \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n  url: 'https://i.imgur.com/AlHTAdDm.jpg',\n  alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.'\n}, {\n  name: 'Terracotta Army',\n  artist: 'Unknown Artist',\n  description: 'The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.',\n  url: 'https://i.imgur.com/HMFmH6m.jpg',\n  alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.'\n}, {\n  name: 'Lunar Landscape',\n  artist: 'Louise Nevelson',\n  description: 'Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism’s geometric abstraction of space and form.',\n  url: 'https://i.imgur.com/rN7hY6om.jpg',\n  alt: 'A black matte sculpture where the individual elements are initially indistinguishable.'\n}, {\n  name: 'Aureole',\n  artist: 'Ranjani Shettar',\n  description: 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n  url: 'https://i.imgur.com/okTpbHhm.jpg',\n  alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.'\n}, {\n  name: 'Hippos',\n  artist: 'Taipei Zoo',\n  description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.',\n  url: 'https://i.imgur.com/6o5Vuyu.jpg',\n  alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.'\n}];\n```\n\n```css\nh2 { margin-top: 10px; margin-bottom: 0; }\nh3 {\n margin-top: 5px;\n font-weight: normal;\n font-size: 100%;\n}\nimg { width: 120px; height: 120px; }\nbutton {\n  display: block;\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n```\n\n</Sandpack>\n\nこの例の `index` と `showMore` のように state が互いに関連していない場合、複数の state 変数を持つのが良いでしょう。ただし、2 つの state 変数を一緒に更新することが多い場合は、それらを 1 つにまとめる方が簡単かもしれません。たとえば、多くのフィールドがあるフォームの場合、フィールドごとに state 変数を持つよりも、オブジェクトを保持する 1 つの state 変数を持つ方が便利です。詳しくは [state 構造の選択](/learn/choosing-the-state-structure)を参照してください。\n\n<DeepDive>\n\n#### React はどの state を返すかをどのようにして知るのか？ {/*how-does-react-know-which-state-to-return*/}\n\n`useState` の呼び出しには、どの state 変数を参照しているかに関する情報が含まれていないことに気付いたかもしれません。`useState` に「識別子」のようなものを渡さないのに、どの state 変数が返されるべきなのか、どのようにしてわかるのでしょうか。あなたの関数を解析するといった魔術的なものに頼っているのでしょうか？ 答えはノーです。\n\nそうではなく、簡潔な構文を実現するため、フックは**同一コンポーネントの各レンダー間で同一の順番で呼び出されることに依存しています**。上記のルール（「フックはトップレベルでのみ呼び出す」）に従っていれば、フックは常に同じ順序で呼び出されるので、これは実用上うまく機能します。また、[リンタプラグイン](https://www.npmjs.com/package/eslint-plugin-react-hooks)がほとんどの間違いをキャッチします。\n\n内部的には、React はすべてのコンポーネントに対して state のペアの配列を保持しています。また、現在のペアインデックスも管理しており、レンダー前に `0` に設定されます。`useState` が呼び出されるたびに、React は次の状態ペアを提供し、インデックスをインクリメントします。このメカニズムについては、[React Hooks: Not Magic, Just Arrays](https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e) で詳しく説明されています。\n\n以下の例は **React を使っていませんが**、`useState` が内部的にどのように機能するかの考え方がわかります。\n\n<Sandpack>\n\n```js src/index.js active\nlet componentHooks = [];\nlet currentHookIndex = 0;\n\n// How useState works inside React (simplified).\nfunction useState(initialState) {\n  let pair = componentHooks[currentHookIndex];\n  if (pair) {\n    // This is not the first render,\n    // so the state pair already exists.\n    // Return it and prepare for next Hook call.\n    currentHookIndex++;\n    return pair;\n  }\n\n  // This is the first time we're rendering,\n  // so create a state pair and store it.\n  pair = [initialState, setState];\n\n  function setState(nextState) {\n    // When the user requests a state change,\n    // put the new value into the pair.\n    pair[0] = nextState;\n    updateDOM();\n  }\n\n  // Store the pair for future renders\n  // and prepare for the next Hook call.\n  componentHooks[currentHookIndex] = pair;\n  currentHookIndex++;\n  return pair;\n}\n\nfunction Gallery() {\n  // Each useState() call will get the next pair.\n  const [index, setIndex] = useState(0);\n  const [showMore, setShowMore] = useState(false);\n\n  function handleNextClick() {\n    setIndex(index + 1);\n  }\n\n  function handleMoreClick() {\n    setShowMore(!showMore);\n  }\n\n  let sculpture = sculptureList[index];\n  // This example doesn't use React, so\n  // return an output object instead of JSX.\n  return {\n    onNextClick: handleNextClick,\n    onMoreClick: handleMoreClick,\n    header: `${sculpture.name} by ${sculpture.artist}`,\n    counter: `${index + 1} of ${sculptureList.length}`,\n    more: `${showMore ? 'Hide' : 'Show'} details`,\n    description: showMore ? sculpture.description : null,\n    imageSrc: sculpture.url,\n    imageAlt: sculpture.alt\n  };\n}\n\nfunction updateDOM() {\n  // Reset the current Hook index\n  // before rendering the component.\n  currentHookIndex = 0;\n  let output = Gallery();\n\n  // Update the DOM to match the output.\n  // This is the part React does for you.\n  nextButton.onclick = output.onNextClick;\n  header.textContent = output.header;\n  moreButton.onclick = output.onMoreClick;\n  moreButton.textContent = output.more;\n  image.src = output.imageSrc;\n  image.alt = output.imageAlt;\n  if (output.description !== null) {\n    description.textContent = output.description;\n    description.style.display = '';\n  } else {\n    description.style.display = 'none';\n  }\n}\n\nlet nextButton = document.getElementById('nextButton');\nlet header = document.getElementById('header');\nlet moreButton = document.getElementById('moreButton');\nlet description = document.getElementById('description');\nlet image = document.getElementById('image');\nlet sculptureList = [{\n  name: 'Homenaje a la Neurocirugía',\n  artist: 'Marta Colvin Andrade',\n  description: 'Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.',\n  url: 'https://i.imgur.com/Mx7dA2Y.jpg',\n  alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.'  \n}, {\n  name: 'Floralis Genérica',\n  artist: 'Eduardo Catalano',\n  description: 'This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.',\n  url: 'https://i.imgur.com/ZF6s192m.jpg',\n  alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.'\n}, {\n  name: 'Eternal Presence',\n  artist: 'John Woodrow Wilson',\n  description: 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n  url: 'https://i.imgur.com/aTtVpES.jpg',\n  alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.'\n}, {\n  name: 'Moai',\n  artist: 'Unknown Artist',\n  description: 'Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.',\n  url: 'https://i.imgur.com/RCwLEoQm.jpg',\n  alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.'\n}, {\n  name: 'Blue Nana',\n  artist: 'Niki de Saint Phalle',\n  description: 'The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.',\n  url: 'https://i.imgur.com/Sd1AgUOm.jpg',\n  alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.'\n}, {\n  name: 'Ultimate Form',\n  artist: 'Barbara Hepworth',\n  description: 'This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.',\n  url: 'https://i.imgur.com/2heNQDcm.jpg',\n  alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.'\n}, {\n  name: 'Cavaliere',\n  artist: 'Lamidi Olonade Fakeye',\n  description: \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n  url: 'https://i.imgur.com/wIdGuZwm.png',\n  alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.'\n}, {\n  name: 'Big Bellies',\n  artist: 'Alina Szapocznikow',\n  description: \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n  url: 'https://i.imgur.com/AlHTAdDm.jpg',\n  alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.'\n}, {\n  name: 'Terracotta Army',\n  artist: 'Unknown Artist',\n  description: 'The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.',\n  url: 'https://i.imgur.com/HMFmH6m.jpg',\n  alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.'\n}, {\n  name: 'Lunar Landscape',\n  artist: 'Louise Nevelson',\n  description: 'Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism’s geometric abstraction of space and form.',\n  url: 'https://i.imgur.com/rN7hY6om.jpg',\n  alt: 'A black matte sculpture where the individual elements are initially indistinguishable.'\n}, {\n  name: 'Aureole',\n  artist: 'Ranjani Shettar',\n  description: 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n  url: 'https://i.imgur.com/okTpbHhm.jpg',\n  alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.'\n}, {\n  name: 'Hippos',\n  artist: 'Taipei Zoo',\n  description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.',\n  url: 'https://i.imgur.com/6o5Vuyu.jpg',\n  alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.'\n}];\n\n// Make UI match the initial state.\nupdateDOM();\n```\n\n```html public/index.html\n<button id=\"nextButton\">\n  Next\n</button>\n<h3 id=\"header\"></h3>\n<button id=\"moreButton\"></button>\n<p id=\"description\"></p>\n<img id=\"image\">\n\n<style>\n* { box-sizing: border-box; }\nbody { font-family: sans-serif; margin: 20px; padding: 0; }\nbutton { display: block; margin-bottom: 10px; }\n</style>\n```\n\n```css\nbutton { display: block; margin-bottom: 10px; }\n```\n\n</Sandpack>\n\nReact を使用するためにこのことを理解する必要はありませんが、脳内モデルとして知っておくと役立つかもしれません。\n\n</DeepDive>\n\n## state は独立しておりプライベート {/*state-is-isolated-and-private*/}\n\nstate は画面上の個々のコンポーネントインスタンスに対してローカルです。言い換えると、**同じコンポーネントを 2 回レンダーした場合、それぞれのコピーは完全に独立した state を有することになります！** そのうちの 1 つを変更しても、もう 1 つには影響しません。\n\nこの例では、先ほどの `Gallery` コンポーネントが、そのロジックには変更を加えずに 2 回レンダーされています。それぞれのギャラリの中のボタンをクリックしてみてください。これらの state が独立していることが分かるでしょう。\n\n<Sandpack>\n\n```js\nimport Gallery from './Gallery.js';\n\nexport default function Page() {\n  return (\n    <div className=\"Page\">\n      <Gallery />\n      <Gallery />\n    </div>\n  );\n}\n\n```\n\n```js src/Gallery.js\nimport { useState } from 'react';\nimport { sculptureList } from './data.js';\n\nexport default function Gallery() {\n  const [index, setIndex] = useState(0);\n  const [showMore, setShowMore] = useState(false);\n\n  function handleNextClick() {\n    setIndex(index + 1);\n  }\n\n  function handleMoreClick() {\n    setShowMore(!showMore);\n  }\n\n  let sculpture = sculptureList[index];\n  return (\n    <section>\n      <button onClick={handleNextClick}>\n        Next\n      </button>\n      <h2>\n        <i>{sculpture.name} </i> \n        by {sculpture.artist}\n      </h2>\n      <h3>  \n        ({index + 1} of {sculptureList.length})\n      </h3>\n      <button onClick={handleMoreClick}>\n        {showMore ? 'Hide' : 'Show'} details\n      </button>\n      {showMore && <p>{sculpture.description}</p>}\n      <img \n        src={sculpture.url} \n        alt={sculpture.alt}\n      />\n    </section>\n  );\n}\n```\n\n```js src/data.js\nexport const sculptureList = [{\n  name: 'Homenaje a la Neurocirugía',\n  artist: 'Marta Colvin Andrade',\n  description: 'Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.',\n  url: 'https://i.imgur.com/Mx7dA2Y.jpg',\n  alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.'  \n}, {\n  name: 'Floralis Genérica',\n  artist: 'Eduardo Catalano',\n  description: 'This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.',\n  url: 'https://i.imgur.com/ZF6s192m.jpg',\n  alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.'\n}, {\n  name: 'Eternal Presence',\n  artist: 'John Woodrow Wilson',\n  description: 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n  url: 'https://i.imgur.com/aTtVpES.jpg',\n  alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.'\n}, {\n  name: 'Moai',\n  artist: 'Unknown Artist',\n  description: 'Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.',\n  url: 'https://i.imgur.com/RCwLEoQm.jpg',\n  alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.'\n}, {\n  name: 'Blue Nana',\n  artist: 'Niki de Saint Phalle',\n  description: 'The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.',\n  url: 'https://i.imgur.com/Sd1AgUOm.jpg',\n  alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.'\n}, {\n  name: 'Ultimate Form',\n  artist: 'Barbara Hepworth',\n  description: 'This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.',\n  url: 'https://i.imgur.com/2heNQDcm.jpg',\n  alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.'\n}, {\n  name: 'Cavaliere',\n  artist: 'Lamidi Olonade Fakeye',\n  description: \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n  url: 'https://i.imgur.com/wIdGuZwm.png',\n  alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.'\n}, {\n  name: 'Big Bellies',\n  artist: 'Alina Szapocznikow',\n  description: \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n  url: 'https://i.imgur.com/AlHTAdDm.jpg',\n  alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.'\n}, {\n  name: 'Terracotta Army',\n  artist: 'Unknown Artist',\n  description: 'The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.',\n  url: 'https://i.imgur.com/HMFmH6m.jpg',\n  alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.'\n}, {\n  name: 'Lunar Landscape',\n  artist: 'Louise Nevelson',\n  description: 'Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism’s geometric abstraction of space and form.',\n  url: 'https://i.imgur.com/rN7hY6om.jpg',\n  alt: 'A black matte sculpture where the individual elements are initially indistinguishable.'\n}, {\n  name: 'Aureole',\n  artist: 'Ranjani Shettar',\n  description: 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n  url: 'https://i.imgur.com/okTpbHhm.jpg',\n  alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.'\n}, {\n  name: 'Hippos',\n  artist: 'Taipei Zoo',\n  description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.',\n  url: 'https://i.imgur.com/6o5Vuyu.jpg',\n  alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.'\n}];\n```\n\n```css\nbutton { display: block; margin-bottom: 10px; }\n.Page > * {\n  float: left;\n  width: 50%;\n  padding: 10px;\n}\nh2 { margin-top: 10px; margin-bottom: 0; }\nh3 {\n  margin-top: 5px;\n  font-weight: normal;\n  font-size: 100%;\n}\nimg { width: 120px; height: 120px; }\nbutton {\n  display: block;\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n```\n\n</Sandpack>\n\nこれが state 変数と、モジュールのトップレベルで宣言する通常の変数との違いです。state は特定の関数呼び出しやコードの場所に紐付いているのではなく、画面上の特定の場所に対して「ローカル」になります。あなたが 2 つの `<Gallery />` コンポーネントをレンダーしたので、それらの state は別々に保持されているのです。\n\nまた、`Page` コンポーネントは、`Gallery` の state の値も、そもそも state が存在するかどうかも「知らない」ということにも注目してください。props と違い、**state はそれを宣言したコンポーネントに完全にプライベートなものです**。親コンポーネントがそれを変更することはできません。このおかげで、任意のコンポーネントに state を追加したり削除したりしても、他のコンポーネントに影響を与えることはありません。\n\n両方のギャラリで state を同期させたい場合はどうすればよいでしょうか？ React での正解は、子コンポーネントから state を*削除*して、それらに最も近い共有の親に追加することです。ここからの数ページでは、1 つのコンポーネント内での state の管理に焦点を当てていますが、[コンポーネント間での state 共有](/learn/sharing-state-between-components)で改めてこのトピックに戻って解説します。\n\n<Recap>\n\n* レンダー間で情報を「記憶」しておく必要があるコンポーネントには、state 変数を使う。\n* state 変数は、`useState` フックを呼び出すことで宣言される。\n* フックは `use` から始まる特殊な関数であり、state などの React 機能に「接続」できる。\n* フックはインポートと似ており、無条件に呼び出す必要がある。`useState` などのフックの呼び出しは、コンポーネントのトップレベルか別のフックでのみ有効である。\n* useState フックは、現在の state とそれを更新する関数の組み合わせを返す。\n* 複数の state 変数を持つことができる。内部で React はそれらを呼び出し順を用いて対応付ける。\n* state はコンポーネントにプライベートなものである。2 つの場所でレンダーすると、それぞれのコピーが独立した state を得る。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### ギャラリの完成 {/*complete-the-gallery*/}\n\n最後の彫刻が表示されているときに \"Next\" を押すと、コードがクラッシュします。クラッシュを防ぐためにロジックを修正してください。そのためには、イベントハンドラに追加のロジックを追加するか、操作が不可能な場合はボタンを無効化しましょう。\n\nクラッシュを修正できたら、\"Previous\" ボタンを追加して、ひとつ前の彫刻を表示するようにしてください。最初の彫刻でクラッシュしないようにしましょう。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { sculptureList } from './data.js';\n\nexport default function Gallery() {\n  const [index, setIndex] = useState(0);\n  const [showMore, setShowMore] = useState(false);\n\n  function handleNextClick() {\n    setIndex(index + 1);\n  }\n\n  function handleMoreClick() {\n    setShowMore(!showMore);\n  }\n\n  let sculpture = sculptureList[index];\n  return (\n    <>\n      <button onClick={handleNextClick}>\n        Next\n      </button>\n      <h2>\n        <i>{sculpture.name} </i> \n        by {sculpture.artist}\n      </h2>\n      <h3>  \n        ({index + 1} of {sculptureList.length})\n      </h3>\n      <button onClick={handleMoreClick}>\n        {showMore ? 'Hide' : 'Show'} details\n      </button>\n      {showMore && <p>{sculpture.description}</p>}\n      <img \n        src={sculpture.url} \n        alt={sculpture.alt}\n      />\n    </>\n  );\n}\n```\n\n```js src/data.js\nexport const sculptureList = [{\n  name: 'Homenaje a la Neurocirugía',\n  artist: 'Marta Colvin Andrade',\n  description: 'Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.',\n  url: 'https://i.imgur.com/Mx7dA2Y.jpg',\n  alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.'  \n}, {\n  name: 'Floralis Genérica',\n  artist: 'Eduardo Catalano',\n  description: 'This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.',\n  url: 'https://i.imgur.com/ZF6s192m.jpg',\n  alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.'\n}, {\n  name: 'Eternal Presence',\n  artist: 'John Woodrow Wilson',\n  description: 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n  url: 'https://i.imgur.com/aTtVpES.jpg',\n  alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.'\n}, {\n  name: 'Moai',\n  artist: 'Unknown Artist',\n  description: 'Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.',\n  url: 'https://i.imgur.com/RCwLEoQm.jpg',\n  alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.'\n}, {\n  name: 'Blue Nana',\n  artist: 'Niki de Saint Phalle',\n  description: 'The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.',\n  url: 'https://i.imgur.com/Sd1AgUOm.jpg',\n  alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.'\n}, {\n  name: 'Ultimate Form',\n  artist: 'Barbara Hepworth',\n  description: 'This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.',\n  url: 'https://i.imgur.com/2heNQDcm.jpg',\n  alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.'\n}, {\n  name: 'Cavaliere',\n  artist: 'Lamidi Olonade Fakeye',\n  description: \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n  url: 'https://i.imgur.com/wIdGuZwm.png',\n  alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.'\n}, {\n  name: 'Big Bellies',\n  artist: 'Alina Szapocznikow',\n  description: \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n  url: 'https://i.imgur.com/AlHTAdDm.jpg',\n  alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.'\n}, {\n  name: 'Terracotta Army',\n  artist: 'Unknown Artist',\n  description: 'The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.',\n  url: 'https://i.imgur.com/HMFmH6m.jpg',\n  alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.'\n}, {\n  name: 'Lunar Landscape',\n  artist: 'Louise Nevelson',\n  description: 'Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism’s geometric abstraction of space and form.',\n  url: 'https://i.imgur.com/rN7hY6om.jpg',\n  alt: 'A black matte sculpture where the individual elements are initially indistinguishable.'\n}, {\n  name: 'Aureole',\n  artist: 'Ranjani Shettar',\n  description: 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n  url: 'https://i.imgur.com/okTpbHhm.jpg',\n  alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.'\n}, {\n  name: 'Hippos',\n  artist: 'Taipei Zoo',\n  description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.',\n  url: 'https://i.imgur.com/6o5Vuyu.jpg',\n  alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.'\n}];\n```\n\n```css\nbutton { display: block; margin-bottom: 10px; }\n.Page > * {\n  float: left;\n  width: 50%;\n  padding: 10px;\n}\nh2 { margin-top: 10px; margin-bottom: 0; }\nh3 {\n  margin-top: 5px;\n  font-weight: normal;\n  font-size: 100%;\n}\nimg { width: 120px; height: 120px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n両方のイベントハンドラ内にガード条件を追加し、必要に応じてボタンを無効化します：\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { sculptureList } from './data.js';\n\nexport default function Gallery() {\n  const [index, setIndex] = useState(0);\n  const [showMore, setShowMore] = useState(false);\n\n  let hasPrev = index > 0;\n  let hasNext = index < sculptureList.length - 1;\n\n  function handlePrevClick() {\n    if (hasPrev) {\n      setIndex(index - 1);\n    }\n  }\n\n  function handleNextClick() {\n    if (hasNext) {\n      setIndex(index + 1);\n    }\n  }\n\n  function handleMoreClick() {\n    setShowMore(!showMore);\n  }\n\n  let sculpture = sculptureList[index];\n  return (\n    <>\n      <button\n        onClick={handlePrevClick}\n        disabled={!hasPrev}\n      >\n        Previous\n      </button>\n      <button\n        onClick={handleNextClick}\n        disabled={!hasNext}\n      >\n        Next\n      </button>\n      <h2>\n        <i>{sculpture.name} </i> \n        by {sculpture.artist}\n      </h2>\n      <h3>  \n        ({index + 1} of {sculptureList.length})\n      </h3>\n      <button onClick={handleMoreClick}>\n        {showMore ? 'Hide' : 'Show'} details\n      </button>\n      {showMore && <p>{sculpture.description}</p>}\n      <img \n        src={sculpture.url} \n        alt={sculpture.alt}\n      />\n    </>\n  );\n}\n```\n\n```js src/data.js hidden\nexport const sculptureList = [{\n  name: 'Homenaje a la Neurocirugía',\n  artist: 'Marta Colvin Andrade',\n  description: 'Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.',\n  url: 'https://i.imgur.com/Mx7dA2Y.jpg',\n  alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.'  \n}, {\n  name: 'Floralis Genérica',\n  artist: 'Eduardo Catalano',\n  description: 'This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.',\n  url: 'https://i.imgur.com/ZF6s192m.jpg',\n  alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.'\n}, {\n  name: 'Eternal Presence',\n  artist: 'John Woodrow Wilson',\n  description: 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as \"a symbolic Black presence infused with a sense of universal humanity.\"',\n  url: 'https://i.imgur.com/aTtVpES.jpg',\n  alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.'\n}, {\n  name: 'Moai',\n  artist: 'Unknown Artist',\n  description: 'Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.',\n  url: 'https://i.imgur.com/RCwLEoQm.jpg',\n  alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.'\n}, {\n  name: 'Blue Nana',\n  artist: 'Niki de Saint Phalle',\n  description: 'The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.',\n  url: 'https://i.imgur.com/Sd1AgUOm.jpg',\n  alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.'\n}, {\n  name: 'Ultimate Form',\n  artist: 'Barbara Hepworth',\n  description: 'This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.',\n  url: 'https://i.imgur.com/2heNQDcm.jpg',\n  alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.'\n}, {\n  name: 'Cavaliere',\n  artist: 'Lamidi Olonade Fakeye',\n  description: \"Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.\",\n  url: 'https://i.imgur.com/wIdGuZwm.png',\n  alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.'\n}, {\n  name: 'Big Bellies',\n  artist: 'Alina Szapocznikow',\n  description: \"Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.\",\n  url: 'https://i.imgur.com/AlHTAdDm.jpg',\n  alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.'\n}, {\n  name: 'Terracotta Army',\n  artist: 'Unknown Artist',\n  description: 'The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.',\n  url: 'https://i.imgur.com/HMFmH6m.jpg',\n  alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.'\n}, {\n  name: 'Lunar Landscape',\n  artist: 'Louise Nevelson',\n  description: 'Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism’s geometric abstraction of space and form.',\n  url: 'https://i.imgur.com/rN7hY6om.jpg',\n  alt: 'A black matte sculpture where the individual elements are initially indistinguishable.'\n}, {\n  name: 'Aureole',\n  artist: 'Ranjani Shettar',\n  description: 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a \"fine synthesis of unlikely materials.\"',\n  url: 'https://i.imgur.com/okTpbHhm.jpg',\n  alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.'\n}, {\n  name: 'Hippos',\n  artist: 'Taipei Zoo',\n  description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.',\n  url: 'https://i.imgur.com/6o5Vuyu.jpg',\n  alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.'\n}];\n```\n\n```css\nbutton { display: block; margin-bottom: 10px; }\n.Page > * {\n  float: left;\n  width: 50%;\n  padding: 10px;\n}\nh2 { margin-top: 10px; margin-bottom: 0; }\nh3 {\n  margin-top: 5px;\n  font-weight: normal;\n  font-size: 100%;\n}\nimg { width: 120px; height: 120px; }\n```\n\n</Sandpack>\n\nJSX を返している部分とイベントハンドラ内の両方で `hasPrev` と `hasNext` を使っていることに注意してください！ この便利なパターンが使えるのは、イベントハンドラ関数は[クロージャー](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)を用いてレンダー時に宣言されたあらゆる変数を参照できるからです。\n\n</Solution>\n\n#### 動かないフォームの修正 {/*fix-stuck-form-inputs*/}\n\n以下の入力フィールドに入力しても、何も表示されません。入力値が空文字列に「固定」されているかのようです。最初の `<input>` の `value` は `firstName` 変数に常に一致するように設定されており、2 番目の `<input>` の `value` は、`lastName` 変数に常に一致するように設定されています。それ自体は正しいです。両方の入力欄には `onChange` イベントハンドラがあり、最新のユーザ入力（`e.target.value`）に基づいて変数を更新しようとしています。ただし、再レンダー間で変数が値を「覚えて」いないようです。代わりに state 変数を使用することで、これを修正してください。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [6]}}\nexport default function Form() {\n  let firstName = '';\n  let lastName = '';\n\n  function handleFirstNameChange(e) {\n    firstName = e.target.value;\n  }\n\n  function handleLastNameChange(e) {\n    lastName = e.target.value;\n  }\n\n  function handleReset() {\n    firstName = '';\n    lastName = '';\n  }\n\n  return (\n    <form onSubmit={e => e.preventDefault()}>\n      <input\n        placeholder=\"First name\"\n        value={firstName}\n        onChange={handleFirstNameChange}\n      />\n      <input\n        placeholder=\"Last name\"\n        value={lastName}\n        onChange={handleLastNameChange}\n      />\n      <h1>Hi, {firstName} {lastName}</h1>\n      <button onClick={handleReset}>Reset</button>\n    </form>\n  );\n}\n```\n\n```css \nh1 { margin-top: 10px; }\n```\n\n</Sandpack>\n\n<Solution>\n\nまず、React から `useState` をインポートします。次に、`useState` を呼び出して宣言された state 変数で `firstName` と `lastName` を置き換えます。最後に、`firstName = ...` という代入を `setFirstName(...)` に置き換え、`lastName` についても同様にします。リセットボタンが機能するように `handleReset` も忘れずに更新してください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [firstName, setFirstName] = useState('');\n  const [lastName, setLastName] = useState('');\n\n  function handleFirstNameChange(e) {\n    setFirstName(e.target.value);\n  }\n\n  function handleLastNameChange(e) {\n    setLastName(e.target.value);\n  }\n\n  function handleReset() {\n    setFirstName('');\n    setLastName('');\n  }\n\n  return (\n    <form onSubmit={e => e.preventDefault()}>\n      <input\n        placeholder=\"First name\"\n        value={firstName}\n        onChange={handleFirstNameChange}\n      />\n      <input\n        placeholder=\"Last name\"\n        value={lastName}\n        onChange={handleLastNameChange}\n      />\n      <h1>Hi, {firstName} {lastName}</h1>\n      <button onClick={handleReset}>Reset</button>\n    </form>\n  );\n}\n```\n\n```css \nh1 { margin-top: 10px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### クラッシュの修正 {/*fix-a-crash*/}\n\nユーザがフィードバックを残すための小さなフォームがあります。フィードバックが送信されると、お礼のメッセージが表示されるはずです。が、\"Rendered fewer hooks than expected\" というエラーメッセージが表示されてクラッシュします。間違いを見つけて修正できますか？\n\n<Hint>\n\nフックを呼び出すことができる*場所*に関する制限がなかったでしょうか。このコンポーネントは何かルールに違反していませんか？ リンタチェックを無効化するコメントがないか確認してください。そういう場所にこそバグが潜んでいるものです！\n\n</Hint>\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [9]}}\nimport { useState } from 'react';\n\nexport default function FeedbackForm() {\n  const [isSent, setIsSent] = useState(false);\n  if (isSent) {\n    return <h1>Thank you!</h1>;\n  } else {\n    // eslint-disable-next-line\n    const [message, setMessage] = useState('');\n    return (\n      <form onSubmit={e => {\n        e.preventDefault();\n        alert(`Sending: \"${message}\"`);\n        setIsSent(true);\n      }}>\n        <textarea\n          placeholder=\"Message\"\n          value={message}\n          onChange={e => setMessage(e.target.value)}\n        />\n        <br />\n        <button type=\"submit\">Send</button>\n      </form>\n    );\n  }\n}\n```\n\n</Sandpack>\n\n<Solution>\n\nフックはコンポーネント関数のトップレベルでのみ呼び出すことができます。ここでは、最初の `isSent` 定義はこのルールに従っていますが、`message` 定義が条件分岐の中に入ってしまっています。\n\n条件分岐の外に出すことで問題を解決しましょう：\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function FeedbackForm() {\n  const [isSent, setIsSent] = useState(false);\n  const [message, setMessage] = useState('');\n\n  if (isSent) {\n    return <h1>Thank you!</h1>;\n  } else {\n    return (\n      <form onSubmit={e => {\n        e.preventDefault();\n        alert(`Sending: \"${message}\"`);\n        setIsSent(true);\n      }}>\n        <textarea\n          placeholder=\"Message\"\n          value={message}\n          onChange={e => setMessage(e.target.value)}\n        />\n        <br />\n        <button type=\"submit\">Send</button>\n      </form>\n    );\n  }\n}\n```\n\n</Sandpack>\n\nフックは無条件で、かつ常に同じ順序で呼び出される必要があることを思い出してください！\n\nまた、不要な `else` ブランチを削除してネストを減らすこともできます。ただし、フックへのすべての呼び出しが最初の `return` の*前に*行われることが重要です。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function FeedbackForm() {\n  const [isSent, setIsSent] = useState(false);\n  const [message, setMessage] = useState('');\n\n  if (isSent) {\n    return <h1>Thank you!</h1>;\n  }\n\n  return (\n    <form onSubmit={e => {\n      e.preventDefault();\n      alert(`Sending: \"${message}\"`);\n      setIsSent(true);\n    }}>\n      <textarea\n        placeholder=\"Message\"\n        value={message}\n        onChange={e => setMessage(e.target.value)}\n      />\n      <br />\n      <button type=\"submit\">Send</button>\n    </form>\n  );\n}\n```\n\n</Sandpack>\n\n2 番目の `useState` の呼び出しを `if` 条件の後ろ側に移動して、再びコードが動かなくなることを確認してください。\n\nリンタが [React 用に設定されている場合](/learn/editor-setup#linting)、このような間違いをした際にリントエラーが表示されるはずです。不具合のあるコードをローカルで試してもエラーが表示されない場合は、プロジェクトのリント設定が必要です。\n\n</Solution>\n\n#### 不要な state を削除 {/*remove-unnecessary-state*/}\n\nこの例では、ボタンがクリックされると、ユーザの名前を尋ねて、挨拶を表示するアラートが表示されるはずです。名前を保持するために state を使用しようとしましたが、なぜか最初は \"Hello, !\" と表示され、その後も \"Hello,&nbsp;[name]!\" という形で、ひとつ前の入力値が表示されてしまいます。\n\nこのコードを修正するには、不要な state 変数を削除してください（[この問題が発生した理由](/learn/state-as-a-snapshot)については後で説明します）。\n\nこの state 変数が不要であった理由を説明できますか？\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function FeedbackForm() {\n  const [name, setName] = useState('');\n\n  function handleClick() {\n    setName(prompt('What is your name?'));\n    alert(`Hello, ${name}!`);\n  }\n\n  return (\n    <button onClick={handleClick}>\n      Greet\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution>\n\nこの修正版では、`name` を必要としている関数内で、通常の変数を宣言しています：\n\n<Sandpack>\n\n```js\nexport default function FeedbackForm() {\n  function handleClick() {\n    const name = prompt('What is your name?');\n    alert(`Hello, ${name}!`);\n  }\n\n  return (\n    <button onClick={handleClick}>\n      Greet\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\nstate 変数は、コンポーネントの再レンダー間で情報を保持するためにのみ必要なものです。単一のイベントハンドラ内では、通常の変数で十分です。通常の変数でうまく動作する場合は、state 変数を追加しないでください。\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/state-as-a-snapshot.md",
    "content": "---\ntitle: state はスナップショットである\n---\n\n<Intro>\n\nstate 変数は、読んだり書いたりできる普通の JavaScript の変数のように見えるかもしれません。しかし、state はむしろ、スナップショットのように振る舞います。state をセットしても、既にある state 変数は変更されず、代わりに再レンダーがトリガされます。\n\n</Intro>\n\n<YouWillLearn>\n\n* state のセットが再レンダーをどのようにトリガするのか\n* state がいつどのように更新されるか\n* state がセットされた直後に更新されない理由\n* イベントハンドラが state の「スナップショット」にどのようにアクセスするのか\n\n</YouWillLearn>\n\n## state のセットでレンダーがトリガされる {/*setting-state-triggers-renders*/}\n\nユーザインターフェースとはクリックなどのユーザイベントに直接反応して更新されるものだ、と考えているかもしれません。React の動作は、このような考え方とは少し異なります。前のページで、[state をセットすることで再レンダーを React に要求](/learn/render-and-commit#step-1-trigger-a-render)しているのだ、ということを見てきました。これは、インターフェースがイベントに応答するためには、*state を更新*する必要があることを意味します。\n\nこの例では、\"Send\" を押すと、`setIsSent(true)` が React に UI の再レンダーを指示します。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [isSent, setIsSent] = useState(false);\n  const [message, setMessage] = useState('Hi!');\n  if (isSent) {\n    return <h1>Your message is on its way!</h1>\n  }\n  return (\n    <form onSubmit={(e) => {\n      e.preventDefault();\n      setIsSent(true);\n      sendMessage(message);\n    }}>\n      <textarea\n        placeholder=\"Message\"\n        value={message}\n        onChange={e => setMessage(e.target.value)}\n      />\n      <button type=\"submit\">Send</button>\n    </form>\n  );\n}\n\nfunction sendMessage(message) {\n  // ...\n}\n```\n\n```css\nlabel, textarea { margin-bottom: 10px; display: block; }\n```\n\n</Sandpack>\n\nボタンをクリックすると次のような処理が行われます：\n\n1. `onSubmit` イベントハンドラが実行されます。\n2. `setIsSent(true)` が `isSent` を `true` にセットし、新しいレンダーを予約します。\n3. React が新しい `isSent` の値を使ってコンポーネントを再レンダーします。\n\nstate とレンダーの関係をもう少し詳しく見ていきましょう。\n\n## レンダーは時間を切り取ってスナップショットを取る {/*rendering-takes-a-snapshot-in-time*/}\n\n[「レンダーする」](/learn/render-and-commit#step-2-react-renders-your-components)とは、React があなたのコンポーネント（関数）を呼び出すということです。関数から返される JSX は、その時点での UI のスナップショットのようなものです。その JSX 内の props、イベントハンドラ、ローカル変数はすべて、**レンダー時の state を使用して計算されます**。\n\n写真や映画のフレームとは違い、返される「UI のスナップショット」はインタラクティブです。イベントハンドラのような、入力に対する応答を指定するためのロジックが含まれています。React は画面をこのスナップショットに合わせて更新し、イベントハンドラを接続します。その結果として、ボタンを押すと JSX に書いたクリックハンドラがトリガされます。\n\nReact がコンポーネントを再レンダーする際には：\n\n1. React が再度あなたの関数を呼び出します。\n2. 関数は新しい JSX のスナップショットを返します。\n3. React はあなたの関数が返したスナップショットに合わせて画面を更新します。\n\n<IllustrationBlock sequential>\n    <Illustration caption=\"React が関数を実行\" src=\"/images/docs/illustrations/i_render1.png\" />\n    <Illustration caption=\"スナップショットを計算\" src=\"/images/docs/illustrations/i_render2.png\" />\n    <Illustration caption=\"DOM ツリーを更新\" src=\"/images/docs/illustrations/i_render3.png\" />\n</IllustrationBlock>\n\nコンポーネントのメモリとしての state は、関数が終了したら消えてしまう通常の変数とは異なります。state は実際には React 自体の中で「生存」しています。まるで棚に保管しているかのように、関数の外部で存在し続けます。React がコンポーネントを呼び出すとき、React はその特定のレンダーに対する state のスナップショットを提供します。あなたのコンポーネントは、props やイベントハンドラの新たな一式を揃えた JSX という形で UI のスナップショットを返し、それらはすべて**その特定のレンダー時の state の値を使って計算されます！**\n\n<IllustrationBlock sequential>\n  <Illustration caption=\"state の更新を React に指示\" src=\"/images/docs/illustrations/i_state-snapshot1.png\" />\n  <Illustration caption=\"React が state の値を更新\" src=\"/images/docs/illustrations/i_state-snapshot2.png\" />\n  <Illustration caption=\"React がコンポーネントに state のスナップショットを渡す\" src=\"/images/docs/illustrations/i_state-snapshot3.png\" />\n</IllustrationBlock>\n\nこれがどのように動作するかを示す小さな実験をしましょう。この例では、\"+3\" ボタンをクリックすると `setNumber(number + 1)` を 3 回呼び出すので、カウンタが 3 回インクリメントされると予想するかもしれません。\n\n\"+3\" ボタンをクリックすると何が起こるか見てみましょう。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [number, setNumber] = useState(0);\n\n  return (\n    <>\n      <h1>{number}</h1>\n      <button onClick={() => {\n        setNumber(number + 1);\n        setNumber(number + 1);\n        setNumber(number + 1);\n      }}>+3</button>\n    </>\n  )\n}\n```\n\n```css\nbutton { display: inline-block; margin: 10px; font-size: 20px; }\nh1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }\n```\n\n</Sandpack>\n\n`number` がクリックごとに 1 しか増えていませんね！\n\n**state をセットしても、それが本当に変更されるのは*次回の*レンダーです**。最初のレンダーでは `number` が `0` でした。だから、*そのレンダーの* `onClick` ハンドラにおいては、`setNumber(number + 1)` が呼ばれた後も `number` が `0` のままだったのです。\n\n```js\n<button onClick={() => {\n  setNumber(number + 1);\n  setNumber(number + 1);\n  setNumber(number + 1);\n}}>+3</button>\n```\n\nこのボタンのクリックハンドラは、以下のように React に指示しています。\n\n1. `setNumber(number + 1)`: `number` が `0` なので `setNumber(0 + 1)`。\n    - React は次回のレンダーで `number` を `1` に更新する準備をする。\n2. `setNumber(number + 1)`: `number` が `0` なので `setNumber(0 + 1)`。\n    - React は次回のレンダーで `number` を `1` に更新する準備をする。\n3. `setNumber(number + 1)`: `number` が `0` なので `setNumber(0 + 1)`。\n    - React は次回のレンダーで `number` を `1` に更新する準備をする。\n\n`setNumber(number + 1)` を 3 回呼び出しましたが、*今回のレンダーの*イベントハンドラでは `number` は常に `0` なので、state を 3 回連続して `1` にセットしていることになります。これが、イベントハンドラが終了した後、React が `number` を `3` ではなく `1` とした上でコンポーネントを再レンダーする理由です。\n\nもっと分かりやすくするために、頭の中でコード内の state 変数を実際の値に置換してみることもできます。*このレンダーでは* `number` という state 変数は `0` なので、イベントハンドラは次のようになっています。\n\n```js\n<button onClick={() => {\n  setNumber(0 + 1);\n  setNumber(0 + 1);\n  setNumber(0 + 1);\n}}>+3</button>\n```\n\n次のレンダーでは、`number` が `1` になるため、*そちらのレンダーの* クリックハンドラは、次のようになります。\n\n```js\n<button onClick={() => {\n  setNumber(1 + 1);\n  setNumber(1 + 1);\n  setNumber(1 + 1);\n}}>+3</button>\n```\n\n以上が、ボタンを再度クリックするとカウンタが `2` にセットされ、次のクリックでは `3` にセットされ、というようになる理由です。\n\n## 時間経過と state {/*state-over-time*/}\n\nなかなか面白い話でした。それでは、このボタンをクリックするとアラートに何が表示されるか予想してみてください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [number, setNumber] = useState(0);\n\n  return (\n    <>\n      <h1>{number}</h1>\n      <button onClick={() => {\n        setNumber(number + 5);\n        alert(number);\n      }}>+5</button>\n    </>\n  )\n}\n```\n\n```css\nbutton { display: inline-block; margin: 10px; font-size: 20px; }\nh1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }\n```\n\n</Sandpack>\n\n上記で説明した置換メソッドを使えば、アラートには \"0\" と表示されることがわかりますね。\n\n```js\nsetNumber(0 + 5);\nalert(0);\n```\n\nでも、アラートにタイマーを設定して、コンポーネントが再レンダーされた*後に*発火するようにしたらどうなるでしょうか？ \"0\" と表示されるのか、\"5\" と表示されるのか推測してみてください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [number, setNumber] = useState(0);\n\n  return (\n    <>\n      <h1>{number}</h1>\n      <button onClick={() => {\n        setNumber(number + 5);\n        setTimeout(() => {\n          alert(number);\n        }, 3000);\n      }}>+5</button>\n    </>\n  )\n}\n```\n\n```css\nbutton { display: inline-block; margin: 10px; font-size: 20px; }\nh1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }\n```\n\n</Sandpack>\n\n驚いたでしょうか？ さきほどの置換メソッドを使ってみれば、アラートに渡された state が「スナップショット」であることが分かるでしょう。\n\n```js\nsetNumber(0 + 5);\nsetTimeout(() => {\n  alert(0);\n}, 3000);\n```\n\nアラートが実行される時点では React に格納されている state は既に更新されているかもしれませんが、アラートはユーザがボタンを操作した時点での state のスナップショットを使ってスケジューリングされました！\n\nイベントハンドラのコードが非同期であっても、**レンダー内の state 変数の値は決して変わりません**。*そのレンダーの* `onClick` 内では、`setNumber(number + 5)` が呼ばれた後も `number` の値は `0` のままです。その値は React があなたのコンポーネントを呼び出して UI の「スナップショットを取った」時に、「固定」されたのです。\n\nここで、このお陰でタイミングにまつわる問題が起きづらくなっている、という例をお示しします。以下のフォームは、5 秒の遅延後にメッセージを送信します。ここでこんなシナリオを想像してみてください：\n\n1. \"Send\" ボタンを押して、\"Hello\" というメッセージをアリスに送る。\n2. 5 秒の遅延が終わる前に、\"To\" フィールドの値を \"Bob\" に変更する。\n\n`alert` に何が表示されると思いますか？ \"You said Hello to Alice\" と表示されるのでしょうか？ それとも \"You said Hello to Bob\" でしょうか？ ここまでの知識に基づいて推測し、実際に試してみましょう。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [to, setTo] = useState('Alice');\n  const [message, setMessage] = useState('Hello');\n\n  function handleSubmit(e) {\n    e.preventDefault();\n    setTimeout(() => {\n      alert(`You said ${message} to ${to}`);\n    }, 5000);\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <label>\n        To:{' '}\n        <select\n          value={to}\n          onChange={e => setTo(e.target.value)}>\n          <option value=\"Alice\">Alice</option>\n          <option value=\"Bob\">Bob</option>\n        </select>\n      </label>\n      <textarea\n        placeholder=\"Message\"\n        value={message}\n        onChange={e => setMessage(e.target.value)}\n      />\n      <button type=\"submit\">Send</button>\n    </form>\n  );\n}\n```\n\n```css\nlabel, textarea { margin-bottom: 10px; display: block; }\n```\n\n</Sandpack>\n\n**React は、レンダー内の state の値を「固定」し、イベントハンドラ内で保持します**。コードが実行されている途中で state が変更されたかどうか心配する必要はありません。\n\nしかし、再レンダー前に最新の state を読み取りたい場合はどうでしょうか？ [state 更新用関数](/learn/queueing-a-series-of-state-updates)を使うことができます。これについては次のページで説明します！\n\n<Recap>\n\n- state のセットは新しいレンダーをリクエストする。\n- React は state をコンポーネントの外側で、まるで棚に保管しておくかのようにして保持する。\n- `useState` を呼び出すと、React は*そのレンダーのための* state のスナップショットを返す。\n- 変数やイベントハンドラは複数レンダーをまたいで「生き残る」ことはない。すべてのレンダーは固有のイベントハンドラを持つ。\n- 各レンダー（およびその中の関数）からは、常に、React が *その*レンダーに渡した state のスナップショットが「見える」。\n- レンダーされた JSX を考える時と同様にして、イベントハンドラ内の state を頭の中で実際の値に置換してみることができる。\n- 過去に作成されたイベントハンドラは、それが作成されたレンダーにおける state の値を持っている。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### 信号機を実装 {/*implement-a-traffic-light*/}\n\n以下は、ボタンが押されると切り替わる歩行者用信号機のコンポーネントです。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function TrafficLight() {\n  const [walk, setWalk] = useState(true);\n\n  function handleClick() {\n    setWalk(!walk);\n  }\n\n  return (\n    <>\n      <button onClick={handleClick}>\n        Change to {walk ? 'Stop' : 'Walk'}\n      </button>\n      <h1 style={{\n        color: walk ? 'darkgreen' : 'darkred'\n      }}>\n        {walk ? 'Walk' : 'Stop'}\n      </h1>\n    </>\n  );\n}\n```\n\n```css\nh1 { margin-top: 20px; }\n```\n\n</Sandpack>\n\nクリックハンドラに `alert` を追加してください。信号が緑で \"Walk\" と表示されている場合、ボタンをクリックすると \"Stop is next\" と表示され、信号が赤で \"Stop\" と表示されている場合、ボタンをクリックすると \"Walk is next\" と表示されるようにしてください。\n\n`alert` を `setWalk` の前に置いた場合と後に置いた場合で、違いはありますか？\n\n<Solution>\n\n`alert` は以下のように書けます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function TrafficLight() {\n  const [walk, setWalk] = useState(true);\n\n  function handleClick() {\n    setWalk(!walk);\n    alert(walk ? 'Stop is next' : 'Walk is next');\n  }\n\n  return (\n    <>\n      <button onClick={handleClick}>\n        Change to {walk ? 'Stop' : 'Walk'}\n      </button>\n      <h1 style={{\n        color: walk ? 'darkgreen' : 'darkred'\n      }}>\n        {walk ? 'Walk' : 'Stop'}\n      </h1>\n    </>\n  );\n}\n```\n\n```css\nh1 { margin-top: 20px; }\n```\n\n</Sandpack>\n\n`alert` を `setWalk` の前に置いた場合と後に置いた場合で、違いはありません。このレンダー中、`walk` の値は固定です。`setWalk` を呼び出しても、*次の*レンダーまで実際の変更は起きず、現在レンダーのイベントハンドラには影響しません。\n\nこの行は最初は直感に反しているように思えるかもしれません。\n\n```js\nalert(walk ? 'Stop is next' : 'Walk is next');\n```\n\nしかし、これを「もし信号が今 \"Walk\" を表示しているなら、メッセージは \"Stop is next\" と言うべきだ」のように読めば、理に適っていることが分かります。イベントハンドラ内の `walk` 変数は、そのレンダーの `walk` の値と一致しており、変わることはありません。\n\n上記で説明している置換メソッドを適用して、このことが正しいことを確認することができます。`walk` が `true` の場合、次のようになります。\n\n```js\n<button onClick={() => {\n  setWalk(false);\n  alert('Stop is next');\n}}>\n  Change to Stop\n</button>\n<h1 style={{color: 'darkgreen'}}>\n  Walk\n</h1>\n```\n\nすなわち、\"Change to Stop\" をクリックすると、`walk` が `false` にセットされたレンダーがキューに入り、\"Stop is next\" というアラートが表示されます。\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/synchronizing-with-effects.md",
    "content": "---\ntitle: 'エフェクトを使って同期を行う'\n---\n\n<Intro>\n\n一部のコンポーネントは外部システムと同期を行う必要があります。例えば、React の state に基づいて非 React 製コンポーネントを制御したり、サーバとの接続を確立したり、コンポーネントが画面に表示されたときに分析用のログを送信したりしたいかもしれません。*エフェクト (Effect)* を使うことで、レンダー後にコードを実行して、React 外のシステムとコンポーネントを同期させることができます。\n\n</Intro>\n\n<YouWillLearn>\n\n- エフェクトとは何か\n- エフェクトとイベントの違い\n- コンポーネントでエフェクトを宣言する方法\n- 不要なエフェクト再実行をスキップする方法\n- 開発中にエフェクトが 2 回実行される理由と対処法\n\n</YouWillLearn>\n\n## エフェクトとは何であり、イベントとどう異なるのか {/*what-are-effects-and-how-are-they-different-from-events*/}\n\nエフェクトについて説明する前に、React コンポーネント内の 2 種類のロジックについて理解しておく必要があります。\n\n- **レンダーコード**（[UI の記述](/learn/describing-the-ui)で説明）とは、コンポーネントのトップレベルにあるものです。ここは、props や state を受け取り、それらを変換し、画面に表示したい JSX を返す場所です。[レンダーコードは純粋でなければなりません](/learn/keeping-components-pure)。数学の式のように結果を*計算*するだけで、他のことは行わないようにする必要があります。\n\n- **イベントハンドラ**（[インタラクティビティの追加](/learn/adding-interactivity)で説明）とは、コンポーネント内にネストされた関数であり、計算だけでなく何かを*実行*するものです。イベントハンドラは、入力フィールドを更新したり、商品を購入するための HTTP POST リクエストを送信したり、ユーザを別の画面に遷移させたりすることができます。イベントハンドラには、特定のユーザアクション（例えば、ボタンクリックや入力）によって引き起こされてプログラムの状態を変更する、[\"副作用 (side effect)\"](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) が含まれています。\n\nしかし、これらだけでは十分でない場合があります。画面に表示されている間、常にチャットサーバに接続していなければならない `ChatRoom` コンポーネントを考えてみてください。サーバへの接続は純粋な計算ではない（副作用がある）ため、レンダー中には行うことができません。しかし、`ChatRoom` が表示される原因となる、クリックのような特定のイベントは存在しません。\n\n***エフェクト*は、特定のイベントによってではなく、レンダー自体によって引き起こされる副作用を指定するためのものです**。チャットでのメッセージ送信は、ユーザが特定のボタンをクリックすることによって直接引き起こされるため、*イベント*です。しかし、サーバ接続のセットアップは、コンポーネントが表示される原因となるインタラクションに関係なく行われるべきであるため、*エフェクト*です。エフェクトは、[コミット](/learn/render-and-commit)の最後に、画面が更新された後に実行されます。ここが、React コンポーネントを外部システム（ネットワークやサードパーティのライブラリなど）と同期させるのに適したタイミングです。\n\n<Note>\n\nこのドキュメントの残りの部分では、\"エフェクト\"（大文字で始まる \"Effect\"）とは、上記の React 固有の定義、つまりレンダーによって引き起こされる副作用のことを指します。より広いプログラミング概念を指す場合は \"副作用 (side effect)\" と呼ぶことにします。\n\n</Note>\n\n\n## エフェクトはおそらく不要なもの {/*you-might-not-need-an-effect*/}\n\n**慌ててエフェクトをコンポーネントに追加しないようにしましょう**。エフェクトは通常、React のコードから「踏み出して」、何らかの*外部*システムと同期するために使用されるものだということを肝に銘じてください。これには、ブラウザ API、サードパーティのウィジェット、ネットワークなどが含まれます。エフェクトが他の state に基づいて state を調整しているだけの場合、[おそらくそのエフェクトは必要ありません](/learn/you-might-not-need-an-effect)。\n\n## エフェクトの書き方 {/*how-to-write-an-effect*/}\n\nエフェクトを書くには、以下の 3 つのステップに従ってください。\n\n1. **エフェクトを宣言する**。デフォルトでは、エフェクトはすべての[コミット](/learn/render-and-commit)後に実行されます。\n2. **エフェクトの依存値 (dependency) の配列を指定する**。ほとんどのエフェクトは、レンダー後に毎回ではなく、*必要に応じて*再実行されるべきものです。例えば、フェードインアニメーションの開始は、コンポーネントが表示されるときにのみ行われるべきです。チャットルームへの接続と切断は、コンポーネントが表示されたり消えたりするときや、チャットルームが変更されたときにのみ行われるべきです。*依存配列*を指定してこれをコントロールする方法について、後で説明します。\n3. **必要に応じてクリーンアップを追加する**。一部のエフェクトは、行っていたことを停止、元に戻す、またはクリーンアップする方法を指定する必要があります。例えば、「接続」には「切断」が必要で、「登録」には「解除」が必要で、「取得」には「キャンセル」または「無視」が必要です。*クリーンアップ関数*を返すことで、これを行う方法を学びます。\n\nそれぞれのステップを詳しく見ていきましょう。\n\n### ステップ 1：エフェクトを宣言する {/*step-1-declare-an-effect*/}\n\nコンポーネントでエフェクトを宣言するには、React から [`useEffect` フック](/reference/react/useEffect)をインポートします。\n\n```js\nimport { useEffect } from 'react';\n```\n\n次に、コンポーネントのトップレベルでそれを呼び出し、エフェクト内にコードを記述します。\n\n```js {2-4}\nfunction MyComponent() {\n  useEffect(() => {\n    // Code here will run after *every* render\n  });\n  return <div />;\n}\n```\n\nコンポーネントがレンダーされるたびに、React は画面を更新し、*その後で* `useEffect` 内のコードを実行します。言い換えると、**`useEffect` はレンダー結果が画面に反映され終わるまで、コードの実行を「遅らせ」ます**。\n\nエフェクトを使って外部システムと同期する方法を見てみましょう。`<VideoPlayer>` という React コンポーネントを考えてみてください。props として `isPlaying` を渡すことで、再生中か一時停止中かを制御できると便利です。\n\n```js\n<VideoPlayer isPlaying={isPlaying} />;\n```\n\nカスタム `VideoPlayer` コンポーネントは、ブラウザ組み込みの [`<video>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video) タグをレンダーします。\n\n```js\nfunction VideoPlayer({ src, isPlaying }) {\n  // TODO: do something with isPlaying\n  return <video src={src} />;\n}\n```\n\nしかしブラウザの `<video>` タグに `isPlaying` プロパティはありません。ビデオを制御する唯一の方法は、DOM 要素上で [`play()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play) および [`pause()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause) メソッドを手動で呼び出すことです。**ビデオが現在再生中であるべきかどうかを示す props である `isPlaying` の値を、`play()` や `pause()` などの呼び出しと同期させる必要があるわけです**。\n\nまず、`<video>` DOM ノードへの [ref を取得](/learn/manipulating-the-dom-with-refs)する必要があります。\n\nレンダー中に `play()` や `pause()` を呼び出したくなるかもしれませんが、それは正しくありません。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [7, 9]}}\nimport { useState, useRef, useEffect } from 'react';\n\nfunction VideoPlayer({ src, isPlaying }) {\n  const ref = useRef(null);\n\n  if (isPlaying) {\n    ref.current.play();  // Calling these while rendering isn't allowed.\n  } else {\n    ref.current.pause(); // Also, this crashes.\n  }\n\n  return <video ref={ref} src={src} loop playsInline />;\n}\n\nexport default function App() {\n  const [isPlaying, setIsPlaying] = useState(false);\n  return (\n    <>\n      <button onClick={() => setIsPlaying(!isPlaying)}>\n        {isPlaying ? 'Pause' : 'Play'}\n      </button>\n      <VideoPlayer\n        isPlaying={isPlaying}\n        src=\"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4\"\n      />\n    </>\n  );\n}\n```\n\n```css\nbutton { display: block; margin-bottom: 20px; }\nvideo { width: 250px; }\n```\n\n</Sandpack>\n\nこのコードが正しくない理由は、レンダー中に DOM ノードで何かをしようとしているからです。React では、[レンダーは JSX の純粋な計算](/learn/keeping-components-pure)であるべきであり、DOM の変更のような副作用を含んではいけません。\n\nそれに、`VideoPlayer` が初めて呼び出されるとき、その DOM はそもそも存在していません！ React は JSX が返されるまでどんな DOM を作成したいのか分からないのですから、`play()` や `pause()` を呼び出すための DOM ノードはまだ存在していません。\n\nここでの解決策は、**副作用を `useEffect` でラップして、レンダーの計算処理の外に出すことです**。\n\n```js {6,12}\nimport { useEffect, useRef } from 'react';\n\nfunction VideoPlayer({ src, isPlaying }) {\n  const ref = useRef(null);\n\n  useEffect(() => {\n    if (isPlaying) {\n      ref.current.play();\n    } else {\n      ref.current.pause();\n    }\n  });\n\n  return <video ref={ref} src={src} loop playsInline />;\n}\n```\n\nDOM の更新をエフェクトでラップすることで、React が先にまず画面を更新できるようになります。その後、エフェクトが実行されます。\n\n`VideoPlayer` コンポーネントがレンダーされるとき（初回または再レンダーのいずれでも）、いくつかのことが起こります。まず、React は画面を更新し、正しいプロパティを持つ `<video>` タグが DOM に存在するようにします。次に、React はエフェクトを実行します。最後に、エフェクトは `isPlaying` の値に応じて `play()` または `pause()` を呼び出します。\n\n再生/一時停止を何度か押して、ビデオプレーヤが `isPlaying` の値に同期していることを確認してください。\n\n<Sandpack>\n\n```js\nimport { useState, useRef, useEffect } from 'react';\n\nfunction VideoPlayer({ src, isPlaying }) {\n  const ref = useRef(null);\n\n  useEffect(() => {\n    if (isPlaying) {\n      ref.current.play();\n    } else {\n      ref.current.pause();\n    }\n  });\n\n  return <video ref={ref} src={src} loop playsInline />;\n}\n\nexport default function App() {\n  const [isPlaying, setIsPlaying] = useState(false);\n  return (\n    <>\n      <button onClick={() => setIsPlaying(!isPlaying)}>\n        {isPlaying ? 'Pause' : 'Play'}\n      </button>\n      <VideoPlayer\n        isPlaying={isPlaying}\n        src=\"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4\"\n      />\n    </>\n  );\n}\n```\n\n```css\nbutton { display: block; margin-bottom: 20px; }\nvideo { width: 250px; }\n```\n\n</Sandpack>\n\nこの例では、React の state に同期させる「外部システム」とはブラウザのメディア API でした。同様のアプローチを使用して、古い非 React コード（jQuery プラグインなど）を宣言的な React コンポーネントにラップできます。\n\nなおビデオプレーヤの制御は実際にはもっと複雑です。`play()` の呼び出しは失敗することがありますし、ユーザはブラウザ組み込みのコントロールを使って再生や一時停止を行うかもしれません。この例は非常に単純化された不完全なものです。\n\n<Pitfall>\n\nデフォルトでは、エフェクトは*毎回の*レンダーの後に実行されます。このため、次のようなコードは**無限ループを引き起こします**。\n\n```js\nconst [count, setCount] = useState(0);\nuseEffect(() => {\n  setCount(count + 1);\n});\n```\n\nエフェクトはレンダーの*結果*として実行されます。state の設定はレンダーを*トリガ*します。エフェクトで直ちに state を設定するというのは、電源コンセントを自分自身に接続するようなものです。エフェクトが実行され、state がセットされ、これによって再レンダーが発生し、エフェクトが実行され、再び state がセットされ、これによってまた再レンダーが発生し、という具合に続きます。\n\nエフェクトは通常、コンポーネントを*外部*システムと同期させるのに使います。外部システムがなく、他の state に基づいて state を調整したいだけの場合、[エフェクトは必要ありません](/learn/you-might-not-need-an-effect)。\n\n</Pitfall>\n\n### ステップ 2：エフェクトの依存配列を指定する {/*step-2-specify-the-effect-dependencies*/}\n\nデフォルトでは、エフェクトは*すべての*レンダー後に実行されます。しかし、これが**望ましくない場合があります**。\n\n- 時にはそれが遅いことがあります。外部システムとの同期は常に瞬時に起こるものではないため、必要でない限り行わない方が良いかもしれません。例えば、キーストロークごとにチャットサーバに再接続することは望ましくありません。\n- 時にはそれが間違っていることがあります。例えば、キーストロークごとにコンポーネントのフェードインアニメーションを開始したくはありません。アニメーションは、コンポーネントが初めて表示されるときに 1 回だけ再生されるべきです。\n\n問題を示すために、前掲の例に、いくつかの `console.log` 呼び出しと、親コンポーネントの state を更新するテキスト入力フィールドを加えたものを示します。タイピングによってエフェクトが再実行されていることを確認してください。\n\n<Sandpack>\n\n```js\nimport { useState, useRef, useEffect } from 'react';\n\nfunction VideoPlayer({ src, isPlaying }) {\n  const ref = useRef(null);\n\n  useEffect(() => {\n    if (isPlaying) {\n      console.log('Calling video.play()');\n      ref.current.play();\n    } else {\n      console.log('Calling video.pause()');\n      ref.current.pause();\n    }\n  });\n\n  return <video ref={ref} src={src} loop playsInline />;\n}\n\nexport default function App() {\n  const [isPlaying, setIsPlaying] = useState(false);\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input value={text} onChange={e => setText(e.target.value)} />\n      <button onClick={() => setIsPlaying(!isPlaying)}>\n        {isPlaying ? 'Pause' : 'Play'}\n      </button>\n      <VideoPlayer\n        isPlaying={isPlaying}\n        src=\"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4\"\n      />\n    </>\n  );\n}\n```\n\n```css\ninput, button { display: block; margin-bottom: 20px; }\nvideo { width: 250px; }\n```\n\n</Sandpack>\n\n`useEffect` の呼び出しの第 2 引数として*依存値*の配列を指定することで、React に**エフェクトの不必要な再実行をスキップ**するように指示できます。まず、上記の例の 14 行目に空の `[]` 配列を追加してください。\n\n```js {3}\n  useEffect(() => {\n    // ...\n  }, []);\n```\n\n`React Hook useEffect has a missing dependency: 'isPlaying'` というエラーが表示されるはずです。\n\n<Sandpack>\n\n```js\nimport { useState, useRef, useEffect } from 'react';\n\nfunction VideoPlayer({ src, isPlaying }) {\n  const ref = useRef(null);\n\n  useEffect(() => {\n    if (isPlaying) {\n      console.log('Calling video.play()');\n      ref.current.play();\n    } else {\n      console.log('Calling video.pause()');\n      ref.current.pause();\n    }\n  }, []); // This causes an error\n\n  return <video ref={ref} src={src} loop playsInline />;\n}\n\nexport default function App() {\n  const [isPlaying, setIsPlaying] = useState(false);\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input value={text} onChange={e => setText(e.target.value)} />\n      <button onClick={() => setIsPlaying(!isPlaying)}>\n        {isPlaying ? 'Pause' : 'Play'}\n      </button>\n      <VideoPlayer\n        isPlaying={isPlaying}\n        src=\"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4\"\n      />\n    </>\n  );\n}\n```\n\n```css\ninput, button { display: block; margin-bottom: 20px; }\nvideo { width: 250px; }\n```\n\n</Sandpack>\n\n問題は、エフェクト内のコードが `isPlaying` プロパティに*依存して*何をすべきかを決定しているにもかかわらず、その依存関係が明示的に宣言されていないことです。この問題を解決するために、依存配列に `isPlaying` を追加してください。\n\n```js {2,7}\n  useEffect(() => {\n    if (isPlaying) { // It's used here...\n      // ...\n    } else {\n      // ...\n    }\n  }, [isPlaying]); // ...so it must be declared here!\n```\n\n依存値がすべて宣言されているので、エラーはなくなりました。依存配列として `[isPlaying]` を指定することで、React に `isPlaying` が前回のレンダー時と同じである場合は、エフェクトの再実行をスキップするように指示しています。この変更により、入力欄に入力してもエフェクトは再実行されず、再生/一時停止ボタンを押した場合は再実行されるようになります。\n\n<Sandpack>\n\n```js\nimport { useState, useRef, useEffect } from 'react';\n\nfunction VideoPlayer({ src, isPlaying }) {\n  const ref = useRef(null);\n\n  useEffect(() => {\n    if (isPlaying) {\n      console.log('Calling video.play()');\n      ref.current.play();\n    } else {\n      console.log('Calling video.pause()');\n      ref.current.pause();\n    }\n  }, [isPlaying]);\n\n  return <video ref={ref} src={src} loop playsInline />;\n}\n\nexport default function App() {\n  const [isPlaying, setIsPlaying] = useState(false);\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input value={text} onChange={e => setText(e.target.value)} />\n      <button onClick={() => setIsPlaying(!isPlaying)}>\n        {isPlaying ? 'Pause' : 'Play'}\n      </button>\n      <VideoPlayer\n        isPlaying={isPlaying}\n        src=\"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4\"\n      />\n    </>\n  );\n}\n```\n\n```css\ninput, button { display: block; margin-bottom: 20px; }\nvideo { width: 250px; }\n```\n\n</Sandpack>\n\n依存配列には複数の依存値を含めることができます。React は、指定したすべての依存値が前回のレンダー時とまったく同じ値である場合に限り、エフェクトの再実行をスキップします。React は、個々の依存値を [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) を用いて比較します。詳細については [`useEffect` リファレンス](/reference/react/useEffect#reference)を参照してください。\n\n**依存値は自分で「選ぶ」ようなものではありません**。エフェクト内のコードに基づいて React が期待する配列と、指定した依存配列が合致しない場合、リントエラーが発生します。これにより、コード内の多くのバグを検出することができます。一部のコードを再実行しない場合は、[*エフェクトのコード自体を編集して*、その依存値が「必要」とならないようにします](/learn/lifecycle-of-reactive-effects#what-to-do-when-you-dont-want-to-re-synchronize)。\n\n<Pitfall>\n\n依存配列がない場合と、*空の* `[]` という依存配列がある場合の挙動は異なります。\n\n```js {3,7,11}\nuseEffect(() => {\n  // 毎回のレンダー後に実行される\n});\n\nuseEffect(() => {\n  // マウント時（コンポーネント出現時）のみ実行される\n}, []);\n\nuseEffect(() => {\n  // マウント時と、a か b の値が前回のレンダーより変わった場合に実行される\n}, [a, b]);\n```\n\n次のステップで、「マウント」とは何かを詳しく見ていきます。\n\n</Pitfall>\n\n<DeepDive>\n\n#### なぜ ref は依存配列にないのか？ {/*why-was-the-ref-omitted-from-the-dependency-array*/}\n\nこのエフェクトでは、`ref` と `isPlaying` の両方が使用されていますが、依存値として宣言されているのは `isPlaying` のみです。\n\n```js {9}\nfunction VideoPlayer({ src, isPlaying }) {\n  const ref = useRef(null);\n  useEffect(() => {\n    if (isPlaying) {\n      ref.current.play();\n    } else {\n      ref.current.pause();\n    }\n  }, [isPlaying]);\n```\n\nこれは、`ref` オブジェクトが*毎回同一のもの*だからです。React は、同じ `useRef` コールから[常に同じオブジェクトが返される](/reference/react/useRef#returns)ことを保証しています。これが変更されることはないため、それ自体がエフェクトの再実行を引き起こすことも決してありません。したがって、それを含めるかどうかは問題となりません。ただし含めても問題ありません：\n\n```js {9}\nfunction VideoPlayer({ src, isPlaying }) {\n  const ref = useRef(null);\n  useEffect(() => {\n    if (isPlaying) {\n      ref.current.play();\n    } else {\n      ref.current.pause();\n    }\n  }, [isPlaying, ref]);\n```\n\n`useState` によって返される [`set` 関数](/reference/react/useState#setstate)も毎回全く同一のものであるため、依存配列から省略されることがよくあります。ある依存値を省略してもリンタのエラーが出ない場合は、それを行っても安全です。\n\n毎回同一である値を依存配列から省略できるのは、リンタがそのオブジェクトが毎回同一であると「判断できる」場合のみです。例えば、`ref` が親コンポーネントから渡される場合は、依存配列にそれを指定する必要があります。親コンポーネントが常に同じ ref を渡すのか、それとも条件付きで違う ref から 1 つ選んで渡すのか、知ることはできないのですから、これは良いことです。あなたのエフェクトは、どの ref が渡されるかに確かに*依存している*ことになります。\n\n</DeepDive>\n\n### ステップ 3：必要に応じてクリーンアップを追加する {/*step-3-add-cleanup-if-needed*/}\n\n別の例を考えてみましょう。表示されたときにチャットサーバに接続する必要がある `ChatRoom` コンポーネントを作成しているとします。`connect()` および `disconnect()` というメソッドを持つオブジェクトを返す `createConnection()` という API があります。コンポーネントがユーザに表示されている間、接続を維持するにはどうすればよいでしょうか？\n\nまず、エフェクトのロジックを書いてみましょう。\n\n```js\nuseEffect(() => {\n  const connection = createConnection();\n  connection.connect();\n});\n```\n\n再レンダー後に毎回チャットに接続するのは遅いため、依存配列を追加します。\n\n```js {4}\nuseEffect(() => {\n  const connection = createConnection();\n  connection.connect();\n}, []);\n```\n\n**エフェクト内のコードは props や state を使用していないため、依存配列は `[]`（空）です。こうすると React に、コンポーネントが「マウント」される、つまり画面に初めて表示されるときにのみこのコードを実行するよう指示することになります**。\n\nこのコードを実行してみましょう。\n\n<Sandpack>\n\n```js\nimport { useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nexport default function ChatRoom() {\n  useEffect(() => {\n    const connection = createConnection();\n    connection.connect();\n  }, []);\n  return <h1>Welcome to the chat!</h1>;\n}\n```\n\n```js src/chat.js\nexport function createConnection() {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected.');\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\n```\n\n</Sandpack>\n\nこのエフェクトはマウント時にのみ実行されるため、コンソールに `\"✅ Connecting...\"` が 1 回だけ表示されると思うかもしれません。**しかし、コンソールを確認すると、`\"✅ Connecting...\"` が 2 回表示されているはずです。なぜこれが起こるのでしょうか？**\n\n`ChatRoom` コンポーネントが、さまざまな画面がある大規模なアプリの一部であると想像してみてください。ユーザは `ChatRoom` ページからナビゲーションを始めます。コンポーネントがマウントされ、`connection.connect()` が呼び出されます。次に、ユーザが別の画面、例えば設定ページに移動します。`ChatRoom` コンポーネントがアンマウントされます。最後に、ユーザが戻るボタンをクリックし、`ChatRoom` が再びマウントされます。これにより 2 つ目の接続が設定されます…が、最初の接続は破棄されていません！ ユーザがアプリ内で移動するたびに、接続がどんどん積み重なっていくことになります。\n\nこのようなバグは、手動での徹底的なテストがないと見逃しやすいものです。これらをすばやく見つけるために、開発環境では React は、初回マウント直後にすべてのコンポーネントを一度だけ再マウントします。\n\n`\"✅ Connecting...\"` のログが 2 回表示されることで、実際の問題に気付くことができます。つまり、コンポーネントがアンマウントされたときに接続を閉じるコードがないということです。\n\nこの問題を解決するには、エフェクトから*クリーンアップ関数*を返すようにします。\n\n```js {4-6}\n  useEffect(() => {\n    const connection = createConnection();\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, []);\n```\n\nReact は、エフェクトが再度実行される前に毎回クリーンアップ関数を呼び出し、コンポーネントがアンマウントされる（削除される）ときにも最後の 1 回の呼び出しを行います。クリーンアップ関数が実装された場合、どのような動作になるか見てみましょう。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nexport default function ChatRoom() {\n  useEffect(() => {\n    const connection = createConnection();\n    connection.connect();\n    return () => connection.disconnect();\n  }, []);\n  return <h1>Welcome to the chat!</h1>;\n}\n```\n\n```js src/chat.js\nexport function createConnection() {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected.');\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\n```\n\n</Sandpack>\n\nこれで、開発中に 3 つのコンソールログが表示されるようになります。\n\n1. `\"✅ Connecting...\"`\n2. `\"❌ Disconnected.\"`\n3. `\"✅ Connecting...\"`\n\n**これが開発環境での正しい動作です**。コンポーネントを再マウントすることで、React はページを離れて戻ってきてもコードが壊れないことを確認します。切断してからの再接続は、まさに起こるべきことなのです！ クリーンアップがうまく実装されていれば、エフェクトを 1 回だけ実行することと、クリーンアップしてから再度実行することとの間に、ユーザにとって目に見える違いはないはずです。開発中にコードのバグを探るために、接続/切断の呼び出しペアが 1 つ余分にあるだけです。これは正常ですので、消そうとしないでください！\n\n**本番環境では、`\"✅ Connecting...\"` が 1 回だけ表示されます**。コンポーネントの再マウントは、クリーンアップが必要なエフェクトを見つけるために開発中にのみ行われます。[Strict Mode](/reference/react/StrictMode) を外すことで、開発時専用のこの挙動をオフにすることができますが、オンにしておくことをお勧めします。これにより、上記のような多くのバグを見つけることができます。\n\n## 開発環境で 2 回発生するエフェクトへの正しい対応 {/*how-to-handle-the-effect-firing-twice-in-development*/}\n\nReact は、開発中に意図的にコンポーネントを再マウントして、前述の例のようなバグを見つけます。**ここでの正しい質問は「エフェクトを 1 回だけ実行する方法」ではなく「再マウントされても正しく動作するようエフェクトを修正する方法」です**。\n\n通常、答えはクリーンアップ関数を実装することです。クリーンアップ関数は、エフェクトが行っていたことを停止または元に戻すべきです。大事なルールとして、ユーザは（本番環境でのように）エフェクトが一度だけ実行される場合と、（開発環境でのように）*セットアップ → クリーンアップ → セットアップ*と続く場合との違いを、見分けることができないようにするべきです。\n\nほとんどのエフェクトは、以下の一般的なパターンのいずれかに合致します。\n\n<Pitfall>\n\n#### ref を使ってエフェクトの実行を抑止しようとしない {/*dont-use-refs-to-prevent-effects-from-firing*/}\n\n開発時にエフェクトが 2 回実行されるのを抑止しようとして `ref` を使おうとするのはよくある誤りです。上記のバグを `useRef` を使って以下のように「修正」しようと思うかもしれません。\n\n```js {1,3-4}\n  const connectionRef = useRef(null);\n  useEffect(() => {\n    // 🚩 This wont fix the bug!!!\n    if (!connectionRef.current) {\n      connectionRef.current = createConnection();\n      connectionRef.current.connect();\n    }\n  }, []);\n```\n\nこれにより `\"✅ Connecting...\"` が開発時に 1 回だけ表示されるようにはなるのですが、これでバグが修正されるわけではありません。\n\nユーザが他のページに移動しても、接続は閉じられず、戻ってきたときに新しい接続が作成されます。ユーザがアプリ内を移動するたびに接続が積み重なっていきます。「修正」前と同じ状況です。\n\nこのバグを修正するには、エフェクトを一度だけ実行するようにするだけでは不十分です。エフェクトは再マウント時にも機能する必要があり、接続は上記の解決策のようにクリーンアップされる必要があるのです。\n\n以下の例を参考に、一般的なパターンの処理方法を確認してください。\n\n</Pitfall>\n\n### React 以外のウィジェットを制御する {/*controlling-non-react-widgets*/}\n\n時に、React で書かれていない UI ウィジェットを追加したい場合があります。例えば、ページに地図コンポーネントを追加しようとしているとします。`setZoomLevel()` メソッドがあり、React のコード内の `zoomLevel` という state 変数と同期させたいとします。エフェクトは次のようになります。\n\n```js\nuseEffect(() => {\n  const map = mapRef.current;\n  map.setZoomLevel(zoomLevel);\n}, [zoomLevel]);\n```\n\nこの場合、クリーンアップは必要ありません。開発環境では React はこのエフェクトを 2 回呼び出しますが、同じ値で `setZoomLevel` を 2 回呼び出しても何も起きません。わずかに遅くはなるかもしれませんが、本番環境では無用に再マウントされることはないので、問題はありません。\n\nAPI によっては、連続して 2 回呼び出すことができない場合があります。例えば、組み込みの [`<dialog>`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement) 要素の [`showModal`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal) メソッドは、2 回呼び出すと例外が発生します。クリーンアップ関数を実装し、ダイアログを閉じるようにしてください。\n\n```js {4}\nuseEffect(() => {\n  const dialog = dialogRef.current;\n  dialog.showModal();\n  return () => dialog.close();\n}, []);\n```\n\n開発中、エフェクトは `showModal()` を呼び出し、すぐに `close()` を呼び出し、再び `showModal()` を呼び出します。ユーザに見える動作としては、本番環境で `showModal()` を 1 回だけ呼び出すのと同じになります。\n\n### イベントのリッスン {/*subscribing-to-events*/}\n\nエフェクトが何かをリッスンしている場合、クリーンアップ関数はそれを解除する必要があります。\n\n```js {6}\nuseEffect(() => {\n  function handleScroll(e) {\n    console.log(window.scrollX, window.scrollY);\n  }\n  window.addEventListener('scroll', handleScroll);\n  return () => window.removeEventListener('scroll', handleScroll);\n}, []);\n```\n\n開発環境では、エフェクトは `addEventListener()` を呼び出し、すぐに `removeEventListener()` を呼び出し、同じハンドラで再び `addEventListener()` を呼び出します。そのため、一度にアクティブなリスナは 1 つだけです。ユーザに見える動作としては、本番環境で `addEventListener()` を 1 回だけ呼び出すのと同じになります。\n\n### アニメーションのトリガ {/*triggering-animations*/}\n\nエフェクトが何かをアニメーションで表示する場合、クリーンアップ関数はアニメーションを初期値にリセットする必要があります。\n\n```js {4-6}\nuseEffect(() => {\n  const node = ref.current;\n  node.style.opacity = 1; // Trigger the animation\n  return () => {\n    node.style.opacity = 0; // Reset to the initial value\n  };\n}, []);\n```\n\n開発中は、opacity が `1` にセットされ、次に `0` にセットされ、再び `1` にセットされます。ユーザに見える動作としては、本番環境で直接 `1` に設定される場合と同じになるべきです。トゥイーンに対応したサードパーティのアニメーションライブラリを使用している場合、クリーンアップ関数はタイムラインを初期状態にリセットする必要があります。\n\n### データのフェッチ {/*fetching-data*/}\n\nエフェクトが何かをフェッチ（fetch, 取得）する場合、クリーンアップ関数は、フェッチを[中止する](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)か、その結果を無視する必要があります。\n\n```js {2,6,13-15}\nuseEffect(() => {\n  let ignore = false;\n\n  async function startFetching() {\n    const json = await fetchTodos(userId);\n    if (!ignore) {\n      setTodos(json);\n    }\n  }\n\n  startFetching();\n\n  return () => {\n    ignore = true;\n  };\n}, [userId]);\n```\n\nすでに発生したネットワークリクエストをなかったことにはできませんが、クリーンアップ関数は、*もはや重要ではなくなった*フェッチがアプリケーションに影響を与え続けないようにする必要があります。`userId` が `'Alice'` から `'Bob'` に変わった場合、クリーンアップは、`'Alice'` のレスポンスが `'Bob'` の後に到着した場合に無視されるようにします。\n\n**開発環境では、ネットワークタブに 2 つのフェッチが表示されます**。これには何の問題もありません。上記のアプローチでは、最初のエフェクトがすぐにクリーンアップされるため、`ignore` 変数のコピーが `true` に設定されます。そのため、余分なリクエストがあっても、`if (!ignore)` チェックのおかげで state に影響を与えません。\n\n**本番環境では、リクエストは 1 回だけになります**。開発環境の 2 つ目のリクエストが気になる場合は、リクエストの重複を排除し、コンポーネント間でレスポンスをキャッシュするソリューションを使用することが最善の方法です。\n\n```js\nfunction TodoList() {\n  const todos = useSomeDataLibrary(`/api/user/${userId}/todos`);\n  // ...\n```\n\nこれにより、開発体験が向上するだけでなく、アプリケーションの動作も高速化されます。例えば、ユーザが戻るボタンを押しても、キャッシュされたデータがあるため、再びデータをロードするのを待つ必要がありません。このようなキャッシュを自分で構築することもできますし、エフェクトでの手動フェッチを行う多数のライブラリの選択肢からいずれかを使用することもできます。\n\n<DeepDive>\n\n#### エフェクトでのデータ取得に代わる良い方法は？ {/*what-are-good-alternatives-to-data-fetching-in-effects*/}\n\n特に完全にクライアントサイドのアプリにおいては、エフェクトの中で `fetch` コールを書くことは[データフェッチの一般的な方法](https://www.robinwieruch.de/react-hooks-fetch-data/)です。しかし、これは非常に手作業頼りのアプローチであり、大きな欠点があります。\n\n- **エフェクトはサーバ上では動作しません**。これは、サーバレンダリングされた初期 HTML にはデータのないローディング中という表示のみが含まれてしまうことを意味します。クライアントのコンピュータは、すべての JavaScript をダウンロードし、アプリをレンダーした後になってやっと、今度はデータを読み込む必要もあるということに気付くことになります。これはあまり効率的ではありません。\n- **エフェクトで直接データフェッチを行うと、「ネットワークのウォーターフォール（滝）」を作成しやすくなります**。親コンポーネントをレンダーし、それが何かデータをフェッチし、それによって子コンポーネントをレンダーし、今度はそれが何かデータのフェッチを開始する、といった具合です。ネットワークがあまり速くない場合、これはすべてのデータを並行で取得するよりもかなり遅くなります。\n- **エフェクト内で直接データフェッチするということはおそらくデータをプリロードもキャッシュもしていないということです**。例えば、コンポーネントがアンマウントされた後に再びマウントされる場合、データを再度取得する必要があります。\n- **人にとって書きやすいコードになりません**。[競合状態](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect)のようなバグを起こさないように `fetch` コールを書こうとすると、かなりのボイラープレートコードが必要です。\n\n上記の欠点は、マウント時にデータをフェッチするのであれば、React に限らずどのライブラリを使う場合でも当てはまる内容です。ルーティングと同様、データフェッチの実装も上手にやろうとすると一筋縄ではいきません。私たちは以下のアプローチをお勧めします。\n\n- **[フレームワーク](/learn/creating-a-react-app#full-stack-frameworks)を使用している場合、組み込みのデータフェッチ機構を使用してください**。モダンな React フレームワークには、効率的で上記の欠点がないデータフェッチ機構が統合されています。\n- **それ以外の場合は、クライアントサイドキャッシュの使用や構築を検討してください**。一般的なオープンソースのソリューションには、[TanStack Query](https://tanstack.com/query/latest)、[useSWR](https://swr.vercel.app/)、および [React Router 6.4+](https://beta.reactrouter.com/en/main/start/overview) が含まれます。自分でソリューションを構築することもできます。その場合、エフェクトを内部で使用しつつ、リクエストの重複排除、レスポンスのキャッシュ、ネットワークのウォーターフォールを回避するためのロジック（データのプリロードやルーティング部へのデータ要求の巻き上げ）を追加することになります。\n\nこれらのアプローチがどちらも適合しない場合は、引き続きエフェクト内で直接データをフェッチすることができます。\n\n</DeepDive>\n\n### アナリティクスログの送信 {/*sending-analytics*/}\n\nページ訪問時にアナリティクスイベントを送信する次のコードを考えてみましょう：\n\n```js\nuseEffect(() => {\n  logVisit(url); // Sends a POST request\n}, [url]);\n```\n\n開発中には、`logVisit` が各 URL ごとに 2 回呼び出されるため、それを修正しようと試みるかもしれません。**このコードはそのままにしておくことをお勧めします**。先の例と同様に、1 度実行することと 2 度実行することとの間に*ユーザに見える*挙動の違いはありません。実用的な観点からは、開発マシンからのログのせいで本番の計測結果がおかしくなることは望まないため、`logVisit` 関数は開発環境では何も行わないはずです。あなたがファイルを保存するたびにコンポーネントは再マウントされるのですから、開発環境において余分な訪問が記録されることはいずれにせよ避けられません。\n\n**本番環境では訪問ログの重複は起こりません**。\n\n送信しているアナリティクスイベントをデバッグするには、アプリを（本番モードで実行される）ステージング環境にデプロイするか、一時的に [Strict Mode](/reference/react/StrictMode) を外して開発環境専用の再マウントチェックを止めることができます。また、エフェクトの代わりにルート変更のイベントハンドラからアナリティクスログを送信することもできます。より正確な分析のために[交差オブザーバ](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)を用いれば、どのコンポーネントがビューポートにありどれだけの時間表示されているかを追跡するのに役立ちます。\n\n### アプリケーション初期化はエフェクトではない {/*not-an-effect-initializing-the-application*/}\n\nアプリケーションの起動時に一度だけ実行されるべきロジックがあります。そのようなものはコンポーネントの外に置くことができます：\n\n```js {2-3}\nif (typeof window !== 'undefined') { // Check if we're running in the browser.\n  checkAuthToken();\n  loadDataFromLocalStorage();\n}\n\nfunction App() {\n  // ...\n}\n```\n\nこれにより、そのようなロジックはブラウザがページを読み込んだ後に一度だけ実行されることが保証されます。\n\n### 商品購入はエフェクトではない {/*not-an-effect-buying-a-product*/}\n\nクリーンアップ関数を書いても、エフェクトを 2 回実行することによるユーザに見える影響を防ぐ方法がないことがあります。例えば、エフェクトが商品の購入のような POST リクエストを送信する場合です：\n\n```js {2-3}\nuseEffect(() => {\n  // 🔴 Wrong: This Effect fires twice in development, exposing a problem in the code.\n  fetch('/api/buy', { method: 'POST' });\n}, []);\n```\n\n同じ商品を 2 度買いたいわけではありません。しかしまさにそれが、そもそもエフェクトにこのロジックを入れてはいけない理由でもあるのです。ユーザが別のページに行ってから戻るボタンを押した場合、どうなるでしょう？ あなたのエフェクトは再び実行されてしまいます。ユーザはページを*訪れる*たびに製品を買いたいわけではなく、*クリック*して購入ボタンを押したときに買いたいのです。\n\n購入はレンダーによって引き起こされるのではなく、特定のユーザ操作によって引き起こされるものです。ユーザがボタンを押したときにのみ実行する必要があります。**エフェクトを削除し、`/api/buy` リクエストを購入ボタンのイベントハンドラに移動してください**。\n\n```js {2-3}\n  function handleClick() {\n    // ✅ Buying is an event because it is caused by a particular interaction.\n    fetch('/api/buy', { method: 'POST' });\n  }\n```\n\n**これで分かるのは、再マウントでアプリケーションのロジックが壊れるなら、通常それは既存のバグが明らかになったのだということです**。ユーザの視点から見ると、ページを訪れることと、ページを訪れてリンクをクリックしてから戻るボタンを押して元のページをもう一度見ることとの間に、違いがあってはいけません。React は、開発環境でコンポーネントを 1 度再マウントすることで、この原則に従っていることを確認します。\n\n## ここまでのまとめ {/*putting-it-all-together*/}\n\n以下のプレイグラウンドは、エフェクトの動作について「感覚を掴む」のに役立ちます。\n\nこの例では、[`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout) を使用して、エフェクトが実行されてから 3 秒後に入力テキストを含むコンソールログが表示されるようにスケジュールしています。クリーンアップ関数は、保留中のタイムアウトをキャンセルします。まず、「コンポーネントをマウント」ボタンを押してください。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nfunction Playground() {\n  const [text, setText] = useState('a');\n\n  useEffect(() => {\n    function onTimeout() {\n      console.log('⏰ ' + text);\n    }\n\n    console.log('🔵 Schedule \"' + text + '\" log');\n    const timeoutId = setTimeout(onTimeout, 3000);\n\n    return () => {\n      console.log('🟡 Cancel \"' + text + '\" log');\n      clearTimeout(timeoutId);\n    };\n  }, [text]);\n\n  return (\n    <>\n      <label>\n        What to log:{' '}\n        <input\n          value={text}\n          onChange={e => setText(e.target.value)}\n        />\n      </label>\n      <h1>{text}</h1>\n    </>\n  );\n}\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Unmount' : 'Mount'} the component\n      </button>\n      {show && <hr />}\n      {show && <Playground />}\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n最初に 3 つのログが表示されます。`Schedule \"a\" log`、`Cancel \"a\" log`、そして再び `Schedule \"a\" log` です。3 秒後には、`a` というログも表示されます。前述のように、スケジュール・キャンセルのペアが 1 回余分に出てくるのは、React が開発中にコンポーネントを一度再マウントして、クリーンアップがうまく実装されていることを確認するためです。\n\n次に、入力欄に `abc` と入力します。十分に早く行えば、`Schedule \"ab\" log`、`Cancel \"ab\" log`、`Schedule \"abc\" log` の順でログが表示されます。**React は常に、次のレンダーのエフェクトの前に、前のレンダーのエフェクトをクリーンアップします**。したがって入力欄に素早く入力しても、同時にスケジュールされるタイムアウトは最大でも 1 つです。入力欄を何度か編集して、コンソールを見て、エフェクトがどのようにクリーンアップされるか、感覚を掴んでください。\n\n入力欄に何か入力してからすぐに \"Unmount the component\" ボタンを押してみてください。アンマウントによって、最後のレンダーのエフェクトがクリーンアップされることに気付くでしょう。この場合は、最後のタイムアウトが、発火する前にクリアされます。\n\n最後に、上のコンポーネントを編集して、クリーンアップ関数をコメントアウトしてタイムアウトがキャンセルされないようにしてみてください。`abcde` を素早く入力してみてください。3 秒後に何が起こると思いますか？ タイムアウト内の `console.log(text)` は、*最新の* `text`、つまり `abcde` というログを 5 回生成するのでしょうか？ あなたの直観を確かめるため実際に試してみましょう！\n\n3 秒後に、`abcde` ログが 5 回表示されるのではなく、ログが順番に表示される（`a`、`ab`、`abc`、`abcd`、`abcde`）はずです。**各エフェクトは、対応するレンダーからの `text` 値を「キャプチャ」します**。`text` の state が変更されたとしても、`text = 'ab'` だったレンダーからのエフェクトには常に `'ab'` という値が見えることになります。言い換えると、各レンダーからのエフェクトは互いに隔離されています。これがどのように動作するか興味がある場合は、[クロージャ](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)について学びましょう。\n\n<DeepDive>\n\n#### 個々のレンダーに別のエフェクトがある {/*each-render-has-its-own-effects*/}\n\n`useEffect` を、レンダー出力に何らかの振る舞いを「付随」させるものであると考えることができます。以下のエフェクトを考えてみましょう。\n\n```js\nexport default function ChatRoom({ roomId }) {\n  useEffect(() => {\n    const connection = createConnection(roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n\n  return <h1>Welcome to {roomId}!</h1>;\n}\n```\n\nユーザがアプリを操作する際に、具体的に何が起こるか見てみましょう。\n\n#### 初期レンダー {/*initial-render*/}\n\nユーザは `<ChatRoom roomId=\"general\" />` を訪れます。`roomId` を `'general'` であると[頭の中で置き換えて](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time)みましょう。\n\n```js\n  // JSX for the first render (roomId = \"general\")\n  return <h1>Welcome to general!</h1>;\n```\n\n**エフェクトもまた、レンダー出力の一部です**。最初のレンダーのエフェクトは次のようになります。\n\n```js\n  // Effect for the first render (roomId = \"general\")\n  () => {\n    const connection = createConnection('general');\n    connection.connect();\n    return () => connection.disconnect();\n  },\n  // Dependencies for the first render (roomId = \"general\")\n  ['general']\n```\n\nReact はこのエフェクトを実行し、`'general'` チャットルームに接続します。\n\n#### 同じ依存値での再レンダー {/*re-render-with-same-dependencies*/}\n\n`<ChatRoom roomId=\"general\" />` が再レンダーされるとしましょう。JSX の出力は同じです。\n\n```js\n  // JSX for the second render (roomId = \"general\")\n  return <h1>Welcome to general!</h1>;\n```\n\nReact はレンダー出力が変更されていないことを認識するため、DOM を更新しません。\n\n2 回目のレンダーからのエフェクトは以下のようになります。\n\n```js\n  // Effect for the second render (roomId = \"general\")\n  () => {\n    const connection = createConnection('general');\n    connection.connect();\n    return () => connection.disconnect();\n  },\n  // Dependencies for the second render (roomId = \"general\")\n  ['general']\n```\n\nReact は 2 回目のレンダーからの `['general']` と、1 回目のレンダーからの `['general']` を比較します。**すべての依存値が同じであるため、React は 2 回目のレンダーからのエフェクトを*無視*します**。エフェクトは呼び出されません。\n\n#### 異なる依存値での再レンダー {/*re-render-with-different-dependencies*/}\n\n次に、ユーザが `<ChatRoom roomId=\"travel\" />` を訪れます。このとき、コンポーネントは異なる JSX を返します。\n\n```js\n  // JSX for the third render (roomId = \"travel\")\n  return <h1>Welcome to travel!</h1>;\n```\n\nReact は DOM を更新して、`\"Welcome to general\"` を `\"Welcome to travel\"` に変更します。\n\n3 回目のレンダーからのエフェクトは以下のようになります。\n\n```js\n  // Effect for the third render (roomId = \"travel\")\n  () => {\n    const connection = createConnection('travel');\n    connection.connect();\n    return () => connection.disconnect();\n  },\n  // Dependencies for the third render (roomId = \"travel\")\n  ['travel']\n```\n\nReact は 3 回目のレンダーからの `['travel']` と、2 回目のレンダーからの `['general']` を比較します。1 つの依存値が異なります。`Object.is('travel', 'general')` は `false` です。このエフェクトはスキップできません。\n\n**React が 3 回目のレンダーからのエフェクトを適用する前に、最後に実行されたエフェクトをクリーンアップする必要があります**。2 回目のレンダーのエフェクトはスキップされたため、React は 1 回目のレンダーのエフェクトをクリーンアップする必要があります。上にスクロールして 1 回目のレンダーを見返すと、そのクリーンアップコードは `createConnection('general')` で作成された接続に対して `disconnect()` を呼び出すことがわかります。これにより、アプリは `'general'` チャットルームから切断されます。\n\nその後、React は 3 回目のレンダーのエフェクトを実行します。これにより、`'travel'` チャットルームに接続されます。\n\n#### アンマウント {/*unmount*/}\n\n最後に、ユーザがページから出て、`ChatRoom` コンポーネントがアンマウントされるとしましょう。React は最後のエフェクトのクリーンアップ関数を実行します。最後のエフェクトは 3 回目のレンダーからのものでした。3 回目のレンダーのクリーンアップは、`createConnection('travel')` の接続を破棄します。そのため、アプリは `'travel'` ルームから切断されます。\n\n#### 開発環境専用の挙動 {/*development-only-behaviors*/}\n\n[Strict Mode](/reference/react/StrictMode) がオンの場合、React はマウント後にすべてのコンポーネントを一度再マウントします（state と DOM は保持されます）。これは、[クリーンアップが必要なエフェクトを見つけるのに役立ちます](#step-3-add-cleanup-if-needed)し、競合状態 (race condition) のようなバグが早期に見つかるようにもします。さらに、React は開発中にあなたがファイルを保存するたびにエフェクトを再マウントします。これらの挙動は開発環境でのみ起こります。\n\n</DeepDive>\n\n<Recap>\n\n- イベントとは異なり、エフェクトは特定のユーザ操作ではなく、レンダー自体によって引き起こされる。\n- エフェクトを使い、コンポーネントを外部システム（サードパーティ API、ネットワークなど）と同期させることができる。\n- デフォルトでは、エフェクトは毎回のレンダー（初回も含む）の後に実行される。\n- すべての依存値が前回のレンダー時と同じ値である場合、React はエフェクトをスキップする。\n- 依存値は「選ぶ」類のものではない。それはエフェクト内のコードによって決定される。\n- 空の依存配列 (`[]`) は、コンポーネントが「マウント」される、つまり画面に追加されることに対応する。\n- Strict Mode では、React はコンポーネントを 2 回マウント（開発環境のみ！）して、エフェクトのストレステストを行う。\n- エフェクトが再マウントにより壊れる場合、クリーンアップ関数を実装する必要がある。\n- React は、次のエフェクトが実行される前とアンマウント中に、クリーンアップ関数を呼び出す。\n\n</Recap>\n\n<Challenges>\n\n#### マウント時にフィールドにフォーカス {/*focus-a-field-on-mount*/}\n\nこの例では、フォームが `<MyInput />` コンポーネントをレンダーします。\n\n入力フィールドの [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) メソッドを使って、`MyInput` が画面に表示されたときに自動的にフォーカスが当たるようにしてください。すでにコメントアウトされた実装がありますが、これはうまく動作しません。なぜ動作しないのかを理解し、修正してください。（`autoFocus` 属性をご存じの場合でも、今はこれが存在しないことにしてください。同じ機能をゼロから再実装しましょう。）\n\n<Sandpack>\n\n```js src/MyInput.js active\nimport { useEffect, useRef } from 'react';\n\nexport default function MyInput({ value, onChange }) {\n  const ref = useRef(null);\n\n  // TODO: This doesn't quite work. Fix it.\n  // ref.current.focus()\n\n  return (\n    <input\n      ref={ref}\n      value={value}\n      onChange={onChange}\n    />\n  );\n}\n```\n\n```js src/App.js hidden\nimport { useState } from 'react';\nimport MyInput from './MyInput.js';\n\nexport default function Form() {\n  const [show, setShow] = useState(false);\n  const [name, setName] = useState('Taylor');\n  const [upper, setUpper] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShow(s => !s)}>{show ? 'Hide' : 'Show'} form</button>\n      <br />\n      <hr />\n      {show && (\n        <>\n          <label>\n            Enter your name:\n            <MyInput\n              value={name}\n              onChange={e => setName(e.target.value)}\n            />\n          </label>\n          <label>\n            <input\n              type=\"checkbox\"\n              checked={upper}\n              onChange={e => setUpper(e.target.checked)}\n            />\n            Make it uppercase\n          </label>\n          <p>Hello, <b>{upper ? name.toUpperCase() : name}</b></p>\n        </>\n      )}\n    </>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-top: 20px;\n  margin-bottom: 20px;\n}\n\nbody {\n  min-height: 150px;\n}\n```\n\n</Sandpack>\n\n\nあなたの答えが動作することを確認するには、\"Show form\" を押して、入力がフォーカスされる（ハイライトされ、カーソルが内部に配置される）ことを確認してください。\"Hide form\" を押してから再度 \"Show form\" を押すと、再び入力欄がハイライトされることを確認してください。\n\n`MyInput` は、毎レンダーごとではなくマウント時にのみフォーカスされる必要があります。この動作が正しいことを確認するには、\"Show form\" を押してから、\"Make it uppercase\" チェックボックスを何度か押してみてください。チェックボックスをクリックしても、その上にある入力フィールドにフォーカスが移動しないようにしてください。\n\n<Solution>\n\nレンダー中に `ref.current.focus()` を呼び出すことは、*副作用*になってしまうため間違いです。副作用は、イベントハンドラ内に配置するか、`useEffect` で宣言する必要があります。この場合、副作用は特定のユーザ操作によるものではなく、コンポーネントが表示されることによって*引き起こされる*ため、エフェクトに入れるのが適切です。\n\nこのミスを修正するために、`ref.current.focus()` の呼び出しをエフェクト宣言にラップします。次に、このエフェクトがマウント時にのみ実行されるよう、空の `[]` という依存配列を追加します。\n\n<Sandpack>\n\n```js src/MyInput.js active\nimport { useEffect, useRef } from 'react';\n\nexport default function MyInput({ value, onChange }) {\n  const ref = useRef(null);\n\n  useEffect(() => {\n    ref.current.focus();\n  }, []);\n\n  return (\n    <input\n      ref={ref}\n      value={value}\n      onChange={onChange}\n    />\n  );\n}\n```\n\n```js src/App.js hidden\nimport { useState } from 'react';\nimport MyInput from './MyInput.js';\n\nexport default function Form() {\n  const [show, setShow] = useState(false);\n  const [name, setName] = useState('Taylor');\n  const [upper, setUpper] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShow(s => !s)}>{show ? 'Hide' : 'Show'} form</button>\n      <br />\n      <hr />\n      {show && (\n        <>\n          <label>\n            Enter your name:\n            <MyInput\n              value={name}\n              onChange={e => setName(e.target.value)}\n            />\n          </label>\n          <label>\n            <input\n              type=\"checkbox\"\n              checked={upper}\n              onChange={e => setUpper(e.target.checked)}\n            />\n            Make it uppercase\n          </label>\n          <p>Hello, <b>{upper ? name.toUpperCase() : name}</b></p>\n        </>\n      )}\n    </>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-top: 20px;\n  margin-bottom: 20px;\n}\n\nbody {\n  min-height: 150px;\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n#### 条件付きでフィールドにフォーカス {/*focus-a-field-conditionally*/}\n\nこのフォームは `<MyInput />` コンポーネントを 2 つレンダーします。\n\n\"Show form\" を押すと、2 番目のフィールドが自動的にフォーカスされることに注意してください。これは、両方の `<MyInput />` コンポーネントが内部のフィールドにフォーカスしようとするためです。2 つの入力フィールドに連続して `focus()` を呼び出すと、最後に呼んだ方が常に「勝ち」になります。\n\n最初のフィールドにフォーカスしたいとしましょう。最初の `MyInput` コンポーネントは、`shouldFocus` という真偽値の props を `true` で受け取ります。`MyInput` が受け取った `shouldFocus` が `true` の場合にのみ `focus()` が呼び出されるよう、ロジックを変更してください。\n\n<Sandpack>\n\n```js src/MyInput.js active\nimport { useEffect, useRef } from 'react';\n\nexport default function MyInput({ shouldFocus, value, onChange }) {\n  const ref = useRef(null);\n\n  // TODO: call focus() only if shouldFocus is true.\n  useEffect(() => {\n    ref.current.focus();\n  }, []);\n\n  return (\n    <input\n      ref={ref}\n      value={value}\n      onChange={onChange}\n    />\n  );\n}\n```\n\n```js src/App.js hidden\nimport { useState } from 'react';\nimport MyInput from './MyInput.js';\n\nexport default function Form() {\n  const [show, setShow] = useState(false);\n  const [firstName, setFirstName] = useState('Taylor');\n  const [lastName, setLastName] = useState('Swift');\n  const [upper, setUpper] = useState(false);\n  const name = firstName + ' ' + lastName;\n  return (\n    <>\n      <button onClick={() => setShow(s => !s)}>{show ? 'Hide' : 'Show'} form</button>\n      <br />\n      <hr />\n      {show && (\n        <>\n          <label>\n            Enter your first name:\n            <MyInput\n              value={firstName}\n              onChange={e => setFirstName(e.target.value)}\n              shouldFocus={true}\n            />\n          </label>\n          <label>\n            Enter your last name:\n            <MyInput\n              value={lastName}\n              onChange={e => setLastName(e.target.value)}\n              shouldFocus={false}\n            />\n          </label>\n          <p>Hello, <b>{upper ? name.toUpperCase() : name}</b></p>\n        </>\n      )}\n    </>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-top: 20px;\n  margin-bottom: 20px;\n}\n\nbody {\n  min-height: 150px;\n}\n```\n\n</Sandpack>\n\nあなたの答えをテストするには、\"Show form\" と \"Hide form\" を何度か押してみます。フォームが表示されると、*最初の*入力欄がフォーカスされるようにしてください。これは、親コンポーネントが 1 番目の入力欄を `shouldFocus={true}` で、2 番目を `shouldFocus={false}` でレンダーしているためです。また、両方の入力フィールドが動作し、入力が行えることも確認してください。\n\n<Hint>\n\n条件付きでエフェクトを宣言することはできませんが、エフェクトに条件付きのロジックを含めることは可能です。\n\n</Hint>\n\n<Solution>\n\n条件付きのロジックはエフェクトの中に入れます。エフェクトの中で `shouldFocus` を使用しているため、依存関係として `shouldFocus` を指定する必要があります。（これは、ある入力欄の `shouldFocus` が `false` から `true` に変わった場合、フォーカスが当たるということでもあります。）\n\n<Sandpack>\n\n```js src/MyInput.js active\nimport { useEffect, useRef } from 'react';\n\nexport default function MyInput({ shouldFocus, value, onChange }) {\n  const ref = useRef(null);\n\n  useEffect(() => {\n    if (shouldFocus) {\n      ref.current.focus();\n    }\n  }, [shouldFocus]);\n\n  return (\n    <input\n      ref={ref}\n      value={value}\n      onChange={onChange}\n    />\n  );\n}\n```\n\n```js src/App.js hidden\nimport { useState } from 'react';\nimport MyInput from './MyInput.js';\n\nexport default function Form() {\n  const [show, setShow] = useState(false);\n  const [firstName, setFirstName] = useState('Taylor');\n  const [lastName, setLastName] = useState('Swift');\n  const [upper, setUpper] = useState(false);\n  const name = firstName + ' ' + lastName;\n  return (\n    <>\n      <button onClick={() => setShow(s => !s)}>{show ? 'Hide' : 'Show'} form</button>\n      <br />\n      <hr />\n      {show && (\n        <>\n          <label>\n            Enter your first name:\n            <MyInput\n              value={firstName}\n              onChange={e => setFirstName(e.target.value)}\n              shouldFocus={true}\n            />\n          </label>\n          <label>\n            Enter your last name:\n            <MyInput\n              value={lastName}\n              onChange={e => setLastName(e.target.value)}\n              shouldFocus={false}\n            />\n          </label>\n          <p>Hello, <b>{upper ? name.toUpperCase() : name}</b></p>\n        </>\n      )}\n    </>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-top: 20px;\n  margin-bottom: 20px;\n}\n\nbody {\n  min-height: 150px;\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n#### 2 回実行されるインターバルを修正 {/*fix-an-interval-that-fires-twice*/}\n\nこの `Counter` コンポーネントは、1 秒ごとにインクリメントするカウンタを表示します。コンポーネントはマウント時に、[`setInterval`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval) を呼び出します。これにより、`onTick` が毎秒実行されます。`onTick` 関数はカウンタをインクリメントします。\n\nしかし、1 秒ごとに 1 回ではなく、2 回インクリメントが発生しています。なぜでしょうか？ バグの原因を見つけて修正してください。\n\n<Hint>\n\n`setInterval` はインターバル ID を返し、これを [`clearInterval`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval) に渡すことでインターバルを停止できることを思い出しましょう。\n\n</Hint>\n\n<Sandpack>\n\n```js src/Counter.js active\nimport { useState, useEffect } from 'react';\n\nexport default function Counter() {\n  const [count, setCount] = useState(0);\n\n  useEffect(() => {\n    function onTick() {\n      setCount(c => c + 1);\n    }\n\n    setInterval(onTick, 1000);\n  }, []);\n\n  return <h1>{count}</h1>;\n}\n```\n\n```js src/App.js hidden\nimport { useState } from 'react';\nimport Counter from './Counter.js';\n\nexport default function Form() {\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShow(s => !s)}>{show ? 'Hide' : 'Show'} counter</button>\n      <br />\n      <hr />\n      {show && <Counter />}\n    </>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-top: 20px;\n  margin-bottom: 20px;\n}\n\nbody {\n  min-height: 150px;\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n[Strict Mode](/reference/react/StrictMode) がオンになっている場合（このサイトのサンドボックスもそうです）、React は開発中に各コンポーネントを一度再マウントします。これにより、インターバルが 2 回設定されるため、カウンタが 1 秒ごとに 2 回インクリメントされます。\n\nただし、React のこの挙動がバグの*原因*なのではありません。バグはコードにすでに存在しているのです。React の挙動はバグに気づきやすくするために存在しているだけです。真の原因は、エフェクトが何かを始めるがそれをクリーンアップする方法を返していないことです。\n\nこのコードを修正するには、`setInterval` によって返されるインターバル ID を保存し、[`clearInterval`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval) を使うクリーンアップ関数を実装します。\n\n<Sandpack>\n\n```js src/Counter.js active\nimport { useState, useEffect } from 'react';\n\nexport default function Counter() {\n  const [count, setCount] = useState(0);\n\n  useEffect(() => {\n    function onTick() {\n      setCount(c => c + 1);\n    }\n\n    const intervalId = setInterval(onTick, 1000);\n    return () => clearInterval(intervalId);\n  }, []);\n\n  return <h1>{count}</h1>;\n}\n```\n\n```js src/App.js hidden\nimport { useState } from 'react';\nimport Counter from './Counter.js';\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShow(s => !s)}>{show ? 'Hide' : 'Show'} counter</button>\n      <br />\n      <hr />\n      {show && <Counter />}\n    </>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-top: 20px;\n  margin-bottom: 20px;\n}\n\nbody {\n  min-height: 150px;\n}\n```\n\n</Sandpack>\n\n開発環境では、React はクリーンアップがうまく実装されていることを確認するために、コンポーネントを 1 度再マウントします。そのため、`setInterval` の呼び出しの直後に `clearInterval` が呼ばれ、再び `setInterval` が呼ばれます。プロダクションでは、`setInterval` は 1 回だけ呼ばれます。どちらの場合もユーザに見える動作は同じで、カウンタは 1 秒ごとに 1 回インクリメントされます。\n\n</Solution>\n\n#### エフェクト内のフェッチを修正 {/*fix-fetching-inside-an-effect*/}\n\nこのコンポーネントは、選択された人物の伝記 (bio) を表示します。伝記を読み込むために、マウント時と `person` が変更されたときに非同期関数 `fetchBio(person)` を呼び出します。その非同期関数は、いずれ文字列に解決 (resolve) される [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) を返します。フェッチが完了すると、その文字列を選択ボックスの下に表示するために `setBio` を呼び出します。\n\n<Sandpack>\n\n{/* not the most efficient, but this validation is enabled in the linter only, so it's fine to ignore it here since we know what we're doing */}\n```js {expectedErrors: {'react-compiler': [9]}} src/App.js\nimport { useState, useEffect } from 'react';\nimport { fetchBio } from './api.js';\n\nexport default function Page() {\n  const [person, setPerson] = useState('Alice');\n  const [bio, setBio] = useState(null);\n\n  useEffect(() => {\n    setBio(null);\n    fetchBio(person).then(result => {\n      setBio(result);\n    });\n  }, [person]);\n\n  return (\n    <>\n      <select value={person} onChange={e => {\n        setPerson(e.target.value);\n      }}>\n        <option value=\"Alice\">Alice</option>\n        <option value=\"Bob\">Bob</option>\n        <option value=\"Taylor\">Taylor</option>\n      </select>\n      <hr />\n      <p><i>{bio ?? 'Loading...'}</i></p>\n    </>\n  );\n}\n```\n\n```js src/api.js hidden\nexport async function fetchBio(person) {\n  const delay = person === 'Bob' ? 2000 : 200;\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve('This is ' + person + '’s bio.');\n    }, delay);\n  })\n}\n\n```\n\n</Sandpack>\n\n\nこのコードにはバグがあります。まず \"Alice\" を選択してください。次に \"Bob\" を選択し、すぐに \"Taylor\" を選択します。これを十分に素早く行うと、バグが発生します。Taylor が選択されているのに、下の段落には \"This is Bob's bio.\" と表示されてしまうのです。\n\nなぜこれが起こるのでしょう？ このエフェクト内にあるバグを修正してください。\n\n<Hint>\n\nエフェクトが非同期に何かをフェッチする場合、通常はクリーンアップが必要です。\n\n</Hint>\n\n<Solution>\n\nこのバグが起きるのは、次の順序でイベントが発生した場合です。\n\n- `'Bob'` を選択し `fetchBio('Bob')` がトリガされる\n- `'Taylor'` を選択し `fetchBio('Taylor')` がトリガされる\n- **`'Taylor'` のフェッチが `'Bob'` のフェッチより先に完了する**\n- `'Taylor'` のレンダーからのエフェクトが `setBio('This is Taylor’s bio')` を呼び出す\n- `'Bob'` のフェッチが完了する\n- `'Bob'` のレンダーからのエフェクトが `setBio('This is Bob’s bio')` を呼び出す\n\nこれが、Taylor が選択されているのに Bob の伝記が表示されてしまう理由です。このようなバグは、2 つの非同期操作が互いに「競争」しており、予期しない順序で到着することにより起こるので、[競合状態 (race condition)](https://ja.wikipedia.org/wiki/%E7%AB%B6%E5%90%88%E7%8A%B6%E6%85%8B) と呼ばれます。\n\nこの競合状態を修正するために、クリーンアップ関数を追加します。\n\n<Sandpack>\n\n{/* not the most efficient, but this validation is enabled in the linter only, so it's fine to ignore it here since we know what we're doing */}\n```js {expectedErrors: {'react-compiler': [9]}} src/App.js\nimport { useState, useEffect } from 'react';\nimport { fetchBio } from './api.js';\n\nexport default function Page() {\n  const [person, setPerson] = useState('Alice');\n  const [bio, setBio] = useState(null);\n  useEffect(() => {\n    let ignore = false;\n    setBio(null);\n    fetchBio(person).then(result => {\n      if (!ignore) {\n        setBio(result);\n      }\n    });\n    return () => {\n      ignore = true;\n    }\n  }, [person]);\n\n  return (\n    <>\n      <select value={person} onChange={e => {\n        setPerson(e.target.value);\n      }}>\n        <option value=\"Alice\">Alice</option>\n        <option value=\"Bob\">Bob</option>\n        <option value=\"Taylor\">Taylor</option>\n      </select>\n      <hr />\n      <p><i>{bio ?? 'Loading...'}</i></p>\n    </>\n  );\n}\n```\n\n```js src/api.js hidden\nexport async function fetchBio(person) {\n  const delay = person === 'Bob' ? 2000 : 200;\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve('This is ' + person + '’s bio.');\n    }, delay);\n  })\n}\n\n```\n\n</Sandpack>\n\n各レンダーのエフェクトは個々に `ignore` 変数を持っています。最初 `ignore` 変数は `false` に設定されています。エフェクトがクリーンアップされる場合（別の人物を選択するなど）、その `ignore` 変数は `true` になります。したがってリクエストがどの順序で完了しても問題なくなります。最後の人物のエフェクトにある `ignore` だけが `false` になっているので、`setBio(result)` が呼び出されます。古いエフェクトはクリーンアップされているので、`if (!ignore)` のチェックが `setBio` の呼び出しを防ぎます。\n\n- `'Bob'` を選択すると `fetchBio('Bob')` がトリガされる\n- `'Taylor'` を選択すると `fetchBio('Taylor')` がトリガされ、**以前の（Bob の）エフェクトがクリーンアップされる**\n- `'Taylor'` の取得が `'Bob'` の取得よりも*先に*完了する\n- `'Taylor'` のレンダーからのエフェクトが `setBio('This is Taylor’s bio')` を呼び出す\n- `'Bob'` の取得が完了する\n- `'Bob'` のレンダーからのエフェクトは、`ignore` フラグが `true` に設定されているため、**何も行わない**\n\n古い API コールの結果を無視するだけでなく、[`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) を使用して、不要になったリクエストをキャンセルすることもできます。ただし、これだけでは競合状態を防ぐのに十分ではありません。フェッチの後にさらに非同期ステップが連続する可能性があるため、`ignore` のような明示的なフラグを使用することが、このタイプの問題を解決する最も確実な方法です。\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/thinking-in-react.md",
    "content": "---\ntitle: React の流儀\n---\n\n<Intro>\n\nReact は、あなたが設計を考える方法やアプリを構築する方法を変化させます。React でユーザインターフェースを構築する際には、まず UI を*コンポーネント*と呼ばれる部品に分割します。次に、各コンポーネントのさまざまな視覚的状態を記述します。最後に、複数のコンポーネントを接続し、それらの間をデータが流れるようにします。このチュートリアルでは、React を使って検索可能な商品データテーブルを作成する際の思考プロセスについて説明します。\n\n</Intro>\n\n## モックアップから始めよう {/*start-with-the-mockup*/}\n\nすでに、JSON API が実装済みで、デザイナからもモックアップがもらえているとしましょう。\n\nJSON API は以下のようなデータを返します。\n\n```json\n[\n  { category: \"Fruits\", price: \"$1\", stocked: true, name: \"Apple\" },\n  { category: \"Fruits\", price: \"$1\", stocked: true, name: \"Dragonfruit\" },\n  { category: \"Fruits\", price: \"$2\", stocked: false, name: \"Passionfruit\" },\n  { category: \"Vegetables\", price: \"$2\", stocked: true, name: \"Spinach\" },\n  { category: \"Vegetables\", price: \"$4\", stocked: false, name: \"Pumpkin\" },\n  { category: \"Vegetables\", price: \"$1\", stocked: true, name: \"Peas\" }\n]\n```\n\nモックは次のような見た目だったとします。\n\n<img src=\"/images/docs/s_thinking-in-react_ui.png\" width=\"300\" style={{margin: '0 auto'}} />\n\nReact で UI を実装する際には、通常以下のような 5 つのステップに従います。\n\n## ステップ 1：UI をコンポーネントの階層に分割する {/*step-1-break-the-ui-into-a-component-hierarchy*/}\n\nまず最初に行うのは、モックアップのすべてのコンポーネントとサブコンポーネントを四角で囲んで、それぞれに名前を付けていくことです。デザイナと一緒に仕事をしている場合は、彼らがデザインツールでこれらのコンポーネントにすでに名前を付けているかもしれませんので、話をしに行きましょう。\n\nあなたの技術的バックグラウンドによって、デザインをコンポーネントに分割するとはどういうことなのか、いろいろな考え方ができるでしょう：\n\n* **プログラマ** -- 新しい関数やオブジェクトを作成するかどうかを決定するときと同じ手法を使いましょう。そのような手法のひとつに[関心の分離](https://en.wikipedia.org/wiki/Separation_of_concerns)があります。つまり、1 つのコンポーネントは理想的には 1 つのことだけを気にするべきだということです。もし大きくなってしまったら、より小さなサブコンポーネントに分解するべきです。\n* **CSS エンジニア** -- どの部分に対してクラスセレクタを作成するのか、と考えてみてください（ただし、コンポーネントの方が少し粒度が低めです）。\n* **デザイナ** -- デザインのレイヤをどのように整理するかを考えてください。\n\nJSON がうまく構造化されている場合、それが UI のコンポーネント構造に自然に対応していることがよくあります。それは、UI とデータモデルが同じ情報アーキテクチャ、つまり同じ形状を持っていることが多いためです。1 つのコンポーネントがデータモデルの 1 つの部分に対応するような形で、UI をコンポーネントに分割していきましょう。\n\nこの画面には 5 つのコンポーネントがあることがわかります。\n\n<FullWidth>\n\n<CodeDiagram flip>\n\n<img src=\"/images/docs/s_thinking-in-react_ui_outline.png\" width=\"500\" style={{margin: '0 auto'}} />\n\n1. `FilterableProductTable`（灰色）はアプリ全体のコンテナ。\n2. `SearchBar`（青）はユーザ入力を受け取る。\n3. `ProductTable`（紫）はユーザ入力に従ってリストを表示およびフィルタリングする。\n4. `ProductCategoryRow`（緑）はカテゴリごとの見出しを表示する。\n5. `ProductRow`（黄）は個々の製品に対応する行を表示する。\n\n</CodeDiagram>\n\n</FullWidth>\n\n`ProductTable`（紫）を見ると、（\"Name\" と \"Price\" というラベルがある）テーブルヘッダ部分が、専用のコンポーネントになっていないことに気付くでしょう。これは好みの問題であり、どちらであっても構いません。今回の例ではヘッダは商品テーブル内に表示するものであるため、`ProductTable` の一部としています。ただし、このヘッダが複雑になる場合（例えばソート機能を追加する場合）、専用の `ProductTableHeader` コンポーネントに移動してもよいでしょう。\n\nモックアップ内にあるコンポーネントを特定したら、それらを階層構造に整理します。モックアップで他のコンポーネントの中にあるコンポーネントを、階層構造でも子要素として配置すればいいのです。\n\n* `FilterableProductTable`\n    * `SearchBar`\n    * `ProductTable`\n        * `ProductCategoryRow`\n        * `ProductRow`\n\n## ステップ 2: React で静的なバージョンを作成する {/*step-2-build-a-static-version-in-react*/}\n\nこれでコンポーネント階層ができたので、アプリの実装に取り掛かりましょう。最も分かりやすいアプローチは、インタラクティブな要素はまだ加えず、単にデータモデルから UI をレンダーするバージョンを作成することです。静的なバージョンを先に構築し、後からインタラクティブ性を追加する方が、多くの場合は簡単です。静的なバージョンを作成する間はタイプ量が多い代わりに考えることはほどんどなく、インタラクティブな要素を追加しようとするとタイプ量が少ない代わりに考えることが多くなります。\n\nデータモデルをレンダーする静的バージョンのアプリを作成するにあたっては、[コンポーネント](/learn/your-first-component)を作成していく際に、他のコンポーネントを再利用しつつ [props](/learn/passing-props-to-a-component) 経由でそれらにデータを渡すようにします。props とは、親から子へとデータを渡すための手段です。（もし [state](/learn/state-a-components-memory) の概念に馴染みがある場合でも、この静的なバージョンを作成している間は state を一切使わないでください。state はインタラクティビティ、つまり時間とともに変化するデータのためにあるものです。今はアプリの静的なバージョンなので state は不要です。）\n\n「トップダウン」で高い階層にあるコンポーネント (`FilterableProductTable`) から構築を始めることも、「ボトムアップ」で低い階層にあるコンポーネント (`ProductRow`) から構築を始めることもできます。通常、単純な例ではトップダウンで作業する方が簡単であり、大規模なプロジェクトではボトムアップで進める方が簡単です。\n\n<Sandpack>\n\n```jsx src/App.js\nfunction ProductCategoryRow({ category }) {\n  return (\n    <tr>\n      <th colSpan=\"2\">\n        {category}\n      </th>\n    </tr>\n  );\n}\n\nfunction ProductRow({ product }) {\n  const name = product.stocked ? product.name :\n    <span style={{ color: 'red' }}>\n      {product.name}\n    </span>;\n\n  return (\n    <tr>\n      <td>{name}</td>\n      <td>{product.price}</td>\n    </tr>\n  );\n}\n\nfunction ProductTable({ products }) {\n  const rows = [];\n  let lastCategory = null;\n\n  products.forEach((product) => {\n    if (product.category !== lastCategory) {\n      rows.push(\n        <ProductCategoryRow\n          category={product.category}\n          key={product.category} />\n      );\n    }\n    rows.push(\n      <ProductRow\n        product={product}\n        key={product.name} />\n    );\n    lastCategory = product.category;\n  });\n\n  return (\n    <table>\n      <thead>\n        <tr>\n          <th>Name</th>\n          <th>Price</th>\n        </tr>\n      </thead>\n      <tbody>{rows}</tbody>\n    </table>\n  );\n}\n\nfunction SearchBar() {\n  return (\n    <form>\n      <input type=\"text\" placeholder=\"Search...\" />\n      <label>\n        <input type=\"checkbox\" />\n        {' '}\n        Only show products in stock\n      </label>\n    </form>\n  );\n}\n\nfunction FilterableProductTable({ products }) {\n  return (\n    <div>\n      <SearchBar />\n      <ProductTable products={products} />\n    </div>\n  );\n}\n\nconst PRODUCTS = [\n  {category: \"Fruits\", price: \"$1\", stocked: true, name: \"Apple\"},\n  {category: \"Fruits\", price: \"$1\", stocked: true, name: \"Dragonfruit\"},\n  {category: \"Fruits\", price: \"$2\", stocked: false, name: \"Passionfruit\"},\n  {category: \"Vegetables\", price: \"$2\", stocked: true, name: \"Spinach\"},\n  {category: \"Vegetables\", price: \"$4\", stocked: false, name: \"Pumpkin\"},\n  {category: \"Vegetables\", price: \"$1\", stocked: true, name: \"Peas\"}\n];\n\nexport default function App() {\n  return <FilterableProductTable products={PRODUCTS} />;\n}\n```\n\n```css\nbody {\n  padding: 5px\n}\nlabel {\n  display: block;\n  margin-top: 5px;\n  margin-bottom: 5px;\n}\nth {\n  padding-top: 10px;\n}\ntd {\n  padding: 2px;\n  padding-right: 40px;\n}\n```\n\n</Sandpack>\n\n（このコードが難解に思える場合は、まず[クイックスタート](/learn/)を参照してください！）\n\nコンポーネントの実装を終えると、データモデルをレンダーするための再利用可能なコンポーネントが一式揃ったということになります。これは静的なアプリですので、コンポーネントは JSX を返す以外のことはしていません。階層の一番上のコンポーネント (`FilterableProductTable`) が、データモデルを props として受け取っています。データがトップレベルのコンポーネントからツリーの下の方にあるコンポーネントに流れていくため、この構造を _単方向データフロー (one-way data flow)_ と呼びます。\n\n<Pitfall>\n\nこの段階では、state の値をまだ使わないでください。それは次のステップで行います！\n\n</Pitfall>\n\n## ステップ 3：UI の状態を最小限かつ完全に表現する方法を見つける {/*step-3-find-the-minimal-but-complete-representation-of-ui-state*/}\n\nUI をインタラクティブにするには、ユーザが背後にあるデータモデルを変更できるようにする必要があります。これには *state* というものを使用します。\n\nstate とは、アプリが記憶する必要のある、変化するデータの最小限のセットのことである、と考えましょう。state の構造を考える上で最も重要な原則は、[DRY（Don't Repeat Yourself; 繰り返しを避ける）](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)です。アプリケーションが必要とする状態に関する必要最小限の表現を見つけ出し、他のすべてのものは必要になったらその場で計算します。例えば、ショッピングリストを作成する場合、商品のリストを配列型の state として格納できます。リスト内の商品数も表示したいという場合は、その数を別の state 値として格納するのではなく、代わりに配列の length を読み取ればいいのです。\n\nこの例のアプリケーションで使われるデータはどのようなものか考えましょう：\n\n1. 元となる商品のリスト\n2. ユーザが入力した検索文字列\n3. チェックボックスの値\n4. フィルタ済みの商品のリスト\n\nこれらのうちどれが state でしょうか？ state でないものを特定してください。\n\n* 時間が経っても**変わらない**ものですか？ そうであれば、state ではありません。\n* 親から props 経由で**渡される**ものですか？ そうであれば、state ではありません。\n* コンポーネント内にある既存の state や props に基づいて**計算可能な**データですか？ そうであれば、それは*絶対に* state ではありません！\n\n残ったものがおそらく state です。\n\nもう一度、それぞれを見ていきましょう：\n\n1. 元の商品リストは **props として渡されるので、state ではありません**。\n2. 検索テキストは state のようです。それは時間が経つと変わりますし、何からも計算することはできません。\n3. チェックボックスの値も state のようです。それは時間が経つと変わりますし、何からも計算することはできません。\n4. フィルタリングされた商品のリストは、元の商品リストを検索テキストとチェックボックスの値に従ってフィルタリングすることで**計算できるため、state ではありません**。\n\nつまり、検索テキストとチェックボックスの値だけが state だということです！ よくできました！\n\n<DeepDive>\n\n#### props と state {/*props-vs-state*/}\n\nReact には 2 種類の \"モデル\" データがあります。props と state です。両者は非常に異なります。\n\n* [**props** は関数に渡す引数のようなものです](/learn/passing-props-to-a-component)。親コンポーネントから子コンポーネントにデータを渡し、その外観をカスタマイズするために使います。例えば、`Form` は `Button` に props として `color` を渡すことができます。\n* [**state** はコンポーネントのメモリのようなものです](/learn/state-a-components-memory)。コンポーネントが情報を追跡し、ユーザ操作に反応して変更できるようにします。例えば、`Button` は `isHovered` という state を保持するかもしれません。\n\nprops と state は異なるものですが、それらは協調して働きます。親コンポーネントは state として情報を保持し（それを変更できるように）、その情報を子コンポーネントに props として*渡し*ます。1 度読んだだけでは違いがまだぼんやりと感じられるとしても問題ありません。少し練習することで完全に頭に入るようになります！\n\n</DeepDive>\n\n## ステップ 4：state を保持すべき場所を特定する {/*step-4-identify-where-your-state-should-live*/}\n\nアプリの最小限の state データを特定した後、この state を変更する責任を持つコンポーネント、つまり state を*所有する*コンポーネントを特定する必要があります。ここで思い出しましょう：React では単方向データフロー、つまり親から子コンポーネントへと階層を下る形でのみデータが渡されます。どのコンポーネントがどの状態を所有すべきか、すぐには分からないかもしれません。この概念が初めてであれば難しいかもしれませんが、以下のような手順に従って解決できます！\n\nアプリケーション内の各 state について：\n\n1. その state に基づいて何かをレンダーする*すべて*のコンポーネントを特定する。\n2. 階層内でそれらすべての上に位置する、最も近い共通の親コンポーネントを見つける。\n3. state がどこにあるべきかを決定する：\n    1. 多くの場合、state をその共通の親に直接置くことができる。\n    2. state を、その共通の親のさらに上にあるコンポーネントに置くこともできる。\n    3. state を所有するのに適切なコンポーネントが見つからない場合は、state を保持するためだけの新しいコンポーネントを作成し、共通の親コンポーネントの階層の上のどこかに追加する。\n\n前のステップで、このアプリケーションに state が 2 つあることがわかりました。検索テキストとチェックボックスの値です。今回の例では、これらは常に一緒に表示されるので、同じ場所に置くことが理にかなっています。\n\nそれではこの戦術をサンプルアプリにも適用してみましょう：\n\n1. **state を使用するコンポーネントの特定：**\n   * `ProductTable` は、これらの state（検索テキストとチェックボックスの値）に基づいて製品リストをフィルタリングする必要があります。\n   * `SearchBar` は、これらの state（検索テキストとチェックボックスの値）を表示する必要があります。\n2. **共通の親を見つける：** 両方のコンポーネントに共通の最初の親コンポーネントは `FilterableProductTable` です。\n3. **state がどこにあるべきかを決定する：** フィルタ文字列とチェック状態の値を `FilterableProductTable` に保持することにします。\n\nしたがって、state の値は `FilterableProductTable` にあることになります。\n\nコンポーネントに state を追加するには、[`useState()` フック](/reference/react/useState)を使用します。フックは、React に「接続 (\"hook into\")」するための特殊な関数です。`FilterableProductTable` の先頭に 2 つの state 変数を追加し、それらの初期値を指定します：\n\n```js\nfunction FilterableProductTable({ products }) {\n  const [filterText, setFilterText] = useState('');\n  const [inStockOnly, setInStockOnly] = useState(false);  \n```\n\n次に、`filterText` と `inStockOnly` を `ProductTable` と `SearchBar` に props として渡します：\n\n```js\n<div>\n  <SearchBar \n    filterText={filterText} \n    inStockOnly={inStockOnly} />\n  <ProductTable \n    products={products}\n    filterText={filterText}\n    inStockOnly={inStockOnly} />\n</div>\n```\n\nだんだんとアプリケーションの動作が見えてきましたね。試しに以下のサンドボックスコードで `filterText` の初期値を、`useState('')` から `useState('fruit')` へと書き換えてみてください。検索テキストとテーブルの両方が更新されることがわかります：\n\n<Sandpack>\n\n```jsx src/App.js\nimport { useState } from 'react';\n\nfunction FilterableProductTable({ products }) {\n  const [filterText, setFilterText] = useState('');\n  const [inStockOnly, setInStockOnly] = useState(false);\n\n  return (\n    <div>\n      <SearchBar \n        filterText={filterText} \n        inStockOnly={inStockOnly} />\n      <ProductTable \n        products={products}\n        filterText={filterText}\n        inStockOnly={inStockOnly} />\n    </div>\n  );\n}\n\nfunction ProductCategoryRow({ category }) {\n  return (\n    <tr>\n      <th colSpan=\"2\">\n        {category}\n      </th>\n    </tr>\n  );\n}\n\nfunction ProductRow({ product }) {\n  const name = product.stocked ? product.name :\n    <span style={{ color: 'red' }}>\n      {product.name}\n    </span>;\n\n  return (\n    <tr>\n      <td>{name}</td>\n      <td>{product.price}</td>\n    </tr>\n  );\n}\n\nfunction ProductTable({ products, filterText, inStockOnly }) {\n  const rows = [];\n  let lastCategory = null;\n\n  products.forEach((product) => {\n    if (\n      product.name.toLowerCase().indexOf(\n        filterText.toLowerCase()\n      ) === -1\n    ) {\n      return;\n    }\n    if (inStockOnly && !product.stocked) {\n      return;\n    }\n    if (product.category !== lastCategory) {\n      rows.push(\n        <ProductCategoryRow\n          category={product.category}\n          key={product.category} />\n      );\n    }\n    rows.push(\n      <ProductRow\n        product={product}\n        key={product.name} />\n    );\n    lastCategory = product.category;\n  });\n\n  return (\n    <table>\n      <thead>\n        <tr>\n          <th>Name</th>\n          <th>Price</th>\n        </tr>\n      </thead>\n      <tbody>{rows}</tbody>\n    </table>\n  );\n}\n\nfunction SearchBar({ filterText, inStockOnly }) {\n  return (\n    <form>\n      <input \n        type=\"text\" \n        value={filterText} \n        placeholder=\"Search...\"/>\n      <label>\n        <input \n          type=\"checkbox\" \n          checked={inStockOnly} />\n        {' '}\n        Only show products in stock\n      </label>\n    </form>\n  );\n}\n\nconst PRODUCTS = [\n  {category: \"Fruits\", price: \"$1\", stocked: true, name: \"Apple\"},\n  {category: \"Fruits\", price: \"$1\", stocked: true, name: \"Dragonfruit\"},\n  {category: \"Fruits\", price: \"$2\", stocked: false, name: \"Passionfruit\"},\n  {category: \"Vegetables\", price: \"$2\", stocked: true, name: \"Spinach\"},\n  {category: \"Vegetables\", price: \"$4\", stocked: false, name: \"Pumpkin\"},\n  {category: \"Vegetables\", price: \"$1\", stocked: true, name: \"Peas\"}\n];\n\nexport default function App() {\n  return <FilterableProductTable products={PRODUCTS} />;\n}\n```\n\n```css\nbody {\n  padding: 5px\n}\nlabel {\n  display: block;\n  margin-top: 5px;\n  margin-bottom: 5px;\n}\nth {\n  padding-top: 5px;\n}\ntd {\n  padding: 2px;\n}\n```\n\n</Sandpack>\n\nまだフォームの編集ができないことに気付いたでしょう。その理由について、上のサンドボックスのコンソールエラーに説明があります。\n\n<ConsoleBlock level=\"error\">\n\nYou provided a \\`value\\` prop to a form field without an \\`onChange\\` handler. This will render a read-only field.\n\n</ConsoleBlock>\n\n上記のサンドボックスでは、`ProductTable` と `SearchBar` は `filterText` と `inStockOnly` という props を読み取り、テーブル、インプット、チェックボックスをレンダーしています。たとえば、`SearchBar` は以下のようにしてインプットの value を指定しています：\n\n```js {1,6}\nfunction SearchBar({ filterText, inStockOnly }) {\n  return (\n    <form>\n      <input \n        type=\"text\" \n        value={filterText} \n        placeholder=\"Search...\"/>\n```\n\nただし、まだユーザからのアクション（タイピングなど）に対応するコードを何も書いていません。これが最後のステップになります。\n\n\n## ステップ 5：逆方向のデータフローを追加する {/*step-5-add-inverse-data-flow*/}\n\n現在のところこのアプリでは、props と state が階層構造の下方向に向かって流れ、適切に表示が行われています。ただし、ユーザの入力に従って state を変更するには、逆方向へのデータの流れをサポートする必要があります。つまり、階層の深いところにあるフォームコンポーネントが、`FilterableProductTable` に存在する state を更新できる必要があるわけです。\n\n双方向データバインディングより多少タイプ量は増えますが、React ではこのデータフローを明示的に記述します。上記の例で何かタイプするか、チェックボックスをオンにしようとしても、React が入力を無視するのがわかるでしょう。これは意図的なものです。`<input value={filterText} />` と記述することで、`input` の props である `value` を、`FilterableProductTable` から渡された state である `filterText` と常に等しくせよ、という意味になります。`filterText` の state はまだ一切セットされていないため、入力値が変わることもありません。\n\nユーザがフォームの内容を変更するたびに、state がそれらの変更を反映して更新されるようにしたいと思います。state は `FilterableProductTable` によって所有されているため、このコンポーネントのみが `setFilterText` と `setInStockOnly` を呼び出すことができます。`SearchBar` が `FilterableProductTable` の state を更新できるようにするには、これらの関数を `SearchBar` に渡す必要があります。\n\n```js {2,3,10,11}\nfunction FilterableProductTable({ products }) {\n  const [filterText, setFilterText] = useState('');\n  const [inStockOnly, setInStockOnly] = useState(false);\n\n  return (\n    <div>\n      <SearchBar \n        filterText={filterText} \n        inStockOnly={inStockOnly}\n        onFilterTextChange={setFilterText}\n        onInStockOnlyChange={setInStockOnly} />\n```\n\n`SearchBar` の中で `onChange` イベントハンドラを追加し、それらから親 state を設定します。\n\n```js {4,5,13,19}\nfunction SearchBar({\n  filterText,\n  inStockOnly,\n  onFilterTextChange,\n  onInStockOnlyChange\n}) {\n  return (\n    <form>\n      <input\n        type=\"text\"\n        value={filterText}\n        placeholder=\"Search...\"\n        onChange={(e) => onFilterTextChange(e.target.value)}\n      />\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={inStockOnly}\n          onChange={(e) => onInStockOnlyChange(e.target.checked)}\n```\n\nこれでアプリは完全に動作するようになりました！\n\n<Sandpack>\n\n```jsx src/App.js\nimport { useState } from 'react';\n\nfunction FilterableProductTable({ products }) {\n  const [filterText, setFilterText] = useState('');\n  const [inStockOnly, setInStockOnly] = useState(false);\n\n  return (\n    <div>\n      <SearchBar \n        filterText={filterText} \n        inStockOnly={inStockOnly} \n        onFilterTextChange={setFilterText} \n        onInStockOnlyChange={setInStockOnly} />\n      <ProductTable \n        products={products} \n        filterText={filterText}\n        inStockOnly={inStockOnly} />\n    </div>\n  );\n}\n\nfunction ProductCategoryRow({ category }) {\n  return (\n    <tr>\n      <th colSpan=\"2\">\n        {category}\n      </th>\n    </tr>\n  );\n}\n\nfunction ProductRow({ product }) {\n  const name = product.stocked ? product.name :\n    <span style={{ color: 'red' }}>\n      {product.name}\n    </span>;\n\n  return (\n    <tr>\n      <td>{name}</td>\n      <td>{product.price}</td>\n    </tr>\n  );\n}\n\nfunction ProductTable({ products, filterText, inStockOnly }) {\n  const rows = [];\n  let lastCategory = null;\n\n  products.forEach((product) => {\n    if (\n      product.name.toLowerCase().indexOf(\n        filterText.toLowerCase()\n      ) === -1\n    ) {\n      return;\n    }\n    if (inStockOnly && !product.stocked) {\n      return;\n    }\n    if (product.category !== lastCategory) {\n      rows.push(\n        <ProductCategoryRow\n          category={product.category}\n          key={product.category} />\n      );\n    }\n    rows.push(\n      <ProductRow\n        product={product}\n        key={product.name} />\n    );\n    lastCategory = product.category;\n  });\n\n  return (\n    <table>\n      <thead>\n        <tr>\n          <th>Name</th>\n          <th>Price</th>\n        </tr>\n      </thead>\n      <tbody>{rows}</tbody>\n    </table>\n  );\n}\n\nfunction SearchBar({\n  filterText,\n  inStockOnly,\n  onFilterTextChange,\n  onInStockOnlyChange\n}) {\n  return (\n    <form>\n      <input \n        type=\"text\" \n        value={filterText} placeholder=\"Search...\" \n        onChange={(e) => onFilterTextChange(e.target.value)} />\n      <label>\n        <input \n          type=\"checkbox\" \n          checked={inStockOnly} \n          onChange={(e) => onInStockOnlyChange(e.target.checked)} />\n        {' '}\n        Only show products in stock\n      </label>\n    </form>\n  );\n}\n\nconst PRODUCTS = [\n  {category: \"Fruits\", price: \"$1\", stocked: true, name: \"Apple\"},\n  {category: \"Fruits\", price: \"$1\", stocked: true, name: \"Dragonfruit\"},\n  {category: \"Fruits\", price: \"$2\", stocked: false, name: \"Passionfruit\"},\n  {category: \"Vegetables\", price: \"$2\", stocked: true, name: \"Spinach\"},\n  {category: \"Vegetables\", price: \"$4\", stocked: false, name: \"Pumpkin\"},\n  {category: \"Vegetables\", price: \"$1\", stocked: true, name: \"Peas\"}\n];\n\nexport default function App() {\n  return <FilterableProductTable products={PRODUCTS} />;\n}\n```\n\n```css\nbody {\n  padding: 5px\n}\nlabel {\n  display: block;\n  margin-top: 5px;\n  margin-bottom: 5px;\n}\nth {\n  padding: 4px;\n}\ntd {\n  padding: 2px;\n}\n```\n\n</Sandpack>\n\nイベントの処理や state の更新に関しては、[インタラクティビティの追加](/learn/adding-interactivity)のセクションで学ぶことができます。\n\n## 次に向かう場所 {/*where-to-go-from-here*/}\n\nここまでが、React を使ってコンポーネントやアプリケーションを構築する際の考え方についての、非常に簡単な紹介でした。今すぐ [React のプロジェクトを始める](/learn/installation)か、あるいは本チュートリアルで使用された[すべての構文について詳しく学ぶ](/learn/describing-the-ui)ことができます。\n"
  },
  {
    "path": "src/content/learn/tutorial-tic-tac-toe.md",
    "content": "---\ntitle: 'チュートリアル：三目並べ'\n---\n\n<Intro>\n\nこのチュートリアルでは、小さな三目並べゲーム (tic-tac-toe) を作成します。このチュートリアルを読むにあたり、React に関する事前知識は一切必要ありません。このチュートリアルで学ぶ技法は React アプリを構築する際の基礎となるものであり、マスターすることで React についての深い理解が得られます。\n\n</Intro>\n\n<Note>\n\nこのチュートリアルは、**実践しながら学ぶ**ことを好む方や、今すぐ何か具体的に動くものを作ってみたいと考えている方向けに設計されています。一歩ずつ概念を学びたい場合は、[UI の記述](/learn/describing-the-ui)から始めてください。\n\n</Note>\n\nこのチュートリアルはいくつかのセクションに分かれています。\n\n- [チュートリアルのセットアップ](#setup-for-the-tutorial)は、以下のチュートリアルを進めていく**出発点**です。\n- [概要](#overview)では、React の**基礎**であるコンポーネント、props、および state を学びます。\n- [ゲームを完成させる](#completing-the-game)では、React 開発における**最も一般的な手法**を学びます。\n- [タイムトラベルの追加](#adding-time-travel)では、React の独自の強みに関する**深い洞察**を得ることができます。\n\n### チュートリアルで作成するもの {/*what-are-you-building*/}\n\nこのチュートリアルでは、React を使ってインタラクティブな三目並べゲームを作成します。\n\n完成したときにどのような見た目になるか、以下で確認できます。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nfunction Square({ value, onSquareClick }) {\n  return (\n    <button className=\"square\" onClick={onSquareClick}>\n      {value}\n    </button>\n  );\n}\n\nfunction Board({ xIsNext, squares, onPlay }) {\n  function handleClick(i) {\n    if (calculateWinner(squares) || squares[i]) {\n      return;\n    }\n    const nextSquares = squares.slice();\n    if (xIsNext) {\n      nextSquares[i] = 'X';\n    } else {\n      nextSquares[i] = 'O';\n    }\n    onPlay(nextSquares);\n  }\n\n  const winner = calculateWinner(squares);\n  let status;\n  if (winner) {\n    status = 'Winner: ' + winner;\n  } else {\n    status = 'Next player: ' + (xIsNext ? 'X' : 'O');\n  }\n\n  return (\n    <>\n      <div className=\"status\">{status}</div>\n      <div className=\"board-row\">\n        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />\n        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />\n        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />\n        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />\n        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />\n        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />\n        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />\n      </div>\n    </>\n  );\n}\n\nexport default function Game() {\n  const [history, setHistory] = useState([Array(9).fill(null)]);\n  const [currentMove, setCurrentMove] = useState(0);\n  const xIsNext = currentMove % 2 === 0;\n  const currentSquares = history[currentMove];\n\n  function handlePlay(nextSquares) {\n    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];\n    setHistory(nextHistory);\n    setCurrentMove(nextHistory.length - 1);\n  }\n\n  function jumpTo(nextMove) {\n    setCurrentMove(nextMove);\n  }\n\n  const moves = history.map((squares, move) => {\n    let description;\n    if (move > 0) {\n      description = 'Go to move #' + move;\n    } else {\n      description = 'Go to game start';\n    }\n    return (\n      <li key={move}>\n        <button onClick={() => jumpTo(move)}>{description}</button>\n      </li>\n    );\n  });\n\n  return (\n    <div className=\"game\">\n      <div className=\"game-board\">\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n      </div>\n      <div className=\"game-info\">\n        <ol>{moves}</ol>\n      </div>\n    </div>\n  );\n}\n\nfunction calculateWinner(squares) {\n  const lines = [\n    [0, 1, 2],\n    [3, 4, 5],\n    [6, 7, 8],\n    [0, 3, 6],\n    [1, 4, 7],\n    [2, 5, 8],\n    [0, 4, 8],\n    [2, 4, 6],\n  ];\n  for (let i = 0; i < lines.length; i++) {\n    const [a, b, c] = lines[i];\n    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {\n      return squares[a];\n    }\n  }\n  return null;\n}\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\nまだコードが理解できない、あるいはこのコードの構文に慣れていない場合でも、心配はいりません！ このチュートリアルの目的は、React とその構文を理解するお手伝いをすることです。\n\nチュートリアルを続ける前に、まずは上記の三目並べゲームを実際に触ってみることをお勧めします。いろいろな機能がありますが、ゲームの盤面 (board) の右側にある番号付きリストに着目してください。このリストはゲーム内で発生したすべての着手 (move) の履歴を示すもので、ゲームが進むにつれて更新されていきます。\n\n完成した三目並べゲームで遊んでみたら、ページのスクロールを続けてください。このチュートリアルではもっとシンプルなテンプレートから始めます。次のステップは、ゲームの作成を始められるように準備を行うことです。\n\n## チュートリアルのセットアップ {/*setup-for-the-tutorial*/}\n\n下にあるライブコードエディタで、右上の **Fork** をクリックして新しいタブを開き、CodeSandbox というウェブサイトのエディタを表示してください。CodeSandbox を使うと、ブラウザ上でコードを書き、作成したアプリがユーザにどのように表示されるかプレビューすることができます。新しいタブには、正方形のマス目 (square) と、このチュートリアルのスタータコードが表示されるはずです。\n\n<Sandpack>\n\n```js src/App.js\nexport default function Square() {\n  return <button className=\"square\">X</button>;\n}\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\n<Note>\n\nこのチュートリアルはローカル開発環境でも進めていくことができます。そのためには以下の手順が必要です。\n\n1. [Node.js](https://nodejs.org/en/) をインストール\n1. さきほど開いた CodeSandbox のタブで、左上隅のボタンを押してメニューを開き、そのメニューで **Download Sandbox** を選択して、ファイルをローカルにアーカイブとしてダウンロード\n1. アーカイブを解凍し、ターミナルを開いて解凍したディレクトリに `cd` する\n1. `npm install` で依存ライブラリをインストール\n1. `npm start` でローカルサーバを起動し、プロンプト通りに操作し、ブラウザで実行されるコードを確認する\n\nうまくいかない場合でもここで挫けるのは止めましょう！ オンラインで進めて、後で再度ローカル環境のセットアップにトライしてください。\n\n</Note>\n\n## 概要 {/*overview*/}\n\nセットアップが完了したので、React の概要を確認してみましょう！\n\n### スタータコードの確認 {/*inspecting-the-starter-code*/}\n\nCodeSandbox 画面には、以下の 3 つの主要なセクションが表示されます。\n\n![CodeSandbox のスタータコード](../images/tutorial/react-starter-code-codesandbox.png)\n\n1. _Files_ セクション：ファイル一覧となっており、`src` フォルダに `App.js`、`index.js`、`styles.css` があるほか、`public` フォルダもある\n1. _コードエディタ_：選択したファイルのソースコードが表示される\n1. _Browser_ セクション：書いたコードがどのように表示されるかがわかる\n\n_Files_ セクションで `App.js` ファイルが選択されているはずです。そのファイルの内容は _コードエディタ_ に以下のように表示されています。\n\n```jsx\nexport default function Square() {\n  return <button className=\"square\">X</button>;\n}\n```\n\n_ブラウザ_ セクションには、以下のように X の入ったマス目が表示されているはずです。\n\n![X と書かれたマス目](../images/tutorial/x-filled-square.png)\n\nそれでは、スタータコードのファイルを見ていきましょう。\n\n#### `App.js` {/*appjs*/}\n\n`App.js` にあるコードは*コンポーネント*を作成します。React では、コンポーネントとは UI の部品を表す再利用可能なコードのことです。コンポーネントは、アプリケーションの UI 要素を表示し、管理し、更新するために使用します。コンポーネントの中身を 1 行ずつ見ていって、何が起こっているかを確認しましょう。\n\n```js {1}\nexport default function Square() {\n  return <button className=\"square\">X</button>;\n}\n```\n\n最初の行では、`Square` という関数を定義しています。`export` という JavaScript キーワードは、この関数をこのファイルの外部からアクセスできるようにします。`default` キーワードは、このコードを使用する他のファイルに、これがこのファイルのメイン関数であるということを伝えます。\n\n```js {2}\nexport default function Square() {\n  return <button className=\"square\">X</button>;\n}\n```\n\n2 行目のコードはボタンを返しています。`return` という JavaScript キーワードは、後に書くものが関数の呼び出し元に値として返されるということを意味します。この `<button>` は *JSX 要素 (JSX element)* と呼ばれます。JSX 要素とは、何を表示したいかを記述するための JavaScript コードと HTML タグの組み合わせです。`className=\"square\"` はこのボタンのプロパティ、または *props* と呼ばれるもので、CSS にボタンをどのようにスタイル付けするか伝えます。`X` はボタンの内部に表示されるテキストです。`</button>` は JSX 要素を閉じて、これ以降に書かれた内容がボタンの内部に出てこないようにします。\n\n#### `styles.css` {/*stylescss*/}\n\nCodeSandbox の _Files_ セクションにある `styles.css` というファイルをクリックしてください。このファイルには、React アプリのスタイルが定義されています。最初の 2 つの _CSS セレクタ_（`*` と `body`）は、アプリケーションの全体的なスタイルを定義しており、`.square` というセレクタは、`className` プロパティが `square` となっているコンポーネントのスタイルを定義します。今回のコードでは、これは `App.js` ファイルの Square コンポーネントが表示するボタンにマッチします。\n\n#### `index.js` {/*indexjs*/}\n\nCodeSandbox の _Files_ セクションにある `index.js` というファイルをクリックしてください。このチュートリアルでこのファイルを編集することはありませんが、`App.js` ファイルで作成したコンポーネントと Web ブラウザとの橋渡しを行っています。\n\n```jsx\nimport { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\n\nimport App from './App';\n```\n\n1〜5 行目で、必要なすべての部品を取り出しています：\n\n* React\n* Web ブラウザとやり取りするための React ライブラリ (React DOM)\n* コンポーネント用のスタイル\n* `App.js` であなたが作成したコンポーネント\n\nファイルの残りの部分では、これらの部品を全部まとめて、最終的な成果物を `public` フォルダ内の `index.html` に注入しています。\n\n### 盤面の作成 {/*building-the-board*/}\n\nそれでは `App.js` に戻りましょう。このチュートリアルの残りは、このファイル内で作業します。\n\n現在の盤面 (board) にはマス目 (square) が 1 つしかありませんが、本来は 9 つ必要です！ 2 つ目のマス目を作るために単純にコピーペーストすると…\n\n```js {2}\nexport default function Square() {\n  return <button className=\"square\">X</button><button className=\"square\">X</button>;\n}\n```\n\n以下のようなエラーが表示されます：\n\n<ConsoleBlock level=\"error\">\n\n/src/App.js: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX Fragment `<>...</>`?\n\n</ConsoleBlock>\n\nReact コンポーネントからは、このボタンのように JSX 要素を複数隣り合わせて返すのではなく、単一の JSX 要素を返す必要があります。これを修正するには、複数の隣接する JSX 要素は、以下のように*フラグメント*（`<>` および `</>`）で囲むようにします。\n\n```js {3-6}\nexport default function Square() {\n  return (\n    <>\n      <button className=\"square\">X</button>\n      <button className=\"square\">X</button>\n    </>\n  );\n}\n```\n\nこれで以下のように表示されるはずです：\n\n![X の入ったマス目が 2 つ](../images/tutorial/two-x-filled-squares.png)\n\n素晴らしいです！ あとは、マス目が 9 個になるまで何度かコピーペーストすれば…\n\n![1 行に X の入ったマス目が 9 個](../images/tutorial/nine-x-filled-squares.png)\n\nあれ？ 盤面のマス目はグリッド状に並べたいのですが、1 行に並んでしまっています。これを修正するには、`div` を使って複数のマス目を行単位でグループ化し、CSS クラスを追加する必要があります。ついでに各マス目に番号をつけて、どれがどこに表示されているのか確認できるようにしましょう。\n\n`App.js` ファイルで、`Square` コンポーネントを以下のように書き換えてください：\n\n```js {3-19}\nexport default function Square() {\n  return (\n    <>\n      <div className=\"board-row\">\n        <button className=\"square\">1</button>\n        <button className=\"square\">2</button>\n        <button className=\"square\">3</button>\n      </div>\n      <div className=\"board-row\">\n        <button className=\"square\">4</button>\n        <button className=\"square\">5</button>\n        <button className=\"square\">6</button>\n      </div>\n      <div className=\"board-row\">\n        <button className=\"square\">7</button>\n        <button className=\"square\">8</button>\n        <button className=\"square\">9</button>\n      </div>\n    </>\n  );\n}\n```\n\n`styles.css` で定義されている CSS が、`className` が `board-row` となっている div をスタイル化します。スタイル化された `div` でコンポーネントを 3 行にまとめたので、三目並べの盤面ができました。\n\n![1 から 9 までの数字が入った三目並べの盤面](../images/tutorial/number-filled-board.png)\n\nしかし別の問題が出てきました。`Square` という名前のコンポーネントなのに、実際にはもう 1 個のマス目ではなくなっています。これを直すため、名前を `Board` に変えます。\n\n```js {1}\nexport default function Board() {\n  //...\n}\n```\n\nこの段階で、コードは次のようになっているはずです。\n\n<Sandpack>\n\n```js\nexport default function Board() {\n  return (\n    <>\n      <div className=\"board-row\">\n        <button className=\"square\">1</button>\n        <button className=\"square\">2</button>\n        <button className=\"square\">3</button>\n      </div>\n      <div className=\"board-row\">\n        <button className=\"square\">4</button>\n        <button className=\"square\">5</button>\n        <button className=\"square\">6</button>\n      </div>\n      <div className=\"board-row\">\n        <button className=\"square\">7</button>\n        <button className=\"square\">8</button>\n        <button className=\"square\">9</button>\n      </div>\n    </>\n  );\n}\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\n<Note>\n\nうーん、ちょっとタイピングが大変ですよね！ このページからコードをコピーペーストしても問題ありません。ただし、ちょっと挑戦してみたい気分であれば、自分で 1 度は手入力したものだけをコピーすることをおすすめします。\n\n</Note>\n\n### props を通してデータを渡す {/*passing-data-through-props*/}\n\n次に、ユーザがマス目をクリックしたら、空白だった中身が \"X\" に変化するようにしたいと思います。ですが先ほどのように盤面を作成していたのでは、この先マス目の中身を更新するコードを 9 回（各マス目に対して 1 回ずつ）コピーペーストしなくてはならなくなってしまいます！ そのようなコピーペーストをする代わりに、React のコンポーネントアーキテクチャを使って再利用可能なコンポーネントを作成することで、重複だらけのごちゃごちゃとしたコードを書かずに済むようになります。\n\nまず、`Board` コンポーネントから最初のマス目を定義している行 (`<button className=\"square\">1</button>`) をコピーし、新たに書く `Square` コンポーネントに貼り付けます。\n\n```js {1-3}\nfunction Square() {\n  return <button className=\"square\">1</button>;\n}\n\nexport default function Board() {\n  // ...\n}\n```\n\n次に、`Board` コンポーネントを更新し、JSX 構文を使用してこの `Square` コンポーネントをレンダーするようにしましょう。\n\n```js {5-19}\n// ...\nexport default function Board() {\n  return (\n    <>\n      <div className=\"board-row\">\n        <Square />\n        <Square />\n        <Square />\n      </div>\n      <div className=\"board-row\">\n        <Square />\n        <Square />\n        <Square />\n      </div>\n      <div className=\"board-row\">\n        <Square />\n        <Square />\n        <Square />\n      </div>\n    </>\n  );\n}\n```\n\nブラウザの `div` とは異なり、自分で作成するコンポーネントである `Board` と `Square` は、大文字で始める必要があることに注意してください。\n\nどうなったか見てみましょう。\n\n![1 だらけの盤面](../images/tutorial/board-filled-with-ones.png)\n\nあれ？ 先ほどまでの番号付きのマス目がなくなってしまいました。すべてのマス目が \"1\" になってしまっています。これを修正するには、各マス目が持つべき値を親コンポーネント (`Board`) から子コンポーネント (`Square`) に伝えるために、*props* というものを使用します。\n\n`Square` コンポーネントを更新して、`Board` から渡される `value` プロパティを読み取るようにします。\n\n```js {1}\nfunction Square({ value }) {\n  return <button className=\"square\">1</button>;\n}\n```\n\n`function Square({ value })` は、`Square` コンポーネントに props として `value` という名前の値が渡されることを示しています。\n\n次は各マス目に、受け取った `value` を表示させる必要があります。次のようにしてみましょう。\n\n```js {2}\nfunction Square({ value }) {\n  return <button className=\"square\">value</button>;\n}\n```\n\nおっと、これは意図したものではありません。\n\n![\"value\" の文字列だらけの盤面](../images/tutorial/board-filled-with-value.png)\n\nコンポーネントから `value` という名前の JavaScript 変数の値を表示させたかったのであって、\"value\" という単語自体を表示させたかったわけではありませんね。ここでは JSX の中から「JavaScript の記法に戻る」ために、波括弧が必要です。JSX の中で `value` の周りに波括弧を追加してみましょう。\n\n```js {2}\nfunction Square({ value }) {\n  return <button className=\"square\">{value}</button>;\n}\n```\n\n今のところ、空白の盤面が表示されているはずです。\n\n![空の盤面](../images/tutorial/empty-board.png)\n\nこれは `Board` コンポーネントが、レンダーしている各 `Square` コンポーネントにまだ props として `value` を渡していないからです。これを修正するには、`Board` コンポーネントがレンダーしている `Square` コンポーネントのそれぞれに、props として `value` を追加していきます：\n\n```js {5-7,10-12,15-17}\nexport default function Board() {\n  return (\n    <>\n      <div className=\"board-row\">\n        <Square value=\"1\" />\n        <Square value=\"2\" />\n        <Square value=\"3\" />\n      </div>\n      <div className=\"board-row\">\n        <Square value=\"4\" />\n        <Square value=\"5\" />\n        <Square value=\"6\" />\n      </div>\n      <div className=\"board-row\">\n        <Square value=\"7\" />\n        <Square value=\"8\" />\n        <Square value=\"9\" />\n      </div>\n    </>\n  );\n}\n```\n\nこれで、再び数値が入ったグリッドが表示されるようになりました：\n\n![1 から 9 までの数字で埋められた三目並べの盤面](../images/tutorial/number-filled-board.png)\n\nここまでで、コードは以下のようになっているはずです：\n\n<Sandpack>\n\n```js src/App.js\nfunction Square({ value }) {\n  return <button className=\"square\">{value}</button>;\n}\n\nexport default function Board() {\n  return (\n    <>\n      <div className=\"board-row\">\n        <Square value=\"1\" />\n        <Square value=\"2\" />\n        <Square value=\"3\" />\n      </div>\n      <div className=\"board-row\">\n        <Square value=\"4\" />\n        <Square value=\"5\" />\n        <Square value=\"6\" />\n      </div>\n      <div className=\"board-row\">\n        <Square value=\"7\" />\n        <Square value=\"8\" />\n        <Square value=\"9\" />\n      </div>\n    </>\n  );\n}\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\n### インタラクティブなコンポーネントの作成 {/*making-an-interactive-component*/}\n\nでは `Square` コンポーネントをクリックすると `X` が表示されるようにしてみましょう。`Square` の中に `handleClick` という関数を宣言します。次に、`Square` から返される button JSX 要素に、props として `onClick` を追加します。\n\n```js {2-4,9}\nfunction Square({ value }) {\n  function handleClick() {\n    console.log('clicked!');\n  }\n\n  return (\n    <button\n      className=\"square\"\n      onClick={handleClick}\n    >\n      {value}\n    </button>\n  );\n}\n```\n\nここでクリックしてみると、CodeSandbox の _Browser_ セクションの下部にある _Console_ タブに `\"clicked!\"` というログが表示されるはずです。複数回クリックすると、再び `\"clicked!\"` がログとして記録されますが、同一のメッセージが繰り返しコンソールに表示されることはありません。代わりに、最初の `\"clicked!\"` ログの隣にカウンタが表示され、その数字が増えていきます。\n\n<Note>\n\nこのチュートリアルをローカルの開発環境で実施している場合は、ブラウザのコンソールを開く必要があります。例えば、Chrome ブラウザを使っている場合は、**Shift + Ctrl + J** (Windows/Linux) または **Option + ⌘ + J** (macOS) というキーボードショートカットで、コンソールを表示できます。\n\n</Note>\n\n次のステップとして、`Square` コンポーネントに、クリックされたことを「記憶」して \"X\" マークを表示してもらうことにします。何かを「記憶」するために、コンポーネントは *state* というものを使用します。\n\nReact は、`useState` という特別な関数を提供しており、コンポーネントからこれを呼び出すことで「記憶」を行わせることができます。`Square` の現在の値を state に保存し、`Square` がクリックされたときにその値を変更しましょう。\n\nファイルの先頭で `useState` をインポートします。`Square` コンポーネントから `value` プロパティを削除します。代わりに、`Square` の先頭に新しい行を追加して `useState` を呼び出します。`value` という名前の state 変数が返されるようにします。\n\n```js {1,3,4}\nimport { useState } from 'react';\n\nfunction Square() {\n  const [value, setValue] = useState(null);\n\n  function handleClick() {\n    //...\n```\n\n`value` が state の現在値を格納し、`setValue` はその値を更新するために使う関数です。`useState` に渡される `null` は、この state 変数の初期値として使用されるので、この `value` はまず `null` という値から始まります。\n\n`Square` コンポーネントがもはや props を受け取らないようになったので、`Board` コンポーネントが作成している 9 個の `Square` コンポーネントすべてから `value` プロパティを削除しましょう。\n\n```js {6-8,11-13,16-18}\n// ...\nexport default function Board() {\n  return (\n    <>\n      <div className=\"board-row\">\n        <Square />\n        <Square />\n        <Square />\n      </div>\n      <div className=\"board-row\">\n        <Square />\n        <Square />\n        <Square />\n      </div>\n      <div className=\"board-row\">\n        <Square />\n        <Square />\n        <Square />\n      </div>\n    </>\n  );\n}\n```\n\n次に、`Square` をクリックすると \"X\" が表示されるようにします。イベントハンドラの `console.log(\"clicked!\");` を `setValue('X');` に置き換えます。これで、`Square` コンポーネントは次のようになります。\n\n```js {5}\nfunction Square() {\n  const [value, setValue] = useState(null);\n\n  function handleClick() {\n    setValue('X');\n  }\n\n  return (\n    <button\n      className=\"square\"\n      onClick={handleClick}\n    >\n      {value}\n    </button>\n  );\n}\n```\n\nこの `set` 関数を `onClick` ハンドラから呼び出すことで、`<button>` がクリックされるたびに React に `Square` を再レンダーするよう要求しています。更新の後では当該 `Square` の `value` は `'X'` になっているので、ゲームの盤面上に \"X\" が表示されるようになります。いずれかのマス目かをクリックすると \"X\" が表示されるはずです。\n\n![盤面に複数の \"X\" を追加](../images/tutorial/tictac-adding-x-s.gif)\n\n各 Square はそれぞれ独自の state を保持しています。それぞれの Square に格納されている `value` は、他のものとは完全に独立しています。コンポーネントの `set` 関数を呼び出すと、React は自動的に内部にある子コンポーネントも更新します。\n\n上記の変更を行った後、コードは次のようになります。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nfunction Square() {\n  const [value, setValue] = useState(null);\n\n  function handleClick() {\n    setValue('X');\n  }\n\n  return (\n    <button\n      className=\"square\"\n      onClick={handleClick}\n    >\n      {value}\n    </button>\n  );\n}\n\nexport default function Board() {\n  return (\n    <>\n      <div className=\"board-row\">\n        <Square />\n        <Square />\n        <Square />\n      </div>\n      <div className=\"board-row\">\n        <Square />\n        <Square />\n        <Square />\n      </div>\n      <div className=\"board-row\">\n        <Square />\n        <Square />\n        <Square />\n      </div>\n    </>\n  );\n}\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\n### React Developer Tools {/*react-developer-tools*/}\n\nReact DevTools を使うと、React コンポーネントの props や state を確認することができます。React DevTools タブは、CodeSandbox の *Browser* セクションの下部にあります。\n\n![CodeSandbox 内の React DevTools](../images/tutorial/codesandbox-devtools.png)\n\n画面上の特定のコンポーネントについて調べるには、React DevTools の左上にあるボタンを使用します。\n\n![React DevTools でページ上のコンポーネントを選択する](../images/tutorial/devtools-select.gif)\n\n<Note>\n\nローカル開発をしている場合、React DevTools は [Chrome](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en)、[Firefox](https://addons.mozilla.org/en-US/firefox/addon/react-devtools/)、そして [Edge](https://microsoftedge.microsoft.com/addons/detail/react-developer-tools/gpphkfbcpidddadnkolkpfckpihlkkil) ブラウザの拡張機能として利用できます。インストールすると、React を利用しているサイトでは *Compontents* タブがブラウザの開発者ツールに表示されるようになります。\n\n</Note>\n\n## ゲームを完成させる {/*completing-the-game*/}\n\nここまでの作業で、三目並べゲームの基本的な構成部品がすべて揃いました。完全に動作するゲームにするためには、盤面上に交互に \"X\" と \"O\" を置けるようにすることと、勝者を決めるための方法が必要です。\n\n### state のリフトアップ {/*lifting-state-up*/}\n\n現在のところ、各 `Square` コンポーネントが、ゲームの状態を少しずつ保持している状況です。三目並べゲームで勝者を決めるためには、`Board` が 9 つの `Square` コンポーネントそれぞれの現在の state を、何らかの形で知る必要があります。\n\nどのようなアプローチが良いでしょうか？ 最初に思いつくのは、`Board` が各 `Square` に現在の state がどうなっているか「問い合わせる」というものですね。このアプローチも React では技術的には可能ですが、コードが理解しにくくなり、バグが発生しやすくなり、リファクタリングが困難になってしまうため、お勧めしません。ここでの最善はそうではなく、ゲームの state を各 `Square` ではなく親の `Board` コンポーネントに保持させることです。`Board` コンポーネントは、それぞれの `Square` に、何を表示するのか props を使って伝えることができます。以前にマス目に数字を渡したときと同じですね。\n\n**複数の子コンポーネントからデータを収集したい、あるいは 2 つの子コンポーネント同士で通信したい、と思ったら、代わりに親コンポーネントに共有の state を宣言するようにしてください。親コンポーネントはその state を子コンポーネントに prop 経由で渡すことができます。これにより、子同士および親子間で、コンポーネントが同期されるようになります。**\n\nstate の親コンポーネントへのリフトアップ（持ち上げ）は、React のコンポーネントのリファクタリングにおいてよく発生します。\n\nこの機会に試してみましょう。`Board` コンポーネントを編集して `squares` という名前の state 変数を宣言し、そのデフォルト値として 9 つのマス目に対応する 9 個の null を持つ配列を与えるようにしましょう：\n\n```js {3}\n// ...\nexport default function Board() {\n  const [squares, setSquares] = useState(Array(9).fill(null));\n  return (\n    // ...\n  );\n}\n```\n\n`Array(9).fill(null)` は、9 個の要素を持つ配列を作成し、それぞれの要素を `null` に設定します。それを囲む `useState()` コールは、state 変数 `squares` を宣言し、初期値をこの配列にします。配列の各要素は、個々のマス目の値に対応します。後で盤面が埋まってくると、`squares` 配列は次のような見た目になる予定です：\n\n```jsx\n['O', null, 'X', 'X', 'X', 'O', 'O', null, null]\n```\n\nそして `Board` コンポーネントは、レンダーする各 `Square` に props として `value` を渡していく必要があります：\n\n```js {6-8,11-13,16-18}\nexport default function Board() {\n  const [squares, setSquares] = useState(Array(9).fill(null));\n  return (\n    <>\n      <div className=\"board-row\">\n        <Square value={squares[0]} />\n        <Square value={squares[1]} />\n        <Square value={squares[2]} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[3]} />\n        <Square value={squares[4]} />\n        <Square value={squares[5]} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[6]} />\n        <Square value={squares[7]} />\n        <Square value={squares[8]} />\n      </div>\n    </>\n  );\n}\n```\n\n次に、`Square` コンポーネントを編集して、`value` プロパティを改めて `Board` コンポーネントから受け取るようにします。同時に、`Square` コンポーネント自身が `value` を state で管理していたコードと、ボタンにある `onClick` プロパティを削除する必要があります。\n\n```js {1,2}\nfunction Square({value}) {\n  return <button className=\"square\">{value}</button>;\n}\n```\n\nこの時点では、空白の三目並べの盤面が表示されているはずです：\n\n![空の盤面](../images/tutorial/empty-board.png)\n\nコードは以下のようになっています：\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nfunction Square({ value }) {\n  return <button className=\"square\">{value}</button>;\n}\n\nexport default function Board() {\n  const [squares, setSquares] = useState(Array(9).fill(null));\n  return (\n    <>\n      <div className=\"board-row\">\n        <Square value={squares[0]} />\n        <Square value={squares[1]} />\n        <Square value={squares[2]} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[3]} />\n        <Square value={squares[4]} />\n        <Square value={squares[5]} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[6]} />\n        <Square value={squares[7]} />\n        <Square value={squares[8]} />\n      </div>\n    </>\n  );\n}\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\nこれで、各 Square は、`'X'` 、`'O'`、または空の場合は `null` の、いずれかの `value` を props として受け取るようになります。\n\n次に、`Square` がクリックされたときに起こることを変更しなければいけません。いまや `Board` コンポーネントが、どのマス目が埋まっているのかを管理しています。`Square` から `Board` の state を更新する手段が必要です。state はそれを定義しているコンポーネントにプライベートなものですので、`Square` から `Board` の state を直接更新することはできません。\n\n代わりに、`Board` コンポーネントから `Square` コンポーネントに関数を渡して、マス目がクリックされたときに `Square` にその関数を呼び出してもらうようにします。クリックされたときに `Square` コンポーネントが呼び出す関数から始めましょう。その関数を `onSquareClick` という名前にします：\n\n```js {3}\nfunction Square({ value }) {\n  return (\n    <button className=\"square\" onClick={onSquareClick}>\n      {value}\n    </button>\n  );\n}\n```\n\n次に、`onSquareClick` 関数を `Square` コンポーネントの props に追加します。\n\n```js {1}\nfunction Square({ value, onSquareClick }) {\n  return (\n    <button className=\"square\" onClick={onSquareClick}>\n      {value}\n    </button>\n  );\n}\n```\n\n次にこの `onSquareClick` プロパティを、`Board` コンポーネント内に `handleClick` という名前で作る関数に接続します。`onSquareClick` を `handleClick` に接続するために、1 番目の `Square` コンポーネントの `onSquareClick` プロパティに関数を渡しましょう：\n\n```js {7}\nexport default function Board() {\n  const [squares, setSquares] = useState(Array(9).fill(null));\n\n  return (\n    <>\n      <div className=\"board-row\">\n        <Square value={squares[0]} onSquareClick={handleClick} />\n        //...\n  );\n}\n```\n\n最後に、盤面の情報を保持する state である `squares` 配列を更新するため、`Board` コンポーネント内に `handleClick` 関数を定義します：\n\n```js {4-8}\nexport default function Board() {\n  const [squares, setSquares] = useState(Array(9).fill(null));\n\n  function handleClick() {\n    const nextSquares = squares.slice();\n    nextSquares[0] = \"X\";\n    setSquares(nextSquares);\n  }\n\n  return (\n    // ...\n  )\n}\n```\n\n`handleClick` 関数は、`slice()` 配列メソッドを使って `squares` 配列のコピー (`nextSquares`) を作成します。次に、`handleClick` は、`nextSquares` 配列を更新して最初の（インデックス `[0]` の）マス目に `X` と書き込みます。\n\n`setSquares` 関数をコールすることで、React はこのコンポーネントの state に変更があったことを知ります。これにより、`squares` という state 変数を使用しているコンポーネント (`Board`)、およびその子コンポーネント（盤面を構成している `Square` コンポーネントすべて）の再レンダーがトリガされます。\n\n<Note>\n\nJavaScript は[クロージャ](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)をサポートしているため、内側の関数（例：`handleClick`）は外側の関数（例：`Board`）で定義されている変数や関数にアクセスできます。`handleClick` 関数は、state である `squares` を読み取ったり、`setSquares` メソッドを呼び出したりできます。これらは両方とも `Board` 関数の内部で定義されているためです。\n\n</Note>\n\nこれで、盤に X を置けるようになりました…が、まだ左上隅のマス目にしか置けません。今の `handleClick` 関数は、左上のマス目に対応するインデックス (`0`) で更新するようハードコードされているからです。`handleClick` を書き換えて、任意のマス目の内容を更新できるようにしましょう。`handleClick` 関数に、更新するマス目のインデックスを指定する引数 `i` を追加します：\n\n```js {4,6}\nexport default function Board() {\n  const [squares, setSquares] = useState(Array(9).fill(null));\n\n  function handleClick(i) {\n    const nextSquares = squares.slice();\n    nextSquares[i] = \"X\";\n    setSquares(nextSquares);\n  }\n\n  return (\n    // ...\n  )\n}\n```\n\n次に、その `i` を `handleClick` に渡す必要があります。以下のように、JSX 内で直接 `Square` の `onSquareClick` プロパティを `handleClick(0)` としたくなるかもしれませんが、これはうまくいきません：\n\n```jsx\n<Square value={squares[0]} onSquareClick={handleClick(0)} />\n```\n\nこれがうまくいかない理由は、`handleClick(0)` の呼び出しが、Board コンポーネントのレンダーの一部として発生してしまうからです。`handleClick(0)` は、`setSquares` を呼び出して Board コンポーネントの state を更新するため、Board コンポーネント全体が再レンダーされます。しかし、これにより `handleClick(0)` が再度実行され、無限ループに陥ります：\n\n<ConsoleBlock level=\"error\">\n\nToo many re-renders. React limits the number of renders to prevent an infinite loop.\n\n</ConsoleBlock>\n\nなぜこの問題が以前には発生しなかったのでしょう？\n\n`onSquareClick={handleClick}` のようにしていたときは、props として `handleClick` 関数を渡していました。呼び出してはいませんでした！ しかし、今はその関数をその場で*呼び出して*しまっているのです。`handleClick(0)` の括弧の部分に注目してください。だからすぐに実行されてしまうのです。ユーザがクリックするまで、`handleClick` を*呼び出したくない*わけです。\n\nこれを解決する方法として、`handleClick(0)` を呼び出す `handleFirstSquareClick` のような関数を作成し、次に `handleClick(1)` を呼び出す `handleSecondSquareClick` のような関数を作成し…のようにしていくことも可能です。これらの関数を `onSquareClick={handleFirstSquareClick}` のようにして、（呼び出すのではなく）props として渡すことができます。これにより無限ループは解決されるでしょう。\n\nしかし、9 つの異なる関数を定義し、それぞれに名前を付けるのは冗長です。代わりに、次のようにしましょう：\n\n```js {6}\nexport default function Board() {\n  // ...\n  return (\n    <>\n      <div className=\"board-row\">\n        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />\n        // ...\n  );\n}\n```\n\n新しい `() =>` 構文に注目してください。この `() => handleClick(0)` は*アロー関数*と呼ばれる、関数を短く定義する方法です。マス目がクリックされると、アロー (`=>`) の後のコードが実行され、`handleClick(0)` が呼び出されます。\n\nそれでは、残り 8 つの Square のコードも更新して、アロー関数の中から `handleClick` が呼び出されるようにしましょう。`handleClick` の各呼び出しの引数が、正しくマス目のインデックスに対応していることを確認してください。\n\n```js {6-8,11-13,16-18}\nexport default function Board() {\n  // ...\n  return (\n    <>\n      <div className=\"board-row\">\n        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />\n        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />\n        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />\n        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />\n        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />\n        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />\n        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />\n      </div>\n    </>\n  );\n};\n```\n\nこれで改めて、盤面上の任意のマス目をクリックして X が置けるようになりました。\n\n![X が置かれた盤面](../images/tutorial/tictac-adding-x-s.gif)\n\nですが今では、state の管理がすべて `Board` コンポーネントによって行われるようになっています！\n\nコードは、以下のようになっているはずです：\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nfunction Square({ value, onSquareClick }) {\n  return (\n    <button className=\"square\" onClick={onSquareClick}>\n      {value}\n    </button>\n  );\n}\n\nexport default function Board() {\n  const [squares, setSquares] = useState(Array(9).fill(null));\n\n  function handleClick(i) {\n    const nextSquares = squares.slice();\n    nextSquares[i] = 'X';\n    setSquares(nextSquares);\n  }\n\n  return (\n    <>\n      <div className=\"board-row\">\n        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />\n        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />\n        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />\n        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />\n        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />\n        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />\n        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />\n      </div>\n    </>\n  );\n}\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\nstate 管理が `Board` コンポーネントに移動されたので、親である `Board` コンポーネントは子の `Square` コンポーネントに props を渡すことで、それらが正しく表示されるようにしています。`Square` をクリックすると、子である `Square` コンポーネントが親である `Board` コンポーネントに盤面の state を更新するように要求します。`Board` の state が変更されると、`Board` コンポーネントとすべての `Square` 子コンポーネントが自動的に再レンダーされます。すべてのマス目の state を `Board` コンポーネントにまとめて保持しておくことで、この後でゲームの勝者を決めることが可能になります。\n\nユーザが盤面の左上のマス目をクリックして `X` を置いた場合を例に、何が起こるのかをおさらいしましょう。\n\n1. 左上のマス目をクリックすると、`button` が props として受け取った `onClick` 関数が実行されます。`Square` コンポーネントはその関数を `Board` から `onSquareClick` プロパティとして受け取っています。`Board` コンポーネントはその関数を JSX の中で直接定義しています。その関数は引数 `0` で `handleClick` を呼び出します。\n1. `handleClick` は引数 `0` を使って、`squares` 配列の最初の要素を `null` から `X` に更新します。\n1. `Board` コンポーネントの state である `squares` が更新されたので、`Board` とそのすべての子が再レンダーされます。これにより、インデックス `0` である `Square` コンポーネントの `value` プロパティが `null` から `X` に変更されます。\n\n最終的に、クリックによって左上のマス目が空白から `X` に変わったという結果をユーザが目にすることになります。\n\n<Note>\n\nDOM の `<button>` 要素は組み込みのコンポーネントなので、その `onClick` 属性は、React にとって特別な意味を持っています。`Square` のような独自コンポーネントの場合、名前は自由に決めることができます。`Square` の `onSquareClick` プロパティや `Board` の `handleClick` 関数にほかのどんな名前を付けても、コードは同じように動作します。React では、イベントを表す props には `onSomething` という名前を使い、それらのイベントを処理するハンドラ関数の定義には `handleSomething` という名前を使うことが一般的です。\n\n</Note>\n\n### なぜイミュータビリティが重要なのか {/*why-immutability-is-important*/}\n\n`handleClick` 内で既存の `squares` 配列を直接変更するのではなく、`.slice()` を使ってコピーを作成していたことを思い返してください。その理由を説明するために、イミュータビリティ（不変性, immutability）という概念と、なぜイミュータビリティを学ぶことが重要であるかについてお話しします。\n\nデータを変更する方法には、一般的に 2 つのアプローチがあります。1 つ目のアプローチは、データの値を直接 *ミューテート（書き換え, mutate）* する方法です。2 つ目のアプローチは、望ましい変更が施された新しいコピーで元のデータを置換する方法です。以下は、`squares` 配列を直接書き換えている例です：\n\n```jsx\nconst squares = [null, null, null, null, null, null, null, null, null];\nsquares[0] = 'X';\n// Now `squares` is [\"X\", null, null, null, null, null, null, null, null];\n```\n\n一方で、以下が `squares` 配列を書き換えずにデータを変更している例です：\n\n```jsx\nconst squares = [null, null, null, null, null, null, null, null, null];\nconst nextSquares = ['X', null, null, null, null, null, null, null, null];\n// Now `squares` is unchanged, but `nextSquares` first element is 'X' rather than `null`\n```\n\n結果は同じですが、元のデータの書き換えを行わないことで、いくつかの利点を得ることができます。\n\nイミュータビリティにより、複雑な機能をはるかに簡単に実装することができます。このチュートリアルの後半では、ゲームの履歴を確認して過去の手に「巻き戻し」ができる、「タイムトラベル」機能を実装することになります。このような機能はゲームに特有のものではなく、アクションの取り消しややり直しはアプリケーションでは一般的な要件です。直接的なデータの書き換えを避けることで、データの過去のバージョンを壊すことなく保持し、後で再利用することができます。\n\nイミュータビリティには、もう 1 つの利点があります。デフォルトでは、親コンポーネントの state が変更されると、すべての子コンポーネントは自動的に再レンダーされます。これには state 変更によって影響を受けていない子コンポーネントも含まれます。再レンダー自体はユーザに気付かれないものですが（積極的に避ける必要はありません！）、パフォーマンス上の理由から、影響を受けていないことが明らかなツリーの一部の再レンダーをスキップしたい場合があります。イミュータビリティにより、コンポーネントがデータが変更されたかどうかを非常に安価に比較することができます。React がコンポーネントの再レンダーをいつ行うかについての詳細は、[`memo` API のリファレンス](/reference/react/memo)を参照してください。\n\n### 手番の処理 {/*taking-turns*/}\n\nさて、この三目並べゲームの重大な欠陥、すなわち \"O\" がまだ盤面上に出てこないという問題を解決する時間がやってきました。\n\nまず先手がデフォルトで \"X\" になるようにしましょう。現在の手番を追跡するために、Board コンポーネントにもう 1 つ state を追加しましょう：\n\n```js {2}\nfunction Board() {\n  const [xIsNext, setXIsNext] = useState(true);\n  const [squares, setSquares] = useState(Array(9).fill(null));\n\n  // ...\n}\n```\n\nプレーヤが着手するたびに、どちらのプレーヤの手番なのかを決める `xIsNext`（真偽値型）が反転して、ゲームの state が保存されます。`Board` の `handleClick` 関数を書き換えて、そこで `xIsNext` の値を反転させましょう：\n\n```js {7,8,9,10,11,13}\nexport default function Board() {\n  const [xIsNext, setXIsNext] = useState(true);\n  const [squares, setSquares] = useState(Array(9).fill(null));\n\n  function handleClick(i) {\n    const nextSquares = squares.slice();\n    if (xIsNext) {\n      nextSquares[i] = \"X\";\n    } else {\n      nextSquares[i] = \"O\";\n    }\n    setSquares(nextSquares);\n    setXIsNext(!xIsNext);\n  }\n\n  return (\n    //...\n  );\n}\n```\n\nこれで、異なるマス目をクリックすると `X` と `O` が正しく交互に表示されるようになりました！\n\nおや、まだ問題があります。同じマス目を何度もクリックしてみてください：\n\n![O が X を上書きしている](../images/tutorial/o-replaces-x.gif)\n\n`X` が `O` で上書きされてしまっています！ これでも大変興味深い特殊ルールにはなりそうですが、今のところはオリジナルのルールを守りましょう。\n\nマス目に `X` や `O` を置く前に、まずそのマス目に既に `X` や `O` の値があるかどうか、まだチェックしていません。これは、*早期リターン (early return)* をすることで修正できます。マス目に既に `X` または `O` があるかどうかを確認し、既にある場合は `handleClick` 関数内から早期 `return` し、盤面の state が更新されてしまわないようにしましょう。\n\n```js {2,3,4}\nfunction handleClick(i) {\n  if (squares[i]) {\n    return;\n  }\n  const nextSquares = squares.slice();\n  //...\n}\n```\n\nこれで空いているマス目にだけ `X` や `O` を追加できるようになりました！ ここまでのコードは以下のようになります。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nfunction Square({value, onSquareClick}) {\n  return (\n    <button className=\"square\" onClick={onSquareClick}>\n      {value}\n    </button>\n  );\n}\n\nexport default function Board() {\n  const [xIsNext, setXIsNext] = useState(true);\n  const [squares, setSquares] = useState(Array(9).fill(null));\n\n  function handleClick(i) {\n    if (squares[i]) {\n      return;\n    }\n    const nextSquares = squares.slice();\n    if (xIsNext) {\n      nextSquares[i] = 'X';\n    } else {\n      nextSquares[i] = 'O';\n    }\n    setSquares(nextSquares);\n    setXIsNext(!xIsNext);\n  }\n\n  return (\n    <>\n      <div className=\"board-row\">\n        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />\n        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />\n        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />\n        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />\n        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />\n        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />\n        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />\n      </div>\n    </>\n  );\n}\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\n### 勝者の宣言 {/*declaring-a-winner*/}\n\nプレーヤが交互に着手できるようになったので、次は勝者が決まった際やこれ以上ゲームを進められなくなった際に、そのように表示することにしましょう。これを実現するために、9 つのマス目の配列を受け取って勝者を判定し `'X'`、`'O'` または `null` を返す、`calculateWinner` という名前のヘルパー関数を追加します。`calculateWinner` 関数の中身は React 特有のものではありませんので、あまり気にしないようにしてください。\n\n```js src/App.js\nexport default function Board() {\n  //...\n}\n\nfunction calculateWinner(squares) {\n  const lines = [\n    [0, 1, 2],\n    [3, 4, 5],\n    [6, 7, 8],\n    [0, 3, 6],\n    [1, 4, 7],\n    [2, 5, 8],\n    [0, 4, 8],\n    [2, 4, 6]\n  ];\n  for (let i = 0; i < lines.length; i++) {\n    const [a, b, c] = lines[i];\n    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {\n      return squares[a];\n    }\n  }\n  return null;\n}\n```\n\n<Note>\n\n`calculateWinner` を `Board` の前後のどちらで定義しても問題ありません。今はコンポーネントを編集するたびにスクロールしないで済むよう、これを最後に置きましょう。\n\n</Note>\n\n`Board` コンポーネントの `handleClick` 関数で `calculateWinner(squares)` を呼び出して、いずれかのプレーヤが勝利したかどうか判定します。これは、ユーザがすでに `X` や `O` のあるマス目をクリックしたかどうかチェックしている場所と同じところで行えます。どちらの場合でも早期リターンを行いたいです。\n\n```js {2}\nfunction handleClick(i) {\n  if (squares[i] || calculateWinner(squares)) {\n    return;\n  }\n  const nextSquares = squares.slice();\n  //...\n}\n```\n\nゲームが終了したことを知らせるために、\"Winner: X\" または \"Winner: O\" というテキストを表示しましょう。これを行うため、`Board` コンポーネントに `status` の欄を追加します。このステータス欄は、ゲームが終了した場合に勝者を表示し、ゲームが続行中の場合は、次がどちらの手番なのか表示します。\n\n```js {3-9,13}\nexport default function Board() {\n  // ...\n  const winner = calculateWinner(squares);\n  let status;\n  if (winner) {\n    status = \"Winner: \" + winner;\n  } else {\n    status = \"Next player: \" + (xIsNext ? \"X\" : \"O\");\n  }\n\n  return (\n    <>\n      <div className=\"status\">{status}</div>\n      <div className=\"board-row\">\n        // ...\n  )\n}\n```\n\nおめでとうございます！ これで、動作する三目並べのゲームができました。そして、あなたが React の基本を学ぶこともできました。本当の勝者は*あなた*です。ここでのコードは以下のようになっています：\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nfunction Square({value, onSquareClick}) {\n  return (\n    <button className=\"square\" onClick={onSquareClick}>\n      {value}\n    </button>\n  );\n}\n\nexport default function Board() {\n  const [xIsNext, setXIsNext] = useState(true);\n  const [squares, setSquares] = useState(Array(9).fill(null));\n\n  function handleClick(i) {\n    if (calculateWinner(squares) || squares[i]) {\n      return;\n    }\n    const nextSquares = squares.slice();\n    if (xIsNext) {\n      nextSquares[i] = 'X';\n    } else {\n      nextSquares[i] = 'O';\n    }\n    setSquares(nextSquares);\n    setXIsNext(!xIsNext);\n  }\n\n  const winner = calculateWinner(squares);\n  let status;\n  if (winner) {\n    status = 'Winner: ' + winner;\n  } else {\n    status = 'Next player: ' + (xIsNext ? 'X' : 'O');\n  }\n\n  return (\n    <>\n      <div className=\"status\">{status}</div>\n      <div className=\"board-row\">\n        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />\n        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />\n        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />\n        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />\n        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />\n        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />\n        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />\n      </div>\n    </>\n  );\n}\n\nfunction calculateWinner(squares) {\n  const lines = [\n    [0, 1, 2],\n    [3, 4, 5],\n    [6, 7, 8],\n    [0, 3, 6],\n    [1, 4, 7],\n    [2, 5, 8],\n    [0, 4, 8],\n    [2, 4, 6],\n  ];\n  for (let i = 0; i < lines.length; i++) {\n    const [a, b, c] = lines[i];\n    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {\n      return squares[a];\n    }\n  }\n  return null;\n}\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\n## タイムトラベルの追加 {/*adding-time-travel*/}\n\n最後の演習として、ゲームを過去の手番に「巻き戻す」ことができるようにしましょう。\n\n### 着手の履歴を保持する {/*storing-a-history-of-moves*/}\n\n`squares` 配列をミューテート（書き換え）していた場合、タイムトラベルの実装は非常に困難になっていたことでしょう。\n\nしかし、各着手ごとに `slice()` を使って `squares` 配列の新しいコピーを作成し、それをイミュータブルなものとして扱ってきました。このおかげで、過去のすべてのバージョンの `squares` 配列を保存し、すでに発生した着手の間で移動することができるようになります。\n\n過去の `squares` 配列を、`history` という名前の別の配列に入れて、それを新たに state 変数として保持することにします。この `history` 配列は、最初の着手から最新の着手まで、盤面のすべての状態を表現しており、以下のような形になります。\n\n```jsx\n[\n  // Before first move\n  [null, null, null, null, null, null, null, null, null],\n  // After first move\n  [null, null, null, null, 'X', null, null, null, null],\n  // After second move\n  [null, null, null, null, 'X', null, null, null, 'O'],\n  // ...\n]\n```\n\n### もう一度 state をリフトアップ {/*lifting-state-up-again*/}\n\nここからは、新しいトップレベルのコンポーネント、`Game` を作成して、過去の着手の一覧を表示するようにします。ゲームの履歴全体を保持する state である `history` は、ここに置くことにします。\n\n`history` 状態を `Game` コンポーネントに配置することで、その子になる `Board` コンポーネントからは `squares` の state を削除できます。`Square` コンポーネントから `Board` コンポーネントに「state をリフトアップ」したときと同じように、`Board` からトップレベルの `Game` コンポーネントに state をリフトアップすることになります。これにより、`Game` コンポーネントは `Board` のデータを完全に制御し、`history` からの過去の盤面の状態を `Board` にレンダーさせることができます。\n\nまず、`export default` を使って `Game` コンポーネントを追加し、`Board` コンポーネントと一部のマークアップをレンダーしてみましょう。\n\n```js {1,5-16}\nfunction Board() {\n  // ...\n}\n\nexport default function Game() {\n  return (\n    <div className=\"game\">\n      <div className=\"game-board\">\n        <Board />\n      </div>\n      <div className=\"game-info\">\n        <ol>{/*TODO*/}</ol>\n      </div>\n    </div>\n  );\n}\n```\n\n`function Board() {` 宣言の前にある `export default` キーワードを削除し、`function Game() {` 宣言の前に追加したことに注意してください。これにより、`index.js` ファイルが `Board` コンポーネントの代わりに `Game` コンポーネントをトップレベルのコンポーネントとして使用するように指示しています。`Game` コンポーネントが返すもう 1 つの `div` は、後で画面に追加するゲーム情報のためのスペースを確保しています。\n\n`Game` コンポーネントに現在の手番と着手の履歴を管理するための state を追加してください。\n\n```js {2-3}\nexport default function Game() {\n  const [xIsNext, setXIsNext] = useState(true);\n  const [history, setHistory] = useState([Array(9).fill(null)]);\n  // ...\n```\n\n`[Array(9).fill(null)]` は要素数が 1 の配列であり、その唯一の要素が 9 つの `null` が入った配列となっていることに注意してください。\n\n現在の盤面をレンダーするには、`history` の最後にあるマス目の配列を読み取る必要があります。これに `useState` は必要ありません。レンダー中にそれを計算するだけの情報がすでにあります。\n\n```js {4}\nexport default function Game() {\n  const [xIsNext, setXIsNext] = useState(true);\n  const [history, setHistory] = useState([Array(9).fill(null)]);\n  const currentSquares = history[history.length - 1];\n  // ...\n```\n\n次に、`Game` コンポーネント内に、ゲーム内容を更新するために `Board` コンポーネントから呼ばれる `handlePlay` 関数を作成します。`xIsNext`、`currentSquares`、そして `handlePlay` を `Board` コンポーネントに props として渡すようにします。\n\n```js {6-8,13}\nexport default function Game() {\n  const [xIsNext, setXIsNext] = useState(true);\n  const [history, setHistory] = useState([Array(9).fill(null)]);\n  const currentSquares = history[history.length - 1];\n\n  function handlePlay(nextSquares) {\n    // TODO\n  }\n\n  return (\n    <div className=\"game\">\n      <div className=\"game-board\">\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n        //...\n  )\n}\n```\n\n次に `Board` コンポーネント側も編集し、渡される props によってこのコンポーネントが完全に制御されるようにしましょう。`Board` コンポーネントが 3 つの props を受け取るようにします。`xIsNext`、`squares`、そして、プレーヤの着手時に `Board` がコールして新たな盤面の状態を伝えるための `onPlay` 関数です。`Board` の冒頭で `useState` を呼び出している 2 行は削除してください。\n\n```js {1}\nfunction Board({ xIsNext, squares, onPlay }) {\n  function handleClick(i) {\n    //...\n  }\n  // ...\n}\n```\n\n次に、`Board` コンポーネント内の `handleClick` で `setSquares` と `setXIsNext` を呼び出しているところを、`onPlay` 関数への単一の呼び出しに置き換えます。これにより、ユーザがマス目をクリックしたときに、`Game` コンポーネントが `Board` を更新できるようになります。\n\n```js {12}\nfunction Board({ xIsNext, squares, onPlay }) {\n  function handleClick(i) {\n    if (calculateWinner(squares) || squares[i]) {\n      return;\n    }\n    const nextSquares = squares.slice();\n    if (xIsNext) {\n      nextSquares[i] = \"X\";\n    } else {\n      nextSquares[i] = \"O\";\n    }\n    onPlay(nextSquares);\n  }\n  //...\n}\n```\n\n`Board` コンポーネントは、`Game` コンポーネントから渡される props によって完全に制御されています。ゲームを再び動作させるために、`Game` コンポーネントの `handlePlay` 関数を実装する必要があります。\n\n`handlePlay` は呼び出されたときに何をすべきでしょうか？ 以前の Board は新しい `square` 配列を作ったら `setSquares` を呼び出していましたが、今では新しい配列を `onPlay` に渡すようになっています。\n\n`handlePlay` 関数は `Game` の state を更新して再レンダーをトリガする必要がありますが、もう呼び出すべき `setSquares` 関数は存在しません。代わりに `history` という state 変数を使って情報を保存しているからです。`history` を更新して、新しい `squares` 配列を新しい履歴エントリとして追加するようにしましょう。また、Board が行っていたように `xIsNext` を切り替える必要もあります。\n\n```js {4-5}\nexport default function Game() {\n  //...\n  function handlePlay(nextSquares) {\n    setHistory([...history, nextSquares]);\n    setXIsNext(!xIsNext);\n  }\n  //...\n}\n```\n\nここで、`[...history, nextSquares]` というコードは、`history` のすべての要素の後に `nextSquares` が繋がった新しい配列を作成します。（この `...history` は[*スプレッド構文*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax)であり、「`history` のすべての項目をここに列挙せよ」のように読みます。）\n\n例えば、`history` が `[[null,null,null], [\"X\",null,null]]` で `nextSquares` が `[\"X\",null,\"O\"]` の場合、新しい `[...history, nextSquares]` 配列は `[[null,null,null], [\"X\",null,null], [\"X\",null,\"O\"]]` になります。\n\nこの時点で、state が `Game` コンポーネントに移動し終わり、UI はリファクタリング前と同様に完全に動作するようになっているはずです。ここでのコードは以下のようになります。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nfunction Square({ value, onSquareClick }) {\n  return (\n    <button className=\"square\" onClick={onSquareClick}>\n      {value}\n    </button>\n  );\n}\n\nfunction Board({ xIsNext, squares, onPlay }) {\n  function handleClick(i) {\n    if (calculateWinner(squares) || squares[i]) {\n      return;\n    }\n    const nextSquares = squares.slice();\n    if (xIsNext) {\n      nextSquares[i] = 'X';\n    } else {\n      nextSquares[i] = 'O';\n    }\n    onPlay(nextSquares);\n  }\n\n  const winner = calculateWinner(squares);\n  let status;\n  if (winner) {\n    status = 'Winner: ' + winner;\n  } else {\n    status = 'Next player: ' + (xIsNext ? 'X' : 'O');\n  }\n\n  return (\n    <>\n      <div className=\"status\">{status}</div>\n      <div className=\"board-row\">\n        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />\n        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />\n        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />\n        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />\n        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />\n        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />\n        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />\n      </div>\n    </>\n  );\n}\n\nexport default function Game() {\n  const [xIsNext, setXIsNext] = useState(true);\n  const [history, setHistory] = useState([Array(9).fill(null)]);\n  const currentSquares = history[history.length - 1];\n\n  function handlePlay(nextSquares) {\n    setHistory([...history, nextSquares]);\n    setXIsNext(!xIsNext);\n  }\n\n  return (\n    <div className=\"game\">\n      <div className=\"game-board\">\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n      </div>\n      <div className=\"game-info\">\n        <ol>{/*TODO*/}</ol>\n      </div>\n    </div>\n  );\n}\n\nfunction calculateWinner(squares) {\n  const lines = [\n    [0, 1, 2],\n    [3, 4, 5],\n    [6, 7, 8],\n    [0, 3, 6],\n    [1, 4, 7],\n    [2, 5, 8],\n    [0, 4, 8],\n    [2, 4, 6],\n  ];\n  for (let i = 0; i < lines.length; i++) {\n    const [a, b, c] = lines[i];\n    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {\n      return squares[a];\n    }\n  }\n  return null;\n}\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\n### 過去の着手の表示 {/*showing-the-past-moves*/}\n\n三目並べのゲームの履歴が記録されるようになったので、プレーヤに過去の着手のリストを表示することができます。\n\n`<button>` などの React 要素は普通の JavaScript オブジェクトでもありますので、アプリケーション内でそれらを受け渡しすることができます。React で複数のアイテムをレンダーするには、React 要素の配列を使うことができます。\n\nすでに state として着手履歴の配列である `history` がありますので、ここでそれを React 要素の配列に変換します。JavaScript では、配列を別の配列に変換するために、[配列の `map` メソッド](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) を使うことができます。\n\n```jsx\n[1, 2, 3].map((x) => x * 2) // [2, 4, 6]\n```\n\n着手の `history` を `map` で変換して、画面上のボタンを表す React 要素の配列にし、過去の着手に「ジャンプ」できるボタンを表示するようにしましょう。Game コンポーネントで `history` をマップしてみましょう。\n\n```js {11-13,15-27,35}\nexport default function Game() {\n  const [xIsNext, setXIsNext] = useState(true);\n  const [history, setHistory] = useState([Array(9).fill(null)]);\n  const currentSquares = history[history.length - 1];\n\n  function handlePlay(nextSquares) {\n    setHistory([...history, nextSquares]);\n    setXIsNext(!xIsNext);\n  }\n\n  function jumpTo(nextMove) {\n    // TODO\n  }\n\n  const moves = history.map((squares, move) => {\n    let description;\n    if (move > 0) {\n      description = 'Go to move #' + move;\n    } else {\n      description = 'Go to game start';\n    }\n    return (\n      <li>\n        <button onClick={() => jumpTo(move)}>{description}</button>\n      </li>\n    );\n  });\n\n  return (\n    <div className=\"game\">\n      <div className=\"game-board\">\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n      </div>\n      <div className=\"game-info\">\n        <ol>{moves}</ol>\n      </div>\n    </div>\n  );\n}\n```\n\nコードは以下のようになります。なお開発者ツールのコンソールには以下のようなエラーが表示されています。\n\n<ConsoleBlock level=\"warning\">\nWarning: Each child in an array or iterator should have a unique \"key\" prop. Check the render method of &#96;Game&#96;.\n</ConsoleBlock>\n\nこのエラーは次のセクションで修正します。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nfunction Square({ value, onSquareClick }) {\n  return (\n    <button className=\"square\" onClick={onSquareClick}>\n      {value}\n    </button>\n  );\n}\n\nfunction Board({ xIsNext, squares, onPlay }) {\n  function handleClick(i) {\n    if (calculateWinner(squares) || squares[i]) {\n      return;\n    }\n    const nextSquares = squares.slice();\n    if (xIsNext) {\n      nextSquares[i] = 'X';\n    } else {\n      nextSquares[i] = 'O';\n    }\n    onPlay(nextSquares);\n  }\n\n  const winner = calculateWinner(squares);\n  let status;\n  if (winner) {\n    status = 'Winner: ' + winner;\n  } else {\n    status = 'Next player: ' + (xIsNext ? 'X' : 'O');\n  }\n\n  return (\n    <>\n      <div className=\"status\">{status}</div>\n      <div className=\"board-row\">\n        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />\n        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />\n        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />\n        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />\n        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />\n        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />\n        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />\n      </div>\n    </>\n  );\n}\n\nexport default function Game() {\n  const [xIsNext, setXIsNext] = useState(true);\n  const [history, setHistory] = useState([Array(9).fill(null)]);\n  const currentSquares = history[history.length - 1];\n\n  function handlePlay(nextSquares) {\n    setHistory([...history, nextSquares]);\n    setXIsNext(!xIsNext);\n  }\n\n  function jumpTo(nextMove) {\n    // TODO\n  }\n\n  const moves = history.map((squares, move) => {\n    let description;\n    if (move > 0) {\n      description = 'Go to move #' + move;\n    } else {\n      description = 'Go to game start';\n    }\n    return (\n      <li>\n        <button onClick={() => jumpTo(move)}>{description}</button>\n      </li>\n    );\n  });\n\n  return (\n    <div className=\"game\">\n      <div className=\"game-board\">\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n      </div>\n      <div className=\"game-info\">\n        <ol>{moves}</ol>\n      </div>\n    </div>\n  );\n}\n\nfunction calculateWinner(squares) {\n  const lines = [\n    [0, 1, 2],\n    [3, 4, 5],\n    [6, 7, 8],\n    [0, 3, 6],\n    [1, 4, 7],\n    [2, 5, 8],\n    [0, 4, 8],\n    [2, 4, 6],\n  ];\n  for (let i = 0; i < lines.length; i++) {\n    const [a, b, c] = lines[i];\n    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {\n      return squares[a];\n    }\n  }\n  return null;\n}\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\n`map` に渡される関数の内部で `history` を反復処理する部分では、`squares` 引数が `history` の各要素を順に受け取り、`move` 引数が配列のインデックス `0`, `1`, `2`, … を順に受け取ります。（大抵は、実際の配列の中身が必要になりますが、今回の着手リストのレンダーで必要なのはインデックスの方だけです。）\n\n三目並べゲームの履歴にある着手のそれぞれについて、ボタン `<button>` の入ったリストアイテム `<li>` を作成します。ボタンには `jumpTo` という関数を呼び出す `onClick` ハンドラがありますが、これはまだ実装していません。\n\n現時点では、ゲーム内で起きた着手の一覧に加え、開発者ツールのコンソールにエラーが表示されているはずです。この \"key\" に関するエラーの意味についてこれから説明します。\n\n### key を選ぶ {/*picking-a-key*/}\n\nリストをレンダーすると、React はレンダーされたリストの各アイテムに関するとある情報を保持します。そのリストが更新されると、React は何が変更されたのかを判断する必要があります。例えばリストのアイテムを追加したのかもしれませんし、削除、並べ替え、あるいは項目の中身の更新を行ったのかもしれません。\n\n次のような状況から：\n\n```html\n<li>Alexa: 7 tasks left</li>\n<li>Ben: 5 tasks left</li>\n```\n\n以下に遷移したと想像してください：\n\n```html\n<li>Ben: 9 tasks left</li>\n<li>Claudia: 8 tasks left</li>\n<li>Alexa: 5 tasks left</li>\n```\n\n人間がこれを読めばおそらく、カウントの数字が変わっていることに加えて、Alexa と Ben の順番が入れ替わり、Claudia が Alexa と Ben の間に挿入された、と言うことでしょう。しかし React はコンピュータプログラムでありあなたの意図を知ることはできません。なのでリストの各項目を兄弟間で区別するために、それぞれのリスト項目に *key* プロパティを指定する必要があります。データがデータベースから取得されている場合、Alexa、Ben、Claudia のデータベース ID を key として使用できます。\n\n```js {1}\n<li key={user.id}>\n  {user.name}: {user.taskCount} tasks left\n</li>\n```\n\nリストが再レンダーされると、React は各リスト項目の key を見て、以前のリストの項目に一致する key があるか探します。現在のリストに以前に存在しなかった key がある場合、React は対応するコンポーネントを作成します。現在のリストから以前のリストに存在した key が消えている場合、React は対応する既存のコンポーネントを破棄します。2 つの key が一致した場合、対応するコンポーネントは移動されます。\n\nkey は、各コンポーネントを識別するための情報を React に与えます。これにより、再レンダー間で state を維持できるのです。コンポーネントの key が変更されると、コンポーネントは破棄され、新しい state で再作成されます。\n\n`key` は React における、特別に予約されたプロパティです。要素が作成されるとき、React は `key` プロパティを抽出し、返される要素に key を直接格納します。`key` が props として渡されているかのように見えますが、`key` は React が自動的に使用して、どのコンポーネントを更新するかを自動的に決定します。子コンポーネント側が、親コンポーネントが指定した `key` が何であるかを知る方法はありません。\n\n**動的なリストを作成する際には、適切な key を割り当てることを強くお勧めします**。適切な key がない場合は、key を含めるようデータ構造の再設計を検討してください。\n\nkey が指定されていない場合、React はエラーを報告し、デフォルトでは配列のインデックスを key として使用します。配列のインデックスを key として使用すると、リストの項目を並べ替えたり、挿入・削除したりする際に問題が生じます。明示的に `key={i}` を渡すとエラーを止めることはできますが、配列のインデックスを使うのと同じ問題になるだけですので、ほとんどの場合お勧めできません。\n\nkey はグローバルに一意である必要はなく、コンポーネントとその兄弟間で一意であれば十分です。\n\n### タイムトラベルの実装 {/*implementing-time-travel*/}\n\n三目並べゲームの履歴では、過去の各着手に、一意の ID が関連付けられています。何番目の着手かを表す連続した数値です。着手は並び変わったり、削除されたり、途中に挿入されることはないため、手番のインデックスを key として使用することは安全です。\n\n`Game` 関数内で、`<li key={move}>` とすることで key を追加できます。これでゲームをリロードすると、React の \"key\" エラーが消えるはずです。\n\n```js {4}\nconst moves = history.map((squares, move) => {\n  //...\n  return (\n    <li key={move}>\n      <button onClick={() => jumpTo(move)}>{description}</button>\n    </li>\n  );\n});\n```\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nfunction Square({ value, onSquareClick }) {\n  return (\n    <button className=\"square\" onClick={onSquareClick}>\n      {value}\n    </button>\n  );\n}\n\nfunction Board({ xIsNext, squares, onPlay }) {\n  function handleClick(i) {\n    if (calculateWinner(squares) || squares[i]) {\n      return;\n    }\n    const nextSquares = squares.slice();\n    if (xIsNext) {\n      nextSquares[i] = 'X';\n    } else {\n      nextSquares[i] = 'O';\n    }\n    onPlay(nextSquares);\n  }\n\n  const winner = calculateWinner(squares);\n  let status;\n  if (winner) {\n    status = 'Winner: ' + winner;\n  } else {\n    status = 'Next player: ' + (xIsNext ? 'X' : 'O');\n  }\n\n  return (\n    <>\n      <div className=\"status\">{status}</div>\n      <div className=\"board-row\">\n        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />\n        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />\n        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />\n        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />\n        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />\n        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />\n        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />\n      </div>\n    </>\n  );\n}\n\nexport default function Game() {\n  const [xIsNext, setXIsNext] = useState(true);\n  const [history, setHistory] = useState([Array(9).fill(null)]);\n  const currentSquares = history[history.length - 1];\n\n  function handlePlay(nextSquares) {\n    setHistory([...history, nextSquares]);\n    setXIsNext(!xIsNext);\n  }\n\n  function jumpTo(nextMove) {\n    // TODO\n  }\n\n  const moves = history.map((squares, move) => {\n    let description;\n    if (move > 0) {\n      description = 'Go to move #' + move;\n    } else {\n      description = 'Go to game start';\n    }\n    return (\n      <li key={move}>\n        <button onClick={() => jumpTo(move)}>{description}</button>\n      </li>\n    );\n  });\n\n  return (\n    <div className=\"game\">\n      <div className=\"game-board\">\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n      </div>\n      <div className=\"game-info\">\n        <ol>{moves}</ol>\n      </div>\n    </div>\n  );\n}\n\nfunction calculateWinner(squares) {\n  const lines = [\n    [0, 1, 2],\n    [3, 4, 5],\n    [6, 7, 8],\n    [0, 3, 6],\n    [1, 4, 7],\n    [2, 5, 8],\n    [0, 4, 8],\n    [2, 4, 6],\n  ];\n  for (let i = 0; i < lines.length; i++) {\n    const [a, b, c] = lines[i];\n    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {\n      return squares[a];\n    }\n  }\n  return null;\n}\n\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\n`jumpTo` を実装する前に、`Game` コンポーネントに、現在ユーザが見ているのが何番目の着手であるのかを管理させる必要があります。これを行うために、`currentMove` という名前の新しい state 変数を定義し、デフォルト値を `0` に設定します：\n\n```js {4}\nexport default function Game() {\n  const [xIsNext, setXIsNext] = useState(true);\n  const [history, setHistory] = useState([Array(9).fill(null)]);\n  const [currentMove, setCurrentMove] = useState(0);\n  const currentSquares = history[history.length - 1];\n  //...\n}\n```\n\n次に、`Game` 内の `jumpTo` 関数を更新して、`currentMove` を更新するようにします。`currentMove` を変更する数値が偶数の場合は、`xIsNext` を `true` に設定します。\n\n```js {4-5}\nexport default function Game() {\n  // ...\n  function jumpTo(nextMove) {\n    setCurrentMove(nextMove);\n    setXIsNext(nextMove % 2 === 0);\n  }\n  //...\n}\n```\n\n次に、マス目をクリックしたときに呼ばれる `Game` の `handlePlay` 関数を 2 か所変更しましょう。\n\n- 過去に戻ってその時点から新しい着手を行う場合、その時点までの履歴を維持して残りは消去したいでしょう。`nextSquares` を `history` のすべての履歴（`...` スプレッド構文）の後に追加するのではなく、履歴の一部である `history.slice(0, currentMove + 1)` の後に追加するようにして、履歴のうち着手時点までの部分のみが保持されるようにします。\n- 着手が起きるたびに、最新の履歴エントリを指し示すように `currentMove` を更新する必要があります。\n\n```js {2-4}\nfunction handlePlay(nextSquares) {\n  const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];\n  setHistory(nextHistory);\n  setCurrentMove(nextHistory.length - 1);\n  setXIsNext(!xIsNext);\n}\n```\n\n最後に、`Game` コンポーネントを変更し、常に最後の着手をレンダーする代わりに、現在選択されている着手をレンダーするようにします：\n\n```js {5}\nexport default function Game() {\n  const [xIsNext, setXIsNext] = useState(true);\n  const [history, setHistory] = useState([Array(9).fill(null)]);\n  const [currentMove, setCurrentMove] = useState(0);\n  const currentSquares = history[currentMove];\n\n  // ...\n}\n```\n\nゲーム履歴内にある任意の着手をクリックすると、三目並べの盤面が即座に更新され、その着手の発生後に対応する盤面が表示されるようになります。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nfunction Square({value, onSquareClick}) {\n  return (\n    <button className=\"square\" onClick={onSquareClick}>\n      {value}\n    </button>\n  );\n}\n\nfunction Board({ xIsNext, squares, onPlay }) {\n  function handleClick(i) {\n    if (calculateWinner(squares) || squares[i]) {\n      return;\n    }\n    const nextSquares = squares.slice();\n    if (xIsNext) {\n      nextSquares[i] = 'X';\n    } else {\n      nextSquares[i] = 'O';\n    }\n    onPlay(nextSquares);\n  }\n\n  const winner = calculateWinner(squares);\n  let status;\n  if (winner) {\n    status = 'Winner: ' + winner;\n  } else {\n    status = 'Next player: ' + (xIsNext ? 'X' : 'O');\n  }\n\n  return (\n    <>\n      <div className=\"status\">{status}</div>\n      <div className=\"board-row\">\n        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />\n        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />\n        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />\n        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />\n        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />\n        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />\n        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />\n      </div>\n    </>\n  );\n}\n\nexport default function Game() {\n  const [xIsNext, setXIsNext] = useState(true);\n  const [history, setHistory] = useState([Array(9).fill(null)]);\n  const [currentMove, setCurrentMove] = useState(0);\n  const currentSquares = history[currentMove];\n\n  function handlePlay(nextSquares) {\n    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];\n    setHistory(nextHistory);\n    setCurrentMove(nextHistory.length - 1);\n    setXIsNext(!xIsNext);\n  }\n\n  function jumpTo(nextMove) {\n    setCurrentMove(nextMove);\n    setXIsNext(nextMove % 2 === 0);\n  }\n\n  const moves = history.map((squares, move) => {\n    let description;\n    if (move > 0) {\n      description = 'Go to move #' + move;\n    } else {\n      description = 'Go to game start';\n    }\n    return (\n      <li key={move}>\n        <button onClick={() => jumpTo(move)}>{description}</button>\n      </li>\n    );\n  });\n\n  return (\n    <div className=\"game\">\n      <div className=\"game-board\">\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n      </div>\n      <div className=\"game-info\">\n        <ol>{moves}</ol>\n      </div>\n    </div>\n  );\n}\n\nfunction calculateWinner(squares) {\n  const lines = [\n    [0, 1, 2],\n    [3, 4, 5],\n    [6, 7, 8],\n    [0, 3, 6],\n    [1, 4, 7],\n    [2, 5, 8],\n    [0, 4, 8],\n    [2, 4, 6],\n  ];\n  for (let i = 0; i < lines.length; i++) {\n    const [a, b, c] = lines[i];\n    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {\n      return squares[a];\n    }\n  }\n  return null;\n}\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\n### 最後のお掃除 {/*final-cleanup*/}\n\nコードを注意深く見ると、`currentMove` が偶数のときは常に `xIsNext === true` であり、`currentMove` が奇数のとき `xIsNext === false` であることに気付くかもしれません。言い換えると、`currentMove` の値さえ知っていれば、`xIsNext` が何であるべきなのかも常に分かるということです。\n\nこれらを両方とも state に格納する理由はありません。実際、冗長な state は常に避けるようにしてください。state に格納するものを単純化すると、バグが減り、コードが理解しやすくなります。`xIsNext` を別の state 変数として保存するのではなく、`currentMove` に基づいて求めるように `Game` を変更しましょう。\n\n```js {4,11,15}\nexport default function Game() {\n  const [history, setHistory] = useState([Array(9).fill(null)]);\n  const [currentMove, setCurrentMove] = useState(0);\n  const xIsNext = currentMove % 2 === 0;\n  const currentSquares = history[currentMove];\n\n  function handlePlay(nextSquares) {\n    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];\n    setHistory(nextHistory);\n    setCurrentMove(nextHistory.length - 1);\n  }\n\n  function jumpTo(nextMove) {\n    setCurrentMove(nextMove);\n  }\n  // ...\n}\n```\n\n`xIsNext` の state 宣言や `setXIsNext` の呼び出しはもはや必要ありません。これにより、コンポーネントのコーディング中にミスがあっても、`xIsNext` が `currentMove` と同期しなくなることはありません。\n\n### まとめ {/*wrapping-up*/}\n\nおめでとうございます！ 以下のような機能を持つ三目並べのゲームが作成できました。\n\n- 三目並べをプレイできる\n- プレーヤがゲームに勝ったときにそれを判定して表示する\n- ゲームの進行に伴って履歴を保存する\n- プレーヤがゲームの履歴を振り返り、盤面の以前のバージョンを確認できる\n\nよくできました！ これで、React の仕組みについてかなりの理解が得られたことを願っています。\n\n最終結果はこちらで確認できます：\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nfunction Square({ value, onSquareClick }) {\n  return (\n    <button className=\"square\" onClick={onSquareClick}>\n      {value}\n    </button>\n  );\n}\n\nfunction Board({ xIsNext, squares, onPlay }) {\n  function handleClick(i) {\n    if (calculateWinner(squares) || squares[i]) {\n      return;\n    }\n    const nextSquares = squares.slice();\n    if (xIsNext) {\n      nextSquares[i] = 'X';\n    } else {\n      nextSquares[i] = 'O';\n    }\n    onPlay(nextSquares);\n  }\n\n  const winner = calculateWinner(squares);\n  let status;\n  if (winner) {\n    status = 'Winner: ' + winner;\n  } else {\n    status = 'Next player: ' + (xIsNext ? 'X' : 'O');\n  }\n\n  return (\n    <>\n      <div className=\"status\">{status}</div>\n      <div className=\"board-row\">\n        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />\n        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />\n        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />\n        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />\n        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />\n      </div>\n      <div className=\"board-row\">\n        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />\n        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />\n        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />\n      </div>\n    </>\n  );\n}\n\nexport default function Game() {\n  const [history, setHistory] = useState([Array(9).fill(null)]);\n  const [currentMove, setCurrentMove] = useState(0);\n  const xIsNext = currentMove % 2 === 0;\n  const currentSquares = history[currentMove];\n\n  function handlePlay(nextSquares) {\n    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];\n    setHistory(nextHistory);\n    setCurrentMove(nextHistory.length - 1);\n  }\n\n  function jumpTo(nextMove) {\n    setCurrentMove(nextMove);\n  }\n\n  const moves = history.map((squares, move) => {\n    let description;\n    if (move > 0) {\n      description = 'Go to move #' + move;\n    } else {\n      description = 'Go to game start';\n    }\n    return (\n      <li key={move}>\n        <button onClick={() => jumpTo(move)}>{description}</button>\n      </li>\n    );\n  });\n\n  return (\n    <div className=\"game\">\n      <div className=\"game-board\">\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n      </div>\n      <div className=\"game-info\">\n        <ol>{moves}</ol>\n      </div>\n    </div>\n  );\n}\n\nfunction calculateWinner(squares) {\n  const lines = [\n    [0, 1, 2],\n    [3, 4, 5],\n    [6, 7, 8],\n    [0, 3, 6],\n    [1, 4, 7],\n    [2, 5, 8],\n    [0, 4, 8],\n    [2, 4, 6],\n  ];\n  for (let i = 0; i < lines.length; i++) {\n    const [a, b, c] = lines[i];\n    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {\n      return squares[a];\n    }\n  }\n  return null;\n}\n```\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\n.square {\n  background: #fff;\n  border: 1px solid #999;\n  float: left;\n  font-size: 24px;\n  font-weight: bold;\n  line-height: 34px;\n  height: 34px;\n  margin-right: -1px;\n  margin-top: -1px;\n  padding: 0;\n  text-align: center;\n  width: 34px;\n}\n\n.board-row:after {\n  clear: both;\n  content: '';\n  display: table;\n}\n\n.status {\n  margin-bottom: 10px;\n}\n.game {\n  display: flex;\n  flex-direction: row;\n}\n\n.game-info {\n  margin-left: 20px;\n}\n```\n\n</Sandpack>\n\nまだ時間がある方や、新しく手に入れた React のスキルを練習したい方向けに、三目並べゲームをさらに改善するためのアイディアをいくつか以下に示します。難易度の低い順にリストアップしています：\n\n1. 現在の着手の部分だけ、ボタンではなく \"You are at move #...\" というメッセージを表示するようにする。\n1. マス目を全部ハードコードするのではなく、`Board` を 2 つのループを使ってレンダーするよう書き直す。\n1. 手順を昇順または降順でソートできるトグルボタンを追加する。\n1. どちらかが勝利したときに、勝利につながった 3 つのマス目をハイライト表示する。引き分けになった場合は、引き分けになったという結果をメッセージに表示する。\n1. 着手履歴リストで、各着手の場所を (row, col) という形式で表示する。\n\nこのチュートリアルを通じて、React のコンセプトである React 要素、コンポーネント、props、state に触れてきました。ゲーム制作においてこれらの概念がどのように機能するかが分かったので、次は [React の流儀](/learn/thinking-in-react)をチェックして、アプリの UI を構築する際にこれらの React のコンセプトがどのように機能するのかを確認してください。"
  },
  {
    "path": "src/content/learn/typescript.md",
    "content": "---\ntitle: TypeScript の使用\nre: https://github.com/reactjs/react.dev/issues/5960\n---\n\n<Intro>\n\nTypeScript は JavaScript コードベースに型定義を追加するための人気の方法です。TypeScript は標準で [JSX をサポート](/learn/writing-markup-with-jsx)しており、プロジェクトに [`@types/react`](https://www.npmjs.com/package/@types/react) と [`@types/react-dom`](https://www.npmjs.com/package/@types/react-dom) を追加することで、React Web に対する完全なサポートを得ることができます。\n\n</Intro>\n\n<YouWillLearn>\n\n* [TypeScript で React コンポーネントを書く方法](/learn/typescript#typescript-with-react-components)\n* [フックの型付けの例](/learn/typescript#example-hooks)\n* [`@types/react` にある一般的な型](/learn/typescript#useful-types)\n* [さらに学習するためのリソース](/learn/typescript#further-learning)\n\n</YouWillLearn>\n\n## インストール {/*installation*/}\n\nすべての[本番環境向け React フレームワーク](/learn/creating-a-react-app#full-stack-frameworks)は TypeScript の使用をサポートしています。フレームワーク個別のガイドに従ってインストールを行ってください。\n\n- [Next.js](https://nextjs.org/docs/app/building-your-application/configuring/typescript)\n- [Remix](https://remix.run/docs/en/1.19.2/guides/typescript)\n- [Gatsby](https://www.gatsbyjs.com/docs/how-to/custom-configuration/typescript/)\n- [Expo](https://docs.expo.dev/guides/typescript/)\n\n### 既存の React プロジェクトに TypeScript を追加する {/*adding-typescript-to-an-existing-react-project*/}\n\nReact の型定義の最新バージョンをインストールするには以下のようにします。\n\n<TerminalBlock>\nnpm install --save-dev @types/react @types/react-dom\n</TerminalBlock>\n\n`tsconfig.json` で以下のコンパイラオプションを設定する必要があります。\n\n1. [`lib`](https://www.typescriptlang.org/tsconfig/#lib) に `dom` が含まれていなければなりません（注：`lib` オプションが指定されない場合、`dom` はデフォルトで含まれます）。\n2. [`jsx`](https://www.typescriptlang.org/tsconfig/#jsx) を有効なオプションのいずれかに設定する必要があります。ほとんどのアプリケーションでは `preserve` で上手くいきます。\n  ライブラリを公開する場合は、選択すべき値について [`jsx` のドキュメンテーション](https://www.typescriptlang.org/tsconfig/#jsx)を参照してください。\n\n## TypeScript で React コンポーネントを書く方法 {/*typescript-with-react-components*/}\n\n<Note>\n\nJSX を含むファイルはすべて `.tsx` というファイル拡張子を使用しなければなりません。これは TypeScript 固有の拡張子であり、ファイルに JSX が含まれていることを TypeScript に伝えるためのものです。\n\n</Note>\n\nTypeScript で React を書くことは、JavaScript で React を書くことに非常に似ています。コンポーネントを扱う際の主な違いは、コンポーネントの props に対して型が指定できることです。これらの型は、正確性チェックと、エディタ内でのインラインドキュメンテーションの表示に使用できます。\n\n[クイックスタート](/learn)ガイドの [`MyButton` コンポーネント](/learn#components)を例にすると、ボタンの `title` に型を追加する方法は以下のようになります。\n\n<Sandpack>\n\n```tsx src/App.tsx active\nfunction MyButton({ title }: { title: string }) {\n  return (\n    <button>{title}</button>\n  );\n}\n\nexport default function MyApp() {\n  return (\n    <div>\n      <h1>Welcome to my app</h1>\n      <MyButton title=\"I'm a button\" />\n    </div>\n  );\n}\n```\n\n```js src/App.js hidden\nimport AppTSX from \"./App.tsx\";\nexport default App = AppTSX;\n```\n</Sandpack>\n\n <Note>\n\n上記のサンドボックスは TypeScript のコードを扱うことができますが、型チェッカは実行されません。つまりこの TypeScript サンドボックスを書き換えて学習することはできますが、型エラーや警告は得られないということです。型チェックが必要な場合は、[TypeScript Playground](https://www.typescriptlang.org/play) を使用するか、より完全な機能を備えたオンラインサンドボックスを使用してください。\n\n</Note>\n\nこのインライン形式の構文は、コンポーネントに対して型を指定する最も簡単な方法ですが、記述するフィールドが複数あるときには扱いにくくなることがあります。代わりに、コンポーネントの props を記述するために `interface` または `type` を使用することができます。\n\n<Sandpack>\n\n```tsx src/App.tsx active\ninterface MyButtonProps {\n  /** The text to display inside the button */\n  title: string;\n  /** Whether the button can be interacted with */\n  disabled: boolean;\n}\n\nfunction MyButton({ title, disabled }: MyButtonProps) {\n  return (\n    <button disabled={disabled}>{title}</button>\n  );\n}\n\nexport default function MyApp() {\n  return (\n    <div>\n      <h1>Welcome to my app</h1>\n      <MyButton title=\"I'm a disabled button\" disabled={true}/>\n    </div>\n  );\n}\n```\n\n```js src/App.js hidden\nimport AppTSX from \"./App.tsx\";\nexport default App = AppTSX;\n```\n\n</Sandpack>\n\nコンポーネントの props を記述する型は、必要に応じて単純なものでも複雑なものでも構いませんが、`type` または `interface` で記述されたオブジェクト型であるべきです。TypeScript でのオブジェクトの記述方法については[オブジェクト型](https://www.typescriptlang.org/docs/handbook/2/objects.html)で学ぶことができます。いくつかの異なる型のうちの 1 つの型になる props を記述するための[ユニオン型](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types)や、より高度な使用例である [Creating Types from Types](https://www.typescriptlang.org/docs/handbook/2/types-from-types.html) ガイドも参照してください。\n\n\n## 例：フック {/*example-hooks*/}\n\n`@types/react` にある型定義には、組み込みのフックに対する型が含まれており、追加のセットアップなしにあなたのコンポーネント内で使用することができます。これらはあなたがコンポーネントに書くコードを考慮できるように作られており、多くの場合は[型の推論](https://www.typescriptlang.org/docs/handbook/type-inference.html)が働くため、理想的にはこまごまと型の指定を行う必要がないようになっています。\n\nとはいえ、フックの型をどのように指定するかについて、ここでいくつかの例を見ておきましょう。\n\n### `useState` {/*typing-usestate*/}\n\n[`useState` フック](/reference/react/useState)は、初期 state として渡された値を利用して、その値の型が何であるべきかを決定します。例えば：\n\n```ts\n// Infer the type as \"boolean\"\nconst [enabled, setEnabled] = useState(false);\n```\n\nこれで `enabled` に `boolean` 型が割り当てられ、また `setEnabled` は引数として `boolean` 値または `boolean` を返す関数を受け取る関数になります。state の型を明示的に指定したい場合は、`useState` の呼び出しに型引数を渡すことで行えます。\n\n```ts\n// Explicitly set the type to \"boolean\"\nconst [enabled, setEnabled] = useState<boolean>(false);\n```\n\n上記の例ではあまりこうする意義はありませんが、明示的に型を指定したい一般的なケースとして、ユニオン型を持たせたい場合があります。例えば、以下では `status` はいくつかの異なる文字列のいずれかになります。\n\n```ts\ntype Status = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nconst [status, setStatus] = useState<Status>(\"idle\");\n```\n\nまたは、[state の構造化の原則](/learn/choosing-the-state-structure#principles-for-structuring-state)で推奨されているように、関連する state をオブジェクトとしてグループ化し、オブジェクト型を介して可能性のある状態を記述することができます。\n\n```ts\ntype RequestState =\n  | { status: 'idle' }\n  | { status: 'loading' }\n  | { status: 'success', data: any }\n  | { status: 'error', error: Error };\n\nconst [requestState, setRequestState] = useState<RequestState>({ status: 'idle' });\n```\n\n### `useReducer` {/*typing-usereducer*/}\n\n[`useReducer` フック](/reference/react/useReducer) は、リデューサ関数と初期 state を受け取る、より複雑なフックです。リデューサ関数の型は初期 state から推論されます。`useReducer` の呼び出しに型引数を含めることで state の型を指定することもできますが、通常は代わりに初期 state 自体に型を指定する方が良いでしょう。\n\n<Sandpack>\n\n```tsx src/App.tsx active\nimport {useReducer} from 'react';\n\ninterface State {\n   count: number\n};\n\ntype CounterAction =\n  | { type: \"reset\" }\n  | { type: \"setCount\"; value: State[\"count\"] }\n\nconst initialState: State = { count: 0 };\n\nfunction stateReducer(state: State, action: CounterAction): State {\n  switch (action.type) {\n    case \"reset\":\n      return initialState;\n    case \"setCount\":\n      return { ...state, count: action.value };\n    default:\n      throw new Error(\"Unknown action\");\n  }\n}\n\nexport default function App() {\n  const [state, dispatch] = useReducer(stateReducer, initialState);\n\n  const addFive = () => dispatch({ type: \"setCount\", value: state.count + 5 });\n  const reset = () => dispatch({ type: \"reset\" });\n\n  return (\n    <div>\n      <h1>Welcome to my counter</h1>\n\n      <p>Count: {state.count}</p>\n      <button onClick={addFive}>Add 5</button>\n      <button onClick={reset}>Reset</button>\n    </div>\n  );\n}\n\n```\n\n```js src/App.js hidden\nimport AppTSX from \"./App.tsx\";\nexport default App = AppTSX;\n```\n\n</Sandpack>\n\n\nここではいくつかの重要な場所で TypeScript が使用されています。\n\n - `interface State` でリデューサの state の型を指定する。\n - `type CounterAction` でリデューサにディスパッチできる各種のアクションを記述する。\n - `const initialState: State` で初期 state の型、およびデフォルトで `useReducer` によって使用される型を指定する。\n - `stateReducer(state: State, action: CounterAction): State` でリデューサ関数の引数と返り値の型を指定する。\n\n`initialState` に型を指定するよりも明示的な代替手段として、`useReducer` に型引数を渡すこともできます。\n\n```ts\nimport { stateReducer, State } from './your-reducer-implementation';\n\nconst initialState = { count: 0 };\n\nexport default function App() {\n  const [state, dispatch] = useReducer<State>(stateReducer, initialState);\n}\n```\n\n### `useContext` {/*typing-usecontext*/}\n\n[`useContext` フック](/reference/react/useContext)は、props を使って多数のコンポーネントを経由させることなく、コンポーネントツリーを通じてデータを下に渡すための技術です。使用するにはプロバイダコンポーネントを作成し、多くの場合は子コンポーネントで値を受け取るためのフックを用います。\n\nコンテクストが提供する値の型は、`createContext` の呼び出しに渡す値から推論されます。\n\n<Sandpack>\n\n```tsx src/App.tsx active\nimport { createContext, useContext, useState } from 'react';\n\ntype Theme = \"light\" | \"dark\" | \"system\";\nconst ThemeContext = createContext<Theme>(\"system\");\n\nconst useGetTheme = () => useContext(ThemeContext);\n\nexport default function MyApp() {\n  const [theme, setTheme] = useState<Theme>('light');\n\n  return (\n    <ThemeContext value={theme}>\n      <MyComponent />\n    </ThemeContext>\n  )\n}\n\nfunction MyComponent() {\n  const theme = useGetTheme();\n\n  return (\n    <div>\n      <p>Current theme: {theme}</p>\n    </div>\n  )\n}\n```\n\n```js src/App.js hidden\nimport AppTSX from \"./App.tsx\";\nexport default App = AppTSX;\n```\n\n</Sandpack>\n\n意味のあるデフォルト値が存在する場合にはこれでうまくいきます。しかし時にはそうではない場合もあり、デフォルト値として `null` が適切に感じられることもあるでしょう。型システムにあなたのコードを理解させるため、`createContext` 呼び出しで `ContextShape | null` と明示的に指定する必要があります。\n\nこれにより、コンテクストを利用する側で `| null` の可能性を排除する必要が生じます。お勧めの方法は、値が存在することをフックで実行時にチェックし、存在しない場合にエラーをスローするようにすることです。\n\n```js {5, 16-20}\nimport { createContext, useContext, useState, useMemo } from 'react';\n\n// This is a simpler example, but you can imagine a more complex object here\ntype ComplexObject = {\n  kind: string\n};\n\n// The context is created with `| null` in the type, to accurately reflect the default value.\nconst Context = createContext<ComplexObject | null>(null);\n\n// The `| null` will be removed via the check in the Hook.\nconst useGetComplexObject = () => {\n  const object = useContext(Context);\n  if (!object) { throw new Error(\"useGetComplexObject must be used within a Provider\") }\n  return object;\n}\n\nexport default function MyApp() {\n  const object = useMemo(() => ({ kind: \"complex\" }), []);\n\n  return (\n    <Context value={object}>\n      <MyComponent />\n    </Context>\n  )\n}\n\nfunction MyComponent() {\n  const object = useGetComplexObject();\n\n  return (\n    <div>\n      <p>Current object: {object.kind}</p>\n    </div>\n  )\n}\n```\n\n### `useMemo` {/*typing-usememo*/}\n\n<Note>\n\n[React Compiler](/learn/react-compiler) は値や関数の自動的なメモ化を行うことで、手作業による `useMemo` 呼び出しの必要性を軽減します。自動でメモ化を行うためにコンパイラを使用可能です。\n\n</Note>\n\n[`useMemo`](/reference/react/useMemo) フックは、関数呼び出しからの値の作成/再アクセスを行い、2 番目の引数として渡された依存配列が変更されたときにのみ関数を再実行します。フックの呼び出し結果の型は、1 番目の引数として指定した関数の返り値の型から推論されます。フックに型引数を指定することで明示的にすることができます。\n\n```ts\n// The type of visibleTodos is inferred from the return value of filterTodos\nconst visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);\n```\n\n\n### `useCallback` {/*typing-usecallback*/}\n\n<Note>\n\n[React Compiler](/learn/react-compiler) は値や関数の自動的なメモ化を行うことで、手作業による `useCallback` 呼び出しの必要性を軽減します。自動でメモ化を行うためにコンパイラを使用可能です。\n\n</Note>\n\n[`useCallback`](/reference/react/useCallback) は、第 2 引数に渡される依存配列が同じである限り、関数への安定した参照を提供するものです。`useMemo` と同様に、関数の型は 1 番目の引数として指定した関数から推論され、フックに型引数を指定することでより明示的にすることができます。\n\n\n```ts\nconst handleClick = useCallback(() => {\n  // ...\n}, [todos]);\n```\n\nTypeScript の strict モードで作業している場合、`useCallback` ではコールバックの引数に型を追加する必要があります。これは、コールバックの型が関数から推論されるため、引数がないと型を完全に決定できないからです。\n\n好みのコードスタイルによっては、React が提供する `*EventHandler` 関数型を使用して、コールバックを定義しながらイベントハンドラの型を提供することができます：\n\n```ts\nimport { useState, useCallback } from 'react';\n\nexport default function Form() {\n  const [value, setValue] = useState(\"Change me\");\n\n  const handleChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>((event) => {\n    setValue(event.currentTarget.value);\n  }, [setValue])\n\n  return (\n    <>\n      <input value={value} onChange={handleChange} />\n      <p>Value: {value}</p>\n    </>\n  );\n}\n```\n\n## 便利な型 {/*useful-types*/}\n\n`@types/react` パッケージには非常に様々な型が存在しており、React と TypeScript の扱いに慣れたら目を通す価値があります。これらは [DefinitelyTyped 内の React のフォルダ](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts)で見ることができます。ここでは一般的な型をいくつか紹介します。\n\n### DOM イベント {/*typing-dom-events*/}\n\nReact で DOM イベントを扱うとき、イベントの型はイベントハンドラから推論できることもあります。しかし、イベントハンドラに渡すための関数を抽出して別に定義したい場合は、イベントの型を明示的に設定する必要があります。\n\n<Sandpack>\n\n```tsx src/App.tsx active\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [value, setValue] = useState(\"Change me\");\n\n  function handleChange(event: React.ChangeEvent<HTMLInputElement>) {\n    setValue(event.currentTarget.value);\n  }\n\n  return (\n    <>\n      <input value={value} onChange={handleChange} />\n      <p>Value: {value}</p>\n    </>\n  );\n}\n```\n\n```js src/App.js hidden\nimport AppTSX from \"./App.tsx\";\nexport default App = AppTSX;\n```\n\n</Sandpack>\n\nReact 型定義には多くの種類のイベントが提供されています。完全なリストは[こちら](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b580df54c0819ec9df62b0835a315dd48b8594a9/types/react/index.d.ts#L1247C1-L1373)にあり、[DOM で最も使われるイベント](https://developer.mozilla.org/en-US/docs/Web/Events)に基づいています。\n\n必要なイベント型を見つけたい場合は、まず使用しているイベントハンドラのホバー情報を見てみると、イベントの型が表示されます。\n\nこのリストに含まれていないイベントを使用する必要がある場合は、すべてのイベントの基本型である `React.SyntheticEvent` 型を使用することができます。\n\n### 子要素 {/*typing-children*/}\n\nコンポーネントの子要素を型として記述する一般的な方法は 2 つあります。1 つ目は `React.ReactNode` 型を使用する方法であり、これは JSX で子要素として渡すことが可能なすべての型のユニオン型です。\n\n```ts\ninterface ModalRendererProps {\n  title: string;\n  children: React.ReactNode;\n}\n```\n\nこれは子要素の非常に包括的な定義です。2 つ目は `React.ReactElement` 型を使用する方法です。こちらは JSX 要素のみを指し、文字列や数値のような JavaScript のプリミティブは含まれません。\n\n```ts\ninterface ModalRendererProps {\n  title: string;\n  children: React.ReactElement;\n}\n```\n\n子要素が特定の JSX 要素の型であることを TypeScript で記述することはできないことに注意してください。子要素として `<li>` のみを受け入れるコンポーネントを型システムで記述することはできません。\n\n[こちらの TypeScript プレイグラウンド](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wChSB6CxYmAOmXRgDkIATJOdNJMGAZzgwAFpxAR+8YADswAVwGkZMJFEzpOjDKw4AFHGEEBvUnDhphwADZsi0gFw0mDWjqQBuUgF9yaCNMlENzgAXjgACjADfkctFnYkfQhDAEpQgD44AB42YAA3dKMo5P46C2tbJGkvLIpcgt9-QLi3AEEwMFCItJDMrPTTbIQ3dKywdIB5aU4kKyQQKpha8drhhIGzLLWODbNs3b3s8YAxKBQAcwXpAThMaGWDvbH0gFloGbmrgQfBzYpd1YjQZbEYARkB6zMwO2SHSAAlZlYIBCdtCRkZpHIrFYahQYQD8UYYFA5EhcfjyGYqHAXnJAsIUHlOOUbHYhMIIHJzsI0Qk4P9SLUBuRqXEXEwAKKfRZcNA8PiCfxWACecAAUgBlAAacFm80W-CU11U6h4TgwUv11yShjgJjMLMqDnN9Dilq+nh8pD8AXgCHdMrCkWisVoAet0R6fXqhWKhjKllZVVxMcavpd4Zg7U6Qaj+2hmdG4zeRF10uu-Aeq0LBfLMEe-V+T2L7zLVu+FBWLdLeq+lc7DYFf39deFVOotMCACNOCh1dq219a+30uC8YWoZsRyuEdjkevR8uvoVMdjyTWt4WiSSydXD4NqZP4AymeZE072ZzuUeZQKheQgA)で、`React.ReactNode` と `React.ReactElement` の両方の例を型チェッカ付きで確認することができます。\n\n### スタイル props {/*typing-style-props*/}\n\nReact でインラインスタイルを使用する際には、`style` プロパティに渡されるオブジェクト型を記述するために `React.CSSProperties` を使用することができます。この型は可能な CSS プロパティすべてのユニオンであり、有効な CSS プロパティを props として `style` に渡していることを保証し、エディタで自動補完を得るための良い方法です。\n\n```ts\ninterface MyComponentProps {\n  style: React.CSSProperties;\n}\n```\n\n## さらに学ぶ {/*further-learning*/}\n\nこのガイドでは React で TypeScript を使用するための基本的な方法を紹介しましたが、まだ学ぶべきことはたくさんあります。\nドキュメントの個々の API ページには、TypeScript とともに使用する方法についてのより詳細な説明がある場合があります。\n\n以下のリソースがお勧めです。\n\n - [The TypeScript handbook](https://www.typescriptlang.org/docs/handbook/) は TypeScript の公式ドキュメントであり、ほとんどの主要な言語機能をカバーしています。\n\n - [TypeScript のリリースノート](https://devblogs.microsoft.com/typescript/)では新機能が詳細に解説されています。\n\n - [React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/) はコミュニティによってメンテナンスされている、React で TypeScript を使用するためのチートシートです。多くの有用なエッジケースをカバーしており、このドキュメントよりも広範な解説が得られます。\n\n - [TypeScript コミュニティ Discord](https://discord.com/invite/typescript) は、TypeScript と React の問題について質問したり、助けを得たりするための素晴らしい場所です。\n"
  },
  {
    "path": "src/content/learn/understanding-your-ui-as-a-tree.md",
    "content": "---\ntitle: UI をツリーとして理解する\n---\n\n<Intro>\n\nReact アプリは、多数のコンポーネントが互いにネストされることで形成されます。React はどのようにアプリのコンポーネント構造を管理しているのでしょうか？\n\nReact をはじめとする多くの UI ライブラリは、UI をツリーとしてモデル化します。アプリをツリーとして捉えることにより、コンポーネント間の関係を理解するのに役立ちます。これを理解することで、これから学んでいくパフォーマンスや state 管理に関連した問題をデバッグするのに役立つでしょう。\n\n</Intro>\n\n<YouWillLearn>\n\n* React にはコンポーネント構造がどのように「見える」のか\n* レンダーツリーとは何で、何の役に立つのか\n* モジュール依存ツリーとは何で、何の役に立つのか\n\n</YouWillLearn>\n\n## UI をツリーとして理解する {/*your-ui-as-a-tree*/}\n\nツリーとはアイテム間の関係を表すモデルの一種です。UI はよくツリー構造を使用して表現されます。例えば、ブラウザは HTML ([DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction)) や CSS ([CSSOM](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model)) をモデル化するためにツリー構造を使用します。モバイルプラットフォームもビューの階層構造を表現するためにツリーを使用します。\n\n<Diagram name=\"preserving_state_dom_tree\" height={193} width={864} alt=\"横に並んだ3つのセクションからなる図。最初のセクションには、'Component A'、'Component B'、'Component C' とラベル付けされた 3 つの長方形が縦に積み重ねられている。次のパネルへの遷移は 'React' とラベル付けされた React ロゴが上にある矢印で示されている。中央セクションには、ルートが 'A' で子が 'B' と 'C' のラベルが付いたコンポーネントのツリーがある。次のセクションへの遷移も、'React DOM' とラベル付けされた React ロゴが上にある矢印で示されている。最後である 3 つ目のセクションは、8 つのノードからなるツリーを含むブラウザのワイヤーフレームで、そのうちの一部だけが強調表示され、中央のセクションからのサブツリーを示している。\">\n\nReact はコンポーネントから UI ツリーを作成する。この例では、UI ツリーは DOM へのレンダーに使用されている。\n</Diagram>\n\nブラウザやモバイルプラットフォームと同様に、React もツリー構造を使用して React アプリ内のコンポーネント間の関係を管理し、モデル化します。そのようなツリーは、React アプリ内をデータがどのように流れるか理解し、レンダーやアプリサイズを最適化する際の有用なツールとなります。\n\n## レンダーツリー {/*the-render-tree*/}\n\nコンポーネントの主要な特徴のひとつは、コンポーネント同士を組み合わせられることです。[コンポーネントをネストする](/learn/your-first-component#nesting-and-organizing-components)ことで、親コンポーネント・子コンポーネントという概念が発生します。その親コンポーネントもまた、別のコンポーネントの子かもしれません。\n\nReact アプリをレンダーする際、この関係性をツリーとしてモデル化することができます。これをレンダーツリーと呼びます。\n\n以下は、ひらめきを与えてくれる格言をレンダーするための React アプリです。\n\n<Sandpack>\n\n```js src/App.js\nimport FancyText from './FancyText';\nimport InspirationGenerator from './InspirationGenerator';\nimport Copyright from './Copyright';\n\nexport default function App() {\n  return (\n    <>\n      <FancyText title text=\"Get Inspired App\" />\n      <InspirationGenerator>\n        <Copyright year={2004} />\n      </InspirationGenerator>\n    </>\n  );\n}\n\n```\n\n```js src/FancyText.js\nexport default function FancyText({title, text}) {\n  return title\n    ? <h1 className='fancy title'>{text}</h1>\n    : <h3 className='fancy cursive'>{text}</h3>\n}\n```\n\n```js src/InspirationGenerator.js\nimport * as React from 'react';\nimport quotes from './quotes';\nimport FancyText from './FancyText';\n\nexport default function InspirationGenerator({children}) {\n  const [index, setIndex] = React.useState(0);\n  const quote = quotes[index];\n  const next = () => setIndex((index + 1) % quotes.length);\n\n  return (\n    <>\n      <p>Your inspirational quote is:</p>\n      <FancyText text={quote} />\n      <button onClick={next}>Inspire me again</button>\n      {children}\n    </>\n  );\n}\n```\n\n```js src/Copyright.js\nexport default function Copyright({year}) {\n  return <p className='small'>©️ {year}</p>;\n}\n```\n\n```js src/quotes.js\nexport default [\n  \"Don’t let yesterday take up too much of today.” — Will Rogers\",\n  \"Ambition is putting a ladder against the sky.\",\n  \"A joy that's shared is a joy made double.\",\n  ];\n```\n\n```css\n.fancy {\n  font-family: 'Georgia';\n}\n.title {\n  color: #007AA3;\n  text-decoration: underline;\n}\n.cursive {\n  font-style: italic;\n}\n.small {\n  font-size: 10px;\n}\n```\n\n</Sandpack>\n\n<Diagram name=\"render_tree\" height={250} width={500} alt=\"5 つのノードからなるツリー。各ノードはコンポーネントを表している。ツリーのルートは App で、それから矢印が 2 つ伸びており、'InspirationGenerator' と 'FancyText' を指している。矢印には 'renders' という言葉が書かれている。'InspirationGenerator' のノードからも矢印が 2 つ伸びており、'FancyText' と 'Copyright' のノードを指している。\">\n\nReact は、レンダーされたコンポーネントから構成される UI ツリーである*レンダーツリー*を作成する\n\n\n</Diagram>\n\nこのアプリから、上のようなレンダーツリーを構築することができます。\n\nツリー構造はノードで構成されており、各ノードがコンポーネントを表します。`App`、`FancyText`、`Copyright` などはすべてこのツリーのノードです。\n\nReact レンダーツリーのルートノードは、アプリの[ルートコンポーネント](/learn/importing-and-exporting-components#the-root-component-file)となります。この場合、ルートコンポーネントは `App` であり、React が最初にレンダーするコンポーネントです。ツリーの各矢印は、親コンポーネントから子コンポーネントに伸びています。\n\n<DeepDive>\n\n#### レンダーツリー内の HTML タグはどこに？ {/*where-are-the-html-elements-in-the-render-tree*/}\n\n上記のレンダーツリーの図には、各コンポーネントがレンダーする HTML タグについては載っていません。これは、レンダーツリーとは React の[コンポーネント](learn/your-first-component#components-ui-building-blocks)だけで構成されるものだからです。\n\nUI フレームワークとしての React は特定のプラットフォームに依存しません。react.dev ではウェブへレンダーする例が紹介されており、そこでは UI のプリミティブとして HTML マークアップが使用されます。しかし、React アプリは同様にモバイルやデスクトッププラットフォームにレンダーすることも可能であり、そこでは [UIView](https://developer.apple.com/documentation/uikit/uiview) や [FrameworkElement](https://learn.microsoft.com/en-us/dotnet/api/system.windows.frameworkelement?view=windowsdesktop-7.0) のような別の UI プリミティブが使用されるでしょう。\n\nこれらのプラットフォームの UI プリミティブは React の一部ではありません。React のレンダーツリーを考えることにより、アプリがどのプラットフォームにレンダーされるのかとは独立して、React アプリを理解できるようになります。\n\n</DeepDive>\n\nレンダーツリーは、React アプリケーションにおける 1 回のレンダーを表します。[条件付きレンダー](/learn/conditional-rendering)を使用することで、親コンポーネントは渡されたデータに応じて異なる子をレンダーすることができます。\n\nアプリを更新して、格言とカラーのいずれかが条件付きでレンダーされるようにしてみましょう。\n\n<Sandpack>\n\n```js src/App.js\nimport FancyText from './FancyText';\nimport InspirationGenerator from './InspirationGenerator';\nimport Copyright from './Copyright';\n\nexport default function App() {\n  return (\n    <>\n      <FancyText title text=\"Get Inspired App\" />\n      <InspirationGenerator>\n        <Copyright year={2004} />\n      </InspirationGenerator>\n    </>\n  );\n}\n\n```\n\n```js src/FancyText.js\nexport default function FancyText({title, text}) {\n  return title\n    ? <h1 className='fancy title'>{text}</h1>\n    : <h3 className='fancy cursive'>{text}</h3>\n}\n```\n\n```js src/Color.js\nexport default function Color({value}) {\n  return <div className=\"colorbox\" style={{backgroundColor: value}} />\n}\n```\n\n```js src/InspirationGenerator.js\nimport * as React from 'react';\nimport inspirations from './inspirations';\nimport FancyText from './FancyText';\nimport Color from './Color';\n\nexport default function InspirationGenerator({children}) {\n  const [index, setIndex] = React.useState(0);\n  const inspiration = inspirations[index];\n  const next = () => setIndex((index + 1) % inspirations.length);\n\n  return (\n    <>\n      <p>Your inspirational {inspiration.type} is:</p>\n      {inspiration.type === 'quote'\n      ? <FancyText text={inspiration.value} />\n      : <Color value={inspiration.value} />}\n\n      <button onClick={next}>Inspire me again</button>\n      {children}\n    </>\n  );\n}\n```\n\n```js src/Copyright.js\nexport default function Copyright({year}) {\n  return <p className='small'>©️ {year}</p>;\n}\n```\n\n```js src/inspirations.js\nexport default [\n  {type: 'quote', value: \"Don’t let yesterday take up too much of today.” — Will Rogers\"},\n  {type: 'color', value: \"#B73636\"},\n  {type: 'quote', value: \"Ambition is putting a ladder against the sky.\"},\n  {type: 'color', value: \"#256266\"},\n  {type: 'quote', value: \"A joy that's shared is a joy made double.\"},\n  {type: 'color', value: \"#F9F2B4\"},\n];\n```\n\n```css\n.fancy {\n  font-family: 'Georgia';\n}\n.title {\n  color: #007AA3;\n  text-decoration: underline;\n}\n.cursive {\n  font-style: italic;\n}\n.small {\n  font-size: 10px;\n}\n.colorbox {\n  height: 100px;\n  width: 100px;\n  margin: 8px;\n}\n```\n</Sandpack>\n\n<Diagram name=\"conditional_render_tree\" height={250} width={561} alt=\"6 つのノードからなるツリー。ツリーのルートは App で、それから矢印が 2 つ伸びており、'InspirationGenerator' と 'FancyText' を指している。矢印は実線であり 'renders' と書かれている。'InspirationGenerator' のノードからは矢印が 3 つ伸びている。そのうち 'FancyText' と 'Color' への矢印は点線であり 'renders?' と書かれている。もう 1 本の矢印は実線で 'Copyright' のノードを指しており、'renders' と書かれている。\">\n\n条件付きレンダーにより、違うレンダーではレンダーツリーが異なるコンポーネントをレンダーする。\n\n</Diagram>\n\nこの例では、`inspiration.type` の値によって、`<FancyText>` または `<Color>` のいずれかがレンダーされます。一連のレンダーが起きるたびに、レンダーツリーは異なったものになる可能性があるのです。\n\n毎回のレンダーごとにレンダーツリーが異なることがあるにせよ、このようなツリーは一般的に、React アプリケーションにおいて*トップレベル*コンポーネントと*リーフ（葉, 末端）* コンポーネントがどれなのかを理解するのに役立ちます。トップレベルコンポーネントとはルートコンポーネントに最も近いコンポーネントです。下にあるすべてのコンポーネントのレンダーパフォーマンスに影響を与え、しばしばとても複雑な内容を含んでいます。リーフコンポーネントはツリーの下側にあり、子コンポーネントを持たず、通常は頻繁に再レンダーされます。\n\nこれらのカテゴリのコンポーネントを特定することにより、アプリケーションのデータの流れとパフォーマンスを理解するのに役立ちます。\n\n## モジュール依存関係ツリー {/*the-module-dependency-tree*/}\n\nReact アプリにおいて、ツリー構造で関係性をモデル化できるものがもうひとつあります。アプリのモジュールの依存関係です。コンポーネントやロジックを別々のファイルに[分割する](/learn/importing-and-exporting-components#exporting-and-importing-a-component)ことで、[JS モジュール](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)を作成し、コンポーネントや関数や定数をエクスポートします。\n\nモジュール依存関係ツリーにおいては、各ノードはモジュールとなり、それぞれの枝はそのモジュール内の `import` 文を表します。\n\n先ほどのひらめきアプリの例では、以下のようなモジュール依存関係ツリー（あるいは単に依存関係ツリー）を作成することができます。\n\n<Diagram name=\"module_dependency_tree\" height={250} width={658} alt=\"7 つのノードからなるツリー。それぞれのノードにモジュール名が書かれている。ツリーのトップレベルのノードには 'App.js' と書かれている。そこから 3 つの矢印が伸びており、'InspirationGenerator.js'、'FancyText.js'、'Copyright.js' を指している。矢印には 'imports' と書かれている。'InspirationGenerator.js' からも 3 つの矢印が伸びており、'FancyText.js'、'Color.js'、'inspirations.js' を指している。矢印には 'imports' と書かれている。\">\n\nひらめきアプリのモジュール依存関係ツリー\n\n</Diagram>\n\nこのツリーのルートノードはルートモジュールで、エントリーポイントファイルとも呼ばれます。これがルートコンポーネントを含んだモジュールであることも多いでしょう。\n\n同じアプリのレンダーツリーと比べると、似た部分もありますが、いくつか注目すべき違いがあります。\n\n* ツリーを構成するノードが表しているのはコンポーネントではなくモジュールです。\n* コンポーネントの書かれていない `inspirations.js` のようなモジュールもこのツリーには含まれています。レンダーツリーはコンポーネントのみを含みます。\n* `Copyright.js` は `App.js` の下に表示されていますが、レンダーツリーの方では、`Copyright` コンポーネントは `InspirationGenerator` の子でした。これは、`InspirationGenerator` が props である [children](/learn/passing-props-to-a-component#passing-jsx-as-children) 経由で JSX を受け付けるためです。`Copyright` を子コンポーネントとしてレンダーしてはいますが、それに対応するモジュールをインポートしているわけではありません。\n\n依存関係ツリーは、React アプリを実行するためにどのモジュールが必要かを判断するのに役立ちます。通常、React アプリを本番環境用にビルドする際には、クライアントに送信するために必要な JavaScript をすべてバンドルにまとめるというビルドステップが存在します。これを担当するツールは[バンドラ](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview#the_modern_tooling_ecosystem)と呼ばれ、バンドラは依存関係ツリーを使用することで、どのモジュールを含めるべきかを決定します。\n\nアプリが成長するにつれて、バンドルサイズも大きくなります。バンドルサイズが大きいと、クライアントがダウンロードして実行するのにコストがかかります。バンドルサイズが大きいと、UI が描画されるまでの時間も遅くなります。アプリの依存関係ツリーを把握することで、これらの問題のデバッグに役立つでしょう。\n\n[comment]: <> (perhaps we should also deep dive on conditional imports)\n\n<Recap>\n\n* ツリー構造とは、何らかの物どうしの関係性を表現する際の一般的な方法である。UI をモデル化するために多用される。\n* レンダーツリーは、1 回のレンダーにおける React コンポーネント間のネスト関係を表現するものである。\n* 条件付きレンダーにより、毎回のレンダー間でレンダーツリーは変化する可能性がある。例えば props の値が変わることでコンポーネントは異なる子コンポーネントをレンダーする可能性がある。\n* レンダーツリーの概念は、トップレベルとリーフコンポーネントを特定するのに役立つ。トップレベルのコンポーネントはそれらの下の全コンポーネントのレンダーパフォーマンスに影響を与え、リーフコンポーネントは頻繁に再レンダーされる。これらを把握することでレンダーパフォーマンスの理解とデバッグに役立つ。\n* 依存関係ツリーは、React アプリ内のモジュール依存関係を表現する。\n* 依存関係ツリーは、アプリを届けるために必要なコードをバンドルするビルドツールによって使用される。\n* 依存関係ツリーは、ペイントまでの時間を遅らせるバンドルサイズの問題をデバッグしたり、どのコードをバンドル対象とするか最適化するきっかけとなることに役立つ。\n\n</Recap>\n\n[TODO]: <> (Add challenges)\n"
  },
  {
    "path": "src/content/learn/updating-arrays-in-state.md",
    "content": "---\ntitle: state 内の配列の更新\n---\n\n<Intro>\n\nJavaScript の配列はミュータブル（mutable, 書き換え可能）なものですが、state に格納する場合はイミュータブル（immutable, 書き換え不能）として扱うべきです。オブジェクトの時と同様に、state に保存された配列を更新する場合は、新しい配列を作成して（または既存の配列をコピーして）、その新しい配列で state をセットする必要があります。\n\n</Intro>\n\n<YouWillLearn>\n\n- React の state 内にある配列に対し要素の追加、削除、変更を行う方法\n- 配列内にあるオブジェクトを更新する方法\n- Immer を使って配列コピーのためのコードの冗長さを緩和する方法\n\n</YouWillLearn>\n\n## 配列を書き換えずに更新する {/*updating-arrays-without-mutation*/}\n\nJavaScript において、配列とは単なるオブジェクトの一種です。[オブジェクトのときと同様に](/learn/updating-objects-in-state)、**React の state 内にある配列は、読み取り専用として扱う必要があります**。これは、`arr[0] = 'bird'` のような形で配列内の要素に再代入を行ってはならず、`push()` や `pop()` のような配列をミューテーション（mutation, 書き換え）するメソッドを使ってもいけないということです。\n\n代わりに、いつでも配列を更新したいときには、新しい配列を state セッタ関数に渡す必要があります。これを実現するために、state から取り出した元の配列から、`filter()` や `map()` といった書き換えを行わないメソッドを呼び出すことで、新しい配列を作成できます。そして、結果として得られた新しい配列を state にセットすることができます。\n\n以下は、一般的な配列操作の参照表です。React の state 内の配列を扱う際には、左の列にあるメソッドを避けて、右の列にあるメソッドを使用する必要があります :\n\n|           | 使わない（配列を書き換える）        | 使う（新しい配列を返す）                                            |\n| --------- | ----------------------------------- | ------------------------------------------------------------------- |\n| 追加      | `push`, `unshift`                   | `concat`, `[...arr]` spread syntax ([例](#adding-to-an-array))      |\n| 削除      | `pop`, `shift`, `splice`            | `filter`, `slice` ([例](#removing-from-an-array))                   |\n| 要素置換  | `splice`, `arr[i] = ...` 代入文     | `map` ([例](#replacing-items-in-an-array))                          |\n| ソート    | `reverse`, `sort`                   | 先に配列をコピー ([例](#making-other-changes-to-an-array))          |\n\nまた、どちらの列のメソッドも使用できるようにしてくれる [Immer を使う](#write-concise-update-logic-with-immer)方法もあります。\n\n<Pitfall>\n\n残念ながら、[`slice`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) と [`splice`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) は名前が似ているものの、非常に異なるものです。\n\n* `slice` は配列や配列の一部をコピーします。\n* `splice` は（要素の挿入や削除という）配列の**ミューテーション**を行います。\n\nReact では、state 内のオブジェクトや配列を書き換えたくないため、`slice`（`p` なし！）の方をより頻繁に使用します。[オブジェクトの更新](/learn/updating-objects-in-state)で、ミューテーションとは何か、それがなぜ state において推奨されないかについて説明されています。\n\n</Pitfall>\n\n### 配列に要素を追加 {/*adding-to-an-array*/}\n\n`push()` は配列の書き換えを行います。これは避けるべきです。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nlet nextId = 0;\n\nexport default function List() {\n  const [name, setName] = useState('');\n  const [artists, setArtists] = useState([]);\n\n  return (\n    <>\n      <h1>Inspiring sculptors:</h1>\n      <input\n        value={name}\n        onChange={e => setName(e.target.value)}\n      />\n      <button onClick={() => {\n        artists.push({\n          id: nextId++,\n          name: name,\n        });\n      }}>Add</button>\n      <ul>\n        {artists.map(artist => (\n          <li key={artist.id}>{artist.name}</li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n```css\nbutton { margin-left: 5px; }\n```\n\n</Sandpack>\n\n代わりに、既存の要素の末尾に新しい要素が加わった、*新しい*配列を作成します。これには複数の方法がありますが、もっとも簡単なのは `...` という[配列スプレッド構文](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#spread_in_array_literals)を使用することです。\n\n```js\nsetArtists( // Replace the state\n  [ // with a new array\n    ...artists, // that contains all the old items\n    { id: nextId++, name: name } // and one new item at the end\n  ]\n);\n```\n\nこれで正しく動作します。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nlet nextId = 0;\n\nexport default function List() {\n  const [name, setName] = useState('');\n  const [artists, setArtists] = useState([]);\n\n  return (\n    <>\n      <h1>Inspiring sculptors:</h1>\n      <input\n        value={name}\n        onChange={e => setName(e.target.value)}\n      />\n      <button onClick={() => {\n        setArtists([\n          ...artists,\n          { id: nextId++, name: name }\n        ]);\n      }}>Add</button>\n      <ul>\n        {artists.map(artist => (\n          <li key={artist.id}>{artist.name}</li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n```css\nbutton { margin-left: 5px; }\n```\n\n</Sandpack>\n\n配列スプレッド構文を使用すれば、元の `...artists` の*先頭*に要素を追加することもできます：\n\n```js\nsetArtists([\n  { id: nextId++, name: name },\n  ...artists // Put old items at the end\n]);\n```\n\nこのように、スプレッド構文は、`push()` で配列の末尾に追加することと `unshift()` で配列の先頭に追加することの両方の役割を果たせます。上記のサンドボックスで試してみてください！\n\n### 配列から要素を削除 {/*removing-from-an-array*/}\n\n配列から要素を削除する最も簡単な方法は、それを*フィルタリング*して取り除いた、新しい配列を作ることです。つまり、その要素を含まない新しい配列を生成します。これには `filter` メソッドを使用します。例えば：\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nlet initialArtists = [\n  { id: 0, name: 'Marta Colvin Andrade' },\n  { id: 1, name: 'Lamidi Olonade Fakeye'},\n  { id: 2, name: 'Louise Nevelson'},\n];\n\nexport default function List() {\n  const [artists, setArtists] = useState(\n    initialArtists\n  );\n\n  return (\n    <>\n      <h1>Inspiring sculptors:</h1>\n      <ul>\n        {artists.map(artist => (\n          <li key={artist.id}>\n            {artist.name}{' '}\n            <button onClick={() => {\n              setArtists(\n                artists.filter(a =>\n                  a.id !== artist.id\n                )\n              );\n            }}>\n              Delete\n            </button>\n          </li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n何度か \"Delete\" ボタンをクリックして動作を確認したら、クリックハンドラを見てみましょう。\n\n```js\nsetArtists(\n  artists.filter(a => a.id !== artist.id)\n);\n```\n\nここで、`artists.filter(a => a.id !== artist.id)` というコードは「`artist.id` と異なる ID を持つ `artists` のみの配列を作成する」という意味です。言い換えると、各アーティストの \"Delete\" ボタンは、該当アーティストを配列からフィルタリングして取り除き、結果として得られる配列で再レンダーを要求します。`filter` は元の配列を書き換えないことに注意してください。\n\n### 配列の変換 {/*transforming-an-array*/}\n\n配列の一部またはすべての要素を変更したい場合は、`map()` を使用して**新しい**配列を作成できます。`map` に渡す関数は、データやインデックス（またはその両方）に基づいて各要素に何をするかを決定できます。\n\nこの例では、配列に 2 つの円と 1 つの正方形の座標が含まれています。ボタンを押すと、円だけが 50 ピクセル下に移動します。これは、`map()` を使用して新しいデータの配列を作成することで行われます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nlet initialShapes = [\n  { id: 0, type: 'circle', x: 50, y: 100 },\n  { id: 1, type: 'square', x: 150, y: 100 },\n  { id: 2, type: 'circle', x: 250, y: 100 },\n];\n\nexport default function ShapeEditor() {\n  const [shapes, setShapes] = useState(\n    initialShapes\n  );\n\n  function handleClick() {\n    const nextShapes = shapes.map(shape => {\n      if (shape.type === 'square') {\n        // No change\n        return shape;\n      } else {\n        // Return a new circle 50px below\n        return {\n          ...shape,\n          y: shape.y + 50,\n        };\n      }\n    });\n    // Re-render with the new array\n    setShapes(nextShapes);\n  }\n\n  return (\n    <>\n      <button onClick={handleClick}>\n        Move circles down!\n      </button>\n      {shapes.map(shape => (\n        <div\n          key={shape.id}\n          style={{\n          background: 'purple',\n          position: 'absolute',\n          left: shape.x,\n          top: shape.y,\n          borderRadius:\n            shape.type === 'circle'\n              ? '50%' : '',\n          width: 20,\n          height: 20,\n        }} />\n      ))}\n    </>\n  );\n}\n```\n\n```css\nbody { height: 300px; }\n```\n\n</Sandpack>\n\n### 配列内の要素の置換 {/*replacing-items-in-an-array*/}\n\n配列内の一部の要素だけを置き換えたい場合がよくあります。`arr[0] = 'bird'` のような代入は元の配列を書き換えてしまうので、代わりにここでも `map` を使用する必要があります。\n\n要素を置き換えるには、`map` を使って新しい配列を作成します。`map` の呼び出し内では、第 2 引数として要素のインデックスを受け取ります。これを使用して、元の要素（第 1 引数）を返すか、他のものを返すかを決定します。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nlet initialCounters = [\n  0, 0, 0\n];\n\nexport default function CounterList() {\n  const [counters, setCounters] = useState(\n    initialCounters\n  );\n\n  function handleIncrementClick(index) {\n    const nextCounters = counters.map((c, i) => {\n      if (i === index) {\n        // Increment the clicked counter\n        return c + 1;\n      } else {\n        // The rest haven't changed\n        return c;\n      }\n    });\n    setCounters(nextCounters);\n  }\n\n  return (\n    <ul>\n      {counters.map((counter, i) => (\n        <li key={i}>\n          {counter}\n          <button onClick={() => {\n            handleIncrementClick(i);\n          }}>+1</button>\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\n```\n\n</Sandpack>\n\n### 配列への挿入 {/*inserting-into-an-array*/}\n\n場合によっては、先頭でも終端でもない特定の位置に要素を挿入したいことがあります。これを行うには、`...` 配列スプレッド構文と `slice()` メソッドを組み合わせて使用できます。`slice()` メソッドを使用すると、配列の「スライス」を切り取ることができます。要素を挿入するには、挿入ポイントの前のスライス、新しい要素、元の配列の残りの部分からなる配列を作成します。\n\nこの例では、\"Insert\" ボタンは常にインデックス `1` の場所に挿入を行います。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nlet nextId = 3;\nconst initialArtists = [\n  { id: 0, name: 'Marta Colvin Andrade' },\n  { id: 1, name: 'Lamidi Olonade Fakeye'},\n  { id: 2, name: 'Louise Nevelson'},\n];\n\nexport default function List() {\n  const [name, setName] = useState('');\n  const [artists, setArtists] = useState(\n    initialArtists\n  );\n\n  function handleClick() {\n    const insertAt = 1; // Could be any index\n    const nextArtists = [\n      // Items before the insertion point:\n      ...artists.slice(0, insertAt),\n      // New item:\n      { id: nextId++, name: name },\n      // Items after the insertion point:\n      ...artists.slice(insertAt)\n    ];\n    setArtists(nextArtists);\n    setName('');\n  }\n\n  return (\n    <>\n      <h1>Inspiring sculptors:</h1>\n      <input\n        value={name}\n        onChange={e => setName(e.target.value)}\n      />\n      <button onClick={handleClick}>\n        Insert\n      </button>\n      <ul>\n        {artists.map(artist => (\n          <li key={artist.id}>{artist.name}</li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n```css\nbutton { margin-left: 5px; }\n```\n\n</Sandpack>\n\n### 配列へのその他の変更 {/*making-other-changes-to-an-array*/}\n\nスプレッド構文や、`map()`、`filter()` などの書き換えを行わないメソッドを使っているだけでは不可能なこともあります。例えば、配列を逆順にしたり、ソートしたりすることができません。JavaScript の `reverse()` や `sort()` メソッドは元の配列を書き換えるため、直接使うことはできません。\n\n**ただし、最初に配列をコピーしてから、そのコピーに変更を加えることはできます。**\n\n例えば、次のようになります。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nconst initialList = [\n  { id: 0, title: 'Big Bellies' },\n  { id: 1, title: 'Lunar Landscape' },\n  { id: 2, title: 'Terracotta Army' },\n];\n\nexport default function List() {\n  const [list, setList] = useState(initialList);\n\n  function handleClick() {\n    const nextList = [...list];\n    nextList.reverse();\n    setList(nextList);\n  }\n\n  return (\n    <>\n      <button onClick={handleClick}>\n        Reverse\n      </button>\n      <ul>\n        {list.map(artwork => (\n          <li key={artwork.id}>{artwork.title}</li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\nここでは、`[...list]` スプレッド構文を使って、最初に元の配列のコピーを作成します。コピーができたら、`nextList.reverse()` や `nextList.sort()` などのミューテーション型のメソッドを使ったり、`nextList[0] = \"something\"` で個々の要素に代入したりすることができます。\n\nただし、**配列をコピーしても、その中の既存のアイテムを直接変更することはできません**。これは、コピーが浅く (shallow) 行われるためです。新しい配列には、元の配列と同じ要素が含まれます。そのため、コピーされた配列内のオブジェクトを書き換えると、既存の state が書き換えられます。例えば、このようなコードは問題があります。\n\n```js\nconst nextList = [...list];\nnextList[0].seen = true; // Problem: mutates list[0]\nsetList(nextList);\n```\n\n`nextList` と `list` は異なる 2 つの配列ですが、**`nextList[0]` と `list[0]` は同じオブジェクトを指しています**。そのため、`nextList[0].seen` を変更することで、`list[0].seen` も変更されます。これは state のミューテーションであり、避けるべきです！ この問題は、[ネストされた JavaScript オブジェクトの更新](/learn/updating-objects-in-state#updating-a-nested-object)と同様の方法で解決できます。変更を加えたい個々の要素を、書き換える代わりにコピーするということです。以下で説明します。\n\n## 配列内のオブジェクトを更新する {/*updating-objects-inside-arrays*/}\n\nオブジェクトは、*実際には*配列の「中に」あるわけではありません。コード中では「中に」あるように見えますが、配列内の各オブジェクトはそれぞれ独立した値であり、配列はそれらを「参照」しています。これが、`list[0]` のようなネストしたフィールドを変更する際に注意が必要な理由です。他の人のアートワークリストが、配列の同じ要素を指しているかもしれません！\n\n**ネストされた state を更新する際には、更新したい箇所からトップレベルまでのコピーを作成する必要があります**。どのように行うのか見てみましょう。\n\nこの例では、2 つの別々のアートワークリストが同じ初期 state を持っています。これらは独立していることになっているのですが、ミューテーションが起きているため state が誤って共有され、一方のリストでボックスをチェックするともう一方のリストに影響してしまっています。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nlet nextId = 3;\nconst initialList = [\n  { id: 0, title: 'Big Bellies', seen: false },\n  { id: 1, title: 'Lunar Landscape', seen: false },\n  { id: 2, title: 'Terracotta Army', seen: true },\n];\n\nexport default function BucketList() {\n  const [myList, setMyList] = useState(initialList);\n  const [yourList, setYourList] = useState(\n    initialList\n  );\n\n  function handleToggleMyList(artworkId, nextSeen) {\n    const myNextList = [...myList];\n    const artwork = myNextList.find(\n      a => a.id === artworkId\n    );\n    artwork.seen = nextSeen;\n    setMyList(myNextList);\n  }\n\n  function handleToggleYourList(artworkId, nextSeen) {\n    const yourNextList = [...yourList];\n    const artwork = yourNextList.find(\n      a => a.id === artworkId\n    );\n    artwork.seen = nextSeen;\n    setYourList(yourNextList);\n  }\n\n  return (\n    <>\n      <h1>Art Bucket List</h1>\n      <h2>My list of art to see:</h2>\n      <ItemList\n        artworks={myList}\n        onToggle={handleToggleMyList} />\n      <h2>Your list of art to see:</h2>\n      <ItemList\n        artworks={yourList}\n        onToggle={handleToggleYourList} />\n    </>\n  );\n}\n\nfunction ItemList({ artworks, onToggle }) {\n  return (\n    <ul>\n      {artworks.map(artwork => (\n        <li key={artwork.id}>\n          <label>\n            <input\n              type=\"checkbox\"\n              checked={artwork.seen}\n              onChange={e => {\n                onToggle(\n                  artwork.id,\n                  e.target.checked\n                );\n              }}\n            />\n            {artwork.title}\n          </label>\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n</Sandpack>\n\n問題はこのコードです。\n\n```js\nconst myNextList = [...myList];\nconst artwork = myNextList.find(a => a.id === artworkId);\nartwork.seen = nextSeen; // Problem: mutates an existing item\nsetMyList(myNextList);\n```\n\n`myNextList` という配列自体は新しいものですが、個々の*要素そのもの*は元の `myList` 配列と同じです。そのため、`artwork.seen` を変更することで、*元の*アートワークも変更されます。そのアートワークは `yourList` 内にも存在するため、バグが発生します。このようなバグは理解するのが難しいことがありますが、幸いにも state のミューテーションさえ避けておけば、起こさずに済みます。\n\n**ミューテーションをせずに、古い要素を更新された要素と置き換えるには、`map` を使用できます**。\n\n```js\nsetMyList(myList.map(artwork => {\n  if (artwork.id === artworkId) {\n    // Create a *new* object with changes\n    return { ...artwork, seen: nextSeen };\n  } else {\n    // No changes\n    return artwork;\n  }\n}));\n```\n\nここで、`...` はオブジェクトのスプレッド構文であり、[オブジェクトのコピーを作成する](/learn/updating-objects-in-state#copying-objects-with-the-spread-syntax)ために使われます。\n\nこのアプローチでは、既存の state の要素を書き換えないため、バグは修正されています。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nlet nextId = 3;\nconst initialList = [\n  { id: 0, title: 'Big Bellies', seen: false },\n  { id: 1, title: 'Lunar Landscape', seen: false },\n  { id: 2, title: 'Terracotta Army', seen: true },\n];\n\nexport default function BucketList() {\n  const [myList, setMyList] = useState(initialList);\n  const [yourList, setYourList] = useState(\n    initialList\n  );\n\n  function handleToggleMyList(artworkId, nextSeen) {\n    setMyList(myList.map(artwork => {\n      if (artwork.id === artworkId) {\n        // Create a *new* object with changes\n        return { ...artwork, seen: nextSeen };\n      } else {\n        // No changes\n        return artwork;\n      }\n    }));\n  }\n\n  function handleToggleYourList(artworkId, nextSeen) {\n    setYourList(yourList.map(artwork => {\n      if (artwork.id === artworkId) {\n        // Create a *new* object with changes\n        return { ...artwork, seen: nextSeen };\n      } else {\n        // No changes\n        return artwork;\n      }\n    }));\n  }\n\n  return (\n    <>\n      <h1>Art Bucket List</h1>\n      <h2>My list of art to see:</h2>\n      <ItemList\n        artworks={myList}\n        onToggle={handleToggleMyList} />\n      <h2>Your list of art to see:</h2>\n      <ItemList\n        artworks={yourList}\n        onToggle={handleToggleYourList} />\n    </>\n  );\n}\n\nfunction ItemList({ artworks, onToggle }) {\n  return (\n    <ul>\n      {artworks.map(artwork => (\n        <li key={artwork.id}>\n          <label>\n            <input\n              type=\"checkbox\"\n              checked={artwork.seen}\n              onChange={e => {\n                onToggle(\n                  artwork.id,\n                  e.target.checked\n                );\n              }}\n            />\n            {artwork.title}\n          </label>\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n</Sandpack>\n\n一般的には、**作成したばかりのオブジェクト以外は書き換えてはいけません**。*新しい*アートワークを挿入する場合にその新しい要素を書き換えても構いませんが、state にすでに存在するものを扱う場合は、コピーを作成する必要があります。\n\n### Immer を使って簡潔な更新ロジックを書く {/*write-concise-update-logic-with-immer*/}\n\n配列がネストされている場合、書き換えなしで更新を行うコードは冗長になりがちです。[オブジェクトのときと同様](/learn/updating-objects-in-state#write-concise-update-logic-with-immer)ですが、\n\n- 一般的には、state を 2〜3 レベル以上深く更新する必要はないはずです。state オブジェクトが非常に深い場合は、[別の構造に再構成](/learn/choosing-the-state-structure#avoid-deeply-nested-state)してフラットにすることができます。\n- state 構造を変更したくない場合は、[Immer](https://github.com/immerjs/use-immer) を使用して、書きやすいミューテート型の構文で記述しつつコピーを生成することができます。\n\n以下は、Immer を使用して書き直したアートワークリストの例です。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { useImmer } from 'use-immer';\n\nlet nextId = 3;\nconst initialList = [\n  { id: 0, title: 'Big Bellies', seen: false },\n  { id: 1, title: 'Lunar Landscape', seen: false },\n  { id: 2, title: 'Terracotta Army', seen: true },\n];\n\nexport default function BucketList() {\n  const [myList, updateMyList] = useImmer(\n    initialList\n  );\n  const [yourList, updateYourList] = useImmer(\n    initialList\n  );\n\n  function handleToggleMyList(id, nextSeen) {\n    updateMyList(draft => {\n      const artwork = draft.find(a =>\n        a.id === id\n      );\n      artwork.seen = nextSeen;\n    });\n  }\n\n  function handleToggleYourList(artworkId, nextSeen) {\n    updateYourList(draft => {\n      const artwork = draft.find(a =>\n        a.id === artworkId\n      );\n      artwork.seen = nextSeen;\n    });\n  }\n\n  return (\n    <>\n      <h1>Art Bucket List</h1>\n      <h2>My list of art to see:</h2>\n      <ItemList\n        artworks={myList}\n        onToggle={handleToggleMyList} />\n      <h2>Your list of art to see:</h2>\n      <ItemList\n        artworks={yourList}\n        onToggle={handleToggleYourList} />\n    </>\n  );\n}\n\nfunction ItemList({ artworks, onToggle }) {\n  return (\n    <ul>\n      {artworks.map(artwork => (\n        <li key={artwork.id}>\n          <label>\n            <input\n              type=\"checkbox\"\n              checked={artwork.seen}\n              onChange={e => {\n                onToggle(\n                  artwork.id,\n                  e.target.checked\n                );\n              }}\n            />\n            {artwork.title}\n          </label>\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"use-immer\": \"0.5.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\nImmer を使用することで **`artwork.seen = nextSeen` のような書き換えができるようになりました**。\n\n```js\nupdateMyTodos(draft => {\n  const artwork = draft.find(a => a.id === artworkId);\n  artwork.seen = nextSeen;\n});\n```\n\nこれが可能なのは、Immer から渡される特別な `draft` オブジェクトを書き換えているのであり、*元の* state は書き換えていないためです。同様に、`draft` の内容に対して `push()` や `pop()` などのミューテーション型のメソッドを使用することもできます。\n\n裏側では、Immer は常に、`draft` に対して行った書き換え操作に基づいて、次の state をゼロから構築します。これにより、state を書き換えてしまう心配をせず、イベントハンドラを非常に簡潔に保つことができます。\n\n<Recap>\n\n- 配列を state に入れることができるが、それを直接変更してはいけない。\n- 配列を変更せず、代わりに*新しい*版を作成し、state を更新する。\n- `[...arr, newItem]` という配列スプレッド構文を使用して、新しい項目を持つ配列を作成できる。\n- `filter()` や `map()` を使用して、フィルタリングされた、あるいは変換されたアイテムを含む新しい配列を作成できる。\n- Immer を使ってコードを簡潔に保つことができる。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### ショッピングカートの商品を更新 {/*update-an-item-in-the-shopping-cart*/}\n\n`handleIncreaseClick` のロジックを埋めて、\"+\" を押すことで対応する数字が増えるようにしてください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nconst initialProducts = [{\n  id: 0,\n  name: 'Baklava',\n  count: 1,\n}, {\n  id: 1,\n  name: 'Cheese',\n  count: 5,\n}, {\n  id: 2,\n  name: 'Spaghetti',\n  count: 2,\n}];\n\nexport default function ShoppingCart() {\n  const [\n    products,\n    setProducts\n  ] = useState(initialProducts)\n\n  function handleIncreaseClick(productId) {\n\n  }\n\n  return (\n    <ul>\n      {products.map(product => (\n        <li key={product.id}>\n          {product.name}\n          {' '}\n          (<b>{product.count}</b>)\n          <button onClick={() => {\n            handleIncreaseClick(product.id);\n          }}>\n            +\n          </button>\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n`map` 関数を使って新しい配列を作成し、その中で `...` オブジェクトスプレッド構文を使って変更された商品に対応するオブジェクトのコピーを作成します：\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nconst initialProducts = [{\n  id: 0,\n  name: 'Baklava',\n  count: 1,\n}, {\n  id: 1,\n  name: 'Cheese',\n  count: 5,\n}, {\n  id: 2,\n  name: 'Spaghetti',\n  count: 2,\n}];\n\nexport default function ShoppingCart() {\n  const [\n    products,\n    setProducts\n  ] = useState(initialProducts)\n\n  function handleIncreaseClick(productId) {\n    setProducts(products.map(product => {\n      if (product.id === productId) {\n        return {\n          ...product,\n          count: product.count + 1\n        };\n      } else {\n        return product;\n      }\n    }))\n  }\n\n  return (\n    <ul>\n      {products.map(product => (\n        <li key={product.id}>\n          {product.name}\n          {' '}\n          (<b>{product.count}</b>)\n          <button onClick={() => {\n            handleIncreaseClick(product.id);\n          }}>\n            +\n          </button>\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### ショッピングカートから商品を削除 {/*remove-an-item-from-the-shopping-cart*/}\n\nこのショッピングカートでは \"+\" ボタンは機能していますが、\"–\" ボタンは何もしません。これに対応するイベントハンドラを追加して、押すことで対応する商品の `count` を減らすようにしてください。カウントが 1 の場合に \"–\" を押すと、商品が自動的にカートから削除されるようにしてください。0 と表示されないようにしてください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nconst initialProducts = [{\n  id: 0,\n  name: 'Baklava',\n  count: 1,\n}, {\n  id: 1,\n  name: 'Cheese',\n  count: 5,\n}, {\n  id: 2,\n  name: 'Spaghetti',\n  count: 2,\n}];\n\nexport default function ShoppingCart() {\n  const [\n    products,\n    setProducts\n  ] = useState(initialProducts)\n\n  function handleIncreaseClick(productId) {\n    setProducts(products.map(product => {\n      if (product.id === productId) {\n        return {\n          ...product,\n          count: product.count + 1\n        };\n      } else {\n        return product;\n      }\n    }))\n  }\n\n  return (\n    <ul>\n      {products.map(product => (\n        <li key={product.id}>\n          {product.name}\n          {' '}\n          (<b>{product.count}</b>)\n          <button onClick={() => {\n            handleIncreaseClick(product.id);\n          }}>\n            +\n          </button>\n          <button>\n            –\n          </button>\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\n```\n\n</Sandpack>\n\n<Solution>\n\nまず `map` を使って新しい配列を生成し、次に `filter` を使って `count` が `0` となっている商品を削除します。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nconst initialProducts = [{\n  id: 0,\n  name: 'Baklava',\n  count: 1,\n}, {\n  id: 1,\n  name: 'Cheese',\n  count: 5,\n}, {\n  id: 2,\n  name: 'Spaghetti',\n  count: 2,\n}];\n\nexport default function ShoppingCart() {\n  const [\n    products,\n    setProducts\n  ] = useState(initialProducts)\n\n  function handleIncreaseClick(productId) {\n    setProducts(products.map(product => {\n      if (product.id === productId) {\n        return {\n          ...product,\n          count: product.count + 1\n        };\n      } else {\n        return product;\n      }\n    }))\n  }\n\n  function handleDecreaseClick(productId) {\n    let nextProducts = products.map(product => {\n      if (product.id === productId) {\n        return {\n          ...product,\n          count: product.count - 1\n        };\n      } else {\n        return product;\n      }\n    });\n    nextProducts = nextProducts.filter(p =>\n      p.count > 0\n    );\n    setProducts(nextProducts)\n  }\n\n  return (\n    <ul>\n      {products.map(product => (\n        <li key={product.id}>\n          {product.name}\n          {' '}\n          (<b>{product.count}</b>)\n          <button onClick={() => {\n            handleIncreaseClick(product.id);\n          }}>\n            +\n          </button>\n          <button onClick={() => {\n            handleDecreaseClick(product.id);\n          }}>\n            –\n          </button>\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### ミューテーションを行わないように修正 {/*fix-the-mutations-using-non-mutative-methods*/}\n\nこの例では、`App.js` のすべてのイベントハンドラがミューテーションを行っています。そのため、todo アイテムの編集や削除が機能していません。`handleAddTodo`、`handleChangeTodo`、`handleDeleteTodo` を、ミューテーションを行わないメソッドを使って書き直してください。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport AddTodo from './AddTodo.js';\nimport TaskList from './TaskList.js';\n\nlet nextId = 3;\nconst initialTodos = [\n  { id: 0, title: 'Buy milk', done: true },\n  { id: 1, title: 'Eat tacos', done: false },\n  { id: 2, title: 'Brew tea', done: false },\n];\n\nexport default function TaskApp() {\n  const [todos, setTodos] = useState(\n    initialTodos\n  );\n\n  function handleAddTodo(title) {\n    todos.push({\n      id: nextId++,\n      title: title,\n      done: false\n    });\n  }\n\n  function handleChangeTodo(nextTodo) {\n    const todo = todos.find(t =>\n      t.id === nextTodo.id\n    );\n    todo.title = nextTodo.title;\n    todo.done = nextTodo.done;\n  }\n\n  function handleDeleteTodo(todoId) {\n    const index = todos.findIndex(t =>\n      t.id === todoId\n    );\n    todos.splice(index, 1);\n  }\n\n  return (\n    <>\n      <AddTodo\n        onAddTodo={handleAddTodo}\n      />\n      <TaskList\n        todos={todos}\n        onChangeTodo={handleChangeTodo}\n        onDeleteTodo={handleDeleteTodo}\n      />\n    </>\n  );\n}\n```\n\n```js src/AddTodo.js\nimport { useState } from 'react';\n\nexport default function AddTodo({ onAddTodo }) {\n  const [title, setTitle] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add todo\"\n        value={title}\n        onChange={e => setTitle(e.target.value)}\n      />\n      <button onClick={() => {\n        setTitle('');\n        onAddTodo(title);\n      }}>Add</button>\n    </>\n  )\n}\n```\n\n```js src/TaskList.js\nimport { useState } from 'react';\n\nexport default function TaskList({\n  todos,\n  onChangeTodo,\n  onDeleteTodo\n}) {\n  return (\n    <ul>\n      {todos.map(todo => (\n        <li key={todo.id}>\n          <Task\n            todo={todo}\n            onChange={onChangeTodo}\n            onDelete={onDeleteTodo}\n          />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ todo, onChange, onDelete }) {\n  const [isEditing, setIsEditing] = useState(false);\n  let todoContent;\n  if (isEditing) {\n    todoContent = (\n      <>\n        <input\n          value={todo.title}\n          onChange={e => {\n            onChange({\n              ...todo,\n              title: e.target.value\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    todoContent = (\n      <>\n        {todo.title}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={todo.done}\n        onChange={e => {\n          onChange({\n            ...todo,\n            done: e.target.checked\n          });\n        }}\n      />\n      {todoContent}\n      <button onClick={() => onDelete(todo.id)}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\n<Solution>\n\n`handleAddTodo` では、配列のスプレッド構文が使えます。`handleChangeTodo` では、`map` で新しい配列を作成します。`handleDeleteTodo` では、`filter` で新しい配列を作成します。これでリストが正しく動作します。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport AddTodo from './AddTodo.js';\nimport TaskList from './TaskList.js';\n\nlet nextId = 3;\nconst initialTodos = [\n  { id: 0, title: 'Buy milk', done: true },\n  { id: 1, title: 'Eat tacos', done: false },\n  { id: 2, title: 'Brew tea', done: false },\n];\n\nexport default function TaskApp() {\n  const [todos, setTodos] = useState(\n    initialTodos\n  );\n\n  function handleAddTodo(title) {\n    setTodos([\n      ...todos,\n      {\n        id: nextId++,\n        title: title,\n        done: false\n      }\n    ]);\n  }\n\n  function handleChangeTodo(nextTodo) {\n    setTodos(todos.map(t => {\n      if (t.id === nextTodo.id) {\n        return nextTodo;\n      } else {\n        return t;\n      }\n    }));\n  }\n\n  function handleDeleteTodo(todoId) {\n    setTodos(\n      todos.filter(t => t.id !== todoId)\n    );\n  }\n\n  return (\n    <>\n      <AddTodo\n        onAddTodo={handleAddTodo}\n      />\n      <TaskList\n        todos={todos}\n        onChangeTodo={handleChangeTodo}\n        onDeleteTodo={handleDeleteTodo}\n      />\n    </>\n  );\n}\n```\n\n```js src/AddTodo.js\nimport { useState } from 'react';\n\nexport default function AddTodo({ onAddTodo }) {\n  const [title, setTitle] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add todo\"\n        value={title}\n        onChange={e => setTitle(e.target.value)}\n      />\n      <button onClick={() => {\n        setTitle('');\n        onAddTodo(title);\n      }}>Add</button>\n    </>\n  )\n}\n```\n\n```js src/TaskList.js\nimport { useState } from 'react';\n\nexport default function TaskList({\n  todos,\n  onChangeTodo,\n  onDeleteTodo\n}) {\n  return (\n    <ul>\n      {todos.map(todo => (\n        <li key={todo.id}>\n          <Task\n            todo={todo}\n            onChange={onChangeTodo}\n            onDelete={onDeleteTodo}\n          />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ todo, onChange, onDelete }) {\n  const [isEditing, setIsEditing] = useState(false);\n  let todoContent;\n  if (isEditing) {\n    todoContent = (\n      <>\n        <input\n          value={todo.title}\n          onChange={e => {\n            onChange({\n              ...todo,\n              title: e.target.value\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    todoContent = (\n      <>\n        {todo.title}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={todo.done}\n        onChange={e => {\n          onChange({\n            ...todo,\n            done: e.target.checked\n          });\n        }}\n      />\n      {todoContent}\n      <button onClick={() => onDelete(todo.id)}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\n</Solution>\n\n\n#### Immer でミューテーションを修正 {/*fix-the-mutations-using-immer*/}\n\nこれは、ひとつ前のチャレンジと同じ例です。今回は、Immer を使用してミューテーションを修正します。`useImmer` はすでにインポートしてありますので、`todos` の state 変数をこれを使って更新してください。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { useImmer } from 'use-immer';\nimport AddTodo from './AddTodo.js';\nimport TaskList from './TaskList.js';\n\nlet nextId = 3;\nconst initialTodos = [\n  { id: 0, title: 'Buy milk', done: true },\n  { id: 1, title: 'Eat tacos', done: false },\n  { id: 2, title: 'Brew tea', done: false },\n];\n\nexport default function TaskApp() {\n  const [todos, setTodos] = useState(\n    initialTodos\n  );\n\n  function handleAddTodo(title) {\n    todos.push({\n      id: nextId++,\n      title: title,\n      done: false\n    });\n  }\n\n  function handleChangeTodo(nextTodo) {\n    const todo = todos.find(t =>\n      t.id === nextTodo.id\n    );\n    todo.title = nextTodo.title;\n    todo.done = nextTodo.done;\n  }\n\n  function handleDeleteTodo(todoId) {\n    const index = todos.findIndex(t =>\n      t.id === todoId\n    );\n    todos.splice(index, 1);\n  }\n\n  return (\n    <>\n      <AddTodo\n        onAddTodo={handleAddTodo}\n      />\n      <TaskList\n        todos={todos}\n        onChangeTodo={handleChangeTodo}\n        onDeleteTodo={handleDeleteTodo}\n      />\n    </>\n  );\n}\n```\n\n```js src/AddTodo.js\nimport { useState } from 'react';\n\nexport default function AddTodo({ onAddTodo }) {\n  const [title, setTitle] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add todo\"\n        value={title}\n        onChange={e => setTitle(e.target.value)}\n      />\n      <button onClick={() => {\n        setTitle('');\n        onAddTodo(title);\n      }}>Add</button>\n    </>\n  )\n}\n```\n\n```js src/TaskList.js\nimport { useState } from 'react';\n\nexport default function TaskList({\n  todos,\n  onChangeTodo,\n  onDeleteTodo\n}) {\n  return (\n    <ul>\n      {todos.map(todo => (\n        <li key={todo.id}>\n          <Task\n            todo={todo}\n            onChange={onChangeTodo}\n            onDelete={onDeleteTodo}\n          />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ todo, onChange, onDelete }) {\n  const [isEditing, setIsEditing] = useState(false);\n  let todoContent;\n  if (isEditing) {\n    todoContent = (\n      <>\n        <input\n          value={todo.title}\n          onChange={e => {\n            onChange({\n              ...todo,\n              title: e.target.value\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    todoContent = (\n      <>\n        {todo.title}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={todo.done}\n        onChange={e => {\n          onChange({\n            ...todo,\n            done: e.target.checked\n          });\n        }}\n      />\n      {todoContent}\n      <button onClick={() => onDelete(todo.id)}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"use-immer\": \"0.5.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\n<Solution>\n\nImmer を使うと、Immer から渡される `draft` のみを書き換えている限りは、ミューテーション型のコードを書くことができます。ここでは、ミューテーションは `draft` に対してのみ行われているため、コードが機能します。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { useImmer } from 'use-immer';\nimport AddTodo from './AddTodo.js';\nimport TaskList from './TaskList.js';\n\nlet nextId = 3;\nconst initialTodos = [\n  { id: 0, title: 'Buy milk', done: true },\n  { id: 1, title: 'Eat tacos', done: false },\n  { id: 2, title: 'Brew tea', done: false },\n];\n\nexport default function TaskApp() {\n  const [todos, updateTodos] = useImmer(\n    initialTodos\n  );\n\n  function handleAddTodo(title) {\n    updateTodos(draft => {\n      draft.push({\n        id: nextId++,\n        title: title,\n        done: false\n      });\n    });\n  }\n\n  function handleChangeTodo(nextTodo) {\n    updateTodos(draft => {\n      const todo = draft.find(t =>\n        t.id === nextTodo.id\n      );\n      todo.title = nextTodo.title;\n      todo.done = nextTodo.done;\n    });\n  }\n\n  function handleDeleteTodo(todoId) {\n    updateTodos(draft => {\n      const index = draft.findIndex(t =>\n        t.id === todoId\n      );\n      draft.splice(index, 1);\n    });\n  }\n\n  return (\n    <>\n      <AddTodo\n        onAddTodo={handleAddTodo}\n      />\n      <TaskList\n        todos={todos}\n        onChangeTodo={handleChangeTodo}\n        onDeleteTodo={handleDeleteTodo}\n      />\n    </>\n  );\n}\n```\n\n```js src/AddTodo.js\nimport { useState } from 'react';\n\nexport default function AddTodo({ onAddTodo }) {\n  const [title, setTitle] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add todo\"\n        value={title}\n        onChange={e => setTitle(e.target.value)}\n      />\n      <button onClick={() => {\n        setTitle('');\n        onAddTodo(title);\n      }}>Add</button>\n    </>\n  )\n}\n```\n\n```js src/TaskList.js\nimport { useState } from 'react';\n\nexport default function TaskList({\n  todos,\n  onChangeTodo,\n  onDeleteTodo\n}) {\n  return (\n    <ul>\n      {todos.map(todo => (\n        <li key={todo.id}>\n          <Task\n            todo={todo}\n            onChange={onChangeTodo}\n            onDelete={onDeleteTodo}\n          />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ todo, onChange, onDelete }) {\n  const [isEditing, setIsEditing] = useState(false);\n  let todoContent;\n  if (isEditing) {\n    todoContent = (\n      <>\n        <input\n          value={todo.title}\n          onChange={e => {\n            onChange({\n              ...todo,\n              title: e.target.value\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    todoContent = (\n      <>\n        {todo.title}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={todo.done}\n        onChange={e => {\n          onChange({\n            ...todo,\n            done: e.target.checked\n          });\n        }}\n      />\n      {todoContent}\n      <button onClick={() => onDelete(todo.id)}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"use-immer\": \"0.5.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\nImmer を使えば、ミューテーション型と非ミューテーション型のアプローチを混在させることもできます。\n\n例えば、以下では `handleAddTodo` は Immer の `draft` を使ってミューテーション型で書かれていますが、`handleChangeTodo` と `handleDeleteTodo` は非ミューテーション型の `map` や `filter` メソッドで書かれています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { useImmer } from 'use-immer';\nimport AddTodo from './AddTodo.js';\nimport TaskList from './TaskList.js';\n\nlet nextId = 3;\nconst initialTodos = [\n  { id: 0, title: 'Buy milk', done: true },\n  { id: 1, title: 'Eat tacos', done: false },\n  { id: 2, title: 'Brew tea', done: false },\n];\n\nexport default function TaskApp() {\n  const [todos, updateTodos] = useImmer(\n    initialTodos\n  );\n\n  function handleAddTodo(title) {\n    updateTodos(draft => {\n      draft.push({\n        id: nextId++,\n        title: title,\n        done: false\n      });\n    });\n  }\n\n  function handleChangeTodo(nextTodo) {\n    updateTodos(todos.map(todo => {\n      if (todo.id === nextTodo.id) {\n        return nextTodo;\n      } else {\n        return todo;\n      }\n    }));\n  }\n\n  function handleDeleteTodo(todoId) {\n    updateTodos(\n      todos.filter(t => t.id !== todoId)\n    );\n  }\n\n  return (\n    <>\n      <AddTodo\n        onAddTodo={handleAddTodo}\n      />\n      <TaskList\n        todos={todos}\n        onChangeTodo={handleChangeTodo}\n        onDeleteTodo={handleDeleteTodo}\n      />\n    </>\n  );\n}\n```\n\n```js src/AddTodo.js\nimport { useState } from 'react';\n\nexport default function AddTodo({ onAddTodo }) {\n  const [title, setTitle] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add todo\"\n        value={title}\n        onChange={e => setTitle(e.target.value)}\n      />\n      <button onClick={() => {\n        setTitle('');\n        onAddTodo(title);\n      }}>Add</button>\n    </>\n  )\n}\n```\n\n```js src/TaskList.js\nimport { useState } from 'react';\n\nexport default function TaskList({\n  todos,\n  onChangeTodo,\n  onDeleteTodo\n}) {\n  return (\n    <ul>\n      {todos.map(todo => (\n        <li key={todo.id}>\n          <Task\n            todo={todo}\n            onChange={onChangeTodo}\n            onDelete={onDeleteTodo}\n          />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ todo, onChange, onDelete }) {\n  const [isEditing, setIsEditing] = useState(false);\n  let todoContent;\n  if (isEditing) {\n    todoContent = (\n      <>\n        <input\n          value={todo.title}\n          onChange={e => {\n            onChange({\n              ...todo,\n              title: e.target.value\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    todoContent = (\n      <>\n        {todo.title}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={todo.done}\n        onChange={e => {\n          onChange({\n            ...todo,\n            done: e.target.checked\n          });\n        }}\n      />\n      {todoContent}\n      <button onClick={() => onDelete(todo.id)}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"use-immer\": \"0.5.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\nImmer を使うことで、個々のケースごとに最も自然に感じるスタイルを選ぶことができます。\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/updating-objects-in-state.md",
    "content": "---\ntitle: state 内のオブジェクトの更新\n---\n\n<Intro>\n\nstate にはどのような JavaScript の値でも保持することができます。これにはオブジェクトも含まれます。しかし、React の state に保持されたオブジェクトを直接書き換えるべきではありません。オブジェクトを更新したい場合、代わりに新しいオブジェクトを作成（または既存のもののコピーを作成）し、それを使って state をセットする必要があります。\n\n</Intro>\n\n<YouWillLearn>\n\n- React の state 内のオブジェクトを正しく更新する方法\n- ミューテートせずにネストされたオブジェクトを更新する方法\n- イミュータビリティとは何で、どのようにして遵守するのか\n- Immer を使ってオブジェクトコピーのためのコードの冗長さを緩和する方法\n\n</YouWillLearn>\n\n## ミューテーションとは？ {/*whats-a-mutation*/}\n\nstate には、どのような JavaScript の値でも格納することができます。\n\n```js\nconst [x, setX] = useState(0);\n```\n\nこれまでに扱ってきたのは、数値、文字列、および真偽値です。これらの種類の JavaScript 値は \"イミュータブル\"（不変, immutable）、つまり値が変わることがなく「読み取り専用」なものです。再レンダーをトリガするには値を*置き換え*ます。\n\n```js\nsetX(5);\n```\n\n`x` という state の値は `0` から `5` に置き換わりましたが、*`0` という数字そのもの*が変化したわけではありません。JavaScript の数値、文字列、真偽値のような組み込みプリミティブの値そのものを変化させることは不可能です。\n\nさて、state にオブジェクトが入っている場合を考えてみましょう。\n\n```js\nconst [position, setPosition] = useState({ x: 0, y: 0 });\n```\n\n技術的には、*オブジェクト自体*の内容を書き換えることが可能です。**これをミューテーション (mutation) と呼びます**。\n\n```js\nposition.x = 5;\n```\n\nしかし、React の state 内にあるオブジェクトは技術的にはミュータブル（mutable, 書き換え可能）であるとしても、数値、真偽値、文字列と同様に、イミュータブルなものであるかのように扱うべきです。書き換えるのではなく、常に置き換えるべきです。\n\n## state を読み取り専用として扱う {/*treat-state-as-read-only*/}\n\n言い換えると、**state として格納するすべての JavaScript オブジェクトは読み取り専用**として扱う必要があります。\n\n以下の例では、現在のポインタ位置を表すオブジェクトを state に保持しています。プレビュー領域でタッチしたりマウスカーソルを動かしたりすると、赤い点が動いて欲しいと思っています。しかし、点が初期位置から動きません：\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [11]}}\nimport { useState } from 'react';\n\nexport default function MovingDot() {\n  const [position, setPosition] = useState({\n    x: 0,\n    y: 0\n  });\n  return (\n    <div\n      onPointerMove={e => {\n        position.x = e.clientX;\n        position.y = e.clientY;\n      }}\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}>\n      <div style={{\n        position: 'absolute',\n        backgroundColor: 'red',\n        borderRadius: '50%',\n        transform: `translate(${position.x}px, ${position.y}px)`,\n        left: -10,\n        top: -10,\n        width: 20,\n        height: 20,\n      }} />\n    </div>\n  );\n}\n```\n\n```css\nbody { margin: 0; padding: 0; height: 250px; }\n```\n\n</Sandpack>\n\n問題は、このコードにあります。\n\n```js\nonPointerMove={e => {\n  position.x = e.clientX;\n  position.y = e.clientY;\n}}\n```\n\nこのコードは、[直近のレンダー](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time)で `position` に割り当てられたオブジェクトを書き換え、つまりミューテートしています。しかし、state セット関数が使用されないと、React はそのオブジェクトが変更されたことを認識できません。そのため、React は何の反応もしません。これは料理をすでに食べた後で注文を変更しようとするようなものです。state のミューテートは一部のケースでは機能することがありますが、おすすめしません。レンダー内でアクセスできる state 値は、読み取り専用として扱うべきです。\n\nこの場合、実際に[再レンダーをトリガする](/learn/state-as-a-snapshot#setting-state-triggers-renders)ためには、***新しい*オブジェクトを作成し、それを state セット関数に渡す必要があります。**\n\n```js\nonPointerMove={e => {\n  setPosition({\n    x: e.clientX,\n    y: e.clientY\n  });\n}}\n```\n\n`setPosition` を使うことで、React に次のことを伝えます。\n\n* `position` をこの新しいオブジェクトに置き換えよ\n* そしてもう一度このコンポーネントをレンダーせよ\n\nプレビューエリアでタッチするかマウスホバーすることで、赤い点がポインタに追随するようになりましたね。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function MovingDot() {\n  const [position, setPosition] = useState({\n    x: 0,\n    y: 0\n  });\n  return (\n    <div\n      onPointerMove={e => {\n        setPosition({\n          x: e.clientX,\n          y: e.clientY\n        });\n      }}\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}>\n      <div style={{\n        position: 'absolute',\n        backgroundColor: 'red',\n        borderRadius: '50%',\n        transform: `translate(${position.x}px, ${position.y}px)`,\n        left: -10,\n        top: -10,\n        width: 20,\n        height: 20,\n      }} />\n    </div>\n  );\n}\n```\n\n```css\nbody { margin: 0; padding: 0; height: 250px; }\n```\n\n</Sandpack>\n\n<DeepDive>\n\n#### ローカルミューテーションは問題なし {/*local-mutation-is-fine*/}\n\n以下のようなコードは、state の*既存の*オブジェクトを変更しているため、問題があります。\n\n```js\nposition.x = e.clientX;\nposition.y = e.clientY;\n```\n\nしかし、以下のようなコードは**全く問題ありません**。なぜなら、*作成したばかりの*新しいオブジェクトを書き換えているからです。\n\n```js\nconst nextPosition = {};\nnextPosition.x = e.clientX;\nnextPosition.y = e.clientY;\nsetPosition(nextPosition);\n```\n\n実際、これは以下のように書くことと全く同等です。\n\n```js\nsetPosition({\n  x: e.clientX,\n  y: e.clientY\n});\n```\n\nstate として存在する*既存の*オブジェクトを変更する場合にのみ、ミューテーションは問題になります。作成したばかりのオブジェクトであれば*他のコードはまだそれを参照していない*ので、書き換えても問題ありません。それを書き換えてもそれに依存する何かに誤って影響を与えることはありません。これを \"ローカルミューテーション (local mutation)\" と呼びます。[レンダー中にも](/learn/keeping-components-pure#local-mutation-your-components-little-secret)ローカルミューテーションを行うことができます。とても便利で、全く問題ありません！\n\n</DeepDive>\n\n## スプレッド構文を使ったオブジェクトのコピー {/*copying-objects-with-the-spread-syntax*/}\n\n前の例では、`position` オブジェクトは現在のカーソル位置から常に新規作成されます。しかし、多くの場合、新しく作成するオブジェクトに*既存の*データも含めたいことがあります。例えば、フォームの *1 つの*フィールドだけを更新し、他のすべてのフィールドについては以前の値を保持したい、ということがあります。\n\n以下の入力フィールドは、`onChange` ハンドラが state を書き換えているため、動作しません。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [11, 15, 19]}}\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [person, setPerson] = useState({\n    firstName: 'Barbara',\n    lastName: 'Hepworth',\n    email: 'bhepworth@sculpture.com'\n  });\n\n  function handleFirstNameChange(e) {\n    person.firstName = e.target.value;\n  }\n\n  function handleLastNameChange(e) {\n    person.lastName = e.target.value;\n  }\n\n  function handleEmailChange(e) {\n    person.email = e.target.value;\n  }\n\n  return (\n    <>\n      <label>\n        First name:\n        <input\n          value={person.firstName}\n          onChange={handleFirstNameChange}\n        />\n      </label>\n      <label>\n        Last name:\n        <input\n          value={person.lastName}\n          onChange={handleLastNameChange}\n        />\n      </label>\n      <label>\n        Email:\n        <input\n          value={person.email}\n          onChange={handleEmailChange}\n        />\n      </label>\n      <p>\n        {person.firstName}{' '}\n        {person.lastName}{' '}\n        ({person.email})\n      </p>\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 5px; margin-bottom: 5px; }\n```\n\n</Sandpack>\n\n例えば、以下の行は過去のレンダーからの state を書き換えてしまっています。\n\n```js\nperson.firstName = e.target.value;\n```\n\n望んだ動作を得る確実な方法は、新しいオブジェクトを作成して `setPerson` に渡すことです。しかし、ここではフィールドのうちの 1 つだけが変更されているため、**既存のデータもコピーしたい**でしょう。\n\n```js\nsetPerson({\n  firstName: e.target.value, // New first name from the input\n  lastName: person.lastName,\n  email: person.email\n});\n```\n\n`...` という[オブジェクトスプレッド](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#spread_in_object_literals)構文を使用することで、すべてのプロパティを個別にコピーする必要がなくなります。\n\n```js\nsetPerson({\n  ...person, // Copy the old fields\n  firstName: e.target.value // But override this one\n});\n```\n\nこれでフォームが機能します！\n\n各入力フィールドに対して state 変数を別々に宣言していないことに注目してください。大きなフォームでは、すべてのデータをオブジェクトにまとめて保持することが非常に便利です。正しく更新さえしていれば！\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [person, setPerson] = useState({\n    firstName: 'Barbara',\n    lastName: 'Hepworth',\n    email: 'bhepworth@sculpture.com'\n  });\n\n  function handleFirstNameChange(e) {\n    setPerson({\n      ...person,\n      firstName: e.target.value\n    });\n  }\n\n  function handleLastNameChange(e) {\n    setPerson({\n      ...person,\n      lastName: e.target.value\n    });\n  }\n\n  function handleEmailChange(e) {\n    setPerson({\n      ...person,\n      email: e.target.value\n    });\n  }\n\n  return (\n    <>\n      <label>\n        First name:\n        <input\n          value={person.firstName}\n          onChange={handleFirstNameChange}\n        />\n      </label>\n      <label>\n        Last name:\n        <input\n          value={person.lastName}\n          onChange={handleLastNameChange}\n        />\n      </label>\n      <label>\n        Email:\n        <input\n          value={person.email}\n          onChange={handleEmailChange}\n        />\n      </label>\n      <p>\n        {person.firstName}{' '}\n        {person.lastName}{' '}\n        ({person.email})\n      </p>\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 5px; margin-bottom: 5px; }\n```\n\n</Sandpack>\n\n`...` スプレッド構文は「浅い (shallow)」ことに注意してください。これは 1 レベルの深さでのみコピーを行います。これは高速ですが、ネストされたプロパティを更新したい場合は、スプレッド構文を複数回使用する必要があるということでもあります。\n\n<DeepDive>\n\n#### 複数のフィールドに単一のイベントハンドラを使う {/*using-a-single-event-handler-for-multiple-fields*/}\n\nオブジェクト定義内で `[` と `]` 括弧を使って、動的な名前のプロパティを指定することもできます。以下は上記と同じ例ですが、3 つの異なるイベントハンドラの代わりに 1 つのイベントハンドラを使用しています。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [person, setPerson] = useState({\n    firstName: 'Barbara',\n    lastName: 'Hepworth',\n    email: 'bhepworth@sculpture.com'\n  });\n\n  function handleChange(e) {\n    setPerson({\n      ...person,\n      [e.target.name]: e.target.value\n    });\n  }\n\n  return (\n    <>\n      <label>\n        First name:\n        <input\n          name=\"firstName\"\n          value={person.firstName}\n          onChange={handleChange}\n        />\n      </label>\n      <label>\n        Last name:\n        <input\n          name=\"lastName\"\n          value={person.lastName}\n          onChange={handleChange}\n        />\n      </label>\n      <label>\n        Email:\n        <input\n          name=\"email\"\n          value={person.email}\n          onChange={handleChange}\n        />\n      </label>\n      <p>\n        {person.firstName}{' '}\n        {person.lastName}{' '}\n        ({person.email})\n      </p>\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 5px; margin-bottom: 5px; }\n```\n\n</Sandpack>\n\nここでは、`e.target.name` は、`<input>` DOM 要素に与えられた `name` プロパティを指しています。\n\n</DeepDive>\n\n## ネストされたオブジェクトの更新 {/*updating-a-nested-object*/}\n\n以下のようなネストされたオブジェクト構造を考えてみましょう。\n\n```js\nconst [person, setPerson] = useState({\n  name: 'Niki de Saint Phalle',\n  artwork: {\n    title: 'Blue Nana',\n    city: 'Hamburg',\n    image: 'https://i.imgur.com/Sd1AgUOm.jpg',\n  }\n});\n```\n\n`person.artwork.city` を更新したい場合、ミューテーションで変更する方法は明らかです。\n\n```js\nperson.artwork.city = 'New Delhi';\n```\n\nしかし、React では state をイミュータブルなものとして扱います！ `city` を更新するためには、まず（既存のデータも含まれた）新しい `artwork` オブジェクトを生成する必要があります。そして、新しい `artwork` を含む新しい `person` オブジェクトを生成します。\n\n```js\nconst nextArtwork = { ...person.artwork, city: 'New Delhi' };\nconst nextPerson = { ...person, artwork: nextArtwork };\nsetPerson(nextPerson);\n```\n\nあるいは、単一の関数呼び出しとして記述する場合は以下のようになります。\n\n```js\nsetPerson({\n  ...person, // Copy other fields\n  artwork: { // but replace the artwork\n    ...person.artwork, // with the same one\n    city: 'New Delhi' // but in New Delhi!\n  }\n});\n```\n\nこれは少し冗長ですが、多くのケースでうまく機能します。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [person, setPerson] = useState({\n    name: 'Niki de Saint Phalle',\n    artwork: {\n      title: 'Blue Nana',\n      city: 'Hamburg',\n      image: 'https://i.imgur.com/Sd1AgUOm.jpg',\n    }\n  });\n\n  function handleNameChange(e) {\n    setPerson({\n      ...person,\n      name: e.target.value\n    });\n  }\n\n  function handleTitleChange(e) {\n    setPerson({\n      ...person,\n      artwork: {\n        ...person.artwork,\n        title: e.target.value\n      }\n    });\n  }\n\n  function handleCityChange(e) {\n    setPerson({\n      ...person,\n      artwork: {\n        ...person.artwork,\n        city: e.target.value\n      }\n    });\n  }\n\n  function handleImageChange(e) {\n    setPerson({\n      ...person,\n      artwork: {\n        ...person.artwork,\n        image: e.target.value\n      }\n    });\n  }\n\n  return (\n    <>\n      <label>\n        Name:\n        <input\n          value={person.name}\n          onChange={handleNameChange}\n        />\n      </label>\n      <label>\n        Title:\n        <input\n          value={person.artwork.title}\n          onChange={handleTitleChange}\n        />\n      </label>\n      <label>\n        City:\n        <input\n          value={person.artwork.city}\n          onChange={handleCityChange}\n        />\n      </label>\n      <label>\n        Image:\n        <input\n          value={person.artwork.image}\n          onChange={handleImageChange}\n        />\n      </label>\n      <p>\n        <i>{person.artwork.title}</i>\n        {' by '}\n        {person.name}\n        <br />\n        (located in {person.artwork.city})\n      </p>\n      <img \n        src={person.artwork.image} \n        alt={person.artwork.title}\n      />\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 5px; margin-bottom: 5px; }\nimg { width: 200px; height: 200px; }\n```\n\n</Sandpack>\n\n<DeepDive>\n\n#### オブジェクトは実際にはネストされない {/*objects-are-not-really-nested*/}\n\nこのようなオブジェクトはコード内で「ネストされている」ように見えるでしょう：\n\n```js\nlet obj = {\n  name: 'Niki de Saint Phalle',\n  artwork: {\n    title: 'Blue Nana',\n    city: 'Hamburg',\n    image: 'https://i.imgur.com/Sd1AgUOm.jpg',\n  }\n};\n```\n\nしかし、オブジェクトの振る舞いを考える場合、「ネスト」という考え方は正確ではありません。コードが実行されてしまえば「ネストされた」オブジェクトというものは存在しません。実際には、2 つの異なるオブジェクトを見ているだけです：\n\n```js\nlet obj1 = {\n  title: 'Blue Nana',\n  city: 'Hamburg',\n  image: 'https://i.imgur.com/Sd1AgUOm.jpg',\n};\n\nlet obj2 = {\n  name: 'Niki de Saint Phalle',\n  artwork: obj1\n};\n```\n\n`obj1` オブジェクトは `obj2` の「内部」にあるのではありません。例えば、`obj3` も `obj1` を「参照する」ことができます：\n\n```js\nlet obj1 = {\n  title: 'Blue Nana',\n  city: 'Hamburg',\n  image: 'https://i.imgur.com/Sd1AgUOm.jpg',\n};\n\nlet obj2 = {\n  name: 'Niki de Saint Phalle',\n  artwork: obj1\n};\n\nlet obj3 = {\n  name: 'Copycat',\n  artwork: obj1\n};\n```\n\n`obj3.artwork.city` を変更すると、`obj2.artwork.city` と `obj1.city` の両方に影響を与えます。これは、`obj3.artwork`、`obj2.artwork`、および `obj1` が同一のオブジェクトであるためです。これは、オブジェクトが「ネストされている」と考えると理解しにくくなります。そうではなく、これらはあくまで別々のオブジェクトであり、プロパティで互いを「参照している」のです。\n\n</DeepDive>\n\n### Immer で簡潔な更新ロジックを書く {/*write-concise-update-logic-with-immer*/}\n\nもし state が深くネストされている場合、[フラットにすることを検討する](/learn/choosing-the-state-structure#avoid-deeply-nested-state)べきかもしれません。しかし、state の構造を変更したくない場合は、スプレッド構文をネストして使うより短いやり方が欲しくなるかもしれません。人気ライブラリである [Immer](https://github.com/immerjs/use-immer) は、使いやすいミューテート型の構文を使って書きつつ、コピーを自動的に作成してくれるというものです。Immer を使うと、あなたのコードは一見オブジェクトをミューテートして「ルール違反」をしているかのように見えます。\n\n```js\nupdatePerson(draft => {\n  draft.artwork.city = 'Lagos';\n});\n```\n\nしかし、通常のミューテーションとは異なり、過去の state は上書きされません！\n\n<DeepDive>\n\n#### Immer はどのように動作するのか？ {/*how-does-immer-work*/}\n\nImmer から渡される `draft` は、[プロキシ (Proxy)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)と呼ばれる特殊なタイプのオブジェクトで、それに対して何を行ったのかを「記録」します。これが好きなだけミューテートができる理由です！ 内部では、Immer は `draft` のどの部分が変更されたかを把握し、あなたの編集内容を反映した完全に新しいオブジェクトを生成します。\n\n</DeepDive>\n\nImmer を試すには：\n\n1. `npm install use-immer` を実行し、Immer を依存ライブラリとして追加する\n2. 次に `import { useState } from 'react'` を `import { useImmer } from 'use-immer'` に置き換える\n\n以下は、Immer に変換された上記の例です：\n\n<Sandpack>\n\n```js\nimport { useImmer } from 'use-immer';\n\nexport default function Form() {\n  const [person, updatePerson] = useImmer({\n    name: 'Niki de Saint Phalle',\n    artwork: {\n      title: 'Blue Nana',\n      city: 'Hamburg',\n      image: 'https://i.imgur.com/Sd1AgUOm.jpg',\n    }\n  });\n\n  function handleNameChange(e) {\n    updatePerson(draft => {\n      draft.name = e.target.value;\n    });\n  }\n\n  function handleTitleChange(e) {\n    updatePerson(draft => {\n      draft.artwork.title = e.target.value;\n    });\n  }\n\n  function handleCityChange(e) {\n    updatePerson(draft => {\n      draft.artwork.city = e.target.value;\n    });\n  }\n\n  function handleImageChange(e) {\n    updatePerson(draft => {\n      draft.artwork.image = e.target.value;\n    });\n  }\n\n  return (\n    <>\n      <label>\n        Name:\n        <input\n          value={person.name}\n          onChange={handleNameChange}\n        />\n      </label>\n      <label>\n        Title:\n        <input\n          value={person.artwork.title}\n          onChange={handleTitleChange}\n        />\n      </label>\n      <label>\n        City:\n        <input\n          value={person.artwork.city}\n          onChange={handleCityChange}\n        />\n      </label>\n      <label>\n        Image:\n        <input\n          value={person.artwork.image}\n          onChange={handleImageChange}\n        />\n      </label>\n      <p>\n        <i>{person.artwork.title}</i>\n        {' by '}\n        {person.name}\n        <br />\n        (located in {person.artwork.city})\n      </p>\n      <img \n        src={person.artwork.image} \n        alt={person.artwork.title}\n      />\n    </>\n  );\n}\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"use-immer\": \"0.5.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 5px; margin-bottom: 5px; }\nimg { width: 200px; height: 200px; }\n```\n\n</Sandpack>\n\nイベントハンドラがどれほど簡潔になったか注目してください。`useState` と `useImmer` は 1 つのコンポーネント内で自由に組み合わせることができます。Immer は、state がネストしておりオブジェクトのコピーのためのコードが冗長になりそうな場合でも、更新を行うハンドラを簡潔に保つことができる素晴らしい方法です。\n\n<DeepDive>\n\n#### React ではなぜ state の変更が非推奨なのか？ {/*why-is-mutating-state-not-recommended-in-react*/}\n\nいくつかの理由があります。\n\n* **デバッグ**：`console.log` を使用しているなら、state を書き換えないことにより、古いログの内容が後に起きた state 変更によって上書きされる心配をしなくて済むようになります。つまり、レンダー間で state がどのように変化したかはっきり見ることができるようになります。\n* **最適化**：React の一般的な[最適化戦略](/reference/react/memo)は、前の props や state が次のものと同一である場合作業をスキップする、ということに依存しています。state を書き換えないことで、変更があったかどうかを非常に素早くチェックすることができます。`prevObj === obj` であれば、内部で何も変更されていないと自信を持って言えるようになります。\n* **新機能**：開発中の新しい React の機能は、state が[スナップショットのように扱われること](/learn/state-as-a-snapshot)を前提としています。過去の state の書き換えを行うと、新しい機能を使用できなくなる可能性があります。\n* **仕様変更のしやすさ**：一部のアプリケーション機能（取り消し/やり直し、履歴の表示、フォームを以前の値にリセットするなど）は、ミューテーションが起きないのであれば実装が容易です。これはメモリ内に過去の state のコピーを保持しておけば、必要に応じて再利用できるからです。ミューテーションを行うアプローチで始めてしまうと、このような機能を後で追加するのが困難になることがあります。\n* **実装のシンプルさ**：React はミューテーションの仕組みに依存しないため、オブジェクトに特別なことを一切しなくてすみます。他の多くの「リアクティブ」系ソリューションは、オブジェクトのプロパティを乗っ取ったり、プロキシにラップしたり、初期化時にその他もろもろの作業を行ったりしていますが、React ではその必要がありません。これが、React がどんな大きさのオブジェクトでも、パフォーマンスや正確性の問題を心配せずに state に入れることができる理由でもあります。\n\n実際には、React の state をミューテートしても「やり過ごせる」場合も多いのですが、そうしないことを強くお勧めします。上記のようなアプローチを念頭に開発された React の新機能を使用できるようにするためです。将来のコントリビュータや、将来のあなた自身が、あなたに感謝することでしょう！\n\n</DeepDive>\n\n<Recap>\n\n* React のすべての state はイミュータブルとして扱う。\n* state にオブジェクトを格納する場合、それらをミューテートしてもレンダーがトリガされない。それは過去のレンダー内の state の「スナップショット」を書き換えているだけである。\n* オブジェクトを書き換えるのではなく、代わりに新たなバージョンのオブジェクトを作成して、その新しいバージョンを新しい値として state をセットすることで再レンダーをトリガする。\n* `{...obj, something: 'newValue'}` というオブジェクトスプレッド構文を使ってオブジェクトのコピーを作成できる。\n* スプレッド構文は「浅い」、つまり 1 レベルのみのコピーを行う。\n* ネストされたオブジェクトを更新するには、更新している場所から一番上までの全オブジェクトのコピーを作成する必要がある。\n* コピーのためのコードが冗長になったら Immer を使う。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### 間違った state 更新を修正 {/*fix-incorrect-state-updates*/}\n\nこのフォームにはいくつかのバグがあります。スコアを増やすボタンを何度かクリックしてみてください。スコアが増えないことに気付くと思います。次に、ファーストネーム欄に入力をしようとすると、思い出したかのようにスコアが最新の値に更新されることを確認してください。最後に、ラストネームを入力してみてください。今度はスコアが完全に消えてしまいます。\n\nあなたの仕事はこれらのバグをすべて修正することです。修正しながら、それぞれのバグがなぜ発生するのかを説明してください。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [11]}}\nimport { useState } from 'react';\n\nexport default function Scoreboard() {\n  const [player, setPlayer] = useState({\n    firstName: 'Ranjani',\n    lastName: 'Shettar',\n    score: 10,\n  });\n\n  function handlePlusClick() {\n    player.score++;\n  }\n\n  function handleFirstNameChange(e) {\n    setPlayer({\n      ...player,\n      firstName: e.target.value,\n    });\n  }\n\n  function handleLastNameChange(e) {\n    setPlayer({\n      lastName: e.target.value\n    });\n  }\n\n  return (\n    <>\n      <label>\n        Score: <b>{player.score}</b>\n        {' '}\n        <button onClick={handlePlusClick}>\n          +1\n        </button>\n      </label>\n      <label>\n        First name:\n        <input\n          value={player.firstName}\n          onChange={handleFirstNameChange}\n        />\n      </label>\n      <label>\n        Last name:\n        <input\n          value={player.lastName}\n          onChange={handleLastNameChange}\n        />\n      </label>\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; margin-bottom: 10px; }\ninput { margin-left: 5px; margin-bottom: 5px; }\n```\n\n</Sandpack>\n\n<Solution>\n\nこちらが両方のバグを修正したバージョンです：\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Scoreboard() {\n  const [player, setPlayer] = useState({\n    firstName: 'Ranjani',\n    lastName: 'Shettar',\n    score: 10,\n  });\n\n  function handlePlusClick() {\n    setPlayer({\n      ...player,\n      score: player.score + 1,\n    });\n  }\n\n  function handleFirstNameChange(e) {\n    setPlayer({\n      ...player,\n      firstName: e.target.value,\n    });\n  }\n\n  function handleLastNameChange(e) {\n    setPlayer({\n      ...player,\n      lastName: e.target.value\n    });\n  }\n\n  return (\n    <>\n      <label>\n        Score: <b>{player.score}</b>\n        {' '}\n        <button onClick={handlePlusClick}>\n          +1\n        </button>\n      </label>\n      <label>\n        First name:\n        <input\n          value={player.firstName}\n          onChange={handleFirstNameChange}\n        />\n      </label>\n      <label>\n        Last name:\n        <input\n          value={player.lastName}\n          onChange={handleLastNameChange}\n        />\n      </label>\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 5px; margin-bottom: 5px; }\n```\n\n</Sandpack>\n\n`handlePlusClick` の問題は、`player` オブジェクトを書き換えてしまっていたことです。その結果、React は再レンダーの必要性を認識せず、画面上のスコアは更新されませんでした。ですがファーストネームを編集すると、対応する state が更新されてレンダーがトリガされるので、その際に画面上の*スコアも併せて*最新の値になった、というわけです。\n\n`handleLastNameChange` の問題は、新しいオブジェクトに既存の `...player` フィールドをコピーしなかったことです。これが、ラストネームを編集した後にスコアが消えてしまった理由です。\n\n</Solution>\n\n#### ミューテーションを探して修正 {/*find-and-fix-the-mutation*/}\n\n固定された背景の上にドラッグ可能なボックスが配置されています。ボックスの色は選択ボックスを使って変更できます。\n\nしかし、バグがあります。まずボックスを移動し、次にその色を変更しようとすると、（動かないはずの）背景が、ボックスの位置まで「ジャンプ」してしまいます。`Background` の `position` プロパティは `initialPosition` に設定されており、それは `{ x: 0, y: 0 }` となっているのですから、こんなことは起きないはずではないでしょうか。どうして色を変更すると背景が動いてしまうのでしょうか。\n\nバグを見つけて修正してください。\n\n<Hint>\n\n予期しない変化が起きたということは、ミューテーションがあるということです。`App.js` のミューテーションを見つけて修正してください。\n\n</Hint>\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [17]}} src/App.js\nimport { useState } from 'react';\nimport Background from './Background.js';\nimport Box from './Box.js';\n\nconst initialPosition = {\n  x: 0,\n  y: 0\n};\n\nexport default function Canvas() {\n  const [shape, setShape] = useState({\n    color: 'orange',\n    position: initialPosition\n  });\n\n  function handleMove(dx, dy) {\n    shape.position.x += dx;\n    shape.position.y += dy;\n  }\n\n  function handleColorChange(e) {\n    setShape({\n      ...shape,\n      color: e.target.value\n    });\n  }\n\n  return (\n    <>\n      <select\n        value={shape.color}\n        onChange={handleColorChange}\n      >\n        <option value=\"orange\">orange</option>\n        <option value=\"lightpink\">lightpink</option>\n        <option value=\"aliceblue\">aliceblue</option>\n      </select>\n      <Background\n        position={initialPosition}\n      />\n      <Box\n        color={shape.color}\n        position={shape.position}\n        onMove={handleMove}\n      >\n        Drag me!\n      </Box>\n    </>\n  );\n}\n```\n\n```js src/Box.js\nimport { useState } from 'react';\n\nexport default function Box({\n  children,\n  color,\n  position,\n  onMove\n}) {\n  const [\n    lastCoordinates,\n    setLastCoordinates\n  ] = useState(null);\n\n  function handlePointerDown(e) {\n    e.target.setPointerCapture(e.pointerId);\n    setLastCoordinates({\n      x: e.clientX,\n      y: e.clientY,\n    });\n  }\n\n  function handlePointerMove(e) {\n    if (lastCoordinates) {\n      setLastCoordinates({\n        x: e.clientX,\n        y: e.clientY,\n      });\n      const dx = e.clientX - lastCoordinates.x;\n      const dy = e.clientY - lastCoordinates.y;\n      onMove(dx, dy);\n    }\n  }\n\n  function handlePointerUp(e) {\n    setLastCoordinates(null);\n  }\n\n  return (\n    <div\n      onPointerDown={handlePointerDown}\n      onPointerMove={handlePointerMove}\n      onPointerUp={handlePointerUp}\n      style={{\n        width: 100,\n        height: 100,\n        cursor: 'grab',\n        backgroundColor: color,\n        position: 'absolute',\n        border: '1px solid black',\n        display: 'flex',\n        justifyContent: 'center',\n        alignItems: 'center',\n        transform: `translate(\n          ${position.x}px,\n          ${position.y}px\n        )`,\n      }}\n    >{children}</div>\n  );\n}\n```\n\n```js src/Background.js\nexport default function Background({\n  position\n}) {\n  return (\n    <div style={{\n      position: 'absolute',\n      transform: `translate(\n        ${position.x}px,\n        ${position.y}px\n      )`,\n      width: 250,\n      height: 250,\n      backgroundColor: 'rgba(200, 200, 0, 0.2)',\n    }} />\n  );\n};\n```\n\n```css\nbody { height: 280px; }\nselect { margin-bottom: 10px; }\n```\n\n</Sandpack>\n\n<Solution>\n\n問題は `handleMove` 内のミューテーションにあります。`shape.position` を書き換えていますが、それは `initialPosition` が指しているのと同一のオブジェクトです。これが、ボックスと背景が両方動いてしまった原因です。（ミューテーションをしていたためすぐには画面に反映されず、色の変更という無関係な更新により再レンダーがトリガされて初めて、位置の変化が画面に反映されたのです。）\n\n修正方法は、`handleMove` からミューテーションを除去し、スプレッド構文を使って shape をコピーすることです。`+=` はミューテーションですので、通常の `+` 操作を使って書き直す必要があります。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport Background from './Background.js';\nimport Box from './Box.js';\n\nconst initialPosition = {\n  x: 0,\n  y: 0\n};\n\nexport default function Canvas() {\n  const [shape, setShape] = useState({\n    color: 'orange',\n    position: initialPosition\n  });\n\n  function handleMove(dx, dy) {\n    setShape({\n      ...shape,\n      position: {\n        x: shape.position.x + dx,\n        y: shape.position.y + dy,\n      }\n    });\n  }\n\n  function handleColorChange(e) {\n    setShape({\n      ...shape,\n      color: e.target.value\n    });\n  }\n\n  return (\n    <>\n      <select\n        value={shape.color}\n        onChange={handleColorChange}\n      >\n        <option value=\"orange\">orange</option>\n        <option value=\"lightpink\">lightpink</option>\n        <option value=\"aliceblue\">aliceblue</option>\n      </select>\n      <Background\n        position={initialPosition}\n      />\n      <Box\n        color={shape.color}\n        position={shape.position}\n        onMove={handleMove}\n      >\n        Drag me!\n      </Box>\n    </>\n  );\n}\n```\n\n```js src/Box.js\nimport { useState } from 'react';\n\nexport default function Box({\n  children,\n  color,\n  position,\n  onMove\n}) {\n  const [\n    lastCoordinates,\n    setLastCoordinates\n  ] = useState(null);\n\n  function handlePointerDown(e) {\n    e.target.setPointerCapture(e.pointerId);\n    setLastCoordinates({\n      x: e.clientX,\n      y: e.clientY,\n    });\n  }\n\n  function handlePointerMove(e) {\n    if (lastCoordinates) {\n      setLastCoordinates({\n        x: e.clientX,\n        y: e.clientY,\n      });\n      const dx = e.clientX - lastCoordinates.x;\n      const dy = e.clientY - lastCoordinates.y;\n      onMove(dx, dy);\n    }\n  }\n\n  function handlePointerUp(e) {\n    setLastCoordinates(null);\n  }\n\n  return (\n    <div\n      onPointerDown={handlePointerDown}\n      onPointerMove={handlePointerMove}\n      onPointerUp={handlePointerUp}\n      style={{\n        width: 100,\n        height: 100,\n        cursor: 'grab',\n        backgroundColor: color,\n        position: 'absolute',\n        border: '1px solid black',\n        display: 'flex',\n        justifyContent: 'center',\n        alignItems: 'center',\n        transform: `translate(\n          ${position.x}px,\n          ${position.y}px\n        )`,\n      }}\n    >{children}</div>\n  );\n}\n```\n\n```js src/Background.js\nexport default function Background({\n  position\n}) {\n  return (\n    <div style={{\n      position: 'absolute',\n      transform: `translate(\n        ${position.x}px,\n        ${position.y}px\n      )`,\n      width: 250,\n      height: 250,\n      backgroundColor: 'rgba(200, 200, 0, 0.2)',\n    }} />\n  );\n};\n```\n\n```css\nbody { height: 280px; }\nselect { margin-bottom: 10px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### Immer を使ってオブジェクトを更新 {/*update-an-object-with-immer*/}\n\nこれはひとつ前のチャレンジと同じ、バグのある例です。今回は、Immer を使ってミューテーションを修正してください。`useImmer` はすでにインポートされているので、`shape` という state 変数の部分を変更して `useImmer` を使うようにしてください。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [18]}} src/App.js\nimport { useState } from 'react';\nimport { useImmer } from 'use-immer';\nimport Background from './Background.js';\nimport Box from './Box.js';\n\nconst initialPosition = {\n  x: 0,\n  y: 0\n};\n\nexport default function Canvas() {\n  const [shape, setShape] = useState({\n    color: 'orange',\n    position: initialPosition\n  });\n\n  function handleMove(dx, dy) {\n    shape.position.x += dx;\n    shape.position.y += dy;\n  }\n\n  function handleColorChange(e) {\n    setShape({\n      ...shape,\n      color: e.target.value\n    });\n  }\n\n  return (\n    <>\n      <select\n        value={shape.color}\n        onChange={handleColorChange}\n      >\n        <option value=\"orange\">orange</option>\n        <option value=\"lightpink\">lightpink</option>\n        <option value=\"aliceblue\">aliceblue</option>\n      </select>\n      <Background\n        position={initialPosition}\n      />\n      <Box\n        color={shape.color}\n        position={shape.position}\n        onMove={handleMove}\n      >\n        Drag me!\n      </Box>\n    </>\n  );\n}\n```\n\n```js src/Box.js\nimport { useState } from 'react';\n\nexport default function Box({\n  children,\n  color,\n  position,\n  onMove\n}) {\n  const [\n    lastCoordinates,\n    setLastCoordinates\n  ] = useState(null);\n\n  function handlePointerDown(e) {\n    e.target.setPointerCapture(e.pointerId);\n    setLastCoordinates({\n      x: e.clientX,\n      y: e.clientY,\n    });\n  }\n\n  function handlePointerMove(e) {\n    if (lastCoordinates) {\n      setLastCoordinates({\n        x: e.clientX,\n        y: e.clientY,\n      });\n      const dx = e.clientX - lastCoordinates.x;\n      const dy = e.clientY - lastCoordinates.y;\n      onMove(dx, dy);\n    }\n  }\n\n  function handlePointerUp(e) {\n    setLastCoordinates(null);\n  }\n\n  return (\n    <div\n      onPointerDown={handlePointerDown}\n      onPointerMove={handlePointerMove}\n      onPointerUp={handlePointerUp}\n      style={{\n        width: 100,\n        height: 100,\n        cursor: 'grab',\n        backgroundColor: color,\n        position: 'absolute',\n        border: '1px solid black',\n        display: 'flex',\n        justifyContent: 'center',\n        alignItems: 'center',\n        transform: `translate(\n          ${position.x}px,\n          ${position.y}px\n        )`,\n      }}\n    >{children}</div>\n  );\n}\n```\n\n```js src/Background.js\nexport default function Background({\n  position\n}) {\n  return (\n    <div style={{\n      position: 'absolute',\n      transform: `translate(\n        ${position.x}px,\n        ${position.y}px\n      )`,\n      width: 250,\n      height: 250,\n      backgroundColor: 'rgba(200, 200, 0, 0.2)',\n    }} />\n  );\n};\n```\n\n```css\nbody { height: 280px; }\nselect { margin-bottom: 10px; }\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"use-immer\": \"0.5.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\n<Solution>\n\n以下が Immer で書き直した解法です。イベントハンドラがミューテーション形式の書き方になっていますが、バグは発生しないことに注目しましょう。これは、Immer は実際には裏側で、既存のオブジェクトが決して書き換わらないようにしているためです。\n\n<Sandpack>\n\n```js src/App.js\nimport { useImmer } from 'use-immer';\nimport Background from './Background.js';\nimport Box from './Box.js';\n\nconst initialPosition = {\n  x: 0,\n  y: 0\n};\n\nexport default function Canvas() {\n  const [shape, updateShape] = useImmer({\n    color: 'orange',\n    position: initialPosition\n  });\n\n  function handleMove(dx, dy) {\n    updateShape(draft => {\n      draft.position.x += dx;\n      draft.position.y += dy;\n    });\n  }\n\n  function handleColorChange(e) {\n    updateShape(draft => {\n      draft.color = e.target.value;\n    });\n  }\n\n  return (\n    <>\n      <select\n        value={shape.color}\n        onChange={handleColorChange}\n      >\n        <option value=\"orange\">orange</option>\n        <option value=\"lightpink\">lightpink</option>\n        <option value=\"aliceblue\">aliceblue</option>\n      </select>\n      <Background\n        position={initialPosition}\n      />\n      <Box\n        color={shape.color}\n        position={shape.position}\n        onMove={handleMove}\n      >\n        Drag me!\n      </Box>\n    </>\n  );\n}\n```\n\n```js src/Box.js\nimport { useState } from 'react';\n\nexport default function Box({\n  children,\n  color,\n  position,\n  onMove\n}) {\n  const [\n    lastCoordinates,\n    setLastCoordinates\n  ] = useState(null);\n\n  function handlePointerDown(e) {\n    e.target.setPointerCapture(e.pointerId);\n    setLastCoordinates({\n      x: e.clientX,\n      y: e.clientY,\n    });\n  }\n\n  function handlePointerMove(e) {\n    if (lastCoordinates) {\n      setLastCoordinates({\n        x: e.clientX,\n        y: e.clientY,\n      });\n      const dx = e.clientX - lastCoordinates.x;\n      const dy = e.clientY - lastCoordinates.y;\n      onMove(dx, dy);\n    }\n  }\n\n  function handlePointerUp(e) {\n    setLastCoordinates(null);\n  }\n\n  return (\n    <div\n      onPointerDown={handlePointerDown}\n      onPointerMove={handlePointerMove}\n      onPointerUp={handlePointerUp}\n      style={{\n        width: 100,\n        height: 100,\n        cursor: 'grab',\n        backgroundColor: color,\n        position: 'absolute',\n        border: '1px solid black',\n        display: 'flex',\n        justifyContent: 'center',\n        alignItems: 'center',\n        transform: `translate(\n          ${position.x}px,\n          ${position.y}px\n        )`,\n      }}\n    >{children}</div>\n  );\n}\n```\n\n```js src/Background.js\nexport default function Background({\n  position\n}) {\n  return (\n    <div style={{\n      position: 'absolute',\n      transform: `translate(\n        ${position.x}px,\n        ${position.y}px\n      )`,\n      width: 250,\n      height: 250,\n      backgroundColor: 'rgba(200, 200, 0, 0.2)',\n    }} />\n  );\n};\n```\n\n```css\nbody { height: 280px; }\nselect { margin-bottom: 10px; }\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"use-immer\": \"0.5.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/writing-markup-with-jsx.md",
    "content": "---\ntitle: JSX でマークアップを記述する\n---\n\n<Intro>\n\n*JSX* とは JavaScript の拡張であり、JavaScript ファイル内に HTML のようなマークアップを書けるようにするものです。コンポーネントを書く手段はほかにも存在しますが、ほとんどの React 開発者は JSX の簡潔さを好んでいるため、ほとんどのコードベースで JSX が使われています。\n\n</Intro>\n\n<YouWillLearn>\n\n* React がマークアップとレンダリングロジックを混在させる理由\n* JSX と HTML の違い\n* JSX で情報を表示する方法\n\n</YouWillLearn>\n\n## JSX: JavaScript にマークアップを入れ込む {/*jsx-putting-markup-into-javascript*/}\n\nこれまで、ウェブは HTML、CSS そして JavaScript を使って作られてきました。長年にわたり、ウェブ開発者はコンテンツは HTML で書き、デザインは CSS で書き、ロジックは JavaScript で書き、そして大抵の場合はそれらを別ファイルにしていました。コンテンツが HTML 内にマークアップされる一方で、そのページのロジックは別ファイルの JavaScript に存在していました。\n\n<DiagramGroup>\n\n<Diagram name=\"writing_jsx_html\" height={237} width={325} alt=\"紫の背景の HTML マークアップ。p と form の 2 つの子タグを含んでいる。\">\n\nHTML\n\n</Diagram>\n\n<Diagram name=\"writing_jsx_js\" height={237} width={325} alt=\"黄色の背景に 3 つの JavaScript のハンドラ。onSubmit, onLogin, onClick の 3 つ。\">\n\nJavaScript\n\n</Diagram>\n\n</DiagramGroup>\n\nしかしウェブがよりインタラクティブなものになるにつれ、ロジックがコンテンツの中身をも決めるようになっていきました。JavaScript が HTML の領分も担当するようになったのです！ これが、**React ではロジックとマークアップを同じ場所、すなわちコンポーネントに書く**理由です。\n\n<DiagramGroup>\n\n<Diagram name=\"writing_jsx_sidebar\" height={330} width={325} alt=\"前述の例の HTML と JavaScript がミックスされた React コンポーネント。関数名は Sidebar であり、内部で isLoggedIn を呼び出している（黄色）。その中に、前述の例の p タグや、後で示すコンポーネントを呼び出すための Form タグがネストされている（紫）。\">\n\n`Sidebar.js` React コンポーネント\n\n</Diagram>\n\n<Diagram name=\"writing_jsx_form\" height={330} width={325} alt=\"前述の例の HTML と JavaScript がミックスされた React コンポーネント。関数名は Form であり、onClick と onSubmit という 2 つのハンドラが含まれている（黄色）。ハンドラの後に HTML が続く（紫）。HTML 部分に、onClick プロパティの設定された input 要素がネストされている form 要素が含まれている。\">\n\n`Form.js` React コンポーネント\n\n</Diagram>\n\n</DiagramGroup>\n\nボタンのレンダリングロジックとマークアップを同じ場所に書くことで、それらが毎回の編集時に同期されることが保証されます。逆に、ボタンのマークアップとサイドバーのマークアップといった互いに関係のない詳細は、互いに分離されるようになるため、それぞれをより安全に独立して更新できるようになります。\n\n個々の React のコンポーネントは JavaScript の関数であり、React がブラウザに表示するためのマークアップを含めることができます。そのマークアップを表現するのに、React コンポーネントは JSX と呼ばれる拡張構文を使用します。JSX は HTML ととてもよく似ていますが、より構文が厳密であり、動的な情報を表示することができます。理解するには、HTML マークアップを JSX マークアップへと変換してみるのが最もよいでしょう。\n\n<Note>\n\nJSX と React は別の物です。一緒に使われることが多いですが、[片方だけを独立して使う](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#whats-a-jsx-transform)ことは*可能*です。JSX とは言語の拡張であり、React は JavaScript ライブラリです。\n\n</Note>\n\n## HTML を JSX に変換する {/*converting-html-to-jsx*/}\n\nこのような（まったく正しい）HTML があるとしましょう：\n\n```html\n<h1>Hedy Lamarr's Todos</h1>\n<img \n  src=\"https://i.imgur.com/yXOvdOSs.jpg\" \n  alt=\"Hedy Lamarr\" \n  class=\"photo\"\n>\n<ul>\n    <li>Invent new traffic lights\n    <li>Rehearse a movie scene\n    <li>Improve the spectrum technology\n</ul>\n```\n\nこれをコンポーネントの中に入れたいとします：\n\n```js\nexport default function TodoList() {\n  return (\n    // ???\n  )\n}\n```\n\nそのままコピー・ペーストした場合、うまく動きません：\n\n\n<Sandpack>\n\n```js\nexport default function TodoList() {\n  return (\n    // This doesn't quite work!\n    <h1>Hedy Lamarr's Todos</h1>\n    <img \n      src=\"https://i.imgur.com/yXOvdOSs.jpg\" \n      alt=\"Hedy Lamarr\" \n      class=\"photo\"\n    >\n    <ul>\n      <li>Invent new traffic lights\n      <li>Rehearse a movie scene\n      <li>Improve the spectrum technology\n    </ul>\n  );\n}\n```\n\n```css\nimg { height: 90px }\n```\n\n</Sandpack>\n\nこれは、JSX の方が厳密であり、HTML よりも若干ルールが多いからです。上記のエラーメッセージを読めばマークアップの修正方法は分かるようになっていますが、今は以下のガイドに従えば大丈夫です。\n\n<Note>\n\n大抵の場合は React が画面上に表示するエラーメッセージが、問題のある場所を見つける手がかりになります。困ったら読んでみてください！\n\n</Note>\n\n## JSX のルール {/*the-rules-of-jsx*/}\n\n### 1. 単一のルート要素を返す {/*1-return-a-single-root-element*/}\n\nコンポーネントから複数の要素を返すには、**それを単一の親タグで囲みます**。\n\n例えば `<div>` を使うことができます。\n\n```js {1,11}\n<div>\n  <h1>Hedy Lamarr's Todos</h1>\n  <img \n    src=\"https://i.imgur.com/yXOvdOSs.jpg\" \n    alt=\"Hedy Lamarr\" \n    class=\"photo\"\n  >\n  <ul>\n    ...\n  </ul>\n</div>\n```\n\n\nマークアップに余分な `<div>` を加えたくない場合は、代わりに `<>` と `</>` を使うことができます。\n\n```js {1,11}\n<>\n  <h1>Hedy Lamarr's Todos</h1>\n  <img \n    src=\"https://i.imgur.com/yXOvdOSs.jpg\" \n    alt=\"Hedy Lamarr\" \n    class=\"photo\"\n  >\n  <ul>\n    ...\n  </ul>\n</>\n```\n\nこの中身のないタグは[*フラグメント (Fragment)*](/reference/react/Fragment) と呼ばれるものです。フラグメントを使えば、ブラウザの HTML ツリーに痕跡を残すことなく、複数の要素をまとめることができます。\n\n<DeepDive>\n\n#### JSX タグが複数あるときにラップしないといけない理由 {/*why-do-multiple-jsx-tags-need-to-be-wrapped*/}\n\nJSX は HTML のように見えますが、裏ではプレーンな JavaScript オブジェクトに変換されます。関数から 2 つのオブジェクトを返したい場合、配列でラップしないといけませんよね。2 つの JSX タグを返したい場合に別のタグかフラグメントでラップしないといけないのも、同じ理由です。\n\n</DeepDive>\n\n### 2. すべてのタグを閉じる {/*2-close-all-the-tags*/}\n\nJSX ではすべてのタグを明示的に閉じる必要があります。`<img>` のような自動で閉じるタグは `<img />` のようになりますし、`<li>oranges` のような囲みタグは `<li>oranges</li>` と書かなければなりません。\n\nHedy Lamarr の画像とリスト項目は、閉じタグを書いた状態では以下のようになります。\n\n```js {2-6,8-10}\n<>\n  <img \n    src=\"https://i.imgur.com/yXOvdOSs.jpg\" \n    alt=\"Hedy Lamarr\" \n    class=\"photo\"\n   />\n  <ul>\n    <li>Invent new traffic lights</li>\n    <li>Rehearse a movie scene</li>\n    <li>Improve the spectrum technology</li>\n  </ul>\n</>\n```\n\n### 3. （ほぼ）すべてキャメルケースで！ {/*3-camelcase-salls-most-of-the-things*/}\n\nJSX は JavaScript に変換され、中に書かれた属性は JavaScript オブジェクトのキーになります。コンポーネント内では、これらの属性を変数に読み出したくなることがよくあります。しかし JavaScript の変数名には一定の制約があります。例えば、名前にハイフンを含めたり `class` のような予約語を使ったりすることはできません。\n\nこのため、React では多くの HTML および SVG の属性はキャメルケースで書かれます。例えば `stroke-width` の代わりに `strokeWidth` を使います。`class` は予約語なので、React では `className` を使います（[対応する DOM プロパティ](https://developer.mozilla.org/en-US/docs/Web/API/Element/className)が由来となっています）。\n\n```js {4}\n<img \n  src=\"https://i.imgur.com/yXOvdOSs.jpg\" \n  alt=\"Hedy Lamarr\" \n  className=\"photo\"\n/>\n```\n\n全リストは [React DOM コンポーネントに存在する属性の一覧](/reference/react-dom/components/common)にあります。何かを間違ったとしても心配は要りません。[ブラウザのコンソール](https://developer.mozilla.org/docs/Tools/Browser_Console)にメッセージと修正の提案が表示されるようになっています。\n\n<Pitfall>\n\n歴史的理由により、[`aria-*`](https://developer.mozilla.org/docs/Web/Accessibility/ARIA) と [`data-*`](https://developer.mozilla.org/docs/Learn/HTML/Howto/Use_data_attributes) 属性は HTML 属性と同じようにハイフン付きで書くことになっています。\n\n</Pitfall>\n\n### ヒント：JSX コンバータを使う {/*pro-tip-use-a-jsx-converter*/}\n\n既存のマークアップの属性をすべて書きかえていくのは時に面倒です！ 既存の HTML や SVG を JSX に変換する場合は[コンバータ](https://transform.tools/html-to-jsx)を使うことをお勧めします。コンバータは実用上非常に役に立ちますが、自分でも楽に JSX が書けるよう、何が起こっているのかを理解しておくことも大切です。\n\n最終結果は以下のようなものになります。\n\n<Sandpack>\n\n```js\nexport default function TodoList() {\n  return (\n    <>\n      <h1>Hedy Lamarr's Todos</h1>\n      <img \n        src=\"https://i.imgur.com/yXOvdOSs.jpg\" \n        alt=\"Hedy Lamarr\" \n        className=\"photo\" \n      />\n      <ul>\n        <li>Invent new traffic lights</li>\n        <li>Rehearse a movie scene</li>\n        <li>Improve the spectrum technology</li>\n      </ul>\n    </>\n  );\n}\n```\n\n```css\nimg { height: 90px }\n```\n\n</Sandpack>\n\n<Recap>\n\nこれで JSX が存在する理由と、コンポーネント内での使い方について理解しました。\n\n* レンダリングロジックとマークアップは互いに関連しているので、React ではそれらをグループ化する。\n* JSX は HTML と似ているがいくつかの違いがある。必要なら[コンバータ](https://transform.tools/html-to-jsx)を使える。\n* エラーメッセージを見れば、大概はマークアップの修正方法について指針が得られる。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### HTML を JSX に変換する {/*convert-some-html-to-jsx*/}\n\nこの HTML はコンポーネント内に貼り付けられたものですが、正しい JSX ではありません。修正してください。\n\n<Sandpack>\n\n```js\nexport default function Bio() {\n  return (\n    <div class=\"intro\">\n      <h1>Welcome to my website!</h1>\n    </div>\n    <p class=\"summary\">\n      You can find my thoughts here.\n      <br><br>\n      <b>And <i>pictures</b></i> of scientists!\n    </p>\n  );\n}\n```\n\n```css\n.intro {\n  background-image: linear-gradient(to left, violet, indigo, blue, green, yellow, orange, red);\n  background-clip: text;\n  color: transparent;\n  -webkit-background-clip: text;\n  -webkit-text-fill-color: transparent;\n}\n\n.summary {\n  padding: 20px;\n  border: 10px solid gold;\n}\n```\n\n</Sandpack>\n\n手作業で直すかコンバータを使うかはお任せします！\n\n<Solution>\n\n<Sandpack>\n\n```js\nexport default function Bio() {\n  return (\n    <div>\n      <div className=\"intro\">\n        <h1>Welcome to my website!</h1>\n      </div>\n      <p className=\"summary\">\n        You can find my thoughts here.\n        <br /><br />\n        <b>And <i>pictures</i></b> of scientists!\n      </p>\n    </div>\n  );\n}\n```\n\n```css\n.intro {\n  background-image: linear-gradient(to left, violet, indigo, blue, green, yellow, orange, red);\n  background-clip: text;\n  color: transparent;\n  -webkit-background-clip: text;\n  -webkit-text-fill-color: transparent;\n}\n\n.summary {\n  padding: 20px;\n  border: 10px solid gold;\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/you-might-not-need-an-effect.md",
    "content": "---\ntitle: 'そのエフェクトは不要かも'\n---\n\n<Intro>\n\nエフェクトは React のパラダイムからの避難ハッチです。React の外に「踏み出して」、非 React ウィジェット、ネットワーク、またはブラウザ DOM などの外部システムと同期させることができるものです。外部システムが関与していない場合（例えば、props や state の変更に合わせてコンポーネントの state を更新したい場合）、エフェクトは必要ありません。不要なエフェクトを削除することで、コードが読みやすくなり、実行速度が向上し、エラーが発生しにくくなります。\n\n</Intro>\n\n<YouWillLearn>\n\n* コンポーネントから不要なエフェクトを削除する理由と方法\n* エフェクトを使わずに高価な計算をキャッシュする方法\n* エフェクトを使わずにコンポーネントの state をリセットおよび調整する方法\n* イベントハンドラ間でロジックを共有する方法\n* イベントハンドラに移動すべきロジック\n* 親コンポーネントに変更を通知する方法\n\n</YouWillLearn>\n\n## 不要なエフェクトの削除方法 {/*how-to-remove-unnecessary-effects*/}\n\nエフェクトが不要な場合として一般的なのは次の 2 つのケースです。\n\n* **レンダーのためのデータ変換にエフェクトは必要ありません**。例えば、表示する前にリストをフィルタリングしたいとします。リストが変更されたときに state 変数を更新するようなエフェクトを書きたくなるかもしれません。しかし、これは非効率的です。state を更新すると、React はまず、画面の表示内容を計算するためにコンポーネントの関数を呼び出します。次に、React はこれらの変更を DOM に [\"コミット\"](/learn/render-and-commit) して、画面を更新します。その後、React はエフェクトを実行します。ここであなたのエフェクトが*また*直ちに state を更新してしまうと、このプロセス全体が最初からやり直しになってしまいます！ 不要なレンダーを避けるために、コンポーネントのトップレベルですべてのデータを変換するようにしましょう。そのコードは、props や state が変更されるたびに自動的に再実行されます。\n* **ユーザイベントの処理にエフェクトは必要ありません**。例えば、ユーザが製品を購入したときに `/api/buy` POST リクエストを送信し、通知を表示したいとします。購入ボタンのクリックイベントハンドラでは、何が起こったかが正確にわかります。エフェクトが実行される時点では、ユーザが*何をした*のか（例えば、どのボタンがクリックされたのか）はもうわかりません。したがって、通常は対応するイベントハンドラでユーザイベントを処理するべきです。\n\nエフェクトは、外部システムと[同期](/learn/synchronizing-with-effects#what-are-effects-and-how-are-they-different-from-events)したい場合には*必要*です。例えば、React の state と jQuery ウィジェットを同期させるエフェクトを書くことができます。また、エフェクトでデータを取得し、例えば現在の検索クエリと検索結果を同期させることができます。ただし、モダンな[フレームワーク](/learn/creating-a-react-app#full-stack-frameworks)は、コンポーネント内で直接エフェクトを書くよりも効率的な組み込みデータ取得メカニズムを提供していることに注意してください。\n\n正しい直観力を養うために、一般的かつ具体的な例をいくつか見ていきましょう！\n\n### props または state に基づいて state を更新する {/*updating-state-based-on-props-or-state*/}\n\n例えば、`firstName` と `lastName` の 2 つの state 変数を持つコンポーネントがあるとします。これらを連結して `fullName` を計算したいとします。となると、`firstName` または `lastName` が変更されたときに `fullName` を更新したくなるでしょう。直観的には、`fullName` という state 変数を追加して、エフェクトでそれを更新すればいいと思うかもしれません。\n\n```js {expectedErrors: {'react-compiler': [8]}} {5-9}\nfunction Form() {\n  const [firstName, setFirstName] = useState('Taylor');\n  const [lastName, setLastName] = useState('Swift');\n\n  // 🔴 Avoid: redundant state and unnecessary Effect\n  const [fullName, setFullName] = useState('');\n  useEffect(() => {\n    setFullName(firstName + ' ' + lastName);\n  }, [firstName, lastName]);\n  // ...\n}\n```\n\nこれは必要以上に複雑です。また、非効率的でもあります。古くなった `fullName` の値でレンダー処理を最後まで行った直後に、更新された値で再レンダーをやり直すことになります。state 変数とエフェクトを削除してください。\n\n```js {4-5}\nfunction Form() {\n  const [firstName, setFirstName] = useState('Taylor');\n  const [lastName, setLastName] = useState('Swift');\n  // ✅ Good: calculated during rendering\n  const fullName = firstName + ' ' + lastName;\n  // ...\n}\n```\n\n**既存の props や state から計算できるものは、[state に入れないでください](/learn/choosing-the-state-structure#avoid-redundant-state)。代わりに、レンダー中に計算します**。これによりコードは（余分な「連動」更新処理が消えたことにより）高速になり、（コードを削減したことにより）簡潔になり、さらに（異なる state 変数が同期しなくなるバグを回避できたことにより）エラーも少なくなります。このアプローチになじみがない場合は、[React の流儀](/learn/thinking-in-react#step-3-find-the-minimal-but-complete-representation-of-ui-state)で state に入れるべきものを説明しています。\n\n### 重たい計算のキャッシュ {/*caching-expensive-calculations*/}\n\nこのコンポーネントは、props で受け取った `todos` を、`filter` プロパティに従ってフィルタリングして `visibleTodos` を計算しています。計算結果を state に格納してエフェクトから更新するようにしたくなるかもしれません。\n\n```js {expectedErrors: {'react-compiler': [7]}} {4-8}\nfunction TodoList({ todos, filter }) {\n  const [newTodo, setNewTodo] = useState('');\n\n  // 🔴 Avoid: redundant state and unnecessary Effect\n  const [visibleTodos, setVisibleTodos] = useState([]);\n  useEffect(() => {\n    setVisibleTodos(getFilteredTodos(todos, filter));\n  }, [todos, filter]);\n\n  // ...\n}\n```\n\n前の例と同様に、これは不必要であり、かつ非効率的です。まず、state とエフェクトを削除します。\n\n```js {3-4}\nfunction TodoList({ todos, filter }) {\n  const [newTodo, setNewTodo] = useState('');\n  // ✅ This is fine if getFilteredTodos() is not slow.\n  const visibleTodos = getFilteredTodos(todos, filter);\n  // ...\n}\n```\n\n通常、このコードに問題はありません！ しかし、`getFilteredTodos()` が遅かったり、大量の `todos` があったりするかもしれません。そのような場合、`newTodo` のような無関係の state 変数が変更されたときに `getFilteredTodos()` の再計算を避けたくなるかもしれません。\n\n重たい計算をキャッシュする（あるいは [\"メモ化する (memoize)\"](https://en.wikipedia.org/wiki/Memoization)）には、[`useMemo`](/reference/react/useMemo) フックでラップします。\n\n<Note>\n\n[React Compiler](/learn/react-compiler) は高価な計算を自動でメモ化してくれるため、多くの場合、手作業による `useMemo` は不要となります。\n\n</Note>\n\n```js {5-8}\nimport { useMemo, useState } from 'react';\n\nfunction TodoList({ todos, filter }) {\n  const [newTodo, setNewTodo] = useState('');\n  const visibleTodos = useMemo(() => {\n    // ✅ Does not re-run unless todos or filter change\n    return getFilteredTodos(todos, filter);\n  }, [todos, filter]);\n  // ...\n}\n```\n\nまたは、1 行で書くと：\n\n```js {5-6}\nimport { useMemo, useState } from 'react';\n\nfunction TodoList({ todos, filter }) {\n  const [newTodo, setNewTodo] = useState('');\n  // ✅ Does not re-run getFilteredTodos() unless todos or filter change\n  const visibleTodos = useMemo(() => getFilteredTodos(todos, filter), [todos, filter]);\n  // ...\n}\n```\n\n**これは、`todos` または `filter` のどちらかが変更されない限り、中の関数を再実行しないよう React に指示するものです**。React は初回レンダー時に `getFilteredTodos()` の返り値を覚えておきます。次回以降のレンダーでは、`todos` または `filter` が異なるかどうかをチェックします。前回と同じ場合、`useMemo` は最後に格納した結果を返します。異なる場合、React は再び中の関数を呼び出し、その結果を格納します。\n\n[`useMemo`](/reference/react/useMemo) でラップする関数はレンダー中に実行されるため、[純粋 (pure) な計算](/learn/keeping-components-pure)に対してのみ機能します。\n\n<DeepDive>\n\n#### 計算コストが高いかどうかを見分ける方法 {/*how-to-tell-if-a-calculation-is-expensive*/}\n\n一般的に、何千ものオブジェクトを作成したりループしたりしていない限り、おそらく高価ではありません。より確信を持ちたい場合は、コンソールログを追加して、コードの実行にかかった時間を計測することができます。\n\n```js {1,3}\nconsole.time('filter array');\nconst visibleTodos = getFilteredTodos(todos, filter);\nconsole.timeEnd('filter array');\n```\n\n測定したいユーザ操作（例えば、入力フィールドへのタイプ）を実行します。その後、コンソールに `filter array: 0.15ms` のようなログが表示されます。全体のログ時間がかなりの量（例えば `1ms` 以上）になる場合、その計算をメモ化する意味があるかもしれません。実験として `useMemo` で計算をラップしてみて、その操作に対する合計時間が減少したかどうかをログで確認できます。\n\n```js\nconsole.time('filter array');\nconst visibleTodos = useMemo(() => {\n  return getFilteredTodos(todos, filter); // Skipped if todos and filter haven't changed\n}, [todos, filter]);\nconsole.timeEnd('filter array');\n```\n\n`useMemo` は*初回*レンダーを高速化しません。更新時に不要な作業をスキップするときにのみ役立ちます。\n\nまた、ほとんどの場合に、あなたが使っているマシンは、ユーザのマシンより高速に動作するであろうことを忘れてはいけません。そのため、意図的に処理速度を低下させてパフォーマンスをテストするのが良いでしょう。例えば、Chrome では [CPU スロットリング](https://developer.chrome.com/blog/new-in-devtools-61/#throttling)オプションが提供されています。\n\nまた、開発環境でのパフォーマンス測定では完全に正確な結果は得られないことに注意してください。（例えば、[Strict Mode](/reference/react/StrictMode) がオンの場合、各コンポーネントが 1 度ではなく 2 度レンダーされることがあります。）最も正確にパフォーマンスを計測するためには、アプリを本番環境用にビルドし、ユーザが持っているようなデバイスでテストしてください。\n\n</DeepDive>\n\n### props が変更されたときにすべての state をリセットする {/*resetting-all-state-when-a-prop-changes*/}\n\nこの `ProfilePage` コンポーネントは props として `userId` を受け取ります。ページにはコメント入力欄があり、その値を保持するために `comment` という state 変数を使用しています。ある日、問題に気付きました。あるプロフィールから別のプロフィールに移動しても、`comment` がリセットされないのです。その結果、うっかり別のユーザのプロフィールにコメントを投稿してしまいやすい状態になっています。この問題を解決するために、`userId` が変更されるたびに `comment` state 変数をクリアしたいと考えています。\n\n```js {expectedErrors: {'react-compiler': [6]}} {4-7}\nexport default function ProfilePage({ userId }) {\n  const [comment, setComment] = useState('');\n\n  // 🔴 Avoid: Resetting state on prop change in an Effect\n  useEffect(() => {\n    setComment('');\n  }, [userId]);\n  // ...\n}\n```\n\nこれは非効率的です。なぜなら、`ProfilePage` とその子コンポーネントは、まず古くなった値でレンダーされ、その後再度レンダーされるからです。また、`ProfilePage` 内にある state を持つ*すべて*のコンポーネントでこれを行う必要があるため、複雑でもあります。例えば、コメント UI がネストされている場合、ネストされたコメントの state もクリアしたいでしょう。\n\nこうする代わりに、各ユーザのプロフィールが概念的には*異なる*プロフィールであることを React に伝えることができます。コンポーネントを 2 つに分割し、外側のコンポーネントから内側のコンポーネントに `key` 属性を渡します：\n\n```js {5,11-12}\nexport default function ProfilePage({ userId }) {\n  return (\n    <Profile\n      userId={userId}\n      key={userId}\n    />\n  );\n}\n\nfunction Profile({ userId }) {\n  // ✅ This and any other state below will reset on key change automatically\n  const [comment, setComment] = useState('');\n  // ...\n}\n```\n\n通常、React は同じコンポーネントが同じ場所でレンダーされるときに state を保持します。**`userId` を `Profile` コンポーネントの `key` として渡すことで、異なる `userId` を持つ 2 つの `Profile` コンポーネントを、state を共有すべきでない 2 つの異なるコンポーネントとして React に扱わせることができます**。（`userId` となるようセットした）key が変更されるたびに、React は DOM を再作成し、`Profile` コンポーネントとそのすべての子コンポーネントの [state をリセット](/learn/preserving-and-resetting-state#option-2-resetting-state-with-a-key)します。これで、プロフィール間を移動するときに `comment` フィールドが自動的にクリアされるようになります。\n\nこの例では、外側の `ProfilePage` コンポーネントのみがプロジェクト内の他のファイルにエクスポートされ、表示されます。`ProfilePage` をレンダーするコンポーネントの側は、key を渡す必要はありません。代わりに、`userId` を通常の props として渡します。`ProfilePage` が内部の `Profile` コンポーネントにそれを `key` として渡していることは、実装の詳細です。\n\n### props が変更されたときに一部の state を調整する {/*adjusting-some-state-when-a-prop-changes*/}\n\n場合によっては、props が変更されたときに全部の state ではなく一部のみをリセットまたは調整したいことがあります。\n\nこの `List` コンポーネントは、`items` リストを props として受け取り、選択中のアイテムを `selection` という state 変数に保持します。`items` が異なる配列を受け取るたびに、`selection` を `null` にリセットしたいとします。\n\n```js {expectedErrors: {'react-compiler': [7]}} {5-8}\nfunction List({ items }) {\n  const [isReverse, setIsReverse] = useState(false);\n  const [selection, setSelection] = useState(null);\n\n  // 🔴 Avoid: Adjusting state on prop change in an Effect\n  useEffect(() => {\n    setSelection(null);\n  }, [items]);\n  // ...\n}\n```\n\nこれもやはり理想的ではありません。`items` が変更されるたびに、`List` とその子コンポーネントはまず既に古くなった `selection` の値でレンダーされてしまいます。その後、React は DOM を更新し、エフェクトを実行します。最後に、`setSelection(null)` の呼び出しによって、`List` とその子コンポーネントのレンダーがもう一度引き起こされ、このプロセス全体が繰り返されます。\n\nまずはエフェクトを削除しましょう。代わりに、レンダー中に直接 state の調整を行います。\n\n```js {5-11}\nfunction List({ items }) {\n  const [isReverse, setIsReverse] = useState(false);\n  const [selection, setSelection] = useState(null);\n\n  // Better: Adjust the state while rendering\n  const [prevItems, setPrevItems] = useState(items);\n  if (items !== prevItems) {\n    setPrevItems(items);\n    setSelection(null);\n  }\n  // ...\n}\n```\n\nこのような[前回のレンダーからの情報を保存する](/reference/react/useState#storing-information-from-previous-renders)手法は理解しにくいかもしれませんが、エフェクトで同一の state を更新するよりはましです。上記の例では、`setSelection` はレンダー中に直接呼び出されます。React は `return` 文で終了した*直後に* `List` を再レンダーします。React はまだ `List` の子のレンダーや DOM の更新を行っていないので、これによって `List` の子が古くなった `selection` の値でレンダーされてしまうことを回避できます。\n\nレンダー中にコンポーネントを更新すると、React は返り値の JSX を破棄して、すぐにレンダーを再試行します。非常に遅くなる連鎖的な再レンダーを避けるために、React はレンダー中に*同じ*コンポーネントの state を更新することしか許可していません。レンダー中に別のコンポーネントの state を更新すると、エラーが表示されます。無限ループを避けるために、`items !== prevItems` のような条件が必要です。このタイプの state 調整は大丈夫ですが、他のあらゆる副作用（DOM の変更やタイムアウトの設定など）は、イベントハンドラやエフェクトに書き、[コンポーネントを純粋に保つ](/learn/keeping-components-pure)必要があります。\n\n**このパターンはエフェクトよりも効率的ですが、ほとんどのコンポーネントではこれすらも必要ありません**。どのようにするにしても、props や他の state に基づいて state を調整すると、データフローが理解しにくくなり、デバッグが難しくなります。代わりに常に、[key ですべての state をリセット](#resetting-all-state-when-a-prop-changes)できないか、[レンダー中にすべてを計算](#updating-state-based-on-props-or-state)できないか、検討してください。例えば、選択された*アイテム*を保存（およびリセット）する代わりに、選択された*アイテム ID* を保存できます。\n\n```js {3-5}\nfunction List({ items }) {\n  const [isReverse, setIsReverse] = useState(false);\n  const [selectedId, setSelectedId] = useState(null);\n  // ✅ Best: Calculate everything during rendering\n  const selection = items.find(item => item.id === selectedId) ?? null;\n  // ...\n}\n```\n\nこうすることでそもそも state を「調整」する必要がなくなります。選択された ID のアイテムがリストにある場合、選択されたままです。そうでない場合、レンダー中に計算される `selection` は、一致するアイテムが見つからないので `null` になります。挙動は変わっていますが、`items` が変わっても大抵はアイテムの選択が維持されるようになるため、むしろ良くなっていると言えるでしょう。\n\n### イベントハンドラ間でのロジックの共有 {/*sharing-logic-between-event-handlers*/}\n\n例えば、2 つのボタン（Buy と Checkout）がある商品ページがあり、どちらのボタンでもその商品を購入できるとします。ユーザが商品をカートに入れるたびに通知を表示したいとします。両方のボタンのクリックハンドラで `showNotification()` を呼び出すのは繰り返しに感じられるため、エフェクトにこのロジックを配置したくなるかもしれません。\n\n```js {2-7}\nfunction ProductPage({ product, addToCart }) {\n  // 🔴 Avoid: Event-specific logic inside an Effect\n  useEffect(() => {\n    if (product.isInCart) {\n      showNotification(`Added ${product.name} to the shopping cart!`);\n    }\n  }, [product]);\n\n  function handleBuyClick() {\n    addToCart(product);\n  }\n\n  function handleCheckoutClick() {\n    addToCart(product);\n    navigateTo('/checkout');\n  }\n  // ...\n}\n```\n\nこのエフェクトは不要です。また、おそらくバグを引き起こすでしょう。例えば、アプリがページのリロード間でショッピングカートを「覚えている」としましょう。一度商品をカートに追加してページを更新すると、通知が再び表示されます。商品ページを更新するたびに通知が表示され続けます。これは、ページの読み込み時に `product.isInCart` がすでに `true` になっているため、上記のエフェクトが `showNotification()` を呼び出すからです。\n\n**あるコードがエフェクトにあるべきか、イベントハンドラにあるべきかわからない場合は、そのコードが実行される*理由*を自問してください。コンポーネントがユーザに*表示されたために*実行されるべきコードにのみエフェクトを使用してください**。この例では、通知はユーザが*ボタンを押した*ために表示されるのであって、ページが表示されたためではありません！ エフェクトを削除し、両方のイベントハンドラから呼び出される新しい関数に共有ロジックを入れるようにしてください。\n\n```js {2-6,9,13}\nfunction ProductPage({ product, addToCart }) {\n  // ✅ Good: Event-specific logic is called from event handlers\n  function buyProduct() {\n    addToCart(product);\n    showNotification(`Added ${product.name} to the shopping cart!`);\n  }\n\n  function handleBuyClick() {\n    buyProduct();\n  }\n\n  function handleCheckoutClick() {\n    buyProduct();\n    navigateTo('/checkout');\n  }\n  // ...\n}\n```\n\nこれにより、不要なエフェクトが削除され、バグも修正されます。\n\n### POST リクエストの送信 {/*sending-a-post-request*/}\n\nこの `Form` コンポーネントは、2 種類の POST リクエストを送信します。マウント時にはアナリティクスイベントを送信します。フォームに入力して送信ボタンをクリックしたときには、`/api/register` エンドポイントに POST リクエストを送信します。\n\n```js {5-8,10-16}\nfunction Form() {\n  const [firstName, setFirstName] = useState('');\n  const [lastName, setLastName] = useState('');\n\n  // ✅ Good: This logic should run because the component was displayed\n  useEffect(() => {\n    post('/analytics/event', { eventName: 'visit_form' });\n  }, []);\n\n  // 🔴 Avoid: Event-specific logic inside an Effect\n  const [jsonToSubmit, setJsonToSubmit] = useState(null);\n  useEffect(() => {\n    if (jsonToSubmit !== null) {\n      post('/api/register', jsonToSubmit);\n    }\n  }, [jsonToSubmit]);\n\n  function handleSubmit(e) {\n    e.preventDefault();\n    setJsonToSubmit({ firstName, lastName });\n  }\n  // ...\n}\n```\n\n前の例と同じ基準を適用してみましょう。\n\nアナリティクスの POST リクエストはエフェクトに残すべきです。これは、フォームが表示されたことがアナリティクスイベントを送信する理由だからです。（開発中に 2 回実行されますが、[こちら](/learn/synchronizing-with-effects#sending-analytics)で対処方法を参照してください。）\n\nただし、`/api/register` の POST リクエストは、フォームが*表示される*ことによって引き起こされるわけではありません。特定の瞬間、すなわちユーザがボタンを押した瞬間にのみリクエストを送信したいのです。リクエストは*その特定の操作時にだけ*発生するべきです。2 つ目のエフェクトを削除し、POST リクエストはイベントハンドラに移動しましょう。\n\n```js {12-13}\nfunction Form() {\n  const [firstName, setFirstName] = useState('');\n  const [lastName, setLastName] = useState('');\n\n  // ✅ Good: This logic runs because the component was displayed\n  useEffect(() => {\n    post('/analytics/event', { eventName: 'visit_form' });\n  }, []);\n\n  function handleSubmit(e) {\n    e.preventDefault();\n    // ✅ Good: Event-specific logic is in the event handler\n    post('/api/register', { firstName, lastName });\n  }\n  // ...\n}\n```\n\nイベントハンドラとエフェクトのどちらにロジックを入れるべきか選択する際には、ユーザの観点から*それがどのようなロジックなのか*を自問自答するようにしましょう。そのロジックが特定のユーザ操作によって引き起こされる場合は、イベントハンドラに保持します。ユーザが画面上でコンポーネントを*見る*ことによって引き起こされる場合は、エフェクトに保持します。\n\n### 計算の数珠繋ぎ {/*chains-of-computations*/}\n\n時々、他の state に基づいて state の一部を調整するエフェクトを連結させたくなることがあります。\n\n```js {7-29}\nfunction Game() {\n  const [card, setCard] = useState(null);\n  const [goldCardCount, setGoldCardCount] = useState(0);\n  const [round, setRound] = useState(1);\n  const [isGameOver, setIsGameOver] = useState(false);\n\n  // 🔴 Avoid: Chains of Effects that adjust the state solely to trigger each other\n  useEffect(() => {\n    if (card !== null && card.gold) {\n      setGoldCardCount(c => c + 1);\n    }\n  }, [card]);\n\n  useEffect(() => {\n    if (goldCardCount > 3) {\n      setRound(r => r + 1)\n      setGoldCardCount(0);\n    }\n  }, [goldCardCount]);\n\n  useEffect(() => {\n    if (round > 5) {\n      setIsGameOver(true);\n    }\n  }, [round]);\n\n  useEffect(() => {\n    alert('Good game!');\n  }, [isGameOver]);\n\n  function handlePlaceCard(nextCard) {\n    if (isGameOver) {\n      throw Error('Game already ended.');\n    } else {\n      setCard(nextCard);\n    }\n  }\n\n  // ...\n```\n\nこのコードには 2 つの問題があります。\n\n1 つ目の問題は、非常に効率が悪いことです。コンポーネント（およびその子）は、連鎖内の各 `set` コールの間で毎回再レンダーする必要があります。上記の例では、最悪の場合、下位のツリーに 3 回の不要な再レンダー（`setCard` → レンダー → `setGoldCardCount` → レンダー → `setRound` → レンダー → `setIsGameOver` → レンダー）が発生することになります。\n\n2 つ目の問題は、たとえこれが遅くなかったとしても、コードが発展するにつれ、書いた「チェイン」が新しい要件に適合しないケースが出てくるということです。例えばゲームの手順を遡る機能を追加しているとしましょう。このためには、各 state 変数を過去のある時点の値に再セットしていくことになります。しかし過去の値から `card` の state をセットした時点で再びエフェクトの連鎖処理がトリガされ、表示されるデータが変更されてしまいます。このようなコードは、硬直的で壊れやすいものです。\n\nこのような場合、レンダー中に計算できるものはそこで行い、イベントハンドラで state の調整を終わらせる方が良いでしょう。\n\n```js {6-7,14-26}\nfunction Game() {\n  const [card, setCard] = useState(null);\n  const [goldCardCount, setGoldCardCount] = useState(0);\n  const [round, setRound] = useState(1);\n\n  // ✅ Calculate what you can during rendering\n  const isGameOver = round > 5;\n\n  function handlePlaceCard(nextCard) {\n    if (isGameOver) {\n      throw Error('Game already ended.');\n    }\n\n    // ✅ Calculate all the next state in the event handler\n    setCard(nextCard);\n    if (nextCard.gold) {\n      if (goldCardCount < 3) {\n        setGoldCardCount(goldCardCount + 1);\n      } else {\n        setGoldCardCount(0);\n        setRound(round + 1);\n        if (round === 5) {\n          alert('Good game!');\n        }\n      }\n    }\n  }\n\n  // ...\n```\n\nこの方がはるかに効率的です。また、ゲームの履歴を表示する方法を実装する場合でも、各 state 変数を過去の手順の時点の値に設定できるようになり、エフェクトが連鎖して他のすべての state が勝手に書き換わるようなことを避けられます。複数のイベントハンドラ間でロジックを再利用する必要がある場合は、[関数を抽出](#sharing-logic-between-event-handlers)して、それらのハンドラから呼び出すことができます。\n\nイベントハンドラ内では、[state はスナップショットのように振る舞う](/learn/state-as-a-snapshot)ことを思い返してください。例えば、`setRound(round + 1)` を呼び出した後でも、`round` 変数はユーザがボタンをクリックしたときの値を反映します。計算のために更新後の値が必要な場合は、`const nextRound = round + 1` のように手動で定義してください。\n\n場合によっては、イベントハンドラ内で次の state を直接計算することができないことがあります。例えば、複数のドロップダウンがあるフォームで、前のドロップダウンの選択値によって次のドロップダウンの選択肢が変わるところを想像してください。その場合は、ネットワークとの同期が発生しているので、エフェクトを連鎖させることは適切です。\n\n### アプリケーションの初期化 {/*initializing-the-application*/}\n\nアプリが読み込まれるときに一度だけ実行されるべきロジックがあります。\n\nそれをトップレベルのコンポーネントのエフェクトに配置したくなるかもしれません。\n\n```js {2-6}\nfunction App() {\n  // 🔴 Avoid: Effects with logic that should only ever run once\n  useEffect(() => {\n    loadDataFromLocalStorage();\n    checkAuthToken();\n  }, []);\n  // ...\n}\n```\n\nですが、開発中にこれが [2 回実行される](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development)ことにすぐ気付くことになるでしょう。これにより問題が発生することがあります。例えば、関数が 2 回呼び出されることを想定しておらず、認証トークンが無効になるかもしれません。一般的に、コンポーネントは再マウントに対応できるようにする必要があり、これにはトップレベルの `App` コンポーネントも含まれます。\n\n本番環境では実際には再マウントされないにしても、すべてのコンポーネントで同じ制約に従うことで、コードの移動や再利用が容易になります。あるロジックが*コンポーネントのマウントごと*ではなく、*アプリのロードごとに* 実行される必要がある場合は、すでに実行されたかどうかを追跡するためのトップレベルの変数を追加します。\n\n```js {1,5-6,10}\nlet didInit = false;\n\nfunction App() {\n  useEffect(() => {\n    if (!didInit) {\n      didInit = true;\n      // ✅ Only runs once per app load\n      loadDataFromLocalStorage();\n      checkAuthToken();\n    }\n  }, []);\n  // ...\n}\n```\n\nまた、当該コードをモジュールの初期化中やアプリのレンダー前に実行することもできます。\n\n```js {1,5}\nif (typeof window !== 'undefined') { // Check if we're running in the browser.\n   // ✅ Only runs once per app load\n  checkAuthToken();\n  loadDataFromLocalStorage();\n}\n\nfunction App() {\n  // ...\n}\n```\n\nトップレベルのコードは、コンポーネントがインポートされたとき（仮にそれが一切レンダーされなかったとしても）に、一度だけ実行されます。コンポーネントをインポートする際の遅延や予期せぬ動作を避けるため、このパターンは過剰に使用しないでください。アプリ全体の初期化ロジックは、`App.js` のようなルートコンポーネントモジュールやアプリケーションのエントリーポイントに保持するようにしましょう。\n\n### 親コンポーネントへの state 変更の通知 {/*notifying-parent-components-about-state-changes*/}\n\n例えば、内部の `isOn` state が `true` または `false` になる `Toggle` コンポーネントを作成しているとします。トグルする方法は複数あります（クリックやドラッグなど）。`Toggle` の内部 state が変更されるたびに親コンポーネントに通知したいので、`onChange` イベントを公開し、エフェクトから呼び出しています。\n\n```js {4-7}\nfunction Toggle({ onChange }) {\n  const [isOn, setIsOn] = useState(false);\n\n  // 🔴 Avoid: The onChange handler runs too late\n  useEffect(() => {\n    onChange(isOn);\n  }, [isOn, onChange])\n\n  function handleClick() {\n    setIsOn(!isOn);\n  }\n\n  function handleDragEnd(e) {\n    if (isCloserToRightEdge(e)) {\n      setIsOn(true);\n    } else {\n      setIsOn(false);\n    }\n  }\n\n  // ...\n}\n```\n\n先ほどと同様ですが、これも理想的ではありません。まず `Toggle` が state を更新し、React が画面を更新します。次に、React がエフェクトを実行し、親コンポーネントから渡された `onChange` 関数を呼び出します。すると親コンポーネントが自身の state を更新するので、一連のレンダー処理が新たにやり直しになります。すべてを 1 回の処理で行う方が良いでしょう。\n\nエフェクトを削除し、代わりに同じイベントハンドラ内で*両方の*コンポーネントの state を更新します。\n\n```js {5-7,11,16,18}\nfunction Toggle({ onChange }) {\n  const [isOn, setIsOn] = useState(false);\n\n  function updateToggle(nextIsOn) {\n    // ✅ Good: Perform all updates during the event that caused them\n    setIsOn(nextIsOn);\n    onChange(nextIsOn);\n  }\n\n  function handleClick() {\n    updateToggle(!isOn);\n  }\n\n  function handleDragEnd(e) {\n    if (isCloserToRightEdge(e)) {\n      updateToggle(true);\n    } else {\n      updateToggle(false);\n    }\n  }\n\n  // ...\n}\n```\n\nこのアプローチであれば、`Toggle` コンポーネントとその親コンポーネントの両方がイベント中に state を更新します。React は、異なるコンポーネントからの更新を[バッチ処理](/learn/queueing-a-series-of-state-updates)するため、レンダー処理は 1 回だけになります。\n\nまたは、state を完全に削除し、代わりに `isOn` を親コンポーネントから受け取ることもできるでしょう。\n\n```js {1,2}\n// ✅ Also good: the component is fully controlled by its parent\nfunction Toggle({ isOn, onChange }) {\n  function handleClick() {\n    onChange(!isOn);\n  }\n\n  function handleDragEnd(e) {\n    if (isCloserToRightEdge(e)) {\n      onChange(true);\n    } else {\n      onChange(false);\n    }\n  }\n\n  // ...\n}\n```\n\n[\"state をリフトアップする\"](/learn/sharing-state-between-components) ようにすれば、親の state を切り替えることで親コンポーネントが `Toggle` を完全に制御できるようになります。これにより、親コンポーネントにはより多くのロジックが含まれることになりますが、全体として考える必要のある state が少なくなります。2 つの異なる state 変数を同期させたいと思ったら常に、代わりに state のリフトアップを試すようにしてください！\n\n### 親にデータを渡す {/*passing-data-to-the-parent*/}\n\nこの `Child` コンポーネントは、データを取得し、それをエフェクト内で `Parent` コンポーネントに渡しています。\n\n```js {9-14}\nfunction Parent() {\n  const [data, setData] = useState(null);\n  // ...\n  return <Child onFetched={setData} />;\n}\n\nfunction Child({ onFetched }) {\n  const data = useSomeAPI();\n  // 🔴 Avoid: Passing data to the parent in an Effect\n  useEffect(() => {\n    if (data) {\n      onFetched(data);\n    }\n  }, [onFetched, data]);\n  // ...\n}\n```\n\nReact では、データは親コンポーネントから子コンポーネントに流れます。画面上で何かおかしなことがあるときは、おかしな情報がどこから来るのかを調べるために、コンポーネントの繋がりを上にたどっていき、どのコンポーネントが間違った props を渡しているのか、あるいは間違った state を持っているのかを見つけます。ですが子コンポーネントがエフェクト内で親コンポーネントの state を更新していると、データの流れの追跡が非常に困難になってしまいます。子と親の両方が同じデータを必要としているのですから、親コンポーネントがそのデータを取得し、子に*渡す*ようにしましょう。\n\n```js {4-5}\nfunction Parent() {\n  const data = useSomeAPI();\n  // ...\n  // ✅ Good: Passing data down to the child\n  return <Child data={data} />;\n}\n\nfunction Child({ data }) {\n  // ...\n}\n```\n\nこれはよりシンプルであり、データは親から子へ流れるため予測可能なものになります。\n\n### 外部ストアへのサブスクライブ {/*subscribing-to-an-external-store*/}\n\nコンポーネントが React の状態の外にあるデータをサブスクライブ（subscribe, 購読）する必要があることがあります。データは、サードパーティ製のライブラリから来るかもしれませんし組み込みのブラウザ API から来るかもしれません。このデータは React の知らないところで変わる可能性があるため、コンポーネントが手動でサブスクライブする必要があります。これは例えば以下のように、よくエフェクトを使って行われます。\n\n```js {2-17}\nfunction useOnlineStatus() {\n  // Not ideal: Manual store subscription in an Effect\n  const [isOnline, setIsOnline] = useState(true);\n  useEffect(() => {\n    function updateState() {\n      setIsOnline(navigator.onLine);\n    }\n\n    updateState();\n\n    window.addEventListener('online', updateState);\n    window.addEventListener('offline', updateState);\n    return () => {\n      window.removeEventListener('online', updateState);\n      window.removeEventListener('offline', updateState);\n    };\n  }, []);\n  return isOnline;\n}\n\nfunction ChatIndicator() {\n  const isOnline = useOnlineStatus();\n  // ...\n}\n```\n\nここでは、コンポーネントが外部データストア（この場合は、ブラウザの `navigator.onLine` API）にサブスクライブしています。この API はサーバ上には存在しない（サーバレンダリング用の初期 HTML には使用できない）ため、最初 state は `true` にセットされます。ブラウザ内のデータストアの値が変更されるたびに、コンポーネントは自身の state を更新します。\n\nエフェクトを使うことも一般的ですが、React には外部ストアへサブスクライブする際に推奨される、専用のフックが用意されています。エフェクトを削除し、[`useSyncExternalStore`](/reference/react/useSyncExternalStore) の呼び出しに置き換えてください。\n\n```js {11-16}\nfunction subscribe(callback) {\n  window.addEventListener('online', callback);\n  window.addEventListener('offline', callback);\n  return () => {\n    window.removeEventListener('online', callback);\n    window.removeEventListener('offline', callback);\n  };\n}\n\nfunction useOnlineStatus() {\n  // ✅ Good: Subscribing to an external store with a built-in Hook\n  return useSyncExternalStore(\n    subscribe, // React won't resubscribe for as long as you pass the same function\n    () => navigator.onLine, // How to get the value on the client\n    () => true // How to get the value on the server\n  );\n}\n\nfunction ChatIndicator() {\n  const isOnline = useOnlineStatus();\n  // ...\n}\n```\n\nこのアプローチにより、エフェクトを使って可変データを React の state に手動で同期させるよりも、エラーが発生しにくなります。通常、上記のような `useOnlineStatus()` のようなカスタムフックを作成して、個々のコンポーネントでこのコードを繰り返さなくて済むようにします。[React コンポーネントから外部ストアにサブスクライブする方法について詳しく読む](/reference/react/useSyncExternalStore)\n\n### データのフェッチ {/*fetching-data*/}\n\n多くのアプリでは、エフェクトを使ってデータのフェッチを開始します。データフェッチ用のエフェクトはよくこのように書かれます。\n\n```js {5-10}\nfunction SearchResults({ query }) {\n  const [results, setResults] = useState([]);\n  const [page, setPage] = useState(1);\n\n  useEffect(() => {\n    // 🔴 Avoid: Fetching without cleanup logic\n    fetchResults(query, page).then(json => {\n      setResults(json);\n    });\n  }, [query, page]);\n\n  function handleNextPageClick() {\n    setPage(page + 1);\n  }\n  // ...\n}\n```\n\nこのフェッチは、イベントハンドラに移動する必要は*ありません*。\n\nこれは、イベントハンドラにロジックを入れる必要があったここまでの例と矛盾しているように思えるかもしれません。しかし、*タイピングというイベント*がデータのフェッチを行う理由だというわけではないことに留意しましょう。検索フィールドは URL から事前入力されることがよくありますし、ユーザは入力フィールドに触れずに戻る・進むといったナビゲーションを行うこともあります。\n\nこの `page` や `query` がどこから来たのかは問題ではありません。このコンポーネントが表示されている間は `results` を、現在の `page` と `query` に対応するネットワークからのデータに[同期させる](/learn/synchronizing-with-effects)必要があるのです。だからこれはエフェクトであるべきだということです。\n\nただし、上記のコードにはバグがあります。例えば、素早く `\"hello\"` と入力すると、`query` は `\"h\"`、`\"he\"`、`\"hel\"`、`\"hell\"`、`\"hello\"` の順に変わります。これにより、別々のフェッチが開始されますが、レスポンスがどの順序で届くかについては何の保証もありません。例えば、`\"hell\"` のレスポンスが `\"hello\"` のレスポンスの*後*に届くかもしれません。それが最後に `setResults()` を呼び出すと、間違った検索結果が表示されることになります。これは [\"競合状態 (race condition)\"](https://en.wikipedia.org/wiki/Race_condition) と呼ばれるもので、2 つの異なるリクエストが予想外の順序で「競争」してしまうという現象です。\n\n**競合状態を修正するには、[クリーンアップ関数を追加して](/learn/synchronizing-with-effects#fetching-data)古いレスポンスを無視する必要があります**。\n\n```js {5,7,9,11-13}\nfunction SearchResults({ query }) {\n  const [results, setResults] = useState([]);\n  const [page, setPage] = useState(1);\n  useEffect(() => {\n    let ignore = false;\n    fetchResults(query, page).then(json => {\n      if (!ignore) {\n        setResults(json);\n      }\n    });\n    return () => {\n      ignore = true;\n    };\n  }, [query, page]);\n\n  function handleNextPageClick() {\n    setPage(page + 1);\n  }\n  // ...\n}\n```\n\nこれにより、エフェクトがデータを取得する際に、最後にリクエストしたもの以外のすべてのレスポンスが無視されます。\n\n競合状態への対処がデータフェッチにまつわる唯一の問題というわけでもありません。レスポンスのキャッシュ（ユーザが「戻る」をクリックしたときに前の画面を即座に表示できるようにする）、サーバ上でのデータフェッチ（サーバレンダリングされた初期 HTML にフェッチされたコンテンツが含まれるようにする）、ネットワークのウォーターフォールの回避（子が親を待たずにデータを取得できるようにする）などが考慮すべき点です。\n\n**これらは React だけでなく、あらゆる UI ライブラリで問題となるものです。これらは一筋縄では解決できないため、現代の[フレームワーク](/learn/creating-a-react-app#full-stack-frameworks)では、エフェクトでデータを取得するよりも効率的な組み込みデータ取得メカニズムが提供されています**。\n\nフレームワークを使用しない（し独自に構築もしたくない）がエフェクトからのデータフェッチをより使いやすくしたい、という場合、以下の例のように、データフェッチのロジックをカスタムフックに抽出することを検討してください。\n\n```js {4}\nfunction SearchResults({ query }) {\n  const [page, setPage] = useState(1);\n  const params = new URLSearchParams({ query, page });\n  const results = useData(`/api/search?${params}`);\n\n  function handleNextPageClick() {\n    setPage(page + 1);\n  }\n  // ...\n}\n\nfunction useData(url) {\n  const [data, setData] = useState(null);\n  useEffect(() => {\n    let ignore = false;\n    fetch(url)\n      .then(response => response.json())\n      .then(json => {\n        if (!ignore) {\n          setData(json);\n        }\n      });\n    return () => {\n      ignore = true;\n    };\n  }, [url]);\n  return data;\n}\n```\n\nまた、エラー処理やコンテンツの読み込み状況を追跡するためのロジックを追加することも検討してください。このようなフックを自分で構築するか、React エコシステムで既に利用可能な多くのソリューションのいずれかを使用することができます。**これだけではフレームワークの組み込みデータフェッチメカニズムほど効率的にはなりませんが、データ取得ロジックをカスタムフックに移動しておけば、後で効率的なデータフェッチ戦略を採用することもより簡単になるでしょう**。\n\n一般的に、エフェクトを書く必要がある場合は常に、上記の `useData` のように、より宣言的かつ目的に応じた API を持つカスタムフックに機能の一部を抽出できないか、目を光らせるようにしてください。コンポーネント内の生の `useEffect` の呼び出しが少なければ少ないほど、アプリケーションのメンテナンスは容易になります。\n\n<Recap>\n\n- レンダー中に計算できるものであれば、エフェクトは必要ない。\n- 重たい計算をキャッシュするには、`useEffect` の代わりに `useMemo` を追加する。\n- コンポーネントツリー全体の state をリセットするには、異なる `key` を渡す。\n- prop の変更に応じて一部の state をリセットする場合、レンダー中に行う。\n- コンポーネントが*表示*されたために実行されるコードはエフェクトに、それ以外はイベントハンドラに入れる。\n- 複数のコンポーネントの state を更新する必要がある場合、単一のイベントで行うことが望ましい。\n- 異なるコンポーネントの state 変数を同期しようと思った際は、常に state のリフトアップを検討する。\n- エフェクトでのデータフェッチは可能だが、競合状態を回避するためにクリーンアップを実装する必要がある。\n\n</Recap>\n\n<Challenges>\n\n#### エフェクトなしでデータを変換 {/*transform-data-without-effects*/}\n\n以下の `TodoList` は、todo のリストを表示します。\"Show only active todos\" チェックボックスにチェックが入っている間は、完了済みの todo はリストに表示されません。todo の表示状態には関係なく、フッターには未完了の todo の数が表示されます。\n\nこのコンポーネントから、不要な state とエフェクトをすべて削除して簡略化してください。\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [12, 16, 20]}}\nimport { useState, useEffect } from 'react';\nimport { initialTodos, createTodo } from './todos.js';\n\nexport default function TodoList() {\n  const [todos, setTodos] = useState(initialTodos);\n  const [showActive, setShowActive] = useState(false);\n  const [activeTodos, setActiveTodos] = useState([]);\n  const [visibleTodos, setVisibleTodos] = useState([]);\n  const [footer, setFooter] = useState(null);\n\n  useEffect(() => {\n    setActiveTodos(todos.filter(todo => !todo.completed));\n  }, [todos]);\n\n  useEffect(() => {\n    setVisibleTodos(showActive ? activeTodos : todos);\n  }, [showActive, todos, activeTodos]);\n\n  useEffect(() => {\n    setFooter(\n      <footer>\n        {activeTodos.length} todos left\n      </footer>\n    );\n  }, [activeTodos]);\n\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={showActive}\n          onChange={e => setShowActive(e.target.checked)}\n        />\n        Show only active todos\n      </label>\n      <NewTodo onAdd={newTodo => setTodos([...todos, newTodo])} />\n      <ul>\n        {visibleTodos.map(todo => (\n          <li key={todo.id}>\n            {todo.completed ? <s>{todo.text}</s> : todo.text}\n          </li>\n        ))}\n      </ul>\n      {footer}\n    </>\n  );\n}\n\nfunction NewTodo({ onAdd }) {\n  const [text, setText] = useState('');\n\n  function handleAddClick() {\n    setText('');\n    onAdd(createTodo(text));\n  }\n\n  return (\n    <>\n      <input value={text} onChange={e => setText(e.target.value)} />\n      <button onClick={handleAddClick}>\n        Add\n      </button>\n    </>\n  );\n}\n```\n\n```js src/todos.js\nlet nextId = 0;\n\nexport function createTodo(text, completed = false) {\n  return {\n    id: nextId++,\n    text,\n    completed\n  };\n}\n\nexport const initialTodos = [\n  createTodo('Get apples', true),\n  createTodo('Get oranges', true),\n  createTodo('Get carrots'),\n];\n```\n\n```css\nlabel { display: block; }\ninput { margin-top: 10px; }\n```\n\n</Sandpack>\n\n<Hint>\n\nレンダー中に何かを計算できる場合、それを更新する state やエフェクトは必要ありません。\n\n</Hint>\n\n<Solution>\n\nこの例では、本質的な state は、`todos` リストと、チェックボックスにチェックが入っているかどうかを示す `showActive` という state 変数の 2 つだけです。他のすべての state 変数は[冗長](/learn/choosing-the-state-structure#avoid-redundant-state)であり、レンダー中に計算することができます。さらに `footer` も、隣にある JSX に直接移動させることができます。\n\n修正後の結果は以下のようになります。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { initialTodos, createTodo } from './todos.js';\n\nexport default function TodoList() {\n  const [todos, setTodos] = useState(initialTodos);\n  const [showActive, setShowActive] = useState(false);\n  const activeTodos = todos.filter(todo => !todo.completed);\n  const visibleTodos = showActive ? activeTodos : todos;\n\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={showActive}\n          onChange={e => setShowActive(e.target.checked)}\n        />\n        Show only active todos\n      </label>\n      <NewTodo onAdd={newTodo => setTodos([...todos, newTodo])} />\n      <ul>\n        {visibleTodos.map(todo => (\n          <li key={todo.id}>\n            {todo.completed ? <s>{todo.text}</s> : todo.text}\n          </li>\n        ))}\n      </ul>\n      <footer>\n        {activeTodos.length} todos left\n      </footer>\n    </>\n  );\n}\n\nfunction NewTodo({ onAdd }) {\n  const [text, setText] = useState('');\n\n  function handleAddClick() {\n    setText('');\n    onAdd(createTodo(text));\n  }\n\n  return (\n    <>\n      <input value={text} onChange={e => setText(e.target.value)} />\n      <button onClick={handleAddClick}>\n        Add\n      </button>\n    </>\n  );\n}\n```\n\n```js src/todos.js\nlet nextId = 0;\n\nexport function createTodo(text, completed = false) {\n  return {\n    id: nextId++,\n    text,\n    completed\n  };\n}\n\nexport const initialTodos = [\n  createTodo('Get apples', true),\n  createTodo('Get oranges', true),\n  createTodo('Get carrots'),\n];\n```\n\n```css\nlabel { display: block; }\ninput { margin-top: 10px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### エフェクトなしで計算結果をキャッシュ {/*cache-a-calculation-without-effects*/}\n\nこの例では、todo リストのフィルタ処理が `getVisibleTodos()` という独立した関数に抽出されています。この関数には、呼び出しがあったときに分かるよう `console.log()` が含まれています。\"Show only active todos\" を切り替えると、`getVisibleTodos()` が再実行されることに気付くでしょう。何を表示したいかを切り替えることで表示される todo リストは変わるので、これは期待通りの動作です。\n\nあなたの課題は、`TodoList` コンポーネント内にある `visibleTodos` リストを再計算しているエフェクトを削除することです。ただし、入力フィールドへのタイプでは `getVisibleTodos()` が*再実行されない*ようにする（ログが表示されないようにする）必要があります。\n\n<Hint>\n\n1 つの解決策は、表示される todo 一覧をキャッシュするために `useMemo` を追加することです。また、気づきづらい解法がもう 1 つあります。\n\n</Hint>\n\n<Sandpack>\n\n```js {expectedErrors: {'react-compiler': [11]}}\nimport { useState, useEffect } from 'react';\nimport { initialTodos, createTodo, getVisibleTodos } from './todos.js';\n\nexport default function TodoList() {\n  const [todos, setTodos] = useState(initialTodos);\n  const [showActive, setShowActive] = useState(false);\n  const [text, setText] = useState('');\n  const [visibleTodos, setVisibleTodos] = useState([]);\n\n  useEffect(() => {\n    setVisibleTodos(getVisibleTodos(todos, showActive));\n  }, [todos, showActive]);\n\n  function handleAddClick() {\n    setText('');\n    setTodos([...todos, createTodo(text)]);\n  }\n\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={showActive}\n          onChange={e => setShowActive(e.target.checked)}\n        />\n        Show only active todos\n      </label>\n      <input value={text} onChange={e => setText(e.target.value)} />\n      <button onClick={handleAddClick}>\n        Add\n      </button>\n      <ul>\n        {visibleTodos.map(todo => (\n          <li key={todo.id}>\n            {todo.completed ? <s>{todo.text}</s> : todo.text}\n          </li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n```js src/todos.js\nlet nextId = 0;\nlet calls = 0;\n\nexport function getVisibleTodos(todos, showActive) {\n  console.log(`getVisibleTodos() was called ${++calls} times`);\n  const activeTodos = todos.filter(todo => !todo.completed);\n  const visibleTodos = showActive ? activeTodos : todos;\n  return visibleTodos;\n}\n\nexport function createTodo(text, completed = false) {\n  return {\n    id: nextId++,\n    text,\n    completed\n  };\n}\n\nexport const initialTodos = [\n  createTodo('Get apples', true),\n  createTodo('Get oranges', true),\n  createTodo('Get carrots'),\n];\n```\n\n```css\nlabel { display: block; }\ninput { margin-top: 10px; }\n```\n\n</Sandpack>\n\n<Solution>\n\nstate 変数とエフェクトを削除し、代わりに `getVisibleTodos()` の呼び出し結果をキャッシュするための `useMemo` を追加します。\n\n<Sandpack>\n\n```js\nimport { useState, useMemo } from 'react';\nimport { initialTodos, createTodo, getVisibleTodos } from './todos.js';\n\nexport default function TodoList() {\n  const [todos, setTodos] = useState(initialTodos);\n  const [showActive, setShowActive] = useState(false);\n  const [text, setText] = useState('');\n  const visibleTodos = useMemo(\n    () => getVisibleTodos(todos, showActive),\n    [todos, showActive]\n  );\n\n  function handleAddClick() {\n    setText('');\n    setTodos([...todos, createTodo(text)]);\n  }\n\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={showActive}\n          onChange={e => setShowActive(e.target.checked)}\n        />\n        Show only active todos\n      </label>\n      <input value={text} onChange={e => setText(e.target.value)} />\n      <button onClick={handleAddClick}>\n        Add\n      </button>\n      <ul>\n        {visibleTodos.map(todo => (\n          <li key={todo.id}>\n            {todo.completed ? <s>{todo.text}</s> : todo.text}\n          </li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n```js src/todos.js\nlet nextId = 0;\nlet calls = 0;\n\nexport function getVisibleTodos(todos, showActive) {\n  console.log(`getVisibleTodos() was called ${++calls} times`);\n  const activeTodos = todos.filter(todo => !todo.completed);\n  const visibleTodos = showActive ? activeTodos : todos;\n  return visibleTodos;\n}\n\nexport function createTodo(text, completed = false) {\n  return {\n    id: nextId++,\n    text,\n    completed\n  };\n}\n\nexport const initialTodos = [\n  createTodo('Get apples', true),\n  createTodo('Get oranges', true),\n  createTodo('Get carrots'),\n];\n```\n\n```css\nlabel { display: block; }\ninput { margin-top: 10px; }\n```\n\n</Sandpack>\n\nこの変更により、`getVisibleTodos()` は `todos` または `showActive` が変更された場合にのみ呼び出されます。入力フィールドへのタイプでは `text` の state 変数のみが変更されるため、`getVisibleTodos()` の呼び出しはトリガされません。\n\nまた、`useMemo` を使わない別の解決策もあります。`text` state 変数は todo リストに影響を与えないので、`NewTodo` フォームを別コンポーネントに抽出し、`text` state 変数をその中に移動することができます。\n\n<Sandpack>\n\n```js\nimport { useState, useMemo } from 'react';\nimport { initialTodos, createTodo, getVisibleTodos } from './todos.js';\n\nexport default function TodoList() {\n  const [todos, setTodos] = useState(initialTodos);\n  const [showActive, setShowActive] = useState(false);\n  const visibleTodos = getVisibleTodos(todos, showActive);\n\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={showActive}\n          onChange={e => setShowActive(e.target.checked)}\n        />\n        Show only active todos\n      </label>\n      <NewTodo onAdd={newTodo => setTodos([...todos, newTodo])} />\n      <ul>\n        {visibleTodos.map(todo => (\n          <li key={todo.id}>\n            {todo.completed ? <s>{todo.text}</s> : todo.text}\n          </li>\n        ))}\n      </ul>\n    </>\n  );\n}\n\nfunction NewTodo({ onAdd }) {\n  const [text, setText] = useState('');\n\n  function handleAddClick() {\n    setText('');\n    onAdd(createTodo(text));\n  }\n\n  return (\n    <>\n      <input value={text} onChange={e => setText(e.target.value)} />\n      <button onClick={handleAddClick}>\n        Add\n      </button>\n    </>\n  );\n}\n```\n\n```js src/todos.js\nlet nextId = 0;\nlet calls = 0;\n\nexport function getVisibleTodos(todos, showActive) {\n  console.log(`getVisibleTodos() was called ${++calls} times`);\n  const activeTodos = todos.filter(todo => !todo.completed);\n  const visibleTodos = showActive ? activeTodos : todos;\n  return visibleTodos;\n}\n\nexport function createTodo(text, completed = false) {\n  return {\n    id: nextId++,\n    text,\n    completed\n  };\n}\n\nexport const initialTodos = [\n  createTodo('Get apples', true),\n  createTodo('Get oranges', true),\n  createTodo('Get carrots'),\n];\n```\n\n```css\nlabel { display: block; }\ninput { margin-top: 10px; }\n```\n\n</Sandpack>\n\nこのアプローチでも要件を満たしています。入力フィールドにタイプすると、`text` state 変数のみが更新されます。`text` state 変数は子コンポーネントである `NewTodo` コンポーネント内にあるため、親コンポーネントの `TodoList` は再レンダーされません。したがってタイプが起きても `getVisibleTodos()` は呼び出されません。（`TodoList` が別の理由で再レンダーされる場合は呼び出されます）。\n\n</Solution>\n\n#### エフェクトなしで state をリセット {/*reset-state-without-effects*/}\n\nこの `EditContact` コンポーネントは、`{ id, name, email }` という形をした連絡先オブジェクトを `savedContact` という props として受け取ります。名前とメールアドレスの入力フィールドを編集してみてください。Save を押すと、フォームの上にある連絡先のボタンが編集後の名前に更新されます。Reset を押すと、フォーム内の編集中の値は破棄されます。まずこの UI を操作して感覚をつかんでください。\n\n上部のボタンで連絡先を選択すると、フォームがその連絡先に対応する詳細データにリセットされます。これは `EditContact.js` 内にあるエフェクトで行われています。このエフェクトを削除してください。`savedContact.id` が変更されたときにフォームをリセットする別の方法を見つけましょう。\n\n<Sandpack>\n\n```js src/App.js hidden\nimport { useState } from 'react';\nimport ContactList from './ContactList.js';\nimport EditContact from './EditContact.js';\n\nexport default function ContactManager() {\n  const [\n    contacts,\n    setContacts\n  ] = useState(initialContacts);\n  const [\n    selectedId,\n    setSelectedId\n  ] = useState(0);\n  const selectedContact = contacts.find(c =>\n    c.id === selectedId\n  );\n\n  function handleSave(updatedData) {\n    const nextContacts = contacts.map(c => {\n      if (c.id === updatedData.id) {\n        return updatedData;\n      } else {\n        return c;\n      }\n    });\n    setContacts(nextContacts);\n  }\n\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedId={selectedId}\n        onSelect={id => setSelectedId(id)}\n      />\n      <hr />\n      <EditContact\n        savedContact={selectedContact}\n        onSave={handleSave}\n      />\n    </div>\n  )\n}\n\nconst initialContacts = [\n  { id: 0, name: 'Taylor', email: 'taylor@mail.com' },\n  { id: 1, name: 'Alice', email: 'alice@mail.com' },\n  { id: 2, name: 'Bob', email: 'bob@mail.com' }\n];\n```\n\n```js src/ContactList.js hidden\nexport default function ContactList({\n  contacts,\n  selectedId,\n  onSelect\n}) {\n  return (\n    <section>\n      <ul>\n        {contacts.map(contact =>\n          <li key={contact.id}>\n            <button onClick={() => {\n              onSelect(contact.id);\n            }}>\n              {contact.id === selectedId ?\n                <b>{contact.name}</b> :\n                contact.name\n              }\n            </button>\n          </li>\n        )}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js {expectedErrors: {'react-compiler': [8, 9]}} src/EditContact.js active\nimport { useState, useEffect } from 'react';\n\nexport default function EditContact({ savedContact, onSave }) {\n  const [name, setName] = useState(savedContact.name);\n  const [email, setEmail] = useState(savedContact.email);\n\n  useEffect(() => {\n    setName(savedContact.name);\n    setEmail(savedContact.email);\n  }, [savedContact]);\n\n  return (\n    <section>\n      <label>\n        Name:{' '}\n        <input\n          type=\"text\"\n          value={name}\n          onChange={e => setName(e.target.value)}\n        />\n      </label>\n      <label>\n        Email:{' '}\n        <input\n          type=\"email\"\n          value={email}\n          onChange={e => setEmail(e.target.value)}\n        />\n      </label>\n      <button onClick={() => {\n        const updatedData = {\n          id: savedContact.id,\n          name: name,\n          email: email\n        };\n        onSave(updatedData);\n      }}>\n        Save\n      </button>\n      <button onClick={() => {\n        setName(savedContact.name);\n        setEmail(savedContact.email);\n      }}>\n        Reset\n      </button>\n    </section>\n  );\n}\n```\n\n```css\nul, li {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli { display: inline-block; }\nli button {\n  padding: 10px;\n}\nlabel {\n  display: block;\n  margin: 10px 0;\n}\nbutton {\n  margin-right: 10px;\n  margin-bottom: 10px;\n}\n```\n\n</Sandpack>\n\n<Hint>\n\n`savedContact.id` が異なる場合は `EditContact` フォームは概念的には*別の連絡先のフォーム*なので、state を保持してはいけない、と React に伝える方法があるといいですね。そのような方法を覚えていませんか？\n\n</Hint>\n\n<Solution>\n\n`EditContact` コンポーネントを 2 つに分割します。フォームの state をすべて内部の `EditForm` コンポーネントに移動します。外部の `EditContact` コンポーネントをエクスポートし、`savedContact.id` を内部の `EditForm` コンポーネントに `key` として渡します。その結果、内部の `EditForm` コンポーネントは、異なる連絡先が選択されるたびにフォームの state をすべてリセットし、DOM を再作成します。\n\n<Sandpack>\n\n```js src/App.js hidden\nimport { useState } from 'react';\nimport ContactList from './ContactList.js';\nimport EditContact from './EditContact.js';\n\nexport default function ContactManager() {\n  const [\n    contacts,\n    setContacts\n  ] = useState(initialContacts);\n  const [\n    selectedId,\n    setSelectedId\n  ] = useState(0);\n  const selectedContact = contacts.find(c =>\n    c.id === selectedId\n  );\n\n  function handleSave(updatedData) {\n    const nextContacts = contacts.map(c => {\n      if (c.id === updatedData.id) {\n        return updatedData;\n      } else {\n        return c;\n      }\n    });\n    setContacts(nextContacts);\n  }\n\n  return (\n    <div>\n      <ContactList\n        contacts={contacts}\n        selectedId={selectedId}\n        onSelect={id => setSelectedId(id)}\n      />\n      <hr />\n      <EditContact\n        savedContact={selectedContact}\n        onSave={handleSave}\n      />\n    </div>\n  )\n}\n\nconst initialContacts = [\n  { id: 0, name: 'Taylor', email: 'taylor@mail.com' },\n  { id: 1, name: 'Alice', email: 'alice@mail.com' },\n  { id: 2, name: 'Bob', email: 'bob@mail.com' }\n];\n```\n\n```js src/ContactList.js hidden\nexport default function ContactList({\n  contacts,\n  selectedId,\n  onSelect\n}) {\n  return (\n    <section>\n      <ul>\n        {contacts.map(contact =>\n          <li key={contact.id}>\n            <button onClick={() => {\n              onSelect(contact.id);\n            }}>\n              {contact.id === selectedId ?\n                <b>{contact.name}</b> :\n                contact.name\n              }\n            </button>\n          </li>\n        )}\n      </ul>\n    </section>\n  );\n}\n```\n\n```js src/EditContact.js active\nimport { useState } from 'react';\n\nexport default function EditContact(props) {\n  return (\n    <EditForm\n      {...props}\n      key={props.savedContact.id}\n    />\n  );\n}\n\nfunction EditForm({ savedContact, onSave }) {\n  const [name, setName] = useState(savedContact.name);\n  const [email, setEmail] = useState(savedContact.email);\n\n  return (\n    <section>\n      <label>\n        Name:{' '}\n        <input\n          type=\"text\"\n          value={name}\n          onChange={e => setName(e.target.value)}\n        />\n      </label>\n      <label>\n        Email:{' '}\n        <input\n          type=\"email\"\n          value={email}\n          onChange={e => setEmail(e.target.value)}\n        />\n      </label>\n      <button onClick={() => {\n        const updatedData = {\n          id: savedContact.id,\n          name: name,\n          email: email\n        };\n        onSave(updatedData);\n      }}>\n        Save\n      </button>\n      <button onClick={() => {\n        setName(savedContact.name);\n        setEmail(savedContact.email);\n      }}>\n        Reset\n      </button>\n    </section>\n  );\n}\n```\n\n```css\nul, li {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\nli { display: inline-block; }\nli button {\n  padding: 10px;\n}\nlabel {\n  display: block;\n  margin: 10px 0;\n}\nbutton {\n  margin-right: 10px;\n  margin-bottom: 10px;\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n#### エフェクトなしでフォームを送信 {/*submit-a-form-without-effects*/}\n\nこの `Form` コンポーネントでは友達にメッセージを送れます。フォームを送信すると、`showForm` state 変数が `false` にセットされます。これにより、`sendMessage(message)` を呼び出すエフェクトがトリガされ、メッセージが送信されます（コンソールで確認できます）。メッセージが送信されると、\"Thank you\" 画面が表示され、\"Open chat\" ボタンでフォームに戻ることができます。\n\nさて、あなたのアプリのユーザはあまりにも多くのメッセージを送信しています。チャットを少しやり辛くするために、フォームではなく*先に* \"Thank you\" 画面を表示することにしました。`showForm` state 変数を `true` ではなく `false` で初期化するように変更してください。そうするとすぐに、空のメッセージが送信された、とコンソールに表示されます。このロジックの何かが間違っています！\n\nこの問題の根本原因は何でしょうか？ どのように修正すればよいでしょうか？\n\n<Hint>\n\nユーザが \"Thank you\" 画面を*見たから*メッセージが送信されるべきなのでしょうか？ 逆ではありませんか？\n\n</Hint>\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function Form() {\n  const [showForm, setShowForm] = useState(true);\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    if (!showForm) {\n      sendMessage(message);\n    }\n  }, [showForm, message]);\n\n  function handleSubmit(e) {\n    e.preventDefault();\n    setShowForm(false);\n  }\n\n  if (!showForm) {\n    return (\n      <>\n        <h1>Thanks for using our services!</h1>\n        <button onClick={() => {\n          setMessage('');\n          setShowForm(true);\n        }}>\n          Open chat\n        </button>\n      </>\n    );\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <textarea\n        placeholder=\"Message\"\n        value={message}\n        onChange={e => setMessage(e.target.value)}\n      />\n      <button type=\"submit\" disabled={message === ''}>\n        Send\n      </button>\n    </form>\n  );\n}\n\nfunction sendMessage(message) {\n  console.log('Sending message: ' + message);\n}\n```\n\n```css\nlabel, textarea { margin-bottom: 10px; display: block; }\n```\n\n</Sandpack>\n\n<Solution>\n\n`showForm` state 変数が、フォームと \"Thank you\" 画面のどちらを表示するのか決定しています。ですが \"Thank you\" 画面が*表示されたから*メッセージを送信する、のではありませんね。ユーザが*フォームを送信したから*メッセージを送信したいのです。混乱の元となっているエフェクトを削除し、`sendMessage` の呼び出しを `handleSubmit` イベントハンドラの中に移動してください。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function Form() {\n  const [showForm, setShowForm] = useState(true);\n  const [message, setMessage] = useState('');\n\n  function handleSubmit(e) {\n    e.preventDefault();\n    setShowForm(false);\n    sendMessage(message);\n  }\n\n  if (!showForm) {\n    return (\n      <>\n        <h1>Thanks for using our services!</h1>\n        <button onClick={() => {\n          setMessage('');\n          setShowForm(true);\n        }}>\n          Open chat\n        </button>\n      </>\n    );\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <textarea\n        placeholder=\"Message\"\n        value={message}\n        onChange={e => setMessage(e.target.value)}\n      />\n      <button type=\"submit\" disabled={message === ''}>\n        Send\n      </button>\n    </form>\n  );\n}\n\nfunction sendMessage(message) {\n  console.log('Sending message: ' + message);\n}\n```\n\n```css\nlabel, textarea { margin-bottom: 10px; display: block; }\n```\n\n</Sandpack>\n\nこのバージョンでは、*フォームの送信*（これはイベントです）だけがメッセージを送信することに注意しましょう。`showForm` が最初に `true` に設定されているか `false` に設定されているかに関係なく、同じようにうまく動作します。（`false` に設定して、コンソールに余分なメッセージが表示されないことを確認しましょう。）\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/learn/your-first-component.md",
    "content": "---\ntitle: 初めてのコンポーネント\n---\n\n<Intro>\n\n*コンポーネント*は、React における最重要コンセプトのひとつです。皆さんがユーザインターフェース (UI) を構築するときの基盤となるものですので、React の旅路はコンポーネントから始めていくことにしましょう！\n\n</Intro>\n\n<YouWillLearn>\n\n* コンポーネントとは何か\n* React アプリでコンポーネントが果たす役割\n* 初めてのコンポーネントの書き方\n\n</YouWillLearn>\n\n## コンポーネント：UI の構成部品 {/*components-ui-building-blocks*/}\n\nWeb の世界では、HTML で `<h1>` や `<li>` といった組み込みタグを使い、リッチで構造化されたドキュメントを作成することができます。\n\n```html\n<article>\n  <h1>My First Component</h1>\n  <ol>\n    <li>Components: UI Building Blocks</li>\n    <li>Defining a Component</li>\n    <li>Using a Component</li>\n  </ol>\n</article>\n```\n\nこのマークアップは、記事自身 (`<article>`)、見出し (`<h1>`)、そして番号付きリスト (`<ol>`) による目次（一部省略しています）を表現しています。我々がウェブで目にするサイドバー、アバター、モーダル、ドロップダウンといったあらゆる UI パーツの裏側では、このようなマークアップが、スタイルのための CSS やユーザ対話のための JavaScript と組み合わさりながら働いています。\n\nReact では、あなたのマークアップと CSS と JavaScript を、独自の \"コンポーネント\" と呼ばれる、**アプリのための再利用可能な UI 要素**にまとめることができます。上記にある目次のためのコードを、`<TableOfContents />` と呼ばれるコンポーネントにすることができるのです。その裏では、やはり `<articles>` や `<h1>` といった同じ HTML タグを使っています。\n\nHTML タグを使う時と同様にして、コンポーネントを組み合わせたり、並び替えたり、ネストしたりして、ページ全体をデザインすることができます。例えばあなたが今読んでいるこのページも、以下のような React コンポーネントから作られています。\n\n```js\n<PageLayout>\n  <NavigationHeader>\n    <SearchBar />\n    <Link to=\"/docs\">Docs</Link>\n  </NavigationHeader>\n  <Sidebar />\n  <PageContent>\n    <TableOfContents />\n    <DocumentationText />\n  </PageContent>\n</PageLayout>\n```\n\nプロジェクトが成長するとともに、設計・デザインのいろいろな部分を、既に書いたコンポーネントを再利用することで構築できるようになり、開発速度がアップすることに気付くでしょう。上記のような目次を、`<TableOfContents />` を書くことでどのページにでも加えることができるのです！ [Chakra UI](https://chakra-ui.com/) や [Material UI](https://material-ui.com/) のような、React オープンソースコミュニティーで共有されている何千ものコンポーネントを使い、プロジェクトを一気にスタートさせることも可能です。\n\n## コンポーネントの定義 {/*defining-a-component*/}\n\n伝統的なウェブページの作成方法とは、ウェブ開発者が先にコンテンツをマークアップしてから、ユーザとのインタラクションを加えるために JavaScript をちょっと添える、というものでした。これはウェブにとってインタラクションが「あると嬉しい」レベルのものだった時代にはうまく機能していました。しかし今や、インタラクションはほぼすべてのサイトとあらゆるアプリで必要とされるものです。React は同じテクノロジを使っていますが、インタラクティビティ・ファーストになっています。すなわち **React コンポーネントとは、マークアップを添えることができる JavaScript 関数**です。コンポーネントは以下のような見た目をしています（以下のコードは編集できます）。\n\n<Sandpack>\n\n```js\nexport default function Profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/MK3eW3Am.jpg\"\n      alt=\"Katherine Johnson\"\n    />\n  )\n}\n```\n\n```css\nimg { height: 200px; }\n```\n\n</Sandpack>\n\nコンポーネントは以下のようにして作成します。\n\n### Step 1: コンポーネントをエクスポートする {/*step-1-export-the-component*/}\n\n頭にある `export default` は[標準的な JavaScript の構文](https://developer.mozilla.org/docs/web/javascript/reference/statements/export)です（React 特有のものではありません）。これでファイル内のメインの関数をマークし、後で他のファイルからそれをインポートできるようにします。（[コンポーネントのインポートとエクスポート](/learn/importing-and-exporting-components)に詳細があります！）\n\n### Step 2: 関数を定義する {/*step-2-define-the-function*/}\n\n`function Profile() { }` のように書くことで、`Profile` という名前の JavaScript 関数を定義します。\n\n<Pitfall>\n\nReact コンポーネントは普通の JavaScript 関数ですが、**名前は大文字から始める必要があります**。さもないと動作しません！\n\n</Pitfall>\n\n### Step 3: マークアップを加える {/*step-3-add-markup*/}\n\nこのコンポーネントは `src` と `alt` という属性を有する `<img />` タグを返しています。`<img />` はまるで HTML のように書かれていますが、裏では実際には JavaScript です！ この構文は [JSX](/learn/writing-markup-with-jsx) と呼ばれるもので、これによりマークアップを JavaScript 内に埋め込めるようになります。\n\nreturn 文は、以下のように 1 行にまとめて書いても構いません。\n\n```js\nreturn <img src=\"https://i.imgur.com/MK3eW3As.jpg\" alt=\"Katherine Johnson\" />;\n```\n\nしかし return キーワードと同じ行にマークアップ全体が収まらない場合は、括弧で囲んで以下のようにする必要があります。\n\n```js\nreturn (\n  <div>\n    <img src=\"https://i.imgur.com/MK3eW3As.jpg\" alt=\"Katherine Johnson\" />\n  </div>\n);\n```\n\n<Pitfall>\n\n括弧がないと、`return` の後にあるコードはすべて[無視されてしまいます](https://stackoverflow.com/questions/2846283/what-are-the-rules-for-javascripts-automatic-semicolon-insertion-asi)！\n\n</Pitfall>\n\n## コンポーネントを使う {/*using-a-component*/}\n\n`Profile` コンポーネントが定義できたので、それをほかのコンポーネント内にネストさせることができます。例えば `Profile` コンポーネントを複数回使う `Gallery` というコンポーネントをエクスポートできます。\n\n<Sandpack>\n\n```js\nfunction Profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/MK3eW3As.jpg\"\n      alt=\"Katherine Johnson\"\n    />\n  );\n}\n\nexport default function Gallery() {\n  return (\n    <section>\n      <h1>Amazing scientists</h1>\n      <Profile />\n      <Profile />\n      <Profile />\n    </section>\n  );\n}\n```\n\n```css\nimg { margin: 0 10px 10px 0; height: 90px; }\n```\n\n</Sandpack>\n\n### ブラウザに見えるもの {/*what-the-browser-sees*/}\n\n大文字・小文字の違いに気をつけてください。\n\n* `<section>` は小文字なので、React はこれが HTML タグを指しているのだと理解します。\n* `<Profile />` は大文字の `P` で始まっているので、React は `Profile` という名前の独自コンポーネントを使いたいのだと理解します。\n\n`Profile` の中には `<img />` という HTML が更に含まれてます。最終的に、ブラウザに見えるのは以下のようなものです。\n\n```html\n<section>\n  <h1>Amazing scientists</h1>\n  <img src=\"https://i.imgur.com/MK3eW3As.jpg\" alt=\"Katherine Johnson\" />\n  <img src=\"https://i.imgur.com/MK3eW3As.jpg\" alt=\"Katherine Johnson\" />\n  <img src=\"https://i.imgur.com/MK3eW3As.jpg\" alt=\"Katherine Johnson\" />\n</section>\n```\n\n### コンポーネントのネストと整理方法 {/*nesting-and-organizing-components*/}\n\nコンポーネントは普通の JavaScript 関数ですので、同じファイルに複数のコンポーネントを書いておくこともできます。これはコンポーネントが比較的小さい場合や互いに密接に関連している場合には便利です。ファイルの中身が増えてきたら、いつでも `Profile` を別のファイルに移動できます。このやり方についてはすぐ後で、[インポートについてのページ](/learn/importing-and-exporting-components)で学びます。\n\n`Profile` コンポーネントは `Gallery` コンポーネントの中でレンダーされています（しかも何回も）ので、`Gallery` は**親コンポーネント**であり、`Profile` を「子」としてレンダーしている、と言うことができます。これが React の魔法です。一度コンポーネントを定義したら、それをどこにでも何回でも好きなだけ使えるということです。\n\n<Pitfall>\n\nコンポーネントがほかのコンポーネントをレンダーすることはできますが、**コンポーネントの定義をネストさせてはいけません**。\n\n```js {2-5}\nexport default function Gallery() {\n  // 🔴 Never define a component inside another component!\n  function Profile() {\n    // ...\n  }\n  // ...\n}\n```\n\n上記のコードは[とても遅く、バグの原因になります](/learn/preserving-and-resetting-state#different-components-at-the-same-position-reset-state)。代わりに、すべてのコンポーネントをトップレベルで定義するようにしてください。\n\n```js {5-8}\nexport default function Gallery() {\n  // ...\n}\n\n// ✅ Declare components at the top level\nfunction Profile() {\n  // ...\n}\n```\n\n子コンポーネントが親コンポーネントの情報を必要とする場合は、コンポーネント定義をネストさせるのではなく [props を通じて渡す](/learn/passing-props-to-a-component)ようにしてください。\n\n</Pitfall>\n\n<DeepDive>\n\n#### 隅から隅までコンポーネント {/*components-all-the-way-down*/}\n\nReact アプリケーションは \"ルート (root)\" コンポーネントから始まります。通常、これは新しいプロジェクトを開始したときに自動的に作成されます。例えば [CodeSandbox](https://codesandbox.io/) を使う場合や、[Next.js](https://nextjs.org/) のようなフレームワークを使う場合、ルートコンポーネントは `pages/index.js` に定義されています。ここまでの例でも、ルートコンポーネントをエクスポートしていたわけです。\n\nほとんどの React アプリでは隅から隅までコンポーネントが使われます。つまり、ボタンのような再利用可能なところでのみ使うのではなく、サイドバーやリスト、最終的にはページ本体といった大きなパーツのためにも使うのです。コンポーネントは、1 回しか使わないような UI コードやマークアップであっても、それらを整理するための有用な手段です。\n\n[React ベースのフレームワーク](/learn/creating-a-react-app)ではこれを更に 1 歩押し進めます。空の HTML ファイルから始めて JavaScript で React にページ内容の管理を引き継がせるのではなく、あなたの書いた React コンポーネントから HTML ファイル*自体も*自動生成するのです。これにより、JavaScript コードがロードされる前にコンテンツの一部をアプリが表示できるようになります。\n\nその一方で、多くのウェブサイトでは React を [\"対話機能をちょっと添える\"](/learn/add-react-to-an-existing-project#using-react-for-a-part-of-your-existing-page) ためにのみ使っています。そのようなサイトはページ全体のためのルートコンポーネントを 1 つだけ持つのではなく、たくさんのルートコンポーネントを持っています。必要しだいで、React を使う量は多くても少なくても構わないのです！\n\n</DeepDive>\n\n<Recap>\n\nこれで初めての React 体験は完了です。キーポイントをいくつかおさらいしておきましょう。\n\n* React により**アプリのための再利用可能な部品**であるコンポーネントを作成できる。\n* React アプリでは UI のあらゆる部品はコンポーネントである。\n* React のコンポーネントとは普通の JavaScript 関数だが、以下の点が異なる。\n\n  1. 名前は常に大文字で始まる。\n  2. JSX マークアップを return する。\n\n</Recap>\n\n\n\n<Challenges>\n\n#### コンポーネントのエクスポート {/*export-the-component*/}\n\nこのサンドボックスはルートコンポーネントがエクスポートされていないため動作しません：\n\n<Sandpack>\n\n```js\nfunction Profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/lICfvbD.jpg\"\n      alt=\"Aklilu Lemma\"\n    />\n  );\n}\n```\n\n```css\nimg { height: 181px; }\n```\n\n</Sandpack>\n\n答えを見る前に自分で修正してみましょう！\n\n<Solution>\n\n以下のように関数の前に `export default` を付けてください：\n\n<Sandpack>\n\n```js\nexport default function Profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/lICfvbD.jpg\"\n      alt=\"Aklilu Lemma\"\n    />\n  );\n}\n```\n\n```css\nimg { height: 181px; }\n```\n\n</Sandpack>\n\nこれを修正するのに `export` とだけ書いたのではなぜ不十分なのか気になるかもしれません。`export` と `export default` の違いについては[コンポーネントのインポートとエクスポート](/learn/importing-and-exporting-components)で学ぶことができます。\n\n</Solution>\n\n#### return 文を直す {/*fix-the-return-statement*/}\n\nこの `return` 文はどうもおかしいようです。直せますか？\n\n<Hint>\n\nもしかしたら修正途中で \"Unexpected token\" というエラーが出るかもしれません。その場合はセミコロンが閉じ括弧の*後*にあることを確認してください。`return ( )` の中にセミコロンが残っているとエラーになります。\n\n</Hint>\n\n\n<Sandpack>\n\n```js\nexport default function Profile() {\n  return\n    <img src=\"https://i.imgur.com/jA8hHMpm.jpg\" alt=\"Katsuko Saruhashi\" />;\n}\n```\n\n```css\nimg { height: 180px; }\n```\n\n</Sandpack>\n\n<Solution>\n\nこのコンポーネントを修正するには、以下のように return 文を 1 行にします：\n\n<Sandpack>\n\n```js\nexport default function Profile() {\n  return <img src=\"https://i.imgur.com/jA8hHMpm.jpg\" alt=\"Katsuko Saruhashi\" />;\n}\n```\n\n```css\nimg { height: 180px; }\n```\n\n</Sandpack>\n\nまたは、return する JSX マークアップを `return` の直後から括弧で囲んでも構いません：\n\n<Sandpack>\n\n```js\nexport default function Profile() {\n  return (\n    <img \n      src=\"https://i.imgur.com/jA8hHMpm.jpg\" \n      alt=\"Katsuko Saruhashi\" \n    />\n  );\n}\n```\n\n```css\nimg { height: 180px; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### どこが間違い？ {/*spot-the-mistake*/}\n\nこの `Profile` の宣言のしかたや使われ方は何か間違っています。間違いがどこか分かりますか？（React がどのようにしてコンポーネントと普通の HTML タグを区別するのか思い出してみましょう！）\n\n<Sandpack>\n\n```js\nfunction profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/QIrZWGIs.jpg\"\n      alt=\"Alan L. Hart\"\n    />\n  );\n}\n\nexport default function Gallery() {\n  return (\n    <section>\n      <h1>Amazing scientists</h1>\n      <profile />\n      <profile />\n      <profile />\n    </section>\n  );\n}\n```\n\n```css\nimg { margin: 0 10px 10px 0; height: 90px; }\n```\n\n</Sandpack>\n\n<Solution>\n\nReact コンポーネントの名前は大文字で始めなければなりません。\n\n`function profile()` を `function Profile()` に、そして `<profile />` をすべて `<Profile />` に書きかえましょう：\n\n<Sandpack>\n\n```js\nfunction Profile() {\n  return (\n    <img\n      src=\"https://i.imgur.com/QIrZWGIs.jpg\"\n      alt=\"Alan L. Hart\"\n    />\n  );\n}\n\nexport default function Gallery() {\n  return (\n    <section>\n      <h1>Amazing scientists</h1>\n      <Profile />\n      <Profile />\n      <Profile />\n    </section>\n  );\n}\n```\n\n```css\nimg { margin: 0 10px 10px 0; }\n```\n\n</Sandpack>\n\n</Solution>\n\n#### 自分で書いてみる {/*your-own-component*/}\n\nゼロからコンポーネントを書いてください。有効な名前ならどんな名前でも構いませんし、どんなマークアップを返しても構いません。何も思いつかないなら `<h1>Good job!</h1>` と表示する `Congratulations` というコンポーネントを書いてみましょう。エクスポートするのを忘れずに！\n\n<Sandpack>\n\n```js\n// Write your component below!\n\n```\n\n</Sandpack>\n\n<Solution>\n\n<Sandpack>\n\n```js\nexport default function Congratulations() {\n  return (\n    <h1>Good job!</h1>\n  );\n}\n```\n\n</Sandpack>\n\n</Solution>\n\n</Challenges>\n"
  },
  {
    "path": "src/content/reference/dev-tools/react-performance-tracks.md",
    "content": "---\ntitle: React Performance tracks\n---\n\n<Intro>\n\nReact Performance tracks are specialized custom entries that appear on the Performance panel's timeline in your browser developer tools.\n\n</Intro>\n\nThese tracks are designed to provide developers with comprehensive insights into their React application's performance by visualizing React-specific events and metrics alongside other critical data sources such as network requests, JavaScript execution, and event loop activity, all synchronized on a unified timeline within the Performance panel for a complete understanding of application behavior.\n\n<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}>\n  <img className=\"w-full light-image\" src=\"/images/docs/performance-tracks/overview.png\" alt=\"React Performance Tracks\" />\n  <img className=\"w-full dark-image\" src=\"/images/docs/performance-tracks/overview.dark.png\" alt=\"React Performance Tracks\" />\n</div>\n\n<InlineToc />\n\n---\n\n## Usage {/*usage*/}\n\nReact Performance tracks are only available in development and profiling builds of React:\n\n- **Development**: enabled by default.\n- **Profiling**: Only Scheduler tracks are enabled by default. The Components track only lists Components that are in subtrees wrapped with [`<Profiler>`](/reference/react/Profiler). If you have [React Developer Tools extension](/learn/react-developer-tools) enabled, all Components are included in the Components track even if they're not wrapped in `<Profiler>`. Server tracks are not available in profiling builds.\n\nIf enabled, tracks should appear automatically in the traces you record with the Performance panel of browsers that provide [extensibility APIs](https://developer.chrome.com/docs/devtools/performance/extension).\n\n<Pitfall>\n\nThe profiling instrumentation that powers React Performance tracks adds some additional overhead, so it is disabled in production builds by default.\nServer Components and Server Requests tracks are only available in development builds.\n\n</Pitfall>\n\n### Using profiling builds {/*using-profiling-builds*/}\n\nIn addition to production and development builds, React also includes a special profiling build.\nTo use profiling builds, you have to use `react-dom/profiling` instead of `react-dom/client`.\nWe recommend that you alias `react-dom/client` to `react-dom/profiling` at build time via bundler aliases instead of manually updating each `react-dom/client` import.\nYour framework might have built-in support for enabling React's profiling build.\n\n---\n\n## Tracks {/*tracks*/}\n\n### Scheduler {/*scheduler*/}\n\nThe Scheduler is an internal React concept used for managing tasks with different priorities. This track consists of 4 subtracks, each representing work of a specific priority:\n\n- **Blocking** - The synchronous updates, which could've been initiated by user interactions.\n- **Transition** - Non-blocking work that happens in the background, usually initiated via [`startTransition`](/reference/react/startTransition).\n- **Suspense** - Work related to Suspense boundaries, such as displaying fallbacks or revealing content.\n- **Idle** - The lowest priority work that is done when there are no other tasks with higher priority.\n\n<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}>\n  <img className=\"w-full light-image\" src=\"/images/docs/performance-tracks/scheduler.png\" alt=\"Scheduler track\" />\n  <img className=\"w-full dark-image\" src=\"/images/docs/performance-tracks/scheduler.dark.png\" alt=\"Scheduler track\" />\n</div>\n\n#### Renders {/*renders*/}\n\nEvery render pass consists of multiple phases that you can see on a timeline:\n\n- **Update** - this is what caused a new render pass.\n- **Render** - React renders the updated subtree by calling render functions of components. You can see the rendered components subtree on [Components track](#components), which follows the same color scheme.\n- **Commit** - After rendering components, React will submit the changes to the DOM and run layout effects, like [`useLayoutEffect`](/reference/react/useLayoutEffect).\n- **Remaining Effects** - React runs passive effects of a rendered subtree. This usually happens after the paint, and this is when React runs hooks like [`useEffect`](/reference/react/useEffect). One known exception is user interactions, like clicks, or other discrete events. In this scenario, this phase could run before the paint.\n\n<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}>\n  <img className=\"w-full light-image\" src=\"/images/docs/performance-tracks/scheduler-update.png\" alt=\"Scheduler track: updates\" />\n  <img className=\"w-full dark-image\" src=\"/images/docs/performance-tracks/scheduler-update.dark.png\" alt=\"Scheduler track: updates\" />\n</div>\n\n[Learn more about renders and commits](/learn/render-and-commit).\n\n#### Cascading updates {/*cascading-updates*/}\n\nCascading updates is one of the patterns for performance regressions. If an update was scheduled during a render pass, React could discard completed work and start a new pass.\n\nIn development builds, React can show you which Component scheduled a new update. This includes both general updates and cascading ones. You can see the enhanced stack trace by clicking on the \"Cascading update\" entry, which should also display the name of the method that scheduled an update.\n\n<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}>\n  <img className=\"w-full light-image\" src=\"/images/docs/performance-tracks/scheduler-cascading-update.png\" alt=\"Scheduler track: cascading updates\" />\n  <img className=\"w-full dark-image\" src=\"/images/docs/performance-tracks/scheduler-cascading-update.dark.png\" alt=\"Scheduler track: cascading updates\" />\n</div>\n\n[Learn more about Effects](/learn/you-might-not-need-an-effect).\n\n### Components {/*components*/}\n\nThe Components track visualizes the durations of React components. They are displayed as a flamegraph, where each entry represents the duration of the corresponding component render and all its descendant children components.\n\n<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}>\n  <img className=\"w-full light-image\" src=\"/images/docs/performance-tracks/components-render.png\" alt=\"Components track: render durations\" />\n  <img className=\"w-full dark-image\" src=\"/images/docs/performance-tracks/components-render.dark.png\" alt=\"Components track: render durations\" />\n</div>\n\nSimilar to render durations, effect durations are also represented as a flamegraph, but with a different color scheme that aligns with the corresponding phase on the Scheduler track.\n\n<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}>\n  <img className=\"w-full light-image\" src=\"/images/docs/performance-tracks/components-effects.png\" alt=\"Components track: effects durations\" />\n  <img className=\"w-full dark-image\" src=\"/images/docs/performance-tracks/components-effects.dark.png\" alt=\"Components track: effects durations\" />\n</div>\n\n<Note>\n\nUnlike renders, not all effects are shown on the Components track by default.\n\nTo maintain performance and prevent UI clutter, React will only display those effects, which had a duration of 0.05ms or longer, or triggered an update.\n\n</Note>\n\nAdditional events may be displayed during the render and effects phases:\n\n- <span style={{padding: '0.125rem 0.25rem', backgroundColor: '#facc15', color: '#1f1f1fff'}}>Mount</span> - A corresponding subtree of component renders or effects was mounted.\n- <span style={{padding: '0.125rem 0.25rem', backgroundColor: '#facc15', color: '#1f1f1fff'}}>Unmount</span> - A corresponding subtree of component renders or effects was unmounted.\n- <span style={{padding: '0.125rem 0.25rem', backgroundColor: '#facc15', color: '#1f1f1fff'}}>Reconnect</span> - Similar to Mount, but limited to cases when [`<Activity>`](/reference/react/Activity) is used.\n- <span style={{padding: '0.125rem 0.25rem', backgroundColor: '#facc15', color: '#1f1f1fff'}}>Disconnect</span> - Similar to Unmount, but limited to cases when [`<Activity>`](/reference/react/Activity) is used.\n\n#### Changed props {/*changed-props*/}\n\nIn development builds, when you click on a component render entry, you can inspect potential changes in props. You can use this information to identify unnecessary renders.\n\n<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}>\n  <img className=\"w-full light-image\" src=\"/images/docs/performance-tracks/changed-props.png\" alt=\"Components track: changed props\" />\n  <img className=\"w-full dark-image\" src=\"/images/docs/performance-tracks/changed-props.dark.png\" alt=\"Components track: changed props\" />\n</div>\n\n### Server {/*server*/}\n\n<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}>\n  <img className=\"w-full light-image\" src=\"/images/docs/performance-tracks/server-overview.png\" alt=\"React Server Performance Tracks\" />\n  <img className=\"w-full dark-image\" src=\"/images/docs/performance-tracks/server-overview.dark.png\" alt=\"React Server Performance Tracks\" />\n</div>\n\n#### Server Requests {/*server-requests*/}\n\nThe Server Requests track visualized all Promises that eventually end up in a React Server Component. This includes any `async` operations like calling `fetch` or async Node.js file operations. \n\nReact will try to combine Promises that are started from inside third-party code into a single span representing the the duration of the entire operation blocking 1st party code.\nFor example, a third party library method called `getUser` that calls `fetch` internally multiple times will be represented as a single span called `getUser`, instead of showing multiple `fetch` spans.\n\nClicking on spans will show you a stack trace of where the Promise was created as well as a view of the value that the Promise resolved to, if available.\n\nRejected Promises are displayed as red with their rejected value.\n\n#### Server Components {/*server-components*/}\n\nThe Server Components tracks visualize the durations of React Server Components Promises they awaited. Timings are displayed as a flamegraph, where each entry represents the duration of the corresponding component render and all its descendant children components.\n\nIf you await a Promise, React will display duration of that Promise. To see all I/O operations, use the Server Requests track.\n\nDifferent colors are used to indicate the duration of the component render. The darker the color, the longer the duration.\n\nThe Server Components track group will always contain a \"Primary\" track. If React is able to render Server Components concurrently, it will display addititional \"Parallel\" tracks.\nIf more than 8 Server Components are rendered concurrently, React will associate them with the last \"Parallel\" track instead of adding more tracks.\n"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/index.md",
    "content": "---\ntitle: eslint-plugin-react-hooks\nversion: rc\n---\n\n<Intro>\n\n`eslint-plugin-react-hooks` provides ESLint rules to enforce the [Rules of React](/reference/rules).\n\n</Intro>\n\nThis plugin helps you catch violations of React's rules at build time, ensuring your components and hooks follow React's rules for correctness and performance. The lints cover both fundamental React patterns (exhaustive-deps and rules-of-hooks) and issues flagged by React Compiler. React Compiler diagnostics are automatically surfaced by this ESLint plugin, and can be used even if your app hasn't adopted the compiler yet.\n\n<Note>\nWhen the compiler reports a diagnostic, it means that the compiler was able to statically detect a pattern that is not supported or breaks the Rules of React. When it detects this, it **automatically** skips over those components and hooks, while keeping the rest of your app compiled. This ensures optimal coverage of safe optimizations that won't break your app.\n\nWhat this means for linting, is that you don’t need to fix all violations immediately. Address them at your own pace to gradually increase the number of optimized components.\n</Note>\n\n## Recommended Rules {/*recommended*/}\n\nThese rules are included in the `recommended` preset in `eslint-plugin-react-hooks`:\n\n* [`exhaustive-deps`](/reference/eslint-plugin-react-hooks/lints/exhaustive-deps) - Validates that dependency arrays for React hooks contain all necessary dependencies\n* [`rules-of-hooks`](/reference/eslint-plugin-react-hooks/lints/rules-of-hooks) - Validates that components and hooks follow the Rules of Hooks\n* [`component-hook-factories`](/reference/eslint-plugin-react-hooks/lints/component-hook-factories) - Validates higher order functions defining nested components or hooks\n* [`config`](/reference/eslint-plugin-react-hooks/lints/config) - Validates the compiler configuration options\n* [`error-boundaries`](/reference/eslint-plugin-react-hooks/lints/error-boundaries) - Validates usage of Error Boundaries instead of try/catch for child errors\n* [`gating`](/reference/eslint-plugin-react-hooks/lints/gating) - Validates configuration of gating mode\n* [`globals`](/reference/eslint-plugin-react-hooks/lints/globals) - Validates against assignment/mutation of globals during render\n* [`immutability`](/reference/eslint-plugin-react-hooks/lints/immutability) - Validates against mutating props, state, and other immutable values\n* [`incompatible-library`](/reference/eslint-plugin-react-hooks/lints/incompatible-library) - Validates against usage of libraries which are incompatible with memoization\n* [`preserve-manual-memoization`](/reference/eslint-plugin-react-hooks/lints/preserve-manual-memoization) - Validates that existing manual memoization is preserved by the compiler\n* [`purity`](/reference/eslint-plugin-react-hooks/lints/purity) - Validates that components/hooks are pure by checking known-impure functions\n* [`refs`](/reference/eslint-plugin-react-hooks/lints/refs) - Validates correct usage of refs, not reading/writing during render\n* [`set-state-in-effect`](/reference/eslint-plugin-react-hooks/lints/set-state-in-effect) - Validates against calling setState synchronously in an effect\n* [`set-state-in-render`](/reference/eslint-plugin-react-hooks/lints/set-state-in-render) - Validates against setting state during render\n* [`static-components`](/reference/eslint-plugin-react-hooks/lints/static-components) - Validates that components are static, not recreated every render\n* [`unsupported-syntax`](/reference/eslint-plugin-react-hooks/lints/unsupported-syntax) - Validates against syntax that React Compiler does not support\n* [`use-memo`](/reference/eslint-plugin-react-hooks/lints/use-memo) - Validates usage of the `useMemo` hook without a return value"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/component-hook-factories.md",
    "content": "---\ntitle: component-hook-factories\n---\n\n<Intro>\n\nValidates against higher order functions defining nested components or hooks. Components and hooks should be defined at the module level.\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nDefining components or hooks inside other functions creates new instances on every call. React treats each as a completely different component, destroying and recreating the entire component tree, losing all state, and causing performance problems.\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js {expectedErrors: {'react-compiler': [14]}}\n// ❌ Factory function creating components\nfunction createComponent(defaultValue) {\n  return function Component() {\n    // ...\n  };\n}\n\n// ❌ Component defined inside component\nfunction Parent() {\n  function Child() {\n    // ...\n  }\n\n  return <Child />;\n}\n\n// ❌ Hook factory function\nfunction createCustomHook(endpoint) {\n  return function useData() {\n    // ...\n  };\n}\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// ✅ Component defined at module level\nfunction Component({ defaultValue }) {\n  // ...\n}\n\n// ✅ Custom hook at module level\nfunction useData(endpoint) {\n  // ...\n}\n```\n\n## Troubleshooting {/*troubleshooting*/}\n\n### I need dynamic component behavior {/*dynamic-behavior*/}\n\nYou might think you need a factory to create customized components:\n\n```js\n// ❌ Wrong: Factory pattern\nfunction makeButton(color) {\n  return function Button({children}) {\n    return (\n      <button style={{backgroundColor: color}}>\n        {children}\n      </button>\n    );\n  };\n}\n\nconst RedButton = makeButton('red');\nconst BlueButton = makeButton('blue');\n```\n\nPass [JSX as children](/learn/passing-props-to-a-component#passing-jsx-as-children) instead:\n\n```js\n// ✅ Better: Pass JSX as children\nfunction Button({color, children}) {\n  return (\n    <button style={{backgroundColor: color}}>\n      {children}\n    </button>\n  );\n}\n\nfunction App() {\n  return (\n    <>\n      <Button color=\"red\">Red</Button>\n      <Button color=\"blue\">Blue</Button>\n    </>\n  );\n}\n```\n"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/config.md",
    "content": "---\ntitle: config\n---\n\n<Intro>\n\nValidates the compiler [configuration options](/reference/react-compiler/configuration).\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nReact Compiler accepts various [configuration options](/reference/react-compiler/configuration)  to control its behavior. This rule validates that your configuration uses correct option names and value types, preventing silent failures from typos or incorrect settings.\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js\n// ❌ Unknown option name\nmodule.exports = {\n  plugins: [\n    ['babel-plugin-react-compiler', {\n      compileMode: 'all' // Typo: should be compilationMode\n    }]\n  ]\n};\n\n// ❌ Invalid option value\nmodule.exports = {\n  plugins: [\n    ['babel-plugin-react-compiler', {\n      compilationMode: 'everything' // Invalid: use 'all' or 'infer'\n    }]\n  ]\n};\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// ✅ Valid compiler configuration\nmodule.exports = {\n  plugins: [\n    ['babel-plugin-react-compiler', {\n      compilationMode: 'infer',\n      panicThreshold: 'critical_errors'\n    }]\n  ]\n};\n```\n\n## Troubleshooting {/*troubleshooting*/}\n\n### Configuration not working as expected {/*config-not-working*/}\n\nYour compiler configuration might have typos or incorrect values:\n\n```js\n// ❌ Wrong: Common configuration mistakes\nmodule.exports = {\n  plugins: [\n    ['babel-plugin-react-compiler', {\n      // Typo in option name\n      compilationMod: 'all',\n      // Wrong value type\n      panicThreshold: true,\n      // Unknown option\n      optimizationLevel: 'max'\n    }]\n  ]\n};\n```\n\nCheck the [configuration documentation](/reference/react-compiler/configuration) for valid options:\n\n```js\n// ✅ Better: Valid configuration\nmodule.exports = {\n  plugins: [\n    ['babel-plugin-react-compiler', {\n      compilationMode: 'all', // or 'infer'\n      panicThreshold: 'none', // or 'critical_errors', 'all_errors'\n      // Only use documented options\n    }]\n  ]\n};\n```\n"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/error-boundaries.md",
    "content": "---\ntitle: error-boundaries\n---\n\n<Intro>\n\nValidates usage of Error Boundaries instead of try/catch for errors in child components.\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nTry/catch blocks can't catch errors that happen during React's rendering process. Errors thrown in rendering methods or hooks bubble up through the component tree. Only [Error Boundaries](/reference/react/Component#catching-rendering-errors-with-an-error-boundary) can catch these errors.\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js {expectedErrors: {'react-compiler': [4]}}\n// ❌ Try/catch won't catch render errors\nfunction Parent() {\n  try {\n    return <ChildComponent />; // If this throws, catch won't help\n  } catch (error) {\n    return <div>Error occurred</div>;\n  }\n}\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// ✅ Using error boundary\nfunction Parent() {\n  return (\n    <ErrorBoundary>\n      <ChildComponent />\n    </ErrorBoundary>\n  );\n}\n```\n\n## Troubleshooting {/*troubleshooting*/}\n\n### Why is the linter telling me not to wrap `use` in `try`/`catch`? {/*why-is-the-linter-telling-me-not-to-wrap-use-in-trycatch*/}\n\nThe `use` hook doesn't throw errors in the traditional sense, it suspends component execution. When `use` encounters a pending promise, it suspends the component and lets React show a fallback. Only Suspense and Error Boundaries can handle these cases. The linter warns against `try`/`catch` around `use` to prevent confusion as the `catch` block would never run.\n\n```js {expectedErrors: {'react-compiler': [5]}}\n// ❌ Try/catch around `use` hook\nfunction Component({promise}) {\n  try {\n    const data = use(promise); // Won't catch - `use` suspends, not throws\n    return <div>{data}</div>;\n  } catch (error) {\n    return <div>Failed to load</div>; // Unreachable\n  }\n}\n\n// ✅ Error boundary catches `use` errors\nfunction App() {\n  return (\n    <ErrorBoundary fallback={<div>Failed to load</div>}>\n      <Suspense fallback={<div>Loading...</div>}>\n        <DataComponent promise={fetchData()} />\n      </Suspense>\n    </ErrorBoundary>\n  );\n}\n```"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/exhaustive-deps.md",
    "content": "---\ntitle: exhaustive-deps\n---\n\n<Intro>\n\nValidates that dependency arrays for React hooks contain all necessary dependencies.\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nReact hooks like `useEffect`, `useMemo`, and `useCallback` accept dependency arrays. When a value referenced inside these hooks isn't included in the dependency array, React won't re-run the effect or recalculate the value when that dependency changes. This causes stale closures where the hook uses outdated values.\n\n## Common Violations {/*common-violations*/}\n\nThis error often happens when you try to \"trick\" React about dependencies to control when an effect runs. Effects should synchronize your component with external systems. The dependency array tells React which values the effect uses, so React knows when to re-synchronize.\n\nIf you find yourself fighting with the linter, you likely need to restructure your code. See [Removing Effect Dependencies](/learn/removing-effect-dependencies) to learn how.\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js\n// ❌ Missing dependency\nuseEffect(() => {\n  console.log(count);\n}, []); // Missing 'count'\n\n// ❌ Missing prop\nuseEffect(() => {\n  fetchUser(userId);\n}, []); // Missing 'userId'\n\n// ❌ Incomplete dependencies\nuseMemo(() => {\n  return items.sort(sortOrder);\n}, [items]); // Missing 'sortOrder'\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// ✅ All dependencies included\nuseEffect(() => {\n  console.log(count);\n}, [count]);\n\n// ✅ All dependencies included\nuseEffect(() => {\n  fetchUser(userId);\n}, [userId]);\n```\n\n## Troubleshooting {/*troubleshooting*/}\n\n### Adding a function dependency causes infinite loops {/*function-dependency-loops*/}\n\nYou have an effect, but you're creating a new function on every render:\n\n```js\n// ❌ Causes infinite loop\nconst logItems = () => {\n  console.log(items);\n};\n\nuseEffect(() => {\n  logItems();\n}, [logItems]); // Infinite loop!\n```\n\nIn most cases, you don't need the effect. Call the function where the action happens instead:\n\n```js\n// ✅ Call it from the event handler\nconst logItems = () => {\n  console.log(items);\n};\n\nreturn <button onClick={logItems}>Log</button>;\n\n// ✅ Or derive during render if there's no side effect\nitems.forEach(item => {\n  console.log(item);\n});\n```\n\nIf you genuinely need the effect (for example, to subscribe to something external), make the dependency stable:\n\n```js\n// ✅ useCallback keeps the function reference stable\nconst logItems = useCallback(() => {\n  console.log(items);\n}, [items]);\n\nuseEffect(() => {\n  logItems();\n}, [logItems]);\n\n// ✅ Or move the logic straight into the effect\nuseEffect(() => {\n  console.log(items);\n}, [items]);\n```\n\n### Running an effect only once {/*effect-on-mount*/}\n\nYou want to run an effect once on mount, but the linter complains about missing dependencies:\n\n```js\n// ❌ Missing dependency\nuseEffect(() => {\n  sendAnalytics(userId);\n}, []); // Missing 'userId'\n```\n\nEither include the dependency (recommended) or use a ref if you truly need to run once:\n\n```js\n// ✅ Include dependency\nuseEffect(() => {\n  sendAnalytics(userId);\n}, [userId]);\n\n// ✅ Or use a ref guard inside an effect\nconst sent = useRef(false);\n\nuseEffect(() => {\n  if (sent.current) {\n    return;\n  }\n\n  sent.current = true;\n  sendAnalytics(userId);\n}, [userId]);\n```\n\n## Options {/*options*/}\n\nYou can configure custom effect hooks using shared ESLint settings (available in `eslint-plugin-react-hooks` 6.1.1 and later):\n\n```js\n{\n  \"settings\": {\n    \"react-hooks\": {\n      \"additionalEffectHooks\": \"(useMyEffect|useCustomEffect)\"\n    }\n  }\n}\n```\n\n- `additionalEffectHooks`: Regex pattern matching custom hooks that should be checked for exhaustive dependencies. This configuration is shared across all `react-hooks` rules.\n\nFor backward compatibility, this rule also accepts a rule-level option:\n\n```js\n{\n  \"rules\": {\n    \"react-hooks/exhaustive-deps\": [\"warn\", {\n      \"additionalHooks\": \"(useMyCustomHook|useAnotherHook)\"\n    }]\n  }\n}\n```\n\n- `additionalHooks`: Regex for hooks that should be checked for exhaustive dependencies. **Note:** If this rule-level option is specified, it takes precedence over the shared `settings` configuration.\n"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/gating.md",
    "content": "---\ntitle: gating\n---\n\n<Intro>\n\nValidates configuration of [gating mode](/reference/react-compiler/gating).\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nGating mode lets you gradually adopt React Compiler by marking specific components for optimization. This rule ensures your gating configuration is valid so the compiler knows which components to process.\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js\n// ❌ Missing required fields\nmodule.exports = {\n  plugins: [\n    ['babel-plugin-react-compiler', {\n      gating: {\n        importSpecifierName: '__experimental_useCompiler'\n        // Missing 'source' field\n      }\n    }]\n  ]\n};\n\n// ❌ Invalid gating type\nmodule.exports = {\n  plugins: [\n    ['babel-plugin-react-compiler', {\n      gating: '__experimental_useCompiler' // Should be object\n    }]\n  ]\n};\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// ✅ Complete gating configuration\nmodule.exports = {\n  plugins: [\n    ['babel-plugin-react-compiler', {\n      gating: {\n        importSpecifierName: 'isCompilerEnabled', // exported function name\n        source: 'featureFlags' // module name\n      }\n    }]\n  ]\n};\n\n// featureFlags.js\nexport function isCompilerEnabled() {\n  // ...\n}\n\n// ✅ No gating (compile everything)\nmodule.exports = {\n  plugins: [\n    ['babel-plugin-react-compiler', {\n      // No gating field - compiles all components\n    }]\n  ]\n};\n```\n"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/globals.md",
    "content": "---\ntitle: globals\n---\n\n<Intro>\n\nValidates against assignment/mutation of globals during render, part of ensuring that [side effects must run outside of render](/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render).\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nGlobal variables exist outside React's control. When you modify them during render, you break React's assumption that rendering is pure. This can cause components to behave differently in development vs production, break Fast Refresh, and make your app impossible to optimize with features like React Compiler.\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js\n// ❌ Global counter\nlet renderCount = 0;\nfunction Component() {\n  renderCount++; // Mutating global\n  return <div>Count: {renderCount}</div>;\n}\n\n// ❌ Modifying window properties\nfunction Component({userId}) {\n  window.currentUser = userId; // Global mutation\n  return <div>User: {userId}</div>;\n}\n\n// ❌ Global array push\nconst events = [];\nfunction Component({event}) {\n  events.push(event); // Mutating global array\n  return <div>Events: {events.length}</div>;\n}\n\n// ❌ Cache manipulation\nconst cache = {};\nfunction Component({id}) {\n  if (!cache[id]) {\n    cache[id] = fetchData(id); // Modifying cache during render\n  }\n  return <div>{cache[id]}</div>;\n}\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// ✅ Use state for counters\nfunction Component() {\n  const [clickCount, setClickCount] = useState(0);\n\n  const handleClick = () => {\n    setClickCount(c => c + 1);\n  };\n\n  return (\n    <button onClick={handleClick}>\n      Clicked: {clickCount} times\n    </button>\n  );\n}\n\n// ✅ Use context for global values\nfunction Component() {\n  const user = useContext(UserContext);\n  return <div>User: {user.id}</div>;\n}\n\n// ✅ Synchronize external state with React\nfunction Component({title}) {\n  useEffect(() => {\n    document.title = title; // OK in effect\n  }, [title]);\n\n  return <div>Page: {title}</div>;\n}\n```\n"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/immutability.md",
    "content": "---\ntitle: immutability\n---\n\n<Intro>\n\nValidates against mutating props, state, and other values that [are immutable](/reference/rules/components-and-hooks-must-be-pure#props-and-state-are-immutable).\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nA component’s props and state are immutable snapshots. Never mutate them directly. Instead, pass new props down, and use the setter function from `useState`.\n\n## Common Violations {/*common-violations*/}\n\n### Invalid {/*invalid*/}\n\n```js\n// ❌ Array push mutation\nfunction Component() {\n  const [items, setItems] = useState([1, 2, 3]);\n\n  const addItem = () => {\n    items.push(4); // Mutating!\n    setItems(items); // Same reference, no re-render\n  };\n}\n\n// ❌ Object property assignment\nfunction Component() {\n  const [user, setUser] = useState({name: 'Alice'});\n\n  const updateName = () => {\n    user.name = 'Bob'; // Mutating!\n    setUser(user); // Same reference\n  };\n}\n\n// ❌ Sort without spreading\nfunction Component() {\n  const [items, setItems] = useState([3, 1, 2]);\n\n  const sortItems = () => {\n    setItems(items.sort()); // sort mutates!\n  };\n}\n```\n\n### Valid {/*valid*/}\n\n```js\n// ✅ Create new array\nfunction Component() {\n  const [items, setItems] = useState([1, 2, 3]);\n\n  const addItem = () => {\n    setItems([...items, 4]); // New array\n  };\n}\n\n// ✅ Create new object\nfunction Component() {\n  const [user, setUser] = useState({name: 'Alice'});\n\n  const updateName = () => {\n    setUser({...user, name: 'Bob'}); // New object\n  };\n}\n```\n\n## Troubleshooting {/*troubleshooting*/}\n\n### I need to add items to an array {/*add-items-array*/}\n\nMutating arrays with methods like `push()` won't trigger re-renders:\n\n```js\n// ❌ Wrong: Mutating the array\nfunction TodoList() {\n  const [todos, setTodos] = useState([]);\n\n  const addTodo = (id, text) => {\n    todos.push({id, text});\n    setTodos(todos); // Same array reference!\n  };\n\n  return (\n    <ul>\n      {todos.map(todo => <li key={todo.id}>{todo.text}</li>)}\n    </ul>\n  );\n}\n```\n\nCreate a new array instead:\n\n```js\n// ✅ Better: Create a new array\nfunction TodoList() {\n  const [todos, setTodos] = useState([]);\n\n  const addTodo = (id, text) => {\n    setTodos([...todos, {id, text}]);\n    // Or: setTodos(todos => [...todos, {id: Date.now(), text}])\n  };\n\n  return (\n    <ul>\n      {todos.map(todo => <li key={todo.id}>{todo.text}</li>)}\n    </ul>\n  );\n}\n```\n\n### I need to update nested objects {/*update-nested-objects*/}\n\nMutating nested properties doesn't trigger re-renders:\n\n```js\n// ❌ Wrong: Mutating nested object\nfunction UserProfile() {\n  const [user, setUser] = useState({\n    name: 'Alice',\n    settings: {\n      theme: 'light',\n      notifications: true\n    }\n  });\n\n  const toggleTheme = () => {\n    user.settings.theme = 'dark'; // Mutation!\n    setUser(user); // Same object reference\n  };\n}\n```\n\nSpread at each level that needs updating:\n\n```js\n// ✅ Better: Create new objects at each level\nfunction UserProfile() {\n  const [user, setUser] = useState({\n    name: 'Alice',\n    settings: {\n      theme: 'light',\n      notifications: true\n    }\n  });\n\n  const toggleTheme = () => {\n    setUser({\n      ...user,\n      settings: {\n        ...user.settings,\n        theme: 'dark'\n      }\n    });\n  };\n}\n```"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/incompatible-library.md",
    "content": "---\ntitle: incompatible-library\n---\n\n<Intro>\n\nValidates against usage of libraries which are incompatible with memoization (manual or automatic).\n\n</Intro>\n\n<Note>\n\nThese libraries were designed before React's memoization rules were fully documented. They made the correct choices at the time to optimize for ergonomic ways to keep components just the right amount of reactive as app state changes. While these legacy patterns worked, we have since discovered that it's incompatible with React's programming model. We will continue working with library authors to migrate these libraries to use patterns that follow the Rules of React.\n\n</Note>\n\n## Rule Details {/*rule-details*/}\n\nSome libraries use patterns that aren't supported by React. When the linter detects usages of these APIs from a [known list](https://github.com/facebook/react/blob/main/compiler/packages/babel-plugin-react-compiler/src/HIR/DefaultModuleTypeProvider.ts), it flags them under this rule. This means that React Compiler can automatically skip over components that use these incompatible APIs, in order to avoid breaking your app.\n\n```js\n// Example of how memoization breaks with these libraries\nfunction Form() {\n  const { watch } = useForm();\n\n  // ❌ This value will never update, even when 'name' field changes\n  const name = useMemo(() => watch('name'), [watch]);\n\n  return <div>Name: {name}</div>; // UI appears \"frozen\"\n}\n```\n\nReact Compiler automatically memoizes values following the Rules of React. If something breaks with manual `useMemo`, it will also break the compiler's automatic optimization. This rule helps identify these problematic patterns.\n\n<DeepDive>\n\n#### Designing APIs that follow the Rules of React {/*designing-apis-that-follow-the-rules-of-react*/}\n\nOne question to think about when designing a library API or hook is whether calling the API can be safely memoized with `useMemo`. If it can't, then both manual and React Compiler memoizations will break your user's code.\n\nFor example, one such incompatible pattern is \"interior mutability\". Interior mutability is when an object or function keeps its own hidden state that changes over time, even though the reference to it stays the same. Think of it like a box that looks the same on the outside but secretly rearranges its contents. React can't tell anything changed because it only checks if you gave it a different box, not what's inside. This breaks memoization, since React relies on the outer object (or function) changing if part of its value has changed.\n\nAs a rule of thumb, when designing React APIs, think about whether `useMemo` would break it:\n\n```js\nfunction Component() {\n  const { someFunction } = useLibrary();\n  // it should always be safe to memoize functions like this\n  const result = useMemo(() => someFunction(), [someFunction]);\n}\n```\n\nInstead, design APIs that return immutable state and use explicit update functions:\n\n```js\n// ✅ Good: Return immutable state that changes reference when updated\nfunction Component() {\n  const { field, updateField } = useLibrary();\n  // this is always safe to memo\n  const greeting = useMemo(() => `Hello, ${field.name}!`, [field.name]);\n\n  return (\n    <div>\n      <input\n        value={field.name}\n        onChange={(e) => updateField('name', e.target.value)}\n      />\n      <p>{greeting}</p>\n    </div>\n  );\n}\n```\n\n</DeepDive>\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js\n// ❌ react-hook-form `watch`\nfunction Component() {\n  const {watch} = useForm();\n  const value = watch('field'); // Interior mutability\n  return <div>{value}</div>;\n}\n\n// ❌ TanStack Table `useReactTable`\nfunction Component({data}) {\n  const table = useReactTable({\n    data,\n    columns,\n    getCoreRowModel: getCoreRowModel(),\n  });\n  // table instance uses interior mutability\n  return <Table table={table} />;\n}\n```\n\n<Pitfall>\n\n#### MobX {/*mobx*/}\n\nMobX patterns like `observer` also break memoization assumptions, but the linter does not yet detect them. If you rely on MobX and find that your app doesn't work with React Compiler, you may need to use the `\"use no memo\" directive`.\n\n```js\n// ❌ MobX `observer`\nconst Component = observer(() => {\n  const [timer] = useState(() => new Timer());\n  return <span>Seconds passed: {timer.secondsPassed}</span>;\n});\n```\n\n</Pitfall>\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// ✅ For react-hook-form, use `useWatch`:\nfunction Component() {\n  const {register, control} = useForm();\n  const watchedValue = useWatch({\n    control,\n    name: 'field'\n  });\n\n  return (\n    <>\n      <input {...register('field')} />\n      <div>Current value: {watchedValue}</div>\n    </>\n  );\n}\n```\n\nSome other libraries do not yet have alternative APIs that are compatible with React's memoization model. If the linter doesn't automatically skip over your components or hooks that call these APIs, please [file an issue](https://github.com/facebook/react/issues) so we can add it to the linter.\n"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/preserve-manual-memoization.md",
    "content": "---\ntitle: preserve-manual-memoization\n---\n\n<Intro>\n\nValidates that existing manual memoization is preserved by the compiler. React Compiler will only compile components and hooks if its inference [matches or exceeds the existing manual memoization](/learn/react-compiler/introduction#what-should-i-do-about-usememo-usecallback-and-reactmemo).\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nReact Compiler preserves your existing `useMemo`, `useCallback`, and `React.memo` calls. If you've manually memoized something, the compiler assumes you had a good reason and won't remove it. However, incomplete dependencies prevent the compiler from understanding your code's data flow and applying further optimizations.\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js\n// ❌ Missing dependencies in useMemo\nfunction Component({ data, filter }) {\n  const filtered = useMemo(\n    () => data.filter(filter),\n    [data] // Missing 'filter' dependency\n  );\n\n  return <List items={filtered} />;\n}\n\n// ❌ Missing dependencies in useCallback\nfunction Component({ onUpdate, value }) {\n  const handleClick = useCallback(() => {\n    onUpdate(value);\n  }, [onUpdate]); // Missing 'value'\n\n  return <button onClick={handleClick}>Update</button>;\n}\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// ✅ Complete dependencies\nfunction Component({ data, filter }) {\n  const filtered = useMemo(\n    () => data.filter(filter),\n    [data, filter] // All dependencies included\n  );\n\n  return <List items={filtered} />;\n}\n\n// ✅ Or let the compiler handle it\nfunction Component({ data, filter }) {\n  // No manual memoization needed\n  const filtered = data.filter(filter);\n  return <List items={filtered} />;\n}\n```\n\n## Troubleshooting {/*troubleshooting*/}\n\n### Should I remove my manual memoization? {/*remove-manual-memoization*/}\n\nYou might wonder if React Compiler makes manual memoization unnecessary:\n\n```js\n// Do I still need this?\nfunction Component({items, sortBy}) {\n  const sorted = useMemo(() => {\n    return [...items].sort((a, b) => {\n      return a[sortBy] - b[sortBy];\n    });\n  }, [items, sortBy]);\n\n  return <List items={sorted} />;\n}\n```\n\nYou can safely remove it if using React Compiler:\n\n```js\n// ✅ Better: Let the compiler optimize\nfunction Component({items, sortBy}) {\n  const sorted = [...items].sort((a, b) => {\n    return a[sortBy] - b[sortBy];\n  });\n\n  return <List items={sorted} />;\n}\n```"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/purity.md",
    "content": "---\ntitle: purity\n---\n\n<Intro>\n\nValidates that [components/hooks are pure](/reference/rules/components-and-hooks-must-be-pure) by checking that they do not call known-impure functions.\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nReact components must be pure functions - given the same props, they should always return the same JSX. When components use functions like `Math.random()` or `Date.now()` during render, they produce different output each time, breaking React's assumptions and causing bugs like hydration mismatches, incorrect memoization, and unpredictable behavior.\n\n## Common Violations {/*common-violations*/}\n\nIn general, any API that returns a different value for the same inputs violates this rule. Usual examples include:\n\n- `Math.random()`\n- `Date.now()` / `new Date()`\n- `crypto.randomUUID()`\n- `performance.now()`\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js\n// ❌ Math.random() in render\nfunction Component() {\n  const id = Math.random(); // Different every render\n  return <div key={id}>Content</div>;\n}\n\n// ❌ Date.now() for values\nfunction Component() {\n  const timestamp = Date.now(); // Changes every render\n  return <div>Created at: {timestamp}</div>;\n}\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// ✅ Stable IDs from initial state\nfunction Component() {\n  const [id] = useState(() => crypto.randomUUID());\n  return <div key={id}>Content</div>;\n}\n```\n\n## Troubleshooting {/*troubleshooting*/}\n\n### I need to show the current time {/*current-time*/}\n\nCalling `Date.now()` during render makes your component impure:\n\n```js {expectedErrors: {'react-compiler': [3]}}\n// ❌ Wrong: Time changes every render\nfunction Clock() {\n  return <div>Current time: {Date.now()}</div>;\n}\n```\n\nInstead, [move the impure function outside of render](/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent):\n\n```js\nfunction Clock() {\n  const [time, setTime] = useState(() => Date.now());\n\n  useEffect(() => {\n    const interval = setInterval(() => {\n      setTime(Date.now());\n    }, 1000);\n\n    return () => clearInterval(interval);\n  }, []);\n\n  return <div>Current time: {time}</div>;\n}\n```"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/refs.md",
    "content": "---\ntitle: refs\n---\n\n<Intro>\n\nValidates correct usage of refs, not reading/writing during render. See the \"pitfalls\" section in [`useRef()` usage](/reference/react/useRef#usage).\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nRefs hold values that aren't used for rendering. Unlike state, changing a ref doesn't trigger a re-render. Reading or writing `ref.current` during render breaks React's expectations. Refs might not be initialized when you try to read them, and their values can be stale or inconsistent.\n\n## How It Detects Refs {/*how-it-detects-refs*/}\n\nThe lint only applies these rules to values it knows are refs. A value is inferred as a ref when the compiler sees any of the following patterns:\n\n- Returned from `useRef()` or `React.createRef()`.\n\n  ```js\n  const scrollRef = useRef(null);\n  ```\n\n- An identifier named `ref` or ending in `Ref` that reads from or writes to `.current`.\n\n  ```js\n  buttonRef.current = node;\n  ```\n\n- Passed through a JSX `ref` prop (for example `<div ref={someRef} />`).\n\n  ```jsx\n  <input ref={inputRef} />\n  ```\n\nOnce something is marked as a ref, that inference follows the value through assignments, destructuring, or helper calls. This lets the lint surface violations even when `ref.current` is accessed inside another function that received the ref as an argument.\n\n## Common Violations {/*common-violations*/}\n\n- Reading `ref.current` during render\n- Updating `refs` during render\n- Using `refs` for values that should be state\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js\n// ❌ Reading ref during render\nfunction Component() {\n  const ref = useRef(0);\n  const value = ref.current; // Don't read during render\n  return <div>{value}</div>;\n}\n\n// ❌ Modifying ref during render\nfunction Component({value}) {\n  const ref = useRef(null);\n  ref.current = value; // Don't modify during render\n  return <div />;\n}\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// ✅ Read ref in effects/handlers\nfunction Component() {\n  const ref = useRef(null);\n\n  useEffect(() => {\n    if (ref.current) {\n      console.log(ref.current.offsetWidth); // OK in effect\n    }\n  });\n\n  return <div ref={ref} />;\n}\n\n// ✅ Use state for UI values\nfunction Component() {\n  const [count, setCount] = useState(0);\n\n  return (\n    <button onClick={() => setCount(count + 1)}>\n      {count}\n    </button>\n  );\n}\n\n// ✅ Lazy initialization of ref value\nfunction Component() {\n  const ref = useRef(null);\n\n  // Initialize only once on first use\n  if (ref.current === null) {\n    ref.current = expensiveComputation(); // OK - lazy initialization\n  }\n\n  const handleClick = () => {\n    console.log(ref.current); // Use the initialized value\n  };\n\n  return <button onClick={handleClick}>Click</button>;\n}\n```\n\n## Troubleshooting {/*troubleshooting*/}\n\n### The lint flagged my plain object with `.current` {/*plain-object-current*/}\n\nThe name heuristic intentionally treats `ref.current` and `fooRef.current` as real refs. If you're modeling a custom container object, pick a different name (for example, `box`) or move the mutable value into state. Renaming avoids the lint because the compiler stops inferring it as a ref.\n"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/rules-of-hooks.md",
    "content": "---\ntitle: rules-of-hooks\n---\n\n<Intro>\n\nValidates that components and hooks follow the [Rules of Hooks](/reference/rules/rules-of-hooks).\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nReact relies on the order in which hooks are called to correctly preserve state between renders. Each time your component renders, React expects the exact same hooks to be called in the exact same order. When hooks are called conditionally or in loops, React loses track of which state corresponds to which hook call, leading to bugs like state mismatches and \"Rendered fewer/more hooks than expected\" errors.\n\n## Common Violations {/*common-violations*/}\n\nThese patterns violate the Rules of Hooks:\n\n- **Hooks in conditions** (`if`/`else`, ternary, `&&`/`||`)\n- **Hooks in loops** (`for`, `while`, `do-while`)\n- **Hooks after early returns**\n- **Hooks in callbacks/event handlers**\n- **Hooks in async functions**\n- **Hooks in class methods**\n- **Hooks at module level**\n\n<Note>\n\n### `use` hook {/*use-hook*/}\n\nThe `use` hook is different from other React hooks. You can call it conditionally and in loops:\n\n```js\n// ✅ `use` can be conditional\nif (shouldFetch) {\n  const data = use(fetchPromise);\n}\n\n// ✅ `use` can be in loops\nfor (const promise of promises) {\n  results.push(use(promise));\n}\n```\n\nHowever, `use` still has restrictions:\n- Can't be wrapped in try/catch\n- Must be called inside a component or hook\n\nLearn more: [`use` API Reference](/reference/react/use)\n\n</Note>\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js\n// ❌ Hook in condition\nif (isLoggedIn) {\n  const [user, setUser] = useState(null);\n}\n\n// ❌ Hook after early return\nif (!data) return <Loading />;\nconst [processed, setProcessed] = useState(data);\n\n// ❌ Hook in callback\n<button onClick={() => {\n  const [clicked, setClicked] = useState(false);\n}}/>\n\n// ❌ `use` in try/catch\ntry {\n  const data = use(promise);\n} catch (e) {\n  // error handling\n}\n\n// ❌ Hook at module level\nconst globalState = useState(0); // Outside component\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\nfunction Component({ isSpecial, shouldFetch, fetchPromise }) {\n  // ✅ Hooks at top level\n  const [count, setCount] = useState(0);\n  const [name, setName] = useState('');\n\n  if (!isSpecial) {\n    return null;\n  }\n\n  if (shouldFetch) {\n    // ✅ `use` can be conditional\n    const data = use(fetchPromise);\n    return <div>{data}</div>;\n  }\n\n  return <div>{name}: {count}</div>;\n}\n```\n\n## Troubleshooting {/*troubleshooting*/}\n\n### I want to fetch data based on some condition {/*conditional-data-fetching*/}\n\nYou're trying to conditionally call useEffect:\n\n```js\n// ❌ Conditional hook\nif (isLoggedIn) {\n  useEffect(() => {\n    fetchUserData();\n  }, []);\n}\n```\n\nCall the hook unconditionally, check condition inside:\n\n```js\n// ✅ Condition inside hook\nuseEffect(() => {\n  if (isLoggedIn) {\n    fetchUserData();\n  }\n}, [isLoggedIn]);\n```\n\n<Note>\n\nThere are better ways to fetch data rather than in a useEffect. Consider using TanStack Query, useSWR, or React Router 6.4+ for data fetching. These solutions handle deduplicating requests, caching responses, and avoiding network waterfalls.\n\nLearn more: [Fetching Data](/learn/synchronizing-with-effects#fetching-data)\n\n</Note>\n\n### I need different state for different scenarios {/*conditional-state-initialization*/}\n\nYou're trying to conditionally initialize state:\n\n```js\n// ❌ Conditional state\nif (userType === 'admin') {\n  const [permissions, setPermissions] = useState(adminPerms);\n} else {\n  const [permissions, setPermissions] = useState(userPerms);\n}\n```\n\nAlways call useState, conditionally set the initial value:\n\n```js\n// ✅ Conditional initial value\nconst [permissions, setPermissions] = useState(\n  userType === 'admin' ? adminPerms : userPerms\n);\n```\n\n## Options {/*options*/}\n\nYou can configure custom effect hooks using shared ESLint settings (available in `eslint-plugin-react-hooks` 6.1.1 and later):\n\n```js\n{\n  \"settings\": {\n    \"react-hooks\": {\n      \"additionalEffectHooks\": \"(useMyEffect|useCustomEffect)\"\n    }\n  }\n}\n```\n\n- `additionalEffectHooks`: Regex pattern matching custom hooks that should be treated as effects. This allows `useEffectEvent` and similar event functions to be called from your custom effect hooks.\n\nThis shared configuration is used by both `rules-of-hooks` and `exhaustive-deps` rules, ensuring consistent behavior across all hook-related linting.\n"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/set-state-in-effect.md",
    "content": "---\ntitle: set-state-in-effect\n---\n\n<Intro>\n\nValidates against calling setState synchronously in an effect, which can lead to re-renders that degrade performance.\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nSetting state immediately inside an effect forces React to restart the entire render cycle. When you update state in an effect, React must re-render your component, apply changes to the DOM, and then run effects again. This creates an extra render pass that could have been avoided by transforming data directly during render or deriving state from props. Transform data at the top level of your component instead. This code will naturally re-run when props or state change without triggering additional render cycles.\n\nSynchronous `setState` calls in effects trigger immediate re-renders before the browser can paint, causing performance issues and visual jank. React has to render twice: once to apply the state update, then again after effects run. This double rendering is wasteful when the same result could be achieved with a single render.\n\nIn many cases, you may also not need an effect at all. Please see [You Might Not Need an Effect](/learn/you-might-not-need-an-effect) for more information.\n\n## Common Violations {/*common-violations*/}\n\nThis rule catches several patterns where synchronous setState is used unnecessarily:\n\n- Setting loading state synchronously\n- Deriving state from props in effects\n- Transforming data in effects instead of render\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js\n// ❌ Synchronous setState in effect\nfunction Component({data}) {\n  const [items, setItems] = useState([]);\n\n  useEffect(() => {\n    setItems(data); // Extra render, use initial state instead\n  }, [data]);\n}\n\n// ❌ Setting loading state synchronously\nfunction Component() {\n  const [loading, setLoading] = useState(false);\n\n  useEffect(() => {\n    setLoading(true); // Synchronous, causes extra render\n    fetchData().then(() => setLoading(false));\n  }, []);\n}\n\n// ❌ Transforming data in effect\nfunction Component({rawData}) {\n  const [processed, setProcessed] = useState([]);\n\n  useEffect(() => {\n    setProcessed(rawData.map(transform)); // Should derive in render\n  }, [rawData]);\n}\n\n// ❌ Deriving state from props\nfunction Component({selectedId, items}) {\n  const [selected, setSelected] = useState(null);\n\n  useEffect(() => {\n    setSelected(items.find(i => i.id === selectedId));\n  }, [selectedId, items]);\n}\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// ✅ setState in an effect is fine if the value comes from a ref\nfunction Tooltip() {\n  const ref = useRef(null);\n  const [tooltipHeight, setTooltipHeight] = useState(0);\n\n  useLayoutEffect(() => {\n    const { height } = ref.current.getBoundingClientRect();\n    setTooltipHeight(height);\n  }, []);\n}\n\n// ✅ Calculate during render\nfunction Component({selectedId, items}) {\n  const selected = items.find(i => i.id === selectedId);\n  return <div>{selected?.name}</div>;\n}\n```\n\n**When something can be calculated from the existing props or state, don't put it in state.** Instead, calculate it during rendering. This makes your code faster, simpler, and less error-prone. Learn more in [You Might Not Need an Effect](/learn/you-might-not-need-an-effect).\n"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/set-state-in-render.md",
    "content": "---\ntitle: set-state-in-render\n---\n\n<Intro>\n\nValidates against unconditionally setting state during render, which can trigger additional renders and potential infinite render loops.\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nCalling `setState` during render unconditionally triggers another render before the current one finishes. This creates an infinite loop that crashes your app.\n\n## Common Violations {/*common-violations*/}\n\n### Invalid {/*invalid*/}\n\n```js {expectedErrors: {'react-compiler': [4]}}\n// ❌ Unconditional setState directly in render\nfunction Component({value}) {\n  const [count, setCount] = useState(0);\n  setCount(value); // Infinite loop!\n  return <div>{count}</div>;\n}\n```\n\n### Valid {/*valid*/}\n\n```js\n// ✅ Derive during render\nfunction Component({items}) {\n  const sorted = [...items].sort(); // Just calculate it in render\n  return <ul>{sorted.map(/*...*/)}</ul>;\n}\n\n// ✅ Set state in event handler\nfunction Component() {\n  const [count, setCount] = useState(0);\n  return (\n    <button onClick={() => setCount(count + 1)}>\n      {count}\n    </button>\n  );\n}\n\n// ✅ Derive from props instead of setting state\nfunction Component({user}) {\n  const name = user?.name || '';\n  const email = user?.email || '';\n  return <div>{name}</div>;\n}\n\n// ✅ Conditionally derive state from props and state from previous renders\nfunction Component({ items }) {\n  const [isReverse, setIsReverse] = useState(false);\n  const [selection, setSelection] = useState(null);\n\n  const [prevItems, setPrevItems] = useState(items);\n  if (items !== prevItems) { // This condition makes it valid\n    setPrevItems(items);\n    setSelection(null);\n  }\n  // ...\n}\n```\n\n## Troubleshooting {/*troubleshooting*/}\n\n### I want to sync state to a prop {/*clamp-state-to-prop*/}\n\nA common problem is trying to \"fix\" state after it renders. Suppose you want to keep a counter from exceeding a `max` prop:\n\n```js\n// ❌ Wrong: clamps during render\nfunction Counter({max}) {\n  const [count, setCount] = useState(0);\n\n  if (count > max) {\n    setCount(max);\n  }\n\n  return (\n    <button onClick={() => setCount(count + 1)}>\n      {count}\n    </button>\n  );\n}\n```\n\nAs soon as `count` exceeds `max`, an infinite loop is triggered.\n\nInstead, it's often better to move this logic to the event (the place where the state is first set). For example, you can enforce the maximum at the moment you update state:\n\n```js\n// ✅ Clamp when updating\nfunction Counter({max}) {\n  const [count, setCount] = useState(0);\n\n  const increment = () => {\n    setCount(current => Math.min(current + 1, max));\n  };\n\n  return <button onClick={increment}>{count}</button>;\n}\n```\n\nNow the setter only runs in response to the click, React finishes the render normally, and `count` never crosses `max`.\n\nIn rare cases, you may need to adjust state based on information from previous renders. For those, follow [this pattern](https://react.dev/reference/react/useState#storing-information-from-previous-renders) of setting state conditionally.\n"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/static-components.md",
    "content": "---\ntitle: static-components\n---\n\n<Intro>\n\nValidates that components are static, not recreated every render. Components that are recreated dynamically can reset state and trigger excessive re-rendering.\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nComponents defined inside other components are recreated on every render. React sees each as a brand new component type, unmounting the old one and mounting the new one, destroying all state and DOM nodes in the process.\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js\n// ❌ Component defined inside component\nfunction Parent() {\n  const ChildComponent = () => { // New component every render!\n    const [count, setCount] = useState(0);\n    return <button onClick={() => setCount(count + 1)}>{count}</button>;\n  };\n\n  return <ChildComponent />; // State resets every render\n}\n\n// ❌ Dynamic component creation\nfunction Parent({type}) {\n  const Component = type === 'button'\n    ? () => <button>Click</button>\n    : () => <div>Text</div>;\n\n  return <Component />;\n}\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// ✅ Components at module level\nconst ButtonComponent = () => <button>Click</button>;\nconst TextComponent = () => <div>Text</div>;\n\nfunction Parent({type}) {\n  const Component = type === 'button'\n    ? ButtonComponent  // Reference existing component\n    : TextComponent;\n\n  return <Component />;\n}\n```\n\n## Troubleshooting {/*troubleshooting*/}\n\n### I need to render different components conditionally {/*conditional-components*/}\n\nYou might define components inside to access local state:\n\n```js {expectedErrors: {'react-compiler': [13]}}\n// ❌ Wrong: Inner component to access parent state\nfunction Parent() {\n  const [theme, setTheme] = useState('light');\n\n  function ThemedButton() { // Recreated every render!\n    return (\n      <button className={theme}>\n        Click me\n      </button>\n    );\n  }\n\n  return <ThemedButton />;\n}\n```\n\nPass data as props instead:\n\n```js\n// ✅ Better: Pass props to static component\nfunction ThemedButton({theme}) {\n  return (\n    <button className={theme}>\n      Click me\n    </button>\n  );\n}\n\nfunction Parent() {\n  const [theme, setTheme] = useState('light');\n  return <ThemedButton theme={theme} />;\n}\n```\n\n<Note>\n\nIf you find yourself wanting to define components inside other components to access local variables, that's a sign you should be passing props instead. This makes components more reusable and testable.\n\n</Note>"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/unsupported-syntax.md",
    "content": "---\ntitle: unsupported-syntax\n---\n\n<Intro>\n\nValidates against syntax that React Compiler does not support. If you need to, you can still use this syntax outside of React, such as in a standalone utility function.\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\nReact Compiler needs to statically analyze your code to apply optimizations. Features like `eval` and `with` make it impossible to statically understand what the code does at compile time, so the compiler can't optimize components that use them.\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js\n// ❌ Using eval in component\nfunction Component({ code }) {\n  const result = eval(code); // Can't be analyzed\n  return <div>{result}</div>;\n}\n\n// ❌ Using with statement\nfunction Component() {\n  with (Math) { // Changes scope dynamically\n    return <div>{sin(PI / 2)}</div>;\n  }\n}\n\n// ❌ Dynamic property access with eval\nfunction Component({propName}) {\n  const value = eval(`props.${propName}`);\n  return <div>{value}</div>;\n}\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// ✅ Use normal property access\nfunction Component({propName, props}) {\n  const value = props[propName]; // Analyzable\n  return <div>{value}</div>;\n}\n\n// ✅ Use standard Math methods\nfunction Component() {\n  return <div>{Math.sin(Math.PI / 2)}</div>;\n}\n```\n\n## Troubleshooting {/*troubleshooting*/}\n\n### I need to evaluate dynamic code {/*evaluate-dynamic-code*/}\n\nYou might need to evaluate user-provided code:\n\n```js {expectedErrors: {'react-compiler': [3]}}\n// ❌ Wrong: eval in component\nfunction Calculator({expression}) {\n  const result = eval(expression); // Unsafe and unoptimizable\n  return <div>Result: {result}</div>;\n}\n```\n\nUse a safe expression parser instead:\n\n```js\n// ✅ Better: Use a safe parser\nimport {evaluate} from 'mathjs'; // or similar library\n\nfunction Calculator({expression}) {\n  const [result, setResult] = useState(null);\n\n  const calculate = () => {\n    try {\n      // Safe mathematical expression evaluation\n      setResult(evaluate(expression));\n    } catch (error) {\n      setResult('Invalid expression');\n    }\n  };\n\n  return (\n    <div>\n      <button onClick={calculate}>Calculate</button>\n      {result && <div>Result: {result}</div>}\n    </div>\n  );\n}\n```\n\n<Note>\n\nNever use `eval` with user input - it's a security risk. Use dedicated parsing libraries for specific use cases like mathematical expressions, JSON parsing, or template evaluation.\n\n</Note>"
  },
  {
    "path": "src/content/reference/eslint-plugin-react-hooks/lints/use-memo.md",
    "content": "---\ntitle: use-memo\n---\n\n<Intro>\n\nValidates that the `useMemo` hook is used with a return value. See [`useMemo` docs](/reference/react/useMemo) for more information.\n\n</Intro>\n\n## Rule Details {/*rule-details*/}\n\n`useMemo` is for computing and caching expensive values, not for side effects. Without a return value, `useMemo` returns `undefined`, which defeats its purpose and likely indicates you're using the wrong hook.\n\n### Invalid {/*invalid*/}\n\nExamples of incorrect code for this rule:\n\n```js {expectedErrors: {'react-compiler': [3]}}\n// ❌ No return value\nfunction Component({ data }) {\n  const processed = useMemo(() => {\n    data.forEach(item => console.log(item));\n    // Missing return!\n  }, [data]);\n\n  return <div>{processed}</div>; // Always undefined\n}\n```\n\n### Valid {/*valid*/}\n\nExamples of correct code for this rule:\n\n```js\n// ✅ Returns computed value\nfunction Component({ data }) {\n  const processed = useMemo(() => {\n    return data.map(item => item * 2);\n  }, [data]);\n\n  return <div>{processed}</div>;\n}\n```\n\n## Troubleshooting {/*troubleshooting*/}\n\n### I need to run side effects when dependencies change {/*side-effects*/}\n\nYou might try to use `useMemo` for side effects:\n\n{/* TODO(@poteto) fix compiler validation to check for unassigned useMemos */}\n```js {expectedErrors: {'react-compiler': [4]}}\n// ❌ Wrong: Side effects in useMemo\nfunction Component({user}) {\n  // No return value, just side effect\n  useMemo(() => {\n    analytics.track('UserViewed', {userId: user.id});\n  }, [user.id]);\n\n  // Not assigned to a variable\n  useMemo(() => {\n    return analytics.track('UserViewed', {userId: user.id});\n  }, [user.id]);\n}\n```\n\nIf the side effect needs to happen in response to user interaction, it's best to colocate the side effect with the event:\n\n```js\n// ✅ Good: Side effects in event handlers\nfunction Component({user}) {\n  const handleClick = () => {\n    analytics.track('ButtonClicked', {userId: user.id});\n    // Other click logic...\n  };\n\n  return <button onClick={handleClick}>Click me</button>;\n}\n```\n\nIf the side effect sychronizes React state with some external state (or vice versa), use `useEffect`:\n\n```js\n// ✅ Good: Synchronization in useEffect\nfunction Component({theme}) {\n  useEffect(() => {\n    localStorage.setItem('preferredTheme', theme);\n    document.body.className = theme;\n  }, [theme]);\n\n  return <div>Current theme: {theme}</div>;\n}\n```\n"
  },
  {
    "path": "src/content/reference/react/Activity.md",
    "content": "---\ntitle: <Activity>\n---\n\n<Intro>\n\n`<Activity>` を使い、UI の一部を非表示にしたり表示したりします。\n\n```js\n<Activity mode={visibility}>\n  <Sidebar />\n</Activity>\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<Activity>` {/*activity*/}\n\nActivity を使用して、アプリケーションの一部を非表示にすることができます。\n\n```js [[1, 1, \"\\\\\"hidden\\\\\"\"], [2, 2, \"<Sidebar />\"], [3, 1, \"\\\\\"visible\\\\\"\"]]\n<Activity mode={isShowingSidebar ? \"visible\" : \"hidden\"}>\n  <Sidebar />\n</Activity>\n```\n\nActivity バウンダリが <CodeStep step={1}>hidden</CodeStep> になっている場合、React は `display: \"none\"` の CSS プロパティを使って<CodeStep step={2}>その子</CodeStep>を視覚的に非表示にします。また、それらのエフェクトを破棄することですべてのアクティブなサブスクリプションをクリーンアップします。\n\n非表示の間も、子は新しい props に反応して再レンダーされますが、他のコンテンツよりも低い優先度で行われます。\n\nバウンダリが再び <CodeStep step={3}>visible</CodeStep> になると、React は以前の state を復元した状態で子を表示し、エフェクトを再作成します。\n\nこのように、Activity は「バックグラウンドアクティビティ」をレンダーするためのメカニズムと考えることができます。再度表示される可能性が高いコンテンツを完全に破棄する代わりに、Activity を使用することでそのコンテンツの UI と内部状態を維持・復元しつつ、非表示のコンテンツが不要な副作用を持たないようにすることができます。\n\n[さらに例を見る](#usage)\n\n#### props {/*props*/}\n\n* `children`: 表示・非表示を切り替えたい UI。\n* `mode`: `'visible'` または `'hidden'` の文字列。省略時は `'visible'` になる。\n\n#### 注意点 {/*caveats*/}\n\n- [`ViewTransition`](/reference/react/ViewTransition) の内部で Activity がレンダーされ、[`startTransition`](/reference/react/startTransition) によって引き起こされた更新の結果として表示されるようになると、`ViewTransition` の `enter` アニメーションが作動します。非表示になると、`exit` アニメーションが作動します。\n- テキストのみをレンダーする Activity は、非表示のテキストをレンダーするのではなく、何もレンダーしません。これは、可視性の変化を適用するための対応する DOM 要素がないためです。例えば、`<Activity mode=\"hidden\"><ComponentThatJustReturnsText /></Activity>` は、`const ComponentThatJustReturnsText = () => \"Hello, World!\"` の場合に DOM に何も出力しません。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 非表示コンポーネントの state を復元する {/*restoring-the-state-of-hidden-components*/}\n\nReact では、条件に応じてコンポーネントの表示、非表示を切り替えたい場合、典型的には条件分岐によってコンポーネントをマウントしたりアンマウントしたりします。\n\n```jsx\n{isShowingSidebar && (\n  <Sidebar />\n)}\n```\n\nしかしコンポーネントをアンマウントすると内部の state が破棄されてしまい、これは必ずしも望ましくはありません。\n\nActivity バウンダリを用いてコンポーネントを非表示にすると、React は state を後で使うために「セーブ」しておくことができます。\n\n```jsx\n<Activity mode={isShowingSidebar ? \"visible\" : \"hidden\"}>\n  <Sidebar />\n</Activity>\n```\n\nこれにより、コンポーネントを非表示にした後で、以前の state を保持した状態で復元することが可能です。\n\n次の例には、展開可能なセクションを持つサイドバーがあります。\"Overview\" を押すと、その下にある 3 つのサブアイテムが表示されます。アプリのメイン領域には、サイドバーを表示したり非表示にしたりするためのボタンもあります。\n\nOverview セクションを展開してから、サイドバーを閉じ、また開いてみてください。\n\n<Sandpack>\n\n```js src/App.js active\nimport { useState } from 'react';\nimport Sidebar from './Sidebar.js';\n\nexport default function App() {\n  const [isShowingSidebar, setIsShowingSidebar] = useState(true);\n\n  return (\n    <>\n      {isShowingSidebar && (\n        <Sidebar />\n      )}\n\n      <main>\n        <button onClick={() => setIsShowingSidebar(!isShowingSidebar)}>\n          Toggle sidebar\n        </button>\n        <h1>Main content</h1>\n      </main>\n    </>\n  );\n}\n```\n\n```js src/Sidebar.js\nimport { useState } from 'react';\n\nexport default function Sidebar() {\n  const [isExpanded, setIsExpanded] = useState(false)\n  \n  return (\n    <nav>\n      <button onClick={() => setIsExpanded(!isExpanded)}>\n        Overview\n        <span className={`indicator ${isExpanded ? 'down' : 'right'}`}>\n          &#9650;\n        </span>\n      </button>\n\n      {isExpanded && (\n        <ul>\n          <li>Section 1</li>\n          <li>Section 2</li>\n          <li>Section 3</li>\n        </ul>\n      )}\n    </nav>\n  );\n}\n```\n\n```css\nbody { height: 275px; margin: 0; }\n#root {\n  display: flex;\n  gap: 10px;\n  height: 100%;\n}\nnav {\n  padding: 10px;\n  background: #eee;\n  font-size: 14px;\n  height: 100%;\n}\nmain {\n  padding: 10px;\n}\np {\n  margin: 0;\n}\nh1 {\n  margin-top: 10px;\n}\n.indicator {\n  margin-left: 4px;\n  display: inline-block;\n  rotate: 90deg;\n}\n.indicator.down {\n  rotate: 180deg;\n}\n```\n\n</Sandpack>\n\nOverview セクションは、表示されるときに毎回折りたたまれた状態で表示されてしまいます。`isShowingSidebar` が `false` になる際にサイドバーをアンマウントするため、その内部の state もすべて失われてしまうのです。\n\nこれは Activity の完璧なユースケースです。サイドバーを視覚的に非表示にしている間でも、その内部 state を保持することができます。\n\nサイドバーの条件付きレンダーを Activity バウンダリに置き換えてみましょう。\n\n```jsx {7,9}\n// Before\n{isShowingSidebar && (\n  <Sidebar />\n)}\n\n// After\n<Activity mode={isShowingSidebar ? 'visible' : 'hidden'}>\n  <Sidebar />\n</Activity>\n```\n\n新しい動作を確認してみてください。\n\n<Sandpack>\n\n```js src/App.js active\nimport { Activity, useState } from 'react';\n\nimport Sidebar from './Sidebar.js';\n\nexport default function App() {\n  const [isShowingSidebar, setIsShowingSidebar] = useState(true);\n\n  return (\n    <>\n      <Activity mode={isShowingSidebar ? 'visible' : 'hidden'}>\n        <Sidebar />\n      </Activity>\n\n      <main>\n        <button onClick={() => setIsShowingSidebar(!isShowingSidebar)}>\n          Toggle sidebar\n        </button>\n        <h1>Main content</h1>\n      </main>\n    </>\n  );\n}\n```\n\n```js src/Sidebar.js\nimport { useState } from 'react';\n\nexport default function Sidebar() {\n  const [isExpanded, setIsExpanded] = useState(false)\n  \n  return (\n    <nav>\n      <button onClick={() => setIsExpanded(!isExpanded)}>\n        Overview\n        <span className={`indicator ${isExpanded ? 'down' : 'right'}`}>\n          &#9650;\n        </span>\n      </button>\n\n      {isExpanded && (\n        <ul>\n          <li>Section 1</li>\n          <li>Section 2</li>\n          <li>Section 3</li>\n        </ul>\n      )}\n    </nav>\n  );\n}\n```\n\n```css\nbody { height: 275px; margin: 0; }\n#root {\n  display: flex;\n  gap: 10px;\n  height: 100%;\n}\nnav {\n  padding: 10px;\n  background: #eee;\n  font-size: 14px;\n  height: 100%;\n}\nmain {\n  padding: 10px;\n}\np {\n  margin: 0;\n}\nh1 {\n  margin-top: 10px;\n}\n.indicator {\n  margin-left: 4px;\n  display: inline-block;\n  rotate: 90deg;\n}\n.indicator.down {\n  rotate: 180deg;\n}\n```\n\n</Sandpack>\n\nサイドバーの実装を変更することなく、内部の state が復元されるようになりました。\n\n---\n\n### 非表示コンポーネントの DOM を保持する {/*restoring-the-dom-of-hidden-components*/}\n\nActivity バウンダリは `display: none` を使って子を非表示にするため、非表示状態の場合には子の DOM も保持されます。これにより、ユーザが再び操作する可能性がある UI の一時的な状態を保持しておくのにも役立ちます。\n\n以下の例では、Contact タブにはユーザがメッセージを入力するための `<textarea>` があります。何かテキストを入力し、Home タブに切り替えて、再び Contact タブに戻ると、下書きのメッセージは消えてしまいます。\n\n<Sandpack>\n\n```js src/App.js \nimport { useState } from 'react';\nimport TabButton from './TabButton.js';\nimport Home from './Home.js';\nimport Contact from './Contact.js';\n\nexport default function App() {\n  const [activeTab, setActiveTab] = useState('contact');\n\n  return (\n    <>\n      <TabButton\n        isActive={activeTab === 'home'}\n        onClick={() => setActiveTab('home')}\n      >\n        Home\n      </TabButton>\n      <TabButton\n        isActive={activeTab === 'contact'}\n        onClick={() => setActiveTab('contact')}\n      >\n        Contact\n      </TabButton>\n\n      <hr />\n\n      {activeTab === 'home' && <Home />}\n      {activeTab === 'contact' && <Contact />}\n    </>\n  );\n}\n```\n\n```js src/TabButton.js\nexport default function TabButton({ onClick, children, isActive }) {\n  if (isActive) {\n    return <b>{children}</b>\n  }\n\n  return (\n    <button onClick={onClick}>\n      {children}\n    </button>\n  );\n}\n```\n\n```js src/Home.js\nexport default function Home() {\n  return (\n    <p>Welcome to my profile!</p>\n  );\n}\n```\n\n```js src/Contact.js active\nexport default function Contact() {\n  return (\n    <div>\n      <p>Send me a message!</p>\n\n      <textarea />\n\n      <p>You can find me online here:</p>\n      <ul>\n        <li>admin@mysite.com</li>\n        <li>+123456789</li>\n      </ul>\n    </div>\n  );\n}\n```\n\n```css\nbody { height: 275px; }\nbutton { margin-right: 10px }\nb { display: inline-block; margin-right: 10px; }\n.pending { color: #777; }\n```\n\n</Sandpack>\n\nこれは `App` 内の `Contact` を完全にアンマウントしているからです。Contact タブがアンマウントされると、`<textarea>` 要素内の DOM の状態も失われてしまいます。\n\nActivity バウンダリを用いて表示・非表示状態を切り替えるようにすることで、それぞれのタブの DOM 要素を保持することができます。テキストを入力した後にタブを切り替え、下書きメッセージが消えなくなったことを確認してください。\n\n<Sandpack>\n\n```js src/App.js active\nimport { Activity, useState } from 'react';\nimport TabButton from './TabButton.js';\nimport Home from './Home.js';\nimport Contact from './Contact.js';\n\nexport default function App() {\n  const [activeTab, setActiveTab] = useState('contact');\n\n  return (\n    <>\n      <TabButton\n        isActive={activeTab === 'home'}\n        onClick={() => setActiveTab('home')}\n      >\n        Home\n      </TabButton>\n      <TabButton\n        isActive={activeTab === 'contact'}\n        onClick={() => setActiveTab('contact')}\n      >\n        Contact\n      </TabButton>\n\n      <hr />\n\n      <Activity mode={activeTab === 'home' ? 'visible' : 'hidden'}>\n        <Home />\n      </Activity>\n      <Activity mode={activeTab === 'contact' ? 'visible' : 'hidden'}>\n        <Contact />\n      </Activity>\n    </>\n  );\n}\n```\n\n```js src/TabButton.js\nexport default function TabButton({ onClick, children, isActive }) {\n  if (isActive) {\n    return <b>{children}</b>\n  }\n\n  return (\n    <button onClick={onClick}>\n      {children}\n    </button>\n  );\n}\n```\n\n```js src/Home.js\nexport default function Home() {\n  return (\n    <p>Welcome to my profile!</p>\n  );\n}\n```\n\n```js src/Contact.js \nexport default function Contact() {\n  return (\n    <div>\n      <p>Send me a message!</p>\n\n      <textarea />\n\n      <p>You can find me online here:</p>\n      <ul>\n        <li>admin@mysite.com</li>\n        <li>+123456789</li>\n      </ul>\n    </div>\n  );\n}\n```\n\n```css\nbody { height: 275px; }\nbutton { margin-right: 10px }\nb { display: inline-block; margin-right: 10px; }\n.pending { color: #777; }\n```\n\n</Sandpack>\n\n今回も、Activity バウンダリのおかげで、Contact タブの実装を書き換えることなく、その内部状態を保持できるようになったわけです。\n\n---\n\n### 表示される可能性が高いコンテンツのプリレンダー {/*pre-rendering-content-thats-likely-to-become-visible*/}\n\nここまでは、ユーザが何らかの操作を行ったコンテンツを非表示にした後も、Activity がその一時的な状態を保持できる、という例を見てきました。\n\nしかし Activity バウンダリは、ユーザがコンテンツを初めて目にする前にそれを**準備**しておくために使用することも可能です。\n\n```jsx [[1, 1, \"\\\\\"hidden\\\\\"\"]]\n<Activity mode=\"hidden\">\n  <SlowComponent />\n</Activity>\n```\n\nActivity バウンダリが初回レンダー時に <CodeStep step={1}>hidden</CodeStep> になっている場合、その子はページ上では表示されませんが、**レンダーは発生します**。ただし表示されているコンテンツよりも優先度は低くなり、かつエフェクトのセットアップも起きません。\n\nこの*プリレンダリング*により、子は事前に必要なコードやデータをロードできます。そのため後で Activity バウンダリが表示される場合に、子をより短い読み込み時間で素早く表示できます。\n\n例を見てみましょう。\n\n以下のデモでは、Posts タブがとあるデータをロードしています。押すと、データがフェッチされている間、サスペンスフォールバックが表示されてしまっています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState, Suspense } from 'react';\nimport TabButton from './TabButton.js';\nimport Home from './Home.js';\nimport Posts from './Posts.js';\n\nexport default function App() {\n  const [activeTab, setActiveTab] = useState('home');\n\n  return (\n    <>\n      <TabButton\n        isActive={activeTab === 'home'}\n        onClick={() => setActiveTab('home')}\n      >\n        Home\n      </TabButton>\n      <TabButton\n        isActive={activeTab === 'posts'}\n        onClick={() => setActiveTab('posts')}\n      >\n        Posts\n      </TabButton>\n\n      <hr />\n\n      <Suspense fallback={<h1>🌀 Loading...</h1>}>\n        {activeTab === 'home' && <Home />}\n        {activeTab === 'posts' && <Posts />}\n      </Suspense>\n    </>\n  );\n}\n```\n\n```js src/TabButton.js hidden\nexport default function TabButton({ onClick, children, isActive }) {\n  if (isActive) {\n    return <b>{children}</b>\n  }\n\n  return (\n    <button onClick={onClick}>\n      {children}\n    </button>\n  );\n}\n```\n\n```js src/Home.js\nexport default function Home() {\n  return (\n    <p>Welcome to my profile!</p>\n  );\n}\n```\n\n```js src/Posts.js\nimport { use } from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Posts() {\n  const posts = use(fetchData('/posts'));\n\n  return (\n    <ul className=\"items\">\n      {posts.map(post =>\n        <li className=\"item\" key={post.id}>\n          {post.title}\n        </li>\n      )}\n    </ul>\n  );\n}\n```\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url.startsWith('/posts')) {\n    return await getPosts();\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getPosts() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 1000);\n  });\n  let posts = [];\n  for (let i = 0; i < 10; i++) {\n    posts.push({\n      id: i,\n      title: 'Post #' + (i + 1)\n    });\n  }\n  return posts;\n}\n```\n\n```css\nbody { height: 275px; }\nbutton { margin-right: 10px }\nb { display: inline-block; margin-right: 10px; }\n.pending { color: #777; }\nvideo { width: 300px; margin-top: 10px; aspect-ratio: 16/9; }\n```\n\n</Sandpack>\n\nこれはタブがアクティブになるまで `App` は `Posts` をマウントしないからです。\n\n`App` を書き換えて、Activity バウンダリを使ってタブの表示状態を切り替えるようにすると、`Posts` はアプリの初回読み込み時にプリレンダーされます。このためタブが実際に表示される前にデータのフェッチが行えます。\n\nPosts タブをクリックしてみてください。\n\n<Sandpack>\n\n```js src/App.js\nimport { Activity, useState, Suspense } from 'react';\nimport TabButton from './TabButton.js';\nimport Home from './Home.js';\nimport Posts from './Posts.js';\n\nexport default function App() {\n  const [activeTab, setActiveTab] = useState('home');\n\n  return (\n    <>\n      <TabButton\n        isActive={activeTab === 'home'}\n        onClick={() => setActiveTab('home')}\n      >\n        Home\n      </TabButton>\n      <TabButton\n        isActive={activeTab === 'posts'}\n        onClick={() => setActiveTab('posts')}\n      >\n        Posts\n      </TabButton>\n\n      <hr />\n\n      <Suspense fallback={<h1>🌀 Loading...</h1>}>\n        <Activity mode={activeTab === 'home' ? 'visible' : 'hidden'}>\n          <Home />\n        </Activity>\n        <Activity mode={activeTab === 'posts' ? 'visible' : 'hidden'}>\n          <Posts />\n        </Activity>\n      </Suspense>\n    </>\n  );\n}\n```\n\n```js src/TabButton.js hidden\nexport default function TabButton({ onClick, children, isActive }) {\n  if (isActive) {\n    return <b>{children}</b>\n  }\n\n  return (\n    <button onClick={onClick}>\n      {children}\n    </button>\n  );\n}\n```\n\n```js src/Home.js\nexport default function Home() {\n  return (\n    <p>Welcome to my profile!</p>\n  );\n}\n```\n\n```js src/Posts.js\nimport { use } from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Posts() {\n  const posts = use(fetchData('/posts'));\n\n  return (\n    <ul className=\"items\">\n      {posts.map(post =>\n        <li className=\"item\" key={post.id}>\n          {post.title}\n        </li>\n      )}\n    </ul>\n  );\n}\n```\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url.startsWith('/posts')) {\n    return await getPosts();\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getPosts() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 1000);\n  });\n  let posts = [];\n  for (let i = 0; i < 10; i++) {\n    posts.push({\n      id: i,\n      title: 'Post #' + (i + 1)\n    });\n  }\n  return posts;\n}\n```\n\n```css\nbody { height: 275px; }\nbutton { margin-right: 10px }\nb { display: inline-block; margin-right: 10px; }\n.pending { color: #777; }\nvideo { width: 300px; margin-top: 10px; aspect-ratio: 16/9; }\n```\n\n</Sandpack>\n\n非表示の Activity バウンダリのおかげで、`Posts` は素早いレンダーに備えることができました。\n\n---\n\n非表示の Activity バウンダリを使ったコンポーネントのプリレンダーは、ユーザが次に操作する可能性が高い UI のロード時間を短縮するための強力な方法です。\n\n<Note>\n\n**プリレンダー中にフェッチされるのは、サスペンス対応のデータソースのみです**。これには以下のものが含まれます。\n\n- [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) や [Next.js](https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense) のようなサスペンス対応のフレームワークでのデータフェッチ\n- [`lazy`](/reference/react/lazy) を使ったコンポーネントコードの遅延ロード\n- [`use`](/reference/react/use) を使ったキャッシュ済みプロミスからの値の読み取り\n\nActivity は、エフェクト内部でフェッチされたデータを検出**しません**。\n\n上記の `Posts` コンポーネントでデータをロードする具体的な方法については、使用しているフレームワークに依存します。サスペンス対応のフレームワークを使用している場合、詳細はそのフレームワークのデータフェッチのドキュメントに記載されています。\n\n使い方に規約のある (opinionated) フレームワーク以外でサスペンス対応のデータフェッチを行うことは、まだサポートされていません。サスペンス対応のデータソースを実装するための要件は安定しておらず、ドキュメント化されていません。データソースをサスペンスと統合するための公式な API は、React の将来のバージョンでリリースされる予定です。\n\n</Note>\n\n---\n\n\n### ページ読み込み中のユーザ操作の高速化 {/*speeding-up-interactions-during-page-load*/}\n\nReact には、選択的ハイドレーション (Selective Hydration) と呼ばれる内部的なパフォーマンス最適化機能が含まれています。これは、アプリの初期 HTML を*分割して*ハイドレーションすることで、ページ上の他のコンポーネントがまだコードやデータをロードしていない場合でも、一部のコンポーネントを操作可能にするというものです。\n\nサスペンスバウンダリは選択的ハイドレーションの構成要素です。コンポーネントツリーを互いに独立した単位に自然に分割するものだからです。\n\n```jsx\nfunction Page() {\n  return (\n    <>\n      <MessageComposer />\n\n      <Suspense fallback=\"Loading chats...\">\n        <Chats />\n      </Suspense>\n    </>\n  )\n}\n```\n\nここでは、`MessageComposer` は、`Chats` がマウントされてデータのフェッチを開始する前であっても、ページの初回レンダー時に完全にハイドレートできます。\n\nこのように、サスペンスを使ってコンポーネントツリーを個別のユニットに分割することで、React はサーバでレンダーされたアプリの HTML を分割してハイドレーションできるようになり、アプリの一部を可能な限り速く操作可能にできます。\n\nサスペンスを使用していないページだとどうなるのでしょうか？\n\n以下の、タブの例を見てみましょう。\n\n```jsx\nfunction Page() {\n  const [activeTab, setActiveTab] = useState('home');\n\n  return (\n    <>\n      <TabButton onClick={() => setActiveTab('home')}>\n        Home\n      </TabButton>\n      <TabButton onClick={() => setActiveTab('video')}>\n        Video\n      </TabButton>\n\n      {activeTab === 'home' && (\n        <Home />\n      )}\n      {activeTab === 'video' && (\n        <Video />\n      )}\n    </>\n  )\n}\n```\n\nこの場合、React はページ全体を一度にハイドレーションしなければなりません。`Home` または `Video` のレンダーが遅い場合、ハイドレーション中にタブボタンが反応しないように感じられる可能性があります。\n\nアクティブなタブの周りにサスペンスを追加すれば、これは解決します。\n\n```jsx {13,20}\nfunction Page() {\n  const [activeTab, setActiveTab] = useState('home');\n\n  return (\n    <>\n      <TabButton onClick={() => setActiveTab('home')}>\n        Home\n      </TabButton>\n      <TabButton onClick={() => setActiveTab('video')}>\n        Video\n      </TabButton>\n\n      <Suspense fallback={<Placeholder />}>\n        {activeTab === 'home' && (\n          <Home />\n        )}\n        {activeTab === 'video' && (\n          <Video />\n        )}\n      </Suspense>\n    </>\n  )\n}\n```\n\n...しかし、初回レンダー時に `Placeholder` フォールバックが表示されるため、UI の見た目が変わってししまいます。\n\n代わりに Activity を使用することができます。Activity バウンダリは子を表示状態を切り替えるためのものなので、すでに自然とコンポーネントツリーを独立したユニットに分割していることになります。つまりサスペンスと同様、この機能により選択的ハイドレーションを構成することができるのです。\n\n上記の例を更新して、アクティブなタブの周りに Activity バウンダリを使用してみましょう。\n\n```jsx {13-18}\nfunction Page() {\n  const [activeTab, setActiveTab] = useState('home');\n\n  return (\n    <>\n      <TabButton onClick={() => setActiveTab('home')}>\n        Home\n      </TabButton>\n      <TabButton onClick={() => setActiveTab('video')}>\n        Video\n      </TabButton>\n\n      <Activity mode={activeTab === \"home\" ? \"visible\" : \"hidden\"}>\n        <Home />\n      </Activity>\n      <Activity mode={activeTab === \"video\" ? \"visible\" : \"hidden\"}>\n        <Video />\n      </Activity>\n    </>\n  )\n}\n```\n\nこれで、サーバでレンダーされる初期 HTML は元のバージョンと同じになりますが、Activity のおかげで、React は `Home` や `Video` をマウントする前に、タブボタンを先にハイドレートすることができます。\n\n---\n\nこのように、コンテンツを非表示にしたり表示したりすることに加えて、Activity バウンダリは、ページのどの部分が独立して操作可能になれるかを React に知らせることで、ハイドレーション中のアプリのパフォーマンスを向上させるのに役立ちます。\n\nそしてページがコンテンツの一部を非表示にすることがない場合でも、常に visible な Activity バウンダリを追加することで、ハイドレーションのパフォーマンスを向上させることも可能です。\n\n```jsx\nfunction Page() {\n  return (\n    <>\n      <Post />\n\n      <Activity>\n        <Comments />\n      </Activity>\n    </>\n  );\n} \n```\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### 非表示コンポーネントに望ましくない副作用がある {/*my-hidden-components-have-unwanted-side-effects*/}\n\nActivity バウンダリは、子に `display: none` を設定し、そのエフェクトをクリーンアップすることで、コンテンツを非表示にします。したがって、副作用を適切にクリーンアップする行儀の良い React コンポーネントのほとんどは、Activity によって非表示にされても問題なく動作するはずです。\n\nしかし、非表示にされたコンポーネントが、アンマウントされた場合とは異なる動作をする状況が存在します。特に顕著なのは、非表示コンポーネントの DOM は破棄されないため、その DOM からの副作用は、コンポーネントが非表示になった後でも持続するということです。\n\n例として、`<video>` タグを考えてみましょう。通常、これはクリーンアップを必要としません。なぜなら、ビデオを再生している最中であっても、タグをアンマウントすればブラウザでのビデオと音声の再生は停止するからです。以下のデモで、ビデオを再生してから Home を押してみてください。\n\n<Sandpack>\n\n```js src/App.js active\nimport { useState } from 'react';\nimport TabButton from './TabButton.js';\nimport Home from './Home.js';\nimport Video from './Video.js';\n\nexport default function App() {\n  const [activeTab, setActiveTab] = useState('video');\n\n  return (\n    <>\n      <TabButton\n        isActive={activeTab === 'home'}\n        onClick={() => setActiveTab('home')}\n      >\n        Home\n      </TabButton>\n      <TabButton\n        isActive={activeTab === 'video'}\n        onClick={() => setActiveTab('video')}\n      >\n        Video\n      </TabButton>\n\n      <hr />\n\n      {activeTab === 'home' && <Home />}\n      {activeTab === 'video' && <Video />}\n    </>\n  );\n}\n```\n\n```js src/TabButton.js hidden\nexport default function TabButton({ onClick, children, isActive }) {\n  if (isActive) {\n    return <b>{children}</b>\n  }\n\n  return (\n    <button onClick={onClick}>\n      {children}\n    </button>\n  );\n}\n```\n\n```js src/Home.js\nexport default function Home() {\n  return (\n    <p>Welcome to my profile!</p>\n  );\n}\n```\n\n```js src/Video.js \nexport default function Video() {\n  return (\n    <video\n      // 'Big Buck Bunny' licensed under CC 3.0 by the Blender foundation. Hosted by archive.org\n      src=\"https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4\"\n      controls\n      playsInline\n    />\n\n  );\n}\n```\n\n```css\nbody { height: 275px; }\nbutton { margin-right: 10px }\nb { display: inline-block; margin-right: 10px; }\n.pending { color: #777; }\nvideo { width: 300px; margin-top: 10px; aspect-ratio: 16/9; }\n```\n\n</Sandpack>\n\n予想通り、ビデオの再生が停止しました。\n\nでは次に、ユーザが最後に視聴していた時点のタイムコードを保持して、Video タブに戻ったときに最初から再生し直さないようにしたいとしましょう。\n\nこれは Activity の素晴らしいユースケースです！\n\n`App` を更新して、非アクティブなタブをアンマウントする代わりに、hidden 状態の Activity バウンダリで隠すようにし、今度はデモがどう動作するか見てみましょう。\n\n<Sandpack>\n\n```js src/App.js active\nimport { Activity, useState } from 'react';\nimport TabButton from './TabButton.js';\nimport Home from './Home.js';\nimport Video from './Video.js';\n\nexport default function App() {\n  const [activeTab, setActiveTab] = useState('video');\n\n  return (\n    <>\n      <TabButton\n        isActive={activeTab === 'home'}\n        onClick={() => setActiveTab('home')}\n      >\n        Home\n      </TabButton>\n      <TabButton\n        isActive={activeTab === 'video'}\n        onClick={() => setActiveTab('video')}\n      >\n        Video\n      </TabButton>\n\n      <hr />\n\n      <Activity mode={activeTab === 'home' ? 'visible' : 'hidden'}>\n        <Home />\n      </Activity>\n      <Activity mode={activeTab === 'video' ? 'visible' : 'hidden'}>\n        <Video />\n      </Activity>\n    </>\n  );\n}\n```\n\n```js src/TabButton.js hidden\nexport default function TabButton({ onClick, children, isActive }) {\n  if (isActive) {\n    return <b>{children}</b>\n  }\n\n  return (\n    <button onClick={onClick}>\n      {children}\n    </button>\n  );\n}\n```\n\n```js src/Home.js\nexport default function Home() {\n  return (\n    <p>Welcome to my profile!</p>\n  );\n}\n```\n\n```js src/Video.js \nexport default function Video() {\n  return (\n    <video\n      controls\n      playsInline\n      // 'Big Buck Bunny' licensed under CC 3.0 by the Blender foundation. Hosted by archive.org\n      src=\"https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4\"\n    />\n\n  );\n}\n```\n\n```css\nbody { height: 275px; }\nbutton { margin-right: 10px }\nb { display: inline-block; margin-right: 10px; }\n.pending { color: #777; }\nvideo { width: 300px; margin-top: 10px; aspect-ratio: 16/9; }\n```\n\n</Sandpack>\n\nおっと！ タブの `<video>` 要素がまだ DOM に残っているため、非表示になった後もビデオと音声が再生され続けてしまいます。\n\nこれを修正するには、ビデオを一時停止するクリーンアップ関数を持つエフェクトを追加します。\n\n```jsx {2,4-10,14}\nexport default function VideoTab() {\n  const ref = useRef();\n\n  useLayoutEffect(() => {\n    const videoRef = ref.current;\n\n    return () => {\n      videoRef.pause()\n    }\n  }, []);\n\n  return (\n    <video\n      ref={ref}\n      controls\n      playsInline\n      src=\"...\"\n    />\n\n  );\n}\n```\n\n`useEffect` の代わりに `useLayoutEffect` を呼び出しています。これは概念的に、クリーンアップコードがコンポーネントの UI が視覚的に非表示にされることに結びついているためです。通常のエフェクトを使用すると、（たとえば）再サスペンドするサスペンスバウンダリやビュー遷移 (view transition) によってコードの実行が遅延する可能性があります。\n\n新しい動作を見てみましょう。ビデオを再生し、Home タブに切り替え、その後 Video タブに戻してみてください。\n\n<Sandpack>\n\n```js src/App.js active\nimport { Activity, useState } from 'react';\nimport TabButton from './TabButton.js';\nimport Home from './Home.js';\nimport Video from './Video.js';\n\nexport default function App() {\n  const [activeTab, setActiveTab] = useState('video');\n\n  return (\n    <>\n      <TabButton\n        isActive={activeTab === 'home'}\n        onClick={() => setActiveTab('home')}\n      >\n        Home\n      </TabButton>\n      <TabButton\n        isActive={activeTab === 'video'}\n        onClick={() => setActiveTab('video')}\n      >\n        Video\n      </TabButton>\n\n      <hr />\n\n      <Activity mode={activeTab === 'home' ? 'visible' : 'hidden'}>\n        <Home />\n      </Activity>\n      <Activity mode={activeTab === 'video' ? 'visible' : 'hidden'}>\n        <Video />\n      </Activity>\n    </>\n  );\n}\n```\n\n```js src/TabButton.js hidden\nexport default function TabButton({ onClick, children, isActive }) {\n  if (isActive) {\n    return <b>{children}</b>\n  }\n\n  return (\n    <button onClick={onClick}>\n      {children}\n    </button>\n  );\n}\n```\n\n```js src/Home.js\nexport default function Home() {\n  return (\n    <p>Welcome to my profile!</p>\n  );\n}\n```\n\n```js src/Video.js \nimport { useRef, useLayoutEffect } from 'react';\n\nexport default function Video() {\n  const ref = useRef();\n\n  useLayoutEffect(() => {\n    const videoRef = ref.current\n\n    return () => {\n      videoRef.pause()\n    };\n  }, [])\n\n  return (\n    <video\n      ref={ref}\n      controls\n      playsInline\n      // 'Big Buck Bunny' licensed under CC 3.0 by the Blender foundation. Hosted by archive.org\n      src=\"https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4\"\n    />\n\n  );\n}\n```\n\n```css\nbody { height: 275px; }\nbutton { margin-right: 10px }\nb { display: inline-block; margin-right: 10px; }\n.pending { color: #777; }\nvideo { width: 300px; margin-top: 10px; aspect-ratio: 16/9; }\n```\n\n</Sandpack>\n\n完璧に動作しますね！ クリーンアップ関数により、Activity バウンダリで非表示にされた場合にビデオが確実に停止するようになりました。さらに良いことに、`<video>` タグが破棄されないため、タイムコードは保持され、ユーザが戻ってきて視聴を続ける際にビデオを再度初期化したりダウンロードしたりする必要もありません。\n\nこれは、非表示になるがユーザがすぐに再び操作する可能性が高い UI パーツについて、一時的な DOM の状態を保持するために Activity を使用できる、優れた例です。\n\n---\n\nこの例は、`<video>` のような特定のタグでは、アンマウントと非表示で動作が異なることを示しています。コンポーネントが副作用を持つ DOM をレンダーしていて、Activity バウンダリがそれを非表示にしたときにその副作用を防ぎたい場合は、クリーンアップするための関数を返すエフェクトを追加するようにしてください。\n\nこれが最も一般的に当てはまるのは、以下のタグです。\n\n  - `<video>`\n  - `<audio>`\n  - `<iframe>`\n\nただし通常は、React コンポーネントのほとんどは、Activity バウンダリによって非表示にされても問題なく動作するはずです。そして概念的には、「非表示」の Activity はアンマウントされているものとして考えるべきです。\n\n適切なクリーンアップを行っていないエフェクトを積極的に発見するために、[`<StrictMode>`](/reference/react/StrictMode) の使用をお勧めします。これは Activity バウンダリだけでなく、React の他の多くの動作にとっても重要です。\n\n---\n\n\n### 非表示コンポーネントのエフェクトが実行されない {/*my-hidden-components-have-effects-that-arent-running*/}\n\n`<Activity>` が \"hidden\" の場合、子のすべてのエフェクトがクリーンアップされます。概念的には、子はアンマウントされますが、React は後で使うために state を保存します。これは Activity の機能です。つまり、UI の非表示部分に対してサブスクリプションがアクティブにならないため、非表示コンテンツに必要な負荷が削減されます。\n\nコンポーネントの副作用をクリーンアップするためにエフェクトのマウントに依存している場合は、代わりにエフェクトから返すクリーンアップ関数内でその作業を行うよう、エフェクトをリファクタリングしてください。\n\n問題のあるエフェクトを積極的に見つけるために、[`<StrictMode>`](/reference/react/StrictMode) を追加することをお勧めします。これは Activity のアンマウントとマウントを積極的に実行して、予期しない副作用をキャッチします。"
  },
  {
    "path": "src/content/reference/react/Children.md",
    "content": "---\ntitle: Children\n---\n\n<Pitfall>\n\n`Children` の使用は一般的ではなく、コードが壊れやすくなる可能性があります。[一般的な代替手段をご覧ください](#alternatives)。\n\n</Pitfall>\n\n<Intro>\n\n`Children` は、props である [`children`](/learn/passing-props-to-a-component#passing-jsx-as-children) から受け取った JSX を操作、変換するために用います。\n\n```js\nconst mappedChildren = Children.map(children, child =>\n  <div className=\"Row\">\n    {child}\n  </div>\n);\n\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `Children.count(children)` {/*children-count*/}\n\n`Children.count(children)` を呼び出して、`children` データ構造内の子の数をカウントします。\n\n```js src/RowList.js active\nimport { Children } from 'react';\n\nfunction RowList({ children }) {\n  return (\n    <>\n      <h1>Total rows: {Children.count(children)}</h1>\n      ...\n    </>\n  );\n}\n```\n\n[さらに例を見る](#counting-children)\n\n#### 引数 {/*children-count-parameters*/}\n\n* `children`: コンポーネントが props として受け取る [`children`](/learn/passing-props-to-a-component#passing-jsx-as-children) の値。\n\n#### 返り値 {/*children-count-returns*/}\n\n当該 `children` 内部にあるノードの数。\n\n#### 注意点 {/*children-count-caveats*/}\n\n- 空のノード（`null`、`undefined`、およびブーリアン値）、文字列、数値、および [React 要素](/reference/react/createElement)が、個々のノードとしてカウントされます。配列自体は個別のノードとしてカウントされませんが、その子はカウントされます。**React 要素より深い走査は行われません**。要素がその場でレンダーされるわけではないため、子の走査も起きません。[フラグメント](/reference/react/Fragment)も走査されません。\n\n---\n\n### `Children.forEach(children, fn, thisArg?)` {/*children-foreach*/}\n\n`Children.forEach(children, fn, thisArg?)` を呼び出して、`children` データ構造内のそれぞれの子に対して何らかのコードを実行することができます。\n\n```js src/RowList.js active\nimport { Children } from 'react';\n\nfunction SeparatorList({ children }) {\n  const result = [];\n  Children.forEach(children, (child, index) => {\n    result.push(child);\n    result.push(<hr key={index} />);\n  });\n  // ...\n```\n\n[さらに例を見る](#running-some-code-for-each-child)\n\n#### 引数 {/*children-foreach-parameters*/}\n\n* `children`: コンポーネントが props として受け取る [`children`](/learn/passing-props-to-a-component#passing-jsx-as-children) の値。\n* `fn`: それぞれの子に対して実行したい関数。[配列の `forEach` メソッド](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) のコールバックに似ています。子を第 1 引数、そのインデックスを第 2 引数として呼び出されます。インデックスは `0` から始まり、呼び出しごとに増加します。\n* **省略可能** `thisArg`: `fn` 関数が呼び出される際の [`this` の値](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this)。省略された場合は `undefined` になります。\n\n#### 返り値 {/*children-foreach-returns*/}\n\n`Children.forEach` は `undefined` を返します。\n\n#### 注意点 {/*children-foreach-caveats*/}\n\n- 空のノード（`null`、`undefined`、およびブーリアン値）、文字列、数値、および [React 要素](/reference/react/createElement)が、個々の子ノードとして扱われます。配列自体は個別のノードとして扱われませんが、その中身は子ノードとして扱われます。**React 要素より深い走査は行われません**。要素がその場でレンダーされるわけではないため、子の走査も起きません。[フラグメント](/reference/react/Fragment)も走査されません。\n\n---\n\n### `Children.map(children, fn, thisArg?)` {/*children-map*/}\n\n`Children.map(children, fn, thisArg?)` を呼び出して、`children` データ構造内のそれぞれの子をマップ（変換）します。\n\n```js src/RowList.js active\nimport { Children } from 'react';\n\nfunction RowList({ children }) {\n  return (\n    <div className=\"RowList\">\n      {Children.map(children, child =>\n        <div className=\"Row\">\n          {child}\n        </div>\n      )}\n    </div>\n  );\n}\n```\n\n[さらに例を見る](#transforming-children)\n\n#### 引数 {/*children-map-parameters*/}\n\n* `children`: コンポーネントが props として受け取る [`children`](/learn/passing-props-to-a-component#passing-jsx-as-children) の値。\n* `fn`：[配列の `map` メソッド](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) のコールバックに似たマッピング関数。子を第 1 引数、そのインデックスを第 2 引数として呼び出されます。インデックスは `0` から始まり、呼び出しごとに増加します。この関数からは React ノードを返す必要があります。つまり空のノード（`null`、`undefined`、またはブーリアン値）、文字列、数値、React 要素、または他の React ノードの配列です。\n* **省略可能** `thisArg`: `fn` 関数が呼び出される際の [`this` の値](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this)。省略された場合は `undefined` になります。\n\n#### 返り値 {/*children-map-returns*/}\n\n`children` が `null` または `undefined` の場合、同じ値を返します。\n\nそれ以外の場合、`fn` 関数から返されたノードで構成されるフラットな配列を返します。返された配列には `null` と `undefined` を除くすべてのノードが含まれます。\n\n#### 注意点 {/*children-map-caveats*/}\n\n- 空のノード（`null`、`undefined`、およびブーリアン値）、文字列、数値、および [React 要素](/reference/react/createElement)が、個々の子ノードとして扱われます。配列自体は個別のノードとして扱われませんが、その中身は子ノードとして扱われます。**React 要素より深い走査は行われません**。要素がその場でレンダーされるわけではないため、子の走査も起きません。[フラグメント](/reference/react/Fragment)も走査されません。\n\n- `fn` から key 付きで要素ないし要素の配列を返す場合、**返された要素の key は、`children` の対応する元の項目のキーと自動的に結合されます**。`fn` から複数の要素を配列で返す場合、それらの key はその内部でローカルに一意であれば十分です。\n\n---\n\n### `Children.only(children)` {/*children-only*/}\n\n\n`Children.only(children)`を呼び出すことで `children` が単一の React 要素を表していることを確認します。\n\n```js\nfunction Box({ children }) {\n  const element = Children.only(children);\n  // ...\n```\n\n#### 引数 {/*children-only-parameters*/}\n\n* `children`: コンポーネントが props として受け取る [`children`](/learn/passing-props-to-a-component#passing-jsx-as-children) の値。\n\n#### 返り値 {/*children-only-returns*/}\n\n`children` が[有効な要素](/reference/react/isValidElement)である場合、その要素を返します。\n\nそれ以外の場合、エラーをスローします。\n\n#### 注意点 {/*children-only-caveats*/}\n\n- このメソッドは、`children` に配列（`Children.map` の返り値など）を渡すと常に**エラーをスローします**。つまり、`children` が単一要素の配列などではなく、単一の React 要素そのものであることを強制します。\n\n---\n\n### `Children.toArray(children)` {/*children-toarray*/}\n\n`Children.toArray(children)` を呼び出して、`children` データ構造から配列を作成します。\n\n```js src/ReversedList.js active\nimport { Children } from 'react';\n\nexport default function ReversedList({ children }) {\n  const result = Children.toArray(children);\n  result.reverse();\n  // ...\n```\n\n#### 引数 {/*children-toarray-parameters*/}\n\n* `children`: コンポーネントが props として受け取る [`children`](/learn/passing-props-to-a-component#passing-jsx-as-children) の値。\n\n#### 返り値 {/*children-toarray-returns*/}\n\n`children` 内の内容のフラットな配列を返します。\n\n#### 注意点 {/*children-toarray-caveats*/}\n\n- 空ノード（`null`、`undefined`、およびブーリアン値）は返される配列からは省かれます。**返される要素の key は、元の要素の key と、そのネストレベルや位置から計算されます**。これにより、配列のフラット化により挙動が変化しないことが保証されます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 子の変換 {/*transforming-children*/}\n\nコンポーネントが [`children` プロパティ](/learn/passing-props-to-a-component#passing-jsx-as-children)として受け取った子の JSX を変換するために、`Children.map` を呼び出します。\n\n```js {6,10}\nimport { Children } from 'react';\n\nfunction RowList({ children }) {\n  return (\n    <div className=\"RowList\">\n      {Children.map(children, child =>\n        <div className=\"Row\">\n          {child}\n        </div>\n      )}\n    </div>\n  );\n}\n```\n\n上記の例では、`RowList` は受け取ったすべての子を `<div className=\"Row\">` というコンテナにラップします。例えば、親コンポーネントが 3 つの `<p>` タグを props 経由で `children` として `RowList` に渡すとしましょう。\n\n```js\n<RowList>\n  <p>This is the first item.</p>\n  <p>This is the second item.</p>\n  <p>This is the third item.</p>\n</RowList>\n```\n\n上記の `RowList` の実装により、最終的にレンダーされる結果は次のようになります。\n\n```js\n<div className=\"RowList\">\n  <div className=\"Row\">\n    <p>This is the first item.</p>\n  </div>\n  <div className=\"Row\">\n    <p>This is the second item.</p>\n  </div>\n  <div className=\"Row\">\n    <p>This is the third item.</p>\n  </div>\n</div>\n```\n\n`Children.map` は [`map()` を使って配列を変換する](/learn/rendering-lists) のと似ています。違いは、`children` のデータ構造を*非公開 (opaque)* のものと見なすべきであることです。これは、`children` が実際に配列である場合があるとしても、それを配列あるいは他の特定のデータ型であると仮定してはならないという意味です。これが、子の変換が必要な場合には `Children.map` を使用すべき理由です。\n\n<Sandpack>\n\n```js\nimport RowList from './RowList.js';\n\nexport default function App() {\n  return (\n    <RowList>\n      <p>This is the first item.</p>\n      <p>This is the second item.</p>\n      <p>This is the third item.</p>\n    </RowList>\n  );\n}\n```\n\n```js src/RowList.js active\nimport { Children } from 'react';\n\nexport default function RowList({ children }) {\n  return (\n    <div className=\"RowList\">\n      {Children.map(children, child =>\n        <div className=\"Row\">\n          {child}\n        </div>\n      )}\n    </div>\n  );\n}\n```\n\n```css\n.RowList {\n  display: flex;\n  flex-direction: column;\n  border: 2px solid grey;\n  padding: 5px;\n}\n\n.Row {\n  border: 2px dashed black;\n  padding: 5px;\n  margin: 5px;\n}\n```\n\n</Sandpack>\n\n<DeepDive>\n\n#### なぜ children が常に配列とは限らないのか？ {/*why-is-the-children-prop-not-always-an-array*/}\n\nReact では props としての `children` は*非公開*のデータ構造だと見なされます。つまりその具体的な構造に依存してはいけないという意味です。子を変換したり、フィルタリングしたり、数えたりするためには、`Children` のメソッドを使用すべきです。\n\n実際には、`children` データ構造は内部的にはしばしば配列として表現されます。しかし、子が 1 つだけの場合、React は不必要なメモリオーバーヘッドを避けるため、余分な配列を作成しません。`children` の中身を直接覗くのではなく、`Children` のメソッドを使用する限り、React がデータ構造の実装方法を変更してもあなたのコードは壊れずに済みます。\n\n`children` が配列である場合でも、`Children.map` には便利な特別な振る舞いがあります。例えば、`Children.map` は、返された要素の [key](/learn/rendering-lists#keeping-list-items-in-order-with-key) と、渡された `children` にある key を組み合わせます。これにより、上記の例のようにラップされても元の子 JSX がキーを「失う」ことはありません。\n\n</DeepDive>\n\n<Pitfall>\n\n`children` データ構造は、JSX として渡されるコンポーネントの**レンダーされた出力を含みません**。以下の例では、`RowList` に渡される `children` には 3 つではなく 2 つのアイテムのみが含まれます。\n\n1. `<p>This is the first item.</p>`\n2. `<MoreRows />`\n\nこのため、この例では 2 つの行ラッパのみが生成されます：\n\n<Sandpack>\n\n```js\nimport RowList from './RowList.js';\n\nexport default function App() {\n  return (\n    <RowList>\n      <p>This is the first item.</p>\n      <MoreRows />\n    </RowList>\n  );\n}\n\nfunction MoreRows() {\n  return (\n    <>\n      <p>This is the second item.</p>\n      <p>This is the third item.</p>\n    </>\n  );\n}\n```\n\n```js src/RowList.js\nimport { Children } from 'react';\n\nexport default function RowList({ children }) {\n  return (\n    <div className=\"RowList\">\n      {Children.map(children, child =>\n        <div className=\"Row\">\n          {child}\n        </div>\n      )}\n    </div>\n  );\n}\n```\n\n```css\n.RowList {\n  display: flex;\n  flex-direction: column;\n  border: 2px solid grey;\n  padding: 5px;\n}\n\n.Row {\n  border: 2px dashed black;\n  padding: 5px;\n  margin: 5px;\n}\n```\n\n</Sandpack>\n\n`children` を使う際に、`<MoreRows />` のような**内側のコンポーネントのレンダー出力を取得する方法はありません**。このため[通常は代替手段のいずれかを使用する方が適切](#alternatives)です。\n\n</Pitfall>\n\n---\n\n### 子のそれぞれに対してコードを実行する {/*running-some-code-for-each-child*/}\n\n`Children.forEach` を呼び出すことで、`children` データ構造の子のそれぞれに対して反復処理を行えます。これは値を返さない、[配列の `forEach` メソッド](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)に似たものです。独自の配列を構築するなどのカスタムロジックを実行するために使用できます。\n\n<Sandpack>\n\n```js\nimport SeparatorList from './SeparatorList.js';\n\nexport default function App() {\n  return (\n    <SeparatorList>\n      <p>This is the first item.</p>\n      <p>This is the second item.</p>\n      <p>This is the third item.</p>\n    </SeparatorList>\n  );\n}\n```\n\n```js src/SeparatorList.js active\nimport { Children } from 'react';\n\nexport default function SeparatorList({ children }) {\n  const result = [];\n  Children.forEach(children, (child, index) => {\n    result.push(child);\n    result.push(<hr key={index} />);\n  });\n  result.pop(); // Remove the last separator\n  return result;\n}\n```\n\n</Sandpack>\n\n<Pitfall>\n\n前述の通り、`children` を使用する際に、内側のコンポーネントのレンダー出力を取得する方法はありません。このため[通常は代替手段のいずれかを使用する方が適切](#alternatives)です。\n\n</Pitfall>\n\n---\n\n### 子の数を数える {/*counting-children*/}\n\n`Children.count(children)` を呼び出して、子の数を計算します。\n\n<Sandpack>\n\n```js\nimport RowList from './RowList.js';\n\nexport default function App() {\n  return (\n    <RowList>\n      <p>This is the first item.</p>\n      <p>This is the second item.</p>\n      <p>This is the third item.</p>\n    </RowList>\n  );\n}\n```\n\n```js src/RowList.js active\nimport { Children } from 'react';\n\nexport default function RowList({ children }) {\n  return (\n    <div className=\"RowList\">\n      <h1 className=\"RowListHeader\">\n        Total rows: {Children.count(children)}\n      </h1>\n      {Children.map(children, child =>\n        <div className=\"Row\">\n          {child}\n        </div>\n      )}\n    </div>\n  );\n}\n```\n\n```css\n.RowList {\n  display: flex;\n  flex-direction: column;\n  border: 2px solid grey;\n  padding: 5px;\n}\n\n.RowListHeader {\n  padding-top: 5px;\n  font-size: 25px;\n  font-weight: bold;\n  text-align: center;\n}\n\n.Row {\n  border: 2px dashed black;\n  padding: 5px;\n  margin: 5px;\n}\n```\n\n</Sandpack>\n\n<Pitfall>\n\n前述の通り、`children` を使用する際に、内側のコンポーネントのレンダー出力を取得する方法はありません。このため[通常は代替手段のいずれかを使用する方が適切](#alternatives)です。\n\n</Pitfall>\n\n---\n\n### 子を配列に変換する {/*converting-children-to-an-array*/}\n\n`Children.toArray(children)` を呼び出して、`children` データ構造を通常の JavaScript 配列に変換します。これにより、[`filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)、[`sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)、[`reverse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse) などの組み込み配列メソッドを使って配列を操作できます。\n\n<Sandpack>\n\n```js\nimport ReversedList from './ReversedList.js';\n\nexport default function App() {\n  return (\n    <ReversedList>\n      <p>This is the first item.</p>\n      <p>This is the second item.</p>\n      <p>This is the third item.</p>\n    </ReversedList>\n  );\n}\n```\n\n```js src/ReversedList.js active\nimport { Children } from 'react';\n\nexport default function ReversedList({ children }) {\n  const result = Children.toArray(children);\n  result.reverse();\n  return result;\n}\n```\n\n</Sandpack>\n\n<Pitfall>\n\n前述の通り、`children` を使用する際に、内側のコンポーネントのレンダー出力を取得する方法はありません。このため[通常は代替手段のいずれかを使用する方が適切](#alternatives)です。\n\n</Pitfall>\n\n---\n\n## 代替手段 {/*alternatives*/}\n\n<Note>\n\nこのセクションで説明しているのは、以下のようにしてインポートする `Children` API（大文字の `C`）の代わりに使える手段です。\n\n```js\nimport { Children } from 'react';\n```\n\nこれを [`children` の使用](/learn/passing-props-to-a-component#passing-jsx-as-children)（小文字の `c`）と混同しないでください。こちらは良いことであり、推奨されています。\n\n</Note>\n\n### 複数のコンポーネントを公開する {/*exposing-multiple-components*/}\n\n`Children` のメソッドを使って children を操作することで、しばしばコードが壊れやすくなります。JSX でコンポーネントに children を渡す場合、通常はコンポーネントにより個々の子が操作されたり変換されたりすることを予想していないでしょう。\n\nできる限り `Children` メソッドの使用は避けてください。例えば、`RowList` のすべての子を `<div className=\"Row\">` でラップしたい場合、`Row` コンポーネントをエクスポートし、このように各行を手動でラップします。\n\n<Sandpack>\n\n```js\nimport { RowList, Row } from './RowList.js';\n\nexport default function App() {\n  return (\n    <RowList>\n      <Row>\n        <p>This is the first item.</p>\n      </Row>\n      <Row>\n        <p>This is the second item.</p>\n      </Row>\n      <Row>\n        <p>This is the third item.</p>\n      </Row>\n    </RowList>\n  );\n}\n```\n\n```js src/RowList.js\nexport function RowList({ children }) {\n  return (\n    <div className=\"RowList\">\n      {children}\n    </div>\n  );\n}\n\nexport function Row({ children }) {\n  return (\n    <div className=\"Row\">\n      {children}\n    </div>\n  );\n}\n```\n\n```css\n.RowList {\n  display: flex;\n  flex-direction: column;\n  border: 2px solid grey;\n  padding: 5px;\n}\n\n.Row {\n  border: 2px dashed black;\n  padding: 5px;\n  margin: 5px;\n}\n```\n\n</Sandpack>\n\n`Children.map` を使用する場合とは異なり、このアプローチではすべての子を自動的にラップしてくれません。**しかし[先ほどの `Children.map` を使用した例](#transforming-children)と比較しても、このアプローチには、さらに多くのコンポーネントを抽出しても機能するという利点があります**。例えば、自前の `MoreRows` コンポーネントを抽出しても機能します。\n\n<Sandpack>\n\n```js\nimport { RowList, Row } from './RowList.js';\n\nexport default function App() {\n  return (\n    <RowList>\n      <Row>\n        <p>This is the first item.</p>\n      </Row>\n      <MoreRows />\n    </RowList>\n  );\n}\n\nfunction MoreRows() {\n  return (\n    <>\n      <Row>\n        <p>This is the second item.</p>\n      </Row>\n      <Row>\n        <p>This is the third item.</p>\n      </Row>\n    </>\n  );\n}\n```\n\n```js src/RowList.js\nexport function RowList({ children }) {\n  return (\n    <div className=\"RowList\">\n      {children}\n    </div>\n  );\n}\n\nexport function Row({ children }) {\n  return (\n    <div className=\"Row\">\n      {children}\n    </div>\n  );\n}\n```\n\n```css\n.RowList {\n  display: flex;\n  flex-direction: column;\n  border: 2px solid grey;\n  padding: 5px;\n}\n\n.Row {\n  border: 2px dashed black;\n  padding: 5px;\n  margin: 5px;\n}\n```\n\n</Sandpack>\n\nこれは `Children.map` では機能しません。なぜなら、`<MoreRows />` が単一の子（つまり単一の行）のように「見える」からです。\n\n---\n\n### 配列を props として受け入れる {/*accepting-an-array-of-objects-as-a-prop*/}\n\n明示的に配列を props として渡すこともできます。例えば、以下の `RowList` は `rows` という配列を props として受け取ります。\n\n<Sandpack>\n\n```js\nimport { RowList, Row } from './RowList.js';\n\nexport default function App() {\n  return (\n    <RowList rows={[\n      { id: 'first', content: <p>This is the first item.</p> },\n      { id: 'second', content: <p>This is the second item.</p> },\n      { id: 'third', content: <p>This is the third item.</p> }\n    ]} />\n  );\n}\n```\n\n```js src/RowList.js\nexport function RowList({ rows }) {\n  return (\n    <div className=\"RowList\">\n      {rows.map(row => (\n        <div className=\"Row\" key={row.id}>\n          {row.content}\n        </div>\n      ))}\n    </div>\n  );\n}\n```\n\n```css\n.RowList {\n  display: flex;\n  flex-direction: column;\n  border: 2px solid grey;\n  padding: 5px;\n}\n\n.Row {\n  border: 2px dashed black;\n  padding: 5px;\n  margin: 5px;\n}\n```\n\n</Sandpack>\n\n`rows` は通常の JavaScript の配列なので、`RowList` コンポーネントは [`map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) のような組み込みの配列メソッドを使用できます。\n\nこのパターンは特に、子と一緒に構造化データとしてより多くの情報を渡したい場合に有用です。以下の例では、`TabSwitcher` コンポーネントは props である `tabs` 経由でオブジェクトの配列を受け取ります。\n\n<Sandpack>\n\n```js\nimport TabSwitcher from './TabSwitcher.js';\n\nexport default function App() {\n  return (\n    <TabSwitcher tabs={[\n      {\n        id: 'first',\n        header: 'First',\n        content: <p>This is the first item.</p>\n      },\n      {\n        id: 'second',\n        header: 'Second',\n        content: <p>This is the second item.</p>\n      },\n      {\n        id: 'third',\n        header: 'Third',\n        content: <p>This is the third item.</p>\n      }\n    ]} />\n  );\n}\n```\n\n```js src/TabSwitcher.js\nimport { useState } from 'react';\n\nexport default function TabSwitcher({ tabs }) {\n  const [selectedId, setSelectedId] = useState(tabs[0].id);\n  const selectedTab = tabs.find(tab => tab.id === selectedId);\n  return (\n    <>\n      {tabs.map(tab => (\n        <button\n          key={tab.id}\n          onClick={() => setSelectedId(tab.id)}\n        >\n          {tab.header}\n        </button>\n      ))}\n      <hr />\n      <div key={selectedId}>\n        <h3>{selectedTab.header}</h3>\n        {selectedTab.content}\n      </div>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\nJSX として子を渡すのとは異なり、このアプローチでは `header` のような追加のデータを各アイテムに関連付けることができます。`tabs` を直接操作しており、それは配列なので、`Children` メソッドは必要ありません。\n\n---\n\n### レンダープロップを呼び出してレンダーをカスタマイズする {/*calling-a-render-prop-to-customize-rendering*/}\n\nすべてのアイテムに対して JSX を生成しておく代わりに、JSX を返す関数を渡し、必要なときにその関数を呼び出してもらうこともできます。以下の例では、`App` コンポーネントは `renderContent` という関数を `TabSwitcher` コンポーネントに渡しています。`TabSwitcher` コンポーネントは選択中のタブのみに対して `renderContent` を呼び出します。\n\n<Sandpack>\n\n```js\nimport TabSwitcher from './TabSwitcher.js';\n\nexport default function App() {\n  return (\n    <TabSwitcher\n      tabIds={['first', 'second', 'third']}\n      getHeader={tabId => {\n        return tabId[0].toUpperCase() + tabId.slice(1);\n      }}\n      renderContent={tabId => {\n        return <p>This is the {tabId} item.</p>;\n      }}\n    />\n  );\n}\n```\n\n```js src/TabSwitcher.js\nimport { useState } from 'react';\n\nexport default function TabSwitcher({ tabIds, getHeader, renderContent }) {\n  const [selectedId, setSelectedId] = useState(tabIds[0]);\n  return (\n    <>\n      {tabIds.map((tabId) => (\n        <button\n          key={tabId}\n          onClick={() => setSelectedId(tabId)}\n        >\n          {getHeader(tabId)}\n        </button>\n      ))}\n      <hr />\n      <div key={selectedId}>\n        <h3>{getHeader(selectedId)}</h3>\n        {renderContent(selectedId)}\n      </div>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n`renderContent` のような props は、ユーザインターフェースの一部をどのようにレンダーするかを指定する props であるため、*レンダープロップ (render prop)* と呼ばれます。しかし、これについて特別なことは何もありません。たまたたま関数型であるというだけの通常の props に過ぎません。\n\nレンダープロップは関数なので、情報を渡すことができます。例えば、以下の `RowList` コンポーネントは、各行の `id` と `index` を `renderRow` というレンダープロップに渡し、`index` を使って偶数行をハイライトします。\n\n<Sandpack>\n\n```js\nimport { RowList, Row } from './RowList.js';\n\nexport default function App() {\n  return (\n    <RowList\n      rowIds={['first', 'second', 'third']}\n      renderRow={(id, index) => {\n        return (\n          <Row isHighlighted={index % 2 === 0}>\n            <p>This is the {id} item.</p>\n          </Row> \n        );\n      }}\n    />\n  );\n}\n```\n\n```js src/RowList.js\nimport { Fragment } from 'react';\n\nexport function RowList({ rowIds, renderRow }) {\n  return (\n    <div className=\"RowList\">\n      <h1 className=\"RowListHeader\">\n        Total rows: {rowIds.length}\n      </h1>\n      {rowIds.map((rowId, index) =>\n        <Fragment key={rowId}>\n          {renderRow(rowId, index)}\n        </Fragment>\n      )}\n    </div>\n  );\n}\n\nexport function Row({ children, isHighlighted }) {\n  return (\n    <div className={[\n      'Row',\n      isHighlighted ? 'RowHighlighted' : ''\n    ].join(' ')}>\n      {children}\n    </div>\n  );\n}\n```\n\n```css\n.RowList {\n  display: flex;\n  flex-direction: column;\n  border: 2px solid grey;\n  padding: 5px;\n}\n\n.RowListHeader {\n  padding-top: 5px;\n  font-size: 25px;\n  font-weight: bold;\n  text-align: center;\n}\n\n.Row {\n  border: 2px dashed black;\n  padding: 5px;\n  margin: 5px;\n}\n\n.RowHighlighted {\n  background: #ffa;\n}\n```\n\n</Sandpack>\n\nこのような方法でも、親コンポーネントと子コンポーネントが、子の操作を行わずに協調動作できるということです。\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### カスタムコンポーネントを渡しているが、`Children` メソッドがそのレンダー結果を表示しない {/*i-pass-a-custom-component-but-the-children-methods-dont-show-its-render-result*/}\n\n`RowList` に以下のように 2 つの子を渡すとします。\n\n```js\n<RowList>\n  <p>First item</p>\n  <MoreRows />\n</RowList>\n```\n\n`RowList` の中で `Children.count(children)` を行うと、結果は `2` になります。`MoreRows` が 10 の異なるアイテムをレンダーする場合でも、`null` を返す場合でも、`Children.count(children)` はやはり `2` になります。`RowList` の視点からは受け取った JSX のみが「見えて」いるからです。`MoreRows` コンポーネントの中身は「見えて」いません。\n\nこの制限はコンポーネントの抽出を困難にします。これが `Children` を使用するのではなく、[代替手段](#alternatives)を使用すべき理由です。\n"
  },
  {
    "path": "src/content/reference/react/Component.md",
    "content": "---\ntitle: Component\n---\n\n<Pitfall>\n\nクラスの代わりに関数でコンポーネントを定義することを推奨します。[移行方法はこちら](#alternatives)。\n\n</Pitfall>\n\n<Intro>\n\n`Component` は React コンポーネントの基底クラスであり、[JavaScript のクラス](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)として定義されています。React は現在でもクラスコンポーネントをサポートしていますが、新しいコードでの使用は推奨されません。\n\n```js\nclass Greeting extends Component {\n  render() {\n    return <h1>Hello, {this.props.name}!</h1>;\n  }\n}\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `Component` {/*component*/}\n\nクラスとして React コンポーネントを定義するには、組み込みの `Component` クラスを継承し、[`render` メソッド](#render)を定義します。\n\n```js\nimport { Component } from 'react';\n\nclass Greeting extends Component {\n  render() {\n    return <h1>Hello, {this.props.name}!</h1>;\n  }\n}\n```\n\n`render` メソッドのみが必須です。他のメソッドはオプションです。\n\n[さらに例を見る](#usage)\n\n---\n\n### `context` {/*context*/}\n\nクラスコンポーネントでは[コンテクスト](/learn/passing-data-deeply-with-context)を `this.context` の形で利用できます。これは、[`static contextType`](#static-contexttype) を使用して受け取りたいコンテクストを指定した場合にのみ利用できます。\n\nクラスコンポーネントは、一度に 1 種類のコンテクストしか読み取ることができません。\n\n```js {2,5}\nclass Button extends Component {\n  static contextType = ThemeContext;\n\n  render() {\n    const theme = this.context;\n    const className = 'button-' + theme;\n    return (\n      <button className={className}>\n        {this.props.children}\n      </button>\n    );\n  }\n}\n\n```\n\n<Note>\n\nクラスコンポーネントで `this.context` を読み取ることは、関数コンポーネントで [`useContext`](/reference/react/useContext) を用いることと同等です。\n\n[移行方法を見る](#migrating-a-component-with-context-from-a-class-to-a-function)\n\n</Note>\n\n---\n\n### `props` {/*props*/}\n\nクラスコンポーネントに渡された props は `this.props` の形で利用できます。\n\n```js {3}\nclass Greeting extends Component {\n  render() {\n    return <h1>Hello, {this.props.name}!</h1>;\n  }\n}\n\n<Greeting name=\"Taylor\" />\n```\n\n<Note>\n\nクラスコンポーネントで `this.props` を読み取ることは、関数コンポーネントで [props を宣言する](/learn/passing-props-to-a-component#step-2-read-props-inside-the-child-component)ことと同等です。\n\n[移行方法を見る](#migrating-a-simple-component-from-a-class-to-a-function)\n\n</Note>\n\n---\n\n### `state` {/*state*/}\n\nクラスコンポーネントの state は `this.state` の形で利用できます。`state` フィールドはオブジェクトでなければなりません。state を直接書き換えてはいけません。state を変更したい場合は、新しい state を引数にして `setState` を呼び出します。\n\n```js {2-4,7-9,18}\nclass Counter extends Component {\n  state = {\n    age: 42,\n  };\n\n  handleAgeChange = () => {\n    this.setState({\n      age: this.state.age + 1 \n    });\n  };\n\n  render() {\n    return (\n      <>\n        <button onClick={this.handleAgeChange}>\n        Increment age\n        </button>\n        <p>You are {this.state.age}.</p>\n      </>\n    );\n  }\n}\n```\n\n<Note>\n\nクラスコンポーネントで `state` を定義することは、関数コンポーネントで [`useState`](/reference/react/useState) を呼び出すことと同等です。\n\n[移行方法を見る](#migrating-a-component-with-state-from-a-class-to-a-function)\n\n</Note>\n\n---\n\n### `constructor(props)` {/*constructor*/}\n\n[コンストラクタ](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/constructor)は、クラスコンポーネントが*マウント*（画面に追加）される前に実行されます。通常 React では、コンストラクタは 2 つの目的でのみ利用されます。state の宣言と、クラスメソッドのクラスインスタンスへの[バインド](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind)です。\n\n```js {2-6}\nclass Counter extends Component {\n  constructor(props) {\n    super(props);\n    this.state = { counter: 0 };\n    this.handleClick = this.handleClick.bind(this);\n  }\n\n  handleClick() {\n    // ...\n  }\n```\n\nモダンな JavaScript 構文を使用している場合、コンストラクタはほとんど必要ありません。代わりに上記のコードは、モダンブラウザや [Babel](https://babeljs.io/) などのツールでサポートされている[パブリッククラスフィールド構文](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Public_class_fields)を使用して書き直すことができます。\n\n```js {2,4}\nclass Counter extends Component {\n  state = { counter: 0 };\n\n  handleClick = () => {\n    // ...\n  }\n```\n\nコンストラクタには副作用やサブスクリプション（subscription, イベント登録や外部データ購読）を一切含めてはいけません。\n\n#### 引数 {/*constructor-parameters*/}\n\n* `props`: コンポーネントの初期 props。\n\n#### 返り値 {/*constructor-returns*/}\n\n`constructor` は何も返してはいけません。\n\n#### 注意点 {/*constructor-caveats*/}\n\n* コンストラクタ内で副作用やサブスクリプションを実行しないでください。代わりに [`componentDidMount`](#componentdidmount) を使用してください。\n\n* コンストラクタ内では、他のすべてのステートメントの前に `super(props)` を呼び出す必要があります。これを行わないと、コンストラクタが実行されている間 `this.props` が `undefined` になるため、混乱を招きバグの原因となる可能性があります。\n\n* [`this.state`](#state) を直接セットして良い唯一の場所がコンストラクタです。他のすべてのメソッド内では、代わりに [`this.setState()`](#setstate) を使用する必要があります。コンストラクタ内で `setState` を呼び出さないでください。\n\n* [サーバレンダリング](/reference/react-dom/server)を使用する場合、コンストラクタもサーバ上で実行され、その後に [`render`](#render) メソッドが続きます。ただし、`componentDidMount` や `componentWillUnmount` のようなライフサイクルメソッドはサーバ上では実行されません。\n\n* [Strict Mode](/reference/react/StrictMode) が有効の場合、React は開発環境において `constructor` を 2 回呼び出し、2 つのインスタンスのうち 1 つを破棄します。これにより、`constructor` 外に移動するべき偶発的な副作用に気づきやすくなります。\n\n<Note>\n\n関数コンポーネントには `constructor` とまったく同等のものは存在しません。関数コンポーネントで state を宣言するには [`useState`](/reference/react/useState) を呼び出します。初期 state の再計算を避けたい場合は [`useState` に関数を渡します](/reference/react/useState#avoiding-recreating-the-initial-state)。\n\n</Note>\n\n---\n\n### `componentDidCatch(error, info)` {/*componentdidcatch*/}\n\n`componentDidCatch` を定義すると、子コンポーネント（遠くの子を含む）がレンダー中にエラーをスローしたときに React がそれを呼び出します。これにより、本番環境でそのエラーをエラーレポートサービスにログとして記録することができます。\n\n通常これは、エラーに反応して state を更新し、ユーザにエラーメッセージを表示するための [`static getDerivedStateFromError`](#static-getderivedstatefromerror) と一緒に使用されます。これらのメソッドを持つコンポーネントのことを*エラーバウンダリ (Error Boundary)* と呼びます。\n\n[例を見る](#catching-rendering-errors-with-an-error-boundary)\n\n#### 引数 {/*componentdidcatch-parameters*/}\n\n* `error`: スローされたエラー。現実的には通常 [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) のインスタンスになりますが、このことは保証されていません。JavaScript では文字列や `null` すら含む、任意の値を [`throw`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw) することが許されているためです。\n\n* `info`: エラーに関する追加情報を含むオブジェクト。その `componentStack` フィールドには、スローしたコンポーネントとそのすべての親コンポーネントの名前およびソース上の位置を含んだスタックトレースが含まれます。本番環境では、コンポーネント名はミニファイされています。本番環境用にエラーレポートを設定する場合は、通常の JavaScript エラースタックと同じように、ソースマップを使用してコンポーネントスタックをデコードできます。\n\n#### 返り値 {/*componentdidcatch-returns*/}\n\n`componentDidCatch` は何も返してはいけません。\n\n#### 注意点 {/*componentdidcatch-caveats*/}\n\n* 以前は、UI を更新してフォールバックのエラーメッセージを表示するために `componentDidCatch` 内で `setState` を呼び出すことが一般的でした。これは非推奨となり、代わりに [`static getDerivedStateFromError`](#static-getderivedstatefromerror) を定義することが推奨されています。\n\n* React の本番用ビルドと開発用ビルドでは、`componentDidCatch` がエラーを処理する方法がわずかに異なります。開発中は、エラーが `window` にまでバブルアップするため、`window.onerror` や `window.addEventListener('error', callback)` といったコードが `componentDidCatch` によってキャッチされたエラーを捕まえることができます。一方で本番環境ではエラーはバブルアップしないため、祖先のエラーハンドラは `componentDidCatch` によって明示的にキャッチされなかったエラーのみを受け取ります。\n\n<Note>\n\n関数コンポーネントには `componentDidCatch` に直接対応するものはまだありません。クラスコンポーネントの作成をなるべく避けたい場合は、上記のような `ErrorBoundary` コンポーネントを 1 つだけ書いてアプリ全体で使用します。あるいはこれを代わりにやってくれる [`react-error-boundary`](https://github.com/bvaughn/react-error-boundary) パッケージを使用することもできます。\n\n</Note>\n\n---\n\n### `componentDidMount()` {/*componentdidmount*/}\n\n`componentDidMount` メソッドを定義すると、コンポーネントが画面に追加（*マウント*）されたときに React がそれを呼び出します。一般的にはここで、データ取得、サブスクリプション設定や DOM ノードの操作を開始します。\n\n`componentDidMount` を実装する場合、通常はバグを避けるために他のライフサイクルメソッドも実装する必要があります。例えば、`componentDidMount` が何らかの state や props を読み取る場合はそれらに変更があった場合に処理するために [`componentDidUpdate`](#componentdidupdate) を実装する必要があり、`componentDidMount` が実行したことをクリーンアップするためには [`componentWillUnmount`](#componentwillunmount) を実装する必要があります。\n\n```js {6-8}\nclass ChatRoom extends Component {\n  state = {\n    serverUrl: 'https://localhost:1234'\n  };\n\n  componentDidMount() {\n    this.setupConnection();\n  }\n\n  componentDidUpdate(prevProps, prevState) {\n    if (\n      this.props.roomId !== prevProps.roomId ||\n      this.state.serverUrl !== prevState.serverUrl\n    ) {\n      this.destroyConnection();\n      this.setupConnection();\n    }\n  }\n\n  componentWillUnmount() {\n    this.destroyConnection();\n  }\n\n  // ...\n}\n```\n\n[他の例を見る](#adding-lifecycle-methods-to-a-class-component)\n\n#### 引数 {/*componentdidmount-parameters*/}\n\n`componentDidMount` は引数を受け取りません。\n\n#### 返り値 {/*componentdidmount-returns*/}\n\n`componentDidMount` は何も返してはいけません。\n\n#### 注意点 {/*componentdidmount-caveats*/}\n\n- [Strict Mode](/reference/react/StrictMode) がオンの場合、React は開発中に `componentDidMount` を呼び出し、直後に [`componentWillUnmount`](#componentwillunmount) を呼び出し、そして再度 `componentDidMount` を呼び出します。これにより、`componentWillUnmount` の実装を忘れた場合や、そのロジックが `componentDidMount` の挙動と正しく「鏡のように対応」していない場合に気づきやすくなります。\n\n- `componentDidMount` の中で直ちに [`setState`](#setstate) を呼び出すことは可能ですが、可能な限り避けるべきです。これは追加のレンダーを引き起こしますが、ブラウザが画面を更新する前に発生します。このため、このケースでは [`render`](#render) が 2 回呼び出されはしますが、ユーザは中途半端な state を見ずに済みます。このパターンはしばしばパフォーマンスの問題を引き起こすため、注意して使用してください。ほとんどの場合、初期 state を [`constructor`](#constructor) で代入できるはずです。ただし、モーダルやツールチップのような場合は、何らかの DOM ノードの位置やサイズに依存する要素をレンダーするのに DOM ノードの測定を行うため、これが必要になることがあります。\n\n<Note>\n\n多くのユースケースにおいて、クラスコンポーネントで `componentDidMount`、`componentDidUpdate`、`componentWillUnmount` をまとめて定義することは、関数コンポーネントで [`useEffect`](/reference/react/useEffect) を呼び出すことと同等です。ブラウザの描画前にコードを実行することが重要となる稀なケースでは、[`useLayoutEffect`](/reference/react/useLayoutEffect) がより近いものになります。\n\n[移行方法を見る](#migrating-a-component-with-lifecycle-methods-from-a-class-to-a-function)\n\n</Note>\n\n---\n\n### `componentDidUpdate(prevProps, prevState, snapshot?)` {/*componentdidupdate*/}\n\n`componentDidUpdate` メソッドを定義すると、コンポーネントが更新後の props や state で再レンダーされた直後に React がそれを呼び出します。このメソッドは初回レンダーでは呼び出されません。\n\n更新後に DOM を操作するためにこれを使用することができます。これはまた、ネットワークリクエストを行う一般的な場所でもありますが、現在の props を前の props と比較する必要があります（例えば props が変更されていない場合、新しいネットワークリクエストは必要ないかもしれません）。通常、[`componentDidMount`](#componentdidmount) や [`componentWillUnmount`](#componentwillunmount) と一緒に使用します。\n\n```js {10-18}\nclass ChatRoom extends Component {\n  state = {\n    serverUrl: 'https://localhost:1234'\n  };\n\n  componentDidMount() {\n    this.setupConnection();\n  }\n\n  componentDidUpdate(prevProps, prevState) {\n    if (\n      this.props.roomId !== prevProps.roomId ||\n      this.state.serverUrl !== prevState.serverUrl\n    ) {\n      this.destroyConnection();\n      this.setupConnection();\n    }\n  }\n\n  componentWillUnmount() {\n    this.destroyConnection();\n  }\n\n  // ...\n}\n```\n\n[他の例を見る](#adding-lifecycle-methods-to-a-class-component)\n\n\n#### 引数 {/*componentdidupdate-parameters*/}\n\n* `prevProps`: 更新前の props。`prevProps` と [`this.props`](#props) を比較して何が変わったかを判断します。\n\n* `prevState`: 更新前の state。`prevState` と [`this.state`](#state) を比較して何が変わったかを判断します。\n\n* `snapshot`: [`getSnapshotBeforeUpdate`](#getsnapshotbeforeupdate) を実装した場合、`snapshot` にはそのメソッドから返された値が含まれます。それ以外の場合は `undefined` になります。\n\n#### 返り値 {/*componentdidupdate-returns*/}\n\n`componentDidUpdate` は何も返してはいけません。\n\n#### 注意点 {/*componentdidupdate-caveats*/}\n\n- [`shouldComponentUpdate`](#shouldcomponentupdate) が定義されており、それが `false` を返す場合、`componentDidUpdate` は呼び出されません。\n\n- `componentDidUpdate` 内のロジックは通常、`this.props` と `prevProps`、`this.state` と `prevState` を比較する条件文でラップする必要があります。そうでなければ、無限ループを作り出すリスクがあります。\n\n- `componentDidUpdate` の中で直ちに [`setState`](#setstate) を呼び出すことは可能ですが、可能な限り避けるべきです。これは追加のレンダーを引き起こしますが、ブラウザが画面を更新する前に発生します。このため、このケースでは [`render`](#render) が 2 回呼び出されはしますが、ユーザは中途半端な state を見ずに済みます。このパターンはしばしばパフォーマンスの問題を引き起こします。ただし、モーダルやツールチップのようなレアなケースでは、何らかの DOM ノードの位置やサイズに依存する要素をレンダーするのに DOM ノードの測定を行うため、これが必要になることがあります。\n\n<Note>\n\n多くのユースケースにおいて、クラスコンポーネントで `componentDidMount`、`componentDidUpdate`、`componentWillUnmount` をまとめて定義することは、関数コンポーネントで [`useEffect`](/reference/react/useEffect) を呼び出すことと同等です。ブラウザの描画前にコードを実行することが重要となる稀なケースでは、[`useLayoutEffect`](/reference/react/useLayoutEffect) がより近いものになります。\n\n[移行方法を見る](#migrating-a-component-with-lifecycle-methods-from-a-class-to-a-function)\n\n</Note>\n---\n\n### `componentWillMount()` {/*componentwillmount*/}\n\n<Deprecated>\n\nこの API は `componentWillMount` から [`UNSAFE_componentWillMount`](#unsafe_componentwillmount) に名前が変更されました。古い名前は非推奨となりました。React の将来のメジャーバージョンでは、新しい名前のみが機能します。\n\n自動的にコンポーネントを更新するには [`rename-unsafe-lifecycles` codemod](https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles) を実行してください。\n\n</Deprecated>\n\n---\n\n### `componentWillReceiveProps(nextProps)` {/*componentwillreceiveprops*/}\n\n<Deprecated>\n\nこの API は `componentWillReceiveProps` から [`UNSAFE_componentWillReceiveProps`](#unsafe_componentwillreceiveprops) に名前が変更されました。古い名前は非推奨となりました。React の将来のメジャーバージョンでは、新しい名前のみが機能します。\n\n自動的にコンポーネントを更新するには [`rename-unsafe-lifecycles` codemod](https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles) を実行してください。\n\n</Deprecated>\n\n---\n\n### `componentWillUpdate(nextProps, nextState)` {/*componentwillupdate*/}\n\n<Deprecated>\n\nこの API は `componentWillUpdate` から [`UNSAFE_componentWillUpdate`](#unsafe_componentwillupdate) に名前が変更されました。古い名前は非推奨となりました。React の将来のメジャーバージョンでは、新しい名前のみが機能します。\n\n自動的にコンポーネントを更新するには [`rename-unsafe-lifecycles` codemod](https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles) を実行してください。\n\n</Deprecated>\n\n---\n\n### `componentWillUnmount()` {/*componentwillunmount*/}\n\n`componentWillUnmount` メソッドを定義すると、React はコンポーネントが画面から削除（アンマウント）される前にこれを呼び出します。ここがデータの取得をキャンセルしたり、サブスクリプションを削除するのに一般的な場所です。\n\n`componentWillUnmount` 内のロジックは [`componentDidMount`](#componentdidmount) 内のロジックと「鏡のように対応」するべきです。例えば、`componentDidMount` がサブスクリプションの設定を行う場合、`componentWillUnmount` はそのサブスクリプションをクリーンアップするべきです。`componentWillUnmount` のクリーンアップロジックが props や state を読み取る場合、通常は古い props と state に対応するリソース（サブスクリプションなど）をクリーンアップするために [`componentDidUpdate`](#componentdidupdate) も実装する必要があります。\n\n```js {20-22}\nclass ChatRoom extends Component {\n  state = {\n    serverUrl: 'https://localhost:1234'\n  };\n\n  componentDidMount() {\n    this.setupConnection();\n  }\n\n  componentDidUpdate(prevProps, prevState) {\n    if (\n      this.props.roomId !== prevProps.roomId ||\n      this.state.serverUrl !== prevState.serverUrl\n    ) {\n      this.destroyConnection();\n      this.setupConnection();\n    }\n  }\n\n  componentWillUnmount() {\n    this.destroyConnection();\n  }\n\n  // ...\n}\n```\n\n[他の例を見る](#adding-lifecycle-methods-to-a-class-component)\n\n#### 引数 {/*componentwillunmount-parameters*/}\n\n`componentWillUnmount` は引数を受け取りません。\n\n#### 返り値 {/*componentwillunmount-returns*/}\n\n`componentWillUnmount` は何も返してはいけません。\n\n#### 注意点 {/*componentwillunmount-caveats*/}\n\n- [Strict Mode](/reference/react/StrictMode) がオンの場合、React は開発中に [`componentDidMount`](#componentdidmount) を呼び出し、直後に `componentWillUnmount` を呼び出し、そして再度 `componentDidMount` を呼び出します。これにより、`componentWillUnmount` の実装を忘れた場合や、そのロジックが `componentDidMount` の挙動と正しく「鏡のように対応」していない場合に気づきやすくなります。\n\n<Note>\n\n多くのユースケースにおいて、クラスコンポーネントで `componentDidMount`、`componentDidUpdate`、`componentWillUnmount` をまとめて定義することは、関数コンポーネントで [`useEffect`](/reference/react/useEffect) を呼び出すことと同等です。ブラウザの描画前にコードを実行することが重要となる稀なケースでは、[`useLayoutEffect`](/reference/react/useLayoutEffect) がより近いものになります。\n\n[移行方法を見る](#migrating-a-component-with-lifecycle-methods-from-a-class-to-a-function)\n\n</Note>\n\n---\n\n### `forceUpdate(callback?)` {/*forceupdate*/}\n\nコンポーネントを強制的に再レンダーします。\n\n通常、これは必要ありません。コンポーネントの [`render`](#render) メソッドが [`this.props`](#props)、[`this.state`](#state) および [`this.context`](#context) からのみ読み取りを行う場合、コンポーネント内またはその親で [`setState`](#setstate) が呼び出されると自動的に再レンダーが発生します。しかし、コンポーネントの `render` メソッドが外部データソースから直接読み取りを行っている場合、そのデータソースが変更されたときに React にユーザインターフェースを更新するように指示する必要があります。`forceUpdate` はそれを行えるようにするためのものです。\n\nあらゆる `forceUpdate` の使用は避け、`render` 内では `this.props` と `this.state` からのみ読み取るようにしてください。\n\n#### 引数 {/*forceupdate-parameters*/}\n\n* **省略可能** `callback`: 指定された場合、React は更新がコミットされた後に、渡された `callback` を呼び出します。\n\n#### 返り値 {/*forceupdate-returns*/}\n\n`forceUpdate` は何も返しません。\n\n#### 注意点 {/*forceupdate-caveats*/}\n\n- `forceUpdate` を呼び出すと、React は [`shouldComponentUpdate`](#shouldcomponentupdate) を呼び出さずに再レンダーします。\n\n<Note>\n\n外部データソースを読み込んで変更に応じて `forceUpdate` でクラスコンポーネントを再レンダーする代わりに、関数コンポーネントにおいては [`useSyncExternalStore`](/reference/react/useSyncExternalStore) を使うようになりました。\n\n</Note>\n\n---\n\n### `getSnapshotBeforeUpdate(prevProps, prevState)` {/*getsnapshotbeforeupdate*/}\n\n`getSnapshotBeforeUpdate` を実装すると、React は DOM を更新する直前にそれを呼び出します。これにより、コンポーネントは DOM から情報（例えばスクロール位置）を取得することができます。このライフサイクルメソッドが返すあらゆる値は、[`componentDidUpdate`](#componentdidupdate) に引数として渡されます。\n\n例えば、更新間でスクロール位置を保持する必要があるチャットスレッドのような UI でこれを使用することができます。\n\n```js {7-15,17}\nclass ScrollingList extends React.Component {\n  constructor(props) {\n    super(props);\n    this.listRef = React.createRef();\n  }\n\n  getSnapshotBeforeUpdate(prevProps, prevState) {\n    // Are we adding new items to the list?\n    // Capture the scroll position so we can adjust scroll later.\n    if (prevProps.list.length < this.props.list.length) {\n      const list = this.listRef.current;\n      return list.scrollHeight - list.scrollTop;\n    }\n    return null;\n  }\n\n  componentDidUpdate(prevProps, prevState, snapshot) {\n    // If we have a snapshot value, we've just added new items.\n    // Adjust scroll so these new items don't push the old ones out of view.\n    // (snapshot here is the value returned from getSnapshotBeforeUpdate)\n    if (snapshot !== null) {\n      const list = this.listRef.current;\n      list.scrollTop = list.scrollHeight - snapshot;\n    }\n  }\n\n  render() {\n    return (\n      <div ref={this.listRef}>{/* ...contents... */}</div>\n    );\n  }\n}\n```\n\n上記の例では、`getSnapshotBeforeUpdate` で直接 `scrollHeight` プロパティを読み取ることが重要です。[`render`](#render)、[`UNSAFE_componentWillReceiveProps`](#unsafe_componentwillreceiveprops)、または [`UNSAFE_componentWillUpdate`](#unsafe_componentwillupdate) で読み取ることは安全ではありません。これらのメソッドが呼び出されてから React が DOM を更新するまでに時間差がある可能性があるためです。\n\n#### 引数 {/*getsnapshotbeforeupdate-parameters*/}\n\n* `prevProps`: 更新前の props。`prevProps` と [`this.props`](#props) を比較して何が変わったかを判断します。\n\n* `prevState`: 更新前の state。`prevState` と [`this.state`](#state) を比較して何が変わったかを判断します。\n\n#### 返り値 {/*getsnapshotbeforeupdate-returns*/}\n\n任意の型のスナップショット値、または `null` を返してください。返した値は、[`componentDidUpdate`](#componentdidupdate) の第 3 引数として渡されます。\n\n#### 注意点 {/*getsnapshotbeforeupdate-caveats*/}\n\n- [`shouldComponentUpdate`](#shouldcomponentupdate) が定義されており、`false` を返す場合、`getSnapshotBeforeUpdate` は呼び出されません。\n\n<Note>\n\n現時点では、関数コンポーネントには `getSnapshotBeforeUpdate` と同等のものはありません。このユースケースは非常に珍しいものですが、これがどうしても必要な場合、現時点ではクラスコンポーネントを書く必要があります。\n\n</Note>\n\n---\n\n### `render()` {/*render*/}\n\n`render` メソッドは、クラスコンポーネントにおける唯一の必須メソッドです。\n\n`render` メソッドは、画面に表示したいものを指定します。例えば：\n\n```js {4-6}\nimport { Component } from 'react';\n\nclass Greeting extends Component {\n  render() {\n    return <h1>Hello, {this.props.name}!</h1>;\n  }\n}\n```\n\nReact はあらゆるタイミングで `render` を呼び出す可能性があるため、特定の時間に実行されることを前提にしてはいけません。通常、`render` メソッドは [JSX](/learn/writing-markup-with-jsx) を返すべきですが、いくつかの[他の型の返り値](#render-returns)（文字列など）もサポートされています。返すべき JSX を計算するために、`render` メソッドは [`this.props`](#props)、[`this.state`](#state)、および [`this.context`](#context) を読み取ることができます。\n\n`render` メソッドは純関数として書くべきです。つまり、props、state、コンテクストが同じであれば同じ結果を返すべきです。また、副作用（サブスクリプションの設定など）を含んだり、ブラウザの API とやり取りしたりするべきではありません。副作用は、イベントハンドラや [`componentDidMount`](#componentdidmount) のようなメソッド内で行うべきです。\n\n#### 引数 {/*render-parameters*/}\n\n`render` は引数を受け取りません。\n\n#### 返り値 {/*render-returns*/}\n\n`render` はあらゆる有効な React ノードを返すことができます。これには、`<div />` のような React 要素、文字列、数値、[ポータル](/reference/react-dom/createPortal)、空のノード（`null`、`undefined`、`true`、`false`）、および React ノードの配列が含まれます。\n\n#### 注意点 {/*render-caveats*/}\n\n- `render` は props、state、コンテクストに対する純関数として書くべきです。副作用を持ってはいけません。\n\n- [`shouldComponentUpdate`](#shouldcomponentupdate) が定義されており `false` を返す場合、`render` は呼び出されません。\n\n- [Strict Mode](/reference/react/StrictMode) が有効の場合、React は開発中に `render` を 2 回呼び出し、そのうちの 1 つの結果を破棄します。これにより、`render` メソッドの外に移動するべき偶発的な副作用に気づきやすくなります。\n\n- `render` の呼び出しとその後の `componentDidMount` や `componentDidUpdate` の呼び出しとの間に一対一の対応関係はありません。React が必要と判断した場合、`render` の呼び出し結果の一部は破棄される可能性があります。\n\n---\n\n### `setState(nextState, callback?)` {/*setstate*/}\n\n`setState` の呼び出しは React コンポーネントの state の更新を行います。\n\n```js {8-10}\nclass Form extends Component {\n  state = {\n    name: 'Taylor',\n  };\n\n  handleNameChange = (e) => {\n    const newName = e.target.value;\n    this.setState({\n      name: newName\n    });\n  }\n\n  render() {\n    return (\n      <>\n        <input value={this.state.name} onChange={this.handleNameChange} />\n        <p>Hello, {this.state.name}.</p>\n      </>\n    );\n  }\n}\n```\n\n`setState` はコンポーネントの state への更新をキューに入れます。これは、このコンポーネントとその子を新しい state で再レンダーする必要があることを React に伝えます。これが、ユーザ操作に対応してユーザインターフェースを更新するための主要な方法となります。\n\n<Pitfall>\n\n`setState` を呼び出しても、すでに実行中のコードの現在の state は**変更されません**。\n\n```js {6}\nfunction handleClick() {\n  console.log(this.state.name); // \"Taylor\"\n  this.setState({\n    name: 'Robin'\n  });\n  console.log(this.state.name); // Still \"Taylor\"!\n}\n```\n\nこれは、*次回の*レンダーから `this.state` が返すものにのみ影響します。\n\n</Pitfall>\n\nまた、`setState` に関数を渡すこともできます。これにより、前の state に基づいて state を更新することができます。\n\n```js {2-6}\n  handleIncreaseAge = () => {\n    this.setState(prevState => {\n      return {\n        age: prevState.age + 1\n      };\n    });\n  }\n```\n\nこうする必要があるわけではありませんが、同じイベント中に複数回 state を更新したい場合には有用です。\n\n#### 引数 {/*setstate-parameters*/}\n\n* `nextState`：オブジェクトまたは関数。\n  * `nextState` としてオブジェクトを渡すと、それは `this.state` に浅く (shallow) マージされます。\n  * `nextState` として関数を渡すと、それは*更新用関数 (updater function)* として扱われます。その関数は純関数でなければならず、state と props の現在値を引数として取り、`this.state` に浅くマージされるためのオブジェクトを返します。React はこの更新用関数をキューに入れてからコンポーネントを再レンダーします。次のレンダー中に、React は前の state に対してキューに入れられたすべての更新用関数を適用し、次回の state を計算します。\n\n* **省略可能** `callback`: 指定された場合、React は更新がコミットされた後に渡された `callback` を呼び出します。\n\n#### 返り値 {/*setstate-returns*/}\n\n`setState` は何も返しません。\n\n#### 注意点 {/*setstate-caveats*/}\n\n- `setState` は即時的なコンポーネントの更新命令ではなく、*リクエスト*だと考えてください。複数のコンポーネントがイベントに応じて state を更新するとき、React はそれらの更新をバッチ（束ね）処理し、イベントの終了時に 1 度だけ再レンダーします。特定の state 更新を強制的に同期的に適用する必要がある稀なケースでは、[`flushSync`](/reference/react-dom/flushSync) でラップすることができますが、これはパフォーマンスを低下させる可能性があります。\n\n- `setState` は `this.state` を即時に更新しません。このため、`setState` を呼び出した直後に `this.state` を読み取ることは潜在的な落とし穴となります。代わりに、[`componentDidUpdate`](#componentdidupdate) または setState の `callback` 引数を使用してください。どちらも更新が適用された後に発火することが保証されています。前の state に基づいた state を設定する必要がある場合、上記で説明したように `nextState` として関数を渡すことができます。\n\n<Note>\n\nクラスコンポーネントで `setState` を呼び出すことは、関数コンポーネントで [`set` 関数](/reference/react/useState#setstate)を呼び出すことに似ています。\n\n[移行方法を見る](#migrating-a-component-with-state-from-a-class-to-a-function)\n\n</Note>\n\n---\n\n### `shouldComponentUpdate(nextProps, nextState, nextContext)` {/*shouldcomponentupdate*/}\n\n`shouldComponentUpdate` を定義すると、React は再レンダーをスキップできるかどうかを判断するためにそれを呼び出します。\n\nこれを本当に手動で書きたい場合は、`this.props` と `nextProps`、`this.state` と `nextState` を比較した上で `false` を返すことで、更新をスキップできると React に伝えることができます。\n\n```js {6-18}\nclass Rectangle extends Component {\n  state = {\n    isHovered: false\n  };\n\n  shouldComponentUpdate(nextProps, nextState) {\n    if (\n      nextProps.position.x === this.props.position.x &&\n      nextProps.position.y === this.props.position.y &&\n      nextProps.size.width === this.props.size.width &&\n      nextProps.size.height === this.props.size.height &&\n      nextState.isHovered === this.state.isHovered\n    ) {\n      // Nothing has changed, so a re-render is unnecessary\n      return false;\n    }\n    return true;\n  }\n\n  // ...\n}\n\n```\n\n新しい props や state を受け取る際に React は `shouldComponentUpdate` を呼び出します。デフォルトは `true` です。このメソッドは初回レンダーの場合と [`forceUpdate`](#forceupdate) が使用された場合には呼び出されません。\n\n#### 引数 {/*shouldcomponentupdate-parameters*/}\n\n- `nextProps`: コンポーネントがレンダーしようとしている次の props。何が変わったかを判断するために `nextProps` を [`this.props`](#props) と比較します。\n- `nextState`: コンポーネントがレンダーしようとしている次の state。何が変わったかを判断するために `nextState` を [`this.state`](#props) と比較します。\n- `nextContext`: コンポーネントがレンダーしようとしている次のコンテクスト。何が変わったかを判断するために `nextContext` を [`this.context`](#context) と比較します。[`static contextType`](#static-contexttype) を指定した場合のみ利用可能です。\n\n#### 返り値 {/*shouldcomponentupdate-returns*/}\n\nコンポーネントを再レンダーしたい場合は `true` を返します。これがデフォルトの挙動です。\n\n再レンダーがスキップ可能であると React に伝えるためには `false` を返します。\n\n#### 注意点 {/*shouldcomponentupdate-caveats*/}\n\n- このメソッドは*パフォーマンス最適化のためだけ*に存在しています。もし、このメソッドがないとコンポーネントが壊れる場合は、まずそれを修正してください。\n\n- `shouldComponentUpdate` を手書きする代わりに、[`PureComponent`](/reference/react/PureComponent) の使用を検討してください。`PureComponent` は props と state を浅く比較し、必要な更新までスキップしてしまう可能性を減らします。\n\n- `shouldComponentUpdate` で深い等価性チェックを行ったり、`JSON.stringify` を使用したりすることはお勧めしません。これによりパフォーマンスがあらゆる props と state のデータ構造に依存するようになり、予測不可能になります。よくてもアプリケーションが数秒間フリーズするリスクがあり、最悪の場合はクラッシュする危険があります。\n\n- `false` を返しても、*子コンポーネントの* state が変更された場合に子コンポーネントの再レンダーが抑止されるわけではありません。\n\n- `false` を返しても、コンポーネントが再レンダーされないことが*保証*されるわけではありません。React は返り値をヒントとして使用しますが、他の理由でコンポーネントを再レンダーすることが理にかなっていると判断した場合、再レンダーを行うことがあります。\n\n<Note>\n\nクラスコンポーネントを `shouldComponentUpdate` で最適化することは、関数コンポーネントを [`memo` で最適化すること](/reference/react/memo)と似ています。関数コンポーネントでは [`useMemo`](/reference/react/useMemo) を使用することで、さらに細かい最適化も行えます。\n\n</Note>\n\n---\n\n### `UNSAFE_componentWillMount()` {/*unsafe_componentwillmount*/}\n\n`UNSAFE_componentWillMount` を定義すると、React は [`constructor` の直後に](#constructor)それを呼び出します。これは歴史的な理由で存在するものであり、新しいコードでは使用すべきではありません。代わりに以下の代替手段のいずれかを使用してください。\n\n- state を初期化するためには、[`state` をクラスフィールドとして宣言する](#state)か、[`constructor`](#constructor) で `this.state` を設定します。\n- 副作用を実行する必要があるか、サブスクリプションを設定する必要がある場合は、そのロジックを [`componentDidMount`](#componentdidmount) に移動します。\n\n[安全でないライフサイクルからの移行例を見る](https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#examples)\n\n#### 引数 {/*unsafe_componentwillmount-parameters*/}\n\n`UNSAFE_componentWillMount` は引数を受け取りません。\n\n#### 返り値 {/*unsafe_componentwillmount-returns*/}\n\n`UNSAFE_componentWillMount` は何も返してはいけません。\n\n#### 注意点 {/*unsafe_componentwillmount-caveats*/}\n\n- コンポーネントが [`static getDerivedStateFromProps`](#static-getderivedstatefromprops) または [`getSnapshotBeforeUpdate`](#getsnapshotbeforeupdate) を実装している場合、`UNSAFE_componentWillMount` は呼び出されません。\n\n- [`Suspense`](/reference/react/Suspense) のようなモダンな React 機能を使用しているアプリでは、`UNSAFE_componentWillMount` はその名前に反して、コンポーネントが*本当にマウント*されることを保証しません。（例えば子コンポーネントのコードがまだロードされていないなどの理由で）レンダーの試行がサスペンドした場合、React は進行中のツリーを破棄し、次の試行にてコンポーネントをゼロから構築しようとします。これがこのメソッドが \"unsafe\" となっている理由です。実際にマウントされたという事実に依存するコード（サブスクリプションの追加など）は [`componentDidMount`](#componentdidmount) に入れるべきです。\n\n- `UNSAFE_componentWillMount` は[サーバレンダリング](/reference/react-dom/server)中に実行される唯一のライフサイクルメソッドです。現実的にはあらゆる利用目的においてこれは [`constructor`](#constructor) と同一であるため、このタイプのロジックには `constructor` を使用すべきです。\n\n<Note>\n\nクラスコンポーネントの `UNSAFE_componentWillMount` 内で state を初期化するために [`setState`](#setstate) を呼び出すことは、関数コンポーネントの [`useState`](/reference/react/useState) に初期 state を渡すことと同等です。\n\n</Note>\n\n---\n\n### `UNSAFE_componentWillReceiveProps(nextProps, nextContext)` {/*unsafe_componentwillreceiveprops*/}\n\n`UNSAFE_componentWillReceiveProps` を定義すると、コンポーネントが新しい props を受け取るときに React がそれを呼び出します。これは歴史的な理由で存在するものであり、新しいコードでは使用すべきではありません。代わりに以下の代替手段のいずれかを使用してください。\n\n- props の変更に応じて**副作用を実行**する必要がある場合（例えば、データの取得、アニメーションの実行、またはサブスクリプションの再初期化）、そのロジックを代わりに [`componentDidUpdate`](#componentdidupdate) に移動してください。\n- 一部の props が変更されたときに**あるデータの再計算を避ける**必要がある場合、代わりに[メモ化ヘルパー](https://legacy.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#what-about-memoization)を使用してください。\n- props が変更されたときに**ある state を「リセット」する**必要がある場合、コンポーネントを[完全に制御されたコンポーネント](https://legacy.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-controlled-component)にするか、[key 付きの非制御コンポーネント](https://legacy.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key)にすることを検討してください。\n- props が変更されたときに**ある state を「調整」する**必要がある場合、レンダー中に props のみから必要な情報をすべて計算できないか確認してください。できない場合は、代わりに [`static getDerivedStateFromProps`](/reference/react/Component#static-getderivedstatefromprops) を使用してください。\n\n[安全でないライフサイクルからの移行の例を見る](https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props)\n\n#### 引数 {/*unsafe_componentwillreceiveprops-parameters*/}\n\n- `nextProps`: コンポーネントが親コンポーネントから受け取ろうとしている次の props。何が変更されたかを判断するために `nextProps` を [`this.props`](#props) と比較します。\n- `nextContext`: コンポーネントが最も近いプロバイダから受け取ろうとしている次のコンテクスト。何が変更されたかを判断するために `nextContext` を [`this.context`](#context) と比較します。[`static contextType`](#static-contexttype) を指定した場合のみ利用可能です。\n\n#### 返り値 {/*unsafe_componentwillreceiveprops-returns*/}\n\n`UNSAFE_componentWillReceiveProps` は何も返してはいけません。\n\n#### 注意点 {/*unsafe_componentwillreceiveprops-caveats*/}\n\n- [`static getDerivedStateFromProps`](#static-getderivedstatefromprops) または [`getSnapshotBeforeUpdate`](#getsnapshotbeforeupdate) を実装しているコンポーネントでは、`UNSAFE_componentWillReceiveProps` は呼び出されません。\n\n- [`Suspense`](/reference/react/Suspense) のようなモダンな React 機能を使用しているアプリでは、`UNSAFE_componentWillReceiveProps` はその名前に反して、コンポーネントがその props を*本当に受け取る*ことを保証しません。（例えば子コンポーネントのコードがまだロードされていないなどの理由で）レンダーの試行がサスペンドした場合、React は進行中のツリーを破棄し、次の試行にてコンポーネントをゼロから構築しようとします。次のレンダーの試行の時点では、props は異なっている可能性があります。これがこのメソッドが \"unsafe\" となっている理由です。コミットされた更新のみに対して実行すべきコード（サブスクリプションのリセットなど）は、[`componentDidUpdate`](#componentdidupdate) に入れるべきです。\n\n- `UNSAFE_componentWillReceiveProps` が呼ばれてもコンポーネントが前回と*異なる* props を受け取ったことを意味するものではありません。何かが変わったかどうかを確認するには `nextProps` と `this.props` を自分で比較する必要があります。\n\n- React はマウント時に `UNSAFE_componentWillReceiveProps` を初期 props を引数に呼び出すことはありません。このメソッドは、コンポーネントの props の一部が更新される予定の場合にのみ呼び出されます。例えば、[`setState`](#setstate) を呼び出しても、一般的には同じコンポーネント内の `UNSAFE_componentWillReceiveProps` はトリガされません。\n\n<Note>\n\nクラスコンポーネントで `UNSAFE_componentWillReceiveProps` 内部で [`setState`](#setstate) を呼び出して state を「調整」することは、関数コンポーネントで[レンダー中に `useState` の `set` 関数を呼び出す](/reference/react/useState#storing-information-from-previous-renders) ことと同等です。\n\n</Note>\n\n---\n\n### `UNSAFE_componentWillUpdate(nextProps, nextState)` {/*unsafe_componentwillupdate*/}\n\n\n`UNSAFE_componentWillUpdate` を定義すると、新しい props または state でレンダーする前に React がそれを呼び出します。これは歴史的な理由で存在するものであり、新しいコードでは使用すべきではありません。代わりに以下の代替手段のいずれかを使用してください。\n\n- prop または state の変更に応じて副作用（例えば、データの取得、アニメーションの実行、サブスクリプションの再初期化など）を実行する必要がある場合、そのロジックを [`componentDidUpdate`](#componentdidupdate) に移動してください。\n- DOM から情報を読み取り（例えば、現在のスクロール位置を保存するなど）、それを後で [`componentDidUpdate`](#componentdidupdate) で使用する場合は、[`getSnapshotBeforeUpdate`](#getsnapshotbeforeupdate) 内で読み取るようにしてください。\n\n[安全でないライフサイクルからの移行例を見る](https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#examples)\n\n#### 引数 {/*unsafe_componentwillupdate-parameters*/}\n\n- `nextProps`: コンポーネントが次にレンダーする予定の props。何が変わったかを判断するために、`nextProps` と [`this.props`](#props) を比較します。\n- `nextState`: コンポーネントが次にレンダーする予定の state。何が変わったかを判断するために、`nextState` と [`this.state`](#state) を比較します。\n\n#### 返り値 {/*unsafe_componentwillupdate-returns*/}\n\n`UNSAFE_componentWillUpdate` は何も返してはいけません。\n\n#### 注意点 {/*unsafe_componentwillupdate-caveats*/}\n\n- [`shouldComponentUpdate`](#shouldcomponentupdate) が定義されており `false` を返す場合、`UNSAFE_componentWillUpdate` は呼び出されません。\n\n- コンポーネントが [`static getDerivedStateFromProps`](#static-getderivedstatefromprops) または [`getSnapshotBeforeUpdate`](#getsnapshotbeforeupdate) を実装している場合、`UNSAFE_componentWillUpdate` は呼び出されません。\n\n- `componentWillUpdate` の呼び出し中に [`setState`](#setstate)（または最終的に `setState` が呼び出されるようなあらゆるメソッド、例えば Redux アクションのディスパッチ）を呼び出すことはサポートされていません。\n\n- [`Suspense`](/reference/react/Suspense) のようなモダンな React 機能を使用しているアプリでは、`UNSAFE_componentWillUpdate` はその名前に反して、コンポーネントが*本当に更新される*ことを保証しません。（例えば子コンポーネントのコードがまだロードされていないなどの理由で）レンダーの試行がサスペンドした場合、React は進行中のツリーを破棄し、次の試行にてコンポーネントをゼロから構築しようとします。次のレンダーの試行の時点では props と state は異なっている可能性があります。これがこのメソッドが \"unsafe\" となっている理由です。コミットされた更新のみに対して実行すべきコード（サブスクリプションのリセットなど）は、[`componentDidUpdate`](#componentdidupdate) に入れるべきです。\n\n- `UNSAFE_componentWillUpdate` が呼ばれてもコンポーネントが前回とは*異なる* props や state を受け取ったことを意味するものではありません。何かが変わったかどうかを確認するには、`nextProps` と `this.props`、`nextState` と `this.state` を自分で比較する必要があります。\n\n- React は props や state の初期値を使ってマウント時に `UNSAFE_componentWillUpdate` を呼び出すことはしません。\n\n<Note>\n\n関数コンポーネントには `UNSAFE_componentWillUpdate` に直接対応するものはありません。\n\n</Note>\n\n---\n\n### `static contextType` {/*static-contexttype*/}\n\nクラスコンポーネントで [`this.context`](#context-instance-field) を読み取りたい場合、読み取りたいコンテクストをこれで指定する必要があります。`static contextType` として指定するコンテクストは、以前に [`createContext`](/reference/react/createContext) で作成されている値でなければなりません。\n\n```js {2}\nclass Button extends Component {\n  static contextType = ThemeContext;\n\n  render() {\n    const theme = this.context;\n    const className = 'button-' + theme;\n    return (\n      <button className={className}>\n        {this.props.children}\n      </button>\n    );\n  }\n}\n```\n\n<Note>\n\nクラスコンポーネントでの `this.context` の読み取りは、関数コンポーネントの [`useContext`](/reference/react/useContext) と同等です。\n\n[移行方法を見る](#migrating-a-component-with-context-from-a-class-to-a-function)\n\n</Note>\n\n---\n\n### `static defaultProps` {/*static-defaultprops*/}\n\nクラスのデフォルトの props を設定するために `static defaultProps` を定義できます。これらは `undefined` および存在しない props に対して使用されますが、`null` である props には使用されません。\n\n例えば以下のようにして、props である `color` がデフォルトで `'blue'` になるよう定義できます。\n\n```js {2-4}\nclass Button extends Component {\n  static defaultProps = {\n    color: 'blue'\n  };\n\n  render() {\n    return <button className={this.props.color}>click me</button>;\n  }\n}\n```\n\n`color` prop が渡されない場合や `undefined` である場合、デフォルトで `'blue'` にセットされます。\n\n```js\n<>\n  {/* this.props.color is \"blue\" */}\n  <Button />\n\n  {/* this.props.color is \"blue\" */}\n  <Button color={undefined} />\n\n  {/* this.props.color is null */}\n  <Button color={null} />\n\n  {/* this.props.color is \"red\" */}\n  <Button color=\"red\" />\n</>\n```\n\n<Note>\n\nクラスコンポーネントで `defaultProps` を定義することは、関数コンポーネントで[デフォルト値](/learn/passing-props-to-a-component#specifying-a-default-value-for-a-prop)を使用するのと同様のものです。\n\n</Note>\n\n---\n\n### `static getDerivedStateFromError(error)` {/*static-getderivedstatefromerror*/}\n\n`static getDerivedStateFromError` を定義すると、子コンポーネント（遠くの子を含む）がレンダー中にエラーをスローしたときに React がそれを呼び出します。これにより、UI をクリアする代わりにエラーメッセージを表示できます。\n\n通常これは、エラーレポートを何らかの分析サービスに送信できるようにするための [`componentDidCatch`](#componentdidcatch) と一緒に使用されます。これらのメソッドを持つコンポーネントのことを*エラーバウンダリ*と呼びます。\n\n[例を見る](#catching-rendering-errors-with-an-error-boundary)\n\n#### 引数 {/*static-getderivedstatefromerror-parameters*/}\n\n* `error`: スローされたエラー。現実的には通常 [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) のインスタンスになりますが、このことは保証されていません。JavaScript では文字列や `null` すら含む、任意の値を [`throw`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw) することが許されているためです。\n\n#### 返り値 {/*static-getderivedstatefromerror-returns*/}\n\n`static getDerivedStateFromError` からは、エラーメッセージを表示するようコンポーネントに指示するための state を返します。\n\n#### 注意点 {/*static-getderivedstatefromerror-caveats*/}\n\n* `static getDerivedStateFromError` は純関数であるべきです。副作用（例えば、分析サービスの呼び出し）を実行したい場合は、[`componentDidCatch`](#componentdidcatch) も実装する必要があります。\n\n<Note>\n\n関数コンポーネントには `static getDerivedStateFromError` に直接対応するものはまだありません。クラスコンポーネントの作成をなるべく避けたい場合は、上記のような `ErrorBoundary` コンポーネントを 1 つだけ書いてアプリ全体で使用します。あるいはこれを代わりにやってくれる [`react-error-boundary`](https://github.com/bvaughn/react-error-boundary) パッケージを使用することもできます。\n\n</Note>\n\n---\n\n### `static getDerivedStateFromProps(props, state)` {/*static-getderivedstatefromprops*/}\n\n`static getDerivedStateFromProps` を定義すると、React は初回のマウントとその後の更新の両方で、[`render`](#render) を呼び出す直前にこれを呼び出します。state を更新するためのオブジェクトを返すか、何も更新しない場合は `null` を返すようにします。\n\nこのメソッドは、state が props の経時的な変化に依存するという[稀なユースケース](https://legacy.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#when-to-use-derived-state)のために存在します。例えば、この `Form` コンポーネントは props である `userID` が変更されたときに state である `email` をリセットします。\n\n```js {7-18}\nclass Form extends Component {\n  state = {\n    email: this.props.defaultEmail,\n    prevUserID: this.props.userID\n  };\n\n  static getDerivedStateFromProps(props, state) {\n    // Any time the current user changes,\n    // Reset any parts of state that are tied to that user.\n    // In this simple example, that's just the email.\n    if (props.userID !== state.prevUserID) {\n      return {\n        prevUserID: props.userID,\n        email: props.defaultEmail\n      };\n    }\n    return null;\n  }\n\n  // ...\n}\n```\n\nこのパターンでは、（`userID` のような）props の前回の値を、（`prevUserID` のような）state に保持する必要があることに注意してください。\n\n<Pitfall>\n\nstate 値の派生はコードを冗長にし、コンポーネントを分かりづらくします。[よりシンプルな代替手段について確実に理解しておいてください](https://legacy.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html)。\n\n- props の変更に応じて**副作用を実行**する必要がある場合（例えば、データの取得やアニメーション）、代わりに [`componentDidUpdate`](#componentdidupdate) メソッドを使用してください。\n- **一部の props が変更されたときだけデータを再計算**する必要がある場合、[代わりにメモ化ヘルパーを使用します](https://legacy.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#what-about-memoization)。\n- props が変更されたときに**ある state を「リセット」する**必要がある場合、コンポーネントを[完全に制御されたコンポーネント](https://legacy.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-controlled-component)にするか、[key 付きの非制御コンポーネント](https://legacy.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key)にすることを検討してください。\n\n</Pitfall>\n\n#### 引数 {/*static-getderivedstatefromprops-parameters*/}\n\n- `props`: コンポーネントがレンダーしようとしている次の props。\n- `state`: コンポーネントがレンダーしようとしている次の state。\n\n#### 返り値 {/*static-getderivedstatefromprops-returns*/}\n\n`static getDerivedStateFromProps` は、state を更新するためのオブジェクトを返すか、何も更新しない場合は `null` を返します。\n\n#### 注意点 {/*static-getderivedstatefromprops-caveats*/}\n\n- このメソッドは、原因に関係なく*すべての*レンダーで発火します。[`UNSAFE_componentWillReceiveProps`](#unsafe_cmoponentwillreceiveprops) はこれとは異なり、親が再レンダーを引き起こしたときのみ発火し、ローカルの `setState` の結果としては発火しません。\n\n- このメソッドはコンポーネントのインスタンスにアクセスできません。お望みであれば、コンポーネントの props と state に対する純関数をクラス定義の外部に抽出することで、`static getDerivedStateFromProps` と他のクラスメソッドとの間でコードを再利用することができます。\n\n<Note>\n\nクラスコンポーネントで `static getDerivedStateFromProps` を実装することは、関数コンポーネントでレンダー中に [`useState` の `set` 関数を呼び出す](/reference/react/useState#storing-information-from-previous-renders) ことと同等です。\n\n</Note>\n\n---\n\n## 使用法 {/*usage*/}\n\n### クラスコンポーネントを定義する {/*defining-a-class-component*/}\n\nReact のコンポーネントをクラスとして定義するには、組み込みの `Component` クラスを継承し、[`render` メソッド](#render)を定義します。\n\n```js\nimport { Component } from 'react';\n\nclass Greeting extends Component {\n  render() {\n    return <h1>Hello, {this.props.name}!</h1>;\n  }\n}\n```\n\nReact は、画面に何を表示するかを知るために、[`render`](#render) メソッドを呼び出します。通常、そこからは [JSX](/learn/writing-markup-with-jsx) を返します。`render` メソッドは[純関数](https://en.wikipedia.org/wiki/Pure_function)である、つまり JSX の計算のみを行う必要があります。\n\n[関数コンポーネントと同様に](/learn/your-first-component#defining-a-component)、クラスコンポーネントでも親コンポーネントから [props を通じて情報を受け取る](/learn/your-first-component#defining-a-component)ことができます。ただし、props を読み取るための構文は異なります。例えば、親コンポーネントが `<Greeting name=\"Taylor\" />` をレンダーする場合、この `name` プロパティは [`this.props`](#props) から `this.props.name` のようにして読み取ります。\n\n<Sandpack>\n\n```js\nimport { Component } from 'react';\n\nclass Greeting extends Component {\n  render() {\n    return <h1>Hello, {this.props.name}!</h1>;\n  }\n}\n\nexport default function App() {\n  return (\n    <>\n      <Greeting name=\"Sara\" />\n      <Greeting name=\"Cahal\" />\n      <Greeting name=\"Edite\" />\n    </>\n  );\n}\n```\n\n</Sandpack>\n\nフック（`use` で始まる [`useState`](/reference/react/useState) のような関数）はクラスコンポーネント内ではサポートされていないことに注意してください。\n\n<Pitfall>\n\nクラスの代わりに関数でコンポーネントを定義することを推奨します。[移行方法はこちら](#migrating-a-simple-component-from-a-class-to-a-function)。\n\n</Pitfall>\n\n---\n\n### クラスコンポーネントに state を追加する {/*adding-state-to-a-class-component*/}\n\nクラスに [state](/learn/state-a-components-memory) を追加するには、[`state`](#state) というプロパティにオブジェクトを割り当てます。state を更新するには、[`this.setState`](#setstate) を呼び出します。\n\n<Sandpack>\n\n```js\nimport { Component } from 'react';\n\nexport default class Counter extends Component {\n  state = {\n    name: 'Taylor',\n    age: 42,\n  };\n\n  handleNameChange = (e) => {\n    this.setState({\n      name: e.target.value\n    });\n  }\n\n  handleAgeChange = () => {\n    this.setState({\n      age: this.state.age + 1 \n    });\n  };\n\n  render() {\n    return (\n      <>\n        <input\n          value={this.state.name}\n          onChange={this.handleNameChange}\n        />\n        <button onClick={this.handleAgeChange}>\n          Increment age\n        </button>\n        <p>Hello, {this.state.name}. You are {this.state.age}.</p>\n      </>\n    );\n  }\n}\n```\n\n```css\nbutton { display: block; margin-top: 10px; }\n```\n\n</Sandpack> \n\n<Pitfall>\n\nクラスの代わりに関数でコンポーネントを定義することを推奨します。[移行方法はこちら](#migrating-a-component-with-state-from-a-class-to-a-function)。\n\n</Pitfall>\n\n---\n\n### クラスコンポーネントにライフサイクルメソッドを追加する {/*adding-lifecycle-methods-to-a-class-component*/}\n\nクラスにはいくつかの特別なメソッドを定義することができます。\n\n[`componentDidMount`](#componentdidmount) メソッドを定義すると、コンポーネントが画面に追加される（*マウントされる*）ときに React がそれを呼び出します。props や state の変更によりコンポーネントが再レンダーされた後に、React は [`componentDidUpdate`](#componentdidupdate) を呼び出します。コンポーネントが画面から削除される（*アンマウントされる*）際に、React は [`componentWillUnmount`](#componentwillunmount) を呼び出します。\n\n`componentDidMount` を実装する場合、通常はバグを避けるために 3 つすべてのライフサイクルを実装する必要があります。例えば、`componentDidMount` が何らかの state や props を読み取る場合はそれらに変更があった場合に処理するために `componentDidUpdate` を実装する必要があり、`componentDidMount` が実行したことをクリーンアップするためには `componentWillUnmount` を実装する必要があります。\n\n例えば、以下の `ChatRoom` コンポーネントは、チャットへの接続を props および state と同期させています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ChatRoom from './ChatRoom.js';\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Close chat' : 'Open chat'}\n      </button>\n      {show && <hr />}\n      {show && <ChatRoom roomId={roomId} />}\n    </>\n  );\n}\n```\n\n```js src/ChatRoom.js active\nimport { Component } from 'react';\nimport { createConnection } from './chat.js';\n\nexport default class ChatRoom extends Component {\n  state = {\n    serverUrl: 'https://localhost:1234'\n  };\n\n  componentDidMount() {\n    this.setupConnection();\n  }\n\n  componentDidUpdate(prevProps, prevState) {\n    if (\n      this.props.roomId !== prevProps.roomId ||\n      this.state.serverUrl !== prevState.serverUrl\n    ) {\n      this.destroyConnection();\n      this.setupConnection();\n    }\n  }\n\n  componentWillUnmount() {\n    this.destroyConnection();\n  }\n\n  setupConnection() {\n    this.connection = createConnection(\n      this.state.serverUrl,\n      this.props.roomId\n    );\n    this.connection.connect();    \n  }\n\n  destroyConnection() {\n    this.connection.disconnect();\n    this.connection = null;\n  }\n\n  render() {\n    return (\n      <>\n        <label>\n          Server URL:{' '}\n          <input\n            value={this.state.serverUrl}\n            onChange={e => {\n              this.setState({\n                serverUrl: e.target.value\n              });\n            }}\n          />\n        </label>\n        <h1>Welcome to the {this.props.roomId} room!</h1>\n      </>\n    );\n  }\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n開発時に [Strict Mode](/reference/react/StrictMode) が有効の場合、React は `componentDidMount` を呼び出し、直後に `componentWillUnmount` を呼び出し、その後再び `componentDidMount` を呼び出します。これにより、`componentWillUnmount` の実装を忘れた場合や、そのロジックが `componentDidMount` の挙動と正しく「鏡のように対応」していない場合に気づきやすくなります。\n\n<Pitfall>\n\nクラスの代わりに関数でコンポーネントを定義することを推奨します。[移行方法はこちら](#migrating-a-component-with-lifecycle-methods-from-a-class-to-a-function)。\n\n</Pitfall>\n\n---\n\n### エラーバウンダリでレンダー中のエラーをキャッチする {/*catching-rendering-errors-with-an-error-boundary*/}\n\nデフォルトでは、アプリケーションがレンダー中にエラーをスローすると、React はその UI を画面から削除します。これを防ぐために、UI を*エラーバウンダリ*にラップすることができます。エラーバウンダリは、クラッシュした部位の代わりに、例えばエラーメッセージなどのフォールバック UI を表示するための、特別なコンポーネントです。\n\n<Note>\n以下の場合、エラーバウンダリはエラーをキャッチしません。\n\n- イベントハンドラ[（詳細）](/learn/responding-to-events)\n- [サーバサイドレンダリング](/reference/react-dom/server)\n- エラーバウンダリ自身（子ではなく）でスローされたエラー\n- 非同期コード（例えば `setTimeout` や `requestAnimationFrame` のコールバック）。ただし [`useTransition`](/reference/react/useTransition) フックが返す [`startTransition`](/reference/react/useTransition#starttransition) 関数の使用は例外です。トランジション関数内でスローされたエラーはエラーバウンダリでキャッチされます[（詳細）](/reference/react/useTransition#displaying-an-error-to-users-with-error-boundary)。\n\n</Note>\n\nエラーバウンダリコンポーネントを実装するためには、エラーに反応して state を更新し、ユーザにエラーメッセージを表示するための [`static getDerivedStateFromError`](#static-getderivedstatefromerror) を提供する必要があります。またオプションで、例えばエラーを分析サービスに記録するなどの追加のロジックを追加するために [`componentDidCatch`](#componentdidcatch) を実装することもできます。\n\n[`captureOwnerStack`](/reference/react/captureOwnerStack) を使うことで開発中にオーナーのスタックトレースを含めることが可能です。\n\n```js {9-12,14-27}\nimport * as React from 'react';\n\nclass ErrorBoundary extends React.Component {\n  constructor(props) {\n    super(props);\n    this.state = { hasError: false };\n  }\n\n  static getDerivedStateFromError(error) {\n    // Update state so the next render will show the fallback UI.\n    return { hasError: true };\n  }\n\n  componentDidCatch(error, info) {\n    logErrorToMyService(\n      error,\n      // Example \"componentStack\":\n      //   in ComponentThatThrows (created by App)\n      //   in ErrorBoundary (created by App)\n      //   in div (created by App)\n      //   in App\n      info.componentStack,\n      // Warning: `captureOwnerStack` is not available in production.\n      React.captureOwnerStack(),\n    );\n  }\n\n  render() {\n    if (this.state.hasError) {\n      // You can render any custom fallback UI\n      return this.props.fallback;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n次に、コンポーネントツリーの一部をこれでラップします。\n\n```js {1,3}\n<ErrorBoundary fallback={<p>Something went wrong</p>}>\n  <Profile />\n</ErrorBoundary>\n```\n\nもし `Profile` あるいはその子コンポーネントがエラーをスローすると、`ErrorBoundary` はそのエラーを「キャッチ」し、指定したエラーメッセージとともにフォールバック UI を表示し、エラーレポートをあなたのエラーレポーティングサービスに送信します。\n\nすべてのコンポーネントを別々のエラーバウンダリでラップする必要はありません。[エラーバウンダリの粒度](https://www.brandondail.com/posts/fault-tolerance-react)について考える際は、エラーメッセージをどこに表示するのが理にかなっているかを考えてみてください。例えば、メッセージングアプリでは、会話のリストをエラーバウンダリで囲むのが理にかなっています。また、メッセージを個別に囲むことも理にかなっているでしょう。しかし、アバターを 1 つずつ囲むことには意味がありません。\n\n<Note>\n\n現在、関数コンポーネントとしてエラーバウンダリを書く方法はありません。しかし、自分でエラーバウンダリクラスを書く必要はありません。例えば、代わりに [`react-error-boundary`](https://github.com/bvaughn/react-error-boundary) を使用することができます。\n\n</Note>\n\n---\n\n## 代替案 {/*alternatives*/}\n\n### シンプルなコンポーネントをクラスから関数へ移行 {/*migrating-a-simple-component-from-a-class-to-a-function*/}\n\n一般的には[コンポーネントは関数として定義](/learn/your-first-component#defining-a-component)します。\n\n例えば、この `Greeting` クラスコンポーネントを関数に書き換えたいとします。\n\n<Sandpack>\n\n```js\nimport { Component } from 'react';\n\nclass Greeting extends Component {\n  render() {\n    return <h1>Hello, {this.props.name}!</h1>;\n  }\n}\n\nexport default function App() {\n  return (\n    <>\n      <Greeting name=\"Sara\" />\n      <Greeting name=\"Cahal\" />\n      <Greeting name=\"Edite\" />\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n`Greeting` という関数を定義してください。ここに `render` 関数の本体を移動します。\n\n```js\nfunction Greeting() {\n  // ... move the code from the render method here ...\n}\n```\n\n`this.props.name` とする代わりに、props である `name` は[分割代入構文を使用して定義](/learn/passing-props-to-a-component)し、直接読み取ります：\n\n```js\nfunction Greeting({ name }) {\n  return <h1>Hello, {name}!</h1>;\n}\n```\n\n完全な例は以下の通りです。\n\n<Sandpack>\n\n```js\nfunction Greeting({ name }) {\n  return <h1>Hello, {name}!</h1>;\n}\n\nexport default function App() {\n  return (\n    <>\n      <Greeting name=\"Sara\" />\n      <Greeting name=\"Cahal\" />\n      <Greeting name=\"Edite\" />\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n---\n\n### state を持つコンポーネントをクラスから関数へ移行 {/*migrating-a-component-with-state-from-a-class-to-a-function*/}\n\nこの `Counter` クラスコンポーネントを関数に書き換えたいとします。\n\n<Sandpack>\n\n```js\nimport { Component } from 'react';\n\nexport default class Counter extends Component {\n  state = {\n    name: 'Taylor',\n    age: 42,\n  };\n\n  handleNameChange = (e) => {\n    this.setState({\n      name: e.target.value\n    });\n  }\n\n  handleAgeChange = (e) => {\n    this.setState({\n      age: this.state.age + 1 \n    });\n  };\n\n  render() {\n    return (\n      <>\n        <input\n          value={this.state.name}\n          onChange={this.handleNameChange}\n        />\n        <button onClick={this.handleAgeChange}>\n          Increment age\n        </button>\n        <p>Hello, {this.state.name}. You are {this.state.age}.</p>\n      </>\n    );\n  }\n}\n```\n\n```css\nbutton { display: block; margin-top: 10px; }\n```\n\n</Sandpack>\n\nまず、必要となる [state 変数](/reference/react/useState#adding-state-to-a-component) を持った関数を宣言します。\n\n```js {4-5}\nimport { useState } from 'react';\n\nfunction Counter() {\n  const [name, setName] = useState('Taylor');\n  const [age, setAge] = useState(42);\n  // ...\n```\n\n次に、イベントハンドラを書き換えます。\n\n```js {5-7,9-11}\nfunction Counter() {\n  const [name, setName] = useState('Taylor');\n  const [age, setAge] = useState(42);\n\n  function handleNameChange(e) {\n    setName(e.target.value);\n  }\n\n  function handleAgeChange() {\n    setAge(age + 1);\n  }\n  // ...\n```\n\n最後に、`this` で始まる値への参照をすべて、コンポーネントで定義した変数と関数に置き換えます。例えば、`this.state.age` を `age` に、`this.handleNameChange` を `handleNameChange` に置き換えます。\n\n完全に書き換えられたコンポーネントはこちらです。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [name, setName] = useState('Taylor');\n  const [age, setAge] = useState(42);\n\n  function handleNameChange(e) {\n    setName(e.target.value);\n  }\n\n  function handleAgeChange() {\n    setAge(age + 1);\n  }\n\n  return (\n    <>\n      <input\n        value={name}\n        onChange={handleNameChange}\n      />\n      <button onClick={handleAgeChange}>\n        Increment age\n      </button>\n      <p>Hello, {name}. You are {age}.</p>\n    </>\n  )\n}\n```\n\n```css\nbutton { display: block; margin-top: 10px; }\n```\n\n</Sandpack>\n\n---\n\n### ライフサイクルメソッドを持つコンポーネントをクラスから関数へ移行 {/*migrating-a-component-with-lifecycle-methods-from-a-class-to-a-function*/}\n\nライフサイクルメソッドを持つ以下の `ChatRoom` クラスコンポーネントを関数に書き換えたいとします。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ChatRoom from './ChatRoom.js';\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Close chat' : 'Open chat'}\n      </button>\n      {show && <hr />}\n      {show && <ChatRoom roomId={roomId} />}\n    </>\n  );\n}\n```\n\n```js src/ChatRoom.js active\nimport { Component } from 'react';\nimport { createConnection } from './chat.js';\n\nexport default class ChatRoom extends Component {\n  state = {\n    serverUrl: 'https://localhost:1234'\n  };\n\n  componentDidMount() {\n    this.setupConnection();\n  }\n\n  componentDidUpdate(prevProps, prevState) {\n    if (\n      this.props.roomId !== prevProps.roomId ||\n      this.state.serverUrl !== prevState.serverUrl\n    ) {\n      this.destroyConnection();\n      this.setupConnection();\n    }\n  }\n\n  componentWillUnmount() {\n    this.destroyConnection();\n  }\n\n  setupConnection() {\n    this.connection = createConnection(\n      this.state.serverUrl,\n      this.props.roomId\n    );\n    this.connection.connect();    \n  }\n\n  destroyConnection() {\n    this.connection.disconnect();\n    this.connection = null;\n  }\n\n  render() {\n    return (\n      <>\n        <label>\n          Server URL:{' '}\n          <input\n            value={this.state.serverUrl}\n            onChange={e => {\n              this.setState({\n                serverUrl: e.target.value\n              });\n            }}\n          />\n        </label>\n        <h1>Welcome to the {this.props.roomId} room!</h1>\n      </>\n    );\n  }\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\nまずは [`componentWillUnmount`](#componentwillunmount) が [`componentDidMount`](#componentdidmount) の逆の動作をしていることを確認してください。上記の例では `componentDidMount` によって確立された接続を切断しているので、そうなっています。そのようなロジックが存在しない場合、まずそれを追加してください。\n\n次に、[`componentDidUpdate`](#componentdidupdate) メソッドが、`componentDidMount` で使用している props と state の変更を処理していることを確認してください。上記の例では、`componentDidMount` は `setupConnection` を呼び出しており、それが `this.state.serverUrl` と `this.props.roomId` を読み取っています。したがって `componentDidUpdate` も `this.state.serverUrl` と `this.props.roomId` が変更されたかどうかを確認し、変更があった場合は接続を再確立しなければなりません。`componentDidUpdate` のロジックが存在しない、あるいは関連するすべての props と state の変更を処理できていない場合は、まずそれを修正してください。\n\n上記の例では、ライフサイクルメソッド内のロジックは、React の外部のシステム（チャットサーバ）にコンポーネントを接続しています。コンポーネントを外部システムに接続するためには、[このロジックを単一のエフェクトとして記述します](/reference/react/useEffect#connecting-to-an-external-system)。\n\n```js {6-12}\nimport { useState, useEffect } from 'react';\n\nfunction ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [serverUrl, roomId]);\n\n  // ...\n}\n```\n\nこの [`useEffect`](/reference/react/useEffect) の呼び出しは、上記のライフサイクルメソッド内のロジックと同等です。ライフサイクルメソッドが複数の互いに関連しないことを行っている場合は、[それらを複数の独立したエフェクトに分割してください](/learn/removing-effect-dependencies#is-your-effect-doing-several-unrelated-things)。以下に完全な例を示します。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ChatRoom from './ChatRoom.js';\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Close chat' : 'Open chat'}\n      </button>\n      {show && <hr />}\n      {show && <ChatRoom roomId={roomId} />}\n    </>\n  );\n}\n```\n\n```js src/ChatRoom.js active\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nexport default function ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [roomId, serverUrl]);\n\n  return (\n    <>\n      <label>\n        Server URL:{' '}\n        <input\n          value={serverUrl}\n          onChange={e => setServerUrl(e.target.value)}\n        />\n      </label>\n      <h1>Welcome to the {roomId} room!</h1>\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n<Note>\n\nコンポーネントが外部システムとの同期を行わない場合、[エフェクトは必要ないかもしれません](/learn/you-might-not-need-an-effect)。\n\n</Note>\n\n---\n\n### コンテクストを持つコンポーネントをクラスから関数へ移行 {/*migrating-a-component-with-context-from-a-class-to-a-function*/}\n\n以下の例では、クラスコンポーネントである `Panel` と `Button` が、[`this.context`](#context) から[コンテクスト](/learn/passing-data-deeply-with-context)を読み取っています。\n\n<Sandpack>\n\n```js\nimport { createContext, Component } from 'react';\n\nconst ThemeContext = createContext(null);\n\nclass Panel extends Component {\n  static contextType = ThemeContext;\n\n  render() {\n    const theme = this.context;\n    const className = 'panel-' + theme;\n    return (\n      <section className={className}>\n        <h1>{this.props.title}</h1>\n        {this.props.children}\n      </section>\n    );    \n  }\n}\n\nclass Button extends Component {\n  static contextType = ThemeContext;\n\n  render() {\n    const theme = this.context;\n    const className = 'button-' + theme;\n    return (\n      <button className={className}>\n        {this.props.children}\n      </button>\n    );\n  }\n}\n\nfunction Form() {\n  return (\n    <Panel title=\"Welcome\">\n      <Button>Sign up</Button>\n      <Button>Log in</Button>\n    </Panel>\n  );\n}\n\nexport default function MyApp() {\n  return (\n    <ThemeContext value=\"dark\">\n      <Form />\n    </ThemeContext>\n  )\n}\n```\n\n```css\n.panel-light,\n.panel-dark {\n  border: 1px solid black;\n  border-radius: 4px;\n  padding: 20px;\n}\n.panel-light {\n  color: #222;\n  background: #fff;\n}\n\n.panel-dark {\n  color: #fff;\n  background: rgb(23, 32, 42);\n}\n\n.button-light,\n.button-dark {\n  border: 1px solid #777;\n  padding: 5px;\n  margin-right: 10px;\n  margin-top: 10px;\n}\n\n.button-dark {\n  background: #222;\n  color: #fff;\n}\n\n.button-light {\n  background: #fff;\n  color: #222;\n}\n```\n\n</Sandpack>\n\nこれらを関数コンポーネントに変換する場合、`this.context` を [`useContext`](/reference/react/useContext) の呼び出しに置き換えます。\n\n<Sandpack>\n\n```js\nimport { createContext, useContext } from 'react';\n\nconst ThemeContext = createContext(null);\n\nfunction Panel({ title, children }) {\n  const theme = useContext(ThemeContext);\n  const className = 'panel-' + theme;\n  return (\n    <section className={className}>\n      <h1>{title}</h1>\n      {children}\n    </section>\n  )\n}\n\nfunction Button({ children }) {\n  const theme = useContext(ThemeContext);\n  const className = 'button-' + theme;\n  return (\n    <button className={className}>\n      {children}\n    </button>\n  );\n}\n\nfunction Form() {\n  return (\n    <Panel title=\"Welcome\">\n      <Button>Sign up</Button>\n      <Button>Log in</Button>\n    </Panel>\n  );\n}\n\nexport default function MyApp() {\n  return (\n    <ThemeContext value=\"dark\">\n      <Form />\n    </ThemeContext>\n  )\n}\n```\n\n```css\n.panel-light,\n.panel-dark {\n  border: 1px solid black;\n  border-radius: 4px;\n  padding: 20px;\n}\n.panel-light {\n  color: #222;\n  background: #fff;\n}\n\n.panel-dark {\n  color: #fff;\n  background: rgb(23, 32, 42);\n}\n\n.button-light,\n.button-dark {\n  border: 1px solid #777;\n  padding: 5px;\n  margin-right: 10px;\n  margin-top: 10px;\n}\n\n.button-dark {\n  background: #222;\n  color: #fff;\n}\n\n.button-light {\n  background: #fff;\n  color: #222;\n}\n```\n\n</Sandpack>\n"
  },
  {
    "path": "src/content/reference/react/Fragment.md",
    "content": "---\ntitle: <Fragment> (<>...</>)\n---\n\n<Intro>\n\n`<Fragment>` を使うことで、ラッパ用のノードを用いずに要素をグループ化することができます。通常は `<>...</>` という構文で使用されます。\n\n<Canary> フラグメントは ref を受け取ることもでき、これによりラッパ要素を追加することなく、内部の DOM ノードとやり取りすることができます。以下のリファレンスと使用法を参照してください。</Canary>\n\n```js\n<>\n  <OneChild />\n  <AnotherChild />\n</>\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<Fragment>` {/*fragment*/}\n\n単一の要素が必要な場面で、複数の要素を `<Fragment>` でラップすることでグループ化することができます。`Fragment` で要素をグループ化しても、出力される DOM には影響を与えません。要素がグループ化されていないときと同じです。空の JSX タグ `<></>` は、ほとんどの場合 `<Fragment></Fragment>` の省略記法です。\n\n#### props {/*props*/}\n\n- **省略可能** `key`: 明示的な `<Fragment>` 構文で宣言されたフラグメントは [key](/learn/rendering-lists#keeping-list-items-in-order-with-key) を持つことができます。\n- <CanaryBadge /> **省略可能** `ref`: ref オブジェクト（例えば [`useRef`](/reference/react/useRef) からのもの）または[コールバック関数](/reference/react-dom/components/common#ref-callback)。React は、フラグメントでラップされた DOM ノードとやり取りするためのメソッドを実装した `FragmentInstance` を ref の値として提供します。\n\n### <CanaryBadge /> FragmentInstance {/*fragmentinstance*/}\n\nフラグメントに ref を渡した場合、React は、フラグメント内にラップした DOM ノードとやり取りするために、以下のメソッドを持つ `FragmentInstance` オブジェクトを提供します。\n\n**イベント処理メソッド：**\n- `addEventListener(type, listener, options?)`: フラグメントのすべての第 1 レベルの DOM の子にイベントリスナを追加します。\n- `removeEventListener(type, listener, options?)`: フラグメントのすべての第 1 レベルの DOM の子からイベントリスナを削除します。\n- `dispatchEvent(event)`: フラグメントの仮想的な単一の子にイベントをディスパッチします。追加されたすべてのリスナを呼び出すとともに、DOM の親にバブルアップさせることができます。\n\n**レイアウトメソッド：**\n- `compareDocumentPosition(otherNode)`: フラグメントのドキュメント内の位置を別のノードと比較します。\n  - フラグメントに子がある場合、ネイティブの `compareDocumentPosition` の値が返されます。\n  - 空のフラグメントは、React ツリー内での位置の比較を試み、`Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC` を含む値を返します。\n  - ポータルやその他の挿入により、React ツリーと DOM ツリーで異なる関係を持つ要素は、`Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC` を返します。\n- `getClientRects()`: すべての子の境界矩形を表す `DOMRect` オブジェクトのフラットな配列を返します。\n- `getRootNode()`: フラグメントの親 DOM ノードを含むルートノードを返します。\n\n**フォーカス管理メソッド:**\n- `focus(options?)`: フラグメント内の最初のフォーカス可能な DOM ノードにフォーカスを当てます。ネストされた子に対して深さ優先でフォーカスを試みます。\n- `focusLast(options?)`: フラグメント内の最後のフォーカス可能な DOM ノードにフォーカスを当てます。ネストされた子に対して深さ優先でフォーカスを試みます。\n- `blur()`: `document.activeElement` がフラグメント内にある場合、フォーカスを外します。\n\n**オブザーバメソッド:**\n- `observeUsing(observer)`: IntersectionObserver または ResizeObserver を使用して、フラグメントの DOM の子の監視を開始します。\n- `unobserveUsing(observer)`: 指定されたオブザーバによるフラグメントの DOM の子の監視を停止します。\n\n#### 注意点 {/*caveats*/}\n\n- `key` をフラグメントに渡したい場合は、`<>...</>` 構文を使用することはできません。`'react'` から `Fragment` を明示的にインポートし、`<Fragment key={yourKey}>...</Fragment>` とレンダーしなければなりません。\n\n- React は、`<><Child /></>` と `[<Child />]` のレンダー間、あるいは `<><Child /></>` と `<Child />` のレンダー間で行き来する場合に [state をリセット](/learn/preserving-and-resetting-state)しません。これは単一レベルの深さのときのみの動作です。例えば、`<><><Child /></></>` から `<Child />` への変更では state がリセットされます。具体的な振る舞いの詳細は[こちら](https://gist.github.com/clemmy/b3ef00f9507909429d8aa0d3ee4f986b)を参照してください。\n\n- <CanaryBadge /> `ref` をフラグメントに渡したい場合は、`<>...</>` 構文を使用することはできません。`'react'` から `Fragment` を明示的にインポートし、`<Fragment ref={yourRef}>...</Fragment>` のようにレンダーしなければなりません。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 複数の要素を返す {/*returning-multiple-elements*/}\n\n複数の要素をグループ化するために `Fragment` や、それと同等の `<>...</>` 構文を使用することができます。これにより単一の要素が置ける場所であればどこにでも、複数の要素を配置することができるようになります。例えば、コンポーネントは 1 つの要素しか返すことができませんが、フラグメントを使用すれば複数の要素をまとめて、グループとして返せます。\n\n```js {3,6}\nfunction Post() {\n  return (\n    <>\n      <PostTitle />\n      <PostBody />\n    </>\n  );\n}\n```\n\nフラグメントが有用なのは、DOM 要素のような他のコンテナで要素をラップする場合と異なり、フラグメントで要素をグループ化してもレイアウトやスタイルに影響を与えないからです。以下の例をブラウザツールでインスペクト（inspect, 調査）してみると、すべての `<h1>` や `<article>` DOM ノードがラップされずに兄弟として表示されることがわかります。\n\n<Sandpack>\n\n```js\nexport default function Blog() {\n  return (\n    <>\n      <Post title=\"An update\" body=\"It's been a while since I posted...\" />\n      <Post title=\"My new blog\" body=\"I am starting a new blog!\" />\n    </>\n  )\n}\n\nfunction Post({ title, body }) {\n  return (\n    <>\n      <PostTitle title={title} />\n      <PostBody body={body} />\n    </>\n  );\n}\n\nfunction PostTitle({ title }) {\n  return <h1>{title}</h1>\n}\n\nfunction PostBody({ body }) {\n  return (\n    <article>\n      <p>{body}</p>\n    </article>\n  );\n}\n```\n\n</Sandpack>\n\n<DeepDive>\n\n#### 特別な構文を使わずにフラグメントを記述する方法 {/*how-to-write-a-fragment-without-the-special-syntax*/}\n\n上述の例は、React から `Fragment` をインポートして以下のように書くことと同じです。\n\n```js {1,5,8}\nimport { Fragment } from 'react';\n\nfunction Post() {\n  return (\n    <Fragment>\n      <PostTitle />\n      <PostBody />\n    </Fragment>\n  );\n}\n```\n\n[`Fragment` に `key` を渡す](#rendering-a-list-of-fragments)場合以外では、通常必要ありません。\n\n</DeepDive>\n\n---\n\n### 複数の要素を変数に割り当てる {/*assigning-multiple-elements-to-a-variable*/}\n\n他の要素と同じように、フラグメントも要素として変数に割り当てたり、props として渡したりすることができます：\n\n```js\nfunction CloseDialog() {\n  const buttons = (\n    <>\n      <OKButton />\n      <CancelButton />\n    </>\n  );\n  return (\n    <AlertDialog buttons={buttons}>\n      Are you sure you want to leave this page?\n    </AlertDialog>\n  );\n}\n```\n\n---\n\n### テキストと要素をグループ化する {/*grouping-elements-with-text*/}\n\n`Fragment` を使うとテキストとコンポーネントをグループ化することができます：\n\n```js\nfunction DateRangePicker({ start, end }) {\n  return (\n    <>\n      From\n      <DatePicker date={start} />\n      to\n      <DatePicker date={end} />\n    </>\n  );\n}\n```\n\n---\n\n### フラグメントのリストをレンダーする {/*rendering-a-list-of-fragments*/}\n\nこちらは `<></>` 構文の代わりに `Fragment` を明示的に記述する必要がある場面です。ループ内で[複数の要素をレンダーする](/learn/rendering-lists)ときには、各要素に `key` を割り当てる必要があります。ループ内の要素がフラグメントの場合は、`key` 属性を渡すために通常の JSX 要素の構文を使用する必要があります。\n\n```js {3,6}\nfunction Blog() {\n  return posts.map(post =>\n    <Fragment key={post.id}>\n      <PostTitle title={post.title} />\n      <PostBody body={post.body} />\n    </Fragment>\n  );\n}\n```\n\nDOM をインスペクトすると、フラグメントの子要素のまわりにラッパ要素がないことを確認できます。\n\n<Sandpack>\n\n```js\nimport { Fragment } from 'react';\n\nconst posts = [\n  { id: 1, title: 'An update', body: \"It's been a while since I posted...\" },\n  { id: 2, title: 'My new blog', body: 'I am starting a new blog!' }\n];\n\nexport default function Blog() {\n  return posts.map(post =>\n    <Fragment key={post.id}>\n      <PostTitle title={post.title} />\n      <PostBody body={post.body} />\n    </Fragment>\n  );\n}\n\nfunction PostTitle({ title }) {\n  return <h1>{title}</h1>\n}\n\nfunction PostBody({ body }) {\n  return (\n    <article>\n      <p>{body}</p>\n    </article>\n  );\n}\n```\n\n</Sandpack>\n\n---\n\n### <CanaryBadge /> フラグメント ref を使った DOM とのやり取り {/*using-fragment-refs-for-dom-interaction*/}\n\nフラグメント ref を使用すると、余分なラッパ要素を追加することなく、フラグメントでラップされた DOM ノードとやり取りすることができます。これは、イベント処理、可視性追跡、フォーカス管理、そして `ReactDOM.findDOMNode()` のような非推奨のパターンの置き換えに役立ちます。\n\n```js\nimport { Fragment } from 'react';\n\nfunction ClickableFragment({ children, onClick }) {\n  return (\n    <Fragment ref={fragmentInstance => {\n      fragmentInstance.addEventListener('click', handleClick);\n      return () => fragmentInstance.removeEventListener('click', handleClick);\n    }}>\n      {children}\n    </Fragment>\n  );\n}\n```\n---\n\n### <CanaryBadge /> フラグメント ref を使った可視性追跡 {/*tracking-visibility-with-fragment-refs*/}\n\nフラグメント ref は、可視性追跡やインターセクション監視に役立ちます。これにより、子コンポーネントがそれぞれ ref を公開せずとも、コンテンツが表示されるようになったタイミングを監視することができます。\n\n```js {19,21,31-34}\nimport { Fragment, useRef, useLayoutEffect } from 'react';\n\nfunction VisibilityObserverFragment({ threshold = 0.5, onVisibilityChange, children }) {\n  const fragmentRef = useRef(null);\n\n  useLayoutEffect(() => {\n    const observer = new IntersectionObserver(\n      (entries) => {\n        onVisibilityChange(entries.some(entry => entry.isIntersecting))\n      },\n      { threshold }\n    );\n    \n    fragmentRef.current.observeUsing(observer);\n    return () => fragmentRef.current.unobserveUsing(observer);\n  }, [threshold, onVisibilityChange]);\n\n  return (\n    <Fragment ref={fragmentRef}>\n      {children}\n    </Fragment>\n  );\n}\n\nfunction MyComponent() {\n  const handleVisibilityChange = (isVisible) => {\n    console.log('Component is', isVisible ? 'visible' : 'hidden');\n  };\n\n  return (\n    <VisibilityObserverFragment onVisibilityChange={handleVisibilityChange}>\n      <SomeThirdPartyComponent />\n      <AnotherComponent />\n    </VisibilityObserverFragment>\n  );\n}\n```\n\nこのパターンは、エフェクトベースの可視性ロギング（ほとんどの場合アンチパターン）の代替手段です。エフェクトを使うだけでは、レンダーされたコンポーネントがユーザに見えることを保証できません。\n\n---\n\n### <CanaryBadge /> フラグメント ref を使ったフォーカス管理 {/*focus-management-with-fragment-refs*/}\n\nフラグメント ref は、フラグメント内のすべての DOM ノードにわたって機能するフォーカス管理メソッドを提供します。\n\n```js\nimport { Fragment, useRef } from 'react';\n\nfunction FocusFragment({ children }) {\n  return (\n    <Fragment ref={(fragmentInstance) => fragmentInstance?.focus()}>\n      {children}\n    </Fragment>\n  );\n}\n```\n\n`focus()` メソッドはフラグメント内の最初のフォーカス可能な要素にフォーカスを当て、`focusLast()` は最後のフォーカス可能な要素にフォーカスを当てます。\n"
  },
  {
    "path": "src/content/reference/react/Profiler.md",
    "content": "---\ntitle: <Profiler>\n---\n\n<Intro>\n\n`<Profiler>` を使うことで、React ツリーのレンダリングパフォーマンスをプログラムで測定することができます。\n\n```js\n<Profiler id=\"App\" onRender={onRender}>\n  <App />\n</Profiler>\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<Profiler>` {/*profiler*/}\n\nコンポーネントツリーを `<Profiler>` でラップすることで、レンダーのパフォーマンスを測定することができます。\n\n```js\n<Profiler id=\"App\" onRender={onRender}>\n  <App />\n</Profiler>\n```\n\n#### props {/*props*/}\n\n* `id`: UI のどの部分を測定対象としているのか識別するための文字列です。\n* `onRender`: プロファイリング対象のツリー内のコンポーネントが更新されるたびに React が呼び出す [`onRender` コールバック](#onrender-callback)。何がレンダーされ、それにどれだけの時間がかかったかについての情報を受け取ります。\n\n#### 注意点 {/*caveats*/}\n\n* プロファイリングには追加のオーバーヘッドが発生するため、**デフォルトでは本番用ビルドでは無効になっています**。本番環境でプロファイリングを行うためには、[プロファイリングを有効にした特別な本番用ビルド](/reference/dev-tools/react-performance-tracks#using-profiling-builds)を明示的に用いる必要があります。\n\n---\n\n### `onRender` コールバック {/*onrender-callback*/}\n\nReact は `onRender` コールバックを呼び出して、何がレンダーされたかについての情報を伝えます。\n\n```js\nfunction onRender(id, phase, actualDuration, baseDuration, startTime, commitTime) {\n  // Aggregate or log render timings...\n}\n```\n\n#### 引数 {/*onrender-parameters*/}\n\n* `id`: たった今コミットされたツリーに対応する `<Profiler>` の `id` プロパティ。複数のプロファイラを使用している場合に、どのツリーがコミットされたかをこれにより識別できます。\n* `phase`: `\"mount\"`、`\"update\"`、または `\"nested-update\"`。これにより、ツリーが初めてマウントされたのか、props、state、またはフックの変更により再レンダーされたのかを知ることができます。\n* `actualDuration`: 現在の更新のために `<Profiler>` とその子要素がレンダーに費やしたミリ秒数。これは、サブツリーがメモ化（例：[`memo`](/reference/react/memo) と [`useMemo`](/reference/react/useMemo)）をどれだけうまく利用できているかを示すものです。理想的には、子要素の多くが自身の props が変更された場合にのみ再レンダーされるようになることで、この値は初回マウント後に大幅に減少していくはずです。\n* `baseDuration`: もし最適化なしで `<Profiler>` サブツリー全体を再レンダーした場合にかかる時間をミリ秒で推定した数値。ツリー内の各コンポーネントの最後のレンダーにかかった時間を合計することで計算されます。この値は、最悪のツリーのレンダーコスト（例：初回マウントやメモ化がない場合）を推定します。メモ化が機能しているかどうかを確認するには、この値を `actualDuration` と比較します。\n* `startTime`: React が現在の更新のレンダーを開始した時刻のタイムスタンプ。\n* `commitTime`: React が現在の更新をコミットした時刻のタイムスタンプ。この値は単一コミット内のすべてのプロファイラ間で共有されるため、必要に応じてグループ化するために利用できます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### レンダーのパフォーマンスをプログラムで測定する {/*measuring-rendering-performance-programmatically*/}\n\nReact ツリーを `<Profiler>` コンポーネントで囲むことで、そのレンダーのパフォーマンスを測定します。\n\n```js {2,4}\n<App>\n  <Profiler id=\"Sidebar\" onRender={onRender}>\n    <Sidebar />\n  </Profiler>\n  <PageContent />\n</App>\n```\n\n`id`（文字列）と `onRender` コールバック（関数）の 2 つの props が必要です。React はツリー内のコンポーネントが更新を「コミット」するたびにこれを呼び出します。\n\n<Pitfall>\n\nプロファイリングには追加のオーバーヘッドが発生するため、**デフォルトでは本番用ビルドでは無効になっています**。本番環境でプロファイリングを行うためには、[プロファイリングを有効にした特別な本番用ビルド](/reference/dev-tools/react-performance-tracks#using-profiling-builds)を明示的に用いる必要があります。\n\n</Pitfall>\n\n<Note>\n\n`<Profiler>` を使うことで測定結果をプログラムで収集することができます。対話型のプロファイラを使いたい場合は、[React Developer Tools](/learn/react-developer-tools) の Profiler タブを試してみてください。これは同様の機能をブラウザの拡張機能として提供します。\n\nComponents wrapped in `<Profiler>` will also be marked in the [Component tracks](/reference/dev-tools/react-performance-tracks#components) of React Performance tracks even in profiling builds.\nIn development builds, all components are marked in the Components track regardless of whether they're wrapped in `<Profiler>`.\n\n</Note>\n\n---\n\n### アプリケーションの複数部位を測定する {/*measuring-different-parts-of-the-application*/}\n\nアプリケーションの複数の部位を測定するために、複数の `<Profiler>` コンポーネントを使用することができます。\n\n```js {5,7}\n<App>\n  <Profiler id=\"Sidebar\" onRender={onRender}>\n    <Sidebar />\n  </Profiler>\n  <Profiler id=\"Content\" onRender={onRender}>\n    <Content />\n  </Profiler>\n</App>\n```\n\nまた、`<Profiler>` コンポーネントをネストすることもできます。\n\n```js {5,7,9,12}\n<App>\n  <Profiler id=\"Sidebar\" onRender={onRender}>\n    <Sidebar />\n  </Profiler>\n  <Profiler id=\"Content\" onRender={onRender}>\n    <Content>\n      <Profiler id=\"Editor\" onRender={onRender}>\n        <Editor />\n      </Profiler>\n      <Preview />\n    </Content>\n  </Profiler>\n</App>\n```\n\n`<Profiler>` は軽量なコンポーネントですが、必要な場合にのみ使用するべきです。使用するたびにアプリケーションに一定の CPU とメモリのオーバーヘッドが生じます。\n\n---\n\n"
  },
  {
    "path": "src/content/reference/react/PureComponent.md",
    "content": "---\ntitle: PureComponent\n---\n\n<Pitfall>\n\nクラスの代わりに関数でコンポーネントを定義することを推奨します。[移行方法はこちら](#alternatives)。\n\n</Pitfall>\n\n<Intro>\n\n`PureComponent` は [`Component`](/reference/react/Component) と似ていますが、同じ props と state に対しては再レンダーをスキップします。クラスコンポーネントはまだ React によってサポートされていますが、新しいコードでの使用は推奨されません。\n\n```js\nclass Greeting extends PureComponent {\n  render() {\n    return <h1>Hello, {this.props.name}!</h1>;\n  }\n}\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `PureComponent` {/*purecomponent*/}\n\n同じ props と state に対してクラスコンポーネントの再レンダーをスキップしたい場合、[`Component`](/reference/react/Component) の代わりに `PureComponent` を継承します。\n\n```js\nimport { PureComponent } from 'react';\n\nclass Greeting extends PureComponent {\n  render() {\n    return <h1>Hello, {this.props.name}!</h1>;\n  }\n}\n```\n\n`PureComponent` は `Component` のサブクラスであり、[すべての `Component` API](/reference/react/Component#reference) をサポートしています。`PureComponent` を拡張することは、props と state を浅く比較するカスタムの [`shouldComponentUpdate`](/reference/react/Component#shouldcomponentupdate) メソッドを定義することと同等です。\n\n[さらに例を見る](#usage)\n\n---\n\n## 使用法 {/*usage*/}\n\n### クラスコンポーネントの不要な再レンダーをスキップする {/*skipping-unnecessary-re-renders-for-class-components*/}\n\nReact は通常、親が再レンダーされるたびにコンポーネントを再レンダーします。最適化として、新しい props や state が古い props や state と同じである限り、親が再レンダーされても React が再レンダーを行わない、というコンポーネントを作成することができます。[クラスコンポーネント](/reference/react/Component)では、`PureComponent` を継承することにより、この挙動を有効化できます。\n\n```js {1}\nclass Greeting extends PureComponent {\n  render() {\n    return <h1>Hello, {this.props.name}!</h1>;\n  }\n}\n```\n\nReact コンポーネントは常に[純粋なレンダーロジックを持つべきです](/learn/keeping-components-pure)。これはすなわち、その props、state、コンテクストが変わらない場合は、常に同じ出力を返す必要がある、という意味です。`PureComponent` を使用することで、あなたのコンポーネントがこの要件を満たしていると React に伝えることができ、そのため React は props と state が変わらない限り再レンダーする必要がないと判断します。ただし、使用しているコンテクストが変更された場合、コンポーネントは再レンダーされます。\n\n以下の例で、`name` が変更されるたびに `Greeting` コンポーネントが再レンダーされる（`name` が props として渡されているため）が、`address` が変更されても再レンダーされない（`Greeting` に props として渡されていないため）ことに注目してください。\n\n<Sandpack>\n\n```js\nimport { PureComponent, useState } from 'react';\n\nclass Greeting extends PureComponent {\n  render() {\n    console.log(\"Greeting was rendered at\", new Date().toLocaleTimeString());\n    return <h3>Hello{this.props.name && ', '}{this.props.name}!</h3>;\n  }\n}\n\nexport default function MyApp() {\n  const [name, setName] = useState('');\n  const [address, setAddress] = useState('');\n  return (\n    <>\n      <label>\n        Name{': '}\n        <input value={name} onChange={e => setName(e.target.value)} />\n      </label>\n      <label>\n        Address{': '}\n        <input value={address} onChange={e => setAddress(e.target.value)} />\n      </label>\n      <Greeting name={name} />\n    </>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-bottom: 16px;\n}\n```\n\n</Sandpack>\n\n<Pitfall>\n\nクラスの代わりに関数でコンポーネントを定義することを推奨します。[移行方法はこちら](#alternatives)。\n\n\n</Pitfall>\n\n---\n\n## 代替手段 {/*alternatives*/}\n\n### `PureComponent` クラスのコンポーネントから関数コンポーネントへの移行 {/*migrating-from-a-purecomponent-class-component-to-a-function*/}\n\n新しいコードでは、[クラスコンポーネント](/reference/react/Component)の代わりに関数コンポーネントを使用することを推奨します。以下では、既存の `PureComponent` を使用したクラスコンポーネントがある場合、どのように移行するかを説明します。こちらが元のコードです。\n\n<Sandpack>\n\n```js\nimport { PureComponent, useState } from 'react';\n\nclass Greeting extends PureComponent {\n  render() {\n    console.log(\"Greeting was rendered at\", new Date().toLocaleTimeString());\n    return <h3>Hello{this.props.name && ', '}{this.props.name}!</h3>;\n  }\n}\n\nexport default function MyApp() {\n  const [name, setName] = useState('');\n  const [address, setAddress] = useState('');\n  return (\n    <>\n      <label>\n        Name{': '}\n        <input value={name} onChange={e => setName(e.target.value)} />\n      </label>\n      <label>\n        Address{': '}\n        <input value={address} onChange={e => setAddress(e.target.value)} />\n      </label>\n      <Greeting name={name} />\n    </>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-bottom: 16px;\n}\n```\n\n</Sandpack>\n\nこのコンポーネントを[クラスから関数に移行](/reference/react/Component#alternatives)したい場合は、[`memo`](/reference/react/memo) でラップしてください。\n\n<Sandpack>\n\n```js\nimport { memo, useState } from 'react';\n\nconst Greeting = memo(function Greeting({ name }) {\n  console.log(\"Greeting was rendered at\", new Date().toLocaleTimeString());\n  return <h3>Hello{name && ', '}{name}!</h3>;\n});\n\nexport default function MyApp() {\n  const [name, setName] = useState('');\n  const [address, setAddress] = useState('');\n  return (\n    <>\n      <label>\n        Name{': '}\n        <input value={name} onChange={e => setName(e.target.value)} />\n      </label>\n      <label>\n        Address{': '}\n        <input value={address} onChange={e => setAddress(e.target.value)} />\n      </label>\n      <Greeting name={name} />\n    </>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-bottom: 16px;\n}\n```\n\n</Sandpack>\n\n<Note>\n\n`PureComponent` とは異なり、[`memo`](/reference/react/memo) は新旧の state を比較しません。関数コンポーネントでは、同一の state 値で [`set` 関数](/reference/react/useState#setstate)を呼び出した場合、`memo` がなくても[デフォルトで再レンダーが防止されます](/reference/react/memo#updating-a-memoized-component-using-state)。\n\n</Note>\n"
  },
  {
    "path": "src/content/reference/react/StrictMode.md",
    "content": "---\ntitle: <StrictMode>\n---\n\n\n<Intro>\n\n`<StrictMode>` は、開発環境においてコンポーネントの一般的なバグを早期に見つけるのに役立ちます。\n\n\n```js\n<StrictMode>\n  <App />\n</StrictMode>\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<StrictMode>` {/*strictmode*/}\n\n`StrictMode` を使用して、内側のコンポーネントツリーに対して開発時専用の挙動と警告を有効にします。\n\n```js\nimport { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);\n```\n\n[さらに例を見る](#usage)\n\nStrict Mode では、以下のような開発時専用の挙動が有効になります。\n\n- コンポーネントは、純粋でない (impure) レンダーによって引き起こされるバグを見つけるために、[レンダーを追加で 1 回行います](#fixing-bugs-found-by-double-rendering-in-development)。\n- コンポーネントは、エフェクトのクリーンアップし忘れによるバグを見つけるために、[エフェクトの実行を追加で 1 回行います](#fixing-bugs-found-by-re-running-effects-in-development)。\n- コンポーネントは、ref のクリーンアップし忘れによるバグを見つけるために、[ref コールバックの実行を追加で 1 回行います](#fixing-bugs-found-by-re-running-ref-callbacks-in-development)。\n- コンポーネントが[非推奨の API を使用していないかチェック](#fixing-deprecation-warnings-enabled-by-strict-mode)します。\n\n#### props {/*props*/}\n\n`StrictMode` は props を受け付けません。\n\n#### 注意点 {/*caveats*/}\n\n* 一旦 `<StrictMode>` でラップされたツリー内で Strict Mode を無効化する方法はありません。これにより、`<StrictMode>` 内のすべてのコンポーネントがチェックされていることを確信できます。あるプロダクト内で、チェックに価値があると感じるかどうかに関して 2 つのチームの意見が割れた場合、合意に達するか、もしくは `<StrictMode>` をツリーの下側へ移動する必要があります。\n\n---\n\n## 使用法 {/*usage*/}\n\n### アプリ全体で Strict Mode を有効にする {/*enabling-strict-mode-for-entire-app*/}\n\nStrict Mode は、`<StrictMode>` コンポーネント内の全コンポーネントツリーに対して追加の開発時専用チェックを有効にします。これらのチェックは、開発プロセスの早い段階でコンポーネントの一般的なバグを見つけるのに役立ちます。\n\n\nアプリ全体で Strict Mode を有効にするには、ルートコンポーネントをレンダーする際にそれを `<StrictMode>` でラップします。\n\n```js {6,8}\nimport { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);\n```\n\n特に新しく作成されたアプリについては、アプリ全体を Strict Mode でラップすることをお勧めします。[`createRoot`](/reference/react-dom/client/createRoot) の呼び出しを自動的に行うフレームワークを使用している場合は、ドキュメンテーションを参照して Strict Mode を有効にする方法を確認してください。\n\nStrict Mode のチェックは**開発中にのみ実行される**ものですが、これらは既にコード内に存在するが本番環境での確実な再現が難しいバグを見つけるのに役立ちます。Strict Mode を使用することで、バグをユーザが報告してくる前に修正することができます。\n\n<Note>\n\nStrict Mode は開発中に以下のチェックを有効にします：\n\n- コンポーネントは、純粋でない (impure) レンダーによって引き起こされるバグを見つけるために、[レンダーを追加で 1 回行います](#fixing-bugs-found-by-double-rendering-in-development)。\n- コンポーネントは、エフェクトのクリーンアップし忘れによるバグを見つけるために、[エフェクトの実行を追加で 1 回行います](#fixing-bugs-found-by-re-running-effects-in-development)。\n- コンポーネントは、ref のクリーンアップし忘れによるバグを見つけるために、[ref コールバックの実行を追加で 1 回行います](#fixing-bugs-found-by-re-running-ref-callbacks-in-development)。\n- コンポーネントが[非推奨の API を使っていないかチェック](#fixing-deprecation-warnings-enabled-by-strict-mode)します。\n\n**これらのチェックはすべて開発環境専用であり、本番用ビルドには影響しません。**\n\n</Note>\n\n---\n\n### アプリの一部で Strict Mode を有効にする {/*enabling-strict-mode-for-a-part-of-the-app*/}\n\nアプリケーションの任意の一部分でのみ Strict Mode を有効にすることも可能です。\n\n```js {7,12}\nimport { StrictMode } from 'react';\n\nfunction App() {\n  return (\n    <>\n      <Header />\n      <StrictMode>\n        <main>\n          <Sidebar />\n          <Content />\n        </main>\n      </StrictMode>\n      <Footer />\n    </>\n  );\n}\n```\n\n上記の例では、Strict Mode のチェックは `Header` と `Footer` コンポーネントに対しては実行されません。しかし、`Sidebar` と `Content`、およびそれらの中にあるすべてのコンポーネントに対しては、どれだけ深いところにあってもチェックが実行されます。\n\n<Note>\n\n`StrictMode` がアプリの一部のみで有効になっている場合、React は本番環境で起こりえる動作のみを再現します。例えば、アプリのルートで `<StrictMode>` が有効になっていない場合、初期マウント時に[エフェクトを追加で再実行](#fixing-bugs-found-by-re-running-effects-in-development)することはなくなります。親エフェクトなしに子エフェクトが二重に発火することは本番環境では起こらないためです。\n\n</Note>\n\n---\n\n### 開発中の二重レンダーによって見つかったバグの修正 {/*fixing-bugs-found-by-double-rendering-in-development*/}\n\n[React は、あなたの書くすべてのコンポーネントが純関数 (pure function) であると仮定しています](/learn/keeping-components-pure)。これは、あなたが書く React コンポーネントは、同じ入力（props、state、context）が与えられた場合に常に同じ JSX を返さなければならないという意味です。\n\nこのルールを守らないコンポーネントは予測不能な挙動を示し、バグを引き起こします。うっかり純粋でなくなってしまったコードを見つけるために、Strict Mode はあなたの関数の一部（純粋であるべきものだけ）を**開発中に 2 回呼び出します**。これには以下が含まれます。\n\n- あなたのコンポーネント関数本体（トップレベルのロジックのみ。イベントハンドラ内のコードは含まれません。）\n- [`useState`](/reference/react/useState)、[`set` 関数](/reference/react/useState#setstate)、[`useMemo`](/reference/react/useMemo)、および [`useReducer`](/reference/react/useReducer) に渡す関数\n- [`constructor`](/reference/react/Component#constructor)、[`render`](/reference/react/Component#render)、[`shouldComponentUpdate`](/reference/react/Component#shouldcomponentupdate) などの一部のクラスコンポーネントメソッド（[全リストを見る](https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects)）\n\n関数が純粋であれば、結果は毎回同じになるので、2 回実行してもその振る舞いは変わりません。しかし、関数が純粋でない（例えば、受け取ったデータを書き換えている）場合、2 回実行することで目に見える影響が出る（まさにそれが純粋でないということです！）傾向があります。これにより、バグを早期に見つけて修正するのに役立ちます。\n\n**以下は、Strict Mode での二重レンダーがどのように早期にバグを見つけるのに役立つかを示す例です**。\n\nこの `StoryTray` コンポーネントは `stories` の配列を受け取り、その最後に \"Create Story\" という項目を加えて表示します。\n\n<Sandpack>\n\n```js src/index.js\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\n\nimport App from './App';\n\nconst root = createRoot(document.getElementById(\"root\"));\nroot.render(<App />);\n```\n\n```js src/App.js\nimport { useState } from 'react';\nimport StoryTray from './StoryTray.js';\n\nlet initialStories = [\n  {id: 0, label: \"Ankit's Story\" },\n  {id: 1, label: \"Taylor's Story\" },\n];\n\nexport default function App() {\n  let [stories, setStories] = useState(initialStories)\n  return (\n    <div\n      style={{\n        width: '100%',\n        height: '100%',\n        textAlign: 'center',\n      }}\n    >\n      <StoryTray stories={stories} />\n    </div>\n  );\n}\n```\n\n```js src/StoryTray.js active\nexport default function StoryTray({ stories }) {\n  const items = stories;\n  items.push({ id: 'create', label: 'Create Story' });\n  return (\n    <ul>\n      {items.map(story => (\n        <li key={story.id}>\n          {story.label}\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```css\nul {\n  margin: 0;\n  list-style-type: none;\n  height: 100%;\n  display: flex;\n  flex-wrap: wrap;\n  padding: 10px;\n}\n\nli {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  float: left;\n  margin: 5px;\n  padding: 5px;\n  width: 70px;\n  height: 100px;\n}\n```\n\n</Sandpack>\n\n上記のコードには間違いがあります。しかし、初回の出力は正しく見えるため、容易に見落としてしまいます。\n\n`StoryTray` コンポーネントが複数回レンダーされるとこの間違いに気付きやすくなります。例えば、マウスを `StoryTray` の上にホバーすると背景色を変えて `StoryTray` が再レンダーされるようにしてみましょう。\n\n<Sandpack>\n\n```js src/index.js\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\n\nimport App from './App';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(<App />);\n```\n\n```js src/App.js\nimport { useState } from 'react';\nimport StoryTray from './StoryTray.js';\n\nlet initialStories = [\n  {id: 0, label: \"Ankit's Story\" },\n  {id: 1, label: \"Taylor's Story\" },\n];\n\nexport default function App() {\n  let [stories, setStories] = useState(initialStories)\n  return (\n    <div\n      style={{\n        width: '100%',\n        height: '100%',\n        textAlign: 'center',\n      }}\n    >\n      <StoryTray stories={stories} />\n    </div>\n  );\n}\n```\n\n```js src/StoryTray.js active\nimport { useState } from 'react';\n\nexport default function StoryTray({ stories }) {\n  const [isHover, setIsHover] = useState(false);\n  const items = stories;\n  items.push({ id: 'create', label: 'Create Story' });\n  return (\n    <ul\n      onPointerEnter={() => setIsHover(true)}\n      onPointerLeave={() => setIsHover(false)}\n      style={{\n        backgroundColor: isHover ? '#ddd' : '#fff'\n      }}\n    >\n      {items.map(story => (\n        <li key={story.id}>\n          {story.label}\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```css\nul {\n  margin: 0;\n  list-style-type: none;\n  height: 100%;\n  display: flex;\n  flex-wrap: wrap;\n  padding: 10px;\n}\n\nli {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  float: left;\n  margin: 5px;\n  padding: 5px;\n  width: 70px;\n  height: 100px;\n}\n```\n\n</Sandpack>\n\n`StoryTray` コンポーネントの上にマウスをホバーするたびに、\"Create Story\" が再度リストに追加されることに注意してください。コードの意図は、最後に一度だけ追加することでした。しかし、`StoryTray` は props の `stories` 配列を直接書き換えています。`StoryTray` がレンダーされるたびに、同じ配列の最後に \"Create Story\" を再び追加しています。つまり、`StoryTray` は純関数ではなく、複数回実行することで異なる結果が返ってきます。\n\nこの問題を解決するためには、配列のコピーを作り、元の配列の代わりにそのコピーを書き換えるようにできます。\n\n```js {2}\nexport default function StoryTray({ stories }) {\n  const items = stories.slice(); // Clone the array\n  // ✅ Good: Pushing into a new array\n  items.push({ id: 'create', label: 'Create Story' });\n```\n\nこれにより、[`StoryTray` 関数は純関数になります](/learn/keeping-components-pure)。呼び出されるたびに、新しい配列のコピーだけが書き換わり、外部のオブジェクトや変数には影響を与えません。これによりバグは修正されましたが、振る舞いに問題があることに気付けるようになる前に、コンポーネントを通常より多く再レンダーする必要がありました。\n\n**この例では、バグが明らかではありませんでした。では、元の（バグがある）コードを `<StrictMode>` でラップしてみましょう：**\n\n<Sandpack>\n\n```js src/index.js\nimport { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\n\nimport App from './App';\n\nconst root = createRoot(document.getElementById(\"root\"));\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);\n```\n\n```js src/App.js\nimport { useState } from 'react';\nimport StoryTray from './StoryTray.js';\n\nlet initialStories = [\n  {id: 0, label: \"Ankit's Story\" },\n  {id: 1, label: \"Taylor's Story\" },\n];\n\nexport default function App() {\n  let [stories, setStories] = useState(initialStories)\n  return (\n    <div\n      style={{\n        width: '100%',\n        height: '100%',\n        textAlign: 'center',\n      }}\n    >\n      <StoryTray stories={stories} />\n    </div>\n  );\n}\n```\n\n```js src/StoryTray.js active\nexport default function StoryTray({ stories }) {\n  const items = stories;\n  items.push({ id: 'create', label: 'Create Story' });\n  return (\n    <ul>\n      {items.map(story => (\n        <li key={story.id}>\n          {story.label}\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```css\nul {\n  margin: 0;\n  list-style-type: none;\n  height: 100%;\n  display: flex;\n  flex-wrap: wrap;\n  padding: 10px;\n}\n\nli {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  float: left;\n  margin: 5px;\n  padding: 5px;\n  width: 70px;\n  height: 100px;\n}\n```\n\n</Sandpack>\n\n**Strict Mode は*常に*レンダー関数を 2 回呼び出すため、すぐに間違った結果が目に入ります**（\"Create Story\" が 2 回表示されます）。これにより、早期にこのような間違いに気づくことができます。Strict Mode でコンポーネントをレンダーするようにすることで、先ほどのホバー機能のような、将来本番環境で発生しうる多くのバグも、あらかじめ潰しておけるのです。\n\n<Sandpack>\n\n```js src/index.js\nimport { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\n\nimport App from './App';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);\n```\n\n```js src/App.js\nimport { useState } from 'react';\nimport StoryTray from './StoryTray.js';\n\nlet initialStories = [\n  {id: 0, label: \"Ankit's Story\" },\n  {id: 1, label: \"Taylor's Story\" },\n];\n\nexport default function App() {\n  let [stories, setStories] = useState(initialStories)\n  return (\n    <div\n      style={{\n        width: '100%',\n        height: '100%',\n        textAlign: 'center',\n      }}\n    >\n      <StoryTray stories={stories} />\n    </div>\n  );\n}\n```\n\n```js src/StoryTray.js active\nimport { useState } from 'react';\n\nexport default function StoryTray({ stories }) {\n  const [isHover, setIsHover] = useState(false);\n  const items = stories.slice(); // Clone the array\n  items.push({ id: 'create', label: 'Create Story' });\n  return (\n    <ul\n      onPointerEnter={() => setIsHover(true)}\n      onPointerLeave={() => setIsHover(false)}\n      style={{\n        backgroundColor: isHover ? '#ddd' : '#fff'\n      }}\n    >\n      {items.map(story => (\n        <li key={story.id}>\n          {story.label}\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```css\nul {\n  margin: 0;\n  list-style-type: none;\n  height: 100%;\n  display: flex;\n  flex-wrap: wrap;\n  padding: 10px;\n}\n\nli {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  float: left;\n  margin: 5px;\n  padding: 5px;\n  width: 70px;\n  height: 100px;\n}\n```\n\n</Sandpack>\n\nStrict Mode がなければ、再レンダーを追加しない限りバグを容易に見逃してしまう状態でした。Strict Mode は同じバグをすぐに見つかるようにします。Strict Mode は、チームやユーザに公開してしまう前にバグを見つけるのに役立ちます。\n\n[コンポーネントを純粋に保つ方法について詳しく読む](/learn/keeping-components-pure)\n\n<Note>\n\n[React DevTools](/learn/react-developer-tools) をインストールしている場合、2 回目のレンダー呼び出し中の `console.log` 呼び出しは少し暗く表示されます。React DevTools には、それらを完全に非表示にする設定もあります（デフォルトではオフ）。\n\n</Note>\n\n---\n\n### 開発中にエフェクトを再実行して見つかったバグの修正 {/*fixing-bugs-found-by-re-running-effects-in-development*/}\n\nStrict Mode は、[エフェクト](/learn/synchronizing-with-effects)のバグを見つけるのにも役立ちます。\n\nすべてのエフェクトにはセットアップコードがあり、一部のエフェクトにはクリーンアップコードもあります。通常、React はコンポーネントが*マウント*（画面に追加）されたときにセットアップコードを呼び出し、コンポーネントが*アンマウント*（画面から削除）されたときにクリーンアップコードを呼び出します。その後、前回のレンダー以降に依存配列が変更された場合、React は再度クリーンアップとセットアップを呼び出します。\n\nStrict Mode がオンの場合、React は開発中にすべてのエフェクトに対して **追加で 1 回、セットアップ+クリーンアップのサイクルを実行します**。この挙動に驚くかもしれませんが、手動で見つけるのが難しい微妙なバグを明らかにするのに役立ちます。\n\n**Strict Mode でエフェクトを再実行することが、早期にバグを見つけるのにどのように役立つかを示す例を示します。**\n\n以下の例では、コンポーネントをチャットに接続しています。\n\n<Sandpack>\n\n```js src/index.js\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\n\nimport App from './App';\n\nconst root = createRoot(document.getElementById(\"root\"));\nroot.render(<App />);\n```\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\nconst roomId = 'general';\n\nexport default function ChatRoom() {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n  }, []);\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n```\n\n```js src/chat.js\nlet connections = 0;\n\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n      connections++;\n      console.log('Active connections: ' + connections);\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n      connections--;\n      console.log('Active connections: ' + connections);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\nこのコードには問題がありますが、すぐには明らかではないかもしれません。\n\n問題を目立たせるため、機能を実装してみましょう。以下の例では、`roomId` はハードコードされておらず、代わりに、ユーザはドロップダウンから接続したい `roomId` を選択できます。\"Open chat\" をクリックし、次にひとつずつ異なるチャットルームを選択してください。コンソールでアクティブな接続の数を数えてみてください。\n\n<Sandpack>\n\n```js src/index.js\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\n\nimport App from './App';\n\nconst root = createRoot(document.getElementById(\"root\"));\nroot.render(<App />);\n```\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n  }, [roomId]);\n\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Close chat' : 'Open chat'}\n      </button>\n      {show && <hr />}\n      {show && <ChatRoom roomId={roomId} />}\n    </>\n  );\n}\n```\n\n```js src/chat.js\nlet connections = 0;\n\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n      connections++;\n      console.log('Active connections: ' + connections);\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n      connections--;\n      console.log('Active connections: ' + connections);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n開いている接続の数が増え続けていくことにお気づきでしょう。実際のアプリケーションでは、これによりパフォーマンスやネットワークの問題が発生します。問題は、[エフェクトにクリーンアップ関数がない](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed)ことです。\n\n```js {4}\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n```\n\nこれでエフェクトが自身を「クリーンアップ」し、古い接続を破棄するようになったので、リークは解消されました。しかし、問題が見えてくるのは、より多くの機能（選択ボックス）を追加した後でした。\n\n**元の例では、バグは明らかではありませんでした。では、元の（バグのある）コードを `<StrictMode>` でラップしてみましょう：**\n\n<Sandpack>\n\n```js src/index.js\nimport { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\n\nimport App from './App';\n\nconst root = createRoot(document.getElementById(\"root\"));\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);\n```\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\nconst roomId = 'general';\n\nexport default function ChatRoom() {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n  }, []);\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n```\n\n```js src/chat.js\nlet connections = 0;\n\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n      connections++;\n      console.log('Active connections: ' + connections);\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n      connections--;\n      console.log('Active connections: ' + connections);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n**Strict Mode を使用すると、すぐに問題があることがわかります**（アクティブな接続の数が 2 に跳ね上がります）。Strict Mode は、すべてのエフェクトに対してセットアップ+クリーンアップのサイクルを追加で実行します。このエフェクトにはクリーンアップロジックがないため、余分な接続が作成されても破棄されませんでした。これは、クリーンアップ関数が欠けていることを示すヒントです。\n\nStrict Mode を使用すると、このようなミスに早期に気付くことができます。Strict Mode でエフェクトにクリーンアップ関数を追加して修正することで、先ほどの選択ボックスのような、将来本番環境で発生しうる多くのバグも、あらかじめ潰しておけるのです。\n\n<Sandpack>\n\n```js src/index.js\nimport { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\n\nimport App from './App';\n\nconst root = createRoot(document.getElementById(\"root\"));\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);\n```\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Close chat' : 'Open chat'}\n      </button>\n      {show && <hr />}\n      {show && <ChatRoom roomId={roomId} />}\n    </>\n  );\n}\n```\n\n```js src/chat.js\nlet connections = 0;\n\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n      connections++;\n      console.log('Active connections: ' + connections);\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n      connections--;\n      console.log('Active connections: ' + connections);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\nコンソールに表示されるアクティブな接続の数が増えていかなくなったことに注目してください。\n\nStrict Mode がなければ、エフェクトがクリーンアップを必要としていることを容易に見逃すところでした。開発中にエフェクトに対して「*セットアップ → クリーンアップ → セットアップ*」を実行することで、Strict Mode はクリーンアップロジックが欠けていることにより気付きやすくしたのです。\n\n[エフェクトのクリーンアップの実装について詳しく読む](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development)\n\n---\n### 開発中に ref コールバックの再実行によって見つかったバグの修正 {/*fixing-bugs-found-by-re-running-ref-callbacks-in-development*/}\n\nStrict Mode は、[コールバック形式の ref](/learn/manipulating-the-dom-with-refs) のバグを見つけるのにも役立ちます。\n\nすべてのコールバック `ref` にはセットアップコードが含まれ、一部にはクリーンアップコードも含まれます。通常、React は要素が**作成されたとき**（DOM に追加されたとき）にセットアップを呼び出し、要素が（DOM から）**削除されたとき**にクリーンアップを呼び出します。\n\nStrict Mode が有効な場合、React は開発中に**すべてのコールバック `ref` に対して追加で 1 回、セットアップ+クリーンアップのサイクルを実行**します。この挙動に驚くかもしれませんが、手動で見つけるのが難しい微妙なバグを明らかにするのに役立ちます。\n\n以下の例を考えてみましょう。この例では、動物の種類を選択した後に、リスト内の動物のいずれかにスクロールすることができます。\"Cats\" から \"Dogs\" に切り替えると、コンソールのログに表示される動物の数が増え続けていき、\"Scroll to\" ボタンが機能しなくなるのがわかります。\n\n<Sandpack>\n\n```js src/index.js\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\n\nimport App from './App';\n\nconst root = createRoot(document.getElementById(\"root\"));\n// ❌ Not using StrictMode.\nroot.render(<App />);\n```\n\n```js src/App.js active\nimport { useRef, useState } from \"react\";\n\nexport default function CatFriends() {\n  const itemsRef = useRef([]);\n  const [catList, setCatList] = useState(setupCatList);\n  const [cat, setCat] = useState('neo');\n\n  function scrollToCat(index) {\n    const list = itemsRef.current;\n    const {node} = list[index];\n    node.scrollIntoView({\n      behavior: \"smooth\",\n      block: \"nearest\",\n      inline: \"center\",\n    });\n  }\n\n  const cats = catList.filter(c => c.type === cat)\n\n  return (\n    <>\n      <nav>\n        <button onClick={() => setCat('neo')}>Neo</button>\n        <button onClick={() => setCat('millie')}>Millie</button>\n      </nav>\n      <hr />\n      <nav>\n        <span>Scroll to:</span>{cats.map((cat, index) => (\n          <button key={cat.src} onClick={() => scrollToCat(index)}>\n            {index}\n          </button>\n        ))}\n      </nav>\n      <div>\n        <ul>\n          {cats.map((cat) => (\n            <li\n              key={cat.src}\n              ref={(node) => {\n                const list = itemsRef.current;\n                const item = {cat: cat, node};\n                list.push(item);\n                console.log(`✅ Adding cat to the map. Total cats: ${list.length}`);\n                if (list.length > 10) {\n                  console.log('❌ Too many cats in the list!');\n                }\n                return () => {\n                  // 🚩 No cleanup, this is a bug!\n                }\n              }}\n            >\n              <img src={cat.src} />\n            </li>\n          ))}\n        </ul>\n      </div>\n    </>\n  );\n}\n\nfunction setupCatList() {\n  const catList = [];\n  for (let i = 0; i < 10; i++) {\n    catList.push({type: 'neo', src: \"https://placecats.com/neo/320/240?\" + i});\n  }\n  for (let i = 0; i < 10; i++) {\n    catList.push({type: 'millie', src: \"https://placecats.com/millie/320/240?\" + i});\n  }\n\n  return catList;\n}\n\n```\n\n```css\ndiv {\n  width: 100%;\n  overflow: hidden;\n}\n\nnav {\n  text-align: center;\n}\n\nbutton {\n  margin: .25rem;\n}\n\nul,\nli {\n  list-style: none;\n  white-space: nowrap;\n}\n\nli {\n  display: inline;\n  padding: 0.5rem;\n}\n```\n\n</Sandpack>\n\n\n**これは本番環境でのバグです！** ref コールバックのクリーンアップでリストから動物を削除していないため、動物のリストが増え続けていっています。これはメモリリークであり、本番環境でパフォーマンスの問題や動作の不具合を引き起こします。\n\n問題は ref コールバックがクリーンアップを正しく行っていないことです。\n\n```js {6-8}\n<li\n  ref={node => {\n    const list = itemsRef.current;\n    const item = {animal, node};\n    list.push(item);\n    return () => {\n      // 🚩 No cleanup, this is a bug!\n    }\n  }}\n</li>\n```\n\n元の（バグのある）例を `<StrictMode>` でラップしてみましょう。\n\n<Sandpack>\n\n```js src/index.js\nimport { createRoot } from 'react-dom/client';\nimport {StrictMode} from 'react';\nimport './styles.css';\n\nimport App from './App';\n\nconst root = createRoot(document.getElementById(\"root\"));\n// ✅ Using StrictMode.\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);\n```\n\n```js src/App.js active\nimport { useRef, useState } from \"react\";\n\nexport default function CatFriends() {\n  const itemsRef = useRef([]);\n  const [catList, setCatList] = useState(setupCatList);\n  const [cat, setCat] = useState('neo');\n\n  function scrollToCat(index) {\n    const list = itemsRef.current;\n    const {node} = list[index];\n    node.scrollIntoView({\n      behavior: \"smooth\",\n      block: \"nearest\",\n      inline: \"center\",\n    });\n  }\n\n  const cats = catList.filter(c => c.type === cat)\n\n  return (\n    <>\n      <nav>\n        <button onClick={() => setCat('neo')}>Neo</button>\n        <button onClick={() => setCat('millie')}>Millie</button>\n      </nav>\n      <hr />\n      <nav>\n        <span>Scroll to:</span>{cats.map((cat, index) => (\n          <button key={cat.src} onClick={() => scrollToCat(index)}>\n            {index}\n          </button>\n        ))}\n      </nav>\n      <div>\n        <ul>\n          {cats.map((cat) => (\n            <li\n              key={cat.src}\n              ref={(node) => {\n                const list = itemsRef.current;\n                const item = {cat: cat, node};\n                list.push(item);\n                console.log(`✅ Adding cat to the map. Total cats: ${list.length}`);\n                if (list.length > 10) {\n                  console.log('❌ Too many cats in the list!');\n                }\n                return () => {\n                  // 🚩 No cleanup, this is a bug!\n                }\n              }}\n            >\n              <img src={cat.src} />\n            </li>\n          ))}\n        </ul>\n      </div>\n    </>\n  );\n}\n\nfunction setupCatList() {\n  const catList = [];\n  for (let i = 0; i < 10; i++) {\n    catList.push({type: 'neo', src: \"https://placecats.com/neo/320/240?\" + i});\n  }\n  for (let i = 0; i < 10; i++) {\n    catList.push({type: 'millie', src: \"https://placecats.com/millie/320/240?\" + i});\n  }\n\n  return catList;\n}\n\n```\n\n```css\ndiv {\n  width: 100%;\n  overflow: hidden;\n}\n\nnav {\n  text-align: center;\n}\n\nbutton {\n  margin: .25rem;\n}\n\nul,\nli {\n  list-style: none;\n  white-space: nowrap;\n}\n\nli {\n  display: inline;\n  padding: 0.5rem;\n}\n```\n\n</Sandpack>\n\n**Strict Mode を使用することで、即座に問題に気づけるようになります**。Strict Mode では、すべてのコールバック `ref` に対して追加のセットアップ+クリーンアップサイクルが実行されます。このコールバック `ref` にはクリーンアップロジックがないため、ref は追加されるだけで削除されません。これはクリーンアップ関数が欠けていることを示すヒントです。\n\nStrict Mode を使うことで、コールバック `ref` のミスを積極的に見つけだすことができます。Strict Mode を使いクリーンアップ関数を追加してコールバックを修正することで、先ほどの \"Scroll to\" のような、将来本番環境で発生しうる多くのバグも、あらかじめ潰しておけるのです。\n\n<Sandpack>\n\n```js src/index.js\nimport { createRoot } from 'react-dom/client';\nimport {StrictMode} from 'react';\nimport './styles.css';\n\nimport App from './App';\n\nconst root = createRoot(document.getElementById(\"root\"));\n// ✅ Using StrictMode.\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);\n```\n\n```js src/App.js active\nimport { useRef, useState } from \"react\";\n\nexport default function CatFriends() {\n  const itemsRef = useRef([]);\n  const [catList, setCatList] = useState(setupCatList);\n  const [cat, setCat] = useState('neo');\n\n  function scrollToCat(index) {\n    const list = itemsRef.current;\n    const {node} = list[index];\n    node.scrollIntoView({\n      behavior: \"smooth\",\n      block: \"nearest\",\n      inline: \"center\",\n    });\n  }\n\n  const cats = catList.filter(c => c.type === cat)\n\n  return (\n    <>\n      <nav>\n        <button onClick={() => setCat('neo')}>Neo</button>\n        <button onClick={() => setCat('millie')}>Millie</button>\n      </nav>\n      <hr />\n      <nav>\n        <span>Scroll to:</span>{cats.map((cat, index) => (\n          <button key={cat.src} onClick={() => scrollToCat(index)}>\n            {index}\n          </button>\n        ))}\n      </nav>\n      <div>\n        <ul>\n          {cats.map((cat) => (\n            <li\n              key={cat.src}\n              ref={(node) => {\n                const list = itemsRef.current;\n                const item = {cat: cat, node};\n                list.push(item);\n                console.log(`✅ Adding cat to the map. Total cats: ${list.length}`);\n                if (list.length > 10) {\n                  console.log('❌ Too many cats in the list!');\n                }\n                return () => {\n                  list.splice(list.indexOf(item), 1);\n                  console.log(`❌ Removing cat from the map. Total cats: ${itemsRef.current.length}`);\n                }\n              }}\n            >\n              <img src={cat.src} />\n            </li>\n          ))}\n        </ul>\n      </div>\n    </>\n  );\n}\n\nfunction setupCatList() {\n  const catList = [];\n  for (let i = 0; i < 10; i++) {\n    catList.push({type: 'neo', src: \"https://placecats.com/neo/320/240?\" + i});\n  }\n  for (let i = 0; i < 10; i++) {\n    catList.push({type: 'millie', src: \"https://placecats.com/millie/320/240?\" + i});\n  }\n\n  return catList;\n}\n\n```\n\n```css\ndiv {\n  width: 100%;\n  overflow: hidden;\n}\n\nnav {\n  text-align: center;\n}\n\nbutton {\n  margin: .25rem;\n}\n\nul,\nli {\n  list-style: none;\n  white-space: nowrap;\n}\n\nli {\n  display: inline;\n  padding: 0.5rem;\n}\n```\n\n</Sandpack>\n\nStrictMode が有効になると、初回レンダー時に ref コールバックがセットアップされ、クリーンアップされ、またセットアップされます。\n\n```\n...\n✅ Adding animal to the map. Total animals: 10\n...\n❌ Removing animal from the map. Total animals: 0\n...\n✅ Adding animal to the map. Total animals: 10\n```\n\n**この挙動は問題ありません**。Strict Mode により、ref コールバックが正しくクリーンアップされており、予想外にサイズが大きくならないことを確認できます。修正後にはメモリリークはなくなり、すべての機能が予期したとおりに動作するようになります。\n\nStrict Mode がなければ、アプリをクリックして動かない機能があることに気付くまで、このバグは見逃される危険がありました。Strict Mode により、本番環境に投入するプッシュする前にバグがすぐに明らかになったのです。\n\n---\n### Strict Mode によって現れるようになった非推奨警告の修正 {/*fixing-deprecation-warnings-enabled-by-strict-mode*/}\n\nReact は、`<StrictMode>` ツリー内のいずれかのコンポーネントが以下の非推奨 API を使用している場合に警告を発します。\n\n* `UNSAFE_` クラスライフサイクルメソッド（[`UNSAFE_componentWillMount`](/reference/react/Component#unsafe_componentwillmount) など）。[代替手段を見る](https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#migrating-from-legacy-lifecycles) \n\nこれらの API は主に古い[クラスコンポーネント](/reference/react/Component)で使用されているものであり、現在のアプリケーションではほとんど見られません。\n"
  },
  {
    "path": "src/content/reference/react/Suspense.md",
    "content": "---\ntitle: <Suspense>\n---\n\n<Intro>\n\n`<Suspense>` を使うことで、子要素が読み込みを完了するまでフォールバックを表示させることができます。\n\n\n```js\n<Suspense fallback={<Loading />}>\n  <SomeComponent />\n</Suspense>\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<Suspense>` {/*suspense*/}\n\n#### props {/*props*/}\n* `children`: レンダーしようとしている実際の UI です。`children` がレンダー中にサスペンド（suspend, 一時中断）すると、サスペンスバウンダリは `fallback` のレンダーに切り替わります。\n* `fallback`: 実際の UI がまだ読み込みを完了していない場合に、その代わりにレンダーする代替 UI です。有効な React ノードであれば何でも受け付けますが、現実的には、フォールバックとは軽量なプレースホルダビュー、つまりローディングスピナやスケルトンのようなものです。`children` がサスペンドすると、サスペンスは自動的に `fallback` に切り替わり、データが準備できたら `children` に戻ります。`fallback` 自体がレンダー中にサスペンドした場合、親のサスペンスバウンダリのうち最も近いものがアクティブになります。\n\n#### 注意点 {/*caveats*/}\n\n- React は、初回マウントが成功するより前にサスペンドしたレンダーに関しては、一切の state を保持しません。コンポーネントが読み込まれたときに、React はサスペンドしていたツリーのレンダーを最初からやり直します。\n- すでにツリーにコンテンツを表示していたサスペンスが再度サスペンドした場合、`fallback` が再び表示されます。しかしその更新が [`startTransition`](/reference/react/startTransition) または [`useDeferredValue`](/reference/react/useDeferredValue) によって引き起こされた場合を除きます。\n- 既に表示されているコンテンツが再度サスペンドしたために React がそれを隠す必要が生じた場合、React はコンテンツツリーの[レイアウトエフェクト](/reference/react/useLayoutEffect)をクリーンアップします。コンテンツが再度表示できるようになったら、React はレイアウトエフェクトを再度実行します。これにより、DOM レイアウトを測定するエフェクトがコンテンツが隠れている間に測定を試みないようにします。\n- React には、*ストリーミングサーバレンダリング*や*選択的ハイドレーション*などの、サスペンスと統合された自動的な最適化が含まれています。詳しくは、[アーキテクチャの概要](https://github.com/reactwg/react-18/discussions/37)や[テクニカルトーク](https://www.youtube.com/watch?v=pj5N-Khihgc)を参照してください。\n\n---\n\n## 使用法 {/*usage*/}\n\n### コンテンツの読み込み中にフォールバックを表示する {/*displaying-a-fallback-while-content-is-loading*/}\n\nアプリケーションの任意の部分をサスペンスバウンダリでラップできます。\n\n```js [[1, 1, \"<Loading />\"], [2, 2, \"<Albums />\"]]\n<Suspense fallback={<Loading />}>\n  <Albums />\n</Suspense>\n```\n\nReact は、<CodeStep step={2}>子要素</CodeStep>が必要とするすべてのコードとデータが読み込まれるまで、<CodeStep step={1}>ロード中のフォールバック</CodeStep>を表示します。\n\n以下の例では、`Albums` コンポーネントはアルバムのリストをフェッチする間、*サスペンド*します。レンダーの準備が整うまで、React は上にある最も近いサスペンスバウンダリを、フォールバック（`Loading` コンポーネント）を表示するように切り替えます。その後データが読み込まれると、React は `Loading` フォールバックを非表示にし、データとともに `Albums` コンポーネントをレンダーします。\n\n<Sandpack>\n\n```js src/App.js hidden\nimport { useState } from 'react';\nimport ArtistPage from './ArtistPage.js';\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  if (show) {\n    return (\n      <ArtistPage\n        artist={{\n          id: 'the-beatles',\n          name: 'The Beatles',\n        }}\n      />\n    );\n  } else {\n    return (\n      <button onClick={() => setShow(true)}>\n        Open The Beatles artist page\n      </button>\n    );\n  }\n}\n```\n\n```js src/ArtistPage.js active\nimport { Suspense } from 'react';\nimport Albums from './Albums.js';\n\nexport default function ArtistPage({ artist }) {\n  return (\n    <>\n      <h1>{artist.name}</h1>\n      <Suspense fallback={<Loading />}>\n        <Albums artistId={artist.id} />\n      </Suspense>\n    </>\n  );\n}\n\nfunction Loading() {\n  return <h2>🌀 Loading...</h2>;\n}\n```\n\n```js src/Albums.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Albums({ artistId }) {\n  const albums = use(fetchData(`/${artistId}/albums`));\n  return (\n    <ul>\n      {albums.map(album => (\n        <li key={album.id}>\n          {album.title} ({album.year})\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url === '/the-beatles/albums') {\n    return await getAlbums();\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getAlbums() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 3000);\n  });\n\n  return [{\n    id: 13,\n    title: 'Let It Be',\n    year: 1970\n  }, {\n    id: 12,\n    title: 'Abbey Road',\n    year: 1969\n  }, {\n    id: 11,\n    title: 'Yellow Submarine',\n    year: 1969\n  }, {\n    id: 10,\n    title: 'The Beatles',\n    year: 1968\n  }, {\n    id: 9,\n    title: 'Magical Mystery Tour',\n    year: 1967\n  }, {\n    id: 8,\n    title: 'Sgt. Pepper\\'s Lonely Hearts Club Band',\n    year: 1967\n  }, {\n    id: 7,\n    title: 'Revolver',\n    year: 1966\n  }, {\n    id: 6,\n    title: 'Rubber Soul',\n    year: 1965\n  }, {\n    id: 5,\n    title: 'Help!',\n    year: 1965\n  }, {\n    id: 4,\n    title: 'Beatles For Sale',\n    year: 1964\n  }, {\n    id: 3,\n    title: 'A Hard Day\\'s Night',\n    year: 1964\n  }, {\n    id: 2,\n    title: 'With The Beatles',\n    year: 1963\n  }, {\n    id: 1,\n    title: 'Please Please Me',\n    year: 1963\n  }];\n}\n```\n\n</Sandpack>\n\n<Note>\n\n**サスペンスコンポーネントをアクティブ化できるのはサスペンス対応のデータソースだけです**。これには以下が含まれます：\n\n- [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) や [Next.js](https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense) のようなサスペンス対応のフレームワークでのデータフェッチ\n- [`lazy`](/reference/react/lazy) を用いたコンポーネントコードの遅延ロード\n- [`use`](/reference/react/use) を用いたキャッシュ済みプロミス (Promise) からの値の読み取り\n\nサスペンスはエフェクトやイベントハンドラ内でデータフェッチが行われた場合にはそれを**検出しません**。\n\n上記の `Albums` コンポーネントで実際にデータをロードする方法は、使用するフレームワークによって異なります。サスペンス対応のフレームワークを使用している場合、詳細はデータフェッチに関するドキュメンテーション内に記載されているはずです。\n\n使い方の規約のある (opinionated) フレームワークを使用せずにサスペンスを使ったデータフェッチを行うことは、まだサポートされていません。サスペンス対応のデータソースを実装するための要件はまだ不安定であり、ドキュメント化されていません。データソースをサスペンスと統合するための公式な API は、React の将来のバージョンでリリースされる予定です。\n\n</Note>\n\n---\n\n### コンテンツを一度にまとめて表示する {/*revealing-content-together-at-once*/}\n\nデフォルトでは、サスペンス内のすべてのツリーはひとつの単位として扱われます。例えば、以下のコンポーネントのうち*どれかひとつでも*データ待ちでサスペンドしていれば、*すべて*がまとめてローディングインジケータに置き換わります。\n\n```js {2-5}\n<Suspense fallback={<Loading />}>\n  <Biography />\n  <Panel>\n    <Albums />\n  </Panel>\n</Suspense>\n```\n\nその後、すべてが表示可能になった時点で、一斉に表示されます。\n\n以下の例では、`Biography` と `Albums` の両方がデータをフェッチしています。しかし、単一のサスペンスバウンダリの下でグループ化されているため、これらのコンポーネントは常に同時に「表示スタート」となります。\n\n<Sandpack>\n\n```js src/App.js hidden\nimport { useState } from 'react';\nimport ArtistPage from './ArtistPage.js';\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  if (show) {\n    return (\n      <ArtistPage\n        artist={{\n          id: 'the-beatles',\n          name: 'The Beatles',\n        }}\n      />\n    );\n  } else {\n    return (\n      <button onClick={() => setShow(true)}>\n        Open The Beatles artist page\n      </button>\n    );\n  }\n}\n```\n\n```js src/ArtistPage.js active\nimport { Suspense } from 'react';\nimport Albums from './Albums.js';\nimport Biography from './Biography.js';\nimport Panel from './Panel.js';\n\nexport default function ArtistPage({ artist }) {\n  return (\n    <>\n      <h1>{artist.name}</h1>\n      <Suspense fallback={<Loading />}>\n        <Biography artistId={artist.id} />\n        <Panel>\n          <Albums artistId={artist.id} />\n        </Panel>\n      </Suspense>\n    </>\n  );\n}\n\nfunction Loading() {\n  return <h2>🌀 Loading...</h2>;\n}\n```\n\n```js src/Panel.js\nexport default function Panel({ children }) {\n  return (\n    <section className=\"panel\">\n      {children}\n    </section>\n  );\n}\n```\n\n```js src/Biography.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Biography({ artistId }) {\n  const bio = use(fetchData(`/${artistId}/bio`));\n  return (\n    <section>\n      <p className=\"bio\">{bio}</p>\n    </section>\n  );\n}\n```\n\n```js src/Albums.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Albums({ artistId }) {\n  const albums = use(fetchData(`/${artistId}/albums`));\n  return (\n    <ul>\n      {albums.map(album => (\n        <li key={album.id}>\n          {album.title} ({album.year})\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url === '/the-beatles/albums') {\n    return await getAlbums();\n  } else if (url === '/the-beatles/bio') {\n    return await getBio();\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getBio() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 1500);\n  });\n\n  return `The Beatles were an English rock band, \n    formed in Liverpool in 1960, that comprised \n    John Lennon, Paul McCartney, George Harrison \n    and Ringo Starr.`;\n}\n\nasync function getAlbums() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 3000);\n  });\n\n  return [{\n    id: 13,\n    title: 'Let It Be',\n    year: 1970\n  }, {\n    id: 12,\n    title: 'Abbey Road',\n    year: 1969\n  }, {\n    id: 11,\n    title: 'Yellow Submarine',\n    year: 1969\n  }, {\n    id: 10,\n    title: 'The Beatles',\n    year: 1968\n  }, {\n    id: 9,\n    title: 'Magical Mystery Tour',\n    year: 1967\n  }, {\n    id: 8,\n    title: 'Sgt. Pepper\\'s Lonely Hearts Club Band',\n    year: 1967\n  }, {\n    id: 7,\n    title: 'Revolver',\n    year: 1966\n  }, {\n    id: 6,\n    title: 'Rubber Soul',\n    year: 1965\n  }, {\n    id: 5,\n    title: 'Help!',\n    year: 1965\n  }, {\n    id: 4,\n    title: 'Beatles For Sale',\n    year: 1964\n  }, {\n    id: 3,\n    title: 'A Hard Day\\'s Night',\n    year: 1964\n  }, {\n    id: 2,\n    title: 'With The Beatles',\n    year: 1963\n  }, {\n    id: 1,\n    title: 'Please Please Me',\n    year: 1963\n  }];\n}\n```\n\n```css\n.bio { font-style: italic; }\n\n.panel {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n}\n```\n\n</Sandpack>\n\nデータをロードするコンポーネントは、サスペンスバウンダリの直接の子である必要はありません。例えば、`Biography` と `Albums` を新しい `Details` コンポーネント内に移動することができます。これによって振る舞いは変わりません。`Biography` と `Albums` の最も近い親のサスペンスバウンダリは同じですので、その表示開始は同時になるよう調整されます。\n\n```js {2,8-11}\n<Suspense fallback={<Loading />}>\n  <Details artistId={artist.id} />\n</Suspense>\n\nfunction Details({ artistId }) {\n  return (\n    <>\n      <Biography artistId={artistId} />\n      <Panel>\n        <Albums artistId={artistId} />\n      </Panel>\n    </>\n  );\n}\n```\n\n---\n\n### ネストされたコンテンツをロード順に表示する {/*revealing-nested-content-as-it-loads*/}\n\nコンポーネントがサスペンドすると、最も近い親のサスペンスコンポーネントがフォールバックを表示します。この仕組みを使うと、複数のサスペンスコンポーネントをネストして、段階的なロードを構築することができます。各サスペンスバウンダリのフォールバックは、1 レベル下にあるコンテンツが利用可能になると実コンテンツで置き換わります。例えば、アルバムリストだけに独自のフォールバックを設定することができます。\n\n```js {3,7}\n<Suspense fallback={<BigSpinner />}>\n  <Biography />\n  <Suspense fallback={<AlbumsGlimmer />}>\n    <Panel>\n      <Albums />\n    </Panel>\n  </Suspense>\n</Suspense>\n```\n\nこれにより、`Biography` の表示が `Albums` のロードを「待つ」必要はなくなります。\n\n以下のような順番になります。\n\n1. `Biography` がまだロードされていない場合、`BigSpinner` が全体のコンテンツエリアの代わりに表示されます。\n2. `Biography` のロードが完了すると、`BigSpinner` はコンテンツに置き換えられます。\n3. `Albums` がまだロードされていない場合、`AlbumsGlimmer` が `Albums` とその親の `Panel` の代わりに表示されます。\n4. 最後に、`Albums` のロードが完了すると、それが `AlbumsGlimmer` を置き換えて表示されます。\n\n<Sandpack>\n\n```js src/App.js hidden\nimport { useState } from 'react';\nimport ArtistPage from './ArtistPage.js';\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  if (show) {\n    return (\n      <ArtistPage\n        artist={{\n          id: 'the-beatles',\n          name: 'The Beatles',\n        }}\n      />\n    );\n  } else {\n    return (\n      <button onClick={() => setShow(true)}>\n        Open The Beatles artist page\n      </button>\n    );\n  }\n}\n```\n\n```js src/ArtistPage.js active\nimport { Suspense } from 'react';\nimport Albums from './Albums.js';\nimport Biography from './Biography.js';\nimport Panel from './Panel.js';\n\nexport default function ArtistPage({ artist }) {\n  return (\n    <>\n      <h1>{artist.name}</h1>\n      <Suspense fallback={<BigSpinner />}>\n        <Biography artistId={artist.id} />\n        <Suspense fallback={<AlbumsGlimmer />}>\n          <Panel>\n            <Albums artistId={artist.id} />\n          </Panel>\n        </Suspense>\n      </Suspense>\n    </>\n  );\n}\n\nfunction BigSpinner() {\n  return <h2>🌀 Loading...</h2>;\n}\n\nfunction AlbumsGlimmer() {\n  return (\n    <div className=\"glimmer-panel\">\n      <div className=\"glimmer-line\" />\n      <div className=\"glimmer-line\" />\n      <div className=\"glimmer-line\" />\n    </div>\n  );\n}\n```\n\n```js src/Panel.js\nexport default function Panel({ children }) {\n  return (\n    <section className=\"panel\">\n      {children}\n    </section>\n  );\n}\n```\n\n```js src/Biography.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Biography({ artistId }) {\n  const bio = use(fetchData(`/${artistId}/bio`));\n  return (\n    <section>\n      <p className=\"bio\">{bio}</p>\n    </section>\n  );\n}\n```\n\n```js src/Albums.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Albums({ artistId }) {\n  const albums = use(fetchData(`/${artistId}/albums`));\n  return (\n    <ul>\n      {albums.map(album => (\n        <li key={album.id}>\n          {album.title} ({album.year})\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url === '/the-beatles/albums') {\n    return await getAlbums();\n  } else if (url === '/the-beatles/bio') {\n    return await getBio();\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getBio() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 500);\n  });\n\n  return `The Beatles were an English rock band, \n    formed in Liverpool in 1960, that comprised \n    John Lennon, Paul McCartney, George Harrison \n    and Ringo Starr.`;\n}\n\nasync function getAlbums() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 3000);\n  });\n\n  return [{\n    id: 13,\n    title: 'Let It Be',\n    year: 1970\n  }, {\n    id: 12,\n    title: 'Abbey Road',\n    year: 1969\n  }, {\n    id: 11,\n    title: 'Yellow Submarine',\n    year: 1969\n  }, {\n    id: 10,\n    title: 'The Beatles',\n    year: 1968\n  }, {\n    id: 9,\n    title: 'Magical Mystery Tour',\n    year: 1967\n  }, {\n    id: 8,\n    title: 'Sgt. Pepper\\'s Lonely Hearts Club Band',\n    year: 1967\n  }, {\n    id: 7,\n    title: 'Revolver',\n    year: 1966\n  }, {\n    id: 6,\n    title: 'Rubber Soul',\n    year: 1965\n  }, {\n    id: 5,\n    title: 'Help!',\n    year: 1965\n  }, {\n    id: 4,\n    title: 'Beatles For Sale',\n    year: 1964\n  }, {\n    id: 3,\n    title: 'A Hard Day\\'s Night',\n    year: 1964\n  }, {\n    id: 2,\n    title: 'With The Beatles',\n    year: 1963\n  }, {\n    id: 1,\n    title: 'Please Please Me',\n    year: 1963\n  }];\n}\n```\n\n```css\n.bio { font-style: italic; }\n\n.panel {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n}\n\n.glimmer-panel {\n  border: 1px dashed #aaa;\n  background: linear-gradient(90deg, rgba(221,221,221,1) 0%, rgba(255,255,255,1) 100%);\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n}\n\n.glimmer-line {\n  display: block;\n  width: 60%;\n  height: 20px;\n  margin: 10px;\n  border-radius: 4px;\n  background: #f0f0f0;\n}\n```\n\n</Sandpack>\n\nサスペンスバウンダリを使用することで、UI のどの部分は同時に「表示スタート」すべきで、どの部分はロード状態の進行につれて徐々にコンテンツを表示すべきなのか、調整を行えます。ツリー内のどこでサスペンスバウンダリの追加、移動、あるいは削除を行っても、アプリの他の動作に影響を与えることはありません。\n\nあらゆるコンポーネントの周りにサスペンスバウンダリを置こうとしないようにしてください。ユーザに見せたいロードの各ステップよりもサスペンスバウンダリを細かく設置すべきではありません。デザイナと一緒に作業している場合は、ロード中表示をどこに配置するべきか尋ねてみてください。おそらく、それはデザインの枠組みにすでに含まれているでしょう。\n\n---\n\n### 新しいコンテンツのロード中に古いコンテンツを表示する {/*showing-stale-content-while-fresh-content-is-loading*/}\n\nこの例では、`SearchResults` コンポーネントは検索結果をフェッチする間サスペンドします。`\"a\"` を入力し、結果を待ってから `\"ab\"` に書き換えてみてください。`\"a\"` の検索結果が、ロード中フォールバックに置換されてしまいます。\n\n<Sandpack>\n\n```js src/App.js\nimport { Suspense, useState } from 'react';\nimport SearchResults from './SearchResults.js';\n\nexport default function App() {\n  const [query, setQuery] = useState('');\n  return (\n    <>\n      <label>\n        Search albums:\n        <input value={query} onChange={e => setQuery(e.target.value)} />\n      </label>\n      <Suspense fallback={<h2>Loading...</h2>}>\n        <SearchResults query={query} />\n      </Suspense>\n    </>\n  );\n}\n```\n\n```js src/SearchResults.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function SearchResults({ query }) {\n  if (query === '') {\n    return null;\n  }\n  const albums = use(fetchData(`/search?q=${query}`));\n  if (albums.length === 0) {\n    return <p>No matches for <i>\"{query}\"</i></p>;\n  }\n  return (\n    <ul>\n      {albums.map(album => (\n        <li key={album.id}>\n          {album.title} ({album.year})\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url.startsWith('/search?q=')) {\n    return await getSearchResults(url.slice('/search?q='.length));\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getSearchResults(query) {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 500);\n  });\n\n  const allAlbums = [{\n    id: 13,\n    title: 'Let It Be',\n    year: 1970\n  }, {\n    id: 12,\n    title: 'Abbey Road',\n    year: 1969\n  }, {\n    id: 11,\n    title: 'Yellow Submarine',\n    year: 1969\n  }, {\n    id: 10,\n    title: 'The Beatles',\n    year: 1968\n  }, {\n    id: 9,\n    title: 'Magical Mystery Tour',\n    year: 1967\n  }, {\n    id: 8,\n    title: 'Sgt. Pepper\\'s Lonely Hearts Club Band',\n    year: 1967\n  }, {\n    id: 7,\n    title: 'Revolver',\n    year: 1966\n  }, {\n    id: 6,\n    title: 'Rubber Soul',\n    year: 1965\n  }, {\n    id: 5,\n    title: 'Help!',\n    year: 1965\n  }, {\n    id: 4,\n    title: 'Beatles For Sale',\n    year: 1964\n  }, {\n    id: 3,\n    title: 'A Hard Day\\'s Night',\n    year: 1964\n  }, {\n    id: 2,\n    title: 'With The Beatles',\n    year: 1963\n  }, {\n    id: 1,\n    title: 'Please Please Me',\n    year: 1963\n  }];\n\n  const lowerQuery = query.trim().toLowerCase();\n  return allAlbums.filter(album => {\n    const lowerTitle = album.title.toLowerCase();\n    return (\n      lowerTitle.startsWith(lowerQuery) ||\n      lowerTitle.indexOf(' ' + lowerQuery) !== -1\n    )\n  });\n}\n```\n\n```css\ninput { margin: 10px; }\n```\n\n</Sandpack>\n\nこの代わりに一般的に使われる UI パターンは、結果リストの更新を*遅延*させて、新しい結果が準備できるまで前の結果を表示し続けるというものです。[`useDeferredValue`](/reference/react/useDeferredValue) フックを使うことで遅延されたバージョンのクエリ文字列を下に渡すことができます。\n\n```js {3,11}\nexport default function App() {\n  const [query, setQuery] = useState('');\n  const deferredQuery = useDeferredValue(query);\n  return (\n    <>\n      <label>\n        Search albums:\n        <input value={query} onChange={e => setQuery(e.target.value)} />\n      </label>\n      <Suspense fallback={<h2>Loading...</h2>}>\n        <SearchResults query={deferredQuery} />\n      </Suspense>\n    </>\n  );\n}\n```\n\n`query` の方はすぐに更新されるため、入力フィールドは新しい値を表示します。しかし、`deferredQuery` はデータが読み込まれるまで前の値を保持するため、`SearchResults` はしばらく古い結果を表示します。\n\nユーザにより明確に状況を伝えるため、古い結果リストが表示されているときにインジケータを表示することができます。\n\n```js {2}\n<div style={{\n  opacity: query !== deferredQuery ? 0.5 : 1 \n}}>\n  <SearchResults query={deferredQuery} />\n</div>\n```\n\n以下の例で `\"a\"` を入力し、結果がロードされるのを待ち、次に入力フィールドを `\"ab\"` に編集してみてください。新しい結果がロードされるまで、サスペンスのフォールバックの代わりに、暗くなった古い結果リストが表示されることに気づくでしょう。\n\n\n<Sandpack>\n\n```js src/App.js\nimport { Suspense, useState, useDeferredValue } from 'react';\nimport SearchResults from './SearchResults.js';\n\nexport default function App() {\n  const [query, setQuery] = useState('');\n  const deferredQuery = useDeferredValue(query);\n  const isStale = query !== deferredQuery;\n  return (\n    <>\n      <label>\n        Search albums:\n        <input value={query} onChange={e => setQuery(e.target.value)} />\n      </label>\n      <Suspense fallback={<h2>Loading...</h2>}>\n        <div style={{ opacity: isStale ? 0.5 : 1 }}>\n          <SearchResults query={deferredQuery} />\n        </div>\n      </Suspense>\n    </>\n  );\n}\n```\n\n```js src/SearchResults.js hidden\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function SearchResults({ query }) {\n  if (query === '') {\n    return null;\n  }\n  const albums = use(fetchData(`/search?q=${query}`));\n  if (albums.length === 0) {\n    return <p>No matches for <i>\"{query}\"</i></p>;\n  }\n  return (\n    <ul>\n      {albums.map(album => (\n        <li key={album.id}>\n          {album.title} ({album.year})\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url.startsWith('/search?q=')) {\n    return await getSearchResults(url.slice('/search?q='.length));\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getSearchResults(query) {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 500);\n  });\n\n  const allAlbums = [{\n    id: 13,\n    title: 'Let It Be',\n    year: 1970\n  }, {\n    id: 12,\n    title: 'Abbey Road',\n    year: 1969\n  }, {\n    id: 11,\n    title: 'Yellow Submarine',\n    year: 1969\n  }, {\n    id: 10,\n    title: 'The Beatles',\n    year: 1968\n  }, {\n    id: 9,\n    title: 'Magical Mystery Tour',\n    year: 1967\n  }, {\n    id: 8,\n    title: 'Sgt. Pepper\\'s Lonely Hearts Club Band',\n    year: 1967\n  }, {\n    id: 7,\n    title: 'Revolver',\n    year: 1966\n  }, {\n    id: 6,\n    title: 'Rubber Soul',\n    year: 1965\n  }, {\n    id: 5,\n    title: 'Help!',\n    year: 1965\n  }, {\n    id: 4,\n    title: 'Beatles For Sale',\n    year: 1964\n  }, {\n    id: 3,\n    title: 'A Hard Day\\'s Night',\n    year: 1964\n  }, {\n    id: 2,\n    title: 'With The Beatles',\n    year: 1963\n  }, {\n    id: 1,\n    title: 'Please Please Me',\n    year: 1963\n  }];\n\n  const lowerQuery = query.trim().toLowerCase();\n  return allAlbums.filter(album => {\n    const lowerTitle = album.title.toLowerCase();\n    return (\n      lowerTitle.startsWith(lowerQuery) ||\n      lowerTitle.indexOf(' ' + lowerQuery) !== -1\n    )\n  });\n}\n```\n\n```css\ninput { margin: 10px; }\n```\n\n</Sandpack>\n\n<Note>\n\n値の遅延 (deferred value) と[トランジション](#preventing-already-revealed-content-from-hiding)はいずれも、サスペンスフォールバックの表示を防いで代わりにインラインでインジケータを表示するために使えます。トランジションは更新の全体を低緊急度 (non-urgent) であるとマークするため、通常はフレームワークやルータライブラリでナビゲーションに使用されます。一方、値の遅延は主にアプリケーションコードで有用であり、UI の一部分を低緊急度とマークして、UI の他の部分に「遅れて」表示できるようにします。\n\n</Note>\n\n---\n\n### すでに表示されているコンテンツが隠れるのを防ぐ {/*preventing-already-revealed-content-from-hiding*/}\n\nコンポーネントがサスペンドすると、直近の親のサスペンスバウンダリがフォールバック表示に切り替わります。すでに何らかのコンテンツを表示していた場合、これによりユーザ体験が不快になる可能性があります。このボタンを押してみてください。\n\n<Sandpack>\n\n```js src/App.js\nimport { Suspense, useState } from 'react';\nimport IndexPage from './IndexPage.js';\nimport ArtistPage from './ArtistPage.js';\nimport Layout from './Layout.js';\n\nexport default function App() {\n  return (\n    <Suspense fallback={<BigSpinner />}>\n      <Router />\n    </Suspense>\n  );\n}\n\nfunction Router() {\n  const [page, setPage] = useState('/');\n\n  function navigate(url) {\n    setPage(url);\n  }\n\n  let content;\n  if (page === '/') {\n    content = (\n      <IndexPage navigate={navigate} />\n    );\n  } else if (page === '/the-beatles') {\n    content = (\n      <ArtistPage\n        artist={{\n          id: 'the-beatles',\n          name: 'The Beatles',\n        }}\n      />\n    );\n  }\n  return (\n    <Layout>\n      {content}\n    </Layout>\n  );\n}\n\nfunction BigSpinner() {\n  return <h2>🌀 Loading...</h2>;\n}\n```\n\n```js src/Layout.js\nexport default function Layout({ children }) {\n  return (\n    <div className=\"layout\">\n      <section className=\"header\">\n        Music Browser\n      </section>\n      <main>\n        {children}\n      </main>\n    </div>\n  );\n}\n```\n\n```js src/IndexPage.js\nexport default function IndexPage({ navigate }) {\n  return (\n    <button onClick={() => navigate('/the-beatles')}>\n      Open The Beatles artist page\n    </button>\n  );\n}\n```\n\n```js src/ArtistPage.js\nimport { Suspense } from 'react';\nimport Albums from './Albums.js';\nimport Biography from './Biography.js';\nimport Panel from './Panel.js';\n\nexport default function ArtistPage({ artist }) {\n  return (\n    <>\n      <h1>{artist.name}</h1>\n      <Biography artistId={artist.id} />\n      <Suspense fallback={<AlbumsGlimmer />}>\n        <Panel>\n          <Albums artistId={artist.id} />\n        </Panel>\n      </Suspense>\n    </>\n  );\n}\n\nfunction AlbumsGlimmer() {\n  return (\n    <div className=\"glimmer-panel\">\n      <div className=\"glimmer-line\" />\n      <div className=\"glimmer-line\" />\n      <div className=\"glimmer-line\" />\n    </div>\n  );\n}\n```\n\n```js src/Albums.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Albums({ artistId }) {\n  const albums = use(fetchData(`/${artistId}/albums`));\n  return (\n    <ul>\n      {albums.map(album => (\n        <li key={album.id}>\n          {album.title} ({album.year})\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```js src/Biography.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Biography({ artistId }) {\n  const bio = use(fetchData(`/${artistId}/bio`));\n  return (\n    <section>\n      <p className=\"bio\">{bio}</p>\n    </section>\n  );\n}\n```\n\n```js src/Panel.js\nexport default function Panel({ children }) {\n  return (\n    <section className=\"panel\">\n      {children}\n    </section>\n  );\n}\n```\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url === '/the-beatles/albums') {\n    return await getAlbums();\n  } else if (url === '/the-beatles/bio') {\n    return await getBio();\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getBio() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 500);\n  });\n\n  return `The Beatles were an English rock band, \n    formed in Liverpool in 1960, that comprised \n    John Lennon, Paul McCartney, George Harrison \n    and Ringo Starr.`;\n}\n\nasync function getAlbums() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 3000);\n  });\n\n  return [{\n    id: 13,\n    title: 'Let It Be',\n    year: 1970\n  }, {\n    id: 12,\n    title: 'Abbey Road',\n    year: 1969\n  }, {\n    id: 11,\n    title: 'Yellow Submarine',\n    year: 1969\n  }, {\n    id: 10,\n    title: 'The Beatles',\n    year: 1968\n  }, {\n    id: 9,\n    title: 'Magical Mystery Tour',\n    year: 1967\n  }, {\n    id: 8,\n    title: 'Sgt. Pepper\\'s Lonely Hearts Club Band',\n    year: 1967\n  }, {\n    id: 7,\n    title: 'Revolver',\n    year: 1966\n  }, {\n    id: 6,\n    title: 'Rubber Soul',\n    year: 1965\n  }, {\n    id: 5,\n    title: 'Help!',\n    year: 1965\n  }, {\n    id: 4,\n    title: 'Beatles For Sale',\n    year: 1964\n  }, {\n    id: 3,\n    title: 'A Hard Day\\'s Night',\n    year: 1964\n  }, {\n    id: 2,\n    title: 'With The Beatles',\n    year: 1963\n  }, {\n    id: 1,\n    title: 'Please Please Me',\n    year: 1963\n  }];\n}\n```\n\n```css\nmain {\n  min-height: 200px;\n  padding: 10px;\n}\n\n.layout {\n  border: 1px solid black;\n}\n\n.header {\n  background: #222;\n  padding: 10px;\n  text-align: center;\n  color: white;\n}\n\n.bio { font-style: italic; }\n\n.panel {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n}\n\n.glimmer-panel {\n  border: 1px dashed #aaa;\n  background: linear-gradient(90deg, rgba(221,221,221,1) 0%, rgba(255,255,255,1) 100%);\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n}\n\n.glimmer-line {\n  display: block;\n  width: 60%;\n  height: 20px;\n  margin: 10px;\n  border-radius: 4px;\n  background: #f0f0f0;\n}\n```\n\n</Sandpack>\n\nボタンを押した時点で、`Router` コンポーネントは `IndexPage` の代わりに `ArtistPage` をレンダーしました。`ArtistPage` 内のコンポーネントがサスペンドしたため、最も近いサスペンスバウンダリがフォールバックを表示し始めました。最も近いサスペンスバウンダリはルート近くにあったため、サイト全体のレイアウトが `BigSpinner` に置き換えられてしまいました。\n\nこれを防ぐため、ナビゲーションの state 更新を [`startTransition`](/reference/react/startTransition) で*トランジション*としてマークすることができます。\n\n```js {5,7}\nfunction Router() {\n  const [page, setPage] = useState('/');\n\n  function navigate(url) {\n    startTransition(() => {\n      setPage(url);      \n    });\n  }\n  // ...\n```\n\nこれにより、React に対して state の遷移が緊急のものではなく、既に表示されている内容を隠すよりも前のページを表示し続ける方が良いと伝えます。これで、ボタンをクリックすると `Biography` の読み込みを「待つ」ようになります。\n\n<Sandpack>\n\n```js src/App.js\nimport { Suspense, startTransition, useState } from 'react';\nimport IndexPage from './IndexPage.js';\nimport ArtistPage from './ArtistPage.js';\nimport Layout from './Layout.js';\n\nexport default function App() {\n  return (\n    <Suspense fallback={<BigSpinner />}>\n      <Router />\n    </Suspense>\n  );\n}\n\nfunction Router() {\n  const [page, setPage] = useState('/');\n\n  function navigate(url) {\n    startTransition(() => {\n      setPage(url);\n    });\n  }\n\n  let content;\n  if (page === '/') {\n    content = (\n      <IndexPage navigate={navigate} />\n    );\n  } else if (page === '/the-beatles') {\n    content = (\n      <ArtistPage\n        artist={{\n          id: 'the-beatles',\n          name: 'The Beatles',\n        }}\n      />\n    );\n  }\n  return (\n    <Layout>\n      {content}\n    </Layout>\n  );\n}\n\nfunction BigSpinner() {\n  return <h2>🌀 Loading...</h2>;\n}\n```\n\n```js src/Layout.js\nexport default function Layout({ children }) {\n  return (\n    <div className=\"layout\">\n      <section className=\"header\">\n        Music Browser\n      </section>\n      <main>\n        {children}\n      </main>\n    </div>\n  );\n}\n```\n\n```js src/IndexPage.js\nexport default function IndexPage({ navigate }) {\n  return (\n    <button onClick={() => navigate('/the-beatles')}>\n      Open The Beatles artist page\n    </button>\n  );\n}\n```\n\n```js src/ArtistPage.js\nimport { Suspense } from 'react';\nimport Albums from './Albums.js';\nimport Biography from './Biography.js';\nimport Panel from './Panel.js';\n\nexport default function ArtistPage({ artist }) {\n  return (\n    <>\n      <h1>{artist.name}</h1>\n      <Biography artistId={artist.id} />\n      <Suspense fallback={<AlbumsGlimmer />}>\n        <Panel>\n          <Albums artistId={artist.id} />\n        </Panel>\n      </Suspense>\n    </>\n  );\n}\n\nfunction AlbumsGlimmer() {\n  return (\n    <div className=\"glimmer-panel\">\n      <div className=\"glimmer-line\" />\n      <div className=\"glimmer-line\" />\n      <div className=\"glimmer-line\" />\n    </div>\n  );\n}\n```\n\n```js src/Albums.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Albums({ artistId }) {\n  const albums = use(fetchData(`/${artistId}/albums`));\n  return (\n    <ul>\n      {albums.map(album => (\n        <li key={album.id}>\n          {album.title} ({album.year})\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```js src/Biography.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Biography({ artistId }) {\n  const bio = use(fetchData(`/${artistId}/bio`));\n  return (\n    <section>\n      <p className=\"bio\">{bio}</p>\n    </section>\n  );\n}\n```\n\n```js src/Panel.js\nexport default function Panel({ children }) {\n  return (\n    <section className=\"panel\">\n      {children}\n    </section>\n  );\n}\n```\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url === '/the-beatles/albums') {\n    return await getAlbums();\n  } else if (url === '/the-beatles/bio') {\n    return await getBio();\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getBio() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 500);\n  });\n\n  return `The Beatles were an English rock band, \n    formed in Liverpool in 1960, that comprised \n    John Lennon, Paul McCartney, George Harrison \n    and Ringo Starr.`;\n}\n\nasync function getAlbums() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 3000);\n  });\n\n  return [{\n    id: 13,\n    title: 'Let It Be',\n    year: 1970\n  }, {\n    id: 12,\n    title: 'Abbey Road',\n    year: 1969\n  }, {\n    id: 11,\n    title: 'Yellow Submarine',\n    year: 1969\n  }, {\n    id: 10,\n    title: 'The Beatles',\n    year: 1968\n  }, {\n    id: 9,\n    title: 'Magical Mystery Tour',\n    year: 1967\n  }, {\n    id: 8,\n    title: 'Sgt. Pepper\\'s Lonely Hearts Club Band',\n    year: 1967\n  }, {\n    id: 7,\n    title: 'Revolver',\n    year: 1966\n  }, {\n    id: 6,\n    title: 'Rubber Soul',\n    year: 1965\n  }, {\n    id: 5,\n    title: 'Help!',\n    year: 1965\n  }, {\n    id: 4,\n    title: 'Beatles For Sale',\n    year: 1964\n  }, {\n    id: 3,\n    title: 'A Hard Day\\'s Night',\n    year: 1964\n  }, {\n    id: 2,\n    title: 'With The Beatles',\n    year: 1963\n  }, {\n    id: 1,\n    title: 'Please Please Me',\n    year: 1963\n  }];\n}\n```\n\n```css\nmain {\n  min-height: 200px;\n  padding: 10px;\n}\n\n.layout {\n  border: 1px solid black;\n}\n\n.header {\n  background: #222;\n  padding: 10px;\n  text-align: center;\n  color: white;\n}\n\n.bio { font-style: italic; }\n\n.panel {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n}\n\n.glimmer-panel {\n  border: 1px dashed #aaa;\n  background: linear-gradient(90deg, rgba(221,221,221,1) 0%, rgba(255,255,255,1) 100%);\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n}\n\n.glimmer-line {\n  display: block;\n  width: 60%;\n  height: 20px;\n  margin: 10px;\n  border-radius: 4px;\n  background: #f0f0f0;\n}\n```\n\n</Sandpack>\n\nトランジションは*すべて*のコンテンツの読み込みを待機するわけではありません。既に表示されたコンテンツを隠さない範囲でのみ待機を行います。例えば、ウェブサイトの `Layout` は既に表示されていたので、それをローディングスピナで隠すのは良くありません。しかし、その内部で `Albums` を囲んでいる `Suspense` バウンダリは新しいものなので、トランジションがそれを待つことはありません。\n\n<Note>\n\nサスペンス対応のルータは、デフォルトでナビゲーションの更新をトランジションにラップすることが期待されます。\n\n</Note>\n\n---\n\n### トランジションが進行中であることを示す {/*indicating-that-a-transition-is-happening*/}\n\n上記の例では、ボタンをクリックした後、ナビゲーションが進行中であることを視覚的に示すものがありません。インジケータを追加するために、[`startTransition`](/reference/react/startTransition) を [`useTransition`](/reference/react/useTransition) に置き換えることで、ブーリアン型の `isPending` 値が得られます。以下の例では、トランジションが進行中である間、ウェブサイトのヘッダのスタイルを変更するために使用されています。\n\n<Sandpack>\n\n```js src/App.js\nimport { Suspense, useState, useTransition } from 'react';\nimport IndexPage from './IndexPage.js';\nimport ArtistPage from './ArtistPage.js';\nimport Layout from './Layout.js';\n\nexport default function App() {\n  return (\n    <Suspense fallback={<BigSpinner />}>\n      <Router />\n    </Suspense>\n  );\n}\n\nfunction Router() {\n  const [page, setPage] = useState('/');\n  const [isPending, startTransition] = useTransition();\n\n  function navigate(url) {\n    startTransition(() => {\n      setPage(url);\n    });\n  }\n\n  let content;\n  if (page === '/') {\n    content = (\n      <IndexPage navigate={navigate} />\n    );\n  } else if (page === '/the-beatles') {\n    content = (\n      <ArtistPage\n        artist={{\n          id: 'the-beatles',\n          name: 'The Beatles',\n        }}\n      />\n    );\n  }\n  return (\n    <Layout isPending={isPending}>\n      {content}\n    </Layout>\n  );\n}\n\nfunction BigSpinner() {\n  return <h2>🌀 Loading...</h2>;\n}\n```\n\n```js src/Layout.js\nexport default function Layout({ children, isPending }) {\n  return (\n    <div className=\"layout\">\n      <section className=\"header\" style={{\n        opacity: isPending ? 0.7 : 1\n      }}>\n        Music Browser\n      </section>\n      <main>\n        {children}\n      </main>\n    </div>\n  );\n}\n```\n\n```js src/IndexPage.js\nexport default function IndexPage({ navigate }) {\n  return (\n    <button onClick={() => navigate('/the-beatles')}>\n      Open The Beatles artist page\n    </button>\n  );\n}\n```\n\n```js src/ArtistPage.js\nimport { Suspense } from 'react';\nimport Albums from './Albums.js';\nimport Biography from './Biography.js';\nimport Panel from './Panel.js';\n\nexport default function ArtistPage({ artist }) {\n  return (\n    <>\n      <h1>{artist.name}</h1>\n      <Biography artistId={artist.id} />\n      <Suspense fallback={<AlbumsGlimmer />}>\n        <Panel>\n          <Albums artistId={artist.id} />\n        </Panel>\n      </Suspense>\n    </>\n  );\n}\n\nfunction AlbumsGlimmer() {\n  return (\n    <div className=\"glimmer-panel\">\n      <div className=\"glimmer-line\" />\n      <div className=\"glimmer-line\" />\n      <div className=\"glimmer-line\" />\n    </div>\n  );\n}\n```\n\n```js src/Albums.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Albums({ artistId }) {\n  const albums = use(fetchData(`/${artistId}/albums`));\n  return (\n    <ul>\n      {albums.map(album => (\n        <li key={album.id}>\n          {album.title} ({album.year})\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```js src/Biography.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Biography({ artistId }) {\n  const bio = use(fetchData(`/${artistId}/bio`));\n  return (\n    <section>\n      <p className=\"bio\">{bio}</p>\n    </section>\n  );\n}\n```\n\n```js src/Panel.js\nexport default function Panel({ children }) {\n  return (\n    <section className=\"panel\">\n      {children}\n    </section>\n  );\n}\n```\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url === '/the-beatles/albums') {\n    return await getAlbums();\n  } else if (url === '/the-beatles/bio') {\n    return await getBio();\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getBio() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 500);\n  });\n\n  return `The Beatles were an English rock band, \n    formed in Liverpool in 1960, that comprised \n    John Lennon, Paul McCartney, George Harrison \n    and Ringo Starr.`;\n}\n\nasync function getAlbums() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 3000);\n  });\n\n  return [{\n    id: 13,\n    title: 'Let It Be',\n    year: 1970\n  }, {\n    id: 12,\n    title: 'Abbey Road',\n    year: 1969\n  }, {\n    id: 11,\n    title: 'Yellow Submarine',\n    year: 1969\n  }, {\n    id: 10,\n    title: 'The Beatles',\n    year: 1968\n  }, {\n    id: 9,\n    title: 'Magical Mystery Tour',\n    year: 1967\n  }, {\n    id: 8,\n    title: 'Sgt. Pepper\\'s Lonely Hearts Club Band',\n    year: 1967\n  }, {\n    id: 7,\n    title: 'Revolver',\n    year: 1966\n  }, {\n    id: 6,\n    title: 'Rubber Soul',\n    year: 1965\n  }, {\n    id: 5,\n    title: 'Help!',\n    year: 1965\n  }, {\n    id: 4,\n    title: 'Beatles For Sale',\n    year: 1964\n  }, {\n    id: 3,\n    title: 'A Hard Day\\'s Night',\n    year: 1964\n  }, {\n    id: 2,\n    title: 'With The Beatles',\n    year: 1963\n  }, {\n    id: 1,\n    title: 'Please Please Me',\n    year: 1963\n  }];\n}\n```\n\n```css\nmain {\n  min-height: 200px;\n  padding: 10px;\n}\n\n.layout {\n  border: 1px solid black;\n}\n\n.header {\n  background: #222;\n  padding: 10px;\n  text-align: center;\n  color: white;\n}\n\n.bio { font-style: italic; }\n\n.panel {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n}\n\n.glimmer-panel {\n  border: 1px dashed #aaa;\n  background: linear-gradient(90deg, rgba(221,221,221,1) 0%, rgba(255,255,255,1) 100%);\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n}\n\n.glimmer-line {\n  display: block;\n  width: 60%;\n  height: 20px;\n  margin: 10px;\n  border-radius: 4px;\n  background: #f0f0f0;\n}\n```\n\n</Sandpack>\n\n---\n\n### ナビゲーション時にサスペンスバウンダリをリセットする {/*resetting-suspense-boundaries-on-navigation*/}\n\nトランジション中、React は既に表示されているコンテンツを隠さないようにします。しかし、異なるパラメータを持つルートに移動する場合、React にそれが*異なる*コンテンツであると伝えたいことがあります。これを表現するために、`key` が使えます。\n\n```js\n<ProfilePage key={queryParams.id} />\n```\n\n単一のユーザのプロフィールページ内を閲覧していて、何かがサスペンドすると想像してみてください。その更新がトランジションでラップされている場合、既に表示されているコンテンツのフォールバックをトリガしません。これは期待される動作です。\n\nしかし、2 人の異なるユーザのプロフィール間を移動していると想像してみてください。その場合は、フォールバックを表示することが理にかなっています。例えば、あるユーザのタイムラインは別のユーザのタイムラインとは*異なるコンテンツ*です。`key` を指定することで、React は異なるユーザのプロフィールを異なるコンポーネントとして扱うので、ナビゲーション中にサスペンスバウンダリをリセットします。サスペンスを統合したルータは、これを自動的に行うべきです。\n\n---\n\n### サーバエラー用およびクライアント専用コンテンツ用のフォールバックを指定する {/*providing-a-fallback-for-server-errors-and-client-only-content*/}\n\n[ストリーミングサーバレンダリング API](/reference/react-dom/server) のいずれか（またはそれらに依存するフレームワーク）を使用する場合も、React は `<Suspense>` バウンダリを使用してサーバ上のエラーを処理します。コンポーネントがサーバ上でエラーをスローしても、React はサーバレンダリングを中止しません。代わりに、上位の最も近い `<Suspense>` コンポーネントを見つけ、そのフォールバック（スピナなど）を、生成されたサーバ HTML に含めます。ユーザには最初にスピナが見えることになります。\n\nクライアント側では、React は同じコンポーネントを再度レンダーしようとします。クライアントでもエラーが発生すると、React はエラーをスローし、最も近い[エラーバウンダリ](/reference/react/Component#static-getderivedstatefromerror)を表示します。しかし、クライアントでエラーが発生しない場合は、最終的にコンテンツが正常に表示されたということになるため、React はユーザにエラーを表示しません。\n\nこれを使用して、サーバ上で一部のコンポーネントのレンダーを明示的に拒否することができます。これを行うには、サーバ環境ではエラーをスローするようにし、コンポーネントを `<Suspense>` バウンダリにラップして、HTML の代わりにフォールバックが表示されるようにします。\n\n```js\n<Suspense fallback={<Loading />}>\n  <Chat />\n</Suspense>\n\nfunction Chat() {\n  if (typeof window === 'undefined') {\n    throw Error('Chat should only render on the client.');\n  }\n  // ...\n}\n```\n\nサーバからの HTML にはローディングインジケータが含まれます。クライアント上で `Chat` コンポーネントに置き換わります。\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### 更新中に UI がフォールバックに置き換わるのを防ぐ方法は？ {/*preventing-unwanted-fallbacks*/}\n\nすでに表示中の UI をフォールバックに置き換えると、ユーザ体験が不快になります。これは、更新がコンポーネントをサスペンドさせるが、最も近いサスペンスバウンダリがすでにユーザにコンテンツを表示している、という場合に発生します。\n\nこれを防ぐには、[`startTransition` を使用して更新を低緊急度としてマークします](#preventing-already-revealed-content-from-hiding)。トランジション中、React は十分なデータがロードされるまで待機し、不要なフォールバックが表示されるのを防ぎます。\n\n```js {2-3,5}\nfunction handleNextPageClick() {\n  // If this update suspends, don't hide the already displayed content\n  startTransition(() => {\n    setCurrentPage(currentPage + 1);\n  });\n}\n```\n\nこれにより、既存のコンテンツが隠されるのを避けることができます。ただし、新たにレンダーされる `Suspense` バウンダリは、UI をブロックするのを避け、利用可能になったらユーザがコンテンツを見ることができるよう、すぐにフォールバックを表示します。\n\n**React が不要なフォールバックを防ぐのは、低緊急度の更新中のみです**。緊急の更新の結果としてレンダーが発生した場合、レンダーの遅延は起こりません。[`startTransition`](/reference/react/startTransition) や [`useDeferredValue`](/reference/react/useDeferredValue) のような API を使用して明示的にオプトインする必要があります。\n\nあなたのルータがサスペンスと統合されている場合、更新は自動的に [`startTransition`](/reference/react/startTransition) でラップされているはずです。\n"
  },
  {
    "path": "src/content/reference/react/ViewTransition.md",
    "content": "---\ntitle: <ViewTransition>\nversion: canary\n---\n\n<Canary>\n\n**The `<ViewTransition />` API is currently only available in React’s Canary and Experimental channels.** \n\n[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels)\n\n</Canary>\n\n<Intro>\n\n`<ViewTransition>` lets you animate elements that update inside a Transition.\n\n\n```js\nimport {ViewTransition} from 'react';\n\n<ViewTransition>\n  <div>...</div>\n</ViewTransition>\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## Reference {/*reference*/}\n\n### `<ViewTransition>` {/*viewtransition*/}\n\nWrap elements in `<ViewTransition>` to animate them when they update inside a [Transition](/reference/react/useTransition). React uses the following heuristics to determine if a View Transition activates for an animation:\n\n- `enter`: If a `ViewTransition` itself gets inserted in this Transition, then this will activate.\n- `exit`: If a `ViewTransition` itself gets deleted in this Transition, then this will activate.\n- `update`: If a `ViewTransition` has any DOM mutations inside it that React is doing (such as a prop changing) or if the `ViewTransition` boundary itself changes size or position due to an immediate sibling. If there are nested` ViewTransition` then the mutation applies to them and not the parent.\n- `share`: If a named `ViewTransition` is inside a deleted subtree and another named `ViewTransition` with the same name is part of an inserted subtree in the same Transition, they form a Shared Element Transition, and it animates from the deleted one to the inserted one.\n\nBy default, `<ViewTransition>` animates with a smooth cross-fade (the browser default view transition). You can customize the animation by providing a [View Transition Class](#view-transition-class) to the `<ViewTransition>` component. You can  customize animations for each kind of trigger (see [Styling View Transitions](#styling-view-transitions)).\n\n<DeepDive>\n\n#### How does `<ViewTransition>` work? {/*how-does-viewtransition-work*/}\n\nUnder the hood, React applies `view-transition-name` to inline styles of the nearest DOM node nested inside the `<ViewTransition>` component. If there are multiple sibling DOM nodes like `<ViewTransition><div /><div /></ViewTransition>` then React adds a suffix to the name to make each unique but conceptually they're part of the same one. React doesn't apply these eagerly but only at the time that boundary should participate in an animation.\n\nReact automatically calls `startViewTransition` itself behind the scenes so you should never do that yourself. In fact, if you have something else on the page running a ViewTransition React will interrupt it. So it's recommended that you use React itself to coordinate these. If you had other ways of trigger ViewTransitions in the past, we recommend that you migrate to the built-in way.\n\nIf there are other React ViewTransitions already running then React will wait for them to finish before starting the next one. However, importantly if there are multiple updates happening while the first one is running, those will all be batched into one. If you start A->B. Then in the meantime you get an update to go to C and then D. When the first A->B animation finishes the next one will animate from B->D.\n\nThe `getSnapshotBeforeUpdate` life-cycle will be called before `startViewTransition` and some `view-transition-name` will update at the same time.\n\nThen React calls `startViewTransition`. Inside the `updateCallback`, React will:\n\n- Apply its mutations to the DOM and invoke useInsertionEffects.\n- Wait for fonts to load.\n- Call componentDidMount, componentDidUpdate, useLayoutEffect and refs.\n- Wait for any pending Navigation to finish.\n- Then React will measure any changes to the layout to see which boundaries will need to animate.\n\nAfter the ready Promise of the `startViewTransition` is resolved, React will then revert the `view-transition-name`. Then React will invoke the `onEnter`, `onExit`, `onUpdate` and `onShare` callbacks to allow for manual programmatic control over the Animations. This will be after the built-in default ones have already been computed.\n\nIf a `flushSync` happens to get in the middle of this sequence, then React will skip the Transition since it relies on being able to complete synchronously.\n\nAfter the finished Promise of the `startViewTransition` is resolved, React will then invoke `useEffect`. This prevents those from interfering with the performance of the Animation. However, this is not a guarantee because if another `setState` happens while the Animation is running it'll still have to invoke the `useEffect` earlier to preserve the sequential guarantees.\n\n</DeepDive>\n\n#### Props {/*props*/}\n\nBy default, `<ViewTransition>` animates with a smooth cross-fade. You can customize the animation, or specify a shared element transition, with these props:\n\n* **optional** `enter`: A string or object. The [View Transition Class](#view-transition-class) to apply when enter is activated.\n* **optional** `exit`: A string or object. The [View Transition Class](#view-transition-class) to apply when exit is activated.\n* **optional** `update`: A string or object. The [View Transition Class](#view-transition-class) to apply when an update is activated.\n* **optional** `share`: A string or object. The [View Transition Class](#view-transition-class) to apply when a shared element is activated.\n* **optional** `default`: A string or object. The [View Transition Class](#view-transition-class) used when no other matching activation prop is found. \n* **optional** `name`: A string or object. The name of the View Transition used for shared element transitions. If not provided, React will use a unique name for each View Transition to prevent unexpected animations.\n\n#### Callback {/*events*/}\n\nThese callbacks allow you to adjust the animation imperatively using the [animate](https://developer.mozilla.org/en-US/docs/Web/API/Element/animate) APIs:\n\n* **optional** `onEnter`: A function. React calls `onEnter` after an \"enter\" animation.\n* **optional** `onExit`: A function. React calls `onExit` after an \"exit\" animation.\n* **optional** `onShare`:  A function. React calls `onShare` after a \"share\" animation.\n* **optional** `onUpdate`:  A function. React calls `onUpdate` after an \"update\" animation.\n\nEach callback receives as arguments:\n- `element`: The DOM element that was animated.\n- `types`: The [Transition Types](/reference/react/addTransitionType) included in the animation.\n\n### View Transition Class {/*view-transition-class*/}\n\nThe View Transition Class is the CSS class name(s) applied by React during the transition when the ViewTransition activates. It can be a string or an object.\n- `string`: the `class` added on the child elements when activated. If `'none'` is provided, no class will be added.\n- `object`: the class added on the child elements will be the key matching View Transition type added with `addTransitionType`. The object can also specify a `default` to use if no matching type is found.\n\nThe value `'none'` can be used to prevent a View Transition from activating for a specific trigger.\n\n### Styling View Transitions {/*styling-view-transitions*/}\n\n<Note>\n\nIn many early examples of View Transitions around the web, you'll have seen using a [`view-transition-name`](https://developer.mozilla.org/en-US/docs/Web/CSS/view-transition-name) and then style it using `::view-transition-...(my-name)` selectors. We don't recommend that for styling. Instead, we normally recommend using a View Transition Class instead.\n\n</Note>\n\nTo customize the animation for a `<ViewTransition>` you can provide a View Transition Class to one of the activation props. The View Transition Class is a CSS class name that React applies to the child elements when the ViewTransition activates.\n\nFor example, to customize an \"enter\" animation, provide a class name to the `enter` prop:\n\n\n```js\n<ViewTransition enter=\"slide-in\">\n```\n\nWhen the `<ViewTransition>` activates an \"enter\" animation, React will add the class name `slide-in`. Then you can refer to this class using [view transition pseudo selectors](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API#pseudo-elements) to build reusable animations:\n\n```css\n::view-transition-group(.slide-in) {\n  \n}\n::view-transition-old(.slide-in) {\n\n}\n::view-transition-new(.slide-in) {\n\n}\n```\nIn the future, CSS libraries may add built-in animations using View Transition Classes to make this easier to use.\n\n#### Caveats {/*caveats*/}\n\n- By default, `setState` updates immediately and does not activate `<ViewTransition>`, only updates wrapped in a [Transition](/reference/react/useTransition). You can also use [`<Suspense>`](/reference/react/Suspense) to opt-in to a Transition to [reveal content](/reference/react/Suspense#revealing-content-together-at-once).\n- `<ViewTransition>` creates an image that can be moved around, scaled and cross-faded. Unlike Layout Animations you may have seen in React Native or Motion, this means that not every individual Element inside of it animates its position. This can lead to better performance and a more continuous feeling, smooth animation compared to animating every individual piece. However, it can also lose continuity in things that should be moving by themselves. So you might have to add more `<ViewTransition>` boundaries manually as a result.\n- Many users may prefer not having animations on the page. React doesn't automatically disable animations for this case. We recommend that using the `@media (prefers-reduced-motion)` media query to disable animations or tone them down based on user preference. In the future, CSS libraries may have this built-in to their presets.\n- Currently, `<ViewTransition>` only works in the DOM. We're working on adding support for React Native and other platforms.\n\n---\n\n\n## Usage {/*usage*/}\n\n### Animating an element on enter/exit {/*animating-an-element-on-enter*/}\n\nEnter/Exit Transitions trigger when a `<ViewTransition>` is added or removed by a component in a transition:\n\n```js\nfunction Child() {\n  return (\n    <ViewTransition>\n      <div>Hi</div>\n    </ViewTransition>\n  );\n}\n\nfunction Parent() {\n  const [show, setShow] = useState();\n  if (show) {\n    return <Child />;\n  }\n  return null;\n}\n```\n\nWhen `setShow` is called, `show` switches to `true` and the `Child` component is rendered. When `setShow` is called inside `startTransition`, and `Child` renders a `ViewTransition` before any other DOM nodes, an `enter` animation is triggered. \n\nWhen `show` switches back to `false`, an `exit` animation is triggered.\n\n<Sandpack>\n\n```js src/Video.js hidden\nfunction Thumbnail({ video, children }) {\n  return (\n    <div\n      aria-hidden=\"true\"\n      tabIndex={-1}\n      className={`thumbnail ${video.image}`}\n    />\n  );\n}\n\nexport function Video({ video }) {\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n      >\n        <Thumbnail video={video}></Thumbnail>\n\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n    </div>\n  );\n}\n```\n\n```js\nimport {\n  ViewTransition,\n  useState,\n  startTransition\n} from 'react';\nimport {Video} from \"./Video\";\nimport videos from \"./data\"\n\nfunction Item() {\n  return (\n    <ViewTransition>\n      <Video video={videos[0]}/>\n    </ViewTransition>\n  );\n}\n\nexport default function Component() {\n  const [showItem, setShowItem] = useState(false);\n  return (\n    <>\n      <button\n        onClick={() => {\n          startTransition(() => {\n            setShowItem((prev) => !prev);\n          });\n        }}\n      >{showItem ? '➖' : '➕'}</button>\n\n      {showItem ? <Item /> : null}\n    </>\n  );\n}\n```\n\n```js src/data.js hidden\nexport default [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  }\n]\n```\n\n\n```css\n#root {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  min-height: 200px;\n}\nbutton {\n  border: none;\n  border-radius: 50%;\n  width: 50px;\n  height: 50px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-color: #f0f8ff;\n  color: white;\n  font-size: 20px;\n  cursor: pointer;\n  transition: background-color 0.3s, border 0.3s;\n}\nbutton:hover {\n  border: 2px solid #ccc;\n  background-color: #e0e8ff;\n}\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n  margin-top: 1em;\n}\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n.video .info:hover {\n  text-decoration: underline;\n}\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  }\n}\n```\n\n</Sandpack>\n\n<Pitfall>\n\n`<ViewTransition>` only activates if it is placed before any DOM node. If `Child` instead looked like this, no animation would trigger:\n\n```js [3, 5]\nfunction Component() {\n  return <ViewTransition>Hi</ViewTransition>;\n}\n```\n\n</Pitfall>\n\n---\n### Animating a shared element {/*animating-a-shared-element*/}\n\nNormally, we don't recommend assigning a name to a `<ViewTransition>` and instead let React assign it an automatic name. The reason you might want to assign a name is to animate between completely different components when one tree unmounts and another tree mounts at the same time. To preserve continuity.\n\n```js\n<ViewTransition name={UNIQUE_NAME}>\n  <Child />\n</ViewTransition>\n```\n\nWhen one tree unmounts and another mounts, if there's a pair where the same name exists in the unmounting tree and the mounting tree, they trigger the \"share\" animation on both. It animates from the unmounting side to the mounting side.\n\nUnlike an exit/enter animation this can be deeply inside the deleted/mounted tree. If a `<ViewTransition>` would also be eligible for exit/enter, then the \"share\" animation takes precedence.\n\nIf Transition first unmounts one side and then leads to a `<Suspense>` fallback being shown before eventually the new name being mounted, then no shared element transition happens.\n\n<Sandpack>\n\n```js\nimport {\n  ViewTransition,\n  useState,\n  startTransition\n} from \"react\";\nimport {Video, Thumbnail, FullscreenVideo} from \"./Video\";\nimport videos from \"./data\";\n\nexport default function Component() {\n  const [fullscreen, setFullscreen] = useState(false);\n  if (fullscreen) {\n    return <FullscreenVideo\n      video={videos[0]}\n      onExit={() => startTransition(() => setFullscreen(false))}\n    />\n  }\n  return <Video\n    video={videos[0]}\n    onClick={() => startTransition(() => setFullscreen(true))}\n  />\n}\n\n```\n\n```js src/Video.js\nimport {ViewTransition} from \"react\";\n\nconst THUMBNAIL_NAME = \"video-thumbnail\"\n\nexport function Thumbnail({ video, children }) {\n  return (\n    <ViewTransition name={THUMBNAIL_NAME}>\n      <div\n        aria-hidden=\"true\"\n        tabIndex={-1}\n        className={`thumbnail ${video.image}`}\n      />\n    </ViewTransition>\n  );\n}\n\nexport function Video({ video, onClick }) {\n  return (\n    <div className=\"video\">\n      <div className=\"link\" onClick={onClick}>\n        <Thumbnail video={video} />\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport function FullscreenVideo({video, onExit}) {\n  return (\n    <div className=\"fullscreenLayout\">\n      <ViewTransition name={THUMBNAIL_NAME}>\n        <div\n          aria-hidden=\"true\"\n          tabIndex={-1}\n          className={`thumbnail ${video.image} fullscreen`}\n        />\n        <button\n          className=\"close-button\"\n          onClick={onExit}\n        >\n          ✖\n        </button>\n      </ViewTransition>\n    </div>\n  );\n}\n```\n\n\n```js src/data.js hidden\nexport default [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  }\n]\n```\n\n\n```css\n#root {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  height: 300px;\n}\nbutton {\n  border: none;\n  border-radius: 50%;\n  width: 50px;\n  height: 50px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-color: #f0f8ff;\n  color: white;\n  font-size: 20px;\n  cursor: pointer;\n  transition: background-color 0.3s, border 0.3s;\n}\nbutton:hover {\n  border: 2px solid #ccc;\n  background-color: #e0e8ff;\n}\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n.thumbnail.red {\n  background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);\n}\n.thumbnail.fullscreen {\n  width: 100%;\n}\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n  margin-top: 1em;\n}\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n.video .info:hover {\n  text-decoration: underline;\n}\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n.fullscreenLayout {\n  position: relative;\n  height: 100%;\n  width: 100%;\n}\n.close-button {\n  position: absolute;\n  top: 10px;\n  right: 10px;\n  color: black;\n}\n@keyframes progress-animation {\n  from {\n    width: 0;\n  }\n  to {\n    width: 100%;\n  }\n}\n```\n\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  }\n}\n```\n\n</Sandpack>\n\n\n<Note>\n\nIf either the mounted or unmounted side of a pair is outside the viewport, then no pair is formed. This ensures that it doesn't fly in or out of the viewport when something is scrolled. Instead it's treated as a regular enter/exit by itself.\n\nThis does not happen if the same Component instance changes position, which triggers an \"update\". Those animate regardless if one position is outside the viewport.\n\nThere's currently a quirk where if a deeply nested unmounted `<ViewTransition>` is inside the viewport but the mounted side is not within the viewport, then the unmounted side animates as its own \"exit\" animation even if it's deeply nested instead of as part of the parent animation.\n\n</Note>\n\n<Pitfall>\n\nIt's important that there's only one thing with the same name mounted at a time in the entire app. Therefore it's important to use unique namespaces for the name to avoid conflicts. To ensure you can do this you might want to add a constant in a separate module that you import.\n\n```js\nexport const MY_NAME = \"my-globally-unique-name\";\nimport {MY_NAME} from './shared-name';\n...\n<ViewTransition name={MY_NAME}>\n```\n\n</Pitfall>\n\n\n---\n\n### Animating reorder of items in a list {/*animating-reorder-of-items-in-a-list*/}\n\n\n```js\nitems.map(item => <Component key={item.id} item={item} />)\n```\n\nWhen reordering a list, without updating the content, the \"update\" animation triggers on each `<ViewTransition>` in the list if they're outside a DOM node. Similar to enter/exit animations.\n\nThis means that this will trigger the animation on this `<ViewTransition>`:\n\n```js\nfunction Component() {\n  return <ViewTransition><div>...</div></ViewTransition>;\n}\n```\n<Sandpack>\n\n```js src/Video.js hidden\nfunction Thumbnail({ video }) {\n  return (\n    <div\n      aria-hidden=\"true\"\n      tabIndex={-1}\n      className={`thumbnail ${video.image}`}\n    />\n  );\n}\n\nexport function Video({ video }) {\n  return (\n    <div className=\"video\">\n      <div className=\"link\">\n        <Thumbnail video={video}></Thumbnail>\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n    </div>\n  );\n}\n```\n\n```js\nimport {\n  ViewTransition,\n  useState,\n  startTransition\n} from \"react\";\nimport {Video} from \"./Video\";\nimport videos from \"./data\";\n\nexport default function Component() {\n  const [orderedVideos, setOrderedVideos] = useState(videos);\n  const reorder = () => {\n    startTransition(() => {\n      setOrderedVideos((prev) => {\n        return [...prev.sort(() => Math.random() - 0.5)];\n      });\n    });\n  };\n  return (\n    <>\n      <button onClick={reorder}>🎲</button>\n      <div className=\"listContainer\">\n        {orderedVideos.map((video, i) => {\n          return (\n            <ViewTransition key={video.title}>\n              <Video video={video} />\n            </ViewTransition>\n          );\n        })}\n      </div>\n    </>\n  );\n}\n  \n\n```\n\n```js src/data.js hidden\nexport default [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  },\n  {\n    id: '2',\n    title: 'Second video',\n    description: 'Video description',\n    image: 'red',\n  },\n  {\n    id: '3',\n    title: 'Third video',\n    description: 'Video description',\n    image: 'green',\n  },\n  {\n    id: '4',\n    title: 'Fourth video',\n    description: 'Video description',\n    image: 'purple',\n  }\n]\n```\n\n\n```css\n#root {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  min-height: 150px;\n}\nbutton {\n  border: none;\n  border-radius: 50%;\n  width: 50px;\n  height: 50px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-color: #f0f8ff;\n  color: white;\n  font-size: 20px;\n  cursor: pointer;\n  transition: background-color 0.3s, border 0.3s;\n}\nbutton:hover {\n  border: 2px solid #ccc;\n  background-color: #e0e8ff;\n}\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n.thumbnail.red {\n  background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);\n}\n.thumbnail.green {\n  background-image: conic-gradient(at top right, #c76a15, #388f7f, #2b3491);\n}\n.thumbnail.purple {\n  background-image: conic-gradient(at top right, #c76a15, #575fb7, #2b3491);\n}\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n  margin-top: 1em;\n}\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n}\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n.video .info:hover {\n  text-decoration: underline;\n}\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n```\n\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  }\n}\n```\n\n</Sandpack>\n\nHowever, this wouldn't animate each individual item:\n\n```js\nfunction Component() {\n  return <div><ViewTransition>...</ViewTransition></div>;\n}\n```\nInstead, any parent `<ViewTransition>` would cross-fade. If there is no parent `<ViewTransition>` then there's no animation in that case.\n\n<Sandpack>\n\n```js src/Video.js hidden\nfunction Thumbnail({ video }) {\n  return (\n    <div\n      aria-hidden=\"true\"\n      tabIndex={-1}\n      className={`thumbnail ${video.image}`}\n    />\n  );\n}\n\nexport function Video({ video }) {\n  return (\n    <div className=\"video\">\n      <div className=\"link\">\n        <Thumbnail video={video}></Thumbnail>\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n    </div>\n  );\n}\n```\n\n```js\nimport {\n  ViewTransition,\n  useState,\n  startTransition\n} from \"react\";\nimport {Video} from \"./Video\";\nimport videos from \"./data\";\n\nexport default function Component() {\n  const [orderedVideos, setOrderedVideos] = useState(videos);\n  const reorder = () => {\n    startTransition(() => {\n      setOrderedVideos((prev) => {\n        return [...prev.sort(() => Math.random() - 0.5)];\n      });\n    });\n  };\n  return (\n    <>\n      <button onClick={reorder}>🎲</button>\n      <ViewTransition>\n        <div className=\"listContainer\">\n          {orderedVideos.map((video, i) => {\n            return <Video video={video} key={video.title} />;\n          })}\n        </div>\n      </ViewTransition>\n    </>\n  );\n}\n  \n\n```\n\n```js src/data.js hidden\nexport default [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  },\n  {\n    id: '2',\n    title: 'Second video',\n    description: 'Video description',\n    image: 'red',\n  },\n  {\n    id: '3',\n    title: 'Third video',\n    description: 'Video description',\n    image: 'green',\n  },\n  {\n    id: '4',\n    title: 'Fourth video',\n    description: 'Video description',\n    image: 'purple',\n  }\n]\n```\n\n\n```css\n#root {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  min-height: 150px;\n}\nbutton {\n  border: none;\n  border-radius: 50%;\n  width: 50px;\n  height: 50px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-color: #f0f8ff;\n  color: white;\n  font-size: 20px;\n  cursor: pointer;\n  transition: background-color 0.3s, border 0.3s;\n}\nbutton:hover {\n  border: 2px solid #ccc;\n  background-color: #e0e8ff;\n}\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n.thumbnail.red {\n  background-image: conic-gradient(at top right, #c76a15, #a6423a, #2b3491);\n}\n.thumbnail.green {\n  background-image: conic-gradient(at top right, #c76a15, #388f7f, #2b3491);\n}\n.thumbnail.purple {\n  background-image: conic-gradient(at top right, #c76a15, #575fb7, #2b3491);\n}\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n  margin-top: 1em;\n}\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n}\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n.video .info:hover {\n  text-decoration: underline;\n}\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n```\n\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  }\n}\n```\n\n</Sandpack>\n\nThis means you might want to avoid wrapper elements in lists where you want to allow the Component to control its own reorder animation:\n\n```\nitems.map(item => <div><Component key={item.id} item={item} /></div>)\n```\n\nThe above rule also applies if one of the items updates to resize, which then causes the siblings to resize, it'll also animate its sibling `<ViewTransition>` but only if they're immediate siblings.\n\nThis means that during an update, which causes a lot of re-layout, it doesn't individually animate every `<ViewTransition>` on the page. That would lead to a lot of noisy animations which distracts from the actual change. Therefore React is more conservative about when an individual animation triggers.\n\n<Pitfall>\n\nIt's important to properly use keys to preserve identity when reordering lists. It might seem like you could use \"name\", shared element transitions, to animate reorders but that would not trigger if one side was outside the viewport. To animate a reorder you often want to show that it went to a position outside the viewport.\n\n</Pitfall>\n\n---\n\n### Animating from Suspense content {/*animating-from-suspense-content*/}\n\nJust like any Transition, React waits for data and new CSS (`<link rel=\"stylesheet\" precedence=\"...\">`) before running the animation. In addition to this, ViewTransitions also wait up to 500ms for new fonts to load before starting the animation to avoid them flickering in later. For the same reason, an image wrapped in ViewTransition will wait for the image to load.\n\nIf it's inside a new Suspense boundary instance, then the fallback is shown first. After the Suspense boundary fully loads, it triggers the `<ViewTransition>` to animate the reveal to the content.\n\nThere are two ways to animate Suspense boundaries depending on where you place the `<ViewTransition>`:\n\nUpdate:\n\n```\n<ViewTransition>\n  <Suspense fallback={<A />}>\n    <B />\n  </Suspense>\n</ViewTransition>\n```\nIn this scenario when the content goes from A to B, it'll be treated as an \"update\" and apply that class if appropriate. Both A and B will get the same view-transition-name and therefore they're acting as a cross-fade by default.\n\n<Sandpack>\n\n```js src/Video.js hidden\nfunction Thumbnail({ video, children }) {\n  return (\n    <div\n      aria-hidden=\"true\"\n      tabIndex={-1}\n      className={`thumbnail ${video.image}`}\n    />\n  );\n}\n\nexport function Video({ video }) {\n  return (\n    <div className=\"video\">\n      <div className=\"link\">\n        <Thumbnail video={video}></Thumbnail>\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport function VideoPlaceholder() {\n  const video = {image: \"loading\"}\n  return (\n    <div className=\"video\">\n      <div className=\"link\">\n        <Thumbnail video={video}></Thumbnail>\n        <div className=\"info\">\n          <div className=\"video-title loading\" />\n          <div className=\"video-description loading\" />\n        </div>\n      </div>\n    </div>\n  );\n}\n```\n\n```js\nimport {\n  ViewTransition,\n  useState,\n  startTransition,\n  Suspense\n} from 'react';\nimport {Video, VideoPlaceholder} from \"./Video\";\nimport {useLazyVideoData} from \"./data\"\n\nfunction LazyVideo() {\n  const video = useLazyVideoData();\n  return (\n    <Video video={video}/>\n  );\n}\n\nexport default function Component() {\n  const [showItem, setShowItem] = useState(false);\n  return (\n    <>\n      <button\n        onClick={() => {\n          startTransition(() => {\n            setShowItem((prev) => !prev);\n          });\n        }}\n      >{showItem ? '➖' : '➕'}</button>\n      {showItem ? (\n        <ViewTransition>\n          <Suspense fallback={<VideoPlaceholder />}>\n            <LazyVideo />\n          </Suspense>\n        </ViewTransition>\n      ) : null}\n    </>\n  );\n}\n```\n\n```js src/data.js hidden\nimport {use} from \"react\";\n\nlet cache = null;\n\nfunction fetchVideo() {\n  if (!cache) {\n    cache = new Promise((resolve) => {\n      setTimeout(() => {\n        resolve({\n          id: '1',\n          title: 'First video',\n          description: 'Video description',\n          image: 'blue',\n        });\n      }, 1000);\n    });\n  }\n  return cache;\n}\n\nexport function useLazyVideoData() {\n  return use(fetchVideo());\n}\n```\n\n\n```css\n#root {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  min-height: 200px;\n}\nbutton {\n  border: none;\n  border-radius: 50%;\n  width: 50px;\n  height: 50px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-color: #f0f8ff;\n  color: white;\n  font-size: 20px;\n  cursor: pointer;\n  transition: background-color 0.3s, border 0.3s;\n}\nbutton:hover {\n  border: 2px solid #ccc;\n  background-color: #e0e8ff;\n}\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n.loading {\n  background-image: linear-gradient(90deg, rgba(173, 216, 230, 0.3) 25%, rgba(135, 206, 250, 0.5) 50%, rgba(173, 216, 230, 0.3) 75%);\n  background-size: 200% 100%;\n  animation: shimmer 1.5s infinite;\n}\n@keyframes shimmer {\n  0% {\n    background-position: -200% 0;\n  }\n  100% {\n    background-position: 200% 0;\n  }\n}\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n  margin-top: 1em;\n}\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n.video .info:hover {\n  text-decoration: underline;\n}\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n.video-title.loading {\n  height: 20px;\n  width: 80px;\n  border-radius: 0.5rem;\n}\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n  border-radius: 0.5rem;\n}\n.video-description.loading {\n  height: 15px;\n  width: 100px;\n}\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  }\n}\n```\n\n</Sandpack>\n\nEnter/Exit:\n\n```\n<Suspense fallback={<ViewTransition><A /></ViewTransition>}>\n  <ViewTransition><B /></ViewTransition>\n</Suspense>\n```\n\nIn this scenario, these are two separate ViewTransition instances each with their own `view-transition-name`. This will be treated as an \"exit\" of the `<A>` and an \"enter\" of the `<B>`.\n\nYou can achieve different effects depending on where you choose to place the `<ViewTransition>` boundary.\n\n---\n### Opting-out of an animation {/*opting-out-of-an-animation*/}\n\nSometimes you're wrapping a large existing component, like a whole page, and you want to animate some updates, such as changing the theme. However, you don't want it to opt-in all updates inside the whole page to cross-fade when they're updating. Especially if you're incrementally adding more animations.\n\nYou can use the class \"none\" to opt-out of an animation. By wrapping your children in a \"none\" you can disable animations for updates to them while the parent still triggers.\n\n```js\n<ViewTransition>\n  <div className={theme}>\n    <ViewTransition update=\"none\">\n      {children}\n    </ViewTransition>\n  </div>\n</ViewTransition>\n```\n\nThis will only animate if the theme changes and not if only the children update. The children can still opt-in again with their own `<ViewTransition>` but at least it's manual again.\n\n---\n\n### Customizing animations {/*customizing-animations*/}\n\nBy default, `<ViewTransition>` includes the default cross-fade from the browser.\n\nTo customize animations, you can provide props to the `<ViewTransition>` component to specify which animations to use, based on how the `<ViewTransition>` activates.\n\nFor example, we can slow down the default cross fade animation:\n\n```js\n<ViewTransition default=\"slow-fade\">\n  <Video />\n</ViewTransition>\n```\n\nAnd define slow-fade in CSS using view transition classes:\n\n```css\n::view-transition-old(.slow-fade) {\n    animation-duration: 500ms;\n}\n\n::view-transition-new(.slow-fade) {\n    animation-duration: 500ms;\n}\n```\n\n<Sandpack>\n\n```js src/Video.js hidden\nfunction Thumbnail({ video, children }) {\n  return (\n    <div\n      aria-hidden=\"true\"\n      tabIndex={-1}\n      className={`thumbnail ${video.image}`}\n    />\n  );\n}\n\nexport function Video({ video }) {\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n      >\n        <Thumbnail video={video}></Thumbnail>\n\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n    </div>\n  );\n}\n```\n\n```js\nimport {\n  ViewTransition,\n  useState,\n  startTransition\n} from 'react';\nimport {Video} from \"./Video\";\nimport videos from \"./data\"\n\nfunction Item() {\n  return (\n    <ViewTransition default=\"slow-fade\">\n      <Video video={videos[0]}/>\n    </ViewTransition>\n  );\n}\n\nexport default function Component() {\n  const [showItem, setShowItem] = useState(false);\n  return (\n    <>\n      <button\n        onClick={() => {\n          startTransition(() => {\n            setShowItem((prev) => !prev);\n          });\n        }}\n      >{showItem ? '➖' : '➕'}</button>\n\n      {showItem ? <Item /> : null}\n    </>\n  );\n}\n```\n\n```js src/data.js hidden\nexport default [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  }\n]\n```\n\n\n```css\n::view-transition-old(.slow-fade) {\n    animation-duration: 500ms;\n}\n\n::view-transition-new(.slow-fade) {\n    animation-duration: 500ms;\n}\n\n#root {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  min-height: 200px;\n}\nbutton {\n  border: none;\n  border-radius: 50%;\n  width: 50px;\n  height: 50px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-color: #f0f8ff;\n  color: white;\n  font-size: 20px;\n  cursor: pointer;\n  transition: background-color 0.3s, border 0.3s;\n}\nbutton:hover {\n  border: 2px solid #ccc;\n  background-color: #e0e8ff;\n}\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n  margin-top: 1em;\n}\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n.video .info:hover {\n  text-decoration: underline;\n}\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  }\n}\n```\n\n</Sandpack>\n\nIn addition to setting the `default`, you can also provide configurations for `enter`, `exit`, `update`, and `share` animations.\n\n<Sandpack>\n\n```js src/Video.js hidden\nfunction Thumbnail({ video, children }) {\n  return (\n    <div\n      aria-hidden=\"true\"\n      tabIndex={-1}\n      className={`thumbnail ${video.image}`}\n    />\n  );\n}\n\nexport function Video({ video }) {\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n      >\n        <Thumbnail video={video}></Thumbnail>\n\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n    </div>\n  );\n}\n```\n\n```js\nimport {\n  ViewTransition,\n  useState,\n  startTransition\n} from 'react';\nimport {Video} from \"./Video\";\nimport videos from \"./data\"\n\nfunction Item() {\n  return (\n    <ViewTransition enter=\"slide-in\" exit=\"slide-out\">\n      <Video video={videos[0]}/>\n    </ViewTransition>\n  );\n}\n\nexport default function Component() {\n  const [showItem, setShowItem] = useState(false);\n  return (\n    <>\n      <button\n        onClick={() => {\n          startTransition(() => {\n            setShowItem((prev) => !prev);\n          });\n        }}\n      >{showItem ? '➖' : '➕'}</button>\n\n      {showItem ? <Item /> : null}\n    </>\n  );\n}\n```\n\n```js src/data.js hidden\nexport default [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  }\n]\n```\n\n\n```css\n::view-transition-old(.slide-in) {\n  animation-name: slideOutRight;\n  animation-duration: 500ms;\n  animation-timing-function: ease-in-out;\n}\n\n::view-transition-new(.slide-in) {\n  animation-name: slideInRight;\n  animation-duration: 500ms;\n  animation-timing-function: ease-in-out;\n}\n\n::view-transition-old(.slide-out) {\n  animation-name: slideOutLeft;\n  animation-duration: 500ms;\n  animation-timing-function: ease-in-out;\n}\n\n::view-transition-new(.slide-out) {\n  animation-name: slideInLeft;\n  animation-duration: 500ms;\n  animation-timing-function: ease-in-out;\n}\n\n@keyframes slideOutLeft {\n  from {\n    transform: translateX(0);\n    opacity: 1;\n  }\n  to {\n    transform: translateX(-100%);\n    opacity: 0;\n  }\n}\n\n@keyframes slideInLeft {\n  from {\n    transform: translateX(-100%);\n    opacity: 0;\n  }\n  to {\n    transform: translateX(0);\n    opacity: 1;\n  }\n}\n\n@keyframes slideOutRight {\n  from {\n    transform: translateX(0);\n    opacity: 1;\n  }\n  to {\n    transform: translateX(100%);\n    opacity: 0;\n  }\n}\n\n@keyframes slideInRight {\n  from {\n    transform: translateX(100%);\n    opacity: 0;\n  }\n  to {\n    transform: translateX(0);\n    opacity: 1;\n  }\n}\n\n@keyframes slideInRight {\n  from {\n    transform: translateX(100%);\n    opacity: 0;\n  }\n  to {\n    transform: translateX(0);\n    opacity: 1;\n  }\n}\n\n#root {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  min-height: 200px;\n}\nbutton {\n  border: none;\n  border-radius: 50%;\n  width: 50px;\n  height: 50px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-color: #f0f8ff;\n  color: white;\n  font-size: 20px;\n  cursor: pointer;\n  transition: background-color 0.3s, border 0.3s;\n}\nbutton:hover {\n  border: 2px solid #ccc;\n  background-color: #e0e8ff;\n}\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n  margin-top: 1em;\n}\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n.video .info:hover {\n  text-decoration: underline;\n}\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  }\n}\n```\n\n</Sandpack>\n\n### Customizing animations with types {/*customizing-animations-with-types*/}\nYou can use the [`addTransitionType`](/reference/react/addTransitionType) API to add a class name to the child elements when a specific transition type is activated for a specific activation trigger. This allows you to customize the animation for each type of transition.\n\nFor example, to customize the animation for all forward and backward navigations:\n\n```js\n<ViewTransition default={{\n  'navigation-back': 'slide-right',\n  'navigation-forward': 'slide-left',\n }}>\n  <div>...</div>\n</ViewTransition>\n \n// in your router:\nstartTransition(() => {\n  addTransitionType('navigation-' + navigationType);\n});\n```\n\nWhen the ViewTransition activates a \"navigation-back\" animation, React will add the class name \"slide-right\". When the ViewTransition activates a \"navigation-forward\" animation, React will add the class name \"slide-left\".\n\nIn the future, routers and other libraries may add support for standard view-transition types and styles.\n\n<Sandpack>\n\n```js src/Video.js hidden\nfunction Thumbnail({ video, children }) {\n  return (\n    <div\n      aria-hidden=\"true\"\n      tabIndex={-1}\n      className={`thumbnail ${video.image}`}\n    />\n  );\n}\n\nexport function Video({ video }) {\n  return (\n    <div className=\"video\">\n      <div\n        className=\"link\"\n      >\n        <Thumbnail video={video}></Thumbnail>\n        <div className=\"info\">\n          <div className=\"video-title\">{video.title}</div>\n          <div className=\"video-description\">{video.description}</div>\n        </div>\n      </div>\n    </div>\n  );\n}\n```\n\n```js\nimport {\n  ViewTransition,\n  addTransitionType,\n  useState,\n  startTransition,\n} from \"react\";\nimport {Video} from \"./Video\";\nimport videos from \"./data\"\n\nfunction Item() {\n  return (\n    <ViewTransition enter={\n        {\n          \"add-video-back\": \"slide-in-back\",\n          \"add-video-forward\": \"slide-in-forward\"\n        }\n      }\n      exit={\n        {\n          \"remove-video-back\": \"slide-in-forward\",\n          \"remove-video-forward\": \"slide-in-back\"\n        }\n      }>\n      <Video video={videos[0]}/>\n    </ViewTransition>\n  );\n}\n\nexport default function Component() {\n  const [showItem, setShowItem] = useState(false);\n  return (\n    <>\n      <div className=\"button-container\">\n        <button\n          onClick={() => {\n            startTransition(() => {\n              if (showItem) {\n                addTransitionType(\"remove-video-back\")\n              } else {\n                addTransitionType(\"add-video-back\")\n              }\n              setShowItem((prev) => !prev);\n            });\n          }}\n        >⬅️</button>\n        <button\n          onClick={() => {\n            startTransition(() => {\n              if (showItem) {\n                addTransitionType(\"remove-video-forward\")\n              } else {\n                addTransitionType(\"add-video-forward\")\n              }\n              setShowItem((prev) => !prev);\n            });\n          }}\n        >➡️</button>\n      </div>\n      {showItem ? <Item /> : null}\n    </>\n  );\n}\n```\n\n```js src/data.js hidden\nexport default [\n  {\n    id: '1',\n    title: 'First video',\n    description: 'Video description',\n    image: 'blue',\n  }\n]\n```\n\n\n```css\n::view-transition-old(.slide-in-back) {\n  animation-name: slideOutRight;\n  animation-duration: 500ms;\n  animation-timing-function: ease-in-out;\n}\n\n::view-transition-new(.slide-in-back) {\n  animation-name: slideInRight;\n  animation-duration: 500ms;\n  animation-timing-function: ease-in-out;\n}\n\n::view-transition-old(.slide-out-back) {\n  animation-name: slideOutLeft;\n  animation-duration: 500ms;\n  animation-timing-function: ease-in-out;\n}\n\n::view-transition-new(.slide-out-back) {\n  animation-name: slideInLeft;\n  animation-duration: 500ms;\n  animation-timing-function: ease-in-out;\n}\n\n::view-transition-old(.slide-in-forward) {\n  animation-name: slideOutLeft;\n  animation-duration: 500ms;\n  animation-timing-function: ease-in-out;\n}\n\n::view-transition-new(.slide-in-forward) {\n  animation-name: slideInLeft;\n  animation-duration: 500ms;\n  animation-timing-function: ease-in-out;\n}\n\n::view-transition-old(.slide-out-forward) {\n  animation-name: slideOutRight;\n  animation-duration: 500ms;\n  animation-timing-function: ease-in-out;\n}\n\n::view-transition-new(.slide-out-forward) {\n  animation-name: slideInRight;\n  animation-duration: 500ms;\n  animation-timing-function: ease-in-out;\n}\n\n@keyframes slideOutLeft {\n  from {\n    transform: translateX(0);\n    opacity: 1;\n  }\n  to {\n    transform: translateX(-100%);\n    opacity: 0;\n  }\n}\n\n@keyframes slideInLeft {\n  from {\n    transform: translateX(-100%);\n    opacity: 0;\n  }\n  to {\n    transform: translateX(0);\n    opacity: 1;\n  }\n}\n\n@keyframes slideOutRight {\n  from {\n    transform: translateX(0);\n    opacity: 1;\n  }\n  to {\n    transform: translateX(100%);\n    opacity: 0;\n  }\n}\n\n@keyframes slideInRight {\n  from {\n    transform: translateX(100%);\n    opacity: 0;\n  }\n  to {\n    transform: translateX(0);\n    opacity: 1;\n  }\n}\n\n@keyframes slideInRight {\n  from {\n    transform: translateX(100%);\n    opacity: 0;\n  }\n  to {\n    transform: translateX(0);\n    opacity: 1;\n  }\n}\n\n#root {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  min-height: 200px;\n}\nbutton {\n  border: none;\n  border-radius: 50%;\n  width: 50px;\n  height: 50px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-color: #f0f8ff;\n  color: white;\n  font-size: 20px;\n  cursor: pointer;\n  transition: background-color 0.3s, border 0.3s;\n}\nbutton:hover {\n  border: 2px solid #ccc;\n  background-color: #e0e8ff;\n}\n.button-container {\n  display: flex;\n}\n.thumbnail {\n  position: relative;\n  aspect-ratio: 16 / 9;\n  display: flex;\n  overflow: hidden;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border-radius: 0.5rem;\n  outline-offset: 2px;\n  width: 8rem;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-size: cover;\n  user-select: none;\n}\n.thumbnail.blue {\n  background-image: conic-gradient(at top right, #c76a15, #087ea4, #2b3491);\n}\n.video {\n  display: flex;\n  flex-direction: row;\n  gap: 0.75rem;\n  align-items: center;\n  margin-top: 1em;\n}\n.video .link {\n  display: flex;\n  flex-direction: row;\n  flex: 1 1 0;\n  gap: 0.125rem;\n  outline-offset: 4px;\n  cursor: pointer;\n}\n.video .info {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  margin-left: 8px;\n  gap: 0.125rem;\n}\n.video .info:hover {\n  text-decoration: underline;\n}\n.video-title {\n  font-size: 15px;\n  line-height: 1.25;\n  font-weight: 700;\n  color: #23272f;\n}\n.video-description {\n  color: #5e687e;\n  font-size: 13px;\n}\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"canary\",\n    \"react-dom\": \"canary\",\n    \"react-scripts\": \"latest\"\n  }\n}\n```\n\n</Sandpack>\n\n### Building View Transition enabled routers {/*building-view-transition-enabled-routers*/}\n\nReact waits for any pending Navigation to finish to ensure that scroll restoration happens within the animation. If the Navigation is blocked on React, your router must unblock in `useLayoutEffect` since `useEffect` would lead to a deadlock.\n\nIf a `startTransition` is started from the legacy popstate event, such as during a \"back\"-navigation then it must finish synchronously to ensure scroll and form restoration works correctly. This is in conflict with running a View Transition animation. Therefore, React will skip animations from popstate. Therefore animations won't run for the back button. You can fix this by upgrading your router to use the Navigation API.\n\n---\n\n## Troubleshooting {/*troubleshooting*/}\n\n### My `<ViewTransition>` is not activating {/*my-viewtransition-is-not-activating*/}\n\n`<ViewTransition>` only activates if it is placed before any DOM node:\n\n```js [3, 5]\nfunction Component() {\n  return (\n    <div>\n      <ViewTransition>Hi</ViewTransition>\n    </div>\n  );\n}\n```\n\nTo fix, ensure that the `<ViewTransition>` comes before any other DOM nodes:\n\n```js [3, 5] \nfunction Component() {\n  return (\n    <ViewTransition>\n      <div>Hi</div>\n    </ViewTransition>\n  );\n}\n```\n\n### I'm getting an error \"There are two `<ViewTransition name=%s>` components with the same name mounted at the same time.\" {/*two-viewtransition-with-same-name*/}\n\nThis error occurs when two `<ViewTransition>` components with the same `name` are mounted at the same time:\n\n\n```js [3]\nfunction Item() {\n  // 🚩 All items will get the same \"name\".\n  return <ViewTransition name=\"item\">...</ViewTransition>;\n}\n\nfunction ItemList({items}) {\n  return (\n    <>\n      {item.map(item => <Item key={item.id} />)}\n    </>\n  );\n}\n```\n\nThis will cause the View Transition to error. In development, React detects this issue to surface it and logs two errors:\n\n<ConsoleBlockMulti>\n<ConsoleLogLine level=\"error\">\n\nThere are two `<ViewTransition name=%s>` components with the same name mounted at the same time. This is not supported and will cause View Transitions to error. Try to use a more unique name e.g. by using a namespace prefix and adding the id of an item to the name.\n{'    '}at Item\n{'    '}at ItemList\n\n</ConsoleLogLine>\n\n<ConsoleLogLine level=\"error\">\n\nThe existing `<ViewTransition name=%s>` duplicate has this stack trace.\n{'    '}at Item\n{'    '}at ItemList\n\n</ConsoleLogLine>\n</ConsoleBlockMulti>\n\nTo fix, ensure that there's only one `<ViewTransition>` with the same name mounted at a time in the entire app by ensuring the `name` is unique, or adding an `id` to the name:\n\n```js [3]\nfunction Item({id}) {\n  // ✅ All items will get the same \"name\".\n  return <ViewTransition name={`item-${id}`}>...</ViewTransition>;\n}\n\nfunction ItemList({items}) {\n  return (\n    <>\n      {item.map(item => <Item key={item.id} item={item} />)}\n    </>\n  );\n}\n```\n"
  },
  {
    "path": "src/content/reference/react/act.md",
    "content": "---\ntitle: act\n---\n\n<Intro>\n\n`act` は、アサーションを行う前に保留中の React の更新を適用するために用いるテストヘルパです。\n\n```js\nawait act(async actFn)\n```\n\n</Intro>\n\nコンポーネントをアサーションが行える状態にまでもっていくために、レンダーや更新を行うコードを `await act()` の呼び出し内にラップします。これにより、テストがブラウザでの React の動作に近づきます。\n\n<Note>\n`act()` を直接使用するのは少し冗長に感じるかもしれません。ボイラープレートを避けるために、[React Testing Library](https://testing-library.com/docs/react-testing-library/intro) のようなライブラリを使用することができます。これらのヘルパは `act()` でラップされています。\n</Note>\n\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `await act(async actFn)` {/*await-act-async-actfn*/}\n\nUI テストを書く際、レンダー、ユーザイベント、データフェッチなどのタスクは、ユーザインターフェースにおける「操作単位 (unit of interaction)」と捉えることができます。React は `act()` というヘルパを提供しており、これによりこれらの「操作単位」に関連するすべての更新が処理されて DOM に適用された後に、アサーションを行えるようになります。\n\n`act` という名前は [Arrange-Act-Assert](https://wiki.c2.com/?ArrangeActAssert) パターンに由来します。\n\n```js {2,4}\nit ('renders with button disabled', async () => {\n  await act(async () => {\n    root.render(<TestComponent />)\n  });\n  expect(container.querySelector('button')).toBeDisabled();\n});\n```\n\n<Note>\n\n`act` を `await` および `async` 関数と一緒に使用することをお勧めします。同期バージョンも多くの場合に機能しますが、すべてのケースで機能するわけではありません。React が内部で更新をスケジュールする方法に絡む問題があり、同期バージョンを使用できるタイミングを予測することは困難です。\n\n将来的には同期バージョンを非推奨にし、削除する予定です。\n\n</Note>\n\n#### 引数 {/*parameters*/}\n\n* `async actFn`: テスト対象のコンポーネントのレンダーやユーザ操作をラップする非同期関数。`actFn` 内でトリガされた更新は内部の act キューに追加され、その後まとめてフラッシュされて DOM に変更が適用されます。非同期であるため、React は非同期境界を越えるコードも実行し、スケジュールされた更新をフラッシュします。\n\n#### 返り値 {/*returns*/}\n\n`act` は何も返しません。\n\n## 使用法 {/*usage*/}\n\nコンポーネントをテストする際に `act` を使用して、コンポーネントの出力についてアサーションを行うことができます。\n\n例えば以下のような `Counter` コンポーネントがあるとしましょう。後で挙げる使用例は、これをテストする方法を示しています。\n\n```js\nfunction Counter() {\n  const [count, setCount] = useState(0);\n  const handleClick = () => {\n    setCount(prev => prev + 1);\n  }\n\n  useEffect(() => {\n    document.title = `You clicked ${count} times`;\n  }, [count]);\n\n  return (\n    <div>\n      <p>You clicked {count} times</p>\n      <button onClick={handleClick}>\n        Click me\n      </button>\n    </div>\n  )\n}\n```\n\n### テスト内でコンポーネントをレンダーする {/*rendering-components-in-tests*/}\n\nコンポーネントのレンダー出力内容をテストするには、レンダーを `act()` 内にラップします。\n\n```js  {10,12}\nimport {act} from 'react';\nimport ReactDOMClient from 'react-dom/client';\nimport Counter from './Counter';\n\nit('can render and update a counter', async () => {\n  container = document.createElement('div');\n  document.body.appendChild(container);\n  \n  // ✅ Render the component inside act().\n  await act(() => {\n    ReactDOMClient.createRoot(container).render(<Counter />);\n  });\n  \n  const button = container.querySelector('button');\n  const label = container.querySelector('p');\n  expect(label.textContent).toBe('You clicked 0 times');\n  expect(document.title).toBe('You clicked 0 times');\n});\n```\n\nここではコンテナを作成し、それをドキュメントに追加し、`Counter` コンポーネントを `act()` 内でレンダーします。これにより、コンポーネントのレンダーとエフェクトの適用が終わってからアサーションが行えることが保証されます。\n\n`act` を使用することで、アサーションを行う前にすべての更新の適用が完了していることが保証されます。\n\n### テスト内でイベントをディスパッチする {/*dispatching-events-in-tests*/}\n\nイベントをテストするには、イベントのディスパッチを `act()` 内にラップします。\n\n```js {14,16}\nimport {act} from 'react';\nimport ReactDOMClient from 'react-dom/client';\nimport Counter from './Counter';\n\nit.only('can render and update a counter', async () => {\n  const container = document.createElement('div');\n  document.body.appendChild(container);\n  \n  await act( async () => {\n    ReactDOMClient.createRoot(container).render(<Counter />);\n  });\n  \n  // ✅ Dispatch the event inside act().\n  await act(async () => {\n    button.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n  });\n\n  const button = container.querySelector('button');\n  const label = container.querySelector('p');\n  expect(label.textContent).toBe('You clicked 1 times');\n  expect(document.title).toBe('You clicked 1 times');\n});\n```\n\nここでは、`act` でコンポーネントをレンダーし、その後別の `act()` 内でイベントをディスパッチしています。これにより、イベントに伴うすべての更新が適用されてからアサーションを行えることが保証されます。\n\n<Pitfall>\n\nDOM イベントをディスパッチできるのは、DOM コンテナがドキュメントに追加されている場合だけであることを忘れないようにしましょう。[React Testing Library](https://testing-library.com/docs/react-testing-library/intro) のようなライブラリを使用することでボイラープレートコードを減らすことができます。\n\n</Pitfall>\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### エラー：\"The current testing environment is not configured to support act(...)\" {/*error-the-current-testing-environment-is-not-configured-to-support-act*/}\n\n`act` を使用するには、テスト環境で `global.IS_REACT_ACT_ENVIRONMENT=true` を設定する必要があります。これは `act` が正しい環境でのみ使用されることを保証するためです。\n\nこのグローバル変数を設定しない場合、次のようなエラーが表示されます。\n\n<ConsoleBlock level=\"error\">\n\nWarning: The current testing environment is not configured to support act(...)\n\n</ConsoleBlock>\n\nこれを修正するには、React テストのグローバルセットアップファイルに次のコードを追加します。\n\n```js\nglobal.IS_REACT_ACT_ENVIRONMENT=true\n```\n\n<Note>\n\n[React Testing Library](https://testing-library.com/docs/react-testing-library/intro) のようなテストフレームワークでは、`IS_REACT_ACT_ENVIRONMENT` はすでに設定されています。\n\n</Note>\n"
  },
  {
    "path": "src/content/reference/react/addTransitionType.md",
    "content": "---\ntitle: addTransitionType\nversion: canary\n---\n\n<Canary>\n\n**The `addTransitionType` API is currently only available in React’s Canary and Experimental channels.** \n\n[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels)\n\n</Canary>\n\n<Intro>\n\n`addTransitionType` lets you specify the cause of a transition.\n\n\n```js\nstartTransition(() => {\n  addTransitionType('my-transition-type');\n  setState(newState);\n});\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## Reference {/*reference*/}\n\n### `addTransitionType` {/*addtransitiontype*/}\n\n#### Parameters {/*parameters*/}\n\n- `type`: The type of transition to add. This can be any string.\n\n#### Returns {/*returns*/}\n\n`addTransitionType` does not return anything.\n\n#### Caveats {/*caveats*/}\n\n- If multiple transitions are combined, all Transition Types are collected. You can also add more than one type to a Transition.\n- Transition Types are reset after each commit. This means a `<Suspense>` fallback will associate the types after a `startTransition`, but revealing the content does not.\n\n---\n\n## Usage {/*usage*/}\n\n### Adding the cause of a transition {/*adding-the-cause-of-a-transition*/}\n\nCall `addTransitionType` inside of `startTransition` to indicate the cause of a transition:\n\n``` [[1, 6, \"addTransitionType\"], [2, 5, \"startTransition\", [3, 6, \"'submit-click'\"]]\nimport { startTransition, addTransitionType } from 'react';\n\nfunction Submit({action) {\n  function handleClick() {\n    startTransition(() => {\n      addTransitionType('submit-click');\n      action();\n    });\n  }\n\n  return <button onClick={handleClick}>Click me</button>;\n}\n\n```\n\nWhen you call <CodeStep step={1}>addTransitionType</CodeStep> inside the scope of <CodeStep step={2}>startTransition</CodeStep>, React will associate <CodeStep step={3}>submit-click</CodeStep> as one of the causes for the Transition.\n\nCurrently, Transition Types can be used to customize different animations based on what caused the Transition. You have three different ways to choose from for how to use them:\n\n- [Customize animations using browser view transition types](#customize-animations-using-browser-view-transition-types)\n- [Customize animations using `View Transition` Class](#customize-animations-using-view-transition-class)\n- [Customize animations using `ViewTransition` events](#customize-animations-using-viewtransition-events) \n\nIn the future, we plan to support more use cases for using the cause of a transition.\n\n---\n### Customize animations using browser view transition types {/*customize-animations-using-browser-view-transition-types*/}\n\nWhen a [`ViewTransition`](/reference/react/ViewTransition) activates from a transition, React adds all the Transition Types as browser [view transition types](https://www.w3.org/TR/css-view-transitions-2/#active-view-transition-pseudo-examples) to the element.\n\nThis allows you to customize different animations based on CSS scopes:\n\n```js [11]\nfunction Component() {\n  return (\n    <ViewTransition>\n      <div>Hello</div>\n    </ViewTransition>\n  );\n}\n\nstartTransition(() => {\n  addTransitionType('my-transition-type');\n  setShow(true);\n});\n```\n\n```css\n:root:active-view-transition-type(my-transition-type) {\n  &::view-transition-...(...) {\n    ...\n  }\n}\n```\n\n---\n\n### Customize animations using `View Transition` Class {/*customize-animations-using-view-transition-class*/}\n\nYou can customize animations for an activated `ViewTransition` based on type by passing an object to the View Transition Class:\n\n```js\nfunction Component() {\n  return (\n    <ViewTransition enter={{\n      'my-transition-type': 'my-transition-class',\n    }}>\n      <div>Hello</div>\n    </ViewTransition>\n  );\n}\n\n// ...\nstartTransition(() => {\n  addTransitionType('my-transition-type');\n  setState(newState);\n});\n```\n\nIf multiple types match, then they're joined together. If no types match then the special \"default\" entry is used instead. If any type has the value \"none\" then that wins and the ViewTransition is disabled (not assigned a name).\n\nThese can be combined with enter/exit/update/layout/share props to match based on kind of trigger and Transition Type.\n\n```js\n<ViewTransition enter={{\n  'navigation-back': 'enter-right',\n  'navigation-forward': 'enter-left',\n}}\nexit={{\n  'navigation-back': 'exit-right',\n  'navigation-forward': 'exit-left',\n}}>\n```\n\n---\n\n### Customize animations using `ViewTransition` events {/*customize-animations-using-viewtransition-events*/}\n\nYou can imperatively customize animations for an activated `ViewTransition` based on type using View Transition events:\n\n```\n<ViewTransition onUpdate={(inst, types) => {\n  if (types.includes('navigation-back')) {\n    ...\n  } else if (types.includes('navigation-forward')) {\n    ...\n  } else {\n    ...\n  }\n}}>\n```\n\nThis allows you to pick different imperative Animations based on the cause.\n"
  },
  {
    "path": "src/content/reference/react/apis.md",
    "content": "---\ntitle: \"React の組み込み API\"\n---\n\n<Intro>\n\n[フック](/reference/react/hooks)や[コンポーネント](/reference/react/components)の他に、`react` パッケージはコンポーネントの定義に役立つ他の API も提供しています。このページでは、残りのモダンな React API をすべてリストアップしています。\n\n</Intro>\n\n---\n\n* [`createContext`](/reference/react/createContext) を利用すると、子コンポーネントに対してコンテクストを定義および提供できます。[`useContext`](/reference/react/useContext) と一緒に使用されます。\n* [`lazy`](/reference/react/lazy) を利用すると、コンポーネントのコードの読み込みを初回レンダーまで遅延することができます。\n* [`memo`](/reference/react/memo) を利用すると、同じ props を持つコンポーネントの再レンダーをスキップできます。[`useMemo`](/reference/react/useMemo) や [`useCallback`](/reference/react/useCallback) と一緒に使用されます。\n* [`startTransition`](/reference/react/startTransition) を使うと、state の更新を低緊急度 (non-urgent) としてマークできます。[`useTransition`](/reference/react/useTransition) に似ています。\n* [`act`](/reference/react/act) を使うと、テスト環境でレンダーやユーザ操作をラップして、アサーションを行う前に更新が確実に処理されるようにします。\n\n---\n\n## リソース API {/*resource-apis*/}\n\n*リソース (resource)* とは、state として保持しなくともコンポーネントからアクセスできる情報のことです。例えばコンポーネントはプロミス (Promise) からメッセージを読み取ったりコンテクストからスタイル情報を読み取ったりできます。\n\nリソースから値を読み取るには以下の API を使用します。\n\n* [`use`](/reference/react/use) を使うと、[プロミス](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)や[コンテクスト](/learn/passing-data-deeply-with-context)のようなリソースから値を読み取ることができます。\n```js\nfunction MessageComponent({ messagePromise }) {\n  const message = use(messagePromise);\n  const theme = use(ThemeContext);\n  // ...\n}\n```\n"
  },
  {
    "path": "src/content/reference/react/cache.md",
    "content": "---\ntitle: cache\n---\n\n<RSC>\n\n`cache` は、[React サーバコンポーネント](/reference/rsc/server-components)専用のものです。\n\n</RSC>\n\n<Intro>\n\n`cache` を使い、データフェッチや計算結果をキャッシュすることができます。\n\n```js\nconst cachedFn = cache(fn);\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `cache(fn)` {/*cache*/}\n\nコンポーネントの外部で `cache` を呼び出して、キャッシュが有効化されたバージョンの関数を作成します。\n\n```js {4,7}\nimport {cache} from 'react';\nimport calculateMetrics from 'lib/metrics';\n\nconst getMetrics = cache(calculateMetrics);\n\nfunction Chart({data}) {\n  const report = getMetrics(data);\n  // ...\n}\n```\n\n`getMetrics` が初めて `data` とともに呼び出されると、`getMetrics` は `calculateMetrics(data)` を呼び出し、その結果をキャッシュに保存します。もし `getMetrics` が同じ `data` で再度呼び出されると、`calculateMetrics(data)` を再度呼び出す代わりにキャッシュされた結果を返します。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n- `fn`: 結果をキャッシュしたい関数。`fn` は任意の引数を取り、任意の値を返すことができます。\n\n#### 返り値 {/*returns*/}\n\n`cache` は、同じ型シグネチャを持ち、キャッシュが有効化されたバージョンの `fn` を返します。その際に `fn` 自体は呼び出されません。\n\n何らかの引数で `cachedFn` を呼び出すと、まずキャッシュにキャッシュ済みの結果が存在するかどうかを確認します。キャッシュ済みの結果が存在する場合、その結果を返します。存在しない場合、与えられた引数で `fn` を呼び出し、結果をキャッシュに保存し、その結果を返します。`fn` が呼び出されるのはキャッシュミスが発生したときだけです。\n\n<Note>\n\n入力に基づいて返り値をキャッシュする最適化は、[*メモ化 (memoization)*](https://en.wikipedia.org/wiki/Memoization) として知られています。`cache` から返される関数をメモ化された関数 (memoized function) と呼びます。\n\n</Note>\n\n#### 注意点 {/*caveats*/}\n\n- React は、サーバへの各リクエストごとにすべてのメモ化された関数のキャッシュを無効化します。\n- `cache` を呼び出すたびに新しい関数が作成されます。これは、同じ関数で `cache` を複数回呼び出すと、同じキャッシュを共有しない異なるメモ化された関数が返されることを意味します。\n- `cachedFn` はエラーもキャッシュします。特定の引数で `fn` がエラーをスローすると、それがキャッシュされ、同じ引数で `cachedFn` が呼び出されると同じエラーが再スローされます。\n- `cache` は、[サーバコンポーネント](/reference/rsc/server-components)でのみ使用できます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 高コストな計算をキャッシュする {/*cache-expensive-computation*/}\n\n重複する処理をスキップするために `cache` を使用します。\n\n```js [[1, 7, \"getUserMetrics(user)\"],[2, 13, \"getUserMetrics(user)\"]]\nimport {cache} from 'react';\nimport calculateUserMetrics from 'lib/user';\n\nconst getUserMetrics = cache(calculateUserMetrics);\n\nfunction Profile({user}) {\n  const metrics = getUserMetrics(user);\n  // ...\n}\n\nfunction TeamReport({users}) {\n  for (let user in users) {\n    const metrics = getUserMetrics(user);\n    // ...\n  }\n  // ...\n}\n```\n\n同じ `user` オブジェクトが `Profile` と `TeamReport` の両方でレンダーされる場合、2 つのコンポーネントは処理を共有でき、その `user` に対して `calculateUserMetrics` が一度だけ呼び出されるようになります。\n\n最初に `Profile` がレンダーされると仮定します。<CodeStep step={1}>`getUserMetrics`</CodeStep> が呼び出され、キャッシュされた結果があるかどうかを確認します。その `user` で `getUserMetrics` を呼び出すのは初めてなので、キャッシュミスが発生します。`getUserMetrics` はその後、その `user` で `calculateUserMetrics` を呼び出し、結果をキャッシュに書き込みます。\n\n`TeamReport` が `users` のリストをレンダーし、同じ `user` オブジェクトに到達すると、<CodeStep step={2}>`getUserMetrics`</CodeStep> を呼び出し、結果をキャッシュから読み取ります。\n\n[`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) を渡すことで `calculateUserMetrics` を中断できる場合、React がレンダーを終了したときに高コストな計算をキャンセルするために [`cacheSignal()`](/reference/react/cacheSignal) を使用することができます。`calculateUserMetrics` は、`cacheSignal` を直接使用することにより、内部的にすでにキャンセル処理を行っている場合もあります。\n\n<Pitfall>\n\n##### メモ化された関数を複数作って呼び出すと異なるキャッシュから読み取られる {/*pitfall-different-memoized-functions*/}\n\n同じキャッシュにアクセスするためには、コンポーネントは同じメモ化された関数を呼び出さなければなりません。\n\n```js [[1, 7, \"getWeekReport\"], [1, 7, \"cache(calculateWeekReport)\"], [1, 8, \"getWeekReport\"]]\n// Temperature.js\nimport {cache} from 'react';\nimport {calculateWeekReport} from './report';\n\nexport function Temperature({cityData}) {\n  // 🚩 Wrong: Calling `cache` in component creates new `getWeekReport` for each render\n  const getWeekReport = cache(calculateWeekReport);\n  const report = getWeekReport(cityData);\n  // ...\n}\n```\n\n```js [[2, 6, \"getWeekReport\"], [2, 6, \"cache(calculateWeekReport)\"], [2, 9, \"getWeekReport\"]]\n// Precipitation.js\nimport {cache} from 'react';\nimport {calculateWeekReport} from './report';\n\n// 🚩 Wrong: `getWeekReport` is only accessible for `Precipitation` component.\nconst getWeekReport = cache(calculateWeekReport);\n\nexport function Precipitation({cityData}) {\n  const report = getWeekReport(cityData);\n  // ...\n}\n```\n\n上記の例では、<CodeStep step={2}>`Precipitation`</CodeStep> と <CodeStep step={1}>`Temperature`</CodeStep> はそれぞれ `cache` を呼び出して、それぞれ独自のキャッシュテーブルを持つ新しいメモ化された関数を作成しています。両方のコンポーネントが同じ `cityData` でレンダーする場合、それぞれが `calculateWeekReport` を呼び出すため、重複した処理が行われることになります。\n\nさらに、`Temperature` はコンポーネントがレンダーされるたびに<CodeStep step={1}>新しいメモ化された関数</CodeStep>を作成しているため、キャッシュによる共有はそもそも一切行えません。\n\nキャッシュヒットを最大化し、処理を減らすためには、2 つのコンポーネントは同じメモ化された関数を呼び出して同じキャッシュにアクセスするべきです。上記のようにするのではなく、複数のコンポーネントから [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) が行えるよう、メモ化された関数をそれ専用のモジュールで定義してください。\n\n```js [[3, 5, \"export default cache(calculateWeekReport)\"]]\n// getWeekReport.js\nimport {cache} from 'react';\nimport {calculateWeekReport} from './report';\n\nexport default cache(calculateWeekReport);\n```\n\n```js [[3, 2, \"getWeekReport\", 0], [3, 5, \"getWeekReport\"]]\n// Temperature.js\nimport getWeekReport from './getWeekReport';\n\nexport default function Temperature({cityData}) {\n\tconst report = getWeekReport(cityData);\n  // ...\n}\n```\n\n```js [[3, 2, \"getWeekReport\", 0], [3, 5, \"getWeekReport\"]]\n// Precipitation.js\nimport getWeekReport from './getWeekReport';\n\nexport default function Precipitation({cityData}) {\n  const report = getWeekReport(cityData);\n  // ...\n}\n```\nこれで、両方のコンポーネントが `./getWeekReport.js` からエクスポートされた<CodeStep step={3}>同じメモ化された関数</CodeStep>を呼び出して、同じキャッシュを読み書きするようになります。\n</Pitfall>\n\n### データのスナップショットを共有する {/*take-and-share-snapshot-of-data*/}\n\nコンポーネント間でデータのスナップショットを共有するためには、`fetch` のようなデータ取得関数を引数にして `cache` を呼び出します。複数のコンポーネントが同じデータを取得すると、リクエストは 1 回だけ行われ、返されたデータはキャッシュされ、コンポーネント間で共有されます。すべてのコンポーネントはサーバレンダー全体で同一のデータスナップショットを参照します。\n\n```js [[1, 4, \"city\"], [1, 5, \"fetchTemperature(city)\"], [2, 4, \"getTemperature\"], [2, 9, \"getTemperature\"], [1, 9, \"city\"], [2, 14, \"getTemperature\"], [1, 14, \"city\"]]\nimport {cache} from 'react';\nimport {fetchTemperature} from './api.js';\n\nconst getTemperature = cache(async (city) => {\n\treturn await fetchTemperature(city);\n});\n\nasync function AnimatedWeatherCard({city}) {\n\tconst temperature = await getTemperature(city);\n\t// ...\n}\n\nasync function MinimalWeatherCard({city}) {\n\tconst temperature = await getTemperature(city);\n\t// ...\n}\n```\n\n`AnimatedWeatherCard` と `MinimalWeatherCard` の両方が同じ <CodeStep step={1}>city</CodeStep> でレンダーする場合、<CodeStep step={2}>メモ化された関数</CodeStep>から同じデータのスナップショットを受け取ります。\n\n`AnimatedWeatherCard` と `MinimalWeatherCard` が異なる <CodeStep step={1}>city</CodeStep> 引数を <CodeStep step={2}>`getTemperature`</CodeStep> に渡した場合、`fetchTemperature` は 2 回呼び出され、それぞれの呼び出しが異なるデータを受け取ります。\n\n<CodeStep step={1}>city</CodeStep> はキャッシュキーとして機能します。\n\n<Note>\n\n<CodeStep step={3}>非同期レンダー</CodeStep>はサーバコンポーネントでのみサポートされています。\n\n```js [[3, 1, \"async\"], [3, 2, \"await\"]]\nasync function AnimatedWeatherCard({city}) {\n\tconst temperature = await getTemperature(city);\n\t// ...\n}\n```\n\nクライアントコンポーネントで非同期データを使用するコンポーネントをレンダーする場合は、[`use()` のドキュメント](/reference/react/use)を参照してください。\n\n</Note>\n\n### データをプリロードする {/*preload-data*/}\n\n時間のかかるデータ取得をキャッシュすることで、コンポーネントのレンダー前に非同期処理を開始することができます。\n\n```jsx [[2, 6, \"await getUser(id)\"], [1, 17, \"getUser(id)\"]]\nconst getUser = cache(async (id) => {\n  return await db.user.query(id);\n});\n\nasync function Profile({id}) {\n  const user = await getUser(id);\n  return (\n    <section>\n      <img src={user.profilePic} />\n      <h2>{user.name}</h2>\n    </section>\n  );\n}\n\nfunction Page({id}) {\n  // ✅ Good: start fetching the user data\n  getUser(id);\n  // ... some computational work\n  return (\n    <>\n      <Profile id={id} />\n    </>\n  );\n}\n```\n\n`Page` のレンダー時にコンポーネントは <CodeStep step={1}>`getUser`</CodeStep> を呼び出していますが、返されたデータを使用していないことに着目してください。この早期の <CodeStep step={1}>`getUser`</CodeStep> 呼び出しは、`Page` が他の計算処理を行ったり子をレンダーしたりしている間に実行される、非同期のデータベースクエリを開始します。\n\n`Profile` をレンダーするとき、再び <CodeStep step={2}>`getUser`</CodeStep> を呼び出します。最初の <CodeStep step={1}>`getUser`</CodeStep> 呼び出しがすでに完了しユーザデータをキャッシュしている場合、`Profile` が <CodeStep step={2}>このデータを要求して待機する時点</CodeStep>では、新たなリモートプロシージャ呼び出しを必要とせずにキャッシュから単に読み取ることができます。もし<CodeStep step={1}>最初のデータリクエスト</CodeStep>がまだ完了していない場合でも、このパターンでデータをプリロードすることで、データ取得の遅延を減らすことができます。\n\n<DeepDive>\n\n#### 非同期処理のキャッシュ {/*caching-asynchronous-work*/}\n\n[非同期関数](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)を評価すると、その処理の[プロミス (Promise)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) を受け取ります。プロミスはその処理の状態 (_pending_、_fulfilled_、_failed_) とその最終的な結果を保持します。\n\nこの例では、非同期関数 <CodeStep step={1}>`fetchData`</CodeStep> は `fetch` 結果を待機するプロミスを返します。\n\n```js [[1, 1, \"fetchData()\"], [2, 8, \"getData()\"], [3, 10, \"getData()\"]]\nasync function fetchData() {\n  return await fetch(`https://...`);\n}\n\nconst getData = cache(fetchData);\n\nasync function MyComponent() {\n  getData();\n  // ... some computational work\n  await getData();\n  // ...\n}\n```\n\n最初の <CodeStep step={2}>`getData`</CodeStep> 呼び出しでは、<CodeStep step={1}>`fetchData`</CodeStep> から返されたプロミスがキャッシュされます。その後のキャッシュ探索では、同じプロミスが返されます。\n\n最初の <CodeStep step={2}>`getData`</CodeStep> 呼び出しでは `await` しておらず、<CodeStep step={3}>2 回目の呼び出し</CodeStep> では `await` していることに注目してください。[`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) は JavaScript の演算子であり、プロミスの結果を待機して返します。最初の <CodeStep step={2}>`getData`</CodeStep> 呼び出しは単に `fetch` を開始してプロミスをキャッシュし、2 回目の <CodeStep step={3}>`getData`</CodeStep> のときに見つかるようにしているのです。\n\n<CodeStep step={3}>2 回目の呼び出し</CodeStep>時点でプロミスがまだ _pending_ の場合、`await` は結果を待ちます。`fetch` を待っている間に React が計算処理を続けることができるため、<CodeStep step={3}>2 回目の呼び出し</CodeStep>の待ち時間を短縮できる、という最適化になります。\n\nプロミスの最終状態がすでに決定 (settled) している場合、結果がエラーの場合でも正常終了 (fulfilled) の場合でも、`await` はその値をすぐに返します。どちらの結果でも、パフォーマンス上の利点があります。\n</DeepDive>\n\n<Pitfall>\n\n##### メモ化された関数をコンポーネント外で呼び出すとキャッシュは使用されない {/*pitfall-memoized-call-outside-component*/}\n\n```jsx [[1, 3, \"getUser\"]]\nimport {cache} from 'react';\n\nconst getUser = cache(async (userId) => {\n  return await db.user.query(userId);\n});\n\n// 🚩 Wrong: Calling memoized function outside of component will not memoize.\ngetUser('demo-id');\n\nasync function DemoProfile() {\n  // ✅ Good: `getUser` will memoize.\n  const user = await getUser('demo-id');\n  return <Profile user={user} />;\n}\n```\n\nReact がメモ化された関数に対してキャッシュアクセスを提供するのはコンポーネント内のみです。コンポーネントの外部で <CodeStep step={1}>`getUser`</CodeStep> を呼び出した場合も関数は評価されますが、キャッシュは読み取られず、更新もされません。\n\nこれは、キャッシュアクセスがコンポーネントからのみアクセス可能な[コンテクスト](/learn/passing-data-deeply-with-context)を通じて提供されるためです。\n\n</Pitfall>\n\n<DeepDive>\n\n#### `cache`、[`memo`](/reference/react/memo)、[`useMemo`](/reference/react/useMemo) のどれをいつ使うべきか {/*cache-memo-usememo*/}\n\n上記のすべての API はメモ化を提供しますが、それらが何をメモ化することを意図しているか、誰がキャッシュにアクセスできるか、そしてキャッシュが無効になるタイミングはいつか、という点で違いがあります。\n\n#### `useMemo` {/*deep-dive-use-memo*/}\n\n一般的に、[`useMemo`](/reference/react/useMemo) は、レンダー間でクライアントコンポーネント内の高コストな計算をキャッシュするために使用すべきです。例えば、コンポーネント内のデータの変換をメモ化するために使用します。\n\n```jsx {expectedErrors: {'react-compiler': [4]}} {4}\n'use client';\n\nfunction WeatherReport({record}) {\n  const avgTemp = useMemo(() => calculateAvg(record), record);\n  // ...\n}\n\nfunction App() {\n  const record = getRecord();\n  return (\n    <>\n      <WeatherReport record={record} />\n      <WeatherReport record={record} />\n    </>\n  );\n}\n```\nこの例では、`App` は同じレコードで 2 つの `WeatherReport` をレンダーしています。両方のコンポーネントが同じ処理を行っていますが、処理を共有することはできません。`useMemo` のキャッシュはコンポーネントのローカルにしか存在しません。\n\nしかし `useMemo` は、`App` が再レンダーされるが `record` オブジェクトが変わらない場合に、コンポーネントの各インスタンスが処理をスキップしてメモ化された `avgTemp` 値を使用できるようにします。`useMemo` は、与えられた依存配列に対応する `avgTemp` の最後の計算結果のみをキャッシュします。\n\n#### `cache` {/*deep-dive-cache*/}\n\n一般的に、`cache` は、コンポーネント間で共有できる処理をメモ化するために、サーバコンポーネントで使用すべきです。\n\n```js [[1, 12, \"<WeatherReport city={city} />\"], [3, 13, \"<WeatherReport city={city} />\"], [2, 1, \"cache(fetchReport)\"]]\nconst cachedFetchReport = cache(fetchReport);\n\nfunction WeatherReport({city}) {\n  const report = cachedFetchReport(city);\n  // ...\n}\n\nfunction App() {\n  const city = \"Los Angeles\";\n  return (\n    <>\n      <WeatherReport city={city} />\n      <WeatherReport city={city} />\n    </>\n  );\n}\n```\n前の例を `cache` を使用して書き直すと、この場合 <CodeStep step={3}>2 番目の `WeatherReport` インスタンス</CodeStep> は重複する処理をスキップし、<CodeStep step={1}>最初の `WeatherReport`</CodeStep> と同じキャッシュから読み取ることができます。前の例とのもうひとつの違いは、`cache` が<CodeStep step={2}>データフェッチのメモ化</CodeStep>にも推奨されているということです。これは `useMemo` が計算のみに使用すべきであることとは対照的です。\n\n現時点では、`cache` はサーバコンポーネントでのみ使用すべきです。キャッシュはサーバリクエストをまたぐと無効化されます。\n\n#### `memo` {/*deep-dive-memo*/}\n\n[`memo`](reference/react/memo) は、props が変わらない場合にコンポーネントの再レンダーを防ぐために使用すべきです。\n\n```js\n'use client';\n\nfunction WeatherReport({record}) {\n  const avgTemp = calculateAvg(record);\n  // ...\n}\n\nconst MemoWeatherReport = memo(WeatherReport);\n\nfunction App() {\n  const record = getRecord();\n  return (\n    <>\n      <MemoWeatherReport record={record} />\n      <MemoWeatherReport record={record} />\n    </>\n  );\n}\n```\n\nこの例では、両方の `MemoWeatherReport` コンポーネントは最初にレンダーされたときに `calculateAvg` を呼び出します。しかし、`App` が再レンダーされ、`record` に変更がない場合、props は一切変わらないため `MemoWeatherReport` は再レンダーされません。\n\n`useMemo` とは異なり、`memo` は特定の計算ではなく props に基づいてコンポーネントのレンダーをメモ化します。一方で最後の props の値に対応する最後のレンダー結果だけがキャッシュされるという点では `useMemo` と似ています。props が変更されるとキャッシュは無効化され、コンポーネントは再レンダーされます。\n\n</DeepDive>\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### メモ化された関数が、同じ引数で呼び出されても実行される {/*memoized-function-still-runs*/}\n\n上で述べた落とし穴を参照してください。\n* [メモ化された関数を複数呼び出すと、別々のキャッシュから読み取られてしまいます](#pitfall-different-memoized-functions)。\n* [メモ化された関数をコンポーネントの外部で呼び出すと、キャッシュは使用されません](#pitfall-memoized-call-outside-component)。\n\n上記のいずれも該当しない場合、何かがキャッシュに存在するかどうかを React がチェックする方法に関連した問題かもしれません。\n\n引数が[プリミティブ](https://developer.mozilla.org/en-US/docs/Glossary/Primitive)でない場合（例：オブジェクト、関数、配列）、同じオブジェクト参照を渡していることを確認してください。\n\nメモ化された関数を呼び出すとき、React は入力された引数を調べて結果がすでにキャッシュされているかどうかを確認します。React は引数に対して浅い (shallow) 比較を行い、キャッシュヒットがあるかどうかを判断します。\n\n```js\nimport {cache} from 'react';\n\nconst calculateNorm = cache((vector) => {\n  // ...\n});\n\nfunction MapMarker(props) {\n  // 🚩 Wrong: props is an object that changes every render.\n  const length = calculateNorm(props);\n  // ...\n}\n\nfunction App() {\n  return (\n    <>\n      <MapMarker x={10} y={10} z={10} />\n      <MapMarker x={10} y={10} z={10} />\n    </>\n  );\n}\n```\n\nこの例では、2 つの `MapMarker` が同じ処理を行っており、`calculateNorm` を `{x: 10, y: 10, z:10}` という同じ値で呼び出しているように見えます。しかしそれぞれのコンポーネントが別々に `props` オブジェクトを作成しているため、これらのオブジェクトは同じ値を含んでいますが、同じオブジェクト参照ではありません。\n\nReact は入力に対して [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) を呼び出し、キャッシュヒットがあるかどうかを確認します。\n\n```js {3,9}\nimport {cache} from 'react';\n\nconst calculateNorm = cache((x, y, z) => {\n  // ...\n});\n\nfunction MapMarker(props) {\n  // ✅ Good: Pass primitives to memoized function\n  const length = calculateNorm(props.x, props.y, props.z);\n  // ...\n}\n\nfunction App() {\n  return (\n    <>\n      <MapMarker x={10} y={10} z={10} />\n      <MapMarker x={10} y={10} z={10} />\n    </>\n  );\n}\n```\n\nこれを解決するひとつの方法は、ベクトルの各次元の値を別々に `calculateNorm` に渡すことです。各次元の値はプリミティブであるため、これは機能します。\n\nありえる別の解決策は、ベクトルオブジェクト自体をコンポーネントの props として渡すことです。同じオブジェクトを両方のコンポーネントインスタンスに渡す必要があります。\n\n```js {3,9,14}\nimport {cache} from 'react';\n\nconst calculateNorm = cache((vector) => {\n  // ...\n});\n\nfunction MapMarker(props) {\n  // ✅ Good: Pass the same `vector` object\n  const length = calculateNorm(props.vector);\n  // ...\n}\n\nfunction App() {\n  const vector = [10, 10, 10];\n  return (\n    <>\n      <MapMarker vector={vector} />\n      <MapMarker vector={vector} />\n    </>\n  );\n}\n```\n"
  },
  {
    "path": "src/content/reference/react/cacheSignal.md",
    "content": "---\ntitle: cacheSignal\n---\n\n<RSC>\n\n`cacheSignal` is currently only used with [React Server Components](/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#react-server-components).\n\n</RSC>\n\n<Intro>\n\n`cacheSignal` allows you to know when the `cache()` lifetime is over.\n\n```js\nconst signal = cacheSignal();\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## Reference {/*reference*/}\n\n### `cacheSignal` {/*cachesignal*/}\n\nCall `cacheSignal` to get an `AbortSignal`.\n\n```js {3,7}\nimport {cacheSignal} from 'react';\nasync function Component() {\n  await fetch(url, { signal: cacheSignal() });\n}\n```\n\nWhen React has finished rendering, the `AbortSignal` will be aborted. This allows you to cancel any in-flight work that is no longer needed.\nRendering is considered finished when:\n- React has successfully completed rendering\n- the render was aborted\n- the render has failed\n\n#### Parameters {/*parameters*/}\n\nThis function does not accept any parameters.\n\n#### Returns {/*returns*/}\n\n`cacheSignal` returns an `AbortSignal` if called during rendering. Otherwise `cacheSignal()` returns `null`.\n\n#### Caveats {/*caveats*/}\n\n- `cacheSignal` is currently for use in [React Server Components](/reference/rsc/server-components) only. In Client Components, it will always return `null`. In the future it will also be used for Client Component when a client cache refreshes or invalidates. You should not assume it'll always be null on the client.\n- If called outside of rendering, `cacheSignal` will return `null` to make it clear that the current scope isn't cached forever.\n\n---\n\n## Usage {/*usage*/}\n\n### Cancel in-flight requests {/*cancel-in-flight-requests*/}\n\nCall <CodeStep step={1}>`cacheSignal`</CodeStep> to abort in-flight requests.\n\n```js [[1, 4, \"cacheSignal()\"]]\nimport {cache, cacheSignal} from 'react';\nconst dedupedFetch = cache(fetch);\nasync function Component() {\n  await dedupedFetch(url, { signal: cacheSignal() });\n}\n```\n\n<Pitfall>\nYou can't use `cacheSignal` to abort async work that was started outside of rendering e.g.\n\n```js\nimport {cacheSignal} from 'react';\n// 🚩 Pitfall: The request will not actually be aborted if the rendering of `Component` is finished.\nconst response = fetch(url, { signal: cacheSignal() });\nasync function Component() {\n  await response;\n}\n```\n</Pitfall>\n\n### Ignore errors after React has finished rendering {/*ignore-errors-after-react-has-finished-rendering*/}\n\nIf a function throws, it may be due to cancellation (e.g. <CodeStep step={1}>the Database connection</CodeStep> has been closed). You can use the <CodeStep step={2}>`aborted` property</CodeStep> to check if the error was due to cancellation or a real error. You may want to <CodeStep step={3}>ignore errors</CodeStep> that were due to cancellation.\n\n```js [[1, 2, \"./database\"], [2, 8, \"cacheSignal()?.aborted\"], [3, 12, \"return null\"]]\nimport {cacheSignal} from \"react\";\nimport {queryDatabase, logError} from \"./database\";\n\nasync function getData(id) {\n  try {\n     return await queryDatabase(id);\n  } catch (x) {\n     if (!cacheSignal()?.aborted) {\n        // only log if it's a real error and not due to cancellation\n       logError(x);\n     }\n     return null;\n  }\n}\n\nasync function Component({id}) {\n  const data = await getData(id);\n  if (data === null) {\n    return <div>No data available</div>;\n  }\n  return <div>{data.name}</div>;\n}\n```\n"
  },
  {
    "path": "src/content/reference/react/captureOwnerStack.md",
    "content": "---\ntitle: captureOwnerStack\n---\n\n<Intro>\n\n`captureOwnerStack` は、開発環境で現在のオーナスタック (Owner Stack) を読み取り、利用可能な場合は文字列として返します。\n\n```js\nconst stack = captureOwnerStack();\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `captureOwnerStack()` {/*captureownerstack*/}\n\n`captureOwnerStack` を呼び出して、現在のオーナスタックを取得します。\n\n```js {5,5}\nimport * as React from 'react';\n\nfunction Component() {\n  if (process.env.NODE_ENV !== 'production') {\n    const ownerStack = React.captureOwnerStack();\n    console.log(ownerStack);\n  }\n}\n```\n\n#### 引数 {/*parameters*/}\n\n`captureOwnerStack` は引数を受け取りません。\n\n#### 返り値 {/*returns*/}\n\n`captureOwnerStack` は `string | null` を返します。\n\nオーナスタックは、以下の状況で利用できます。\n- コンポーネントのレンダー中\n- エフェクト（`useEffect` など）内\n- React のイベントハンドラ内（`<button onClick={...} />` など）\n- React のエラーハンドラ（[React ルートオプション](/reference/react-dom/client/createRoot#parameters) の `onCaughtError`、`onRecoverableError`、`onUncaughtError` など）\n\nオーナスタックが利用できない場合は `null` が返されます（[トラブルシューティング：オーナスタックが `null` になる](#the-owner-stack-is-null) を参照）。\n\n#### 注意点 {/*caveats*/}\n\n- オーナスタックは開発環境でのみ利用できます。開発環境以外では `captureOwnerStack` は常に `null` を返します。\n\n<DeepDive>\n\n#### オーナスタックとコンポーネントスタックの違い {/*owner-stack-vs-component-stack*/}\n\nオーナスタックは、React のエラーハンドラで利用できるコンポーネントスタック（例：[`onUncaughtError` 内での `errorInfo.componentStack`](/reference/react-dom/client/hydrateRoot#error-logging-in-production)）とは異なります。\n\n例えば、次のコードを考えてみましょう。\n\n<Sandpack>\n\n```js src/App.js\nimport {Suspense} from 'react';\n\nfunction SubComponent({disabled}) {\n  if (disabled) {\n    throw new Error('disabled');\n  }\n}\n\nexport function Component({label}) {\n  return (\n    <fieldset>\n      <legend>{label}</legend>\n      <SubComponent key={label} disabled={label === 'disabled'} />\n    </fieldset>\n  );\n}\n\nfunction Navigation() {\n  return null;\n}\n\nexport default function App({children}) {\n  return (\n    <Suspense fallback=\"loading...\">\n      <main>\n        <Navigation />\n        {children}\n      </main>\n    </Suspense>\n  );\n}\n```\n\n```js src/index.js\nimport {captureOwnerStack} from 'react';\nimport {createRoot} from 'react-dom/client';\nimport App, {Component} from './App.js';\nimport './styles.css';\n\ncreateRoot(document.createElement('div'), {\n  onUncaughtError: (error, errorInfo) => {\n    // The stacks are logged instead of showing them in the UI directly to\n    // highlight that browsers will apply sourcemaps to the logged stacks.\n    // Note that sourcemapping is only applied in the real browser console not\n    // in the fake one displayed on this page.\n    // Press \"fork\" to be able to view the sourcemapped stack in a real console.\n    console.log(errorInfo.componentStack);\n    console.log(captureOwnerStack());\n  },\n}).render(\n  <App>\n    <Component label=\"disabled\" />\n  </App>\n);\n```\n\n```html public/index.html hidden\n<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Document</title>\n  </head>\n  <body>\n    <p>Check the console output.</p>\n  </body>\n</html>\n```\n\n</Sandpack>\n\n`SubComponent` でエラーがスローされるとします。\nそのエラーのコンポーネントスタックは次のようになります。\n\n```\nat SubComponent\nat fieldset\nat Component\nat main\nat React.Suspense\nat App\n```\n\n一方、オーナスタックは次のようになります。\n\n```\nat Component\n```\n\nこのスタックでは、`App` や DOM コンポーネント（例：`fieldset`）は「オーナ」扱いとなりません。それらは `SubComponent` を「作成」したわけではなく、ノードを単に転送しただけだからです。`Component` は `<SubComponent />` というマークアップを通じて `SubComponent` ノードを作成しているのに対し、`App` は `children` を単にレンダーしているだけです。\n\nまた、`Navigation` や `legend` も、`<SubComponent />` を含むノードの兄弟であるためスタックには含まれません。\n\n`SubComponent` はすでにコールスタックに含まれているため、オーナスタックには表示されません。\n\n</DeepDive>\n\n## 使用法 {/*usage*/}\n\n### カスタムのエラーオーバーレイの機能強化 {/*enhance-a-custom-error-overlay*/}\n\n```js [[1, 5, \"console.error\"], [4, 7, \"captureOwnerStack\"]]\nimport { captureOwnerStack } from \"react\";\nimport { instrumentedConsoleError } from \"./errorOverlay\";\n\nconst originalConsoleError = console.error;\nconsole.error = function patchedConsoleError(...args) {\n  originalConsoleError.apply(console, args);\n  const ownerStack = captureOwnerStack();\n  onConsoleError({\n    // Keep in mind that in a real application, console.error can be\n    // called with multiple arguments which you should account for.\n    consoleMessage: args[0],\n    ownerStack,\n  });\n};\n```\n\n<CodeStep step={1}>`console.error`</CodeStep> の呼び出しをインターセプトしてエラーオーバーレイとして表示する場合、<CodeStep step={2}>`captureOwnerStack`</CodeStep> を呼び出してオーナスタックを含めることができます。\n\n<Sandpack>\n\n```css src/styles.css\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  margin: 20px;\n  padding: 0;\n}\n\nh1 {\n  margin-top: 0;\n  font-size: 22px;\n}\n\nh2 {\n  margin-top: 0;\n  font-size: 20px;\n}\n\ncode {\n  font-size: 1.2em;\n}\n\nul {\n  padding-inline-start: 20px;\n}\n\nlabel, button { display: block; margin-bottom: 20px; }\nhtml, body { min-height: 300px; }\n\n#error-dialog {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  background-color: white;\n  padding: 15px;\n  opacity: 0.9;\n  text-wrap: wrap;\n  overflow: scroll;\n}\n\n.text-red {\n  color: red;\n}\n\n.-mb-20 {\n  margin-bottom: -20px;\n}\n\n.mb-0 {\n  margin-bottom: 0;\n}\n\n.mb-10 {\n  margin-bottom: 10px;\n}\n\npre {\n  text-wrap: wrap;\n}\n\npre.nowrap {\n  text-wrap: nowrap;\n}\n\n.hidden {\n display: none;  \n}\n```\n\n```html public/index.html hidden\n<!DOCTYPE html>\n<html>\n<head>\n  <title>My app</title>\n</head>\n<body>\n<!--\n  Error dialog in raw HTML\n  since an error in the React app may crash.\n-->\n<div id=\"error-dialog\" class=\"hidden\">\n  <h1 id=\"error-title\" class=\"text-red\">Error</h1>\n  <p>\n    <pre id=\"error-body\"></pre>\n  </p>\n  <h2 class=\"-mb-20\">Owner Stack:</h4>\n  <pre id=\"error-owner-stack\" class=\"nowrap\"></pre>\n  <button\n    id=\"error-close\"\n    class=\"mb-10\"\n    onclick=\"document.getElementById('error-dialog').classList.add('hidden')\"\n  >\n    Close\n  </button>\n</div>\n<!-- This is the DOM node -->\n<div id=\"root\"></div>\n</body>\n</html>\n\n```\n\n```js src/errorOverlay.js\n\nexport function onConsoleError({ consoleMessage, ownerStack }) {\n  const errorDialog = document.getElementById(\"error-dialog\");\n  const errorBody = document.getElementById(\"error-body\");\n  const errorOwnerStack = document.getElementById(\"error-owner-stack\");\n\n  // Display console.error() message\n  errorBody.innerText = consoleMessage;\n\n  // Display owner stack\n  errorOwnerStack.innerText = ownerStack;\n\n  // Show the dialog\n  errorDialog.classList.remove(\"hidden\");\n}\n```\n\n```js src/index.js active\nimport { captureOwnerStack } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport App from './App';\nimport { onConsoleError } from \"./errorOverlay\";\nimport './styles.css';\n\nconst originalConsoleError = console.error;\nconsole.error = function patchedConsoleError(...args) {\n  originalConsoleError.apply(console, args);\n  const ownerStack = captureOwnerStack();\n  onConsoleError({\n    // Keep in mind that in a real application, console.error can be\n    // called with multiple arguments which you should account for.\n    consoleMessage: args[0],\n    ownerStack,\n  });\n};\n\nconst container = document.getElementById(\"root\");\ncreateRoot(container).render(<App />);\n```\n\n```js src/App.js\nfunction Component() {\n  return <button onClick={() => console.error('Some console error')}>Trigger console.error()</button>;\n}\n\nexport default function App() {\n  return <Component />;\n}\n```\n\n</Sandpack>\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### オーナスタックが `null` になる {/*the-owner-stack-is-null*/}\n\n`captureOwnerStack` の呼び出しが React 管理外の関数（`setTimeout` のコールバック、`fetch` の後、カスタム DOM イベントハンドラなど）で行われています。レンダー中、エフェクト内、React 管理のイベントハンドラやエラーハンドラ（例：`hydrateRoot#options.onCaughtError`）内では、オーナスタックが利用できるはずです。\n\n以下の例では、`captureOwnerStack` の呼び出しがカスタム DOM イベントハンドラ内で行われているため、ボタンをクリックしてもログに出力されるオーナスタックは空になります。オーナスタックの取得は、呼び出しをエフェクト本体に移動するなどして先に取得しておく必要があります。\n<Sandpack>\n\n```js\nimport {captureOwnerStack, useEffect} from 'react';\n\nexport default function App() {\n  useEffect(() => {\n    // Should call `captureOwnerStack` here.\n    function handleEvent() {\n      // Calling it in a custom DOM event handler is too late.\n      // The Owner Stack will be `null` at this point.\n      console.log('Owner Stack: ', captureOwnerStack());\n    }\n\n    document.addEventListener('click', handleEvent);\n\n    return () => {\n      document.removeEventListener('click', handleEvent);\n    }\n  })\n\n  return <button>Click me to see that Owner Stacks are not available in custom DOM event handlers</button>;\n}\n```\n\n</Sandpack>\n\n### `captureOwnerStack` 関数にアクセスできない {/*captureownerstack-is-not-available*/}\n\n`captureOwnerStack` は開発ビルドでのみエクスポートされます。本番ビルドでは `undefined` になります。開発・本番両方でバンドルされるファイルで `captureOwnerStack` を使う場合は、名前付きインポートではなく名前空間インポートを使い、条件付きでアクセスするようにしてください。\n\n```js\n// Don't use named imports of `captureOwnerStack` in files that are bundled for development and production.\nimport {captureOwnerStack} from 'react';\n// Use a namespace import instead and access `captureOwnerStack` conditionally.\nimport * as React from 'react';\n\nif (process.env.NODE_ENV !== 'production') {\n  const ownerStack = React.captureOwnerStack();\n  console.log('Owner Stack', ownerStack);\n}\n```\n"
  },
  {
    "path": "src/content/reference/react/cloneElement.md",
    "content": "---\ntitle: cloneElement\n---\n\n<Pitfall>\n\n`cloneElement` の使用は一般的ではなく、コードが壊れやすくなる可能性があります。[一般的な代替手段をご覧ください](#alternatives)。\n\n</Pitfall>\n\n<Intro>\n\n`cloneElement` を使用すると、別の要素に基づいて新しい React 要素を作成することができます。\n\n```js\nconst clonedElement = cloneElement(element, props, ...children)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `cloneElement(element, props, ...children)` {/*cloneelement*/}\n\n`cloneElement` を呼び出して、`element` を基に、異なる `props` と `children` を持った React 要素を作成します。\n\n```js\nimport { cloneElement } from 'react';\n\n// ...\nconst clonedElement = cloneElement(\n  <Row title=\"Cabbage\">\n    Hello\n  </Row>,\n  { isHighlighted: true },\n  'Goodbye'\n);\n\nconsole.log(clonedElement); // <Row title=\"Cabbage\" isHighlighted={true}>Goodbye</Row>\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `element`: `element` 引数は有効な React 要素でなければなりません。例えば、`<Something />` のような JSX ノード、[`createElement`](/reference/react/createElement) の呼び出し結果、または別の `cloneElement` の呼び出し結果などです。\n\n* `props`: `props` 引数はオブジェクトか `null` でなければなりません。`null` を渡すと、クローンされた要素は元の `element.props` をすべて保持します。それ以外の場合、`props` オブジェクト内のすべての項目について、返される要素では `element.props` の値よりも `props` からの値が「優先」されます。残りの props は元の `element.props` から埋められます。`props.key` や `props.ref` を渡した場合、それらは元のものを置き換えます。\n\n* **省略可能** `...children`: ゼロ個以上の子ノード。あらゆる React ノード、つまり React 要素、文字列、数値、[ポータル](/reference/react-dom/createPortal)、空ノード（`null`、`undefined`、`true`、`false`）、React ノードの配列になります。`...children` 引数を渡さない場合、元の `element.props.children` が保持されます。\n\n#### 返り値 {/*returns*/}\n\n`cloneElement` は以下のプロパティを持つ React 要素オブジェクトを返します。\n\n* `type`: `element.type` と同じ。\n* `props`: `element.props` に、渡された上書き用の `props` を浅くマージした結果。\n* `ref`: 元の `element.ref`。ただし、`props.ref` によって上書きされた場合は除く。\n* `key`: 元の `element.key`。ただし、`props.key` によって上書きされた場合は除く。\n\n通常、この要素をコンポーネントから返すか、他の要素の子として用います。要素のプロパティを読み取ることは可能ですが、作成後は要素の構造を非公開 (opaque) として扱い、レンダーのみ行うようにするべきです。\n\n#### 注意点 {/*caveats*/}\n\n* 要素をクローンしても**元の要素は変更されません**。\n\n* **複数の子の内容がすべて静的に分かっている場合**、`cloneElement` には子を `cloneElement(element, null, child1, child2, child3)` のように**複数の引数として渡してください**。子が動的な場合は、配列全体を第 3 引数として `cloneElement(element, null, listItems)` のように渡してください。これにより、React は動的なリストに `key` が欠けている場合に[警告を出す](/learn/rendering-lists#keeping-list-items-in-order-with-key)ようになります。静的なリストでは並び替えは決して発生しないため、key は必要ありません。\n\n* `cloneElement` を使うとデータフローの追跡が難しくなるため、代わりに[代替手段](#alternatives)を試してみてください。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 要素の props を上書きする {/*overriding-props-of-an-element*/}\n\n<CodeStep step={1}>React 要素</CodeStep> の props を上書きするには、それを `cloneElement` に渡し、<CodeStep step={2}>上書きしたい props</CodeStep> を指定します。\n\n```js [[1, 5, \"<Row title=\\\\\"Cabbage\\\\\" />\"], [2, 6, \"{ isHighlighted: true }\"], [3, 4, \"clonedElement\"]]\nimport { cloneElement } from 'react';\n\n// ...\nconst clonedElement = cloneElement(\n  <Row title=\"Cabbage\" />,\n  { isHighlighted: true }\n);\n```\n\nこの場合、結果となる<CodeStep step={3}>クローンされた要素</CodeStep>は `<Row title=\"Cabbage\" isHighlighted={true} />` になります。\n\n**例を使って、これが役立つ場面を見てみましょう**。\n\n選択可能な行のリストと、選択されている行を変更する \"Next\" ボタンをレンダーする `List` コンポーネントを想像してみてください。`List` コンポーネントは、選択された `Row` を異なる方法でレンダーする必要があるため、受け取ったすべての `<Row>` をクローンし、`isHighlighted: true` または `isHighlighted: false` を追加の props として指定します。\n\n```js {6-8}\nexport default function List({ children }) {\n  const [selectedIndex, setSelectedIndex] = useState(0);\n  return (\n    <div className=\"List\">\n      {Children.map(children, (child, index) =>\n        cloneElement(child, {\n          isHighlighted: index === selectedIndex \n        })\n      )}\n```\n\n例えば `List` が受け取る元の JSX が以下のようなものである場合を考えます。\n\n```js {2-4}\n<List>\n  <Row title=\"Cabbage\" />\n  <Row title=\"Garlic\" />\n  <Row title=\"Apple\" />\n</List>\n```\n\n子要素をクローンすることで、`List` は内部のすべての `Row` に追加情報を渡すことができます。結果は以下のようになります。\n\n```js {4,8,12}\n<List>\n  <Row\n    title=\"Cabbage\"\n    isHighlighted={true} \n  />\n  <Row\n    title=\"Garlic\"\n    isHighlighted={false} \n  />\n  <Row\n    title=\"Apple\"\n    isHighlighted={false} \n  />\n</List>\n```\n\n\"Next\" を押すと `List` の state が更新され、異なる行がハイライトされることに着目してください。\n\n<Sandpack>\n\n```js\nimport List from './List.js';\nimport Row from './Row.js';\nimport { products } from './data.js';\n\nexport default function App() {\n  return (\n    <List>\n      {products.map(product =>\n        <Row\n          key={product.id}\n          title={product.title} \n        />\n      )}\n    </List>\n  );\n}\n```\n\n```js src/List.js active\nimport { Children, cloneElement, useState } from 'react';\n\nexport default function List({ children }) {\n  const [selectedIndex, setSelectedIndex] = useState(0);\n  return (\n    <div className=\"List\">\n      {Children.map(children, (child, index) =>\n        cloneElement(child, {\n          isHighlighted: index === selectedIndex \n        })\n      )}\n      <hr />\n      <button onClick={() => {\n        setSelectedIndex(i =>\n          (i + 1) % Children.count(children)\n        );\n      }}>\n        Next\n      </button>\n    </div>\n  );\n}\n```\n\n```js src/Row.js\nexport default function Row({ title, isHighlighted }) {\n  return (\n    <div className={[\n      'Row',\n      isHighlighted ? 'RowHighlighted' : ''\n    ].join(' ')}>\n      {title}\n    </div>\n  );\n}\n```\n\n```js src/data.js\nexport const products = [\n  { title: 'Cabbage', id: 1 },\n  { title: 'Garlic', id: 2 },\n  { title: 'Apple', id: 3 },\n];\n```\n\n```css\n.List {\n  display: flex;\n  flex-direction: column;\n  border: 2px solid grey;\n  padding: 5px;\n}\n\n.Row {\n  border: 2px dashed black;\n  padding: 5px;\n  margin: 5px;\n}\n\n.RowHighlighted {\n  background: #ffa;\n}\n\nbutton {\n  height: 40px;\n  font-size: 20px;\n}\n```\n\n</Sandpack>\n\nおさらいすると、`List` は受け取った `<Row />` 要素をクローンし、それらに追加の props を付加したということです。\n\n<Pitfall>\n\n子要素をクローンすると、データがアプリケーションを通じてどのように流れるかを把握するのが難しくなります。[代替手段](#alternatives)のいずれかを試してみてください。\n\n</Pitfall>\n\n---\n\n## 代替手段 {/*alternatives*/}\n\n### レンダープロップを用いてデータを渡す {/*passing-data-with-a-render-prop*/}\n\n`cloneElement` を使用する代わりに、`renderItem` のような*レンダープロップ (render prop)* を受け取るようにすることを検討してみてください。以下の例では、`List` は `renderItem` を props として受け取ります。`List` は各アイテムに対して `renderItem` を呼び出し、`isHighlighted` を引数として渡します。\n\n```js {1,7}\nexport default function List({ items, renderItem }) {\n  const [selectedIndex, setSelectedIndex] = useState(0);\n  return (\n    <div className=\"List\">\n      {items.map((item, index) => {\n        const isHighlighted = index === selectedIndex;\n        return renderItem(item, isHighlighted);\n      })}\n```\n\n`renderItem` のようなものは「レンダープロップ」と呼ばれます。何かをレンダーする方法を指定するための props だからです。例えば、与えられた `isHighlighted` の値で `<Row>` をレンダーする `renderItem` の実装を渡すことができます。\n\n```js {3,7}\n<List\n  items={products}\n  renderItem={(product, isHighlighted) =>\n    <Row\n      key={product.id}\n      title={product.title}\n      isHighlighted={isHighlighted}\n    />\n  }\n/>\n```\n\n最終的な結果は `cloneElement` と同じです。\n\n```js {4,8,12}\n<List>\n  <Row\n    title=\"Cabbage\"\n    isHighlighted={true} \n  />\n  <Row\n    title=\"Garlic\"\n    isHighlighted={false} \n  />\n  <Row\n    title=\"Apple\"\n    isHighlighted={false} \n  />\n</List>\n```\n\nしかし、`isHighlighted` 値がどこから来ているかを明確に追跡することができます。\n\n<Sandpack>\n\n```js\nimport List from './List.js';\nimport Row from './Row.js';\nimport { products } from './data.js';\n\nexport default function App() {\n  return (\n    <List\n      items={products}\n      renderItem={(product, isHighlighted) =>\n        <Row\n          key={product.id}\n          title={product.title}\n          isHighlighted={isHighlighted}\n        />\n      }\n    />\n  );\n}\n```\n\n```js src/List.js active\nimport { useState } from 'react';\n\nexport default function List({ items, renderItem }) {\n  const [selectedIndex, setSelectedIndex] = useState(0);\n  return (\n    <div className=\"List\">\n      {items.map((item, index) => {\n        const isHighlighted = index === selectedIndex;\n        return renderItem(item, isHighlighted);\n      })}\n      <hr />\n      <button onClick={() => {\n        setSelectedIndex(i =>\n          (i + 1) % items.length\n        );\n      }}>\n        Next\n      </button>\n    </div>\n  );\n}\n```\n\n```js src/Row.js\nexport default function Row({ title, isHighlighted }) {\n  return (\n    <div className={[\n      'Row',\n      isHighlighted ? 'RowHighlighted' : ''\n    ].join(' ')}>\n      {title}\n    </div>\n  );\n}\n```\n\n```js src/data.js\nexport const products = [\n  { title: 'Cabbage', id: 1 },\n  { title: 'Garlic', id: 2 },\n  { title: 'Apple', id: 3 },\n];\n```\n\n```css\n.List {\n  display: flex;\n  flex-direction: column;\n  border: 2px solid grey;\n  padding: 5px;\n}\n\n.Row {\n  border: 2px dashed black;\n  padding: 5px;\n  margin: 5px;\n}\n\n.RowHighlighted {\n  background: #ffa;\n}\n\nbutton {\n  height: 40px;\n  font-size: 20px;\n}\n```\n\n</Sandpack>\n\nこのパターンはより明示的であるため、`cloneElement` よりも推奨されます。\n\n---\n\n### コンテクストでデータを渡す {/*passing-data-through-context*/}\n\n`cloneElement` の別の代替手段として[コンテクストを通じてデータを渡す](/learn/passing-data-deeply-with-context)ことが可能です。\n\n\n例として、`createContext` を呼び出して `HighlightContext` を定義しましょう。\n\n```js\nexport const HighlightContext = createContext(false);\n```\n\n`List` コンポーネントは、レンダーするすべてのアイテムを `HighlightContext` プロバイダでラップします。\n\n```js {8,10}\nexport default function List({ items, renderItem }) {\n  const [selectedIndex, setSelectedIndex] = useState(0);\n  return (\n    <div className=\"List\">\n      {items.map((item, index) => {\n        const isHighlighted = index === selectedIndex;\n        return (\n          <HighlightContext key={item.id} value={isHighlighted}>\n            {renderItem(item)}\n          </HighlightContext>\n        );\n      })}\n```\n\nこのアプローチでは、`Row` は props で `isHighlighted` を受け取る必要が一切ありません。代わりにコンテクストから読み取ります。\n\n```js src/Row.js {2}\nexport default function Row({ title }) {\n  const isHighlighted = useContext(HighlightContext);\n  // ...\n```\n\nこれにより、呼び出し元のコンポーネントは `<Row>` に `isHighlighted` を渡すことについて知る必要も、気にする必要もなくなります。\n\n```js {4}\n<List\n  items={products}\n  renderItem={product =>\n    <Row title={product.title} />\n  }\n/>\n```\n\n代わりに、`List` と `Row` はコンテクストを通じ、ハイライトのロジックに関して協調して動作します。\n\n<Sandpack>\n\n```js\nimport List from './List.js';\nimport Row from './Row.js';\nimport { products } from './data.js';\n\nexport default function App() {\n  return (\n    <List\n      items={products}\n      renderItem={(product) =>\n        <Row title={product.title} />\n      }\n    />\n  );\n}\n```\n\n```js src/List.js active\nimport { useState } from 'react';\nimport { HighlightContext } from './HighlightContext.js';\n\nexport default function List({ items, renderItem }) {\n  const [selectedIndex, setSelectedIndex] = useState(0);\n  return (\n    <div className=\"List\">\n      {items.map((item, index) => {\n        const isHighlighted = index === selectedIndex;\n        return (\n          <HighlightContext\n            key={item.id}\n            value={isHighlighted}\n          >\n            {renderItem(item)}\n          </HighlightContext>\n        );\n      })}\n      <hr />\n      <button onClick={() => {\n        setSelectedIndex(i =>\n          (i + 1) % items.length\n        );\n      }}>\n        Next\n      </button>\n    </div>\n  );\n}\n```\n\n```js src/Row.js\nimport { useContext } from 'react';\nimport { HighlightContext } from './HighlightContext.js';\n\nexport default function Row({ title }) {\n  const isHighlighted = useContext(HighlightContext);\n  return (\n    <div className={[\n      'Row',\n      isHighlighted ? 'RowHighlighted' : ''\n    ].join(' ')}>\n      {title}\n    </div>\n  );\n}\n```\n\n```js src/HighlightContext.js\nimport { createContext } from 'react';\n\nexport const HighlightContext = createContext(false);\n```\n\n```js src/data.js\nexport const products = [\n  { title: 'Cabbage', id: 1 },\n  { title: 'Garlic', id: 2 },\n  { title: 'Apple', id: 3 },\n];\n```\n\n```css\n.List {\n  display: flex;\n  flex-direction: column;\n  border: 2px solid grey;\n  padding: 5px;\n}\n\n.Row {\n  border: 2px dashed black;\n  padding: 5px;\n  margin: 5px;\n}\n\n.RowHighlighted {\n  background: #ffa;\n}\n\nbutton {\n  height: 40px;\n  font-size: 20px;\n}\n```\n\n</Sandpack>\n\n[コンテクストを通じてデータを深く渡す方法について詳しく学ぶ](/reference/react/useContext#passing-data-deeply-into-the-tree)\n\n---\n\n### ロジックをカスタムフックに抽出する {/*extracting-logic-into-a-custom-hook*/}\n\n試すべき別のアプローチは、「非視覚的」なロジックを自前のフックに抽出し、フックから返される情報を使用して何をレンダーするかを決定することです。例えば次のような `useList` カスタムフックを書くことができます。\n\n```js\nimport { useState } from 'react';\n\nexport default function useList(items) {\n  const [selectedIndex, setSelectedIndex] = useState(0);\n\n  function onNext() {\n    setSelectedIndex(i =>\n      (i + 1) % items.length\n    );\n  }\n\n  const selected = items[selectedIndex];\n  return [selected, onNext];\n}\n```\n\nこれを以下のように使用できます。\n\n```js {2,9,13}\nexport default function App() {\n  const [selected, onNext] = useList(products);\n  return (\n    <div className=\"List\">\n      {products.map(product =>\n        <Row\n          key={product.id}\n          title={product.title}\n          isHighlighted={selected === product}\n        />\n      )}\n      <hr />\n      <button onClick={onNext}>\n        Next\n      </button>\n    </div>\n  );\n}\n```\n\nデータフローは明示的ですが、state は任意のコンポーネントから使用できる `useList` カスタムフック内にあります。\n\n<Sandpack>\n\n```js\nimport Row from './Row.js';\nimport useList from './useList.js';\nimport { products } from './data.js';\n\nexport default function App() {\n  const [selected, onNext] = useList(products);\n  return (\n    <div className=\"List\">\n      {products.map(product =>\n        <Row\n          key={product.id}\n          title={product.title}\n          isHighlighted={selected === product}\n        />\n      )}\n      <hr />\n      <button onClick={onNext}>\n        Next\n      </button>\n    </div>\n  );\n}\n```\n\n```js src/useList.js\nimport { useState } from 'react';\n\nexport default function useList(items) {\n  const [selectedIndex, setSelectedIndex] = useState(0);\n\n  function onNext() {\n    setSelectedIndex(i =>\n      (i + 1) % items.length\n    );\n  }\n\n  const selected = items[selectedIndex];\n  return [selected, onNext];\n}\n```\n\n```js src/Row.js\nexport default function Row({ title, isHighlighted }) {\n  return (\n    <div className={[\n      'Row',\n      isHighlighted ? 'RowHighlighted' : ''\n    ].join(' ')}>\n      {title}\n    </div>\n  );\n}\n```\n\n```js src/data.js\nexport const products = [\n  { title: 'Cabbage', id: 1 },\n  { title: 'Garlic', id: 2 },\n  { title: 'Apple', id: 3 },\n];\n```\n\n```css\n.List {\n  display: flex;\n  flex-direction: column;\n  border: 2px solid grey;\n  padding: 5px;\n}\n\n.Row {\n  border: 2px dashed black;\n  padding: 5px;\n  margin: 5px;\n}\n\n.RowHighlighted {\n  background: #ffa;\n}\n\nbutton {\n  height: 40px;\n  font-size: 20px;\n}\n```\n\n</Sandpack>\n\nこのアプローチは、特にこのロジックを異なるコンポーネント間で再利用したい場合に有用です。\n"
  },
  {
    "path": "src/content/reference/react/components.md",
    "content": "---\ntitle: \"組み込みの React コンポーネント\"\n---\n\n<Intro>\n\nReact は、JSX で使用できるいくつかの組み込みコンポーネントを公開しています。\n\n</Intro>\n\n---\n\n## 組み込みコンポーネント {/*built-in-components*/}\n\n* [`<Fragment>`](/reference/react/Fragment) を使い、複数の JSX ノードをまとめることができます。別の書き方として `<>...</>` があります。\n* [`<Profiler>`](/reference/react/Profiler) を使い、React ツリーのレンダーパフォーマンスをプログラム上で測定することができます。\n* [`<Suspense>`](/reference/react/Suspense) を使い、子コンポーネントがロードされる間、フォールバックを表示することができます。\n* [`<StrictMode>`](/reference/react/StrictMode) を使い、バグを早期に見つけるための追加の開発環境専用チェックを有効化します。\n* [`<Activity>`](/reference/react/Activity) を使い、内部状態を復元しつつ UI の表示状態を切り替えます。\n\n---\n\n## 自分自身のコンポーネント {/*your-own-components*/}\n\nJavaScript 関数として[自分自身のコンポーネントを定義](/learn/your-first-component)することもできます。\n"
  },
  {
    "path": "src/content/reference/react/createContext.md",
    "content": "---\ntitle: createContext\n---\n\n<Intro>\n\n`createContext` は、コンポーネントが提供または読み取りできる[コンテクスト](/learn/passing-data-deeply-with-context)を作成するための関数です。\n\n```js\nconst SomeContext = createContext(defaultValue)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `createContext(defaultValue)` {/*createcontext*/}\n\n`createContext` をコンポーネントの外部で呼び出してコンテクストを作成します。\n\n```js\nimport { createContext } from 'react';\n\nconst ThemeContext = createContext('light');\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `defaultValue`: コンポーネントがコンテクストを読み取るときに、その上のツリー内で対応するコンテクストプロバイダがない場合にコンテクストが持つ値です。デフォルト値が必要ない場合は `null` を指定します。デフォルト値は「最後の手段」として使われるように意図されています。これは静的な値であり、時間が経過しても変化しません。\n\n#### 返り値 {/*returns*/}\n\n`createContext` はコンテクストオブジェクトを返します。\n\n**コンテクストオブジェクト自体は情報を持っていません**。他のコンポーネントが*どの*コンテクストを読み取るか、または提供するかを表します。通常、上位のコンポーネントで [`SomeContext`](#provider) を使用してコンテクストの値を指定し、下位のコンポーネントで [`useContext(SomeContext)`](/reference/react/useContext) を呼び出してコンテクストを読み取ります。コンテクストオブジェクトにはいくつかのプロパティがあります：\n\n* `SomeContext` で、コンポーネントにコンテクストの値を提供できます。\n* `SomeContext.Consumer` は、コンテクストの値を読み取るための方法ですが、あまり使用されません。\n* `SomeContext.Provider` は、React 19 より前のバージョンでコンテクストの値を提供するためのレガシーな記法です。\n\n---\n\n### `SomeContext` プロバイダ {/*provider*/}\n\nコンポーネントをコンテクストプロバイダでラップすると、内部のコンポーネントに対してこのコンテクストの値を指定できます。\n\n```js\nfunction App() {\n  const [theme, setTheme] = useState('light');\n  // ...\n  return (\n    <ThemeContext value={theme}>\n      <Page />\n    </ThemeContext>\n  );\n}\n```\n\n<Note>\n\nReact 19 以降では、`<SomeContext>` 自体をプロバイダとしてレンダーできます。\n\n以前のバージョンでは、代わりに `<SomeContext.Provider>` を使用してください。\n\n</Note>\n\n#### props {/*provider-props*/}\n\n* `value`: このプロバイダの内側（深さに関わらず）にあるコンポーネントがコンテクストを読み取る際に、渡したい値です。コンテクストの値は任意の型にすることができます。プロバイダ内で [`useContext(SomeContext)`](/reference/react/useContext) を呼び出しているコンポーネントは、それより上位かつ最も内側にある対応するコンテクストプロバイダの `value` を受け取ります。\n\n---\n\n### `SomeContext.Consumer` {/*consumer*/}\n\n`useContext` が存在する前には、コンテクストを読み取る古い方法がありました：\n\n```js\nfunction Button() {\n  // 🟡 Legacy way (not recommended)\n  return (\n    <ThemeContext.Consumer>\n      {theme => (\n        <button className={theme} />\n      )}\n    </ThemeContext.Consumer>\n  );\n}\n```\n\nこの古い方法はまだ動作しますが、**新しく書かれたコードは [`useContext()`](/reference/react/useContext) を使ってコンテクストを読み取るべきです：**\n\n```js\nfunction Button() {\n  // ✅ Recommended way\n  const theme = useContext(ThemeContext);\n  return <button className={theme} />;\n}\n```\n\n#### props {/*consumer-props*/}\n\n* `children`: 関数です。React は、[`useContext()`](/reference/react/useContext) と同じアルゴリズムによって定まる現在のコンテクスト値で関数を呼び出し、その関数から返される結果をレンダーします。親コンポーネントからのコンテクストが変更されると、React はこの関数を再実行し、UI を更新します。\n\n---\n\n## 使用法 {/*usage*/}\n\n### コンテクストの作成 {/*creating-context*/}\n\nコンテクストを利用することで、明示的に props を渡さずに、コンポーネントに[深くまで情報を渡す](/learn/passing-data-deeply-with-context)ことができます。\n\nコンポーネントの外部で `createContext` を呼び出して、コンテクストを 1 つまたは複数個作成します。\n\n```js [[1, 3, \"ThemeContext\"], [1, 4, \"AuthContext\"], [3, 3, \"'light'\"], [3, 4, \"null\"]]\nimport { createContext } from 'react';\n\nconst ThemeContext = createContext('light');\nconst AuthContext = createContext(null);\n```\n\n`createContext` は<CodeStep step={1}>コンテクストオブジェクト</CodeStep>を返します。それを [`useContext()`](/reference/react/useContext) に渡すことで、コンポーネントからコンテクストを読み取ることができます：\n\n```js [[1, 2, \"ThemeContext\"], [1, 7, \"AuthContext\"]]\nfunction Button() {\n  const theme = useContext(ThemeContext);\n  // ...\n}\n\nfunction Profile() {\n  const currentUser = useContext(AuthContext);\n  // ...\n}\n```\n\nデフォルトでは、コンポーネントが受け取る値は、コンテクストを作成するときに指定した<CodeStep step={3}>デフォルトの値</CodeStep>になります。しかし、デフォルトの値は決して変わらないため、これ自体では役に立ちませんね。\n\nコンテクストが便利なのは、**コンポーネントから動的な値を提供できる**からです：\n\n```js {8-9,11-12}\nfunction App() {\n  const [theme, setTheme] = useState('dark');\n  const [currentUser, setCurrentUser] = useState({ name: 'Taylor' });\n\n  // ...\n\n  return (\n    <ThemeContext value={theme}>\n      <AuthContext value={currentUser}>\n        <Page />\n      </AuthContext>\n    </ThemeContext>\n  );\n}\n```\n\nこれで、`Page` コンポーネントとその内部のすべてのコンポーネントは、どんなに深くても、渡されたコンテクストの値を「見る」ことができます。渡されたコンテクストの値が変更されると、React はコンテクストを読み取るコンポーネントを再レンダーします。\n\n[コンテクストの読み取りと提供、例についてさらに読む](/reference/react/useContext)\n\n---\n\n### ファイルからのコンテクストのインポートとエクスポート {/*importing-and-exporting-context-from-a-file*/}\n\n異なるファイルにあるコンポーネントが同じコンテクストにアクセスする必要があることがよくあります。そのため、一般的には別ファイルでコンテクストを宣言します。他のファイルでコンテクストを利用できるようにするために、[`export` 文](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export)を使用できます：\n\n```js {4-5}\n// Contexts.js\nimport { createContext } from 'react';\n\nexport const ThemeContext = createContext('light');\nexport const AuthContext = createContext(null);\n```\n\n他のファイルで宣言されたコンポーネントは、[`import`](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/import) 文を使用して、このコンテクストを読み取ったり、提供したりすることができます：\n\n```js {2}\n// Button.js\nimport { ThemeContext } from './Contexts.js';\n\nfunction Button() {\n  const theme = useContext(ThemeContext);\n  // ...\n}\n```\n\n```js {2}\n// App.js\nimport { ThemeContext, AuthContext } from './Contexts.js';\n\nfunction App() {\n  // ...\n  return (\n    <ThemeContext value={theme}>\n      <AuthContext value={currentUser}>\n        <Page />\n      </AuthContext>\n    </ThemeContext>\n  );\n}\n```\n\nこれは[コンポーネントのインポートとエクスポート](/learn/importing-and-exporting-components)と同様に動作します。\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### コンテクストの値を変更する方法が見つからない {/*i-cant-find-a-way-to-change-the-context-value*/}\n\n\nこのようなコードは*デフォルト*のコンテクストの値を指定します：\n\n```js\nconst ThemeContext = createContext('light');\n```\n\nこの値は決して変わりません。React は、対応するプロバイダを上位のコンポーネントで見つけられない場合にのみ、この値をフォールバックとして使用します。\n\nコンテクストを時間の経過とともに変化させるには、[state を追加し、コンポーネントをコンテクストプロバイダでラップ](/reference/react/useContext#updating-data-passed-via-context)します。\n"
  },
  {
    "path": "src/content/reference/react/createElement.md",
    "content": "---\ntitle: createElement\n---\n\n<Intro>\n\n`createElement` によって React 要素を作成できます。これは [JSX](/learn/writing-markup-with-jsx) を書く代わりの手段として利用できます。\n\n```js\nconst element = createElement(type, props, ...children)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `createElement(type, props, ...children)` {/*createelement*/}\n\n`createElement` を呼び出して、指定した `type`、`props`、`children` を持った React 要素を作成します。\n\n```js\nimport { createElement } from 'react';\n\nfunction Greeting({ name }) {\n  return createElement(\n    'h1',\n    { className: 'greeting' },\n    'Hello'\n  );\n}\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `type`: `type` 引数は有効な React のコンポーネント型でなければなりません。例えば、タグ名の文字列（`'div'` や `'span'`）や、React コンポーネント（関数、クラス、または [`Fragment`](/reference/react/Fragment) のような特別なコンポーネント）が該当します。\n\n* `props`: `props` 引数はオブジェクトか `null` でなければなりません。`null` を渡すと、空のオブジェクトと同じように扱われます。React は、渡された `props` と同じ props を持った要素を作成します。`props` オブジェクトの `ref` と `key` は特別であり、返された `element` の `element.props.ref` や `element.props.key` として*利用できません*。`element.ref` ないし `element.key` となります。\n\n* **省略可能** `...children`: ゼロ個以上の子ノード。これらは React ノード、つまり、React 要素、文字列、数値、[ポータル](/reference/react-dom/createPortal)、空ノード（`null`、`undefined`、`true`、`false`）、あるいは React ノードの配列となります。\n\n#### 返り値 {/*returns*/}\n\n`createElement` は以下のプロパティを持つ React 要素オブジェクトを返します。\n\n* `type`: 指定した `type`。\n* `props`: 指定した `props`、ただし `ref` と `key` は除く。\n* `ref`: 指定した `ref`。未指定の場合は `null`。\n* `key`: 指定した `key`。強制的に文字列に変換されます。未指定の場合は `null`。\n\n通常、この要素をコンポーネントから返すか、他の要素の子として用います。要素のプロパティを読み取ることは可能ですが、作成後は要素の構造を非公開 (opaque) として扱い、レンダーのみ行うようにするべきです。\n\n#### 注意点 {/*caveats*/}\n\n* **React 要素とその props は[イミュータブル (immutable)](https://en.wikipedia.org/wiki/Immutable_object) として扱い**、作成後にその内容を変更してはなりません。これを強制するために、React は開発環境において、返された要素とその `props` プロパティを浅く[凍結 (freeze)](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) します。\n\n* JSX を使用する場合、**独自のカスタムコンポーネントをレンダーするためにはタグを大文字で始める必要があります**。つまり、`<Something />` は `createElement(Something)` と同等ですが、`<something />`（小文字）は `createElement('something')` と同等です（文字列なので、組み込みの HTML タグとして扱われます）。\n\n* **複数の子の内容がすべて静的に分かっている場合**、`createElement` には子を `createElement('h1', {}, child1, child2, child3)` のように**複数の引数として渡してください**。子が動的な場合は、配列全体を第 3 引数として `createElement('ul', {}, listItems)` のように渡してください。これにより、React は動的なリストに `key` が欠けている場合に[警告を出す](/learn/rendering-lists#keeping-list-items-in-order-with-key)ようになります。静的なリストでは並び替えは決して発生しないため、key は必要ありません。\n\n---\n\n## 使用法 {/*usage*/}\n\n### JSX を使わずに要素を作成する {/*creating-an-element-without-jsx*/}\n\n[JSX](/learn/writing-markup-with-jsx) が好きでない場合や、プロジェクトで使用できない場合には、代わりに `createElement` を使用できます。\n\nJSX を使わずに要素を作成するには、`createElement` を呼び出して、何らかの <CodeStep step={1}>type</CodeStep>、<CodeStep step={2}>props</CodeStep>、<CodeStep step={3}>children</CodeStep> を引数として渡します。\n\n```js [[1, 5, \"'h1'\"], [2, 6, \"{ className: 'greeting' }\"], [3, 7, \"'Hello ',\"], [3, 8, \"createElement('i', null, name),\"], [3, 9, \"'. Welcome!'\"]]\nimport { createElement } from 'react';\n\nfunction Greeting({ name }) {\n  return createElement(\n    'h1',\n    { className: 'greeting' },\n    'Hello ',\n    createElement('i', null, name),\n    '. Welcome!'\n  );\n}\n```\n\n<CodeStep step={3}>children</CodeStep> はオプションで、必要なだけ渡すことができます（上記の例では子が 3 つあります）。このコードは、`<h1>` ヘッダに挨拶文字列を入れて表示します。比較のため、以下に JSX を使って書き直した同じ例を示します。\n\n```js [[1, 3, \"h1\"], [2, 3, \"className=\\\\\"greeting\\\\\"\"], [3, 4, \"Hello <i>{name}</i>. Welcome!\"], [1, 5, \"h1\"]]\nfunction Greeting({ name }) {\n  return (\n    <h1 className=\"greeting\">\n      Hello <i>{name}</i>. Welcome!\n    </h1>\n  );\n}\n```\n\n自身のカスタム React コンポーネントをレンダーするには、`'h1'` のような文字列ではなく `Greeting` のような関数を <CodeStep step={1}>type</CodeStep> として渡します。\n\n```js [[1, 2, \"Greeting\"], [2, 2, \"{ name: 'Taylor' }\"]]\nexport default function App() {\n  return createElement(Greeting, { name: 'Taylor' });\n}\n```\n\nJSX を使用した場合は以下のようになります。\n\n```js [[1, 2, \"Greeting\"], [2, 2, \"name=\\\\\"Taylor\\\\\"\"]]\nexport default function App() {\n  return <Greeting name=\"Taylor\" />;\n}\n```\n\n以下は、`createElement` を使用して書かれたフルのサンプルです。\n\n<Sandpack>\n\n```js\nimport { createElement } from 'react';\n\nfunction Greeting({ name }) {\n  return createElement(\n    'h1',\n    { className: 'greeting' },\n    'Hello ',\n    createElement('i', null, name),\n    '. Welcome!'\n  );\n}\n\nexport default function App() {\n  return createElement(\n    Greeting,\n    { name: 'Taylor' }\n  );\n}\n```\n\n```css\n.greeting {\n  color: darkgreen;\n  font-family: Georgia;\n}\n```\n\n</Sandpack>\n\n同じものを JSX で書くと以下のようになります。\n\n<Sandpack>\n\n```js\nfunction Greeting({ name }) {\n  return (\n    <h1 className=\"greeting\">\n      Hello <i>{name}</i>. Welcome!\n    </h1>\n  );\n}\n\nexport default function App() {\n  return <Greeting name=\"Taylor\" />;\n}\n```\n\n```css\n.greeting {\n  color: darkgreen;\n  font-family: Georgia;\n}\n```\n\n</Sandpack>\n\nどちらのコーディングスタイルも問題ありませんので、プロジェクトに合わせて好きな方を使用してください。`createElement` と比較して JSX を使用する場合の主な利点は、どの閉じタグがどの開きタグに対応しているかが簡単にわかることです。\n\n<DeepDive>\n\n#### React 要素とは要するに何なのか？ {/*what-is-a-react-element-exactly*/}\n\n要素 (element) とは、ユーザインターフェースの軽量な説明書きのことです。例えば、`<Greeting name=\"Taylor\" />` と `createElement(Greeting, { name: 'Taylor' })` はいずれも、次のようなオブジェクトを生成します。\n\n```js\n// Slightly simplified\n{\n  type: Greeting,\n  props: {\n    name: 'Taylor'\n  },\n  key: null,\n  ref: null,\n}\n```\n\n**このオブジェクトを作成しただけでは、`Greeting` コンポーネントがレンダーされたり、DOM 要素が作成されたりするわけではないことに注意してください**。\n\nReact 要素とは、むしろ指示書のようなものです。React に後で `Greeting` コンポーネントをレンダーするよう指示するものです。このオブジェクトを `App` コンポーネントから返すことで、React に次に何をすべきかを伝えるのです。\n\n要素の作成は非常に安価であるため、最適化したり避けたりする必要はありません。\n\n</DeepDive>\n"
  },
  {
    "path": "src/content/reference/react/createRef.md",
    "content": "---\ntitle: createRef\n---\n\n<Pitfall>\n\n`createRef` は主に[クラスコンポーネント](/reference/react/Component)で使用されます。関数コンポーネントでは通常代わりに [`useRef`](/reference/react/useRef) を用います。\n\n</Pitfall>\n\n<Intro>\n\n`createRef` は任意の値を保持できる [ref](/learn/referencing-values-with-refs) オブジェクトを作成します。\n\n```js\nclass MyInput extends Component {\n  inputRef = createRef();\n  // ...\n}\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `createRef()` {/*createref*/}\n\n`createRef` を呼び出して、[クラスコンポーネント](/reference/react/Component)内で [ref](/learn/referencing-values-with-refs) を宣言します。\n\n```js\nimport { createRef, Component } from 'react';\n\nclass MyComponent extends Component {\n  intervalRef = createRef();\n  inputRef = createRef();\n  // ...\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n`createRef` は引数を取りません。\n\n#### 返り値 {/*returns*/}\n\n`createRef` は単一のプロパティを持つオブジェクトを返します。\n\n* `current`: `null` に初期化されています。後で他の値にセットすることができます。JSX ノードの `ref` 属性として React に ref オブジェクトを渡すと、React はその `current` プロパティを適切にセットします。\n\n#### 注意点 {/*caveats*/}\n\n* `createRef` は常に*異なる*オブジェクトを返します。これは自分で `{ current: null }` を書くのと同等です。\n* 関数コンポーネントでは、同じオブジェクトを常に返す [`useRef`](/reference/react/useRef) を代わりに使用することをお勧めします。\n* `const ref = useRef()` は `const [ref, _] = useState(() => createRef(null))` と同等です。\n\n---\n\n## 使用法 {/*usage*/}\n\n### クラスコンポーネントで ref を宣言する {/*declaring-a-ref-in-a-class-component*/}\n\n[クラスコンポーネント](/reference/react/Component)内で ref を宣言するには、`createRef` を呼び出し、その結果をクラスフィールドに割り当てます。\n\n```js {4}\nimport { Component, createRef } from 'react';\n\nclass Form extends Component {\n  inputRef = createRef();\n\n  // ...\n}\n```\n\nこれで JSX の `<input>` に `ref={this.inputRef}` を渡すと、React は `this.inputRef.current` を input の DOM ノードにセットします。例えば以下のようにして、input をフォーカスするボタンを作ることができます。\n\n<Sandpack>\n\n```js\nimport { Component, createRef } from 'react';\n\nexport default class Form extends Component {\n  inputRef = createRef();\n\n  handleClick = () => {\n    this.inputRef.current.focus();\n  }\n\n  render() {\n    return (\n      <>\n        <input ref={this.inputRef} />\n        <button onClick={this.handleClick}>\n          Focus the input\n        </button>\n      </>\n    );\n  }\n}\n```\n\n</Sandpack>\n\n<Pitfall>\n\n`createRef` は主に[クラスコンポーネント](/reference/react/Component)で使用されます。関数コンポーネントでは通常代わりに [`useRef`](/reference/react/useRef) を用います。\n\n</Pitfall>\n\n---\n\n## 代替手段 {/*alternatives*/}\n\n### `createRef` を使ったクラスから `useRef` を使った関数への移行 {/*migrating-from-a-class-with-createref-to-a-function-with-useref*/}\n\n新しいコードでは[クラスコンポーネント](/reference/react/Component)の代わりに関数コンポーネントの使用を推奨します。以下に、`createRef` を使用している既存のクラスコンポーネントがある場合の移行方法を示します。こちらが元のコードです。\n\n<Sandpack>\n\n```js\nimport { Component, createRef } from 'react';\n\nexport default class Form extends Component {\n  inputRef = createRef();\n\n  handleClick = () => {\n    this.inputRef.current.focus();\n  }\n\n  render() {\n    return (\n      <>\n        <input ref={this.inputRef} />\n        <button onClick={this.handleClick}>\n          Focus the input\n        </button>\n      </>\n    );\n  }\n}\n```\n\n</Sandpack>\n\nこのコンポーネントを[クラスから関数に移行する](/reference/react/Component#alternatives)場合、`createRef` の呼び出しを [`useRef`](/reference/react/useRef) の呼び出しに置き換えます。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\n\nexport default function Form() {\n  const inputRef = useRef(null);\n\n  function handleClick() {\n    inputRef.current.focus();\n  }\n\n  return (\n    <>\n      <input ref={inputRef} />\n      <button onClick={handleClick}>\n        Focus the input\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n"
  },
  {
    "path": "src/content/reference/react/experimental_taintObjectReference.md",
    "content": "---\ntitle: experimental_taintObjectReference\nversion: experimental\n---\n\n<Experimental>\n\n**This API is experimental and is not available in a stable version of React yet.**\n\nYou can try it by upgrading React packages to the most recent experimental version:\n\n- `react@experimental`\n- `react-dom@experimental`\n- `eslint-plugin-react-hooks@experimental`\n\nExperimental versions of React may contain bugs. Don't use them in production.\n\nThis API is only available inside React Server Components.\n\n</Experimental>\n\n\n<Intro>\n\n`taintObjectReference` lets you prevent a specific object instance from being passed to a Client Component like a `user` object.\n\n```js\nexperimental_taintObjectReference(message, object);\n```\n\nTo prevent passing a key, hash or token, see [`taintUniqueValue`](/reference/react/experimental_taintUniqueValue).\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## Reference {/*reference*/}\n\n### `taintObjectReference(message, object)` {/*taintobjectreference*/}\n\nCall `taintObjectReference` with an object to register it with React as something that should not be allowed to be passed to the Client as is:\n\n```js\nimport {experimental_taintObjectReference} from 'react';\n\nexperimental_taintObjectReference(\n  'Do not pass ALL environment variables to the client.',\n  process.env\n);\n```\n\n[See more examples below.](#usage)\n\n#### Parameters {/*parameters*/}\n\n* `message`: The message you want to display if the object gets passed to a Client Component. This message will be displayed as a part of the Error that will be thrown if the object gets passed to a Client Component.\n\n* `object`: The object to be tainted. Functions and class instances can be passed to `taintObjectReference` as `object`. Functions and classes are already blocked from being passed to Client Components but the React's default error message will be replaced by what you defined in `message`. When a specific instance of a Typed Array is passed to `taintObjectReference` as `object`, any other copies of the Typed Array will not be tainted.\n\n#### Returns {/*returns*/}\n\n`experimental_taintObjectReference` returns `undefined`.\n\n#### Caveats {/*caveats*/}\n\n- Recreating or cloning a tainted object creates a new untainted object which may contain sensitive data. For example, if you have a tainted `user` object, `const userInfo = {name: user.name, ssn: user.ssn}` or `{...user}` will create new objects which are not tainted. `taintObjectReference` only protects against simple mistakes when the object is passed through to a Client Component unchanged.\n\n<Pitfall>\n\n**Do not rely on just tainting for security.** Tainting an object doesn't prevent leaking of every possible derived value. For example, the clone of a tainted object will create a new untainted object. Using data from a tainted object (e.g. `{secret: taintedObj.secret}`) will create a new value or object that is not tainted. Tainting is a layer of protection; a secure app will have multiple layers of protection, well designed APIs, and isolation patterns.\n\n</Pitfall>\n\n---\n\n## Usage {/*usage*/}\n\n### Prevent user data from unintentionally reaching the client {/*prevent-user-data-from-unintentionally-reaching-the-client*/}\n\nA Client Component should never accept objects that carry sensitive data. Ideally, the data fetching functions should not expose data that the current user should not have access to. Sometimes mistakes happen during refactoring. To protect against these mistakes happening down the line we can \"taint\" the user object in our data API.\n\n```js\nimport {experimental_taintObjectReference} from 'react';\n\nexport async function getUser(id) {\n  const user = await db`SELECT * FROM users WHERE id = ${id}`;\n  experimental_taintObjectReference(\n    'Do not pass the entire user object to the client. ' +\n      'Instead, pick off the specific properties you need for this use case.',\n    user,\n  );\n  return user;\n}\n```\n\nNow whenever anyone tries to pass this object to a Client Component, an error will be thrown with the passed in error message instead.\n\n<DeepDive>\n\n#### Protecting against leaks in data fetching {/*protecting-against-leaks-in-data-fetching*/}\n\nIf you're running a Server Components environment that has access to sensitive data, you have to be careful not to pass objects straight through:\n\n```js\n// api.js\nexport async function getUser(id) {\n  const user = await db`SELECT * FROM users WHERE id = ${id}`;\n  return user;\n}\n```\n\n```js\nimport { getUser } from 'api.js';\nimport { InfoCard } from 'components.js';\n\nexport async function Profile(props) {\n  const user = await getUser(props.userId);\n  // DO NOT DO THIS\n  return <InfoCard user={user} />;\n}\n```\n\n```js\n// components.js\n\"use client\";\n\nexport async function InfoCard({ user }) {\n  return <div>{user.name}</div>;\n}\n```\n\nIdeally, the `getUser` should not expose data that the current user should not have access to. To prevent passing the `user` object to a Client Component down the line we can \"taint\" the user object:\n\n\n```js\n// api.js\nimport {experimental_taintObjectReference} from 'react';\n\nexport async function getUser(id) {\n  const user = await db`SELECT * FROM users WHERE id = ${id}`;\n  experimental_taintObjectReference(\n    'Do not pass the entire user object to the client. ' +\n      'Instead, pick off the specific properties you need for this use case.',\n    user,\n  );\n  return user;\n}\n```\n\nNow if anyone tries to pass the `user` object to a Client Component, an error will be thrown with the passed in error message.\n\n</DeepDive>\n"
  },
  {
    "path": "src/content/reference/react/experimental_taintUniqueValue.md",
    "content": "---\ntitle: experimental_taintUniqueValue\nversion: experimental\n---\n\n<Experimental>\n\n**This API is experimental and is not available in a stable version of React yet.**\n\nYou can try it by upgrading React packages to the most recent experimental version:\n\n- `react@experimental`\n- `react-dom@experimental`\n- `eslint-plugin-react-hooks@experimental`\n\nExperimental versions of React may contain bugs. Don't use them in production.\n\nThis API is only available inside [React Server Components](/reference/rsc/use-client).\n\n</Experimental>\n\n\n<Intro>\n\n`taintUniqueValue` lets you prevent unique values from being passed to Client Components like passwords, keys, or tokens.\n\n```js\ntaintUniqueValue(errMessage, lifetime, value)\n```\n\nTo prevent passing an object containing sensitive data, see [`taintObjectReference`](/reference/react/experimental_taintObjectReference).\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## Reference {/*reference*/}\n\n### `taintUniqueValue(message, lifetime, value)` {/*taintuniquevalue*/}\n\nCall `taintUniqueValue` with a password, token, key or hash to register it with React as something that should not be allowed to be passed to the Client as is:\n\n```js\nimport {experimental_taintUniqueValue} from 'react';\n\nexperimental_taintUniqueValue(\n  'Do not pass secret keys to the client.',\n  process,\n  process.env.SECRET_KEY\n);\n```\n\n[See more examples below.](#usage)\n\n#### Parameters {/*parameters*/}\n\n* `message`: The message you want to display if `value` is passed to a Client Component. This message will be displayed as a part of the Error that will be thrown if `value` is passed to a Client Component.\n\n* `lifetime`: Any object that indicates how long `value` should be tainted. `value` will be blocked from being sent to any Client Component while this object still exists. For example, passing `globalThis` blocks the value for the lifetime of an app. `lifetime` is typically an object whose properties contains `value`.\n\n* `value`: A string, bigint or TypedArray. `value` must be a unique sequence of characters or bytes with high entropy such as a cryptographic token, private key, hash, or a long password. `value` will be blocked from being sent to any Client Component.\n\n#### Returns {/*returns*/}\n\n`experimental_taintUniqueValue` returns `undefined`.\n\n#### Caveats {/*caveats*/}\n\n* Deriving new values from tainted values can compromise tainting protection. New values created by uppercasing tainted values, concatenating tainted string values into a larger string, converting tainted values to base64, substringing tainted values, and other similar transformations are not tainted unless you explicitly call `taintUniqueValue` on these newly created values.\n* Do not use `taintUniqueValue` to protect low-entropy values such as PIN codes or phone numbers. If any value in a request is controlled by an attacker, they could infer which value is tainted by enumerating all possible values of the secret.\n\n---\n\n## Usage {/*usage*/}\n\n### Prevent a token from being passed to Client Components {/*prevent-a-token-from-being-passed-to-client-components*/}\n\nTo ensure that sensitive information such as passwords, session tokens, or other unique values do not inadvertently get passed to Client Components, the `taintUniqueValue` function provides a layer of protection. When a value is tainted, any attempt to pass it to a Client Component will result in an error. \n\nThe `lifetime` argument defines the duration for which the value remains tainted. For values that should remain tainted indefinitely, objects like [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis) or `process` can serve as the `lifetime` argument. These objects have a lifespan that spans the entire duration of your app's execution.\n\n```js\nimport {experimental_taintUniqueValue} from 'react';\n\nexperimental_taintUniqueValue(\n  'Do not pass a user password to the client.',\n  globalThis,\n  process.env.SECRET_KEY\n);\n```\n\nIf the tainted value's lifespan is tied to a object, the `lifetime` should be the object that encapsulates the value. This ensures the tainted value remains protected for the lifetime of the encapsulating object.\n\n```js\nimport {experimental_taintUniqueValue} from 'react';\n\nexport async function getUser(id) {\n  const user = await db`SELECT * FROM users WHERE id = ${id}`;\n  experimental_taintUniqueValue(\n    'Do not pass a user session token to the client.',\n    user,\n    user.session.token\n  );\n  return user;\n}\n```\n\nIn this example, the `user` object serves as the `lifetime` argument. If this object gets stored in a global cache or is accessible by another request, the session token remains tainted.\n\n<Pitfall>\n\n**Do not rely solely on tainting for security.** Tainting a value doesn't block every possible derived value. For example, creating a new value by upper casing a tainted string will not taint the new value.\n\n\n```js\nimport {experimental_taintUniqueValue} from 'react';\n\nconst password = 'correct horse battery staple';\n\nexperimental_taintUniqueValue(\n  'Do not pass the password to the client.',\n  globalThis,\n  password\n);\n\nconst uppercasePassword = password.toUpperCase() // `uppercasePassword` is not tainted\n```\n\nIn this example, the constant `password` is tainted. Then `password` is used to create a new value `uppercasePassword` by calling the `toUpperCase` method on `password`. The newly created `uppercasePassword` is not tainted.\n\nOther similar ways of deriving new values from tainted values like concatenating it into a larger string, converting it to base64, or returning a substring create untained values.\n\nTainting only protects against simple mistakes like explicitly passing secret values to the client. Mistakes in calling the `taintUniqueValue` like using a global store outside of React, without the corresponding lifetime object, can cause the tainted value to become untainted. Tainting is a layer of protection; a secure app will have multiple layers of protection, well designed APIs, and isolation patterns.\n\n</Pitfall>\n\n<DeepDive>\n\n#### Using `server-only` and `taintUniqueValue` to prevent leaking secrets {/*using-server-only-and-taintuniquevalue-to-prevent-leaking-secrets*/}\n\nIf you're running a Server Components environment that has access to private keys or passwords such as database passwords, you have to be careful not to pass that to a Client Component.\n\n```js\nexport async function Dashboard(props) {\n  // DO NOT DO THIS\n  return <Overview password={process.env.API_PASSWORD} />;\n}\n```\n\n```js\n\"use client\";\n\nimport {useEffect} from '...'\n\nexport async function Overview({ password }) {\n  useEffect(() => {\n    const headers = { Authorization: password };\n    fetch(url, { headers }).then(...);\n  }, [password]);\n  ...\n}\n```\n\nThis example would leak the secret API token to the client. If this API token can be used to access data this particular user shouldn't have access to, it could lead to a data breach.\n\n[comment]: <> (TODO: Link to `server-only` docs once they are written)\n\nIdeally, secrets like this are abstracted into a single helper file that can only be imported by trusted data utilities on the server. The helper can even be tagged with [`server-only`](https://www.npmjs.com/package/server-only) to ensure that this file isn't imported on the client.\n\n```js\nimport \"server-only\";\n\nexport function fetchAPI(url) {\n  const headers = { Authorization: process.env.API_PASSWORD };\n  return fetch(url, { headers });\n}\n```\n\nSometimes mistakes happen during refactoring and not all of your colleagues might know about this. \nTo protect against this mistakes happening down the line we can \"taint\" the actual password:\n\n```js\nimport \"server-only\";\nimport {experimental_taintUniqueValue} from 'react';\n\nexperimental_taintUniqueValue(\n  'Do not pass the API token password to the client. ' +\n    'Instead do all fetches on the server.'\n  process,\n  process.env.API_PASSWORD\n);\n```\n\nNow whenever anyone tries to pass this password to a Client Component, or send the password to a Client Component with a Server Function, an error will be thrown with message you defined when you called `taintUniqueValue`.\n\n</DeepDive>\n\n---\n"
  },
  {
    "path": "src/content/reference/react/forwardRef.md",
    "content": "---\ntitle: forwardRef\n---\n\n<Deprecated>\n\nReact 19 では、`forwardRef` は不要となりました。代わりに props として `ref` を渡すようにしてください。\n\n`forwardRef` は将来のリリースでは非推奨化される予定です。詳しくは[こちら](/blog/2024/04/25/react-19#ref-as-a-prop)を参照してください。\n\n</Deprecated>\n\n<Intro>\n\n`forwardRef` は、親コンポーネントに対して DOM ノードを [ref](/learn/manipulating-the-dom-with-refs) として公開できるようにします。\n\n```js\nconst SomeComponent = forwardRef(render)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `forwardRef(render)` {/*forwardref*/}\n\n`forwardRef()` を呼び出すことで、コンポーネントが ref を受け取ってそれを子コンポーネントに転送 (forward) できるようになります。\n\n```js\nimport { forwardRef } from 'react';\n\nconst MyInput = forwardRef(function MyInput(props, ref) {\n  // ...\n});\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `render`: コンポーネントのレンダー関数です。React はこの関数を親から受け取った props および `ref` とともに呼び出します。返す JSX がコンポーネントの出力となります。\n\n#### 返り値 {/*returns*/}\n\n`forwardRef` は JSX でレンダーできる React コンポーネントを返します。プレーンな関数として定義された React コンポーネントとは異なり、`forwardRef` によって返されるコンポーネントは `ref` 属性を受け取ることもできます。\n\n#### 注意点 {/*caveats*/}\n\n* Strict Mode では、React は[レンダー関数が誤って純関数でなくなってしまう問題を見つけやすくする](/reference/react/useState#my-initializer-or-updater-function-runs-twice)ため、**レンダー関数を 2 回呼び出します**。これは開発環境専用の挙動であり、本番環境には影響しません。レンダー関数が純粋である場合（そうであるべきです）、これはコンポーネントのロジックに影響を与えません。呼び出しのうちの一方からの結果は無視されます。\n\n\n---\n\n### `render` 関数 {/*render-function*/}\n\n`forwardRef` は引数としてレンダー関数を受け取ります。React はこの関数を `props` および `ref` とともに呼び出します。\n\n```js\nconst MyInput = forwardRef(function MyInput(props, ref) {\n  return (\n    <label>\n      {props.label}\n      <input ref={ref} />\n    </label>\n  );\n});\n```\n\n#### 引数 {/*render-parameters*/}\n\n* `props`: 親コンポーネントから渡された props です。\n\n* `ref`: 親コンポーネントから渡された `ref` 属性です。`ref` はオブジェクトの場合と関数の場合があります。親コンポーネントが ref を渡していない場合は `null` になります。受け取った `ref` は、別のコンポーネントに渡すか、[`useImperativeHandle`](/reference/react/useImperativeHandle) に渡します。\n\n#### 返り値 {/*render-returns*/}\n\n`forwardRef` は JSX でレンダーできる React コンポーネントを返します。プレーンな関数として定義された React コンポーネントとは異なり、`forwardRef` によって返されるコンポーネントは `ref` 属性を受け取ることができます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 親コンポーネントに DOM ノードを公開する {/*exposing-a-dom-node-to-the-parent-component*/}\n\nデフォルトでは、各コンポーネント内の DOM ノードはプライベートです。しかし、時には親に DOM ノードを公開することが有用な場合があります。例えば、ノードにフォーカスを当てることを許可したい場合です。これを明示的に許可するために、コンポーネント定義を `forwardRef()` でラップします。\n\n```js {3,11}\nimport { forwardRef } from 'react';\n\nconst MyInput = forwardRef(function MyInput(props, ref) {\n  const { label, ...otherProps } = props;\n  return (\n    <label>\n      {label}\n      <input {...otherProps} />\n    </label>\n  );\n});\n```\n\nprops の後の第 2 引数として <CodeStep step={1}>ref</CodeStep> が渡されます。公開したい DOM ノードにそれを渡してください。\n\n```js {8} [[1, 3, \"ref\"], [1, 8, \"ref\", 30]]\nimport { forwardRef } from 'react';\n\nconst MyInput = forwardRef(function MyInput(props, ref) {\n  const { label, ...otherProps } = props;\n  return (\n    <label>\n      {label}\n      <input {...otherProps} ref={ref} />\n    </label>\n  );\n});\n```\n\nこれで、親の `Form` コンポーネントが、`MyInput` によって公開された <CodeStep step={2}>`<input>` DOM ノード</CodeStep>にアクセスできるようになります。\n\n```js [[1, 2, \"ref\"], [1, 10, \"ref\", 41], [2, 5, \"ref.current\"]]\nfunction Form() {\n  const ref = useRef(null);\n\n  function handleClick() {\n    ref.current.focus();\n  }\n\n  return (\n    <form>\n      <MyInput label=\"Enter your name:\" ref={ref} />\n      <button type=\"button\" onClick={handleClick}>\n        Edit\n      </button>\n    </form>\n  );\n}\n```\n\nこの `Form` コンポーネントは `MyInput` に [ref を渡しています](/reference/react/useRef#manipulating-the-dom-with-a-ref)。`MyInput` コンポーネントはその ref をブラウザの `<input>` タグに*転送*しています。その結果、`Form` コンポーネントはこの `<input>` DOM ノードにアクセスし、[`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) を呼び出すことができるようになります。\n\nコンポーネント内の DOM ノードへの ref を公開することで、後でコンポーネントの内部を変更するのが難しくなることに注意してください。通常は、ボタンやテキスト入力フィールドなどの再利用可能な低レベルコンポーネントからは DOM ノードの公開を行いますが、アバターやコメントのようなアプリケーションレベルのコンポーネントでは行いません。\n\n<Recipes titleText=\"ref の転送の例\">\n\n#### テキスト入力フィールドにフォーカス {/*focusing-a-text-input*/}\n\nボタンをクリックすると、入力フィールドにフォーカスが当てられます。`Form` コンポーネントは ref を定義し、それを `MyInput` コンポーネントに渡します。`MyInput` コンポーネントはその ref をブラウザの `<input>` に転送します。これにより、`Form` コンポーネントは `<input>` にフォーカスを当てられるようになります。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\nimport MyInput from './MyInput.js';\n\nexport default function Form() {\n  const ref = useRef(null);\n\n  function handleClick() {\n    ref.current.focus();\n  }\n\n  return (\n    <form>\n      <MyInput label=\"Enter your name:\" ref={ref} />\n      <button type=\"button\" onClick={handleClick}>\n        Edit\n      </button>\n    </form>\n  );\n}\n```\n\n```js src/MyInput.js\nimport { forwardRef } from 'react';\n\nconst MyInput = forwardRef(function MyInput(props, ref) {\n  const { label, ...otherProps } = props;\n  return (\n    <label>\n      {label}\n      <input {...otherProps} ref={ref} />\n    </label>\n  );\n});\n\nexport default MyInput;\n```\n\n```css\ninput {\n  margin: 5px;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### ビデオの再生と一時停止 {/*playing-and-pausing-a-video*/}\n\nボタンをクリックすると、`<video>` DOM ノードの [`play()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play) と [`pause()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause) が呼び出されます。`App` コンポーネントは ref を定義し、それを `MyVideoPlayer` コンポーネントに渡しています。`MyVideoPlayer` コンポーネントはその ref をブラウザの `<video>` ノードに転送しています。これにより、`App` コンポーネントは `<video>` の再生と一時停止を行うことができるようになります。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\nimport MyVideoPlayer from './MyVideoPlayer.js';\n\nexport default function App() {\n  const ref = useRef(null);\n  return (\n    <>\n      <button onClick={() => ref.current.play()}>\n        Play\n      </button>\n      <button onClick={() => ref.current.pause()}>\n        Pause\n      </button>\n      <br />\n      <MyVideoPlayer\n        ref={ref}\n        src=\"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4\"\n        type=\"video/mp4\"\n        width=\"250\"\n      />\n    </>\n  );\n}\n```\n\n```js src/MyVideoPlayer.js\nimport { forwardRef } from 'react';\n\nconst VideoPlayer = forwardRef(function VideoPlayer({ src, type, width }, ref) {\n  return (\n    <video width={width} ref={ref}>\n      <source\n        src={src}\n        type={type}\n      />\n    </video>\n  );\n});\n\nexport default VideoPlayer;\n```\n\n```css\nbutton { margin-bottom: 10px; margin-right: 10px; }\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n---\n\n### 複数コンポーネントを経由した ref の転送 {/*forwarding-a-ref-through-multiple-components*/}\n\n`ref` を DOM ノードに転送する代わりに、独自コンポーネントである `MyInput` に転送することもできます。\n\n```js {1,5}\nconst FormField = forwardRef(function FormField(props, ref) {\n  // ...\n  return (\n    <>\n      <MyInput ref={ref} />\n      ...\n    </>\n  );\n});\n```\n\nさらにその `MyInput` コンポーネントが自身の `<input>` に ref を転送すれば、`FormField` への ref はその `<input>` への参照を受け取ることになります。\n\n```js {2,5,10}\nfunction Form() {\n  const ref = useRef(null);\n\n  function handleClick() {\n    ref.current.focus();\n  }\n\n  return (\n    <form>\n      <FormField label=\"Enter your name:\" ref={ref} isRequired={true} />\n      <button type=\"button\" onClick={handleClick}>\n        Edit\n      </button>\n    </form>\n  );\n}\n```\n\n`Form` コンポーネントは ref を定義し、それを `FormField` に渡しています。`FormField` コンポーネントはその ref を `MyInput` に転送し、`MyInput` はそれをブラウザの `<input>` DOM ノードに転送しています。これで `Form` が DOM ノードにアクセスできるようになります。\n\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\nimport FormField from './FormField.js';\n\nexport default function Form() {\n  const ref = useRef(null);\n\n  function handleClick() {\n    ref.current.focus();\n  }\n\n  return (\n    <form>\n      <FormField label=\"Enter your name:\" ref={ref} isRequired={true} />\n      <button type=\"button\" onClick={handleClick}>\n        Edit\n      </button>\n    </form>\n  );\n}\n```\n\n```js src/FormField.js\nimport { forwardRef, useState } from 'react';\nimport MyInput from './MyInput.js';\n\nconst FormField = forwardRef(function FormField({ label, isRequired }, ref) {\n  const [value, setValue] = useState('');\n  return (\n    <>\n      <MyInput\n        ref={ref}\n        label={label}\n        value={value}\n        onChange={e => setValue(e.target.value)} \n      />\n      {(isRequired && value === '') &&\n        <i>Required</i>\n      }\n    </>\n  );\n});\n\nexport default FormField;\n```\n\n\n```js src/MyInput.js\nimport { forwardRef } from 'react';\n\nconst MyInput = forwardRef((props, ref) => {\n  const { label, ...otherProps } = props;\n  return (\n    <label>\n      {label}\n      <input {...otherProps} ref={ref} />\n    </label>\n  );\n});\n\nexport default MyInput;\n```\n\n```css\ninput, button {\n  margin: 5px;\n}\n```\n\n</Sandpack>\n\n---\n\n### DOM ノードの代わりに命令型ハンドルを公開する {/*exposing-an-imperative-handle-instead-of-a-dom-node*/}\n\nDOM ノードをまるごと公開する代わりに、使用できるメソッドを制限したカスタムオブジェクトである、*命令型ハンドル (imperative handle)* を公開することができます。これを行うには、DOM ノードを保持するための別の ref を定義します。\n\n```js {2,6}\nconst MyInput = forwardRef(function MyInput(props, ref) {\n  const inputRef = useRef(null);\n\n  // ...\n\n  return <input {...props} ref={inputRef} />;\n});\n```\n\nそして受け取った `ref` を [`useImperativeHandle`](/reference/react/useImperativeHandle) に渡し、`ref` で公開したい値を指定します。\n\n```js {6-15}\nimport { forwardRef, useRef, useImperativeHandle } from 'react';\n\nconst MyInput = forwardRef(function MyInput(props, ref) {\n  const inputRef = useRef(null);\n\n  useImperativeHandle(ref, () => {\n    return {\n      focus() {\n        inputRef.current.focus();\n      },\n      scrollIntoView() {\n        inputRef.current.scrollIntoView();\n      },\n    };\n  }, []);\n\n  return <input {...props} ref={inputRef} />;\n});\n```\n\n何らかのコンポーネントが `MyInput` への ref を取得すると、DOM ノードの代わりにあなたが書いた `{ focus, scrollIntoView }` というオブジェクトを受け取ります。これにより、DOM ノードについて公開する情報を最小限に制限することができます。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\nimport MyInput from './MyInput.js';\n\nexport default function Form() {\n  const ref = useRef(null);\n\n  function handleClick() {\n    ref.current.focus();\n    // This won't work because the DOM node isn't exposed:\n    // ref.current.style.opacity = 0.5;\n  }\n\n  return (\n    <form>\n      <MyInput placeholder=\"Enter your name\" ref={ref} />\n      <button type=\"button\" onClick={handleClick}>\n        Edit\n      </button>\n    </form>\n  );\n}\n```\n\n```js src/MyInput.js\nimport { forwardRef, useRef, useImperativeHandle } from 'react';\n\nconst MyInput = forwardRef(function MyInput(props, ref) {\n  const inputRef = useRef(null);\n\n  useImperativeHandle(ref, () => {\n    return {\n      focus() {\n        inputRef.current.focus();\n      },\n      scrollIntoView() {\n        inputRef.current.scrollIntoView();\n      },\n    };\n  }, []);\n\n  return <input {...props} ref={inputRef} />;\n});\n\nexport default MyInput;\n```\n\n```css\ninput {\n  margin: 5px;\n}\n```\n\n</Sandpack>\n\n[命令型ハンドルの使用について詳しく読む](/reference/react/useImperativeHandle)\n\n<Pitfall>\n\n**ref の過度な使用に注意してください**。ref は、props として表現できない、*命令型*の動作にのみ使用するべきです。例えば、ノードへのスクロール、ノードへのフォーカス、アニメーションのトリガ、テキストの選択などです。\n\n**何かを props として表現できる場合は、ref を使用すべきではありません**。例えば、`Modal` コンポーネントから `{ open, close }` のような命令型のハンドルを公開するのではなく、`<Modal isOpen={isOpen} />` のように、`isOpen` を props として受け取る方が良いでしょう。命令型の動作を props として公開する際には[エフェクト](/learn/synchronizing-with-effects)が役立ちます。\n\n</Pitfall>\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### コンポーネントを `forwardRef` でラップしているのに、`ref` が常に `null` になる {/*my-component-is-wrapped-in-forwardref-but-the-ref-to-it-is-always-null*/}\n\nこれは通常、受け取った `ref` を実際に使用するのを忘れていることを意味します。\n\n例えば、このコンポーネントは `ref` を全く使用していません：\n\n```js {1}\nconst MyInput = forwardRef(function MyInput({ label }, ref) {\n  return (\n    <label>\n      {label}\n      <input />\n    </label>\n  );\n});\n```\n\n修正するにはこの `ref` を、DOM ノードか、ref を受け入れることができる別のコンポーネントに渡します。\n\n```js {1,5}\nconst MyInput = forwardRef(function MyInput({ label }, ref) {\n  return (\n    <label>\n      {label}\n      <input ref={ref} />\n    </label>\n  );\n});\n```\n\n一部のロジックが条件付きである場合にも、`MyInput` への `ref` が `null` になることがあります。\n\n```js {1,5}\nconst MyInput = forwardRef(function MyInput({ label, showInput }, ref) {\n  return (\n    <label>\n      {label}\n      {showInput && <input ref={ref} />}\n    </label>\n  );\n});\n```\n\n`showInput` が `false` の場合、ref はどのノードにも転送されないため、`MyInput` への ref は空のままになります。特に、以下の例のように条件が別のコンポーネント、例えば `Panel` の中に隠されている場合、これを見落としがちです。\n\n```js {5,7}\nconst MyInput = forwardRef(function MyInput({ label, showInput }, ref) {\n  return (\n    <label>\n      {label}\n      <Panel isExpanded={showInput}>\n        <input ref={ref} />\n      </Panel>\n    </label>\n  );\n});\n```\n"
  },
  {
    "path": "src/content/reference/react/hooks.md",
    "content": "---\ntitle: \"組み込みの React フック\"\n---\n\n<Intro>\n\n*フック*を用いると、コンポーネントから様々な React の機能を使えるようになります。組み込みのフックを使うこともできますし、組み合わせて自分だけのものを作ることもできます。このページでは、React に組み込まれたすべてのフックを説明します。\n\n</Intro>\n\n---\n\n## state フック {/*state-hooks*/}\n\n*state* を使うと、ユーザの入力などの情報を[コンポーネントに「記憶」](/learn/state-a-components-memory)させることができます。例えば、フォームコンポーネントは入力された文字を保持し、画像ギャラリのコンポーネントは選択された画像を保持できます。\n\nコンポーネントに state を追加するには、次のフックのいずれかを使います：\n\n* [`useState`](/reference/react/useState) は直接的に更新できる state 変数を定義します。\n* [`useReducer`](/reference/react/useReducer) は、[リデューサ関数](/learn/extracting-state-logic-into-a-reducer)内に書いたロジックを用いて更新を行う state 変数を定義します。\n\n```js\nfunction ImageGallery() {\n  const [index, setIndex] = useState(0);\n  // ...\n```\n\n---\n\n## コンテクストフック {/*context-hooks*/}\n\n*コンテクスト*を用いると、コンポーネントは props を渡すことなく、[離れた親要素から情報を取得できるようになります](/learn/passing-props-to-a-component)。例えば、アプリの最上位のコンポーネントが、現在の UI テーマをコンポーネントの階層に関係なくすべてのコンポーネントに渡すことができます。\n\n* [`useContext`](/reference/react/useContext) は、コンテクストの値を読み取り、変更を受け取れるようにします。\n\n```js\nfunction Button() {\n  const theme = useContext(ThemeContext);\n  // ...\n```\n\n---\n\n## ref フック {/*ref-hooks*/}\n\n*ref* を用いると、コンポーネントは DOM ノードやタイムアウト ID などの、[レンダーに用いない情報を保持](/learn/referencing-values-with-refs)することができます。state と違い、ref の値を更新してもコンポーネントは再レンダーされません。ref は、React パラダイムからの「避難ハッチ」です。これらは組み込みのブラウザ API などの、React 外のシステムを取り扱うときに役立ちます。\n\n* [`useRef`](/reference/react/useRef) は ref を宣言します。useRef にはどんな値でも格納できますが、多くの場合、DOM ノードを格納するために使われます。\n* [`useImperativeHandle`](/reference/react/useImperativeHandle) を用いると、コンポーネントが公開する ref をカスタマイズできます。これはほとんど用いられることはありません。\n\n```js\nfunction Form() {\n  const inputRef = useRef(null);\n  // ...\n```\n\n---\n\n## エフェクトフック {/*effect-hooks*/}\n\n*エフェクト*を使うことで、[コンポーネントを外部システムに接続し、同期させる](/learn/synchronizing-with-effects)ことができます。これには、ネットワーク、ブラウザの DOM、アニメーション、別の UI ライブラリを使って書かれたウィジェット、その他の非 React コードの処理が含まれます。\n\n* [`useEffect`](/reference/react/useEffect) は外部のシステムとコンポーネントを接続します。\n\n```js\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    const connection = createConnection(roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n  // ...\n```\n\nエフェクトは、React パラダイムからの「脱出ハッチ」です。エフェクトをアプリケーションのデータフローを調整するために使ってはいけません。外部のシステムとやりとりを行わないならば、[エフェクトは必要ないかもしれません](/learn/you-might-not-need-an-effect)。\n\n`useEffect` には、実行タイミングが異なり、まれに使われることのある 2 つのバリエーションがあります：\n\n* [`useLayoutEffect`](/reference/react/useLayoutEffect) はブラウザが画面を再描画する前に発火します。このフックでレイアウトを測定できます。\n* [`useInsertionEffect`](/reference/react/useInsertionEffect) は React が DOM に変更を加える前に発火します。ライブラリは動的な CSS をこのフックで挿入できます。\n\nYou can also separate events from Effects:\n\n- [`useEffectEvent`](/reference/react/useEffectEvent) creates a non-reactive event to fire from any Effect hook.\n---\n\n## パフォーマンス関連フック {/*performance-hooks*/}\n\n再レンダーのパフォーマンスを最適化するためのよくある方法は、不要な処理を減らすことです。例えばキャッシュ済みの計算結果を再利用したり、データの変更がない場合の再レンダーをスキップしたりするよう、React に伝えることができます。\n\n不要な計算やレンダーをスキップするためには、以下のフックを用いることができます：\n\n- [`useMemo`](/reference/react/useMemo) を用いると高負荷な計算の結果をキャッシュできます。\n- [`useCallback`](/reference/react/useCallback) を用いると、最適化済みのコンポーネントに渡すために関数定義をキャッシュしておくことができます。\n\n```js\nfunction TodoList({ todos, tab, theme }) {\n  const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);\n  // ...\n}\n```\n\n画面の更新が実際にあるため、再レンダーをスキップできない場合もあるでしょう。その場合、同期的に行う必要があるユーザインターフェイスをブロックする更新（ユーザの文字入力など）を、ユーザインターフェイスをブロックする必要のないノンブロッキングな更新（図の更新など）から分離することで、パフォーマンスを向上することができます。\n\nレンダーの優先度付けを行うために、以下のフックを用いることができます：\n\n- [`useTransition`](/reference/react/useTransition) を用いることで、state の遷移をノンブロッキングなものとしてマークし、他の更新による割り込みを許可します。\n- [`useDeferredValue`](/reference/react/useDeferredValue) を用いると、UI の重要でない部分の更新を遅延させて、他の部分を先に更新させることができます。\n\n---\n\n## その他のフック {/*other-hooks*/}\n\nこれらのフックはライブラリの開発者には有用ですが、アプリケーションコードでは通常は用いられることはありません。\n\n- [`useDebugValue`](/reference/react/useDebugValue) を用いると、React DevTools が表示するカスタムフックのラベルをカスタマイズできます。\n- [`useId`](/reference/react/useId) を用いると、コンポーネントにユニークな ID を関連付けることができます。通常はアクセシビリティ API とともに使用されます。 \n- [`useSyncExternalStore`](/reference/react/useSyncExternalStore) を用いると、コンポーネントは外部のストアを参照できるようになります。\n- [`useActionState`](/reference/react/useActionState) によってアクションの state を管理できます。\n\n---\n\n## 独自のフック {/*your-own-hooks*/}\n\nJavaScript の関数として[独自のカスタムフックを定義](/learn/reusing-logic-with-custom-hooks#extracting-your-own-custom-hook-from-a-component)することもできます。"
  },
  {
    "path": "src/content/reference/react/index.md",
    "content": "---\ntitle: React リファレンス概要\n---\n\n<Intro>\n\nこのセクションは React で開発をする際の詳細なリファレンスドキュメントです。React の使い方の概要については [Learn](/learn) セクションをご覧ください。\n\n</Intro>\n\nReact リファレンスは機能別にいくつかのサブセクションに分かれています。\n\n## React {/*react*/}\n\nプログラムから利用する React の機能です。\n\n* [フック](/reference/react/hooks) - コンポーネント内から使用する様々な React の機能\n* [コンポーネント](/reference/react/components) - JSX 内で用いる組み込みコンポーネント\n* [API](/reference/react/apis) - コンポーネントの定義に用いる API\n* [ディレクティブ](/reference/rsc/directives) - React Server Components 互換のバンドラに与えるための指示情報\n\n## React DOM {/*react-dom*/}\n\nReact DOM には（ブラウザの DOM 環境で動作する）ウェブアプリケーションでのみ用いられる機能が含まれます。以下のセクションに分かれています。\n\n* [フック](/reference/react-dom/hooks) - ブラウザの DOM 環境で実行されるウェブアプリケーションのためのフック\n* [コンポーネント](/reference/react-dom/components) - React がサポートする組み込みの HTML および SVG コンポーネント\n* [API](/reference/react-dom) - ウェブアプリケーションでのみ用いられる `react-dom` パッケージのメソッド\n* [クライアント API](/reference/react-dom/client) - クライアント（ブラウザ）で React コンポーネントをレンダーするための `react-dom/client` API 群\n* [サーバ API](/reference/react-dom/server) - サーバで React コンポーネントを HTML にレンダーするための `react-dom/server` API 群\n* [静的 API](/reference/react-dom/static) - React コンポーネントから静的 HTML を生成するための `react-dom/static` API 群\n\n## React Compiler {/*react-compiler*/}\n\nReact Compiler はビルド時に使用する最適化ツールであり、あなたの React コンポーネントや値に自動的にメモ化を適用します。\n\n* [Configuration](/reference/react-compiler/configuration) - React Compiler の設定オプション\n* [Directives](/reference/react-compiler/directives) - コンパイル動作を制御するための関数レベルのディレクティブ\n* [Compiling Libraries](/reference/react-compiler/compiling-libraries) - コンパイル済みのライブラリコードをリリースする際のガイド\n\n## React フック用の ESLint プラグイン {/*eslint-plugin-react-hooks*/}\n\n[React フック用の ESLint プラグイン](/reference/eslint-plugin-react-hooks) を用いることで React のルールを強制できます。\n\n* [リント](/reference/eslint-plugin-react-hooks) - 個々のリントルールについての実例つきドキュメント\n\n## React のルール {/*rules-of-react*/}\n\nReact には、理解しやすい方法でパターンを表現し高品質なアプリケーションを産み出すための慣用的な記法、ないしルールが存在します。\n\n* [コンポーネントとフックを純粋に保つ](/reference/rules/components-and-hooks-must-be-pure) – これらを純粋に保つことにより、コードの理解やデバッグが容易になり、React がコンポーネントやフックを自動的に正しく最適化できるようになります。\n* [コンポーネントやフックを呼び出すのは React](/reference/rules/react-calls-components-and-hooks) – ユーザ体験を最適化するために必要に応じてコンポーネントやフックを呼び出すというのは React 自身の責務です。\n* [フックのルール](/reference/rules/rules-of-hooks) – フックは再利用可能な UI ロジックを表す JavaScript の関数として定義されており、呼び出せる場所に関する制約があります。\n\n## レガシー API {/*legacy-apis*/}\n\n* [レガシー API](/reference/react/legacy) - `react` パッケージからエクスポートされているが新しいコードでは使用が推奨されないもの\n"
  },
  {
    "path": "src/content/reference/react/isValidElement.md",
    "content": "---\ntitle: isValidElement\n---\n\n<Intro>\n\n`isValidElement` は値が React 要素 (React element) であるかどうかを判定します。\n\n```js\nconst isElement = isValidElement(value)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `isValidElement(value)` {/*isvalidelement*/}\n\n`isValidElement(value)` を呼び出して、`value` が React 要素であるかどうかを判定します。\n\n```js\nimport { isValidElement, createElement } from 'react';\n\n// ✅ React elements\nconsole.log(isValidElement(<p />)); // true\nconsole.log(isValidElement(createElement('p'))); // true\n\n// ❌ Not React elements\nconsole.log(isValidElement(25)); // false\nconsole.log(isValidElement('Hello')); // false\nconsole.log(isValidElement({ age: 42 })); // false\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `value`: 判定対象の値。任意の型の値を指定できます。\n\n#### 返り値 {/*returns*/}\n\n`isValidElement` は `value` が React 要素であれば `true` を返します。それ以外の場合は `false` を返します。\n\n#### 注意点 {/*caveats*/}\n\n* **React 要素と見なされるのは、[JSX タグ](/learn/writing-markup-with-jsx)と、[`createElement`](/reference/react/createElement) によって返されるオブジェクトだけです**。例えば、`42` のような数値は有効な React *ノード (node)* ではあります（コンポーネントから返すことができるため）が、有効な React 要素ではありません。配列や、[`createPortal`](/reference/react-dom/createPortal) で作成されたポータルも、React 要素とは*見なされません*。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 値が React 要素かどうかを判定する {/*checking-if-something-is-a-react-element*/}\n\n`isValidElement` を呼び出して、ある値が *React 要素*であるかどうかを判定します。\n\nReact 要素とは、以下のようなものです。\n\n- [JSX タグ](/learn/writing-markup-with-jsx)を書くことによって生成される値\n- [`createElement`](/reference/react/createElement) を呼び出すことによって生成される値\n\nReact 要素に対しては、`isValidElement` は `true` を返します。\n\n```js\nimport { isValidElement, createElement } from 'react';\n\n// ✅ JSX tags are React elements\nconsole.log(isValidElement(<p />)); // true\nconsole.log(isValidElement(<MyComponent />)); // true\n\n// ✅ Values returned by createElement are React elements\nconsole.log(isValidElement(createElement('p'))); // true\nconsole.log(isValidElement(createElement(MyComponent))); // true\n```\n\n文字列、数値、または任意のオブジェクトや配列などの他の値は、React 要素ではありません。\n\nそれらに対しては、`isValidElement` は `false` を返します。\n\n```js\n// ❌ These are *not* React elements\nconsole.log(isValidElement(null)); // false\nconsole.log(isValidElement(25)); // false\nconsole.log(isValidElement('Hello')); // false\nconsole.log(isValidElement({ age: 42 })); // false\nconsole.log(isValidElement([<div />, <div />])); // false\nconsole.log(isValidElement(MyComponent)); // false\n```\n\n`isValidElement` が必要となることは非常に稀です。これが主に役立つのは、要素のみを受け入れる他の API（例えば [`cloneElement`](/reference/react/cloneElement) がそうです）を呼び出しており、引数が React 要素でないことによるエラーを避けたい場合です。\n\n`isValidElement` のチェックを追加するための特段の理由がない限り、おそらくこれは必要ありません。\n\n<DeepDive>\n\n#### React「要素」と React「ノード」 {/*react-elements-vs-react-nodes*/}\n\nコンポーネントを書くとき、そこからは任意の *React ノード* を返すことができます。\n\n```js\nfunction MyComponent() {\n  // ... you can return any React node ...\n}\n```\n\nReact ノードとは、以下のようなものです。\n\n- `<div />` や `createElement('div')` のようにして作成された React 要素\n- [`createPortal`](/reference/react-dom/createPortal) で作成されたポータル\n- 文字列\n- 数値\n- `true`, `false`, `null`, `undefined`（これらは表示されません）\n- 他の React ノードの配列\n\n**`isValidElement` は引数が *React 要素*であるかどうかを判定しますが、それが React ノードであるかどうかを判定するわけではありません**。例えば、`42` は有効な React 要素ではありません。しかし、これは完全に有効な React ノードです。\n\n```js\nfunction MyComponent() {\n  return 42; // It's ok to return a number from component\n}\n```\n\nしたがって、何かがレンダーできるかどうかをチェックする方法として、`isValidElement` を使うべきではありません。\n\n</DeepDive>\n"
  },
  {
    "path": "src/content/reference/react/lazy.md",
    "content": "---\ntitle: lazy\n---\n\n<Intro>\n\n`lazy` を使うことで、あるコンポーネントが初めてレンダーされるまで、そのコードの読み込みを遅延させることができます。\n\n```js\nconst SomeComponent = lazy(load)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `lazy(load)` {/*lazy*/}\n\n`lazy` をコンポーネントの外部で呼び出し、遅延読み込みされる React コンポーネントを宣言します。\n\n```js\nimport { lazy } from 'react';\n\nconst MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `load`: [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) または *thenable*（`then` メソッドを持つ Promise のようなオブジェクト）を返す関数。返されたコンポーネントを初めてレンダーしようとするときまで React は `load` を呼び出しません。React が初めて `load` を呼び出した後、それが解決 (resolve) するのを待ち、解決した値の `.default` を React コンポーネントとしてレンダーします。返された Promise と解決済みの値は両方ともキャッシュされるため、React は `load` を 2 度以上呼び出しません。Promise が reject された場合、React はその理由を `throw` し、最も近いエラーバウンダリで処理できるようにします。\n\n#### 返り値 {/*returns*/}\n\n`lazy` は、ツリー内でレンダーできる React コンポーネントを返します。遅延コンポーネントのコードがまだ読み込まれていない間、レンダーしようとするとサスペンド (suspend) します。[`<Suspense>`](/reference/react/Suspense) を使用して、読み込み中にローディングインジケータを表示します。\n\n---\n\n### `load` 関数 {/*load*/}\n\n#### 引数 {/*load-parameters*/}\n\n`load` は引数を受け取りません。\n\n#### 返り値 {/*load-returns*/}\n\n[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) または何らかの *thenable*（`then` メソッドを持つ Promise のようなオブジェクト）を返す必要があります。最終的に、有効な React コンポーネント型、つまり例えば関数、[`memo`](/reference/react/memo)、または [`forwardRef`](/reference/react/forwardRef) コンポーネントのようなものを `.default` プロパティとして持つオブジェクトに解決される必要があります。\n\n---\n\n## 使用法 {/*usage*/}\n\n### サスペンスを使ったコンポーネントの遅延読み込み {/*suspense-for-code-splitting*/}\n\n通常、コンポーネントは静的な [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) 宣言を使ってインポートします。\n\n```js\nimport MarkdownPreview from './MarkdownPreview.js';\n```\n\nこのコンポーネントのコードの読み込みを、初めてレンダーされるときまで遅延させるには、このインポートを以下のように置き換えます。\n\n```js\nimport { lazy } from 'react';\n\nconst MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));\n```\n\nこのコードは[ダイナミック `import()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) を用いており、あなたのバンドラやフレームワークからのサポートが必要かもしれません。このパターンを用いる場合は、遅延インポートしようとしているコンポーネントが `default` でエクスポートされている必要があります。\n\nコンポーネントのコードがオンデマンドで読み込まれるようになったので、読み込みの最中には何を表示するべきかを指定する必要があります。これは、遅延コンポーネントまたはその親のいずれかを [`<Suspense>`](/reference/react/Suspense) バウンダリでラップすることで行うことができます。\n\n```js {1,4}\n<Suspense fallback={<Loading />}>\n  <h2>Preview</h2>\n  <MarkdownPreview />\n</Suspense>\n```\n\nこの例では、`MarkdownPreview` のコードはレンダーしようとするまで読み込まれません。もし `MarkdownPreview` がまだ読み込まれていない場合、その代わりに `Loading` が表示されます。チェックボックスをオンにしてみてください。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState, Suspense, lazy } from 'react';\nimport Loading from './Loading.js';\n\nconst MarkdownPreview = lazy(() => delayForDemo(import('./MarkdownPreview.js')));\n\nexport default function MarkdownEditor() {\n  const [showPreview, setShowPreview] = useState(false);\n  const [markdown, setMarkdown] = useState('Hello, **world**!');\n  return (\n    <>\n      <textarea value={markdown} onChange={e => setMarkdown(e.target.value)} />\n      <label>\n        <input type=\"checkbox\" checked={showPreview} onChange={e => setShowPreview(e.target.checked)} />\n        Show preview\n      </label>\n      <hr />\n      {showPreview && (\n        <Suspense fallback={<Loading />}>\n          <h2>Preview</h2>\n          <MarkdownPreview markdown={markdown} />\n        </Suspense>\n      )}\n    </>\n  );\n}\n\n// Add a fixed delay so you can see the loading state\nfunction delayForDemo(promise) {\n  return new Promise(resolve => {\n    setTimeout(resolve, 2000);\n  }).then(() => promise);\n}\n```\n\n```js src/Loading.js\nexport default function Loading() {\n  return <p><i>Loading...</i></p>;\n}\n```\n\n```js src/MarkdownPreview.js\nimport { Remarkable } from 'remarkable';\n\nconst md = new Remarkable();\n\nexport default function MarkdownPreview({ markdown }) {\n  return (\n    <div\n      className=\"content\"\n      dangerouslySetInnerHTML={{__html: md.render(markdown)}}\n    />\n  );\n}\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"remarkable\": \"2.0.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```css\nlabel {\n  display: block;\n}\n\ninput, textarea {\n  margin-bottom: 10px;\n}\n\nbody {\n  min-height: 200px;\n}\n```\n\n</Sandpack>\n\nこのデモは人為的に遅延させて読み込まれます。もう一度チェックボックスをオフにしてからオンにすると、`Preview` はキャッシュされているので、ローディング状態は表示されません。再度ローディング状態を表示するには、サンドボックスの \"Reset\" をクリックしてください。\n\n[サスペンスを使ったローディング状態の管理についてもっと学ぶ](/reference/react/Suspense)\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### `lazy` コンポーネントの state が予期せずリセットされる {/*my-lazy-components-state-gets-reset-unexpectedly*/}\n\n`lazy` コンポーネントを他のコンポーネントの*内部*で宣言しないでください。\n\n```js {4-5}\nimport { lazy } from 'react';\n\nfunction Editor() {\n  // 🔴 Bad: This will cause all state to be reset on re-renders\n  const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));\n  // ...\n}\n```\n\n代わりに、常にモジュールのトップレベルで宣言してください。\n\n```js {3-4}\nimport { lazy } from 'react';\n\n// ✅ Good: Declare lazy components outside of your components\nconst MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));\n\nfunction Editor() {\n  // ...\n}\n```\n"
  },
  {
    "path": "src/content/reference/react/legacy.md",
    "content": "---\ntitle: \"レガシー React API\"\n---\n\n<Intro>\n\nこれらの API は `react` パッケージからエクスポートされていますが、新しく書くコードでの使用は推奨されていません。代替手段については、リンク先の個々の API ページを参照してください。\n\n</Intro>\n\n---\n\n## レガシー API {/*legacy-apis*/}\n\n* [`Children`](/reference/react/Children) を用いて、props として受け取る `children` の JSX を操作・変換します。[代替手段](/reference/react/Children#alternatives)\n* [`cloneElement`](/reference/react/cloneElement) を用いて、別の要素に基づいて React 要素を作成します。[代替手段](/reference/react/cloneElement#alternatives)\n* [`Component`](/reference/react/Component) を用いて、JavaScript クラスとして React コンポーネントを定義します。[代替手段](/reference/react/Component#alternatives)\n* [`createElement`](/reference/react/createElement) を用いて、React 要素を作成します。通常は代わりに JSX を使用します。\n* [`createRef`](/reference/react/createRef) を用いて、任意の値を保持できる ref オブジェクトを作成します。[代替手段](/reference/react/createRef#alternatives)\n* [`forwardRef`](/reference/react/forwardRef) を用いて、親コンポーネントに [ref](/learn/manipulating-the-dom-with-refs) 経由で DOM ノードを公開できます。\n* [`isValidElement`](/reference/react/isValidElement) を用いて、値が React 要素であるかどうかを確認します。通常は [`cloneElement`](/reference/react/cloneElement) と一緒に使用されます。\n* [`PureComponent`](/reference/react/PureComponent) は [`Component`](/reference/react/Component) に似ていますが、同じ props での再レンダーをスキップします。[代替手段](/reference/react/PureComponent#alternatives)\n\n---\n\n## 削除済み API {/*removed-apis*/}\n\n以下の API は React 19 で削除されました。\n\n* [`createFactory`](https://18.react.dev/reference/react/createFactory): 代わりに JSX を使用してください。\n* クラスコンポーネントの [`static contextTypes`](https://18.react.dev//reference/react/Component#static-contexttypes): 代わりに [`static contextType`](#static-contexttype) を使用してください。\n* クラスコンポーネントの [`static childContextTypes`](https://18.react.dev//reference/react/Component#static-childcontexttypes): 代わりに [`static contextType`](#static-contexttype) を使用してください。\n* クラスコンポーネントの [`static getChildContext`](https://18.react.dev//reference/react/Component#getchildcontext): 代わりに [`Context`](/reference/react/createContext#provider) を使用してください。\n* クラスコンポーネントの [`static propTypes`](https://18.react.dev//reference/react/Component#static-proptypes): 代わりに [TypeScript](https://www.typescriptlang.org/) などの型システムを使用してください。\n* クラスコンポーネントの [`this.refs`](https://18.react.dev//reference/react/Component#refs): 代わりに [`createRef`](/reference/react/createRef) を使用してください。\n"
  },
  {
    "path": "src/content/reference/react/memo.md",
    "content": "---\ntitle: memo\n---\n\n<Intro>\n\n`memo` を使うことで、props が変更されていない場合にコンポーネントの再レンダーをスキップできます。\n\n```\nconst MemoizedComponent = memo(SomeComponent, arePropsEqual?)\n```\n\n</Intro>\n\n<Note>\n\n[React Compiler](/learn/react-compiler) はすべてのコンポーネントに自動で `memo` を適用するため、手作業によるメモ化の必要性を減らします。コンパイラを使って自動的にコンポーネントのメモ化を行えます。\n\n</Note>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `memo(Component, arePropsEqual?)` {/*memo*/}\n\nコンポーネントを `memo` でラップすることで、そのコンポーネントの*メモ化 (memoize)* されたバージョンが得られます。このメモ化されたバージョンのコンポーネントは、親コンポーネントが再レンダーされても、自身の props が変更されていない限り通常は再レンダーされなくなります。ただしメモ化は、パフォーマンス最適化であって保証ではないため、React が再レンダーを行うこともありえます。\n\n```js\nimport { memo } from 'react';\n\nconst SomeComponent = memo(function SomeComponent(props) {\n  // ...\n});\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `Component`: メモ化したいコンポーネント。`memo` はこのコンポーネントを変更するのではなく、メモ化が有効になった新たなコンポーネントを返します。関数コンポーネントや [`forwardRef`](/reference/react/forwardRef) によるコンポーネントを含む、任意の有効な React コンポーネントを受け付けます。\n\n* **省略可能** `arePropsEqual`: コンポーネントの前回の props と新しい props の 2 つを引数に取る関数。古い props と新しい props が等しい場合、つまり、新しい props でもコンポーネントが古い props と同じ出力をレンダーして同じように動作する場合は `true` を返すようにします。それ以外の場合は `false` を返すようにします。通常はこの関数を指定することはありません。デフォルトでは、React は個々の props を [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) を使って比較します。\n\n#### 返り値 {/*returns*/}\n\n`memo` は新しい React コンポーネントを返します。これは `memo` に渡したコンポーネントと同様に動作しますが、親が再レンダーされた際に自身の props が変更されていない場合、React が再レンダーを行わない、という点が異なります。\n\n---\n\n## 使用法 {/*usage*/}\n\n### props が変更されていない場合に再レンダーをスキップする {/*skipping-re-rendering-when-props-are-unchanged*/}\n\nReact は通常、親コンポーネントが再レンダーされると常にコンポーネントを再レンダーします。`memo` を使用すると、新しい props が古い props と同じである限り、親が再レンダーされても React によって再レンダーされないコンポーネントを作成できます。そのようなコンポーネントは*メモ化された (memoized)* コンポーネントと呼ばれます。\n\nコンポーネントをメモ化するには、それを `memo` でラップし、返された値を元のコンポーネントの代わりに使用します。\n\n```js\nconst Greeting = memo(function Greeting({ name }) {\n  return <h1>Hello, {name}!</h1>;\n});\n\nexport default Greeting;\n```\n\nReact コンポーネントは常に[純粋なレンダーロジック](/learn/keeping-components-pure)を持つべきです。これは、props、state およびコンテクストが変更されていない場合、常に同じ出力を返す必要があるという意味です。`memo` を使用することで、コンポーネントがこの要件を満たしており、props が変更されない限り再レンダーの必要がないということを React に伝えることができます。`memo` を使用しても、コンポーネント自体の state が変更された場合や、使用しているコンテクストが変更された場合には再レンダーが発生します。\n\n以下の例において、`name` は（props の一部なので）変更されるたびに `Greeting` コンポーネントが再レンダーされる一方で、`address` は（`Greeting` に props として渡されていないため）変更されても再レンダーされないということに注目してください。\n\n<Sandpack>\n\n```js\nimport { memo, useState } from 'react';\n\nexport default function MyApp() {\n  const [name, setName] = useState('');\n  const [address, setAddress] = useState('');\n  return (\n    <>\n      <label>\n        Name{': '}\n        <input value={name} onChange={e => setName(e.target.value)} />\n      </label>\n      <label>\n        Address{': '}\n        <input value={address} onChange={e => setAddress(e.target.value)} />\n      </label>\n      <Greeting name={name} />\n    </>\n  );\n}\n\nconst Greeting = memo(function Greeting({ name }) {\n  console.log(\"Greeting was rendered at\", new Date().toLocaleTimeString());\n  return <h3>Hello{name && ', '}{name}!</h3>;\n});\n```\n\n```css\nlabel {\n  display: block;\n  margin-bottom: 16px;\n}\n```\n\n</Sandpack>\n\n<Note>\n\n**`memo` はパフォーマンスの最適化としてのみ使用してください**。もしコードがそれなしでは動作しない場合、根本的な問題を見つけて先に修正してください。その後で `memo` を追加してパフォーマンスを向上させることができます。\n\n</Note>\n\n<DeepDive>\n\n#### あらゆる場所に memo を追加すべきか？ {/*should-you-add-memo-everywhere*/}\n\nあなたのアプリがこのサイトのように、ほとんどのインタラクションが大まかなもの（ページ全体やセクション全体の置き換えなど）である場合、メモ化は通常不要です。一方、あなたのアプリが描画エディタのようなもので、ほとんどのインタラクションが細かなもの（図形を移動させるなど）である場合、メモ化は非常に役に立つでしょう。\n\n`memo` による最適化は、コンポーネントが全く同一の props で頻繁に再レンダーされ、しかもその再レンダーロジックが高コストである場合にのみ価値があります。コンポーネントが再レンダーされても遅延を感じられない場合、`memo` は不要です。レンダー中に定義されたオブジェクトやプレーンな関数を渡しているなどでコンポーネントに渡される props が*毎回異なる*場合、`memo` は全く無意味であることを覚えておいてください。これが、`memo` と一緒に [`useMemo`](/reference/react/useMemo#skipping-re-rendering-of-components) や [`useCallback`](/reference/react/useCallback#skipping-re-rendering-of-components) がよく必要となる理由です。\n\nその他のケースでコンポーネントを `memo` でラップすることにメリットはありません。それを行っても重大な害はないため、個別のケースを考えずに、可能な限りすべてをメモ化するようにしているチームもあります。このアプローチのデメリットは、コードが読みにくくなることです。また、すべてのメモ化が効果的なわけではありません。例えば、毎回変化する値が 1 つ存在するだけで、コンポーネント全体のメモ化が無意味になってしまうこともあります。\n\n**実際には、以下のいくつかの原則に従うことで、多くのメモ化を不要にすることができます**。\n\n1. コンポーネントが他のコンポーネントを視覚的にラップするときは、それが[子として JSX を受け入れるようにします](/learn/passing-props-to-a-component#passing-jsx-as-children)。これにより、ラッパコンポーネントが自身の state を更新しても、React はその子を再レンダーする必要がないことを認識します。\n1. ローカル state を優先し、必要以上に [state のリフトアップ](/learn/sharing-state-between-components)を行わないようにします。フォームや、アイテムがホバーされているかどうか、といった頻繁に変化する state は、ツリーのトップやグローバルの状態ライブラリに保持しないでください。\n1. [レンダーロジックを純粋に](/learn/keeping-components-pure)保ちます。コンポーネントの再レンダーが問題を引き起こしたり、何らかの目に見える視覚的な結果を生じたりする場合、それはあなたのコンポーネントのバグです！ メモ化を追加するのではなく、バグを修正します。\n1. [state を更新する不要なエフェクトを避けてください](/learn/you-might-not-need-an-effect)。React アプリケーションのパフォーマンス問題の大部分は、エフェクト内での連鎖的な state 更新によってコンポーネントのレンダーが何度も引き起こされるために生じます。\n1. [エフェクトから不要な依存値をできるだけ削除します](/learn/removing-effect-dependencies)。例えば、メモ化する代わりに、オブジェクトや関数をエフェクトの中や外に移動させるだけで、簡単に解決できる場合があります。\n\nそれでも特定のインタラクションが遅いと感じる場合は、[React Developer Tools のプロファイラを使用して](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html)、どのコンポーネントでのメモ化が最も有効かを確認し、そこでメモ化を行いましょう。これらの原則を守ることで、コンポーネントのデバッグや理解が容易になるため、常に原則に従うことをおすすめします。長期的には、この問題を一挙に解決できる[自動的なメモ化](https://www.youtube.com/watch?v=lGEMwh32soc)について研究を行っています。\n\n</DeepDive>\n\n---\n\n### state を使ってメモ化されたコンポーネントを更新する {/*updating-a-memoized-component-using-state*/}\n\nコンポーネントがメモ化されていても、自身の state が変更されたときには再レンダーが発生します。メモ化は、親からコンポーネントに渡される props にのみ関係します。\n\n<Sandpack>\n\n```js\nimport { memo, useState } from 'react';\n\nexport default function MyApp() {\n  const [name, setName] = useState('');\n  const [address, setAddress] = useState('');\n  return (\n    <>\n      <label>\n        Name{': '}\n        <input value={name} onChange={e => setName(e.target.value)} />\n      </label>\n      <label>\n        Address{': '}\n        <input value={address} onChange={e => setAddress(e.target.value)} />\n      </label>\n      <Greeting name={name} />\n    </>\n  );\n}\n\nconst Greeting = memo(function Greeting({ name }) {\n  console.log('Greeting was rendered at', new Date().toLocaleTimeString());\n  const [greeting, setGreeting] = useState('Hello');\n  return (\n    <>\n      <h3>{greeting}{name && ', '}{name}!</h3>\n      <GreetingSelector value={greeting} onChange={setGreeting} />\n    </>\n  );\n});\n\nfunction GreetingSelector({ value, onChange }) {\n  return (\n    <>\n      <label>\n        <input\n          type=\"radio\"\n          checked={value === 'Hello'}\n          onChange={e => onChange('Hello')}\n        />\n        Regular greeting\n      </label>\n      <label>\n        <input\n          type=\"radio\"\n          checked={value === 'Hello and welcome'}\n          onChange={e => onChange('Hello and welcome')}\n        />\n        Enthusiastic greeting\n      </label>\n    </>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-bottom: 16px;\n}\n```\n\n</Sandpack>\n\nstate 変数を現在値そのものにセットする場合、React は `memo` がなくてもコンポーネントの再レンダーをスキップします。コンポーネント関数が余分に呼び出されることがあるかもしれませんが、その結果は破棄されます。\n\n---\n\n### コンテクストを使ってメモ化されたコンポーネントを更新する {/*updating-a-memoized-component-using-a-context*/}\n\nコンポーネントがメモ化されていても、使用しているコンテクストが変更されたときには再レンダーが発生します。メモ化は、親からコンポーネントに渡される props にのみ関係します。\n\n<Sandpack>\n\n```js\nimport { createContext, memo, useContext, useState } from 'react';\n\nconst ThemeContext = createContext(null);\n\nexport default function MyApp() {\n  const [theme, setTheme] = useState('dark');\n\n  function handleClick() {\n    setTheme(theme === 'dark' ? 'light' : 'dark');\n  }\n\n  return (\n    <ThemeContext value={theme}>\n      <button onClick={handleClick}>\n        Switch theme\n      </button>\n      <Greeting name=\"Taylor\" />\n    </ThemeContext>\n  );\n}\n\nconst Greeting = memo(function Greeting({ name }) {\n  console.log(\"Greeting was rendered at\", new Date().toLocaleTimeString());\n  const theme = useContext(ThemeContext);\n  return (\n    <h3 className={theme}>Hello, {name}!</h3>\n  );\n});\n```\n\n```css\nlabel {\n  display: block;\n  margin-bottom: 16px;\n}\n\n.light {\n  color: black;\n  background-color: white;\n}\n\n.dark {\n  color: white;\n  background-color: black;\n}\n```\n\n</Sandpack>\n\nコンテクストの一部が変化したときだけコンポーネントが再レンダーされるようにするには、コンポーネントを 2 つに分割してください。外側のコンポーネントでコンテクストから必要な情報を読み取って、それをメモ化された子コンポーネントに props として渡します。\n\n---\n\n### props の変更を可能な限り減らす {/*minimizing-props-changes*/}\n\n`memo` を使用すると、コンポーネントは props のいずれかが*浅い (shallow) 比較で*前回と等しくない場合に再レンダーされます。つまり、React はコンポーネントのすべての props を前回の値と [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) を使用して比較します。`Object.is(3, 3)` は `true` ですが、`Object.is({}, {})` は `false` です。\n\n\n`memo` の利点を最大限活かすためには、props が変更される回数を最小限に抑えます。例えば、ある props がオブジェクトである場合、親コンポーネント側で、そのオブジェクトが毎回再作成されるのを防ぐために [`useMemo`](/reference/react/useMemo) を使用します。\n\n```js {5-8}\nfunction Page() {\n  const [name, setName] = useState('Taylor');\n  const [age, setAge] = useState(42);\n\n  const person = useMemo(\n    () => ({ name, age }),\n    [name, age]\n  );\n\n  return <Profile person={person} />;\n}\n\nconst Profile = memo(function Profile({ person }) {\n  // ...\n});\n```\n\nprops の変更を最小限に抑えるより良い方法は、コンポーネントが最小限の情報を props として受け入れるようにすることです。例えば、オブジェクト全体の代わりにその中の個々の値を受け入れるようにできます。\n\n```js {4,7}\nfunction Page() {\n  const [name, setName] = useState('Taylor');\n  const [age, setAge] = useState(42);\n  return <Profile name={name} age={age} />;\n}\n\nconst Profile = memo(function Profile({ name, age }) {\n  // ...\n});\n```\n\nさらにそのような個々の値を、より変化しづらい値に投射できることもあります。例えば以下では、コンポーネントが値そのものではなく、値が存在するかどうかのみを表すブーリアンを受け入れるようになっています。\n\n```js {3}\nfunction GroupsLanding({ person }) {\n  const hasGroups = person.groups !== null;\n  return <CallToAction hasGroups={hasGroups} />;\n}\n\nconst CallToAction = memo(function CallToAction({ hasGroups }) {\n  // ...\n});\n```\n\nメモ化されたコンポーネントに関数を渡す必要がある場合、それが変化しないようにコンポーネント外で宣言するか、または [`useCallback`](/reference/react/useCallback#skipping-re-rendering-of-components) を使用することで再レンダーをまたいで定義をキャッシュします。\n\n---\n\n### カスタム比較関数の指定 {/*specifying-a-custom-comparison-function*/}\n\nまれに、メモ化されたコンポーネントの props の変更を最小限に抑えることが不可能な場合があります。その場合、カスタム比較関数を提供することができます。React は浅い比較の代わりに、これを使用して古い props と新しい props を比較します。この関数は `memo` の第 2 引数として渡します。新しい props が古い props と同じ出力をもたらす場合にのみ `true` を返し、それ以外の場合は `false` を返すようにします。\n\n```js {3}\nconst Chart = memo(function Chart({ dataPoints }) {\n  // ...\n}, arePropsEqual);\n\nfunction arePropsEqual(oldProps, newProps) {\n  return (\n    oldProps.dataPoints.length === newProps.dataPoints.length &&\n    oldProps.dataPoints.every((oldPoint, index) => {\n      const newPoint = newProps.dataPoints[index];\n      return oldPoint.x === newPoint.x && oldPoint.y === newPoint.y;\n    })\n  );\n}\n```\n\nこれを行う際は、ブラウザの開発者ツールの Performance パネルを使用して、比較関数を用いることでコンポーネントを再レンダーするより実際に高速化されることを確認してください。結果に驚くかもしれません。\n\nパフォーマンス測定を行うときは、React が本番モードで動作していることを確認してください。\n\n<Pitfall>\n\nカスタムの `arePropsEqual` 実装を提供する場合、**関数を含むすべての prop を比較する必要があります**。しばしば関数はよく親コンポーネントにある props と state を[クロージャ内に閉じ込め](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)ます。`oldProps.onClick !== newProps.onClick` なのに `true` を返すと、コンポーネントは `onClick` ハンドラ内で以前のレンダー時の props と state を「見続ける」ことになり、非常に混乱するバグを引き起こします。\n\n対象のデータ構造が既知の有限の深さを持つことが 100% 確定している場合を除き、`arePropsEqual` 内で深い等価性チェックを行うことは避けてください。**深い等価性チェックは非常に遅くなる危険性がある**ため、誰かが後でデータ構造を変更することによりアプリが何秒間もフリーズしてしまう可能性があります。\n\n</Pitfall>\n\n---\n\n### React Compiler を使用する場合でも React.memo は必要？ {/*react-compiler-memo*/}\n\n[React Compiler](/learn/react-compiler) を有効にすると、通常は `React.memo` は不要になります。コンパイラがコンポーネントの再レンダーを自動的に最適化してくれるからです。\n\n仕組みは以下の通りです。\n\n**React Compiler なし**の場合、不要な再レンダーを防ぐために `React.memo` が必要です。\n\n```js\n// Parent re-renders every second\nfunction Parent() {\n  const [seconds, setSeconds] = useState(0);\n\n  useEffect(() => {\n    const interval = setInterval(() => {\n      setSeconds(s => s + 1);\n    }, 1000);\n    return () => clearInterval(interval);\n  }, []);\n\n  return (\n    <>\n      <h1>Seconds: {seconds}</h1>\n      <ExpensiveChild name=\"John\" />\n    </>\n  );\n}\n\n// Without memo, this re-renders every second even though props don't change\nconst ExpensiveChild = memo(function ExpensiveChild({ name }) {\n  console.log('ExpensiveChild rendered');\n  return <div>Hello, {name}!</div>;\n});\n```\n\n**React Compiler が有効**の場合、同じ最適化が自動的に行われます。\n\n```js\n// No memo needed - compiler prevents re-renders automatically\nfunction ExpensiveChild({ name }) {\n  console.log('ExpensiveChild rendered');\n  return <div>Hello, {name}!</div>;\n}\n```\n\nReact Compiler が生成するコードの重要な部分は以下の通りです。\n\n```js {6-12}\nfunction Parent() {\n  const $ = _c(7);\n  const [seconds, setSeconds] = useState(0);\n  // ... other code ...\n\n  let t3;\n  if ($[4] === Symbol.for(\"react.memo_cache_sentinel\")) {\n    t3 = <ExpensiveChild name=\"John\" />;\n    $[4] = t3;\n  } else {\n    t3 = $[4];\n  }\n  // ... return statement ...\n}\n```\n\nハイライトされた行に注目してください：コンパイラは `<ExpensiveChild name=\"John\" />` をキャッシュチェックでラップしています。props である `name` は常に `\"John\"` であるため、この JSX は一度作成されたら、親の再レンダーのたびに再利用されます。これはまさに `React.memo` が行うことと同じです。つまり props が変更されていない場合、子コンポーネントの再レンダーを防いでいます。\n\nReact Compiler は自動的に以下を行います。\n1. `ExpensiveChild` に渡される props である `name` が変更されていないことを追跡\n2. `<ExpensiveChild name=\"John\" />` に対する以前に作成された JSX を再利用\n3. `ExpensiveChild` の再レンダーを完全にスキップ\n\nつまり、**React Compiler を使用する場合、コンポーネントから `React.memo` を安全に削除できます**。コンパイラが同じ最適化を自動的に提供し、コードをよりクリーンで保守しやすくしてくれます。\n\n<Note>\n\nコンパイラの最適化は実際には `React.memo` よりも包括的です。コンポーネント内の中間値や高コストな計算もメモ化し、コンポーネントツリー全体で `React.memo` と `useMemo` を組み合わせたのと似たような効果をもたらします。\n\n</Note>\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n### props がオブジェクト・配列・関数の場合にコンポーネントが再レンダーされる {/*my-component-rerenders-when-a-prop-is-an-object-or-array*/}\n\nReact は古い props と新しい props とを浅く比較します。つまり、新しい props のそれぞれの値が古い props と参照ベースで等価であるかどうかを比較します。親が再レンダーのたびに新しいオブジェクトや配列を作成している場合、個々の要素が同じであっても、React は変更があったと考えます。同様に、親コンポーネントのレンダー時に新しい関数を作成すると、関数の定義が同じであっても、React はそれを別のものだと考えます。これを避けるためには、[親コンポーネントで props を単純化するか、または props をメモ化してください](#minimizing-props-changes)。\n"
  },
  {
    "path": "src/content/reference/react/startTransition.md",
    "content": "---\ntitle: startTransition\n---\n\n<Intro>\n\n`startTransition` を使うことで、UI を部分的にバックグラウンドでレンダーできるようになります。\n\n```js\nstartTransition(action)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `startTransition(action)` {/*starttransition*/}\n\n`startTransition` 関数を使用すると、state の更新をトランジションとしてマークすることができます。\n\n```js {7,9}\nimport { startTransition } from 'react';\n\nfunction TabContainer() {\n  const [tab, setTab] = useState('about');\n\n  function selectTab(nextTab) {\n    startTransition(() => {\n      setTab(nextTab);\n    });\n  }\n  // ...\n}\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `action`: 1 つ以上の [`set` 関数](/reference/react/useState#setstate)を呼び出して state を更新する関数。React は引数なしで直ちに `action` を呼び出し、`action` 関数呼び出し中に同期的にスケジュールされたすべての state 更新をトランジションとしてマークします。`action` 内で await されている非同期関数のコールもトランジションに含まれるべきですが、現時点では `await` の後に来る `set` 関数は別の `startTransition` にラップする必要があります（[トラブルシューティング](/reference/react/useTransition#react-doesnt-treat-my-state-update-after-await-as-a-transition))参照）。トランジションとしてマークされた state の更新は[ノンブロッキング](#marking-a-state-update-as-a-non-blocking-transition)になり、[不要なローディングインジケータを表示しない](/reference/react/useTransition#preventing-unwanted-loading-indicators)ようになります。\n\n#### 返り値 {/*returns*/}\n\n`startTransition` は何も返しません。\n\n#### 注意点 {/*caveats*/}\n\n* `startTransition` は、トランジションが保留中 (pending) であるかどうかを知るための方法を提供しません。トランジションの進行中にインジケータを表示するには、代わりに [`useTransition`](/reference/react/useTransition) が必要です。\n\n* state の `set` 関数にアクセスできる場合にのみ、state 更新をトランジションにラップできます。ある props やカスタムフックの値に反応してトランジションを開始したい場合は、代わりに [`useDeferredValue`](/reference/react/useDeferredValue) を試してみてください。\n\n* `startTransition` に渡された関数は即座に呼び出され、その関数の実行中に発生するすべての state 更新がトランジションとしてマークされます。しかし例えば、`setTimeout` 内で state を更新しようとした場合は、それはトランジションとしてマークされません。\n\n* 非同期リクエスト後に state 更新を行いたい場合は、トランジションとしてマークするために別の `startTransition` でラップする必要があります。これは既知の制限であり、将来的に修正される予定です（詳細は[トラブルシューティング](/reference/react/useTransition#react-doesnt-treat-my-state-update-after-await-as-a-transition)を参照してください）。\n\n* トランジションとしてマークされた state 更新は、他の state 更新によって中断されます。例えば、トランジション内でチャートコンポーネントを更新した後、チャートの再レンダーの途中で入力フィールドに入力を始めた場合、React は入力欄の更新の処理後にチャートコンポーネントのレンダー作業を再開します。\n\n* トランジションによる更新はテキスト入力欄の制御には使用できません。\n\n* 進行中のトランジションが複数ある場合、React は現在それらをひとつに束ねる処理を行います。この制限は将来のリリースでは削除される可能性があります。\n\n---\n\n## 使用法 {/*usage*/}\n\n### state 更新をノンブロッキングのトランジションとしてマークする {/*marking-a-state-update-as-a-non-blocking-transition*/}\n\nstate 更新を*トランジション*としてマークするには、それを `startTransition` の呼び出しでラップします。\n\n```js {7,9}\nimport { startTransition } from 'react';\n\nfunction TabContainer() {\n  const [tab, setTab] = useState('about');\n\n  function selectTab(nextTab) {\n    startTransition(() => {\n      setTab(nextTab);\n    });\n  }\n  // ...\n}\n```\n\nトランジションを使用することで、遅いデバイスでもユーザインターフェースの更新をレスポンシブに保つことができます。\n\nトランジションを使用すると、再レンダーの途中でも UI がレスポンシブに保たれます。例えば、ユーザがタブをクリックしたが、その後気が変わって別のタブをクリックする場合、最初の再レンダーが終了するのを待つことなくそれを行うことができます。\n\n<Note>\n\n`startTransition` は [`useTransition`](/reference/react/useTransition) と非常に似ていますが、トランジションが進行中かどうかを追跡する `isPending` フラグを提供しない点が異なります。`useTransition` が利用できない場合でも `startTransition` を呼び出すことができます。例えば、`startTransition` はコンポーネントの外部、たとえばデータライブラリ内でも動作します。\n\n[`useTransition` ページでトランジションについて学び、例を見ることができます](/reference/react/useTransition)。\n\n</Note>\n"
  },
  {
    "path": "src/content/reference/react/use.md",
    "content": "---\ntitle: use\n---\n\n<Intro>\n\n`use` は[プロミス (Promise)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) や[コンテクスト](/learn/passing-data-deeply-with-context)などのリソースから値を読み取るための React API です。\n\n```js\nconst value = use(resource);\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `use(resource)` {/*use*/}\n\nコンポーネント内で `use` を呼び出し、[プロミス](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)や[コンテクスト](/learn/passing-data-deeply-with-context)などのリソースから値を読み取ります。\n\n```jsx\nimport { use } from 'react';\n\nfunction MessageComponent({ messagePromise }) {\n  const message = use(messagePromise);\n  const theme = use(ThemeContext);\n  // ...\n```\n\nReact フックとは異なり、`use` は `if` のようなループや条件文内でも呼び出すことができます。ただし React フックと同様に、`use` を呼び出す関数はコンポーネントまたはフック内でなければなりません。\n\nプロミスを引数にして呼び出した場合、`use` API は [`Suspense`](/reference/react/Suspense) や[エラーバウンダリ (Error Boundary)](/reference/react/Component#catching-rendering-errors-with-an-error-boundary) と協調して動作します。`use` を呼び出すコンポーネントは、`use` に渡されたプロミスが保留中 (pending) である間、*サスペンド (suspend)* します。`use` を呼び出すコンポーネントがサスペンスバウンダリでラップされている場合、フォールバックが表示されます。プロミスが解決 (resolve) された時点で、サスペンスフォールバックは、`use` API から返されたデータを使用してレンダーされたコンポーネントの内容に置き換わります。`use` に渡されたプロミスが拒否 (reject) されると、最も近いエラーバウンダリのフォールバックが表示されます。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `resource`: 値を読み取りたいデータソース。リソースは[プロミス](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)または [コンテクスト](/learn/passing-data-deeply-with-context)のいずれかになります。\n\n#### 返り値 {/*returns*/}\n\n`use` API は、[プロミス](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)の解決された値や[コンテクスト](/learn/passing-data-deeply-with-context)など、リソースから読み取った値を返します。\n\n#### 注意点 {/*caveats*/}\n\n* `use` API は、コンポーネントまたは他のフック内で呼び出す必要があります。\n* [サーバコンポーネント](/reference/rsc/server-components)でデータをフェッチする際は、`use` よりも `async` と `await` を優先して使用してください。`async` と `await` は `await` が呼び出された地点からレンダーを再開しますが、`use` はデータが解決した後にコンポーネントを最初からレンダーします。\n* [クライアントコンポーネント](/reference/rsc/use-client)でプロミスを作成するよりも、なるべく[サーバコンポーネント](/reference/rsc/server-components)でプロミスを作成してそれをクライアントコンポーネントに渡すようにしてください。クライアントコンポーネントで作成されたプロミスは、レンダーごとに再作成されます。サーバコンポーネントからクライアントコンポーネントに渡されたプロミスは、再レンダー間で不変です。[こちらの例を参照してください](#streaming-data-from-server-to-client)。\n\n---\n\n## 使用法 {/*usage*/}\n\n### `use` でコンテクストを読み取る {/*reading-context-with-use*/}\n\n[コンテクスト](/learn/passing-data-deeply-with-context)が `use` に渡された場合、[`useContext`](/reference/react/useContext) と同様に動作します。`useContext` はコンポーネントのトップレベルで呼び出す必要がありますが、`use` は `if` や `for` などの条件式の中でも呼び出すことができます。`use` はより柔軟であるため、`useContext` よりも優先的に使用してください。\n\n```js [[2, 4, \"theme\"], [1, 4, \"ThemeContext\"]]\nimport { use } from 'react';\n\nfunction Button() {\n  const theme = use(ThemeContext);\n  // ... \n```\n\n`use` は、渡した<CodeStep step={1}>コンテクスト</CodeStep>の<CodeStep step={2}>値</CodeStep>を返します。コンテクストの値を決定するために、React はコンポーネントツリーを上方向に検索し、当該コンテクストに対応する**最も近いコンテクストプロバイダ (context provider)** を見つけます。\n\n`Button` にコンテクストを渡すには、それまたはその親コンポーネントのいずれかを、対応するコンテクストプロバイダでラップします。\n\n```js [[1, 3, \"ThemeContext\"], [2, 3, \"\\\\\"dark\\\\\"\"], [1, 5, \"ThemeContext\"]]\nfunction MyPage() {\n  return (\n    <ThemeContext value=\"dark\">\n      <Form />\n    </ThemeContext>\n  );\n}\n\nfunction Form() {\n  // ... renders buttons inside ...\n}\n```\n\nプロバイダと `Button` の間に何層のコンポーネントがあっても問題ありません。`Form` の内部の*どこか*で `Button` が `use(ThemeContext)` を呼び出すと、値として `\"dark\"` を受け取ることになります。\n\n[`useContext`](/reference/react/useContext) とは異なり、<CodeStep step={2}>`use`</CodeStep> は <CodeStep step={1}>`if`</CodeStep> などの条件式やループの中で呼び出すことができます。\n\n```js [[1, 2, \"if\"], [2, 3, \"use\"]]\nfunction HorizontalRule({ show }) {\n  if (show) {\n    const theme = use(ThemeContext);\n    return <hr className={theme} />;\n  }\n  return false;\n}\n```\n\n<CodeStep step={2}>`use`</CodeStep> は <CodeStep step={1}>`if`</CodeStep> 文の中から呼び出さているため、条件付きでコンテクストから値を読み取ることができます。\n\n<Pitfall>\n\n`useContext` と同様に、`use(context)` は常にそれを呼び出しているコンポーネントの*上側*にある最も近いコンテクストプロバイダを探します。上方向に検索するため、`use(context)` を呼び出しているコンポーネント自体にあるコンテクストプロバイダは**考慮されません**。\n\n</Pitfall>\n\n<Sandpack>\n\n```js\nimport { createContext, use } from 'react';\n\nconst ThemeContext = createContext(null);\n\nexport default function MyApp() {\n  return (\n    <ThemeContext value=\"dark\">\n      <Form />\n    </ThemeContext>\n  )\n}\n\nfunction Form() {\n  return (\n    <Panel title=\"Welcome\">\n      <Button show={true}>Sign up</Button>\n      <Button show={false}>Log in</Button>\n    </Panel>\n  );\n}\n\nfunction Panel({ title, children }) {\n  const theme = use(ThemeContext);\n  const className = 'panel-' + theme;\n  return (\n    <section className={className}>\n      <h1>{title}</h1>\n      {children}\n    </section>\n  )\n}\n\nfunction Button({ show, children }) {\n  if (show) {\n    const theme = use(ThemeContext);\n    const className = 'button-' + theme;\n    return (\n      <button className={className}>\n        {children}\n      </button>\n    );\n  }\n  return false\n}\n```\n\n```css\n.panel-light,\n.panel-dark {\n  border: 1px solid black;\n  border-radius: 4px;\n  padding: 20px;\n}\n.panel-light {\n  color: #222;\n  background: #fff;\n}\n\n.panel-dark {\n  color: #fff;\n  background: rgb(23, 32, 42);\n}\n\n.button-light,\n.button-dark {\n  border: 1px solid #777;\n  padding: 5px;\n  margin-right: 10px;\n  margin-top: 10px;\n}\n\n.button-dark {\n  background: #222;\n  color: #fff;\n}\n\n.button-light {\n  background: #fff;\n  color: #222;\n}\n```\n\n</Sandpack>\n\n### サーバからクライアントへのデータストリーミング {/*streaming-data-from-server-to-client*/}\n\n<CodeStep step={1}>サーバコンポーネント</CodeStep>から<CodeStep step={2}>クライアントコンポーネント</CodeStep> に props としてプロミスを渡すことで、サーバからクライアントにデータをストリーミングすることができます。\n\n```js [[1, 4, \"App\"], [2, 2, \"Message\"], [3, 7, \"Suspense\"], [4, 8, \"messagePromise\", 30], [4, 5, \"messagePromise\"]]\nimport { fetchMessage } from './lib.js';\nimport { Message } from './message.js';\n\nexport default function App() {\n  const messagePromise = fetchMessage();\n  return (\n    <Suspense fallback={<p>waiting for message...</p>}>\n      <Message messagePromise={messagePromise} />\n    </Suspense>\n  );\n}\n```\n\n<CodeStep step={2}>クライアントコンポーネント</CodeStep> は、<CodeStep step={4}>受け取ったプロミス</CodeStep> を <CodeStep step={5}>`use`</CodeStep> API に渡します。これにより<CodeStep step={2}>クライアントコンポーネント</CodeStep>は、サーバコンポーネントが最初に作成した<CodeStep step={4}>プロミス</CodeStep>から値を読み取ることができます。\n\n```js [[2, 6, \"Message\"], [4, 6, \"messagePromise\"], [4, 7, \"messagePromise\"], [5, 7, \"use\"]]\n// message.js\n'use client';\n\nimport { use } from 'react';\n\nexport function Message({ messagePromise }) {\n  const messageContent = use(messagePromise);\n  return <p>Here is the message: {messageContent}</p>;\n}\n```\n<CodeStep step={2}>`Message`</CodeStep> は <CodeStep step={3}>[`Suspense`](/reference/react/Suspense)</CodeStep> でラップされているため、プロミスが解決されるまでフォールバックが表示されます。プロミスが解決されると、その値が <CodeStep step={5}>`use`</CodeStep> API によって読み取られ、<CodeStep step={2}>`Message`</CodeStep> コンポーネントがサスペンスフォールバックを置き換えます。\n\n<Sandpack>\n\n```js src/message.js active\n\"use client\";\n\nimport { use, Suspense } from \"react\";\n\nfunction Message({ messagePromise }) {\n  const messageContent = use(messagePromise);\n  return <p>Here is the message: {messageContent}</p>;\n}\n\nexport function MessageContainer({ messagePromise }) {\n  return (\n    <Suspense fallback={<p>⌛Downloading message...</p>}>\n      <Message messagePromise={messagePromise} />\n    </Suspense>\n  );\n}\n```\n\n```js src/App.js hidden\nimport { useState } from \"react\";\nimport { MessageContainer } from \"./message.js\";\n\nfunction fetchMessage() {\n  return new Promise((resolve) => setTimeout(resolve, 1000, \"⚛️\"));\n}\n\nexport default function App() {\n  const [messagePromise, setMessagePromise] = useState(null);\n  const [show, setShow] = useState(false);\n  function download() {\n    setMessagePromise(fetchMessage());\n    setShow(true);\n  }\n\n  if (show) {\n    return <MessageContainer messagePromise={messagePromise} />;\n  } else {\n    return <button onClick={download}>Download message</button>;\n  }\n}\n```\n\n```js src/index.js hidden\nimport React, { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\n\n// TODO: update this example to use\n// the Codesandbox Server Component\n// demo environment once it is created\nimport App from './App';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);\n```\n\n</Sandpack>\n\n<Note>\n\nサーバコンポーネントからクライアントコンポーネントにプロミスを渡す場合、その解決値は、サーバとクライアント間でやりとり可能になるよう、シリアライズ可能でなければなりません。関数のようなデータ型はシリアライズ可能ではないため、プロミスの解決値として利用できません。\n\n</Note>\n\n\n<DeepDive>\n\n#### プロミスをサーバコンポーネントで解決するか、クライアントコンポーネントで解決するか？ {/*resolve-promise-in-server-or-client-component*/}\n\nプロミスはサーバコンポーネントからクライアントコンポーネントに渡し、`use` API を使ってクライアントコンポーネントで解決することができます。また、`await` を使ってサーバコンポーネント側でプロミスを解決し、必要なデータを props としてクライアントコンポーネントに渡すことも可能でしょう。\n\n```js\nexport default async function App() {\n  const messageContent = await fetchMessage();\n  return <Message messageContent={messageContent} />\n}\n```\n\nしかし[サーバコンポーネント](/reference/rsc/server-components)で `await` を使用すると、`await` 文が終了するまでそのレンダーがブロックされます。サーバコンポーネントからクライアントコンポーネントにプロミスを渡すことで、プロミスがサーバコンポーネントのレンダーをブロックすることを防ぐことができます。\n\n</DeepDive>\n\n### 拒否されたプロミスの取り扱い {/*dealing-with-rejected-promises*/}\n\n場合によっては、`use` に渡されたプロミスが拒否されることがあります。プロミスが拒否された場合にそれを処理する方法は以下の 2 つです。\n\n1. [エラーバウンダリを使ってユーザにエラーを表示する](#displaying-an-error-to-users-with-error-boundary)\n2. [`Promise.catch` で代替値を提供する](#providing-an-alternative-value-with-promise-catch)\n\n<Pitfall>\n`use` は try-catch ブロック内で呼び出すことはできません。try-catch ブロックを使う代わりに、[コンポーネントをエラーバウンダリでラップする](#displaying-an-error-to-users-with-error-boundary)か、または[プロミスの `.catch` メソッドで代替値を提供](#providing-an-alternative-value-with-promise-catch)してください。\n</Pitfall>\n\n#### エラーバウンダリを使ってユーザにエラーを表示する {/*displaying-an-error-to-users-with-error-boundary*/}\n\nプロミスが拒否されたときにユーザにエラーを表示したい場合は、[エラーバウンダリ](/reference/react/Component#catching-rendering-errors-with-an-error-boundary) を使用できます。エラーバウンダリを使用するには、`use` API を呼び出しているコンポーネントをエラーバウンダリでラップします。`use` に渡されたプロミスが拒否されると、エラーバウンダリに書かれたフォールバックが表示されます。\n\n<Sandpack>\n\n```js src/message.js active\n\"use client\";\n\nimport { use, Suspense } from \"react\";\nimport { ErrorBoundary } from \"react-error-boundary\";\n\nexport function MessageContainer({ messagePromise }) {\n  return (\n    <ErrorBoundary fallback={<p>⚠️Something went wrong</p>}>\n      <Suspense fallback={<p>⌛Downloading message...</p>}>\n        <Message messagePromise={messagePromise} />\n      </Suspense>\n    </ErrorBoundary>\n  );\n}\n\nfunction Message({ messagePromise }) {\n  const content = use(messagePromise);\n  return <p>Here is the message: {content}</p>;\n}\n```\n\n```js src/App.js hidden\nimport { useState } from \"react\";\nimport { MessageContainer } from \"./message.js\";\n\nfunction fetchMessage() {\n  return new Promise((resolve, reject) => setTimeout(reject, 1000));\n}\n\nexport default function App() {\n  const [messagePromise, setMessagePromise] = useState(null);\n  const [show, setShow] = useState(false);\n  function download() {\n    setMessagePromise(fetchMessage());\n    setShow(true);\n  }\n\n  if (show) {\n    return <MessageContainer messagePromise={messagePromise} />;\n  } else {\n    return <button onClick={download}>Download message</button>;\n  }\n}\n```\n\n```js src/index.js hidden\nimport React, { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\n\n// TODO: update this example to use\n// the Codesandbox Server Component\n// demo environment once it is created\nimport App from './App';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"19.0.0\",\n    \"react-dom\": \"19.0.0\",\n    \"react-scripts\": \"^5.0.0\",\n    \"react-error-boundary\": \"4.0.3\"\n  },\n  \"main\": \"/index.js\"\n}\n```\n</Sandpack>\n\n#### `Promise.catch` で代替値を提供する {/*providing-an-alternative-value-with-promise-catch*/}\n\n`use` に渡されたプロミスが拒否されたときに代替値を提供したい場合、プロミスの <CodeStep step={1}>[`catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch)</CodeStep> メソッドを使用できます。\n\n```js [[1, 6, \"catch\"],[2, 7, \"return\"]]\nimport { Message } from './message.js';\n\nexport default function App() {\n  const messagePromise = new Promise((resolve, reject) => {\n    reject();\n  }).catch(() => {\n    return \"no new message found.\";\n  });\n\n  return (\n    <Suspense fallback={<p>waiting for message...</p>}>\n      <Message messagePromise={messagePromise} />\n    </Suspense>\n  );\n}\n```\n\nプロミスの <CodeStep step={1}>`catch`</CodeStep> メソッドを使用するには、プロミスオブジェクトの <CodeStep step={1}>`catch`</CodeStep> を呼び出します。<CodeStep step={1}>`catch`</CodeStep> はエラーメッセージを引数とする関数を唯一の関数として受け取ります。<CodeStep step={1}>`catch`</CodeStep> に渡された関数によって<CodeStep step={2}>返された任意の値</CodeStep>が、プロミスの解決値として使用されます。\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### \"Suspense Exception: This is not a real error!\" {/*suspense-exception-error*/}\n\nあなたは React コンポーネントまたはフック関数の外部で `use` を呼び出しているか、または try-catch ブロック内で `use` を呼び出しています。try-catch ブロック内で `use` を呼び出している場合は、コンポーネントをエラーバウンダリでラップするか、プロミスの `catch` を呼び出してエラーをキャッチし、別の値でプロミスを解決します。[こちらの例を参照してください](#dealing-with-rejected-promises)。\n\nReact コンポーネントまたはフック関数の外部で `use` を呼び出している場合は、`use` の呼び出しを React コンポーネントまたはフック関数に移動します。\n\n```jsx\nfunction MessageComponent({messagePromise}) {\n  function download() {\n    // ❌ the function calling `use` is not a Component or Hook\n    const message = use(messagePromise);\n    // ...\n```\n\n上記の場合、コンポーネントのクロージャの外で `use` を呼び出すようにすることで、コンポーネントまたはフックから `use` を呼び出すという条件を満たすようになります。\n\n```jsx\nfunction MessageComponent({messagePromise}) {\n  // ✅ `use` is being called from a component. \n  const message = use(messagePromise);\n  // ...\n```\n"
  },
  {
    "path": "src/content/reference/react/useActionState.md",
    "content": "---\ntitle: useActionState\n---\n\n<Intro>\n\n`useActionState` は、フォームアクションの結果に基づいて state を更新するためのフックです。\n\n```js\nconst [state, formAction, isPending] = useActionState(fn, initialState, permalink?);\n```\n\n</Intro>\n\n<Note>\n\nReact Canary の以前のバージョンでは、この API は React DOM の一部であり `useFormState` という名前でした。\n\n</Note>\n\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useActionState(action, initialState, permalink?)` {/*useactionstate*/}\n\n{/* TODO T164397693: link to actions documentation once it exists */}\n\nコンポーネントのトップレベルで `useActionState` を呼び出してコンポーネントの state を作成し、[フォームアクションが呼び出されたとき](/reference/react-dom/components/form)に更新されるようにします。既存のフォームアクション関数と初期 state を `useActionState` に渡し、フォームで使用する新しいアクションと最新のフォーム state、およびアクションの進行状況が返されます。あなたが渡した関数にも、最新のフォーム state が渡されるようになります。\n\n```js\nimport { useActionState } from \"react\";\n\nasync function increment(previousState, formData) {\n  return previousState + 1;\n}\n\nfunction StatefulForm({}) {\n  const [state, formAction] = useActionState(increment, 0);\n  return (\n    <form>\n      {state}\n      <button formAction={formAction}>Increment</button>\n    </form>\n  )\n}\n```\n\nフォーム state とは、フォームが最後に送信されたときにアクションによって返される値です。フォームがまだ送信されていない場合は、渡された初期 state が使われます。\n\nサーバ関数と併用して `useActionState` を使うことで、ハイドレーションが完了する前にフォームが送信された場合でも、そのサーバからのレスポンスを表示できるようになります。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `fn`: フォームが送信されたりボタンが押されたりしたときに呼び出される関数。この関数が呼び出される際には、1 番目の引数としてはフォームの前回 state（初回は渡した `initialState`、2 回目以降は前回の返り値）を受け取り、次の引数としてはフォームアクションが通常受け取る引数を受け取ります。\n* `initialState`: state の初期値として使いたい値。シリアライズ可能な任意の値です。この引数はアクションが一度呼び出された後は無視されます。\n* **省略可能** `permalink`: このフォームが書き換えの対象とするユニークなページ URL を含んだ文字列。ダイナミックなコンテンツ（ページフィードなど）のあるページでプログレッシブエンハンスメントを組み合わせる場合に使用します。`fn` が[サーバ関数](/reference/rsc/server-functions)であり、かつフォームが JavaScript バンドルの読み込み完了前に送信された場合、ブラウザは現在のページ URL ではなくこの指定されたパーマリンク用 URL に移動するようになります。React が state を正しく受け渡せるよう、移動先となるページでも（アクション `fn` と `permalink` も含む）同じフォームが必ずレンダーされるようにしてください。フォームのハイドレーションが完了した後は、このパラメータは無視されます。\n\n{/* TODO T164397693: link to serializable values docs once it exists */}\n\n#### 返り値 {/*returns*/}\n\n`useActionState` は以下の値を含む配列を返します。\n\n1. 現在の state。初回レンダー時には、渡した `initialState` と等しくなります。アクションが呼び出された後は、そのアクションが返した値と等しくなります。\n2. フォームコンポーネントの `action` プロパティや、フォーム内の任意の `button` コンポーネントの `formAction` プロパティとして渡すことができる新しいアクション。アクションは [`startTransition`](/reference/react/startTransition) 内で手動で呼び出すことも可能です。\n3. 進行中のトランジションがあるかどうかを表す `isPending` フラグ。\n\n#### 注意点 {/*caveats*/}\n\n* React Server Components をサポートするフレームワークで使用する場合、`useActionState` はクライアント上で JavaScript が実行される前にフォームを操作可能にできます。Server Components なしで使用する場合、コンポーネントのローカル state と同様のものになります。\n* `useActionState` に渡される関数は、追加の 1 番目の引数として、前回 state ないし初期 state を受け取ります。従って `useActionState` を使用せずに直接フォームアクションとして使用する場合とは異なるシグネチャになります。\n\n---\n\n## 使用法 {/*usage*/}\n\n### フォームアクションによって返された情報の使用 {/*using-information-returned-by-a-form-action*/}\n\nコンポーネントのトップレベルで `useActionState` を呼び出し、最後にフォームが送信された際のアクションの返り値にアクセスします。\n\n```js [[1, 5, \"state\"], [2, 5, \"formAction\"], [3, 5, \"action\"], [4, 5, \"null\"], [2, 8, \"formAction\"]]\nimport { useActionState } from 'react';\nimport { action } from './actions.js';\n\nfunction MyComponent() {\n  const [state, formAction] = useActionState(action, null);\n  // ...\n  return (\n    <form action={formAction}>\n      {/* ... */}\n    </form>\n  );\n}\n```\n\n`useActionState` は、以下の項目を含む配列を返します。\n\n1. フォームの <CodeStep step={1}>state の現在値</CodeStep>。初期値はあなたが渡した <CodeStep step={4}>初期 state</CodeStep> となり、フォームが送信された後はあなたが渡した<CodeStep step={3}>アクション</CodeStep>の返り値となります。\n2. `<form>` の props である `action` に渡すか、手動で呼び出すことで利用できる<CodeStep step={2}>新しいアクション</CodeStep>。\n3. アクションが処理中かどうかを知るのに利用できる <CodeStep step={1}>pending 状態</CodeStep>。\n\nフォームが送信されると、あなたが渡した<CodeStep step={3}>アクション</CodeStep>関数が呼び出されます。その返り値が、新たなフォームの <CodeStep step={1}>state 現在値</CodeStep>になります。\n\nあなたが渡す<CodeStep step={3}>アクション</CodeStep>は、新たな第 1 引数として、フォームの<CodeStep step={1}>state の現在値</CodeStep>を受け取ります。フォームが初めて送信されるとき、これはあなたが渡した<CodeStep step={4}>初期 state</CodeStep> と等しくなります。次回以降の送信では、アクションが前回呼び出されたときの返り値になります。残りの引数は `useActionState` を使用しなかった場合と同じです。\n\n```js [[3, 1, \"action\"], [1, 1, \"currentState\"]]\nfunction action(currentState, formData) {\n  // ...\n  return 'next state';\n}\n```\n\n<Recipes titleText=\"フォーム送信後に情報を表示\" titleId=\"display-information-after-submitting-a-form\">\n\n#### フォームエラーの表示 {/*display-form-errors*/}\n\nサーバ関数によって返されるメッセージをエラーメッセージやトーストとして表示するには、そのアクションを `useActionState` の呼び出しでラップします。\n\n<Sandpack>\n\n```js src/App.js\nimport { useActionState, useState } from \"react\";\nimport { addToCart } from \"./actions.js\";\n\nfunction AddToCartForm({itemID, itemTitle}) {\n  const [message, formAction, isPending] = useActionState(addToCart, null);\n  return (\n    <form action={formAction}>\n      <h2>{itemTitle}</h2>\n      <input type=\"hidden\" name=\"itemID\" value={itemID} />\n      <button type=\"submit\">Add to Cart</button>\n      {isPending ? \"Loading...\" : message}\n    </form>\n  );\n}\n\nexport default function App() {\n  return (\n    <>\n      <AddToCartForm itemID=\"1\" itemTitle=\"JavaScript: The Definitive Guide\" />\n      <AddToCartForm itemID=\"2\" itemTitle=\"JavaScript: The Good Parts\" />\n    </>\n  )\n}\n```\n\n```js src/actions.js\n\"use server\";\n\nexport async function addToCart(prevState, queryData) {\n  const itemID = queryData.get('itemID');\n  if (itemID === \"1\") {\n    return \"Added to cart\";\n  } else {\n    // Add a fake delay to make waiting noticeable.\n    await new Promise(resolve => {\n      setTimeout(resolve, 2000);\n    });\n    return \"Couldn't add to cart: the item is sold out.\";\n  }\n}\n```\n\n```css src/styles.css hidden\nform {\n  border: solid 1px black;\n  margin-bottom: 24px;\n  padding: 12px\n}\n\nform button {\n  margin-right: 12px;\n}\n```\n</Sandpack>\n\n<Solution />\n\n#### フォーム送信後に構造化された情報を表示 {/*display-structured-information-after-submitting-a-form*/}\n\nサーバ関数からの返り値は、シリアライズ可能な値であれば任意です。例えばオブジェクトにして、アクションが成功したかどうかを示すブーリアン値や、エラーメッセージや、更新後の情報を含めることもできます。\n\n<Sandpack>\n\n```js src/App.js\nimport { useActionState, useState } from \"react\";\nimport { addToCart } from \"./actions.js\";\n\nfunction AddToCartForm({itemID, itemTitle}) {\n  const [formState, formAction] = useActionState(addToCart, {});\n  return (\n    <form action={formAction}>\n      <h2>{itemTitle}</h2>\n      <input type=\"hidden\" name=\"itemID\" value={itemID} />\n      <button type=\"submit\">Add to Cart</button>\n      {formState?.success &&\n        <div className=\"toast\">\n          Added to cart! Your cart now has {formState.cartSize} items.\n        </div>\n      }\n      {formState?.success === false &&\n        <div className=\"error\">\n          Failed to add to cart: {formState.message}\n        </div>\n      }\n    </form>\n  );\n}\n\nexport default function App() {\n  return (\n    <>\n      <AddToCartForm itemID=\"1\" itemTitle=\"JavaScript: The Definitive Guide\" />\n      <AddToCartForm itemID=\"2\" itemTitle=\"JavaScript: The Good Parts\" />\n    </>\n  )\n}\n```\n\n```js src/actions.js\n\"use server\";\n\nexport async function addToCart(prevState, queryData) {\n  const itemID = queryData.get('itemID');\n  if (itemID === \"1\") {\n    return {\n      success: true,\n      cartSize: 12,\n    };\n  } else {\n    return {\n      success: false,\n      message: \"The item is sold out.\",\n    };\n  }\n}\n```\n\n```css src/styles.css hidden\nform {\n  border: solid 1px black;\n  margin-bottom: 24px;\n  padding: 12px\n}\n\nform button {\n  margin-right: 12px;\n}\n```\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### アクションが送信されたフォームデータを読み取れなくなった {/*my-action-can-no-longer-read-the-submitted-form-data*/}\n\n`useActionState` でアクションをラップすると、追加の引数が *1 番目の引数として*加わります。したがって、通常は 1 番目の引数であるはずの送信フォームデータは、*2 番目の*引数になります。追加される新しい第 1 引数は、フォーム state の現在値です。\n\n```js\nfunction action(currentState, formData) {\n  // ...\n}\n```\n"
  },
  {
    "path": "src/content/reference/react/useCallback.md",
    "content": "---\ntitle: useCallback\n---\n\n<Intro>\n\n`useCallback` は、再レンダー間で関数定義をキャッシュできるようにする React フックです。\n\n```js\nconst cachedFn = useCallback(fn, dependencies)\n```\n\n</Intro>\n\n<Note>\n\n[React Compiler](/learn/react-compiler) は値や関数の自動的なメモ化を行うことで、手作業による `useCallback` 呼び出しの必要性を軽減します。自動でメモ化を行うためにコンパイラを使用可能です。\n\n</Note>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useCallback(fn, dependencies)` {/*usecallback*/}\n\nコンポーネントのトップレベルで `useCallback` を呼び出し、再レンダー間で関数定義をキャッシュします。\n\n```js {4,9}\nimport { useCallback } from 'react';\n\nexport default function ProductPage({ productId, referrer, theme }) {\n  const handleSubmit = useCallback((orderDetails) => {\n    post('/product/' + productId + '/buy', {\n      referrer,\n      orderDetails,\n    });\n  }, [productId, referrer]);\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `fn`: キャッシュしたい関数型の値。任意の引数を取り、任意の値を返すことができます。React は初回のレンダー時にはその関数をそのまま返します（呼び出しません！）。次回以降のレンダーでは、ひとつ前のレンダー時から `dependencies` が変更されていない場合、React は再び同じ関数を返します。それ以外の場合は、今回のレンダー時に渡された関数を返しつつ、後で再利用できる場合に備えて保存します。React は関数を呼び出しません。関数自体が返されるので、呼ぶか呼ばないか、いつ呼ぶのかについてはあなたが決定できます。\n\n* `dependencies`: `fn` コード内で参照されるすべてのリアクティブな値のリストです。リアクティブな値には、props、state、コンポーネント本体に直接宣言されたすべての変数および関数が含まれます。リンタが [React 用に設定されている場合](/learn/editor-setup#linting)、すべてのリアクティブな値が依存値として正しく指定されているか確認できます。依存値のリストは要素数が一定である必要があり、`[dep1, dep2, dep3]` のようにインラインで記述する必要があります。React は、[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) を使った比較で、それぞれの依存値を以前の値と比較します。\n\n#### 返り値 {/*returns*/}\n\n初回のレンダー時、`useCallback` は渡された `fn` 関数を返します。\n\nその後のレンダー時には、前回のレンダーからすでに保存されている `fn` 関数を返すか（依存配列が変更されていない場合）、このレンダー時に渡された `fn` 関数を返します。\n\n#### 注意点 {/*caveats*/}\n\n* `useCallback` はフックですので、**コンポーネントのトップレベル**または独自のフックでのみ呼び出すことができます。ループや条件の中で呼び出すことはできません。それが必要な場合は、新しいコンポーネントを抽出し、その中にその状態を移動させてください。\n* React は、**特定の理由がない限り、キャッシュされた関数を破棄しません**。たとえば、開発環境では、コンポーネントのファイルを編集すると React はキャッシュを破棄します。開発環境と本番環境の両方で、初回マウント時にコンポーネントがサスペンドすると、React はキャッシュを破棄します。将来的に、React はキャッシュを破棄することを活用したさらなる機能を追加するかもしれません。例えば、将来的に React が仮想化リストに対する組み込みサポートを追加する場合、仮想化されたテーブルのビューポートからスクロールアウトした項目のキャッシュを破棄することが理にかなっています。これは、`useCallback` をパフォーマンスの最適化として利用する場合に期待に沿った動作となります。そうでない場合は、[state 変数](/reference/react/useState#im-trying-to-set-state-to-a-function-but-it-gets-called-instead) や [ref](/reference/react/useRef#avoiding-recreating-the-ref-contents) の方が適切かもしれません。\n\n---\n\n## 使用法 {/*usage*/}\n\n### コンポーネントの再レンダーをスキップする {/*skipping-re-rendering-of-components*/}\n\nレンダーのパフォーマンスを最適化する際には、子コンポーネントに渡す関数をキャッシュする必要があることがあります。まずは、これを実現するための構文を見て、その後、どのような場合に便利かを見ていきましょう。\n\nコンポーネントの再レンダー間で関数をキャッシュするには、その定義を `useCallback` フックでラップします。\n\n```js [[3, 4, \"handleSubmit\"], [2, 9, \"[productId, referrer]\"]]\nimport { useCallback } from 'react';\n\nfunction ProductPage({ productId, referrer, theme }) {\n  const handleSubmit = useCallback((orderDetails) => {\n    post('/product/' + productId + '/buy', {\n      referrer,\n      orderDetails,\n    });\n  }, [productId, referrer]);\n  // ...\n```\n\n`useCallback` には 2 つの要素を渡す必要があります。\n\n1. 再レンダー間でキャッシュしたい関数定義。\n2. 関数内で使用される、コンポーネント内のすべての値を含む<CodeStep step={2}>依存値のリスト</CodeStep>。\n\n初回のレンダー時に `useCallback` から<CodeStep step={3}>返される関数</CodeStep>、つまりあなたが受け取る関数は、あなたが渡した関数そのものになります。\n\n次回以降のレンダーでは、React は<CodeStep step={2}>依存配列</CodeStep>を前回のレンダー時に渡した依存配列と比較します。（[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) を使った比較で）依存配列が変更されていない場合、`useCallback` は前回と同じ関数を返します。それ以外の場合、`useCallback` は*今回の*レンダーで渡された関数を返します。\n\n言い換えると、`useCallback` は依存配列が変更されるまでの再レンダー間で関数をキャッシュします。\n\n**例を通して、これが有用な場合を見ていきましょう**。\n\n例えば、`ProductPage` から `ShippingForm` コンポーネントに `handleSubmit` 関数を渡しているとします。\n\n```js {5}\nfunction ProductPage({ productId, referrer, theme }) {\n  // ...\n  return (\n    <div className={theme}>\n      <ShippingForm onSubmit={handleSubmit} />\n    </div>\n  );\n```\n\n`theme` プロパティを切り替えるとアプリが一瞬フリーズすることに気付きましたが、JSX から `<ShippingForm />` を取り除くと、高速に感じられるのだとしましょう。これは `ShippingForm` コンポーネントの最適化を試みる価値があることを示してます。\n\n**デフォルトでは、コンポーネントが再レンダーされると、React はその子要素すべてを再帰的に再レンダーします**。これが、`ProductPage` が異なる `theme` で再レンダーされると、`ShippingForm` コンポーネント*も*再レンダーされる理由です。再レンダーに多くの計算を必要としないコンポーネントにとっては問題ありません。しかし、再レンダーが遅いことを確認できたなら、[`memo`](/reference/react/memo) でラップすることで、props が前回のレンダー時と同じである場合に `ShippingForm` に再レンダーをスキップするように指示することができます。\n\n```js {3,5}\nimport { memo } from 'react';\n\nconst ShippingForm = memo(function ShippingForm({ onSubmit }) {\n  // ...\n});\n```\n\n**この変更により、すべての props が前回のレンダー時と*同じ*場合、`ShippingForm` は再レンダーをスキップするようになります**。ここで関数のキャッシュが重要になってきます！ `handleSubmit` を `useCallback` なしで定義したとしましょう。\n\n```js {2,3,8,12-13}\nfunction ProductPage({ productId, referrer, theme }) {\n  // Every time the theme changes, this will be a different function...\n  function handleSubmit(orderDetails) {\n    post('/product/' + productId + '/buy', {\n      referrer,\n      orderDetails,\n    });\n  }\n\n  return (\n    <div className={theme}>\n      {/* ... so ShippingForm's props will never be the same, and it will re-render every time */}\n      <ShippingForm onSubmit={handleSubmit} />\n    </div>\n  );\n}\n```\n\n**JavaScript では、`function () {}` または `() => {}` は常に*異なる*関数を作成します**。これは `{}` のオブジェクトリテラルが常に新しいオブジェクトを作成するのと似ています。通常、これは問題になりませんが、`ShippingForm` の props が決して同じにならないので [`memo`](/reference/react/memo) による最適化は機能しなくなるということでもあります。このようなときに有用になってくるのが `useCallback` です。\n\n```js {2,3,8,12-13}\nfunction ProductPage({ productId, referrer, theme }) {\n  // Tell React to cache your function between re-renders...\n  const handleSubmit = useCallback((orderDetails) => {\n    post('/product/' + productId + '/buy', {\n      referrer,\n      orderDetails,\n    });\n  }, [productId, referrer]); // ...so as long as these dependencies don't change...\n\n  return (\n    <div className={theme}>\n      {/* ...ShippingForm will receive the same props and can skip re-rendering */}\n      <ShippingForm onSubmit={handleSubmit} />\n    </div>\n  );\n}\n```\n\n**`handleSubmit` を `useCallback` でラップすることで、再レンダー間でそれを*同一の*関数にすることができます**（依存配列が変更されるまで）。それをする特定の理由がない限り、関数を `useCallback` でラップする*必要はありません*。今回の例における理由とは、この関数を [`memo`](/reference/react/memo) でラップされたコンポーネントに渡せば再レンダーをスキップできるということです。このページの後半で説明されているように、`useCallback` が必要な他の理由もあります。\n\n<Note>\n\n**`useCallback` はパフォーマンスの最適化としてのみ用いるべきです**。もしコードがそれなしでは動作しない場合は、背後にある問題を見つけてまずそれを修正してください。その後、`useCallback` を再度追加することができます。\n\n</Note>\n\n<DeepDive>\n\n#### useCallback と useMemo の関係 {/*how-is-usecallback-related-to-usememo*/}\n\n`useCallback` と並んで [`useMemo`](/reference/react/useMemo) をよく見かけることでしょう。子コンポーネントを最適化しようとするとき、どちらも有用です。これらはあなたが下位に渡している何かを[メモ化する (memoize)](https://en.wikipedia.org/wiki/Memoization)（言い換えると、キャッシュする）ことを可能にします。\n\n```js {6-8,10-15,19}\nimport { useMemo, useCallback } from 'react';\n\nfunction ProductPage({ productId, referrer }) {\n  const product = useData('/product/' + productId);\n\n  const requirements = useMemo(() => { // Calls your function and caches its result\n    return computeRequirements(product);\n  }, [product]);\n\n  const handleSubmit = useCallback((orderDetails) => { // Caches your function itself\n    post('/product/' + productId + '/buy', {\n      referrer,\n      orderDetails,\n    });\n  }, [productId, referrer]);\n\n  return (\n    <div className={theme}>\n      <ShippingForm requirements={requirements} onSubmit={handleSubmit} />\n    </div>\n  );\n}\n```\n\nその違いはキャッシュできる*内容*です。\n\n* **[`useMemo`](/reference/react/useMemo) はあなたの関数の呼び出し*結果*をキャッシュします**。この例では、`product` が変更されない限り、`computeRequirements(product)` の呼び出し結果をキャッシュします。これにより、`ShippingForm` を不必要に再レンダーすることなく、`requirements` オブジェクトを下位に渡すことができます。必要に応じて、React はレンダー中にあなたが渡した関数を呼び出して結果を計算します。\n* **`useCallback` は*関数自体*をキャッシュします**。`useMemo` とは異なり、あなたが提供する関数を呼び出しません。代わりに、あなたが提供した関数をキャッシュして、`productId` または `referrer` が変更されない限り、`handleSubmit` *自体*が変更されないようにします。これにより、`ShippingForm` を不必要に再レンダーすることなく、`handleSubmit` 関数を下位に渡すことができます。ユーザがフォームを送信するまであなたのコードは実行されません。\n\nすでに [`useMemo`](/reference/react/useMemo) に詳しい場合、`useCallback` を次のように考えると役立つかもしれません。\n\n```js {expectedErrors: {'react-compiler': [3]}}\n// Simplified implementation (inside React)\nfunction useCallback(fn, dependencies) {\n  return useMemo(() => fn, dependencies);\n}\n```\n\n[`useMemo` と `useCallback` の違いについてもっと読む](/reference/react/useMemo#memoizing-a-function)\n\n</DeepDive>\n\n<DeepDive>\n\n#### あらゆる場所に useCallback を追加すべきか？ {/*should-you-add-usecallback-everywhere*/}\n\nあなたのアプリがこのサイトのように、ほとんどのインタラクションが大まかなもの（ページ全体やセクション全体の置き換えなど）である場合、メモ化は通常不要です。一方、あなたのアプリが描画エディタのようなもので、ほとんどのインタラクションが細かなもの（図形を移動させるなど）である場合、メモ化は非常に役に立つでしょう。\n\n`useCallback` で関数をキャッシュすることが有用なのはいくつかのケースに限られます。\n\n- それを [`memo`](/reference/react/memo) でラップされたコンポーネントに props として渡すケース。この場合は、値が変化していない場合には再レンダーをスキップしたいでしょう。メモ化することで、依存値が異なる場合にのみコンポーネントを再レンダーさせることができます。\n- あなたが渡している関数が、後で何らかのフックの依存値として使用されるケース。たとえば、他の `useCallback` でラップされた関数がそれに依存している、または [`useEffect`](/reference/react/useEffect) からこの関数に依存しているケースです。\n\nこれらのケース以外では、関数を `useCallback` でラップすることにメリットはありません。それを行っても重大な害はないため、個別のケースを考えずに、可能な限りすべてをメモ化するようにしているチームもあります。このアプローチのデメリットは、コードが読みにくくなることです。また、すべてのメモ化が効果的なわけではありません。例えば、毎回変化する値が 1 つ存在するだけで、コンポーネント全体のメモ化が無意味になってしまうこともあります。\n\n`useCallback` は関数の*作成*を防ぐわけではないことに注意してください。あなたは常に関数を作成しています（それは問題ありません！）。しかし、何も変わらない場合、React はそれを無視し、キャッシュされた関数を返します。\n\n**実際には、以下のいくつかの原則に従うことで、多くのメモ化を不要にすることができます**。\n\n1. コンポーネントが他のコンポーネントを視覚的にラップするときは、それが[子として JSX を受け入れるようにします](/learn/passing-props-to-a-component#passing-jsx-as-children)。これにより、ラッパコンポーネントが自身の state を更新しても、React はその子を再レンダーする必要がないことを認識します。\n2. ローカル state を優先し、必要以上に [state のリフトアップ](/learn/sharing-state-between-components)を行わないようにします。フォームや、アイテムがホバーされているかどうか、といった頻繁に変化する state は、ツリーのトップやグローバルの状態ライブラリに保持しないでください。\n3. [レンダーロジックを純粋に](/learn/keeping-components-pure)保ちます。コンポーネントの再レンダーが問題を引き起こしたり、何らかの目に見える視覚的な結果を生じたりする場合、それはあなたのコンポーネントのバグです！ メモ化を追加するのではなく、バグを修正します。\n4. [state を更新する不要なエフェクトを避けてください](/learn/you-might-not-need-an-effect)。React アプリケーションのパフォーマンス問題の大部分は、エフェクト内での連鎖的な state 更新によってコンポーネントのレンダーが何度も引き起こされるために生じます。\n5. [エフェクトから不要な依存値をできるだけ削除します](/learn/removing-effect-dependencies)。例えば、メモ化する代わりに、オブジェクトや関数をエフェクトの中や外に移動させるだけで、簡単に解決できる場合があります。\n\nそれでも特定のインタラクションが遅いと感じる場合は、[React Developer Tools のプロファイラを使用して](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html)、どのコンポーネントでのメモ化が最も有効かを確認し、そこでメモ化を行いましょう。これらの原則を守ることで、コンポーネントのデバッグや理解が容易になるため、常に原則に従うことをおすすめします。長期的には、この問題を一挙に解決できる[自動的なメモ化](https://www.youtube.com/watch?v=lGEMwh32soc)について研究を行っています。\n\n</DeepDive>\n\n<Recipes titleText=\"useCallback と直接関数を宣言することの違い\" titleId=\"examples-rerendering\">\n\n#### `useCallback` と `memo` を使用して再レンダーをスキップする {/*skipping-re-rendering-with-usecallback-and-memo*/}\n\nこの例では、`ShippingForm` コンポーネントを**人為的に遅く**しているため、あなたがレンダーしている React コンポーネントが本当に遅いときに何が起こるかを見ることができます。カウンタを増加させたり、テーマを切り替えたりしてみてください。\n\nカウンタの増加は遅く感じられますが、これは低速な `ShippingForm` を再レンダーせざるを得ないからです。カウンタが変更され、ユーザが行った選択を画面上に反映する必要があるのですから、これ自体は予想通りの動作です。\n\n次に、テーマを切り替えてみてください。**人為的に遅くしているにも関わらず、`useCallback` と [`memo`](/reference/react/memo) を組み合わせたおかげで高速に動作しています**！ `ShippingForm` は、`handleSubmit` 関数が変更されていないため、再レンダーをスキップしました。`handleSubmit` 関数は変更されていません。なぜなら、`productId` と `referrer`（あなたの `useCallback` の依存値）のいずれも、最後のレンダー以降に変更されていないからです。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ProductPage from './ProductPage.js';\n\nexport default function App() {\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Dark mode\n      </label>\n      <hr />\n      <ProductPage\n        referrerId=\"wizard_of_oz\"\n        productId={123}\n        theme={isDark ? 'dark' : 'light'}\n      />\n    </>\n  );\n}\n```\n\n```js src/ProductPage.js active\nimport { useCallback } from 'react';\nimport ShippingForm from './ShippingForm.js';\n\nexport default function ProductPage({ productId, referrer, theme }) {\n  const handleSubmit = useCallback((orderDetails) => {\n    post('/product/' + productId + '/buy', {\n      referrer,\n      orderDetails,\n    });\n  }, [productId, referrer]);\n\n  return (\n    <div className={theme}>\n      <ShippingForm onSubmit={handleSubmit} />\n    </div>\n  );\n}\n\nfunction post(url, data) {\n  // Imagine this sends a request...\n  console.log('POST /' + url);\n  console.log(data);\n}\n```\n\n```js {expectedErrors: {'react-compiler': [7, 8]}} src/ShippingForm.js\nimport { memo, useState } from 'react';\n\nconst ShippingForm = memo(function ShippingForm({ onSubmit }) {\n  const [count, setCount] = useState(1);\n\n  console.log('[ARTIFICIALLY SLOW] Rendering <ShippingForm />');\n  let startTime = performance.now();\n  while (performance.now() - startTime < 500) {\n    // Do nothing for 500 ms to emulate extremely slow code\n  }\n\n  function handleSubmit(e) {\n    e.preventDefault();\n    const formData = new FormData(e.target);\n    const orderDetails = {\n      ...Object.fromEntries(formData),\n      count\n    };\n    onSubmit(orderDetails);\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <p><b>Note: <code>ShippingForm</code> is artificially slowed down!</b></p>\n      <label>\n        Number of items:\n        <button type=\"button\" onClick={() => setCount(count - 1)}>–</button>\n        {count}\n        <button type=\"button\" onClick={() => setCount(count + 1)}>+</button>\n      </label>\n      <label>\n        Street:\n        <input name=\"street\" />\n      </label>\n      <label>\n        City:\n        <input name=\"city\" />\n      </label>\n      <label>\n        Postal code:\n        <input name=\"zipCode\" />\n      </label>\n      <button type=\"submit\">Submit</button>\n    </form>\n  );\n});\n\nexport default ShippingForm;\n```\n\n```css\nlabel {\n  display: block; margin-top: 10px;\n}\n\ninput {\n  margin-left: 5px;\n}\n\nbutton[type=\"button\"] {\n  margin: 5px;\n}\n\n.dark {\n  background-color: black;\n  color: white;\n}\n\n.light {\n  background-color: white;\n  color: black;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### 常にコンポーネントを再レンダーする {/*always-re-rendering-a-component*/}\n\nこの例でも、`ShippingForm` の実装を**人為的に遅く**してあり、レンダーしている React コンポーネントが本当に遅いときに何が起こるかを見ることができます。カウンタを増加させたり、テーマを切り替えたりしてみてください。\n\n前の例とは異なり、今度はテーマの切り替えも低速です！ これは、**このバージョンには `useCallback` の呼び出しがないため**、`handleSubmit` は常に新しい関数であり、遅くなっている `ShippingForm` コンポーネントの再レンダーをスキップできないからです。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ProductPage from './ProductPage.js';\n\nexport default function App() {\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Dark mode\n      </label>\n      <hr />\n      <ProductPage\n        referrerId=\"wizard_of_oz\"\n        productId={123}\n        theme={isDark ? 'dark' : 'light'}\n      />\n    </>\n  );\n}\n```\n\n```js src/ProductPage.js active\nimport ShippingForm from './ShippingForm.js';\n\nexport default function ProductPage({ productId, referrer, theme }) {\n  function handleSubmit(orderDetails) {\n    post('/product/' + productId + '/buy', {\n      referrer,\n      orderDetails,\n    });\n  }\n\n  return (\n    <div className={theme}>\n      <ShippingForm onSubmit={handleSubmit} />\n    </div>\n  );\n}\n\nfunction post(url, data) {\n  // Imagine this sends a request...\n  console.log('POST /' + url);\n  console.log(data);\n}\n```\n\n```js {expectedErrors: {'react-compiler': [7, 8]}} src/ShippingForm.js\nimport { memo, useState } from 'react';\n\nconst ShippingForm = memo(function ShippingForm({ onSubmit }) {\n  const [count, setCount] = useState(1);\n\n  console.log('[ARTIFICIALLY SLOW] Rendering <ShippingForm />');\n  let startTime = performance.now();\n  while (performance.now() - startTime < 500) {\n    // Do nothing for 500 ms to emulate extremely slow code\n  }\n\n  function handleSubmit(e) {\n    e.preventDefault();\n    const formData = new FormData(e.target);\n    const orderDetails = {\n      ...Object.fromEntries(formData),\n      count\n    };\n    onSubmit(orderDetails);\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <p><b>Note: <code>ShippingForm</code> is artificially slowed down!</b></p>\n      <label>\n        Number of items:\n        <button type=\"button\" onClick={() => setCount(count - 1)}>–</button>\n        {count}\n        <button type=\"button\" onClick={() => setCount(count + 1)}>+</button>\n      </label>\n      <label>\n        Street:\n        <input name=\"street\" />\n      </label>\n      <label>\n        City:\n        <input name=\"city\" />\n      </label>\n      <label>\n        Postal code:\n        <input name=\"zipCode\" />\n      </label>\n      <button type=\"submit\">Submit</button>\n    </form>\n  );\n});\n\nexport default ShippingForm;\n```\n\n```css\nlabel {\n  display: block; margin-top: 10px;\n}\n\ninput {\n  margin-left: 5px;\n}\n\nbutton[type=\"button\"] {\n  margin: 5px;\n}\n\n.dark {\n  background-color: black;\n  color: white;\n}\n\n.light {\n  background-color: white;\n  color: black;\n}\n```\n\n</Sandpack>\n\n\nしかし、以下は**人為的な遅さを取り除いた**同じコードです。`useCallback` が無いことに、気づくほどの影響があるでしょうか？\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport ProductPage from './ProductPage.js';\n\nexport default function App() {\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Dark mode\n      </label>\n      <hr />\n      <ProductPage\n        referrerId=\"wizard_of_oz\"\n        productId={123}\n        theme={isDark ? 'dark' : 'light'}\n      />\n    </>\n  );\n}\n```\n\n```js src/ProductPage.js active\nimport ShippingForm from './ShippingForm.js';\n\nexport default function ProductPage({ productId, referrer, theme }) {\n  function handleSubmit(orderDetails) {\n    post('/product/' + productId + '/buy', {\n      referrer,\n      orderDetails,\n    });\n  }\n\n  return (\n    <div className={theme}>\n      <ShippingForm onSubmit={handleSubmit} />\n    </div>\n  );\n}\n\nfunction post(url, data) {\n  // Imagine this sends a request...\n  console.log('POST /' + url);\n  console.log(data);\n}\n```\n\n```js src/ShippingForm.js\nimport { memo, useState } from 'react';\n\nconst ShippingForm = memo(function ShippingForm({ onSubmit }) {\n  const [count, setCount] = useState(1);\n\n  console.log('Rendering <ShippingForm />');\n\n  function handleSubmit(e) {\n    e.preventDefault();\n    const formData = new FormData(e.target);\n    const orderDetails = {\n      ...Object.fromEntries(formData),\n      count\n    };\n    onSubmit(orderDetails);\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <label>\n        Number of items:\n        <button type=\"button\" onClick={() => setCount(count - 1)}>–</button>\n        {count}\n        <button type=\"button\" onClick={() => setCount(count + 1)}>+</button>\n      </label>\n      <label>\n        Street:\n        <input name=\"street\" />\n      </label>\n      <label>\n        City:\n        <input name=\"city\" />\n      </label>\n      <label>\n        Postal code:\n        <input name=\"zipCode\" />\n      </label>\n      <button type=\"submit\">Submit</button>\n    </form>\n  );\n});\n\nexport default ShippingForm;\n```\n\n```css\nlabel {\n  display: block; margin-top: 10px;\n}\n\ninput {\n  margin-left: 5px;\n}\n\nbutton[type=\"button\"] {\n  margin: 5px;\n}\n\n.dark {\n  background-color: black;\n  color: white;\n}\n\n.light {\n  background-color: white;\n  color: black;\n}\n```\n\n</Sandpack>\n\n\nかなりの頻度で、メモ化なしのコードでも問題なく動作します。あなたのインタラクションが十分に速い場合、メモ化は必要ありません。\n\nあなたのアプリが遅くなっている真の理由を把握するためには、React を本番モードで実行し、[React Developer Tools](/learn/react-developer-tools) を無効にし、あなたのアプリのユーザが使用しているデバイスと同様のデバイスを使用する必要があることに留意してください。\n\n<Solution />\n\n</Recipes>\n\n---\n\n### メモ化されたコールバックからの state 更新 {/*updating-state-from-a-memoized-callback*/}\n\n場合によっては、メモ化されたコールバックから前回の state に基づいて state を更新する必要があります。\n\nこの `handleAddTodo` 関数は、次の todo リストを計算するために `todos` を依存値として指定します。\n\n```js {6,7}\nfunction TodoList() {\n  const [todos, setTodos] = useState([]);\n\n  const handleAddTodo = useCallback((text) => {\n    const newTodo = { id: nextId++, text };\n    setTodos([...todos, newTodo]);\n  }, [todos]);\n  // ...\n```\n\n通常、メモ化された関数からは可能な限り依存値を少なくしたいと思うでしょう。何らかの state を次の state を計算するためだけに読み込んでいる場合、代わりに[更新用関数 (updater function)](/reference/react/useState#updating-state-based-on-the-previous-state) を渡すことでその依存値を削除できます。\n\n```js {6,7}\nfunction TodoList() {\n  const [todos, setTodos] = useState([]);\n\n  const handleAddTodo = useCallback((text) => {\n    const newTodo = { id: nextId++, text };\n    setTodos(todos => [...todos, newTodo]);\n  }, []); // ✅ No need for the todos dependency\n  // ...\n```\n\nここでは、`todos` を依存値として内部で読み込む代わりに、*どのように* state を更新するかについての指示（`todos => [...todos, newTodo]`）を React に渡します。[更新用関数についての詳細はこちら](/reference/react/useState#updating-state-based-on-the-previous-state)。\n\n---\n\n### エフェクトが頻繁に発火するのを防ぐ {/*preventing-an-effect-from-firing-too-often*/}\n\n時々、[エフェクト](/learn/synchronizing-with-effects) の内部から関数を呼び出したいことがあるかもしれません。\n\n```js {4-9,12}\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  function createOptions() {\n    return {\n      serverUrl: 'https://localhost:1234',\n      roomId: roomId\n    };\n  }\n\n  useEffect(() => {\n    const options = createOptions();\n    const connection = createConnection(options);\n    connection.connect();\n    // ...\n```\n\nこれには問題があります。[すべてのリアクティブな値はエフェクトの依存値として宣言されなければなりません](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency)。しかし、`createOptions` を依存値として宣言すると、あなたのエフェクトがチャットルームに常に再接続することになります。\n\n\n```js {6}\n  useEffect(() => {\n    const options = createOptions();\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [createOptions]); // 🔴 Problem: This dependency changes on every render\n  // ...\n```\n\nこれを解決するために、エフェクトから呼び出す必要がある関数を `useCallback` でラップすることができます。\n\n```js {4-9,16}\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  const createOptions = useCallback(() => {\n    return {\n      serverUrl: 'https://localhost:1234',\n      roomId: roomId\n    };\n  }, [roomId]); // ✅ Only changes when roomId changes\n\n  useEffect(() => {\n    const options = createOptions();\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [createOptions]); // ✅ Only changes when createOptions changes\n  // ...\n```\n\nこれにより、`roomId` が同じ場合に再レンダー間で `createOptions` 関数が同じであることが保証されます。**しかし、関数型の依存値を必要としないようにする方がさらに望ましいでしょう**。関数をエフェクトの*内部*に移動します。\n\n```js {5-10,16}\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    function createOptions() { // ✅ No need for useCallback or function dependencies!\n      return {\n        serverUrl: 'https://localhost:1234',\n        roomId: roomId\n      };\n    }\n\n    const options = createOptions();\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]); // ✅ Only changes when roomId changes\n  // ...\n```\n\nこれでコードはよりシンプルになり、`useCallback` が不要になりました。[エフェクトの依存値の削除についてさらに学ぶ](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect)。\n\n---\n\n### カスタムフックの最適化 {/*optimizing-a-custom-hook*/}\n\nあなたが[カスタムフック](/learn/reusing-logic-with-custom-hooks)を書いている場合、それが返すあらゆる関数は `useCallback` でラップすることが推奨されます。\n\n```js {4-6,8-10}\nfunction useRouter() {\n  const { dispatch } = useContext(RouterStateContext);\n\n  const navigate = useCallback((url) => {\n    dispatch({ type: 'navigate', url });\n  }, [dispatch]);\n\n  const goBack = useCallback(() => {\n    dispatch({ type: 'back' });\n  }, [dispatch]);\n\n  return {\n    navigate,\n    goBack,\n  };\n}\n```\n\nこれにより、フックを利用する側が必要に応じて自身のコードを最適化することができます。\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### コンポーネントがレンダーするたびに `useCallback` が異なる関数を返す {/*every-time-my-component-renders-usecallback-returns-a-different-function*/}\n\n第 2 引数として依存配列を指定したかを確認してください！\n\n依存配列を忘れると、`useCallback` は毎回新しい関数を返します。\n\n```js {7}\nfunction ProductPage({ productId, referrer }) {\n  const handleSubmit = useCallback((orderDetails) => {\n    post('/product/' + productId + '/buy', {\n      referrer,\n      orderDetails,\n    });\n  }); // 🔴 Returns a new function every time: no dependency array\n  // ...\n```\n\n以下は、第 2 引数として依存配列を渡す修正版です。\n\n```js {7}\nfunction ProductPage({ productId, referrer }) {\n  const handleSubmit = useCallback((orderDetails) => {\n    post('/product/' + productId + '/buy', {\n      referrer,\n      orderDetails,\n    });\n  }, [productId, referrer]); // ✅ Does not return a new function unnecessarily\n  // ...\n```\n\nこれでもうまくいかない場合、問題は、少なくとも 1 つの依存値が前回のレンダーと異なることです。依存値を手動でコンソールにログ出力することで、この問題をデバッグできます。\n\n```js {5}\n  const handleSubmit = useCallback((orderDetails) => {\n    // ..\n  }, [productId, referrer]);\n\n  console.log([productId, referrer]);\n```\n\nその後、コンソール内の異なる再レンダーからの配列を右クリックすると、それぞれに対して「グローバル変数として保存」が選択できます。最初のものが `temp1` として、2 つ目が `temp2` として保存されたと仮定すると、ブラウザのコンソールを使用して、両方の配列内の各依存値が同一であるかどうかを以下のように確認できます。\n\n```js\nObject.is(temp1[0], temp2[0]); // Is the first dependency the same between the arrays?\nObject.is(temp1[1], temp2[1]); // Is the second dependency the same between the arrays?\nObject.is(temp1[2], temp2[2]); // ... and so on for every dependency ...\n```\n\nメモ化を壊している依存値を見つけたら、それを取り除く方法を見つけるか、または[それもメモ化します。](/reference/react/useMemo#memoizing-a-dependency-of-another-hook)\n\n---\n\n### ループ内の各リスト要素で `useCallback` を呼び出す必要があるが、それは許されていない {/*i-need-to-call-usememo-for-each-list-item-in-a-loop-but-its-not-allowed*/}\n\n`Chart` コンポーネントが [`memo`](/reference/react/memo) でラップされていると仮定します。`ReportList` コンポーネントが再レンダーするときに、リスト内の `Chart` がすべて再レンダーされてしまわないよう、一部をスキップしたいとしましょう。しかし、ループの中で `useCallback` を呼び出すことはできません。\n\n```js {expectedErrors: {'react-compiler': [6]}} {5-14}\nfunction ReportList({ items }) {\n  return (\n    <article>\n      {items.map(item => {\n        // 🔴 You can't call useCallback in a loop like this:\n        const handleClick = useCallback(() => {\n          sendReport(item)\n        }, [item]);\n\n        return (\n          <figure key={item.id}>\n            <Chart onClick={handleClick} />\n          </figure>\n        );\n      })}\n    </article>\n  );\n}\n```\n\n代わりに、個々のアイテムに対応するコンポーネントを抽出し、その中に `useCallback` を配置します。\n\n```js {5,12-21}\nfunction ReportList({ items }) {\n  return (\n    <article>\n      {items.map(item =>\n        <Report key={item.id} item={item} />\n      )}\n    </article>\n  );\n}\n\nfunction Report({ item }) {\n  // ✅ Call useCallback at the top level:\n  const handleClick = useCallback(() => {\n    sendReport(item)\n  }, [item]);\n\n  return (\n    <figure>\n      <Chart onClick={handleClick} />\n    </figure>\n  );\n}\n```\n\nもしくは、最後のスニペットから `useCallback` を削除し、代わりに `Report` 自体を [`memo`](/reference/react/memo) でラップすることもできます。`item` プロパティが変更されない場合、`Report` は再レンダーをスキップするため、`Chart` も再レンダーをスキップします。\n\n```js {5,6-8,15}\nfunction ReportList({ items }) {\n  // ...\n}\n\nconst Report = memo(function Report({ item }) {\n  function handleClick() {\n    sendReport(item);\n  }\n\n  return (\n    <figure>\n      <Chart onClick={handleClick} />\n    </figure>\n  );\n});\n```\n"
  },
  {
    "path": "src/content/reference/react/useContext.md",
    "content": "---\ntitle: useContext\n---\n\n<Intro>\n\n`useContext` はコンポーネントで[コンテクスト (Context)](/learn/passing-data-deeply-with-context) の読み取りとサブスクライブ（subscribe, 変更の受け取り）を行うための React フックです。\n\n```js\nconst value = useContext(SomeContext)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useContext(SomeContext)` {/*usecontext*/}\n\nコンポーネントのトップレベルで `useContext` を呼び出して、[コンテクスト](/learn/passing-data-deeply-with-context)を読み取り、サブスクライブします。\n\n```js\nimport { useContext } from 'react';\n\nfunction MyComponent() {\n  const theme = useContext(ThemeContext);\n  // ...\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `SomeContext`: 事前に [`createContext`](/reference/react/createContext) で作成したコンテクストです。コンテクストとはそれ自体が情報を保持しているわけではなく、コンポーネントで提供 (provide) したり読み取ったりできる「情報の種別」を表すものです。\n\n#### 返り値 {/*returns*/}\n\n`useContext` は、呼び出したコンポーネントに対応するコンテクストの値を返します。値は、ツリー内で `useContext` を呼び出したコンポーネントの上位かつ最も近い `SomeContext` に渡された `value` として決定されます。そのようなプロバイダが存在しない場合は、返り値はそのコンテクストの [`createContext`](/reference/react/createContext) に渡した `defaultValue` になります。返り値は常にコンテクストの最新の値です。React は、コンテクストに変更があると、それを読み取っているコンポーネントを自動的に再レンダーします。\n\n#### 注意点 {/*caveats*/}\n\n* コンポーネントの `useContext()` 呼び出しは、*同じ*コンポーネントから返されるプロバイダの影響を受けません。対応する `<Context>` は、`useContext()` を呼び出すコンポーネントの***上*にある**必要があります。\n* あるコンテクストのプロバイダが異なる `value` を受け取ると、当該プロバイダより下にありそのコンテクストを使用しているすべての子コンポーネントは、React によって**自動的に再レンダーされます**。前の値と次の値は、[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) で比較されます。[`memo`](/reference/react/memo) を使って再レンダーをスキップする場合でも、子コンポーネントがコンテクストから新しい値を受け取ることによる再レンダーは妨げられません。\n* ビルドシステムが生成する出力の中にモジュールの重複がある場合（シンボリックリンクで起こり得る場合がある）、コンテクストが壊れる可能性があります。コンテクストを介した値の受け渡しが動作するのは、コンテクストを提供するために使用する `SomeContext` と、読み込むために使用する `SomeContext` が、`===` による比較で***厳密*に同じオブジェクト**である場合のみです。\n\n---\n\n## 使用法 {/*usage*/}\n\n\n### ツリーの深くにデータを渡す {/*passing-data-deeply-into-the-tree*/}\n\nコンポーネントのトップレベルで `useContext` を呼び出して[コンテクスト](/learn/passing-data-deeply-with-context)を読み取り、サブスクライブします。\n\n```js [[2, 4, \"theme\"], [1, 4, \"ThemeContext\"]]\nimport { useContext } from 'react';\n\nfunction Button() {\n  const theme = useContext(ThemeContext);\n  // ... \n```\n\n`useContext` は<CodeStep step={1}>渡したコンテクスト</CodeStep>に対応する<CodeStep step={2}>コンテクストの値</CodeStep>を返します。コンテクストの値を決定するために、React はコンポーネントツリーを探索し、そのコンテクストに対して**最も近い上位のコンテクストプロバイダ**を見つけます。\n\nコンテクストを上記の `Button` に渡すには、該当のボタンあるいはその親コンポーネントのいずれかを、対応するコンテクストプロバイダでラップします。\n\n```js [[1, 3, \"ThemeContext\"], [2, 3, \"\\\\\"dark\\\\\"\"], [1, 5, \"ThemeContext\"]]\nfunction MyPage() {\n  return (\n    <ThemeContext value=\"dark\">\n      <Form />\n    </ThemeContext>\n  );\n}\n\nfunction Form() {\n  // ... renders buttons inside ...\n}\n```\n\nプロバイダと `Button` の間にどれだけ多くのコンポーネントが挟まっていても関係ありません。`Form` の内部の*どこか*で `Button` が `useContext(ThemeContext)` を呼び出すとき、値として `\"dark\"` を受け取ります。\n\n<Pitfall>\n\n`useContext()` は常に、呼び出すコンポーネントより最も近い、*上位*にあるプロバイダを探します。上方向に探索を行うので、`useContext()` を呼び出すコンポーネント自体にあるプロバイダは**考慮しません**。\n\n</Pitfall>\n\n<Sandpack>\n\n```js\nimport { createContext, useContext } from 'react';\n\nconst ThemeContext = createContext(null);\n\nexport default function MyApp() {\n  return (\n    <ThemeContext value=\"dark\">\n      <Form />\n    </ThemeContext>\n  )\n}\n\nfunction Form() {\n  return (\n    <Panel title=\"Welcome\">\n      <Button>Sign up</Button>\n      <Button>Log in</Button>\n    </Panel>\n  );\n}\n\nfunction Panel({ title, children }) {\n  const theme = useContext(ThemeContext);\n  const className = 'panel-' + theme;\n  return (\n    <section className={className}>\n      <h1>{title}</h1>\n      {children}\n    </section>\n  )\n}\n\nfunction Button({ children }) {\n  const theme = useContext(ThemeContext);\n  const className = 'button-' + theme;\n  return (\n    <button className={className}>\n      {children}\n    </button>\n  );\n}\n```\n\n```css\n.panel-light,\n.panel-dark {\n  border: 1px solid black;\n  border-radius: 4px;\n  padding: 20px;\n}\n.panel-light {\n  color: #222;\n  background: #fff;\n}\n\n.panel-dark {\n  color: #fff;\n  background: rgb(23, 32, 42);\n}\n\n.button-light,\n.button-dark {\n  border: 1px solid #777;\n  padding: 5px;\n  margin-right: 10px;\n  margin-top: 10px;\n}\n\n.button-dark {\n  background: #222;\n  color: #fff;\n}\n\n.button-light {\n  background: #fff;\n  color: #222;\n}\n```\n\n</Sandpack>\n\n---\n\n### コンテクスト経由で渡されたデータの更新 {/*updating-data-passed-via-context*/}\n\n多くの場合、時間とともにコンテクストを変化させたいと思うでしょう。コンテクストを更新するには、それを [state](/reference/react/useState) と組み合わせます。親コンポーネントで state 変数を宣言し、現在の state を<CodeStep step={2}>コンテクストの値</CodeStep>としてプロバイダに渡します。\n\n```js {2} [[1, 4, \"ThemeContext\"], [2, 4, \"theme\"], [1, 11, \"ThemeContext\"]]\nfunction MyPage() {\n  const [theme, setTheme] = useState('dark');\n  return (\n    <ThemeContext value={theme}>\n      <Form />\n      <Button onClick={() => {\n        setTheme('light');\n      }}>\n        Switch to light theme\n      </Button>\n    </ThemeContext>\n  );\n}\n```\n\nこれにより、プロバイダの内部にある、どの `Button` も現在の `theme` の値を受け取るようになります。`setTheme` を呼び出してプロバイダに渡す `theme` 値を更新すると、すべての `Button` コンポーネントは新たな値である `'light'` を使って再レンダーされます。\n\n<Recipes titleText=\"Examples of updating context\" titleId=\"examples-basic\">\n\n#### コンテクスト経由で渡された値の更新 {/*updating-a-value-via-context*/}\n\nこの例では、`MyApp` コンポーネントが state 変数を保持し、それが `ThemeContext` プロバイダに渡されます。\"Dark mode\" のチェックボックスを選択すると、state が更新されます。プロバイダに渡す値を更新すると、そのコンテクストを使用しているすべてのコンポーネントが再レンダーされます。\n\n<Sandpack>\n\n```js\nimport { createContext, useContext, useState } from 'react';\n\nconst ThemeContext = createContext(null);\n\nexport default function MyApp() {\n  const [theme, setTheme] = useState('light');\n  return (\n    <ThemeContext value={theme}>\n      <Form />\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={theme === 'dark'}\n          onChange={(e) => {\n            setTheme(e.target.checked ? 'dark' : 'light')\n          }}\n        />\n        Use dark mode\n      </label>\n    </ThemeContext>\n  )\n}\n\nfunction Form({ children }) {\n  return (\n    <Panel title=\"Welcome\">\n      <Button>Sign up</Button>\n      <Button>Log in</Button>\n    </Panel>\n  );\n}\n\nfunction Panel({ title, children }) {\n  const theme = useContext(ThemeContext);\n  const className = 'panel-' + theme;\n  return (\n    <section className={className}>\n      <h1>{title}</h1>\n      {children}\n    </section>\n  )\n}\n\nfunction Button({ children }) {\n  const theme = useContext(ThemeContext);\n  const className = 'button-' + theme;\n  return (\n    <button className={className}>\n      {children}\n    </button>\n  );\n}\n```\n\n```css\n.panel-light,\n.panel-dark {\n  border: 1px solid black;\n  border-radius: 4px;\n  padding: 20px;\n  margin-bottom: 10px;\n}\n.panel-light {\n  color: #222;\n  background: #fff;\n}\n\n.panel-dark {\n  color: #fff;\n  background: rgb(23, 32, 42);\n}\n\n.button-light,\n.button-dark {\n  border: 1px solid #777;\n  padding: 5px;\n  margin-right: 10px;\n  margin-top: 10px;\n}\n\n.button-dark {\n  background: #222;\n  color: #fff;\n}\n\n.button-light {\n  background: #fff;\n  color: #222;\n}\n```\n\n</Sandpack>\n\n`value=\"dark\"` は `\"dark\"` という文字列を渡しますが、`value={theme}` は JavaScript の `theme` 変数の値を [JSX の波括弧](/learn/javascript-in-jsx-with-curly-braces)で渡していることに注意してください。波括弧を使うことで、文字列以外のコンテクスト値も渡すことができます。\n\n<Solution />\n\n#### コンテクストを介してオブジェクトを更新する {/*updating-an-object-via-context*/}\n\nこの例では、オブジェクトを保持する `currentUser` という state 変数があります。`{ currentUser, setCurrentUser }` という形で 1 つのオブジェクトにまとめ、`value={}` の中でコンテクストを介して渡しています。これにより、`LoginButton` のような下位のコンポーネントは `currentUser` と `setCurrentUser` の両方を読み取り、必要に応じて `setCurrentUser` を呼び出すことができます。\n\n<Sandpack>\n\n```js\nimport { createContext, useContext, useState } from 'react';\n\nconst CurrentUserContext = createContext(null);\n\nexport default function MyApp() {\n  const [currentUser, setCurrentUser] = useState(null);\n  return (\n    <CurrentUserContext\n      value={{\n        currentUser,\n        setCurrentUser\n      }}\n    >\n      <Form />\n    </CurrentUserContext>\n  );\n}\n\nfunction Form({ children }) {\n  return (\n    <Panel title=\"Welcome\">\n      <LoginButton />\n    </Panel>\n  );\n}\n\nfunction LoginButton() {\n  const {\n    currentUser,\n    setCurrentUser\n  } = useContext(CurrentUserContext);\n\n  if (currentUser !== null) {\n    return <p>You logged in as {currentUser.name}.</p>;\n  }\n\n  return (\n    <Button onClick={() => {\n      setCurrentUser({ name: 'Advika' })\n    }}>Log in as Advika</Button>\n  );\n}\n\nfunction Panel({ title, children }) {\n  return (\n    <section className=\"panel\">\n      <h1>{title}</h1>\n      {children}\n    </section>\n  )\n}\n\nfunction Button({ children, onClick }) {\n  return (\n    <button className=\"button\" onClick={onClick}>\n      {children}\n    </button>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n}\n\n.panel {\n  border: 1px solid black;\n  border-radius: 4px;\n  padding: 20px;\n  margin-bottom: 10px;\n}\n\n.button {\n  border: 1px solid #777;\n  padding: 5px;\n  margin-right: 10px;\n  margin-top: 10px;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### 複数のコンテクスト {/*multiple-contexts*/}\n\nこの例には 2 つの独立したコンテクストがあります。`ThemeContext` は現在のテーマを文字列として提供し、一方で `CurrentUserContext` は現在のユーザを表すオブジェクトを保持しています。\n\n<Sandpack>\n\n```js\nimport { createContext, useContext, useState } from 'react';\n\nconst ThemeContext = createContext(null);\nconst CurrentUserContext = createContext(null);\n\nexport default function MyApp() {\n  const [theme, setTheme] = useState('light');\n  const [currentUser, setCurrentUser] = useState(null);\n  return (\n    <ThemeContext value={theme}>\n      <CurrentUserContext\n        value={{\n          currentUser,\n          setCurrentUser\n        }}\n      >\n        <WelcomePanel />\n        <label>\n          <input\n            type=\"checkbox\"\n            checked={theme === 'dark'}\n            onChange={(e) => {\n              setTheme(e.target.checked ? 'dark' : 'light')\n            }}\n          />\n          Use dark mode\n        </label>\n      </CurrentUserContext>\n    </ThemeContext>\n  )\n}\n\nfunction WelcomePanel({ children }) {\n  const {currentUser} = useContext(CurrentUserContext);\n  return (\n    <Panel title=\"Welcome\">\n      {currentUser !== null ?\n        <Greeting /> :\n        <LoginForm />\n      }\n    </Panel>\n  );\n}\n\nfunction Greeting() {\n  const {currentUser} = useContext(CurrentUserContext);\n  return (\n    <p>You logged in as {currentUser.name}.</p>\n  )\n}\n\nfunction LoginForm() {\n  const {setCurrentUser} = useContext(CurrentUserContext);\n  const [firstName, setFirstName] = useState('');\n  const [lastName, setLastName] = useState('');\n  const canLogin = firstName.trim() !== '' && lastName.trim() !== '';\n  return (\n    <>\n      <label>\n        First name{': '}\n        <input\n          required\n          value={firstName}\n          onChange={e => setFirstName(e.target.value)}\n        />\n      </label>\n      <label>\n        Last name{': '}\n        <input\n        required\n          value={lastName}\n          onChange={e => setLastName(e.target.value)}\n        />\n      </label>\n      <Button\n        disabled={!canLogin}\n        onClick={() => {\n          setCurrentUser({\n            name: firstName + ' ' + lastName\n          });\n        }}\n      >\n        Log in\n      </Button>\n      {!canLogin && <i>Fill in both fields.</i>}\n    </>\n  );\n}\n\nfunction Panel({ title, children }) {\n  const theme = useContext(ThemeContext);\n  const className = 'panel-' + theme;\n  return (\n    <section className={className}>\n      <h1>{title}</h1>\n      {children}\n    </section>\n  )\n}\n\nfunction Button({ children, disabled, onClick }) {\n  const theme = useContext(ThemeContext);\n  const className = 'button-' + theme;\n  return (\n    <button\n      className={className}\n      disabled={disabled}\n      onClick={onClick}\n    >\n      {children}\n    </button>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n}\n\n.panel-light,\n.panel-dark {\n  border: 1px solid black;\n  border-radius: 4px;\n  padding: 20px;\n  margin-bottom: 10px;\n}\n.panel-light {\n  color: #222;\n  background: #fff;\n}\n\n.panel-dark {\n  color: #fff;\n  background: rgb(23, 32, 42);\n}\n\n.button-light,\n.button-dark {\n  border: 1px solid #777;\n  padding: 5px;\n  margin-right: 10px;\n  margin-top: 10px;\n}\n\n.button-dark {\n  background: #222;\n  color: #fff;\n}\n\n.button-light {\n  background: #fff;\n  color: #222;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### プロバイダをコンポーネントに抽出する {/*extracting-providers-to-a-component*/}\n\nアプリが大きくなるにつれ、アプリのルート近くにコンテクストの「ピラミッド」ができることでしょう。これには何の問題もありません。ですが、入れ子になった見た目が気に入らない場合、プロバイダを単一のコンポーネントに抽出することができます。この例では、`MyProviders` が「配管」を隠蔽し、アプリが必要とするプロバイダの内部に、渡された子をレンダーしています。`theme` state と `setTheme` は `MyApp` 自身の中で必要ですので、この state は依然 `MyApp` が所有していることに注意してください。\n\n<Sandpack>\n\n```js\nimport { createContext, useContext, useState } from 'react';\n\nconst ThemeContext = createContext(null);\nconst CurrentUserContext = createContext(null);\n\nexport default function MyApp() {\n  const [theme, setTheme] = useState('light');\n  return (\n    <MyProviders theme={theme} setTheme={setTheme}>\n      <WelcomePanel />\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={theme === 'dark'}\n          onChange={(e) => {\n            setTheme(e.target.checked ? 'dark' : 'light')\n          }}\n        />\n        Use dark mode\n      </label>\n    </MyProviders>\n  );\n}\n\nfunction MyProviders({ children, theme, setTheme }) {\n  const [currentUser, setCurrentUser] = useState(null);\n  return (\n    <ThemeContext value={theme}>\n      <CurrentUserContext\n        value={{\n          currentUser,\n          setCurrentUser\n        }}\n      >\n        {children}\n      </CurrentUserContext>\n    </ThemeContext>\n  );\n}\n\nfunction WelcomePanel({ children }) {\n  const {currentUser} = useContext(CurrentUserContext);\n  return (\n    <Panel title=\"Welcome\">\n      {currentUser !== null ?\n        <Greeting /> :\n        <LoginForm />\n      }\n    </Panel>\n  );\n}\n\nfunction Greeting() {\n  const {currentUser} = useContext(CurrentUserContext);\n  return (\n    <p>You logged in as {currentUser.name}.</p>\n  )\n}\n\nfunction LoginForm() {\n  const {setCurrentUser} = useContext(CurrentUserContext);\n  const [firstName, setFirstName] = useState('');\n  const [lastName, setLastName] = useState('');\n  const canLogin = firstName !== '' && lastName !== '';\n  return (\n    <>\n      <label>\n        First name{': '}\n        <input\n          required\n          value={firstName}\n          onChange={e => setFirstName(e.target.value)}\n        />\n      </label>\n      <label>\n        Last name{': '}\n        <input\n        required\n          value={lastName}\n          onChange={e => setLastName(e.target.value)}\n        />\n      </label>\n      <Button\n        disabled={!canLogin}\n        onClick={() => {\n          setCurrentUser({\n            name: firstName + ' ' + lastName\n          });\n        }}\n      >\n        Log in\n      </Button>\n      {!canLogin && <i>Fill in both fields.</i>}\n    </>\n  );\n}\n\nfunction Panel({ title, children }) {\n  const theme = useContext(ThemeContext);\n  const className = 'panel-' + theme;\n  return (\n    <section className={className}>\n      <h1>{title}</h1>\n      {children}\n    </section>\n  )\n}\n\nfunction Button({ children, disabled, onClick }) {\n  const theme = useContext(ThemeContext);\n  const className = 'button-' + theme;\n  return (\n    <button\n      className={className}\n      disabled={disabled}\n      onClick={onClick}\n    >\n      {children}\n    </button>\n  );\n}\n```\n\n```css\nlabel {\n  display: block;\n}\n\n.panel-light,\n.panel-dark {\n  border: 1px solid black;\n  border-radius: 4px;\n  padding: 20px;\n  margin-bottom: 10px;\n}\n.panel-light {\n  color: #222;\n  background: #fff;\n}\n\n.panel-dark {\n  color: #fff;\n  background: rgb(23, 32, 42);\n}\n\n.button-light,\n.button-dark {\n  border: 1px solid #777;\n  padding: 5px;\n  margin-right: 10px;\n  margin-top: 10px;\n}\n\n.button-dark {\n  background: #222;\n  color: #fff;\n}\n\n.button-light {\n  background: #fff;\n  color: #222;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### コンテクストとリデューサでスケールアップ {/*scaling-up-with-context-and-a-reducer*/}\n\n大規模なアプリでは、コンテクストと[リデューサ (reducer)](/reference/react/useReducer) を組み合わせて、コンポーネントからある state に関連するロジックを抽出することが一般的に行われます。以下の例では、すべての「繋ぎ込み」コードが、リデューサと 2 つの独立したコンテクストが含まれる `TasksContext.js` 内に隠蔽されています。\n\nこの例の[詳細なウォークスルーはこちら](/learn/scaling-up-with-reducer-and-context)。\n\n<Sandpack>\n\n```js src/App.js\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\nimport { TasksProvider } from './TasksContext.js';\n\nexport default function TaskApp() {\n  return (\n    <TasksProvider>\n      <h1>Day off in Kyoto</h1>\n      <AddTask />\n      <TaskList />\n    </TasksProvider>\n  );\n}\n```\n\n```js src/TasksContext.js\nimport { createContext, useContext, useReducer } from 'react';\n\nconst TasksContext = createContext(null);\n\nconst TasksDispatchContext = createContext(null);\n\nexport function TasksProvider({ children }) {\n  const [tasks, dispatch] = useReducer(\n    tasksReducer,\n    initialTasks\n  );\n\n  return (\n    <TasksContext value={tasks}>\n      <TasksDispatchContext value={dispatch}>\n        {children}\n      </TasksDispatchContext>\n    </TasksContext>\n  );\n}\n\nexport function useTasks() {\n  return useContext(TasksContext);\n}\n\nexport function useTasksDispatch() {\n  return useContext(TasksDispatchContext);\n}\n\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [...tasks, {\n        id: action.id,\n        text: action.text,\n        done: false\n      }];\n    }\n    case 'changed': {\n      return tasks.map(t => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter(t => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nconst initialTasks = [\n  { id: 0, text: 'Philosopher’s Path', done: true },\n  { id: 1, text: 'Visit the temple', done: false },\n  { id: 2, text: 'Drink matcha', done: false }\n];\n```\n\n```js src/AddTask.js\nimport { useState, useContext } from 'react';\nimport { useTasksDispatch } from './TasksContext.js';\n\nexport default function AddTask() {\n  const [text, setText] = useState('');\n  const dispatch = useTasksDispatch();\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button onClick={() => {\n        setText('');\n        dispatch({\n          type: 'added',\n          id: nextId++,\n          text: text,\n        }); \n      }}>Add</button>\n    </>\n  );\n}\n\nlet nextId = 3;\n```\n\n```js src/TaskList.js\nimport { useState, useContext } from 'react';\nimport { useTasks, useTasksDispatch } from './TasksContext.js';\n\nexport default function TaskList() {\n  const tasks = useTasks();\n  return (\n    <ul>\n      {tasks.map(task => (\n        <li key={task.id}>\n          <Task task={task} />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ task }) {\n  const [isEditing, setIsEditing] = useState(false);\n  const dispatch = useTasksDispatch();\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={e => {\n            dispatch({\n              type: 'changed',\n              task: {\n                ...task,\n                text: e.target.value\n              }\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={e => {\n          dispatch({\n            type: 'changed',\n            task: {\n              ...task,\n              done: e.target.checked\n            }\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => {\n        dispatch({\n          type: 'deleted',\n          id: task.id\n        });\n      }}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n---\n\n### フォールバックとなるデフォルト値の指定 {/*specifying-a-fallback-default-value*/}\n\nReact がある<CodeStep step={1}>コンテクスト</CodeStep>に対応するプロバイダを親ツリーで見つけられない場合、`useContext()` が返すコンテクストの値は、[コンテクストを作成](/reference/react/createContext)したときに指定した<CodeStep step={3}>デフォルト値</CodeStep>と等しくなります：\n\n```js [[1, 1, \"ThemeContext\"], [3, 1, \"null\"]]\nconst ThemeContext = createContext(null);\n```\n\nデフォルト値は**絶対に変更されません**。コンテクストを更新したい場合、[上記で説明したように](#updating-data-passed-via-context)、state と組み合わせて使用します。\n\n多くの場合、`null` の代わりにデフォルト値として使える、意味のある値があるはずです。例えば：\n\n```js [[1, 1, \"ThemeContext\"], [3, 1, \"light\"]]\nconst ThemeContext = createContext('light');\n```\n\nこうすれば、対応するプロバイダなしにコンポーネントを間違ってレンダーしてしまっても、壊れることはありません。テスト環境でも、テストコードにプロバイダをたくさん設定せずともコンポーネントがうまく動作するようになります。\n\n下記の例では、\"Toggle theme\" ボタンは**あらゆるテーマコンテクストプロバイダの外部にあり**、かつテーマコンテクストのデフォルト値が `'light'` であるため、常に light の色調で表示されます。テーマの初期値を `'dark'` に変更してみてください。\n\n<Sandpack>\n\n```js\nimport { createContext, useContext, useState } from 'react';\n\nconst ThemeContext = createContext('light');\n\nexport default function MyApp() {\n  const [theme, setTheme] = useState('light');\n  return (\n    <>\n      <ThemeContext value={theme}>\n        <Form />\n      </ThemeContext>\n      <Button onClick={() => {\n        setTheme(theme === 'dark' ? 'light' : 'dark');\n      }}>\n        Toggle theme\n      </Button>\n    </>\n  )\n}\n\nfunction Form({ children }) {\n  return (\n    <Panel title=\"Welcome\">\n      <Button>Sign up</Button>\n      <Button>Log in</Button>\n    </Panel>\n  );\n}\n\nfunction Panel({ title, children }) {\n  const theme = useContext(ThemeContext);\n  const className = 'panel-' + theme;\n  return (\n    <section className={className}>\n      <h1>{title}</h1>\n      {children}\n    </section>\n  )\n}\n\nfunction Button({ children, onClick }) {\n  const theme = useContext(ThemeContext);\n  const className = 'button-' + theme;\n  return (\n    <button className={className} onClick={onClick}>\n      {children}\n    </button>\n  );\n}\n```\n\n```css\n.panel-light,\n.panel-dark {\n  border: 1px solid black;\n  border-radius: 4px;\n  padding: 20px;\n  margin-bottom: 10px;\n}\n.panel-light {\n  color: #222;\n  background: #fff;\n}\n\n.panel-dark {\n  color: #fff;\n  background: rgb(23, 32, 42);\n}\n\n.button-light,\n.button-dark {\n  border: 1px solid #777;\n  padding: 5px;\n  margin-right: 10px;\n  margin-top: 10px;\n}\n\n.button-dark {\n  background: #222;\n  color: #fff;\n}\n\n.button-light {\n  background: #fff;\n  color: #222;\n}\n```\n\n</Sandpack>\n\n---\n\n### ツリーの一部でコンテクストの値を上書きする {/*overriding-context-for-a-part-of-the-tree*/}\n\n異なる値を持つプロバイダでツリーの一部をラップすることにより、その部分のコンテクストを上書きできます。\n\n```js {3,5}\n<ThemeContext value=\"dark\">\n  ...\n  <ThemeContext value=\"light\">\n    <Footer />\n  </ThemeContext>\n  ...\n</ThemeContext>\n```\n\nプロバイダのネストと上書きは必要なだけ行うことができます。\n\n<Recipes titleText=\"Examples of overriding context\">\n\n#### テーマの上書き {/*overriding-a-theme*/}\n\nこの例では、`Footer` の*内部*にあるボタンは、外部にあるボタン（`\"dark\"`）とは違うコンテクスト値（`\"light\"`）を受け取ります。\n\n<Sandpack>\n\n```js\nimport { createContext, useContext } from 'react';\n\nconst ThemeContext = createContext(null);\n\nexport default function MyApp() {\n  return (\n    <ThemeContext value=\"dark\">\n      <Form />\n    </ThemeContext>\n  )\n}\n\nfunction Form() {\n  return (\n    <Panel title=\"Welcome\">\n      <Button>Sign up</Button>\n      <Button>Log in</Button>\n      <ThemeContext value=\"light\">\n        <Footer />\n      </ThemeContext>\n    </Panel>\n  );\n}\n\nfunction Footer() {\n  return (\n    <footer>\n      <Button>Settings</Button>\n    </footer>\n  );\n}\n\nfunction Panel({ title, children }) {\n  const theme = useContext(ThemeContext);\n  const className = 'panel-' + theme;\n  return (\n    <section className={className}>\n      {title && <h1>{title}</h1>}\n      {children}\n    </section>\n  )\n}\n\nfunction Button({ children }) {\n  const theme = useContext(ThemeContext);\n  const className = 'button-' + theme;\n  return (\n    <button className={className}>\n      {children}\n    </button>\n  );\n}\n```\n\n```css\nfooter {\n  margin-top: 20px;\n  border-top: 1px solid #aaa;\n}\n\n.panel-light,\n.panel-dark {\n  border: 1px solid black;\n  border-radius: 4px;\n  padding: 20px;\n}\n.panel-light {\n  color: #222;\n  background: #fff;\n}\n\n.panel-dark {\n  color: #fff;\n  background: rgb(23, 32, 42);\n}\n\n.button-light,\n.button-dark {\n  border: 1px solid #777;\n  padding: 5px;\n  margin-right: 10px;\n  margin-top: 10px;\n}\n\n.button-dark {\n  background: #222;\n  color: #fff;\n}\n\n.button-light {\n  background: #fff;\n  color: #222;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### 自動的な見出しのネスト {/*automatically-nested-headings*/}\n\nコンテクストプロバイダをネストすることで、情報の「累積計算」ができます。この例では、`Section` コンポーネントはセクションのネストの深さを指定する `LevelContext` を管理しています。親セクションから `LevelContext` を読み取り、その数値に 1 を加えた `LevelContext` の値を子に提供します。その結果、`Heading` コンポーネントは、`<h1>`、`<h2>`、`<h3>`、... のうちどのタグを使用するかを、自身の外側にネストされている `Section` コンポーネントの数に従って、自動的に決定できます。\n\nこの例の[詳細なウォークスルーはこちら](/learn/passing-data-deeply-with-context)。\n\n<Sandpack>\n\n```js\nimport Heading from './Heading.js';\nimport Section from './Section.js';\n\nexport default function Page() {\n  return (\n    <Section>\n      <Heading>Title</Heading>\n      <Section>\n        <Heading>Heading</Heading>\n        <Heading>Heading</Heading>\n        <Heading>Heading</Heading>\n        <Section>\n          <Heading>Sub-heading</Heading>\n          <Heading>Sub-heading</Heading>\n          <Heading>Sub-heading</Heading>\n          <Section>\n            <Heading>Sub-sub-heading</Heading>\n            <Heading>Sub-sub-heading</Heading>\n            <Heading>Sub-sub-heading</Heading>\n          </Section>\n        </Section>\n      </Section>\n    </Section>\n  );\n}\n```\n\n```js src/Section.js\nimport { useContext } from 'react';\nimport { LevelContext } from './LevelContext.js';\n\nexport default function Section({ children }) {\n  const level = useContext(LevelContext);\n  return (\n    <section className=\"section\">\n      <LevelContext value={level + 1}>\n        {children}\n      </LevelContext>\n    </section>\n  );\n}\n```\n\n```js src/Heading.js\nimport { useContext } from 'react';\nimport { LevelContext } from './LevelContext.js';\n\nexport default function Heading({ children }) {\n  const level = useContext(LevelContext);\n  switch (level) {\n    case 0:\n      throw Error('Heading must be inside a Section!');\n    case 1:\n      return <h1>{children}</h1>;\n    case 2:\n      return <h2>{children}</h2>;\n    case 3:\n      return <h3>{children}</h3>;\n    case 4:\n      return <h4>{children}</h4>;\n    case 5:\n      return <h5>{children}</h5>;\n    case 6:\n      return <h6>{children}</h6>;\n    default:\n      throw Error('Unknown level: ' + level);\n  }\n}\n```\n\n```js src/LevelContext.js\nimport { createContext } from 'react';\n\nexport const LevelContext = createContext(0);\n```\n\n```css\n.section {\n  padding: 10px;\n  margin: 5px;\n  border-radius: 5px;\n  border: 1px solid #aaa;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n---\n\n### オブジェクトや関数を渡すときの再レンダーの最適化 {/*optimizing-re-renders-when-passing-objects-and-functions*/}\n\nコンテクストを介して、オブジェクトや関数を含んだどんな値も渡すことができます。\n\n```js [[2, 10, \"{ currentUser, login }\"]] \nfunction MyApp() {\n  const [currentUser, setCurrentUser] = useState(null);\n\n  function login(response) {\n    storeCredentials(response.credentials);\n    setCurrentUser(response.user);\n  }\n\n  return (\n    <AuthContext value={{ currentUser, login }}>\n      <Page />\n    </AuthContext>\n  );\n}\n```\n\nここでは、<CodeStep step={2}>コンテクストの値</CodeStep>は、2 つのプロパティを持つ JavaScript オブジェクトであり、そのうちの 1 つは関数になります。`MyApp` が再レンダーされるたびに（例えば、ページ遷移など）、これは*異なる*関数の入った*異なる*オブジェクトを指すため、React はツリーにある `useContext(AuthContext)` を呼び出しているすべてのコンポーネントを再レンダーしなければなりません。\n\n小規模なアプリでは、問題になりません。ですが、`currentUser` のような内部のデータが変更されていないなら、再レンダーする必要はありません。データが変わっていないという事実を React が最大限に活用できるように、`login` 関数を [`useCallback`](/reference/react/useCallback) でラップし、オブジェクトの生成を [`useMemo`](/reference/react/useMemo) でラップすることができます。これはパフォーマンスの最適化です：\n\n```js {6,9,11,14,17}\nimport { useCallback, useMemo } from 'react';\n\nfunction MyApp() {\n  const [currentUser, setCurrentUser] = useState(null);\n\n  const login = useCallback((response) => {\n    storeCredentials(response.credentials);\n    setCurrentUser(response.user);\n  }, []);\n\n  const contextValue = useMemo(() => ({\n    currentUser,\n    login\n  }), [currentUser, login]);\n\n  return (\n    <AuthContext value={contextValue}>\n      <Page />\n    </AuthContext>\n  );\n}\n```\n\nこの変更の結果、`MyApp` が再レンダーする必要があっても、`currentUser` が変更されていない限り、`useContext(AuthContext)` を呼び出しているコンポーネントを再レンダーする必要はなくなります。\n\n詳しくは [`useMemo`](/reference/react/useMemo#skipping-re-rendering-of-components) と [`useCallback`](/reference/react/useCallback#skipping-re-rendering-of-components) を参照してください。\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### プロバイダに渡した値が自分のコンポーネントから見えない {/*my-component-doesnt-see-the-value-from-my-provider*/}\n\nこれが起こる一般的な理由はいくつかあります：\n\n1. `useContext()` を呼び出しているコンポーネントと同じ（または下位の）コンポーネントで `<SomeContext>` をレンダーしている。`<SomeContext>` を `useContext()` を呼び出すコンポーネントの*外側かつ上位*に移動してください。\n2. コンポーネントを `<SomeContext>` でラップし忘れているか、ツリー内の思っているのとは違う場所に配置してしまっている。[React DevTools](/learn/react-developer-tools) を使ってツリー階層が正しいか確認してみてください。\n3. プロバイダコンポーネントから見た `SomeContext` と、利用側のコンポーネントから見た `SomeContext` が、ビルドツールの問題により 2 つの異なるオブジェクトになっている。これは例えば、シンボリックリンクを使用している場合などに発生します。これを確認するために、それらを `window.SomeContext1` や `window.SomeContext2` のようなグローバル変数に割り当て、コンソールで `window.SomeContext1 === window.SomeContext2` が成り立つか確認してみてください。もし同一でないなら、ビルドツールのレベルで、その問題を修正する必要があります。\n\n### 違うデフォルト値を指定しているのにコンテクストから常に `undefined` が返ってくる {/*i-am-always-getting-undefined-from-my-context-although-the-default-value-is-different*/}\n\nツリーの中に `value` のないプロバイダがあるのかもしれません：\n\n```js {1,2}\n// 🚩 Doesn't work: no value prop\n<ThemeContext>\n   <Button />\n</ThemeContext>\n```\n\n`value` を指定し忘れた場合、それは `value={undefined}` を渡すのと同じです。\n\nまた、誤って props として違う名前を使っているのかもしれません：\n\n```js {1,2}\n// 🚩 Doesn't work: prop should be called \"value\"\n<ThemeContext theme={theme}>\n   <Button />\n</ThemeContext>\n```\n\nどちらの場合も、React からの警告がコンソールに表示されるはずです。修正するには、props として `value` を使います：\n\n```js {1,2}\n// ✅ Passing the value prop\n<ThemeContext value={theme}>\n   <Button />\n</ThemeContext>\n```\n\n[createContext(defaultValue) で指定するデフォルト値](#specifying-a-fallback-default-value)は、ツリーの上側に**一致するプロバイダが一切存在しない場合**にのみ使用されることに注意してください。親のツリーのどこかに `<SomeContext value={undefined}>` のようなコンポーネントがあれば、`useContext(SomeContext)` を呼び出すコンポーネントはコンテクスト値として*その* `undefined` を受け取ります。\n"
  },
  {
    "path": "src/content/reference/react/useDebugValue.md",
    "content": "---\ntitle: useDebugValue\n---\n\n<Intro>\n\n`useDebugValue` は React フックであり、[React DevTools](/learn/react-developer-tools) でカスタムフックにラベルを追加できるようにします。\n\n```js\nuseDebugValue(value, format?)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useDebugValue(value, format?)` {/*usedebugvalue*/}\n\n[カスタムフック](/learn/reusing-logic-with-custom-hooks)のトップレベルで `useDebugValue` を呼び出して、読みやすいデバッグ値を表示します。\n\n```js\nimport { useDebugValue } from 'react';\n\nfunction useOnlineStatus() {\n  // ...\n  useDebugValue(isOnline ? 'Online' : 'Offline');\n  // ...\n}\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `value`: React DevTools に表示したい値。任意の型が使えます。\n* **省略可能** `format`: フォーマッタ関数。コンポーネントがインスペクト（inspect, 調査）されると、React DevTools は `value` を引数としてフォーマッタ関数を呼び出し、返されたフォーマット済みの値（任意の型が使えます）を表示します。フォーマッタ関数を指定しない場合、元の `value` 自体が表示されます。\n\n#### 返り値 {/*returns*/}\n\n`useDebugValue` は何も返しません。\n\n## 使用法 {/*usage*/}\n\n### カスタムフックにラベルを追加する {/*adding-a-label-to-a-custom-hook*/}\n\n[カスタムフック](/learn/reusing-logic-with-custom-hooks)のトップレベルで `useDebugValue` を呼び出し、[React DevTools](/learn/react-developer-tools) に対して読みやすい<CodeStep step={1}>デバッグ値</CodeStep>を表示します。\n\n```js [[1, 5, \"isOnline ? 'Online' : 'Offline'\"]]\nimport { useDebugValue } from 'react';\n\nfunction useOnlineStatus() {\n  // ...\n  useDebugValue(isOnline ? 'Online' : 'Offline');\n  // ...\n}\n```\n\nこれにより、`useOnlineStatus` を呼び出すコンポーネントをインスペクトすると `OnlineStatus: \"Online\"` のようなラベルが付きます。\n\n![React DevTools がデバッグ値を表示するスクリーンショット](/images/docs/react-devtools-usedebugvalue.png)\n\n`useDebugValue` の呼び出しがない場合、元のデータ（この例では `true`）のみが表示されます。\n\n<Sandpack>\n\n```js\nimport { useOnlineStatus } from './useOnlineStatus.js';\n\nfunction StatusBar() {\n  const isOnline = useOnlineStatus();\n  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;\n}\n\nexport default function App() {\n  return <StatusBar />;\n}\n```\n\n```js src/useOnlineStatus.js active\nimport { useSyncExternalStore, useDebugValue } from 'react';\n\nexport function useOnlineStatus() {\n  const isOnline = useSyncExternalStore(subscribe, () => navigator.onLine, () => true);\n  useDebugValue(isOnline ? 'Online' : 'Offline');\n  return isOnline;\n}\n\nfunction subscribe(callback) {\n  window.addEventListener('online', callback);\n  window.addEventListener('offline', callback);\n  return () => {\n    window.removeEventListener('online', callback);\n    window.removeEventListener('offline', callback);\n  };\n}\n```\n\n</Sandpack>\n\n<Note>\n\nすべてのカスタムフックにデバッグ値を追加しないでください。デバッグ値が最も有用なのは、共有ライブラリの一部であり、内部のデータ構造が複雑で調査が難しいカスタムフックです。\n\n</Note>\n\n---\n\n### デバッグ値のフォーマットを遅延させる {/*deferring-formatting-of-a-debug-value*/}\n\n`useDebugValue` の第 2 引数としてフォーマッタ関数も渡すことができます：\n\n```js [[1, 1, \"date\", 18], [2, 1, \"date.toDateString()\"]]\nuseDebugValue(date, date => date.toDateString());\n```\n\nあなたのフォーマッタ関数は、<CodeStep step={1}>デバッグ値</CodeStep>を引数として受け取り、<CodeStep step={2}>フォーマットされた表示値</CodeStep>を返す必要があります。コンポーネントがインスペクトされると、React DevTools はこの関数を呼び出し、その結果を表示します。\n\nこれにより、コンポーネントが実際にインスペクトされない限り、コストがかかる可能性があるフォーマットロジックを実行することを回避できます。例えば、`date` が Date 値の場合、レンダーの度に `toDateString()` を呼び出すことを回避できます。"
  },
  {
    "path": "src/content/reference/react/useDeferredValue.md",
    "content": "---\ntitle: useDeferredValue\n---\n\n<Intro>\n\n`useDeferredValue` は、UI の一部の更新を遅延させるための React フックです。\n\n```js\nconst deferredValue = useDeferredValue(value)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useDeferredValue(value, initialValue?)` {/*usedeferredvalue*/}\n\nコンポーネントのトップレベルで `useDeferredValue` を呼び出し、その値の遅延されたバージョンを取得します。\n\n```js\nimport { useState, useDeferredValue } from 'react';\n\nfunction SearchPage() {\n  const [query, setQuery] = useState('');\n  const deferredQuery = useDeferredValue(query);\n  // ...\n}\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `value`: 遅延させたい値。任意の型を持つことができます。\n* **省略可能** `initialValue`: コンポーネントの初回レンダー時に使用する値です。このオプションが省略された場合、初回レンダー時には代わりにレンダーできる `value` の前のバージョンがないことになるので、`useDeferredValue` は値の遅延を行いません。\n\n\n#### 返り値 {/*returns*/}\n\n- `currentValue`: 初回レンダー時には、`initialValue` を遅延された値として返すか、もしくはあなたが渡した値をそのまま返します。更新時には、React はまず古い値で再レンダーを試み（つまり返り値は古い値になり）、次に新しい値でバックグラウンドで再レンダーを試みます（返り値は更新後の値になります）。\n\n#### 注意点 {/*caveats*/}\n\n- 更新がトランジション内で発生する場合、更新は既に遅延されているため、`useDeferredValue` は常に新しい `value` のみを返し、新たな遅延レンダーを発生させません。\n\n- `useDeferredValue` に渡す値は、プリミティブな値（文字列や数値など）またはレンダーの外部で作成されたオブジェクトであるべきです。レンダー中に新しいオブジェクトを作成してすぐにそれを `useDeferredValue` に渡すと、それは毎回のレンダーで異なるものとなるため、不必要なバックグラウンドでの再レンダーを引き起こします。\n\n- `useDeferredValue` が（[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) で比較して）異なる値を受け取ると、（前回の値を使用する）現在のレンダーに加えて、新しい値でバックグラウンドで再レンダーをスケジュールします。バックグラウンドでの再レンダーは中断可能です。`value` に別の更新があると、React はバックグラウンドでの再レンダーを最初からやり直します。例えば、ユーザが素早く入力を行い、それがその値を受け取るチャートコンポーネントが再レンダーできるよりも速かった場合、チャートはユーザがタイプを止めたあとに再表示されることになります。\n\n- `useDeferredValue` は [`<Suspense>`](/reference/react/Suspense) と統合されています。新しい値によって引き起こされるバックグラウンド更新が UI をサスペンドした場合でも、ユーザにフォールバックは表示されません。データが読み込まれるまで、以前の遅延された値が表示され続けます。\n\n- `useDeferredValue` 自体に余計なネットワークリクエストを防ぐ仕組みはありません。\n\n- `useDeferredValue` 自体による固定の遅延はありません。React が元の再レンダーを終えるとすぐに、新しい遅延値でのバックグラウンド再レンダー作業を開始します。イベント（タイピングなど）による更新は、バックグラウンドの再レンダーを中断して優先的に処理されます。\n\n- `useDeferredValue` によるバックグラウンドの再レンダーは、画面にコミットされるまでエフェクトを実行しません。バックグラウンドの再レンダーがサスペンドする場合、その再レンダーに対応するエフェクトはデータの読み込みと UI の更新の後に実行されます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 新しいコンテンツが読み込まれている間、古いコンテンツを表示する {/*showing-stale-content-while-fresh-content-is-loading*/}\n\nUI の一部の更新を遅延させるために、コンポーネントのトップレベルで `useDeferredValue` を呼び出します。\n\n```js [[1, 5, \"query\"], [2, 5, \"deferredQuery\"]]\nimport { useState, useDeferredValue } from 'react';\n\nfunction SearchPage() {\n  const [query, setQuery] = useState('');\n  const deferredQuery = useDeferredValue(query);\n  // ...\n}\n```\n\n初回レンダー時には、<CodeStep step={2}>遅延される値</CodeStep>は関数に渡した<CodeStep step={1}>値</CodeStep>と同じになります。\n\n更新時には、<CodeStep step={2}>遅延される値</CodeStep>は最新の<CodeStep step={1}>値</CodeStep>から「遅れ」ます。具体的には、React はまず遅延値を*更新せずに*再レンダーを行い、次に新たに受け取った値でバックグラウンドでの再レンダーを試みます。\n\n**例を使って、これが役立つ場面を見ていきましょう**。\n\n<Note>\n\nこの例では、以下のようなサスペンス (Suspense) 対応のデータソースを使用していることを前提としています。\n\n- [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) や [Next.js](https://nextjs.org/docs/app/getting-started/fetching-data#with-suspense) のようなサスペンス対応のフレームワークでのデータフェッチ\n- [`lazy`](/reference/react/lazy) を用いたコンポーネントコードの遅延ロード\n- [`use`](/reference/react/use) を用いたプロミス (Promise) からの値の読み取り\n\n[サスペンスとその制限について詳しく学ぶ。](/reference/react/Suspense)\n\n</Note>\n\n\nこの例では、`SearchResults` コンポーネントは検索結果をフェッチする間[サスペンド](/reference/react/Suspense#displaying-a-fallback-while-content-is-loading)します。`\"a\"` を入力し、結果を待ってから `\"ab\"` に書き換えてみてください。`\"a\"` の検索結果が、ロード中フォールバックに置換されてしまいます。\n\n<Sandpack>\n\n```js src/App.js\nimport { Suspense, useState } from 'react';\nimport SearchResults from './SearchResults.js';\n\nexport default function App() {\n  const [query, setQuery] = useState('');\n  return (\n    <>\n      <label>\n        Search albums:\n        <input value={query} onChange={e => setQuery(e.target.value)} />\n      </label>\n      <Suspense fallback={<h2>Loading...</h2>}>\n        <SearchResults query={query} />\n      </Suspense>\n    </>\n  );\n}\n```\n\n```js src/SearchResults.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function SearchResults({ query }) {\n  if (query === '') {\n    return null;\n  }\n  const albums = use(fetchData(`/search?q=${query}`));\n  if (albums.length === 0) {\n    return <p>No matches for <i>\"{query}\"</i></p>;\n  }\n  return (\n    <ul>\n      {albums.map(album => (\n        <li key={album.id}>\n          {album.title} ({album.year})\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url.startsWith('/search?q=')) {\n    return await getSearchResults(url.slice('/search?q='.length));\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getSearchResults(query) {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 1000);\n  });\n\n  const allAlbums = [{\n    id: 13,\n    title: 'Let It Be',\n    year: 1970\n  }, {\n    id: 12,\n    title: 'Abbey Road',\n    year: 1969\n  }, {\n    id: 11,\n    title: 'Yellow Submarine',\n    year: 1969\n  }, {\n    id: 10,\n    title: 'The Beatles',\n    year: 1968\n  }, {\n    id: 9,\n    title: 'Magical Mystery Tour',\n    year: 1967\n  }, {\n    id: 8,\n    title: 'Sgt. Pepper\\'s Lonely Hearts Club Band',\n    year: 1967\n  }, {\n    id: 7,\n    title: 'Revolver',\n    year: 1966\n  }, {\n    id: 6,\n    title: 'Rubber Soul',\n    year: 1965\n  }, {\n    id: 5,\n    title: 'Help!',\n    year: 1965\n  }, {\n    id: 4,\n    title: 'Beatles For Sale',\n    year: 1964\n  }, {\n    id: 3,\n    title: 'A Hard Day\\'s Night',\n    year: 1964\n  }, {\n    id: 2,\n    title: 'With The Beatles',\n    year: 1963\n  }, {\n    id: 1,\n    title: 'Please Please Me',\n    year: 1963\n  }];\n\n  const lowerQuery = query.trim().toLowerCase();\n  return allAlbums.filter(album => {\n    const lowerTitle = album.title.toLowerCase();\n    return (\n      lowerTitle.startsWith(lowerQuery) ||\n      lowerTitle.indexOf(' ' + lowerQuery) !== -1\n    )\n  });\n}\n```\n\n```css\ninput { margin: 10px; }\n```\n\n</Sandpack>\n\nこの代わりに一般的に使われる UI パターンは、結果リストの更新を*遅延*させて、新しい結果が準備できるまで前の結果を表示し続けるというものです。遅延バージョンのクエリ文字列を渡すために `useDeferredValue` を呼び出します：\n\n```js {3,11}\nexport default function App() {\n  const [query, setQuery] = useState('');\n  const deferredQuery = useDeferredValue(query);\n  return (\n    <>\n      <label>\n        Search albums:\n        <input value={query} onChange={e => setQuery(e.target.value)} />\n      </label>\n      <Suspense fallback={<h2>Loading...</h2>}>\n        <SearchResults query={deferredQuery} />\n      </Suspense>\n    </>\n  );\n}\n```\n\n`query` の方はすぐに更新されるため、入力フィールドは新しい値を表示します。しかし、`deferredQuery` はデータが読み込まれるまで前の値を保持するため、`SearchResults` はしばらく古い結果を表示します。\n\n以下の例で `\"a\"` を入力し、結果が読み込まれるのを待ち、次に入力欄を `\"ab\"` に書き換えてみてください。新しい結果が読み込まれるまでは、サスペンスによるフォールバックの代わりに古い結果リストが表示され続けることに着目してください。\n\n<Sandpack>\n\n```js src/App.js\nimport { Suspense, useState, useDeferredValue } from 'react';\nimport SearchResults from './SearchResults.js';\n\nexport default function App() {\n  const [query, setQuery] = useState('');\n  const deferredQuery = useDeferredValue(query);\n  return (\n    <>\n      <label>\n        Search albums:\n        <input value={query} onChange={e => setQuery(e.target.value)} />\n      </label>\n      <Suspense fallback={<h2>Loading...</h2>}>\n        <SearchResults query={deferredQuery} />\n      </Suspense>\n    </>\n  );\n}\n```\n\n```js src/SearchResults.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function SearchResults({ query }) {\n  if (query === '') {\n    return null;\n  }\n  const albums = use(fetchData(`/search?q=${query}`));\n  if (albums.length === 0) {\n    return <p>No matches for <i>\"{query}\"</i></p>;\n  }\n  return (\n    <ul>\n      {albums.map(album => (\n        <li key={album.id}>\n          {album.title} ({album.year})\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url.startsWith('/search?q=')) {\n    return await getSearchResults(url.slice('/search?q='.length));\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getSearchResults(query) {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 1000);\n  });\n\n  const allAlbums = [{\n    id: 13,\n    title: 'Let It Be',\n    year: 1970\n  }, {\n    id: 12,\n    title: 'Abbey Road',\n    year: 1969\n  }, {\n    id: 11,\n    title: 'Yellow Submarine',\n    year: 1969\n  }, {\n    id: 10,\n    title: 'The Beatles',\n    year: 1968\n  }, {\n    id: 9,\n    title: 'Magical Mystery Tour',\n    year: 1967\n  }, {\n    id: 8,\n    title: 'Sgt. Pepper\\'s Lonely Hearts Club Band',\n    year: 1967\n  }, {\n    id: 7,\n    title: 'Revolver',\n    year: 1966\n  }, {\n    id: 6,\n    title: 'Rubber Soul',\n    year: 1965\n  }, {\n    id: 5,\n    title: 'Help!',\n    year: 1965\n  }, {\n    id: 4,\n    title: 'Beatles For Sale',\n    year: 1964\n  }, {\n    id: 3,\n    title: 'A Hard Day\\'s Night',\n    year: 1964\n  }, {\n    id: 2,\n    title: 'With The Beatles',\n    year: 1963\n  }, {\n    id: 1,\n    title: 'Please Please Me',\n    year: 1963\n  }];\n\n  const lowerQuery = query.trim().toLowerCase();\n  return allAlbums.filter(album => {\n    const lowerTitle = album.title.toLowerCase();\n    return (\n      lowerTitle.startsWith(lowerQuery) ||\n      lowerTitle.indexOf(' ' + lowerQuery) !== -1\n    )\n  });\n}\n```\n\n```css\ninput { margin: 10px; }\n```\n\n</Sandpack>\n\n<DeepDive>\n\n#### 値の遅延は内部でどのように動作するのか？ {/*how-does-deferring-a-value-work-under-the-hood*/}\n\n値の遅延は 2 つのステップで行われると考えることができます：\n\n1. **まず React は、`query` は新しい値 (`\"ab\"`) だが `deferredQuery` は古い値 (`\"a\"`) のまま、という状態で再レンダーを試みます**。結果リストに渡す側の値である `deferredQuery` は*遅延されて*おり、`query` の値に「遅れて」ついていきます。\n\n2. **バックグラウンドで React は、`query` と `deferredQuery` の両方が `\"ab\"` に更新された状態で再レンダーを試みます**。この再レンダーが完了した場合、React はそれを画面に表示します。しかし、それがサスペンドした（`\"ab\"` の結果がまだ読み込まれていない）場合、React はこのレンダーの試行を放棄し、データが読み込まれた後にこの再レンダーを再試行します。ユーザは、データが準備できるまで古い遅延された値を見続けます。\n\n遅延された「バックグラウンド」レンダーは中断可能です。例えば、再度入力欄にタイプを行うと、React はそれを放棄し、新しい値でやり直します。React は常に最後に提供された値を使用します。\n\n各キーストロークごとにネットワークリクエストは発生していることに注意してください。ここで（準備ができるまで）遅延させているのは結果の表示であり、ネットワークリクエスト自体ではありません。ユーザが入力を続けた場合でも、各キーストロークのレスポンスはキャッシュされているため、Backspace を押すと即座に反応し、再度のフェッチは起きません。\n\n</DeepDive>\n\n---\n\n### コンテンツが古いことをインジケータで表示する {/*indicating-that-the-content-is-stale*/}\n\n上記の例では、最新のクエリの結果リストがまだロード中であることを示すインジケータがありません。新しい結果がロードされるのに時間がかかると、ユーザの混乱を招く可能性があります。結果リストが最新のクエリと一致していないことをユーザに明確に伝えるために、古い結果リストが表示されているときに視覚的なインジケータを追加することができます：\n\n```js {2}\n<div style={{\n  opacity: query !== deferredQuery ? 0.5 : 1,\n}}>\n  <SearchResults query={deferredQuery} />\n</div>\n```\n\nこれにより、入力を開始すると直ちに、古い結果リストがわずかに暗くなり、新しい結果リストがロードされるまでその状態が続きます。以下の例のように、暗くなるのを遅延させる CSS トランジションを追加することで、徐々に変化するように感じさせることもできます。\n\n<Sandpack>\n\n```js src/App.js\nimport { Suspense, useState, useDeferredValue } from 'react';\nimport SearchResults from './SearchResults.js';\n\nexport default function App() {\n  const [query, setQuery] = useState('');\n  const deferredQuery = useDeferredValue(query);\n  const isStale = query !== deferredQuery;\n  return (\n    <>\n      <label>\n        Search albums:\n        <input value={query} onChange={e => setQuery(e.target.value)} />\n      </label>\n      <Suspense fallback={<h2>Loading...</h2>}>\n        <div style={{\n          opacity: isStale ? 0.5 : 1,\n          transition: isStale ? 'opacity 0.2s 0.2s linear' : 'opacity 0s 0s linear'\n        }}>\n          <SearchResults query={deferredQuery} />\n        </div>\n      </Suspense>\n    </>\n  );\n}\n```\n\n```js src/SearchResults.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function SearchResults({ query }) {\n  if (query === '') {\n    return null;\n  }\n  const albums = use(fetchData(`/search?q=${query}`));\n  if (albums.length === 0) {\n    return <p>No matches for <i>\"{query}\"</i></p>;\n  }\n  return (\n    <ul>\n      {albums.map(album => (\n        <li key={album.id}>\n          {album.title} ({album.year})\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url.startsWith('/search?q=')) {\n    return await getSearchResults(url.slice('/search?q='.length));\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getSearchResults(query) {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 1000);\n  });\n\n  const allAlbums = [{\n    id: 13,\n    title: 'Let It Be',\n    year: 1970\n  }, {\n    id: 12,\n    title: 'Abbey Road',\n    year: 1969\n  }, {\n    id: 11,\n    title: 'Yellow Submarine',\n    year: 1969\n  }, {\n    id: 10,\n    title: 'The Beatles',\n    year: 1968\n  }, {\n    id: 9,\n    title: 'Magical Mystery Tour',\n    year: 1967\n  }, {\n    id: 8,\n    title: 'Sgt. Pepper\\'s Lonely Hearts Club Band',\n    year: 1967\n  }, {\n    id: 7,\n    title: 'Revolver',\n    year: 1966\n  }, {\n    id: 6,\n    title: 'Rubber Soul',\n    year: 1965\n  }, {\n    id: 5,\n    title: 'Help!',\n    year: 1965\n  }, {\n    id: 4,\n    title: 'Beatles For Sale',\n    year: 1964\n  }, {\n    id: 3,\n    title: 'A Hard Day\\'s Night',\n    year: 1964\n  }, {\n    id: 2,\n    title: 'With The Beatles',\n    year: 1963\n  }, {\n    id: 1,\n    title: 'Please Please Me',\n    year: 1963\n  }];\n\n  const lowerQuery = query.trim().toLowerCase();\n  return allAlbums.filter(album => {\n    const lowerTitle = album.title.toLowerCase();\n    return (\n      lowerTitle.startsWith(lowerQuery) ||\n      lowerTitle.indexOf(' ' + lowerQuery) !== -1\n    )\n  });\n}\n```\n\n```css\ninput { margin: 10px; }\n```\n\n</Sandpack>\n\n---\n\n### UI の一部分の再レンダーを遅延させる {/*deferring-re-rendering-for-a-part-of-the-ui*/}\n\n`useDeferredValue` をパフォーマンス最適化として適用することもできます。これは、UI の一部の再レンダーに時間がかかり、それを最適化する簡単な方法がないが、それによって UI の他の部分がブロックされるのを防ぎたい、という場合に有用です。\n\nテキストフィールドと、各キーストロークごとに再レンダーするコンポーネント（チャートや長いリストなど）があると想像してみてください：\n\n```js\nfunction App() {\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input value={text} onChange={e => setText(e.target.value)} />\n      <SlowList text={text} />\n    </>\n  );\n}\n```\n\nまずは `SlowList` を最適化して、props が同じ場合は再レンダーをスキップするようにします。これを行うには、[`memo` でラップします](/reference/react/memo#skipping-re-rendering-when-props-are-unchanged)。\n\n```js {1,3}\nconst SlowList = memo(function SlowList({ text }) {\n  // ...\n});\n```\n\nしかし、これが有用なのは `SlowList` の props が前回のレンダー時と*同一*である場合のみです。現在直面している問題は、props が*異なっており*現に別の見た目の結果を表示しないといけない場合に遅い、ということです。\n\n具体的には、主なパフォーマンスの問題は、入力フィールドに何かを入力するたびに、`SlowList` が新しい props を受け取り、そのツリー全体を再レンダーするため、入力がもたつく感じになるということです。このようなケースでは、`useDeferredValue` を使うことで、入力フィールドの更新（速くなければならない）を結果リストの更新（遅くても許される）よりも優先することが可能です。\n\n```js {3,7}\nfunction App() {\n  const [text, setText] = useState('');\n  const deferredText = useDeferredValue(text);\n  return (\n    <>\n      <input value={text} onChange={e => setText(e.target.value)} />\n      <SlowList text={deferredText} />\n    </>\n  );\n}\n```\n\nこれによって `SlowList` の再レンダー自体を高速化しているわけではありません。しかし、React に対して、リストの再レンダーは優先度を下げても良いと伝えることで、キーストロークをブロックしないようにします。リストは入力フィールドの「後を追う」形になり、その後「追いつきます」。元と同様に、React はできるだけ早くリストを更新しようとしますが、ユーザの入力をブロックすることはなくなります。\n\n<Recipes titleText=\"useDeferredValue と最適化されていない再レンダーの違い\" titleId=\"examples\">\n\n#### 遅延されたリストの再レンダー {/*deferred-re-rendering-of-the-list*/}\n\nこの例では、`useDeferredValue` が入力をレスポンシブに保つ方法を確認できるよう、`SlowList` コンポーネントの各アイテムが**人為的に遅延**させられています。入力フィールドに入力してみて、スムースに入力できる一方で、リストがそれを「追いかける」様子を確認してください。\n\n<Sandpack>\n\n```js\nimport { useState, useDeferredValue } from 'react';\nimport SlowList from './SlowList.js';\n\nexport default function App() {\n  const [text, setText] = useState('');\n  const deferredText = useDeferredValue(text);\n  return (\n    <>\n      <input value={text} onChange={e => setText(e.target.value)} />\n      <SlowList text={deferredText} />\n    </>\n  );\n}\n```\n\n```js {expectedErrors: {'react-compiler': [19, 20]}} src/SlowList.js\nimport { memo } from 'react';\n\nconst SlowList = memo(function SlowList({ text }) {\n  // Log once. The actual slowdown is inside SlowItem.\n  console.log('[ARTIFICIALLY SLOW] Rendering 250 <SlowItem />');\n\n  let items = [];\n  for (let i = 0; i < 250; i++) {\n    items.push(<SlowItem key={i} text={text} />);\n  }\n  return (\n    <ul className=\"items\">\n      {items}\n    </ul>\n  );\n});\n\nfunction SlowItem({ text }) {\n  let startTime = performance.now();\n  while (performance.now() - startTime < 1) {\n    // Do nothing for 1 ms per item to emulate extremely slow code\n  }\n\n  return (\n    <li className=\"item\">\n      Text: {text}\n    </li>\n  )\n}\n\nexport default SlowList;\n```\n\n```css\n.items {\n  padding: 0;\n}\n\n.item {\n  list-style: none;\n  display: block;\n  height: 40px;\n  padding: 5px;\n  margin-top: 10px;\n  border-radius: 4px;\n  border: 1px solid #aaa;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### 最適化されていないリストの再レンダー {/*unoptimized-re-rendering-of-the-list*/}\n\nこの例では、`SlowList` コンポーネントの各アイテムに**人為的な遅延**がありますが、`useDeferredValue` がありません。\n\n入力フィールドに入力すると非常にもたつく感じがすることに気づくでしょう。これは、`useDeferredValue` がないと、各キーストロークが強制的に、リスト全体を即座にかつ中断不可能な方法で再レンダーするからです。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport SlowList from './SlowList.js';\n\nexport default function App() {\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input value={text} onChange={e => setText(e.target.value)} />\n      <SlowList text={text} />\n    </>\n  );\n}\n```\n\n```js {expectedErrors: {'react-compiler': [19, 20]}} src/SlowList.js\nimport { memo } from 'react';\n\nconst SlowList = memo(function SlowList({ text }) {\n  // Log once. The actual slowdown is inside SlowItem.\n  console.log('[ARTIFICIALLY SLOW] Rendering 250 <SlowItem />');\n\n  let items = [];\n  for (let i = 0; i < 250; i++) {\n    items.push(<SlowItem key={i} text={text} />);\n  }\n  return (\n    <ul className=\"items\">\n      {items}\n    </ul>\n  );\n});\n\nfunction SlowItem({ text }) {\n  let startTime = performance.now();\n  while (performance.now() - startTime < 1) {\n    // Do nothing for 1 ms per item to emulate extremely slow code\n  }\n\n  return (\n    <li className=\"item\">\n      Text: {text}\n    </li>\n  )\n}\n\nexport default SlowList;\n```\n\n```css\n.items {\n  padding: 0;\n}\n\n.item {\n  list-style: none;\n  display: block;\n  height: 40px;\n  padding: 5px;\n  margin-top: 10px;\n  border-radius: 4px;\n  border: 1px solid #aaa;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n<Pitfall>\n\nこの最適化が動作するには `SlowList` が [`memo`](/reference/react/memo) でラップされていることが必要です。これは、`text` が変更されるたびに、React が親コンポーネント側を素早く再レンダーできるようにする必要があるからです。その再レンダー中には、`deferredText` はまだ前の値になっており、`SlowList` は（props が変更されていないので）再レンダーをスキップできます。[`memo`](/reference/react/memo) がなければ、`SlowList` は常に再レンダーされてしまい、最適化の意味が失われてしまいます。\n\n</Pitfall>\n\n<DeepDive>\n\n#### 値の遅延とデバウンスやスロットリングとの違い {/*how-is-deferring-a-value-different-from-debouncing-and-throttling*/}\n\nこのようなシナリオにおいて使ったことがあるかもしれない、よくある最適化手法が 2 つあります。\n\n- *デバウンス (debounce)* は、ユーザが入力を（例えば 1 秒間）停止するまでリストの更新を待つという意味です。\n- *スロットリング (throttling)* は、一定の間隔（例えば最大で 1 秒に 1 回）でリストを更新するという意味です。\n\nこれらの手法は一部のケースで役立ちますが、`useDeferredValue` は React 自体と深く統合されており、ユーザのデバイスに適応するため、レンダーの最適化により適しています。\n\nデバウンスやスロットリングとは異なり、遅延される時間を固定で選ぶ必要はありません。ユーザのデバイスが速い場合（例えばパワフルなラップトップ）、遅延された再レンダーはほぼ即座に行われるため、気づかれません。ユーザのデバイスが遅い場合、リストはデバイスの遅さに比例するように入力から「遅れ」ていきます。\n\nまた、デバウンスやスロットリングとは異なり、`useDeferredValue` による遅延された再レンダーはデフォルトで中断可能です。これは、React が大きなリストを再レンダーしている途中で、ユーザが別のキーストロークを行うと、React はその再レンダーを放棄し、キーストロークを処理し、再びバックグラウンドでレンダーをやり直せるという意味です。対照的に、デバウンスやスロットリングの動作は*ブロッキング*であるため、やはり不快な体験を生み出します。それらはレンダーがキーストロークをブロックするタイミングを単に遅らせているに過ぎないのです。\n\n最適化しようとしている作業がレンダーの最中に行われるものでない場合、デバウンスとスロットリングは依然として有用です。例えば、ネットワークリクエストの回数を減らすことができます。これらの手法を一緒に使用することもできます。\n\n</DeepDive>\n"
  },
  {
    "path": "src/content/reference/react/useEffect.md",
    "content": "---\ntitle: useEffect\n---\n\n<Intro>\n\n`useEffect` は、[コンポーネントを外部システムと同期させる](/learn/synchronizing-with-effects)ための React フックです。\n\n```js\nuseEffect(setup, dependencies?)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useEffect(setup, dependencies?)` {/*useeffect*/}\n\nコンポーネントのトップレベルで `useEffect` を呼び出して、エフェクト (Effect) を宣言します。\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nfunction ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [serverUrl, roomId]);\n  // ...\n}\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `setup`: エフェクトのロジックが記述された関数です。このセットアップ関数は、オプションで*クリーンアップ*関数を返すことができます。[コンポーネントがコミットされる](/learn/render-and-commit#step-3-react-commits-changes-to-the-dom)と、React はセットアップ関数を実行します。依存配列 (dependencies) が変更された次のコミット時には、React はまず古い値を使ってクリーンアップ関数（あれば）を実行し、次に新しい値を使ってセットアップ関数を実行します。コンポーネントが DOM から削除された後、React はクリーンアップ関数を最後にもう一度実行します。\n \n* **省略可能** `dependencies`: `setup` コード内で参照されるすべてのリアクティブな値のリストです。リアクティブな値には、props、state、コンポーネント本体に直接宣言されたすべての変数および関数が含まれます。リンタが [React 用に設定されている場合](/learn/editor-setup#linting)、すべてのリアクティブな値が依存値として正しく指定されているか確認できます。依存値のリストは要素数が一定である必要があり、`[dep1, dep2, dep3]` のようにインラインで記述する必要があります。React は、[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) を使った比較で、それぞれの依存値を以前の値と比較します。この引数を省略すると、エフェクトはコンポーネントの毎回のコミット後に再実行されます。[依存値の配列を渡す場合と空の配列を渡す場合、および何も渡さない場合の違い](#examples-dependencies)を確認してください。\n\n#### 返り値 {/*returns*/}\n\n`useEffect` は `undefined` を返します。\n\n#### 注意点 {/*caveats*/}\n\n* `useEffect` はフックであるため、**コンポーネントのトップレベル**やカスタムフック内でのみ呼び出すことができます。ループや条件文の中で呼び出すことはできません。これが必要な場合は、新しいコンポーネントを抽出し、その中に state を移動させてください。\n\n* 外部システムと同期する必要が**ない場合**、[エフェクトはおそらく不要です](/learn/you-might-not-need-an-effect)。\n\n* Strict Mode が有効な場合、React は本物のセットアップの前に、**開発時専用のセットアップ+クリーンアップサイクルを 1 回追加で実行**します。これは、クリーンアップロジックがセットアップロジックと鏡のように対応しており、セットアップで行われたことを停止または元に戻していることを保証するためのストレステストです。問題が発生した場合は、[クリーンアップ関数を実装します](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development)。\n\n* 依存配列の一部にコンポーネント内で定義されたオブジェクトや関数がある場合、**エフェクトが必要以上に再実行される**可能性があります。これを修正するには、[オブジェクト型](#removing-unnecessary-object-dependencies)および[関数型](#removing-unnecessary-function-dependencies)の不要な依存値を削除します。また、エフェクトの外部に [state の更新](#updating-state-based-on-previous-state-from-an-effect)や[非リアクティブなロジック](#reading-the-latest-props-and-state-from-an-effect)を抽出することもできます。\n\n* エフェクトがユーザ操作（クリックなど）によって引き起こされたものでない場合、React は通常、ブラウザが**新しい画面を描画した後にエフェクトを実行**します。あなたのエフェクトが（ツールチップの配置など）何か視覚的な作業を行っており遅延が目立つ場合（ちらつくなど）、`useEffect` を [`useLayoutEffect` に置き換えてください](/reference/react/useLayoutEffect)。\n\n* エフェクトがユーザ操作（クリックなど）によって引き起こされた場合、**React はブラウザが更新後の画面を描画する前にエフェクトを実行することがあります**。これによりエフェクトの結果がイベントシステムに見えることが保証されます。これは通常は期待通りに動作します。しかし、`alert()` のように描画後まで作業を遅らせる必要がある場合は、`setTimeout` を使用できます。詳細については、[reactwg/react-18/128](https://github.com/reactwg/react-18/discussions/128) を参照してください。\n\n* エフェクトがユーザ操作（クリックなど）によって引き起こされた場合、**React はエフェクト内で起きた state 更新を処理する前に、ブラウザに画面を再描画させる**ことがあります。これは通常は期待通りに動作します。しかし、ブラウザによる画面の再描画をブロックしなければならない場合は、`useEffect` を [`useLayoutEffect`](/reference/react/useLayoutEffect) に置き換える必要があります。\n\n* エフェクトは**クライアント上でのみ実行されます**。サーバレンダリング中には実行されません。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 外部システムへの接続 {/*connecting-to-an-external-system*/}\n\nコンポーネントによっては自身がページに表示されている間、ネットワーク、何らかのブラウザ API、またはサードパーティライブラリとの接続を維持する必要があるものがあります。これらのシステムは React によって制御されていないため、*外部 (external)* のものです。\n\n[コンポーネントを外部システムに接続する](/learn/synchronizing-with-effects)には、コンポーネントのトップレベルで `useEffect` を呼び出します。\n\n```js [[1, 8, \"const connection = createConnection(serverUrl, roomId);\"], [1, 9, \"connection.connect();\"], [2, 11, \"connection.disconnect();\"], [3, 13, \"[serverUrl, roomId]\"]]\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nfunction ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useEffect(() => {\n  \tconst connection = createConnection(serverUrl, roomId);\n    connection.connect();\n  \treturn () => {\n      connection.disconnect();\n  \t};\n  }, [serverUrl, roomId]);\n  // ...\n}\n```\n\n`useEffect` には 2 つの引数を渡す必要があります。\n\n1. システムに接続する<CodeStep step={1}>セットアップコード</CodeStep>を含む*セットアップ関数*。\n   - そのシステムから切断する<CodeStep step={2}>クリーンアップコード</CodeStep>を含む*クリーンアップ関数*を返す必要があります。\n2. これらの関数内で使用されるコンポーネントからのすべての値を含んだ<CodeStep step={3}>依存値のリスト</CodeStep>。\n\n**React は必要に応じてセットアップ関数とクリーンアップ関数を呼び出し、これは複数回行われることがあります。**\n\n1. コンポーネントがページに追加（*マウント*）されると、<CodeStep step={1}>セットアップコード</CodeStep>が実行されます。\n2. <CodeStep step={3}>依存値</CodeStep>が変更された上でコンポーネントがコミットされる度に：\n   - まず、古い props と state で<CodeStep step={2}>クリーンアップコード</CodeStep>が実行されます。\n   - 次に、新しい props と state で<CodeStep step={1}>セットアップコード</CodeStep>が実行されます。\n3. コンポーネントがページから削除（*アンマウント*）されると、最後に<CodeStep step={2}>クリーンアップコード</CodeStep>が実行されます。\n\n**上記の例でこのシーケンスを説明しましょう。**\n\n上記の `ChatRoom` コンポーネントがページに追加されると、`serverUrl` と `roomId` の初期値を使ってチャットルームに接続します。`serverUrl` または `roomId` がコミットの結果として変更される場合（例えば、ユーザがドロップダウンで別のチャットルームを選択した場合）、あなたのエフェクトは*以前のルームから切断し、次のルームに接続します*。`ChatRoom` コンポーネントがページから削除されると、あなたのエフェクトは最後の切断を行います。\n\n**[バグを見つけ出すために](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed)、開発中には React は<CodeStep step={1}>セットアップ</CodeStep>と<CodeStep step={2}>クリーンアップ</CodeStep>を、<CodeStep step={1}>セットアップ</CodeStep>の前に 1 回余分に実行します**。これは、エフェクトのロジックが正しく実装されていることを確認するストレステストです。これが目に見える問題を引き起こす場合、クリーンアップ関数に一部のロジックが欠けています。クリーンアップ関数は、セットアップ関数が行っていたことを停止ないし元に戻す必要があります。基本ルールとして、ユーザはセットアップが一度しか呼ばれていない（本番環境の場合）か、*セットアップ* → *クリーンアップ* → *セットアップ*のシーケンス（開発環境の場合）で呼ばれているかを区別できないようにする必要があります。[一般的な解決法を参照してください](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development)。\n\n**[各エフェクトを独立したプロセスとして記述](/learn/lifecycle-of-reactive-effects#each-effect-represents-a-separate-synchronization-process)するようにし、[一回のセットアップ／クリーンアップのサイクルだけを考える](/learn/lifecycle-of-reactive-effects#thinking-from-the-effects-perspective)ようにしてください**。コンポーネントが現在マウント、更新、アンマウントのどれを行っているかを考慮すべきではありません。セットアップロジックが正しくクリーンアップロジックと「対応」されることで、エフェクトはセットアップとクリーンアップを必要に応じて何度実行しても問題が起きない、堅牢なものとなります。\n\n<Note>\n\nエフェクトは、コンポーネントを外部システム（チャットサービスのようなもの）と[同期させるために使います](/learn/synchronizing-with-effects)。ここでいう*外部システム*とは、React が制御していないコードの一部で、たとえば以下のようなものです。\n\n* <CodeStep step={1}>[`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval)</CodeStep> と <CodeStep step={2}>[`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval)</CodeStep> で管理されるタイマー。\n* <CodeStep step={1}>[`window.addEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)</CodeStep> と <CodeStep step={2}>[`window.removeEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener)</CodeStep> を使ったイベントサブスクリプション。\n* <CodeStep step={1}>`animation.start()`</CodeStep> と <CodeStep step={2}>`animation.reset()`</CodeStep> のような API を持つサードパーティのアニメーションライブラリ。\n\n**外部システムに接続していない場合は、[恐らくエフェクトは不要です](/learn/you-might-not-need-an-effect)**。\n\n</Note>\n\n<Recipes titleText=\"外部システムへの接続例\" titleId=\"examples-connecting\">\n\n#### チャットサーバへの接続 {/*connecting-to-a-chat-server*/}\n\nこの例では、`ChatRoom` コンポーネントがエフェクトを使って `chat.js` で定義された外部システムに接続しています。\"Open chat\" を押すと `ChatRoom` コンポーネントが表示されます。このサンドボックスは開発モードで実行されているため、[こちらで説明されているように](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed)、接続と切断のサイクルが 1 回追加で発生します。`roomId` と `serverUrl` をドロップダウンと入力欄で変更して、エフェクトがチャットに再接続する様子を確認してみてください。\"Close chat\" を押すと、エフェクトが最後の 1 回の切断作業を行います。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nfunction ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [roomId, serverUrl]);\n\n  return (\n    <>\n      <label>\n        Server URL:{' '}\n        <input\n          value={serverUrl}\n          onChange={e => setServerUrl(e.target.value)}\n        />\n      </label>\n      <h1>Welcome to the {roomId} room!</h1>\n    </>\n  );\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Close chat' : 'Open chat'}\n      </button>\n      {show && <hr />}\n      {show && <ChatRoom roomId={roomId} />}\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n<Solution />\n\n#### グローバルなブラウザイベントへのリッスン {/*listening-to-a-global-browser-event*/}\n\nこの例では、外部システムはブラウザの DOM 自体です。イベントリスナは通常 JSX で指定しますが、この方法ではグローバルな [`window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) オブジェクトへはリッスンすることはできません。エフェクトを使うことで、`window` オブジェクトに接続し、そのイベントをリッスンできます。`pointermove` イベントにリッスンすることで、カーソル（または指）の位置を追跡し、赤い点をそれに合わせて移動させることができます。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function App() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n\n  useEffect(() => {\n    function handleMove(e) {\n      setPosition({ x: e.clientX, y: e.clientY });\n    }\n    window.addEventListener('pointermove', handleMove);\n    return () => {\n      window.removeEventListener('pointermove', handleMove);\n    };\n  }, []);\n\n  return (\n    <div style={{\n      position: 'absolute',\n      backgroundColor: 'pink',\n      borderRadius: '50%',\n      opacity: 0.6,\n      transform: `translate(${position.x}px, ${position.y}px)`,\n      pointerEvents: 'none',\n      left: -20,\n      top: -20,\n      width: 40,\n      height: 40,\n    }} />\n  );\n}\n```\n\n```css\nbody {\n  min-height: 300px;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### アニメーションのトリガ {/*triggering-an-animation*/}\n\nこの例では、外部システムは `animation.js` に書かれているアニメーションライブラリです。これは、DOM ノードを引数に取る `FadeInAnimation` という JavaScript クラスを提供し、アニメーションを制御するための `start()` および `stop()` メソッドを公開しています。このコンポーネントは、背後にある DOM ノードにアクセスするために [ref を使います](/learn/manipulating-the-dom-with-refs)。エフェクトは ref から DOM ノードを読み取り、コンポーネントが表示されたときにそのノードのアニメーションを自動的に開始します。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect, useRef } from 'react';\nimport { FadeInAnimation } from './animation.js';\n\nfunction Welcome() {\n  const ref = useRef(null);\n\n  useEffect(() => {\n    const animation = new FadeInAnimation(ref.current);\n    animation.start(1000);\n    return () => {\n      animation.stop();\n    };\n  }, []);\n\n  return (\n    <h1\n      ref={ref}\n      style={{\n        opacity: 0,\n        color: 'white',\n        padding: 50,\n        textAlign: 'center',\n        fontSize: 50,\n        backgroundImage: 'radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%)'\n      }}\n    >\n      Welcome\n    </h1>\n  );\n}\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Remove' : 'Show'}\n      </button>\n      <hr />\n      {show && <Welcome />}\n    </>\n  );\n}\n```\n\n```js src/animation.js\nexport class FadeInAnimation {\n  constructor(node) {\n    this.node = node;\n  }\n  start(duration) {\n    this.duration = duration;\n    if (this.duration === 0) {\n      // Jump to end immediately\n      this.onProgress(1);\n    } else {\n      this.onProgress(0);\n      // Start animating\n      this.startTime = performance.now();\n      this.frameId = requestAnimationFrame(() => this.onFrame());\n    }\n  }\n  onFrame() {\n    const timePassed = performance.now() - this.startTime;\n    const progress = Math.min(timePassed / this.duration, 1);\n    this.onProgress(progress);\n    if (progress < 1) {\n      // We still have more frames to paint\n      this.frameId = requestAnimationFrame(() => this.onFrame());\n    }\n  }\n  onProgress(progress) {\n    this.node.style.opacity = progress;\n  }\n  stop() {\n    cancelAnimationFrame(this.frameId);\n    this.startTime = null;\n    this.frameId = null;\n    this.duration = 0;\n  }\n}\n```\n\n```css\nlabel, button { display: block; margin-bottom: 20px; }\nhtml, body { min-height: 300px; }\n```\n\n</Sandpack>\n\n<Solution />\n\n#### モーダルダイアログの制御 {/*controlling-a-modal-dialog*/}\n\nこの例では、外部システムはブラウザの DOM です。`ModalDialog` コンポーネントは [`<dialog>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) 要素をレンダーします。`isOpen` プロパティを [`showModal()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal) および [`close()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/close) メソッド呼び出しに同期させるためにエフェクトを使用しています。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport ModalDialog from './ModalDialog.js';\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShow(true)}>\n        Open dialog\n      </button>\n      <ModalDialog isOpen={show}>\n        Hello there!\n        <br />\n        <button onClick={() => {\n          setShow(false);\n        }}>Close</button>\n      </ModalDialog>\n    </>\n  );\n}\n```\n\n```js src/ModalDialog.js active\nimport { useEffect, useRef } from 'react';\n\nexport default function ModalDialog({ isOpen, children }) {\n  const ref = useRef();\n\n  useEffect(() => {\n    if (!isOpen) {\n      return;\n    }\n    const dialog = ref.current;\n    dialog.showModal();\n    return () => {\n      dialog.close();\n    };\n  }, [isOpen]);\n\n  return <dialog ref={ref}>{children}</dialog>;\n}\n```\n\n```css\nbody {\n  min-height: 300px;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### 要素の可視性の追跡 {/*tracking-element-visibility*/}\n\nこの例では、外部システムは再びブラウザの DOM です。`App` コンポーネントは長いリストを表示し、その後に `Box` コンポーネントを表示し、もう一度長いリストを表示します。リストを下にスクロールしてみてください。`Box` コンポーネントの全体が完全にビューポート内に表示されると、背景色が黒に変わることに気付くでしょう。これを実装するために、`Box` コンポーネントはエフェクトを使用して [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) を管理しています。このブラウザ API は、DOM 要素がビューポートに表示されているときに通知してくれるものです。\n\n<Sandpack>\n\n```js\nimport Box from './Box.js';\n\nexport default function App() {\n  return (\n    <>\n      <LongSection />\n      <Box />\n      <LongSection />\n      <Box />\n      <LongSection />\n    </>\n  );\n}\n\nfunction LongSection() {\n  const items = [];\n  for (let i = 0; i < 50; i++) {\n    items.push(<li key={i}>Item #{i} (keep scrolling)</li>);\n  }\n  return <ul>{items}</ul>\n}\n```\n\n```js src/Box.js active\nimport { useRef, useEffect } from 'react';\n\nexport default function Box() {\n  const ref = useRef(null);\n\n  useEffect(() => {\n    const div = ref.current;\n    const observer = new IntersectionObserver(entries => {\n      const entry = entries[0];\n      if (entry.isIntersecting) {\n        document.body.style.backgroundColor = 'black';\n        document.body.style.color = 'white';\n      } else {\n        document.body.style.backgroundColor = 'white';\n        document.body.style.color = 'black';\n      }\n    }, {\n       threshold: 1.0\n    });\n    observer.observe(div);\n    return () => {\n      observer.disconnect();\n    }\n  }, []);\n\n  return (\n    <div ref={ref} style={{\n      margin: 20,\n      height: 100,\n      width: 100,\n      border: '2px solid black',\n      backgroundColor: 'blue'\n    }} />\n  );\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n---\n\n### カスタムフックにエフェクトをラップする {/*wrapping-effects-in-custom-hooks*/}\n\nエフェクトは[「避難ハッチ」](/learn/escape-hatches)です。React の外に出る必要があり、かつ特定のユースケースに対してより良い組み込みのソリューションがない場合に使用します。エフェクトを手で何度も書く必要があることに気付いたら、通常それは、あなたのコンポーネントが依存する共通の振る舞いのための[カスタムフック](/learn/reusing-logic-with-custom-hooks)を抽出する必要があるというサインです。\n\n例えば、この `useChatRoom` カスタムフックは、エフェクトのロジックをより宣言的な API の背後に「隠蔽」します。\n\n```js {1,11}\nfunction useChatRoom({ serverUrl, roomId }) {\n  useEffect(() => {\n    const options = {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId, serverUrl]);\n}\n```\n\nこの後で、任意のコンポーネントから以下のように使うことができます。\n\n```js {4-7}\nfunction ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useChatRoom({\n    roomId: roomId,\n    serverUrl: serverUrl\n  });\n  // ...\n```\n\nほかにも React のエコシステムには、さまざまな目的のための優れたカスタムフックが多数公開されています。\n\n[カスタムフックでエフェクトをラップする方法についてもっと学ぶ](/learn/reusing-logic-with-custom-hooks)\n\n<Recipes titleText=\"カスタムフックでエフェクトをラップする例\" titleId=\"examples-custom-hooks\">\n\n#### カスタム `useChatRoom` フック {/*custom-usechatroom-hook*/}\n\nこの例は、[これまでの例](#examples-connecting) のいずれかと同じですが、カスタムフックにロジックが抽出されています。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { useChatRoom } from './useChatRoom.js';\n\nfunction ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n\n  useChatRoom({\n    roomId: roomId,\n    serverUrl: serverUrl\n  });\n\n  return (\n    <>\n      <label>\n        Server URL:{' '}\n        <input\n          value={serverUrl}\n          onChange={e => setServerUrl(e.target.value)}\n        />\n      </label>\n      <h1>Welcome to the {roomId} room!</h1>\n    </>\n  );\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Close chat' : 'Open chat'}\n      </button>\n      {show && <hr />}\n      {show && <ChatRoom roomId={roomId} />}\n    </>\n  );\n}\n```\n\n```js src/useChatRoom.js\nimport { useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nexport function useChatRoom({ serverUrl, roomId }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [roomId, serverUrl]);\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n<Solution />\n\n#### カスタム `useWindowListener` フック {/*custom-usewindowlistener-hook*/}\n\nこの例は、[前の例](#examples-connecting)の中の 1 つと同じですが、ロジックがカスタムフックに抽出されています。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { useWindowListener } from './useWindowListener.js';\n\nexport default function App() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n\n  useWindowListener('pointermove', (e) => {\n    setPosition({ x: e.clientX, y: e.clientY });\n  });\n\n  return (\n    <div style={{\n      position: 'absolute',\n      backgroundColor: 'pink',\n      borderRadius: '50%',\n      opacity: 0.6,\n      transform: `translate(${position.x}px, ${position.y}px)`,\n      pointerEvents: 'none',\n      left: -20,\n      top: -20,\n      width: 40,\n      height: 40,\n    }} />\n  );\n}\n```\n\n```js src/useWindowListener.js\nimport { useState, useEffect } from 'react';\n\nexport function useWindowListener(eventType, listener) {\n  useEffect(() => {\n    window.addEventListener(eventType, listener);\n    return () => {\n      window.removeEventListener(eventType, listener);\n    };\n  }, [eventType, listener]);\n}\n```\n\n```css\nbody {\n  min-height: 300px;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### カスタム `useIntersectionObserver` フック {/*custom-useintersectionobserver-hook*/}\n\nこの例は、[前の例](#examples-connecting)の中の 1 つと同じですが、ロジックが部分的にカスタムフックに抽出されています。\n\n<Sandpack>\n\n```js\nimport Box from './Box.js';\n\nexport default function App() {\n  return (\n    <>\n      <LongSection />\n      <Box />\n      <LongSection />\n      <Box />\n      <LongSection />\n    </>\n  );\n}\n\nfunction LongSection() {\n  const items = [];\n  for (let i = 0; i < 50; i++) {\n    items.push(<li key={i}>Item #{i} (keep scrolling)</li>);\n  }\n  return <ul>{items}</ul>\n}\n```\n\n```js src/Box.js active\nimport { useRef, useEffect } from 'react';\nimport { useIntersectionObserver } from './useIntersectionObserver.js';\n\nexport default function Box() {\n  const ref = useRef(null);\n  const isIntersecting = useIntersectionObserver(ref);\n\n  useEffect(() => {\n   if (isIntersecting) {\n      document.body.style.backgroundColor = 'black';\n      document.body.style.color = 'white';\n    } else {\n      document.body.style.backgroundColor = 'white';\n      document.body.style.color = 'black';\n    }\n  }, [isIntersecting]);\n\n  return (\n    <div ref={ref} style={{\n      margin: 20,\n      height: 100,\n      width: 100,\n      border: '2px solid black',\n      backgroundColor: 'blue'\n    }} />\n  );\n}\n```\n\n```js src/useIntersectionObserver.js\nimport { useState, useEffect } from 'react';\n\nexport function useIntersectionObserver(ref) {\n  const [isIntersecting, setIsIntersecting] = useState(false);\n\n  useEffect(() => {\n    const div = ref.current;\n    const observer = new IntersectionObserver(entries => {\n      const entry = entries[0];\n      setIsIntersecting(entry.isIntersecting);\n    }, {\n       threshold: 1.0\n    });\n    observer.observe(div);\n    return () => {\n      observer.disconnect();\n    }\n  }, [ref]);\n\n  return isIntersecting;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n---\n\n### 非 React ウィジェットの制御 {/*controlling-a-non-react-widget*/}\n\n外部システムをあなたのコンポーネントの props や state に同期させたいことがあります。\n\n例えば、React を使っていないサードパーティ製のマップウィジェットやビデオプレーヤコンポーネントがある場合、エフェクトを使ってそちらのメソッドを呼び出し、そちらの状態を React コンポーネントの現在 state に合わせることができます。以下では、`map-widget.js` に定義された `MapWidget` クラスのインスタンスをエフェクトが作成します。`Map` コンポーネントの props である `zoomLevel` が変更されると、エフェクトがクラスインスタンスの `setZoom()` を呼び出して、同期を保ちます。\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"leaflet\": \"1.9.1\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"remarkable\": \"2.0.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js src/App.js\nimport { useState } from 'react';\nimport Map from './Map.js';\n\nexport default function App() {\n  const [zoomLevel, setZoomLevel] = useState(0);\n  return (\n    <>\n      Zoom level: {zoomLevel}x\n      <button onClick={() => setZoomLevel(zoomLevel + 1)}>+</button>\n      <button onClick={() => setZoomLevel(zoomLevel - 1)}>-</button>\n      <hr />\n      <Map zoomLevel={zoomLevel} />\n    </>\n  );\n}\n```\n\n```js src/Map.js active\nimport { useRef, useEffect } from 'react';\nimport { MapWidget } from './map-widget.js';\n\nexport default function Map({ zoomLevel }) {\n  const containerRef = useRef(null);\n  const mapRef = useRef(null);\n\n  useEffect(() => {\n    if (mapRef.current === null) {\n      mapRef.current = new MapWidget(containerRef.current);\n    }\n\n    const map = mapRef.current;\n    map.setZoom(zoomLevel);\n  }, [zoomLevel]);\n\n  return (\n    <div\n      style={{ width: 200, height: 200 }}\n      ref={containerRef}\n    />\n  );\n}\n```\n\n```js src/map-widget.js\nimport 'leaflet/dist/leaflet.css';\nimport * as L from 'leaflet';\n\nexport class MapWidget {\n  constructor(domNode) {\n    this.map = L.map(domNode, {\n      zoomControl: false,\n      doubleClickZoom: false,\n      boxZoom: false,\n      keyboard: false,\n      scrollWheelZoom: false,\n      zoomAnimation: false,\n      touchZoom: false,\n      zoomSnap: 0.1\n    });\n    L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {\n      maxZoom: 19,\n      attribution: '© OpenStreetMap'\n    }).addTo(this.map);\n    this.map.setView([0, 0], 0);\n  }\n  setZoom(level) {\n    this.map.setZoom(level);\n  }\n}\n```\n\n```css\nbutton { margin: 5px; }\n```\n\n</Sandpack>\n\nこの例では、クリーンアップ関数は必要ありません。なぜなら、`MapWidget` クラスは自身に渡された DOM ノードのみを管理しているためです。React の `Map` コンポーネントがツリーから削除された後、DOM ノードと `MapWidget` クラスインスタンスは、ブラウザの JavaScript エンジンによって自動的にガベージコレクションされます。\n\n---\n\n### エフェクトを使ったデータフェッチ {/*fetching-data-with-effects*/}\n\nエフェクトを使って、コンポーネントに必要なデータをフェッチ（fetch, 取得）することができます。ただし[フレームワークを使用している場合](/learn/creating-a-react-app#full-stack-frameworks)は、エフェクトを自力で記述するよりも、フレームワークのデータフェッチメカニズムを使用する方がはるかに効率的であることに注意してください。\n\nエフェクトを使って自力でデータをフェッチしたい場合は、以下のようなコードを書くことになります。\n\n```js\nimport { useState, useEffect } from 'react';\nimport { fetchBio } from './api.js';\n\nexport default function Page() {\n  const [person, setPerson] = useState('Alice');\n  const [bio, setBio] = useState(null);\n\n  useEffect(() => {\n    let ignore = false;\n    setBio(null);\n    fetchBio(person).then(result => {\n      if (!ignore) {\n        setBio(result);\n      }\n    });\n    return () => {\n      ignore = true;\n    };\n  }, [person]);\n\n  // ...\n```\n\n`ignore` 変数に注目してください。これは `false` で初期化され、クリーンアップ時に `true` に設定されます。これにより、[コードが \"競合状態 (race condition)\" に悩まされない](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect)ようになります。ネットワークレスポンスは、送信した順序と異なる順序で届くことがあることに注意しましょう。\n\n<Sandpack>\n\n{/* TODO(@poteto) - investigate potential false positives in react compiler validation */}\n```js {expectedErrors: {'react-compiler': [9]}} src/App.js\nimport { useState, useEffect } from 'react';\nimport { fetchBio } from './api.js';\n\nexport default function Page() {\n  const [person, setPerson] = useState('Alice');\n  const [bio, setBio] = useState(null);\n  useEffect(() => {\n    let ignore = false;\n    setBio(null);\n    fetchBio(person).then(result => {\n      if (!ignore) {\n        setBio(result);\n      }\n    });\n    return () => {\n      ignore = true;\n    }\n  }, [person]);\n\n  return (\n    <>\n      <select value={person} onChange={e => {\n        setPerson(e.target.value);\n      }}>\n        <option value=\"Alice\">Alice</option>\n        <option value=\"Bob\">Bob</option>\n        <option value=\"Taylor\">Taylor</option>\n      </select>\n      <hr />\n      <p><i>{bio ?? 'Loading...'}</i></p>\n    </>\n  );\n}\n```\n\n```js src/api.js hidden\nexport async function fetchBio(person) {\n  const delay = person === 'Bob' ? 2000 : 200;\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve('This is ' + person + '’s bio.');\n    }, delay);\n  })\n}\n```\n\n</Sandpack>\n\nまた、[`async` / `await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) 構文を使って書き直すこともできますが、この場合でもクリーンアップ関数を渡す必要があります。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState, useEffect } from 'react';\nimport { fetchBio } from './api.js';\n\nexport default function Page() {\n  const [person, setPerson] = useState('Alice');\n  const [bio, setBio] = useState(null);\n  useEffect(() => {\n    async function startFetching() {\n      setBio(null);\n      const result = await fetchBio(person);\n      if (!ignore) {\n        setBio(result);\n      }\n    }\n\n    let ignore = false;\n    startFetching();\n    return () => {\n      ignore = true;\n    }\n  }, [person]);\n\n  return (\n    <>\n      <select value={person} onChange={e => {\n        setPerson(e.target.value);\n      }}>\n        <option value=\"Alice\">Alice</option>\n        <option value=\"Bob\">Bob</option>\n        <option value=\"Taylor\">Taylor</option>\n      </select>\n      <hr />\n      <p><i>{bio ?? 'Loading...'}</i></p>\n    </>\n  );\n}\n```\n\n```js src/api.js hidden\nexport async function fetchBio(person) {\n  const delay = person === 'Bob' ? 2000 : 200;\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve('This is ' + person + '’s bio.');\n    }, delay);\n  })\n}\n```\n\n</Sandpack>\n\nエフェクト内で直接データフェッチを書くとコードの繰り返しが増え、キャッシュやサーバレンダリングといった最適化を後から追加することが難しくなります。[独自の、あるいはコミュニティがメンテナンスしているカスタムフックを使う方が簡単です](/learn/reusing-logic-with-custom-hooks#when-to-use-custom-hooks)。\n\n<DeepDive>\n\n#### エフェクトでのデータ取得に代わる良い方法は？ {/*what-are-good-alternatives-to-data-fetching-in-effects*/}\n\n特に完全にクライアントサイドのアプリにおいては、エフェクトの中で `fetch` コールを書くことは[データフェッチの一般的な方法](https://www.robinwieruch.de/react-hooks-fetch-data/)です。しかし、これは非常に手作業頼りのアプローチであり、大きな欠点があります。\n\n- **エフェクトはサーバ上では動作しません**。これは、サーバレンダリングされた初期 HTML にはデータのないローディング中という表示のみが含まれてしまうことを意味します。クライアントのコンピュータは、すべての JavaScript をダウンロードし、アプリをレンダーした後になってやっと、今度はデータを読み込む必要もあるということに気付くことになります。これはあまり効率的ではありません。\n- **エフェクトで直接データフェッチを行うと、「ネットワークのウォーターフォール（滝）」を作成しやすくなります**。親コンポーネントをレンダーし、それが何かデータをフェッチし、それによって子コンポーネントをレンダーし、今度はそれが何かデータのフェッチを開始する、といった具合です。ネットワークがあまり速くない場合、これはすべてのデータを並行で取得するよりもかなり遅くなります。\n- **エフェクト内で直接データフェッチするということはおそらくデータをプリロードもキャッシュもしていないということです**。例えば、コンポーネントがアンマウントされた後に再びマウントされる場合、データを再度取得する必要があります。\n- **人にとって書きやすいコードになりません**。[競合状態](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect)のようなバグを起こさないように `fetch` コールを書こうとすると、かなりのボイラープレートコードが必要です。\n\n上記の欠点は、マウント時にデータをフェッチするのであれば、React に限らずどのライブラリを使う場合でも当てはまる内容です。ルーティングと同様、データフェッチの実装も上手にやろうとすると一筋縄ではいきません。私たちは以下のアプローチをお勧めします。\n\n- **[フレームワーク](/learn/creating-a-react-app#full-stack-frameworks)を使用している場合、組み込みのデータフェッチ機構を使用してください**。モダンな React フレームワークには、効率的で上記の欠点がないデータフェッチ機構が統合されています。\n- **それ以外の場合は、クライアントサイドキャッシュの使用や構築を検討してください**。一般的なオープンソースのソリューションには、[TanStack Query](https://tanstack.com/query/latest/)、[useSWR](https://swr.vercel.app/)、および [React Router 6.4+](https://beta.reactrouter.com/en/main/start/overview) が含まれます。自分でソリューションを構築することもできます。その場合、エフェクトを内部で使用しつつ、リクエストの重複排除、レスポンスのキャッシュ、ネットワークのウォーターフォールを回避するためのロジック（データのプリロードやルーティング部へのデータ要求の巻き上げ）を追加することになります。\n\nこれらのアプローチがどちらも適合しない場合は、引き続きエフェクト内で直接データをフェッチすることができます。\n\n</DeepDive>\n\n---\n\n### リアクティブな依存配列の指定 {/*specifying-reactive-dependencies*/}\n\n**エフェクトの依存配列は、自分で「選ぶ」たぐいの物ではないことに注意してください**。エフェクトのコードによって使用されるすべての<CodeStep step={2}>リアクティブな値</CodeStep>は、依存値として宣言されなければなりません。エフェクトの依存値のリストは、周囲のコードによって決定されます。\n\n```js [[2, 1, \"roomId\"], [2, 2, \"serverUrl\"], [2, 5, \"serverUrl\"], [2, 5, \"roomId\"], [2, 8, \"serverUrl\"], [2, 8, \"roomId\"]]\nfunction ChatRoom({ roomId }) { // This is a reactive value\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // This is a reactive value too\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId); // This Effect reads these reactive values\n    connection.connect();\n    return () => connection.disconnect();\n  }, [serverUrl, roomId]); // ✅ So you must specify them as dependencies of your Effect\n  // ...\n}\n```\n\n`serverUrl` または `roomId` が変更されると、エフェクトは新しい値を使用してチャットに再接続します。\n\n**[リアクティブな値](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values)には、props と、コンポーネント内に直接宣言されたすべての変数および関数が含まれます**。`roomId` と `serverUrl` はリアクティブな値であるため、依存値のリストから削除することはできません。それらを省略しようとした場合、[React 用のリンタが正しく設定](/learn/editor-setup#linting)されていれば、リンタはこれを修正が必要な誤りであると指摘します。\n\n```js {8}\nfunction ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n  \n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, []); // 🔴 React Hook useEffect has missing dependencies: 'roomId' and 'serverUrl'\n  // ...\n}\n```\n\n**依存配列から何かを削除するには、[リンタに対し、それが依存値である*理由がない*ことを「証明」する必要があります](/learn/removing-effect-dependencies#removing-unnecessary-dependencies)**。例えば、`serverUrl` をコンポーネントの外に移動すれば、それがリアクティブな値ではなく、再レンダー時に変更されないものであることを証明できます。\n\n```js {1,8}\nconst serverUrl = 'https://localhost:1234'; // Not a reactive value anymore\n\nfunction ChatRoom({ roomId }) {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]); // ✅ All dependencies declared\n  // ...\n}\n```\n\nこれで `serverUrl` がリアクティブな値でなくなった（再レンダー時に変更されない）ため、依存配列に入れる必要がなくなりました。**エフェクトのコードがリアクティブな値を使用していない場合、その依存配列は空 (`[]`) であるべきです**。\n\n```js {1,2,9}\nconst serverUrl = 'https://localhost:1234'; // Not a reactive value anymore\nconst roomId = 'music'; // Not a reactive value anymore\n\nfunction ChatRoom() {\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, []); // ✅ All dependencies declared\n  // ...\n}\n```\n\n[空の依存配列で定義したエフェクト](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means)は、コンポーネントの props や state が変更された場合でも再実行されません。\n\n<Pitfall>\n\n既存のコードベースがある場合、以下のようにしてリンタを黙らせているエフェクトを見かけるかもしれません。\n\n```js {3-4}\nuseEffect(() => {\n  // ...\n  // 🔴 Avoid suppressing the linter like this:\n  // eslint-ignore-next-line react-hooks/exhaustive-deps\n}, []);\n```\n\n**依存配列がコードと一致しない場合、バグが発生するリスクが高くなります**。リンタを抑制することで、エフェクトが依存する値について React に「嘘」をつくことになります。[代わりにそれらが不要であることを証明してください](/learn/removing-effect-dependencies#removing-unnecessary-dependencies)。\n\n</Pitfall>\n\n<Recipes titleText=\"リアクティブな依存値の配列を渡す例\" titleId=\"examples-dependencies\">\n\n#### 依存配列を渡す {/*passing-a-dependency-array*/}\n\n依存配列を指定すると、エフェクトは**最初のコミット後*および*依存配列が変わった後の再コミット後に実行されます。**\n\n```js {3}\nuseEffect(() => {\n  // ...\n}, [a, b]); // Runs again if a or b are different\n```\n\n以下の例では、`serverUrl` と `roomId` は [リアクティブな値](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values)であるため、両方とも依存配列の中で指定する必要があります。その結果、ドロップダウンで別のルームを選択したり、サーバ URL の入力欄を編集したりすると、チャットが再接続されます。ただし、`message` はエフェクトで使用されていない（依存する値ではない）ため、メッセージを編集してもチャットが再接続されることはありません。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nfunction ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [serverUrl, roomId]);\n\n  return (\n    <>\n      <label>\n        Server URL:{' '}\n        <input\n          value={serverUrl}\n          onChange={e => setServerUrl(e.target.value)}\n        />\n      </label>\n      <h1>Welcome to the {roomId} room!</h1>\n      <label>\n        Your message:{' '}\n        <input value={message} onChange={e => setMessage(e.target.value)} />\n      </label>\n    </>\n  );\n}\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n        <button onClick={() => setShow(!show)}>\n          {show ? 'Close chat' : 'Open chat'}\n        </button>\n      </label>\n      {show && <hr />}\n      {show && <ChatRoom roomId={roomId}/>}\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { margin-bottom: 10px; }\nbutton { margin-left: 5px; }\n```\n\n</Sandpack>\n\n<Solution />\n\n#### 空の依存配列を渡す {/*passing-an-empty-dependency-array*/}\n\nあなたのエフェクトがリアクティブな値を本当に使っていないのであれば、それは**初回のコミット後に**のみ実行されます。\n\n```js {3}\nuseEffect(() => {\n  // ...\n}, []); // Does not run again (except once in development)\n```\n\n**空の依存配列であっても、セットアップとクリーンアップは[開発中には 1 回余分に実行](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development)され、バグを見つけるのに役立ちます**。\n\n\n以下の例では、`serverUrl` と `roomId` の両方がハードコードされています。コンポーネントの外側で宣言されているため、これらはリアクティブな値ではなく、従って依存配列に入れる必要もありません。依存値のリストは空なので、再レンダー時にこのエフェクトが再実行されることもありません。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\nconst roomId = 'music';\n\nfunction ChatRoom() {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => connection.disconnect();\n  }, []);\n\n  return (\n    <>\n      <h1>Welcome to the {roomId} room!</h1>\n      <label>\n        Your message:{' '}\n        <input value={message} onChange={e => setMessage(e.target.value)} />\n      </label>\n    </>\n  );\n}\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShow(!show)}>\n        {show ? 'Close chat' : 'Open chat'}\n      </button>\n      {show && <hr />}\n      {show && <ChatRoom />}\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n\n#### 依存配列を渡さない {/*passing-no-dependency-array-at-all*/}\n\n依存配列自体をまったく渡さない場合、コンポーネントの**毎回のコミット後に**エフェクトが実行されます。\n\n```js {3}\nuseEffect(() => {\n  // ...\n}); // Always runs again\n```\n\nこの例では、`serverUrl` と `roomId` が変更されるとエフェクトが再実行され、それは理にかなっています。ただし、`message` が変更された場合でも*やはり*エフェクトは再実行され、それはおそらく望ましくありません。ですので通常は依存配列を指定します。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nfunction ChatRoom({ roomId }) {\n  const [serverUrl, setServerUrl] = useState('https://localhost:1234');\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }); // No dependency array at all\n\n  return (\n    <>\n      <label>\n        Server URL:{' '}\n        <input\n          value={serverUrl}\n          onChange={e => setServerUrl(e.target.value)}\n        />\n      </label>\n      <h1>Welcome to the {roomId} room!</h1>\n      <label>\n        Your message:{' '}\n        <input value={message} onChange={e => setMessage(e.target.value)} />\n      </label>\n    </>\n  );\n}\n\nexport default function App() {\n  const [show, setShow] = useState(false);\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n        <button onClick={() => setShow(!show)}>\n          {show ? 'Close chat' : 'Open chat'}\n        </button>\n      </label>\n      {show && <hr />}\n      {show && <ChatRoom roomId={roomId}/>}\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection(serverUrl, roomId) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { margin-bottom: 10px; }\nbutton { margin-left: 5px; }\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n---\n\n### エフェクト内で以前の state に基づいて state を更新する {/*updating-state-based-on-previous-state-from-an-effect*/}\n\nエフェクトから以前の state に基づいて state を更新したい場合、問題が発生するかもしれません。\n\n```js {6,9}\nfunction Counter() {\n  const [count, setCount] = useState(0);\n\n  useEffect(() => {\n    const intervalId = setInterval(() => {\n      setCount(count + 1); // You want to increment the counter every second...\n    }, 1000)\n    return () => clearInterval(intervalId);\n  }, [count]); // 🚩 ... but specifying `count` as a dependency always resets the interval.\n  // ...\n}\n```\n\n`count` はリアクティブな値なので、依存配列に指定する必要があります。ただし、このままでは `count` が変更されるたびに、エフェクトがクリーンアップとセットアップを繰り返すことになります。これは望ましくありません。\n\nこの問題を解決するには、`setCount` に [`c => c + 1` という state 更新用関数を渡します](/reference/react/useState#updating-state-based-on-the-previous-state)。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nexport default function Counter() {\n  const [count, setCount] = useState(0);\n\n  useEffect(() => {\n    const intervalId = setInterval(() => {\n      setCount(c => c + 1); // ✅ Pass a state updater\n    }, 1000);\n    return () => clearInterval(intervalId);\n  }, []); // ✅ Now count is not a dependency\n\n  return <h1>{count}</h1>;\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-top: 20px;\n  margin-bottom: 20px;\n}\n\nbody {\n  min-height: 150px;\n}\n```\n\n</Sandpack>\n\n`c => c + 1` を `count + 1` の代わりに渡すようになったので、[このエフェクトはもう `count` に依存する必要はありません](/learn/removing-effect-dependencies#are-you-reading-some-state-to-calculate-the-next-state)。この修正の結果、`count` が変化するたびにインターバルのクリーンアップとセットアップを行わなくてもよくなります。\n\n---\n\n\n### オブジェクト型の不要な依存値を削除する {/*removing-unnecessary-object-dependencies*/}\n\nエフェクトがレンダー中に作成されたオブジェクトや関数に依存している場合、必要以上にエフェクトが実行されてしまうことがあります。たとえば、このエフェクトは `options` オブジェクトが[レンダーごとに異なる](/learn/removing-effect-dependencies#does-some-reactive-value-change-unintentionally)ため、毎回のコミット後に再接続を行ってしまいます。\n\n```js {6-9,12,15}\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  const options = { // 🚩 This object is created from scratch on every re-render\n    serverUrl: serverUrl,\n    roomId: roomId\n  };\n\n  useEffect(() => {\n    const connection = createConnection(options); // It's used inside the Effect\n    connection.connect();\n    return () => connection.disconnect();\n  }, [options]); // 🚩 As a result, these dependencies are always different on a commit\n  // ...\n```\n\nレンダー中に新たに作成されたオブジェクトを依存値として使用しないでください。代わりに、エフェクトの中でオブジェクトを作成します：\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const options = {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n\n  return (\n    <>\n      <h1>Welcome to the {roomId} room!</h1>\n      <input value={message} onChange={e => setMessage(e.target.value)} />\n    </>\n  );\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom roomId={roomId} />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\nエフェクトの中で `options` オブジェクトを作成するようになったので、エフェクト自体は `roomId` 文字列にしか依存しません。\n\nこの修正により、入力フィールドに文字を入力してもチャットが再接続されることはなくなります。オブジェクトは再レンダーのたびに再作成されるのとは異なり、`roomId` のような文字列は別の値に設定しない限り変更されません。[依存値の削除に関する詳細を読む](/learn/removing-effect-dependencies)。\n\n---\n\n### 関数型の不要な依存値を削除する {/*removing-unnecessary-function-dependencies*/}\n\nエフェクトがレンダー中に作成されたオブジェクトや関数に依存している場合、必要以上にエフェクトが実行されてしまうことがあります。たとえば、このエフェクトは `createOptions` 関数が[レンダーごとに異なる](/learn/removing-effect-dependencies#does-some-reactive-value-change-unintentionally)ため、毎回のコミット後に再接続を行ってしまいます。\n\n```js {4-9,12,16}\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  function createOptions() { // 🚩 This function is created from scratch on every re-render\n    return {\n      serverUrl: serverUrl,\n      roomId: roomId\n    };\n  }\n\n  useEffect(() => {\n    const options = createOptions(); // It's used inside the Effect\n    const connection = createConnection();\n    connection.connect();\n    return () => connection.disconnect();\n  }, [createOptions]); // 🚩 As a result, these dependencies are always different on a commit\n  // ...\n```\n\n再レンダーのたびに新しい関数を作成すること、それ自体には問題はなく、最適化しようとする必要はありません。ただし、エフェクトの依存値としてそれを使用する場合、毎回のコミット後にエフェクトが再実行されてしまうことになります。\n\nレンダー中に作成された関数を依存値として使用することは避けてください。代わりに、エフェクトの内部で宣言するようにします。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\nimport { createConnection } from './chat.js';\n\nconst serverUrl = 'https://localhost:1234';\n\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    function createOptions() {\n      return {\n        serverUrl: serverUrl,\n        roomId: roomId\n      };\n    }\n\n    const options = createOptions();\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]);\n\n  return (\n    <>\n      <h1>Welcome to the {roomId} room!</h1>\n      <input value={message} onChange={e => setMessage(e.target.value)} />\n    </>\n  );\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <hr />\n      <ChatRoom roomId={roomId} />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nexport function createConnection({ serverUrl, roomId }) {\n  // A real implementation would actually connect to the server\n  return {\n    connect() {\n      console.log('✅ Connecting to \"' + roomId + '\" room at ' + serverUrl + '...');\n    },\n    disconnect() {\n      console.log('❌ Disconnected from \"' + roomId + '\" room at ' + serverUrl);\n    }\n  };\n}\n```\n\n```css\ninput { display: block; margin-bottom: 20px; }\nbutton { margin-left: 10px; }\n```\n\n</Sandpack>\n\n`createOptions` 関数をエフェクト内で定義するようにしたので、エフェクト自体は `roomId` 文字列にのみ依存することになります。この修正により、入力欄に入力してもチャットが再接続されなくなります。再レンダー時に再作成される関数とは異なり、`roomId` のような文字列は他の値に設定しない限り変更されません。[依存値の削除について詳しくはこちら](/learn/removing-effect-dependencies)。\n\n---\n\n### エフェクトから最新の props と state を読み取る {/*reading-the-latest-props-and-state-from-an-effect*/}\n\nデフォルトでは、エフェクトからリアクティブな値を読み取るときは、それを依存値として追加する必要があります。これにより、エフェクトはその値の変更に対して「反応」することが保証されます。ほとんどの依存値については、それが望む挙動です。\n\n**ただし、時には「反応」をせずに最新の props や state を エフェクト内から読み取りたいことがあるでしょう**。例えば、ショッピングカート内のアイテム数をページ訪問ごとに記録する場合を想像してみてください。\n\n```js {3}\nfunction Page({ url, shoppingCart }) {\n  useEffect(() => {\n    logVisit(url, shoppingCart.length);\n  }, [url, shoppingCart]); // ✅ All dependencies declared\n  // ...\n}\n```\n\n**`url` の変更ごとに新しいページ訪問を記録したいが、`shoppingCart` の変更のみでは記録したくない場合はどうすればいいのでしょうか？** [リアクティブルール](#specifying-reactive-dependencies)に反することなく `shoppingCart` を依存配列から除外することはできません。しかし、エフェクト内から呼ばれるコードの一部であるにもかかわらず、そのコードが変更に「反応」しないことを示すことができます。[`useEffectEvent`](/reference/react/useEffectEvent) フックを使用して、[*エフェクトイベント (Effect Event)* を宣言](/learn/separating-events-from-effects#declaring-an-effect-event)し、`shoppingCart` を読み取るコードをその内部に移動してください。\n\n```js {2-4,7,8}\nfunction Page({ url, shoppingCart }) {\n  const onVisit = useEffectEvent(visitedUrl => {\n    logVisit(visitedUrl, shoppingCart.length)\n  });\n\n  useEffect(() => {\n    onVisit(url);\n  }, [url]); // ✅ All dependencies declared\n  // ...\n}\n```\n\n**エフェクトイベントはリアクティブではないため、あなたのエフェクトの依存配列からは常に除く必要があります**。これにより、非リアクティブなコード（最新の props や state の値を読むことができるコード）をエフェクトイベント内に入れることができます。`onVisit` の中で `shoppingCart` を読むことで、`shoppingCart` がエフェクトを再実行することがなくなります。\n\n[エフェクトイベントがリアクティブなコードと非リアクティブなコードをどのように分離するか詳しく読む](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events)。\n\n\n---\n\n### サーバとクライアントで異なるコンテンツを表示する {/*displaying-different-content-on-the-server-and-the-client*/}\n\nアプリがサーバレンダリングを（[直接](/reference/react-dom/server)ないし[フレームワーク](/learn/creating-a-react-app#full-stack-frameworks)経由で）使用している場合、コンポーネントは 2 種類の環境でレンダーされます。サーバ上では、初期 HTML を生成するためにレンダーされます。クライアント上では、React がその HTML にイベントハンドラをアタッチするために再度レンダーコードを実行します。これが、[ハイドレーション](/reference/react-dom/client/hydrateRoot#hydrating-server-rendered-html)が動作するためには初回レンダーの出力がクライアントとサーバの両方で同一でなければならない理由です。\n\nまれに、クライアント側で異なるコンテンツを表示する必要がある場合があります。たとえば、アプリが [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) からデータを読み込む場合、サーバ上ではそれを行うことができません。これは以下の方法で実装できます。\n\n\n{/* TODO(@poteto) - investigate potential false positives in react compiler validation */}\n```js {expectedErrors: {'react-compiler': [5]}}\nfunction MyComponent() {\n  const [didMount, setDidMount] = useState(false);\n\n  useEffect(() => {\n    setDidMount(true);\n  }, []);\n\n  if (didMount) {\n    // ... return client-only JSX ...\n  }  else {\n    // ... return initial JSX ...\n  }\n}\n```\n\nアプリがロードされている間、ユーザは初期レンダーの出力を表示します。ロードとハイドレーションが完了したら、エフェクトが実行され、`didMount` が `true` にセットされ、再レンダーがトリガされます。これにより、クライアント専用のレンダー出力に切り替わります。エフェクトはサーバ上では実行されないため、初回サーバレンダー時には `didMount` は `false` のままになります。\n\nこのパターンは節度を持って使用してください。遅い接続のユーザは初期コンテンツをかなり長い時間、場合によっては数秒以上表示することになります。なのでコンポーネントの見た目に違和感を与える変更をしないようにしてください。多くの場合、CSS で条件付きに異なるものを表示することで、このようなことはしなくてよくなります。\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### コンポーネントのマウント時にエフェクトが 2 回実行される {/*my-effect-runs-twice-when-the-component-mounts*/}\n\nStrict Mode がオンの場合、開発時に React は実際のセットアップの前に、セットアップとクリーンアップをもう一度実行します。\n\nこれは、エフェクトのロジックが正しく実装されていることを確認するためのストレステストです。これが目に見える問題を引き起こす場合、クリーンアップ関数に一部のロジックが欠けています。クリーンアップ関数は、セットアップ関数が行っていたことを停止ないし元に戻す必要があります。基本原則は、ユーザがセットアップが一度呼ばれた場合（本番環境の場合）と、セットアップ → クリーンアップ → セットアップというシーケンスで呼ばれた場合（開発環境の場合）で、違いを見分けられてはいけない、ということです。\n\n[どのようにバグを見つけるのに役立つか](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) と、[ロジックを修正する方法](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) について詳しく読む。\n\n---\n\n### エフェクトが再レンダーごとに実行される {/*my-effect-runs-after-every-re-render*/}\n\nまず、依存配列の指定を忘れていないか確認してください。\n\n```js {3}\nuseEffect(() => {\n  // ...\n}); // 🚩 No dependency array: re-runs after every commit!\n```\n\n依存配列を指定しているにもかかわらず、エフェクトがループで再実行される場合、それは再レンダーごとに依存する値のどれかが変わっているためです。\n\nこの問題は、手動で依存する値をコンソールにログ出力することでデバッグできます。\n\n```js {5}\n  useEffect(() => {\n    // ..\n  }, [serverUrl, roomId]);\n\n  console.log([serverUrl, roomId]);\n```\n\n次に、コンソール上の異なる再レンダーから表示された配列を右クリックし、それぞれで \"Store as a global variable\" を選択します。最初のものが `temp1` として保存され、2 番目のものが `temp2` として保存されたとすると、以下のようにブラウザのコンソールを使って、両方の配列でそれぞれの値が同じかどうかを確認できます。\n\n```js\nObject.is(temp1[0], temp2[0]); // Is the first dependency the same between the arrays?\nObject.is(temp1[1], temp2[1]); // Is the second dependency the same between the arrays?\nObject.is(temp1[2], temp2[2]); // ... and so on for every dependency ...\n```\n\n再レンダーごとに値の変わる依存値が見つかった場合、通常は次の方法のいずれかで修正できます。\n\n- [エフェクトからの前回の state に基づく state の更新](#updating-state-based-on-previous-state-from-an-effect)\n- [オブジェクト型の不要な依存値を削除する](#removing-unnecessary-object-dependencies)\n- [関数型の不要な依存値を削除する](#removing-unnecessary-function-dependencies)\n- [エフェクトから最新の props と state を読み取る](#reading-the-latest-props-and-state-from-an-effect)\n\n最後の手段として、上記の方法がうまくいかなかった場合、その値を作っているところを [`useMemo`](/reference/react/useMemo#memoizing-a-dependency-of-another-hook) または（関数の場合）[`useCallback`](/reference/react/useCallback#preventing-an-effect-from-firing-too-often) でラップしてください。\n\n---\n\n### エフェクトが無限ループで再実行され続ける {/*my-effect-keeps-re-running-in-an-infinite-cycle*/}\n\nエフェクトが無限ループで実行される場合、以下の 2 つの条件が成立しているはずです。\n\n- エフェクトが何らかの state を更新している。\n- その state 更新により再レンダーが発生し、それによりエフェクトの依存配列が変更されている。\n\n問題を修正する前に、エフェクトが外部システム（DOM、ネットワーク、サードパーティのウィジェットなど）に接続しているかどうかを確認してください。エフェクトが state を設定する必要がある理由は何ですか？ 外部システムと同期するためですか？ それとも、アプリケーションのデータフローをそれで管理しようとしているのでしょうか？\n\n外部システムがない場合、[そもそもエフェクトを削除する](/learn/you-might-not-need-an-effect)ことでロジックが簡略化されるかどうか、検討してください。\n\nもし本当に外部システムと同期している場合は、エフェクトがいつ、どのような条件下で state を更新する必要があるか考えてみてください。何か、コンポーネントの視覚的な出力に影響を与える変更があるのでしょうか？ レンダーに使用されないデータを管理する必要がある場合は、[ref](/reference/react/useRef#referencing-a-value-with-a-ref)（再レンダーをトリガしない）の方が適切かもしれません。エフェクトが必要以上に state を更新（して再レンダーをトリガ）していないことを確認してください。\n\n最後に、エフェクトが適切なタイミングで state を更新しているものの、それでも無限ループが残っている場合は、その state の更新によりエフェクトの依存配列のどれかが変更されているためです。[依存配列の変更をデバッグする方法を確認してください](/reference/react/useEffect#my-effect-runs-after-every-re-render)。\n\n---\n\n### コンポーネントがアンマウントされていないのにクリーンアップロジックが実行される {/*my-cleanup-logic-runs-even-though-my-component-didnt-unmount*/}\n\nクリーンアップ関数は、アンマウント時だけでなく、依存配列が変更された後の再レンダー後にも実行されます。また、開発中には、React が[コンポーネントのマウント直後に、セットアップ+クリーンアップを 1 回追加で実行](#my-effect-runs-twice-when-the-component-mounts)します。\n\n対応するセットアップコードのないクリーンアップコードをお持ちの場合、通常はコードの問題があります。\n\n```js {2-5}\nuseEffect(() => {\n  // 🔴 Avoid: Cleanup logic without corresponding setup logic\n  return () => {\n    doSomething();\n  };\n}, []);\n```\n\nクリーンアップロジックはセットアップロジックと「対称的」であり、セットアップが行ったことを停止ないし元に戻す必要があります。\n\n```js {2-3,5}\n  useEffect(() => {\n    const connection = createConnection(serverUrl, roomId);\n    connection.connect();\n    return () => {\n      connection.disconnect();\n    };\n  }, [serverUrl, roomId]);\n```\n\n[エフェクトのライフサイクルがコンポーネントのライフサイクルとどのように異なるかを学びましょう](/learn/lifecycle-of-reactive-effects#the-lifecycle-of-an-effect)。\n\n---\n\n### エフェクトが表示に関することを行っており、実行前にちらつきが見られる {/*my-effect-does-something-visual-and-i-see-a-flicker-before-it-runs*/}\n\nエフェクトがブラウザの[画面描画をブロック](/learn/render-and-commit#epilogue-browser-paint)する必要がある場合は、`useEffect` の代わりに [`useLayoutEffect`](/reference/react/useLayoutEffect) を使用してください。ただし、これは**ほとんどのエフェクトには必要ない**ということに注意してください。これは、ブラウザ描画の前にエフェクトを実行することが重要な場合にのみ必要です。例えば、ユーザがツールチップを見る前に、ツールチップのサイズを測定して配置するために使用します。\n"
  },
  {
    "path": "src/content/reference/react/useEffectEvent.md",
    "content": "---\ntitle: useEffectEvent\n---\n\n<Intro>\n\n`useEffectEvent` は、イベントをエフェクトから分離できるようにする React フックです。\n\n```js\nconst onEvent = useEffectEvent(callback)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useEffectEvent(callback)` {/*useeffectevent*/}\n\nコンポーネントのトップレベルで `useEffectEvent` を呼び出し、エフェクトイベント (Effect Event) を作成します。\n\n```js {4,6}\nimport { useEffectEvent, useEffect } from 'react';\n\nfunction ChatRoom({ roomId, theme }) {\n  const onConnected = useEffectEvent(() => {\n    showNotification('Connected!', theme);\n  });\n}\n```\n\nエフェクトイベントはエフェクトのロジックの一部ですが、よりイベントハンドラに近いふるまいをします。常に最新のレンダー時の値（props や state など）が \"見える\" 一方で、エフェクトの再同期を起こさないため、エフェクトの依存配列には入れません。詳しくは[エフェクトからイベントを分離する](/learn/separating-events-from-effects#extracting-non-reactive-logic-out-of-effects)を参照してください。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `callback`: エフェクトイベントのロジックを含む関数。この関数は任意の数の引数を受け取り、任意の値を返せます。返されたエフェクトイベント関数を呼び出すと、`callback` は呼び出し時点でレンダーにコミット済みの最新の値に常にアクセスします。\n\n#### 返り値 {/*returns*/}\n\n`useEffectEvent` は、`callback` と同じ型シグネチャのエフェクトイベント関数を返します。\n\nこの関数は `useEffect`、`useLayoutEffect`、`useInsertionEffect` の中、または同じコンポーネント内の他のエフェクトイベント内から呼び出せます。\n\n#### 注意点 {/*caveats*/}\n\n* `useEffectEvent` はフックであるため、**コンポーネントのトップレベル**やカスタムフック内でのみ呼び出すことができます。ループや条件文の中で呼び出すことはできません。これが必要な場合は、新しいコンポーネントを抽出し、その中にエフェクトイベントを移動させてください。\n* エフェクトイベントは、エフェクトまたは他のエフェクトイベントの内部からのみ呼び出せます。レンダー中に呼び出したり、他のコンポーネントやフックへ渡したりしないでください。[`eslint-plugin-react-hooks`](/reference/eslint-plugin-react-hooks) リンタがこの制約を強制します。\n* エフェクトの依存配列に依存値を書かないで済ますための手段として `useEffectEvent` を使わないでください。これはバグを隠蔽し、コードを理解しにくくします。エフェクトから発火する、真にイベントとしてのロジックにのみ使用してください。\n* エフェクトイベント関数には安定した同一性がありません。意図的に、レンダーごとに変化します。\n\n<DeepDive>\n\n#### エフェクトイベントがレンダーごとに異なる理由 {/*why-are-effect-events-not-stable*/}\n\n`useState` の `set` 関数や ref とは異なり、エフェクトイベント関数には安定した同一性がありません。意図的に、レンダーごとに変化します。\n\n```js\n// 🔴 Wrong: including Effect Event in dependencies\nuseEffect(() => {\n  onSomething();\n}, [onSomething]); // ESLint will warn about this\n```\n\nこれは意図的な設計判断です。エフェクトイベントは、同じコンポーネント内のエフェクトからのみ呼び出されることを想定しています。ローカルでしか呼び出せず、他のコンポーネントに渡したり依存配列に含めたりできないため、同一の関数にすることには意味がなく、むしろバグを隠してしまいます。\n\n毎回異なる関数であることは実行時チェックとしても機能します。あなたのコードが誤って関数の同一性に依存している場合、エフェクトがレンダーごとに再実行され、バグが表面化します。\n\nこの設計が示しているのは、エフェクトイベントとは概念的に特定のエフェクトに属するものであり、リアクティブ性を回避するための汎用 API ではないということです。\n\n</DeepDive>\n\n---\n\n## 使用法 {/*usage*/}\n\n\n### エフェクト内でイベントを使う {/*using-an-event-in-an-effect*/}\n\nコンポーネントのトップレベルで `useEffectEvent` を呼び出し、*エフェクトイベント*を作成します。\n\n\n```js [[1, 1, \"onConnected\"]]\nconst onConnected = useEffectEvent(() => {\n  if (!muted) {\n    showNotification('Connected!');\n  }\n});\n```\n\n`useEffectEvent` は `event callback` を受け取り、<CodeStep step={1}>エフェクトイベント</CodeStep>を返します。このエフェクトイベントは、再接続を発生させずにエフェクト内部から呼び出せる関数です。\n\n```js [[1, 3, \"onConnected\"]]\nuseEffect(() => {\n  const connection = createConnection(roomId);\n  connection.on('connected', onConnected);\n  connection.connect();\n  return () => {\n    connection.disconnect();\n  }\n}, [roomId]);\n```\n\n`onConnected` は<CodeStep step={1}>エフェクトイベント</CodeStep>なので、`muted` と `onConnect` はエフェクトの依存値に含めません。\n\n<Pitfall>\n\n##### エフェクトイベントを依存値を無視するために使わない {/*pitfall-skip-dependencies*/}\n\n`useEffectEvent` を使って「不要そうな」依存値の列挙を避けたくなるかもしれません。しかし、これはバグを隠し、コードを理解しにくくします。\n\n```js\n// 🔴 Wrong: Using Effect Events to hide dependencies\nconst logVisit = useEffectEvent(() => {\n  log(pageUrl);\n});\n\nuseEffect(() => {\n  logVisit()\n}, []); // Missing pageUrl means you miss logs\n```\n\nある値によってエフェクトを再実行すべきなら、その値は依存値として残してください。エフェクトイベントは、エフェクトを本当に再トリガすべきでないロジックにのみ使ってください。\n\n詳しくは[エフェクトからイベントを分離する](/learn/separating-events-from-effects)を参照してください。\n\n</Pitfall>\n\n---\n\n### タイマーで最新の値を使う {/*using-a-timer-with-latest-values*/}\n\nエフェクト内で `setInterval` や `setTimeout` を使う際に、レンダーから最新の値を読み取りたいがタイマーの再起動は避けたい場合があります。\n\n以下のカウンタは、1 秒ごとに現在の `increment` の値だけ `count` をインクリメントします。`onTick` エフェクトイベントは、interval をリスタートさせることなく、最新の `count` と `increment` を読み取ります。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect, useEffectEvent } from 'react';\n\nexport default function Timer() {\n  const [count, setCount] = useState(0);\n  const [increment, setIncrement] = useState(1);\n\n  const onTick = useEffectEvent(() => {\n    setCount(count + increment);\n  });\n\n  useEffect(() => {\n    const id = setInterval(() => {\n      onTick();\n    }, 1000);\n    return () => {\n      clearInterval(id);\n    };\n  }, []);\n\n  return (\n    <>\n      <h1>\n        Counter: {count}\n        <button onClick={() => setCount(0)}>Reset</button>\n      </h1>\n      <hr />\n      <p>\n        Every second, increment by:\n        <button disabled={increment === 0} onClick={() => {\n          setIncrement(i => i - 1);\n        }}>–</button>\n        <b>{increment}</b>\n        <button onClick={() => {\n          setIncrement(i => i + 1);\n        }}>+</button>\n      </p>\n    </>\n  );\n}\n```\n\n```css\nbutton { margin: 10px; }\n```\n\n</Sandpack>\n\nタイマーを動かしたまま `increment` の値を変更してみてください。カウンタはすぐに新しい増分値を参照しますが、タイマーは再起動せず滑らかに動き続けます。\n\n---\n\n### 最新の値でイベントリスナを使う {/*using-an-event-listener-with-latest-values*/}\n\nエフェクト内でイベントリスナをセットアップする際は、しばしばコールバック内でレンダーからの最新の値を読み取る必要があります。`useEffectEvent` がなければ、それらの値を依存値に含める必要があり、値が変わるたびにリスナが解除・再登録されてしまいます。\n\n以下の例では、\"Can move\" にチェックがあるときだけカーソルを追いかけるドットを表示します。`onMove` エフェクトイベントは、エフェクトを再実行せずに常に最新の `canMove` 値を読み取ります。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect, useEffectEvent } from 'react';\n\nexport default function App() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n  const [canMove, setCanMove] = useState(true);\n\n  const onMove = useEffectEvent(e => {\n    if (canMove) {\n      setPosition({ x: e.clientX, y: e.clientY });\n    }\n  });\n\n  useEffect(() => {\n    window.addEventListener('pointermove', onMove);\n    return () => window.removeEventListener('pointermove', onMove);\n  }, []);\n\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={canMove}\n          onChange={e => setCanMove(e.target.checked)}\n        />\n        The dot is allowed to move\n      </label>\n      <hr />\n      <div style={{\n        position: 'absolute',\n        backgroundColor: 'pink',\n        borderRadius: '50%',\n        opacity: 0.6,\n        transform: `translate(${position.x}px, ${position.y}px)`,\n        pointerEvents: 'none',\n        left: -20,\n        top: -20,\n        width: 40,\n        height: 40,\n      }} />\n    </>\n  );\n}\n```\n\n```css\nbody {\n  height: 200px;\n}\n```\n\n</Sandpack>\n\nチェックボックスを切り替えてカーソルを動かしてみてください。ドットはチェック状態に即座に反応しますが、イベントリスナはコンポーネントのマウント時に一度だけ設定されます。\n\n---\n\n### 外部システムへの過度な再接続を避ける {/*showing-a-notification-without-reconnecting*/}\n\n`useEffectEvent` のよくある用途は、エフェクトへの反応として何かを実行したいが、その「何か」がリアクティブにしたくない値に依存している場合です。\n\n以下の例では、チャットコンポーネントがルームに接続し、接続時に通知を表示します。ユーザはチェックボックスで通知をミュートできます。ただし、ユーザがこの設定を切り替えるたびにチャットルームへ再接続したいわけではありませんね。\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"toastify-js\": \"1.12.0\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js\nimport { useState, useEffect, useEffectEvent } from 'react';\nimport { createConnection } from './chat.js';\nimport { showNotification } from './notifications.js';\n\nfunction ChatRoom({ roomId, muted }) {\n  const onConnected = useEffectEvent((roomId) => {\n    console.log('✅ Connected to ' + roomId + ' (muted: ' + muted + ')');\n    if (!muted) {\n      showNotification('Connected to ' + roomId);\n    }\n  });\n\n  useEffect(() => {\n    const connection = createConnection(roomId);\n    console.log('⏳ Connecting to ' + roomId + '...');\n    connection.on('connected', () => {\n      onConnected(roomId);\n    });\n    connection.connect();\n    return () => {\n      console.log('❌ Disconnected from ' + roomId);\n      connection.disconnect();\n    }\n  }, [roomId]);\n\n  return <h1>Welcome to the {roomId} room!</h1>;\n}\n\nexport default function App() {\n  const [roomId, setRoomId] = useState('general');\n  const [muted, setMuted] = useState(false);\n  return (\n    <>\n      <label>\n        Choose the chat room:{' '}\n        <select\n          value={roomId}\n          onChange={e => setRoomId(e.target.value)}\n        >\n          <option value=\"general\">general</option>\n          <option value=\"travel\">travel</option>\n          <option value=\"music\">music</option>\n        </select>\n      </label>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={muted}\n          onChange={e => setMuted(e.target.checked)}\n        />\n        Mute notifications\n      </label>\n      <hr />\n      <ChatRoom\n        roomId={roomId}\n        muted={muted}\n      />\n    </>\n  );\n}\n```\n\n```js src/chat.js\nconst serverUrl = 'https://localhost:1234';\n\nexport function createConnection(roomId) {\n  // A real implementation would actually connect to the server\n  let connectedCallback;\n  let timeout;\n  return {\n    connect() {\n      timeout = setTimeout(() => {\n        if (connectedCallback) {\n          connectedCallback();\n        }\n      }, 100);\n    },\n    on(event, callback) {\n      if (connectedCallback) {\n        throw Error('Cannot add the handler twice.');\n      }\n      if (event !== 'connected') {\n        throw Error('Only \"connected\" event is supported.');\n      }\n      connectedCallback = callback;\n    },\n    disconnect() {\n      clearTimeout(timeout);\n    }\n  };\n}\n```\n\n```js src/notifications.js\nimport Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\n\nexport function showNotification(message, theme) {\n  Toastify({\n    text: message,\n    duration: 2000,\n    gravity: 'top',\n    position: 'right',\n    style: {\n      background: theme === 'dark' ? 'black' : 'white',\n      color: theme === 'dark' ? 'white' : 'black',\n    },\n  }).showToast();\n}\n```\n\n```css\nlabel { display: block; margin-top: 10px; }\n```\n\n</Sandpack>\n\nルームを切り替えてみてください。チャットの再接続が起こり、通知が表示されますね。次に通知をミュートしてみてください。`muted` はエフェクトではなくエフェクトイベント内で読み取られるため、チャット接続は維持されます。\n\n---\n\n### カスタムフックでエフェクトイベントを使う {/*using-effect-events-in-custom-hooks*/}\n\n独自のカスタムフック内でも `useEffectEvent` を使えます。これにより、一部の値を非リアクティブに保ちながらエフェクトをカプセル化した、再利用可能なフックを作成できます。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect, useEffectEvent } from 'react';\n\nfunction useInterval(callback, delay) {\n  const onTick = useEffectEvent(callback);\n\n  useEffect(() => {\n    if (delay === null) {\n      return;\n    }\n    const id = setInterval(() => {\n      onTick();\n    }, delay);\n    return () => clearInterval(id);\n  }, [delay]);\n}\n\nfunction Counter({ incrementBy }) {\n  const [count, setCount] = useState(0);\n\n  useInterval(() => {\n    setCount(c => c + incrementBy);\n  }, 1000);\n\n  return (\n    <div>\n      <h2>Count: {count}</h2>\n      <p>Incrementing by {incrementBy} every second</p>\n    </div>\n  );\n}\n\nexport default function App() {\n  const [incrementBy, setIncrementBy] = useState(1);\n\n  return (\n    <>\n      <label>\n        Increment by:{' '}\n        <select\n          value={incrementBy}\n          onChange={(e) => setIncrementBy(Number(e.target.value))}\n        >\n          <option value={1}>1</option>\n          <option value={5}>5</option>\n          <option value={10}>10</option>\n        </select>\n      </label>\n      <hr />\n      <Counter incrementBy={incrementBy} />\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; margin-bottom: 8px; }\n```\n\n</Sandpack>\n\n上記の例では、`useInterval` は interval をセットアップするためのカスタムフックです。渡された `callback` はエフェクトイベントでラップされているため、レンダーごとに新しい `callback` が渡されても interval のリセットは起きません。\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### \"A function wrapped in useEffectEvent can't be called during rendering\" というエラーが出る {/*cant-call-during-rendering*/}\n\nこのエラーは、コンポーネントのレンダーフェーズ中にエフェクトイベント関数を呼び出していることを意味します。エフェクトイベントは、エフェクトまたは他のエフェクトイベントの内部からのみ呼び出せます。\n\n```js\nfunction MyComponent({ data }) {\n  const onLog = useEffectEvent(() => {\n    console.log(data);\n  });\n\n  // 🔴 Wrong: calling during render\n  onLog();\n\n  // ✅ Correct: call from an Effect\n  useEffect(() => {\n    onLog();\n  }, []);\n\n  return <div>{data}</div>;\n}\n```\n\nレンダー中にロジックを実行する必要がある場合は、`useEffectEvent` でラップしないでください。ロジックを直接呼び出すか、エフェクト内へ移動してください。\n\n---\n\n### \"Functions returned from useEffectEvent must not be included in the dependency array\" というリントエラーが出る {/*effect-event-in-deps*/}\n\n\"Functions returned from `useEffectEvent` must not be included in the dependency array\" のような警告が出たら、依存値からエフェクトイベントを取り除いてください。\n\n```js\nconst onSomething = useEffectEvent(() => {\n  // ...\n});\n\n// 🔴 Wrong: Effect Event in dependencies\nuseEffect(() => {\n  onSomething();\n}, [onSomething]);\n\n// ✅ Correct: no Effect Event in dependencies\nuseEffect(() => {\n  onSomething();\n}, []);\n```\n\nエフェクトイベントは、依存値として列挙せずにエフェクトから呼び出すよう設計されています。関数の同一性は[意図的に毎回異なる](#why-are-effect-events-not-stable)ため、リンタがこれを強制します。依存値に含めると、エフェクトがレンダーごとに再実行されてしまいます。\n\n---\n\n### \"... is a function created with useEffectEvent, and can only be called from Effects\" というリントエラーが出る {/*effect-event-called-outside-effect*/}\n\n\"... is a function created with React Hook `useEffectEvent`, and can only be called from Effects and Effect Events\" のような警告が出る場合、関数を呼び出す場所が誤っています。\n\n```js\nconst onSomething = useEffectEvent(() => {\n  console.log(value);\n});\n\n// 🔴 Wrong: calling from event handler\nfunction handleClick() {\n  onSomething();\n}\n\n// 🔴 Wrong: passing to child component\nreturn <Child onSomething={onSomething} />;\n\n// ✅ Correct: calling from Effect\nuseEffect(() => {\n  onSomething();\n}, []);\n```\n\nエフェクトイベントは、それを定義したコンポーネント内のローカルなエフェクトで使うために設計されています。イベントハンドラ用のコールバックや子コンポーネントへ渡すコールバックが必要な場合は、通常の関数または `useCallback` を使ってください。\n"
  },
  {
    "path": "src/content/reference/react/useId.md",
    "content": "---\ntitle: useId\n---\n\n<Intro>\n\n`useId` は、アクセシビリティ属性に渡すことができる一意の ID を生成するための React フックです。\n\n```js\nconst id = useId()\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useId()` {/*useid*/}\n\nコンポーネントのトップレベルで `useId` を呼び出して、一意の ID を生成します。\n\n```js\nimport { useId } from 'react';\n\nfunction PasswordField() {\n  const passwordHintId = useId();\n  // ...\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n`useId` は引数を受け取りません。\n\n#### 返り値 {/*returns*/}\n\n`useId` は、特定のコンポーネント内でこの特定の `useId` の呼び出しに関連付けられた、一意の ID 文字列を返します。\n\n#### 注意点 {/*caveats*/}\n\n* `useId` はフックであるため、**コンポーネントのトップレベル**または独自のフック内でのみ呼び出すことができます。ループまたは条件分岐内で呼び出すことはできません。もし必要な場合は、新しいコンポーネントを作成し、状態を移動させる必要があります。\n\n* `useId` は [use()](/reference/react/use) の**キャッシュキーの生成には使用しないでください**。ID はコンポーネントのマウント中は安定していますが、レンダー中に変わる可能性があります。キャッシュキーはデータから生成する必要があります。\n\n* `useId` を、**リスト内の key の生成には使用しないでください**。[key はデータから生成される必要があります。](/learn/rendering-lists#where-to-get-your-key)\n\n* `useId` currently cannot be used in [async Server Components](/reference/rsc/server-components#async-components-with-server-components).\n\n---\n\n## 使用法 {/*usage*/}\n\n<Pitfall>\n\n**リスト内の key を生成するために `useId` を呼び出さないでください。**[key はデータから生成される必要があります。](/learn/rendering-lists#where-to-get-your-key)\n\n</Pitfall>\n\n### アクセシビリティ属性のための一意の ID の生成 {/*generating-unique-ids-for-accessibility-attributes*/}\n\nコンポーネントのトップレベルで `useId` を呼び出して、一意の ID を生成します。\n\n```js [[1, 4, \"passwordHintId\"]]\nimport { useId } from 'react';\n\nfunction PasswordField() {\n  const passwordHintId = useId();\n  // ...\n```\n\nその後、<CodeStep step={1}>生成された ID</CodeStep> をさまざまな属性に渡すことができます。\n\n```js [[1, 2, \"passwordHintId\"], [1, 3, \"passwordHintId\"]]\n<>\n  <input type=\"password\" aria-describedby={passwordHintId} />\n  <p id={passwordHintId}>\n</>\n```\n\n**これがどのような場合に役立つかを、例を通してみてみましょう。** \n\n[`aria-describedby`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-describedby) のような [HTML アクセシビリティ属性](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA)を使用すると、2 つのタグが相互に関連していることを指定することができます。例えば、入力フィールドのような要素が、段落などの別の要素で説明されていることを指定することができます。\n\n通常の HTML では、次のように記述します。\n\n```html {5,8}\n<label>\n  Password:\n  <input\n    type=\"password\"\n    aria-describedby=\"password-hint\"\n  />\n</label>\n<p id=\"password-hint\">\n  The password should contain at least 18 characters\n</p>\n```\n\nただし、このように ID をハードコードすることは、React ではおすすめできません。コンポーネントはページ上で複数回レンダー可能ですが、ID は一意である必要があります！ ID をハードコードするのではなく、`useId` を使用して一意の ID を生成します。\n\n```js {4,11,14}\nimport { useId } from 'react';\n\nfunction PasswordField() {\n  const passwordHintId = useId();\n  return (\n    <>\n      <label>\n        Password:\n        <input\n          type=\"password\"\n          aria-describedby={passwordHintId}\n        />\n      </label>\n      <p id={passwordHintId}>\n        The password should contain at least 18 characters\n      </p>\n    </>\n  );\n}\n```\n\nこれで、画面上に `PasswordField` が複数回表示される場合でも、生成される ID が同じになることはありません。\n\n<Sandpack>\n\n```js\nimport { useId } from 'react';\n\nfunction PasswordField() {\n  const passwordHintId = useId();\n  return (\n    <>\n      <label>\n        Password:\n        <input\n          type=\"password\"\n          aria-describedby={passwordHintId}\n        />\n      </label>\n      <p id={passwordHintId}>\n        The password should contain at least 18 characters\n      </p>\n    </>\n  );\n}\n\nexport default function App() {\n  return (\n    <>\n      <h2>Choose password</h2>\n      <PasswordField />\n      <h2>Confirm password</h2>\n      <PasswordField />\n    </>\n  );\n}\n```\n\n```css\ninput { margin: 5px; }\n```\n\n</Sandpack>\n\n[このビデオを見て](https://www.youtube.com/watch?v=0dNzNcuEuOo)支援技術による、ユーザ体験の違いを確認してみてください。\n\n<Pitfall>\n\n[サーバレンダリング](/reference/react-dom/server)では、**`useId` にはサーバとクライアント上で同一のコンポーネントツリーが必要です**。サーバ上とクライアント上でレンダーされるツリーが正確に一致しない場合、生成される ID は一致しません。\n\n</Pitfall>\n\n<DeepDive>\n\n#### useId が増分カウンタよりも良い理由 {/*why-is-useid-better-than-an-incrementing-counter*/}\n\n`useId` が `nextId++` のようなグローバル変数を増分するよりもなぜ良いのか、疑問に思われるかもしれません。\n\n`useId` の主な利点は、React が、[サーバレンダリング](/reference/react-dom/server)で確実に機能することを保証していることです。サーバでのレンダー中に、コンポーネントは HTML 形式の出力を生成します。その後クライアント上で、生成された HTML に、[ハイドレーション](/reference/react-dom/client/hydrateRoot)によって、イベントハンドラがアタッチされます。ハイドレーションが機能するには、クライアントの出力がサーバの HTML と一致する必要があります。\n\nクライアントコンポーネントがハイドレートされる順序は、サーバ HTML が出力された順序と一致しない可能性があるため、これをインクリメントカウンタで保証することは非常に困難です。`useId` を呼び出すことで、ハイドレーションが機能し、サーバとクライアントの間で出力が一致することが保証されます。\n\nReact 内部では、呼び出し元コンポーネントの \"親のパス (parent path)\" から `useId` が生成されます。そのため、クライアントとサーバのツリーが同じであれば、レンダー順序に関係なく \"親のパス\" が一致することになります。\n\n</DeepDive>\n\n---\n\n### 複数の関連要素に対して ID を生成する {/*generating-ids-for-several-related-elements*/}\n\n複数の関連要素に ID を与える必要がある場合は、`useId` で生成した値を共有のプレフィックスとして使用できます。\n\n<Sandpack>\n\n```js\nimport { useId } from 'react';\n\nexport default function Form() {\n  const id = useId();\n  return (\n    <form>\n      <label htmlFor={id + '-firstName'}>First Name:</label>\n      <input id={id + '-firstName'} type=\"text\" />\n      <hr />\n      <label htmlFor={id + '-lastName'}>Last Name:</label>\n      <input id={id + '-lastName'} type=\"text\" />\n    </form>\n  );\n}\n```\n\n```css\ninput { margin: 5px; }\n```\n\n</Sandpack>\n\nこれにより、一意の ID を必要とするすべての要素に対して `useId` を呼び出す必要がなくなります。\n\n---\n\n### 生成されるすべての ID に共有プレフィックスを指定する {/*specifying-a-shared-prefix-for-all-generated-ids*/}\n\n複数の独立した React アプリケーションを 1 つのページにレンダーする場合は、オプションとして `identifierPrefix` を [`createRoot`](/reference/react-dom/client/createRoot#parameters) または [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) に渡します。これにより、`useId` で生成されたすべての識別子が指定した個別のプレフィックスで始まるため、2 つの異なるアプリによって生成された ID が衝突することがなくなります。\n\n<Sandpack>\n\n```html public/index.html\n<!DOCTYPE html>\n<html>\n  <head><title>My app</title></head>\n  <body>\n    <div id=\"root1\"></div>\n    <div id=\"root2\"></div>\n  </body>\n</html>\n```\n\n```js\nimport { useId } from 'react';\n\nfunction PasswordField() {\n  const passwordHintId = useId();\n  console.log('Generated identifier:', passwordHintId)\n  return (\n    <>\n      <label>\n        Password:\n        <input\n          type=\"password\"\n          aria-describedby={passwordHintId}\n        />\n      </label>\n      <p id={passwordHintId}>\n        The password should contain at least 18 characters\n      </p>\n    </>\n  );\n}\n\nexport default function App() {\n  return (\n    <>\n      <h2>Choose password</h2>\n      <PasswordField />\n    </>\n  );\n}\n```\n\n```js src/index.js active\nimport { createRoot } from 'react-dom/client';\nimport App from './App.js';\nimport './styles.css';\n\nconst root1 = createRoot(document.getElementById('root1'), {\n  identifierPrefix: 'my-first-app-'\n});\nroot1.render(<App />);\n\nconst root2 = createRoot(document.getElementById('root2'), {\n  identifierPrefix: 'my-second-app-'\n});\nroot2.render(<App />);\n```\n\n```css\n#root1 {\n  border: 5px solid blue;\n  padding: 10px;\n  margin: 5px;\n}\n\n#root2 {\n  border: 5px solid green;\n  padding: 10px;\n  margin: 5px;\n}\n\ninput { margin: 5px; }\n```\n\n</Sandpack>\n\n---\n\n### クライアントとサーバで同じ ID プレフィックスを使う {/*using-the-same-id-prefix-on-the-client-and-the-server*/}\n\nもし[同じページ上で複数の独立した React アプリをレンダー](#specifying-a-shared-prefix-for-all-generated-ids)しており、そのうちいくつかがサーバでレンダーされる場合は、クライアント側の [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) 呼び出しに渡す `identifierPrefix` が、[`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream) などの[サーバ API](/reference/react-dom/server) に渡す `identifierPrefix` と同じになるようにしてください。\n\n```js\n// Server\nimport { renderToPipeableStream } from 'react-dom/server';\n\nconst { pipe } = renderToPipeableStream(\n  <App />,\n  { identifierPrefix: 'react-app1' }\n);\n```\n\n```js\n// Client\nimport { hydrateRoot } from 'react-dom/client';\n\nconst domNode = document.getElementById('root');\nconst root = hydrateRoot(\n  domNode,\n  reactNode,\n  { identifierPrefix: 'react-app1' }\n);\n```\n\nページ内に React アプリが 1 つしかない場合は `identifierPrefix` を渡す必要はありません。\n"
  },
  {
    "path": "src/content/reference/react/useImperativeHandle.md",
    "content": "---\ntitle: useImperativeHandle\n---\n\n<Intro>\n\n`useImperativeHandle` は、[ref](/learn/manipulating-the-dom-with-refs) として公開されるハンドルをカスタマイズするための React フックです。\n\n```js\nuseImperativeHandle(ref, createHandle, dependencies?)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useImperativeHandle(ref, createHandle, dependencies?)` {/*useimperativehandle*/}\n\n`useImperativeHandle` をコンポーネントのトップレベルで呼び出し、公開される ref ハンドルをカスタマイズします。\n\n```js\nimport { useImperativeHandle } from 'react';\n\nfunction MyInput({ ref }) {\n  useImperativeHandle(ref, () => {\n    return {\n      // ... your methods ...\n    };\n  }, []);\n  // ...\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `ref`: `MyInput` コンポーネントから props として受け取る `ref` です。\n\n* `createHandle`: 引数を受け取らず、公開したい ref ハンドルを返す関数です。ref ハンドルは任意の型が使えます。通常、公開したいメソッドを持つオブジェクトを返します。\n\n* **省略可能** `dependencies`: `createHandle` コード内で参照されるすべてのリアクティブな値のリストです。リアクティブな値には、props、state、コンポーネント本体に直接宣言されたすべての変数および関数が含まれます。リンタが [React 用に設定されている場合](/learn/editor-setup#linting)、すべてのリアクティブな値が依存値として正しく指定されているか確認できます。依存値のリストは要素数が一定である必要があり、`[dep1, dep2, dep3]` のようにインラインで記述する必要があります。React は、[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) を使った比較で、それぞれの依存値を以前の値と比較します。再レンダーにより依存値のいずれかが変更された場合、または引数自体を省略した場合、`createHandle` 関数は再実行され、新しく作成されたハンドルが ref に割り当てられます。\n\n<Note>\n\nReact 19 からは [`ref` は props として渡されます](/blog/2024/12/05/react-19#ref-as-a-prop)。React 18 以前では、`ref` を受け取るために [`forwardRef`](/reference/react/forwardRef) が必要でした。\n\n</Note>\n\n#### 返り値 {/*returns*/}\n\n`useImperativeHandle` は `undefined` を返します。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 親コンポーネントにカスタム ref ハンドルを公開する {/*exposing-a-custom-ref-handle-to-the-parent-component*/}\n\n親要素に DOM ノードを公開するには、props として `ref` を受け取るようにします。\n\n```js {2}\nfunction MyInput({ ref }) {\n  return <input ref={ref} />;\n};\n```\n\n上記のコードでは、[`MyInput` の ref は `<input>` の DOM ノードを受け取ります](/learn/manipulating-the-dom-with-refs)。ただし、代わりにカスタムな値を公開することもできます。公開されるハンドルをカスタマイズするには、コンポーネントのトップレベルで `useImperativeHandle` を呼び出します。\n\n```js {4-8}\nimport { useImperativeHandle } from 'react';\n\nfunction MyInput({ ref }) {\n  useImperativeHandle(ref, () => {\n    return {\n      // ... your methods ...\n    };\n  }, []);\n\n  return <input />;\n};\n```\n\n上記のコードでは、`ref` が `<input>` に受け渡しされなくなっていることに注意してください。\n\n例えば、`<input>` DOM ノード全体を公開したくはないが、その 2 つのメソッド、`focus` と `scrollIntoView` は公開したいとします。これを行うには、実際のブラウザの DOM を別の ref に保持しておきます。そして、`useImperativeHandle` を使用して、親コンポーネントに呼び出してほしいメソッドのみを含むハンドルを公開します。\n\n```js {7-14}\nimport { useRef, useImperativeHandle } from 'react';\n\nfunction MyInput({ ref }) {\n  const inputRef = useRef(null);\n\n  useImperativeHandle(ref, () => {\n    return {\n      focus() {\n        inputRef.current.focus();\n      },\n      scrollIntoView() {\n        inputRef.current.scrollIntoView();\n      },\n    };\n  }, []);\n\n  return <input ref={inputRef} />;\n};\n```\n\nこれで、親コンポーネントが `MyInput` への ref を取得し、そのコンポーネントで `focus` メソッドと `scrollIntoView` メソッドを呼び出すことができるようになります。ただし、親コンポーネントは背後にある `<input>` DOM ノードへの完全なアクセス権は持ちません。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\nimport MyInput from './MyInput.js';\n\nexport default function Form() {\n  const ref = useRef(null);\n\n  function handleClick() {\n    ref.current.focus();\n    // This won't work because the DOM node isn't exposed:\n    // ref.current.style.opacity = 0.5;\n  }\n\n  return (\n    <form>\n      <MyInput placeholder=\"Enter your name\" ref={ref} />\n      <button type=\"button\" onClick={handleClick}>\n        Edit\n      </button>\n    </form>\n  );\n}\n```\n\n```js src/MyInput.js\nimport { useRef, useImperativeHandle } from 'react';\n\nfunction MyInput({ ref, ...props }) {\n  const inputRef = useRef(null);\n\n  useImperativeHandle(ref, () => {\n    return {\n      focus() {\n        inputRef.current.focus();\n      },\n      scrollIntoView() {\n        inputRef.current.scrollIntoView();\n      },\n    };\n  }, []);\n\n  return <input {...props} ref={inputRef} />;\n};\n\nexport default MyInput;\n```\n\n```css\ninput {\n  margin: 5px;\n}\n```\n\n</Sandpack>\n\n---\n\n### 独自の命令型メソッドを公開する {/*exposing-your-own-imperative-methods*/}\n\n命令型ハンドルを介して公開するメソッドは、DOM メソッドと正確に一致する必要はありません。例えば、この `Post` コンポーネントは、命令型ハンドルを介して `scrollAndFocusAddComment` メソッドを公開します。これにより、ボタンをクリックすると、親である `Page` がコメントのリストをスクロールできる*だけでなく*、入力フィールドにフォーカスもできるようになります。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\nimport Post from './Post.js';\n\nexport default function Page() {\n  const postRef = useRef(null);\n\n  function handleClick() {\n    postRef.current.scrollAndFocusAddComment();\n  }\n\n  return (\n    <>\n      <button onClick={handleClick}>\n        Write a comment\n      </button>\n      <Post ref={postRef} />\n    </>\n  );\n}\n```\n\n```js src/Post.js\nimport { useRef, useImperativeHandle } from 'react';\nimport CommentList from './CommentList.js';\nimport AddComment from './AddComment.js';\n\nfunction Post({ ref }) {\n  const commentsRef = useRef(null);\n  const addCommentRef = useRef(null);\n\n  useImperativeHandle(ref, () => {\n    return {\n      scrollAndFocusAddComment() {\n        commentsRef.current.scrollToBottom();\n        addCommentRef.current.focus();\n      }\n    };\n  }, []);\n\n  return (\n    <>\n      <article>\n        <p>Welcome to my blog!</p>\n      </article>\n      <CommentList ref={commentsRef} />\n      <AddComment ref={addCommentRef} />\n    </>\n  );\n};\n\nexport default Post;\n```\n\n\n```js src/CommentList.js\nimport { useRef, useImperativeHandle } from 'react';\n\nfunction CommentList({ ref }) {\n  const divRef = useRef(null);\n\n  useImperativeHandle(ref, () => {\n    return {\n      scrollToBottom() {\n        const node = divRef.current;\n        node.scrollTop = node.scrollHeight;\n      }\n    };\n  }, []);\n\n  let comments = [];\n  for (let i = 0; i < 50; i++) {\n    comments.push(<p key={i}>Comment #{i}</p>);\n  }\n\n  return (\n    <div className=\"CommentList\" ref={divRef}>\n      {comments}\n    </div>\n  );\n}\n\nexport default CommentList;\n```\n\n```js src/AddComment.js\nimport { useRef, useImperativeHandle } from 'react';\n\nfunction AddComment({ ref }) {\n  return <input placeholder=\"Add comment...\" ref={ref} />;\n}\n\nexport default AddComment;\n```\n\n```css\n.CommentList {\n  height: 100px;\n  overflow: scroll;\n  border: 1px solid black;\n  margin-top: 20px;\n  margin-bottom: 20px;\n}\n```\n\n</Sandpack>\n\n<Pitfall>\n\n**ref の過度な使用に注意してください**。ref は、props として表現できない、*命令型*の動作にのみ使用するべきです。例えば、ノードへのスクロール、ノードへのフォーカス、アニメーションのトリガ、テキストの選択などです。\n\n**何かを props として表現できる場合は、ref を使用すべきではありません**。例えば、`Modal` コンポーネントから `{ open, close }` のような命令型のハンドルを公開するのではなく、`<Modal isOpen={isOpen} />` のように、`isOpen` を props として受け取る方が良いでしょう。命令型の動作を props として公開する際には[エフェクト](/learn/synchronizing-with-effects)が役立ちます。\n\n</Pitfall>\n"
  },
  {
    "path": "src/content/reference/react/useInsertionEffect.md",
    "content": "---\ntitle: useInsertionEffect\n---\n\n<Pitfall>\n\n`useInsertionEffect` は CSS-in-JS ライブラリの作者向けです。CSS-in-JS ライブラリの開発をしておりスタイルを挿入する場所を必要としているのでない限り、おそらく [`useEffect`](/reference/react/useEffect) または [`useLayoutEffect`](/reference/react/useLayoutEffect) の方が適切です。\n\n</Pitfall>\n\n<Intro>\n\n`useInsertionEffect` はレイアウトエフェクトが発火する前に DOM に要素を挿入するために使用します。\n\n```js\nuseInsertionEffect(setup, dependencies?)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useInsertionEffect(setup, dependencies?)` {/*useinsertioneffect*/}\n\n`useInsertionEffect` を呼び出して、レイアウトを読み取る可能性があるエフェクトが実行される前にスタイルの挿入を行います。\n\n```js\nimport { useInsertionEffect } from 'react';\n\n// Inside your CSS-in-JS library\nfunction useCSS(rule) {\n  useInsertionEffect(() => {\n    // ... inject <style> tags here ...\n  });\n  return rule;\n}\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `setup`: エフェクトのロジックが記述された関数です。このセットアップ関数は、オプションで*クリーンアップ*関数を返すことができます。コンポーネントが初めて DOM に追加された後、レイアウトエフェクトが発火する前に、React はセットアップ関数を実行します。依存配列 (dependencies) が変更された再レンダー時には、React はまず古い値を使ってクリーンアップ関数（あれば）を実行し、次に新しい値を使ってセットアップ関数を実行します。コンポーネントが DOM から削除された後、React はクリーンアップ関数を最後にもう一度実行します。\n \n* **省略可能** `dependencies`: `setup` コード内で参照されるすべてのリアクティブな値のリストです。リアクティブな値には、props、state、コンポーネント本体に直接宣言されたすべての変数および関数が含まれます。リンタが [React 用に設定されている場合](/learn/editor-setup#linting)、すべてのリアクティブな値が依存値として正しく指定されているか確認できます。依存値のリストは要素数が一定である必要があり、`[dep1, dep2, dep3]` のようにインラインで記述する必要があります。React は、[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) を使った比較で、それぞれの依存値を以前の値と比較します。この引数を省略すると、エフェクトはコンポーネントの毎回のレンダー後に再実行されます。\n\n#### 返り値 {/*returns*/}\n\n`useInsertionEffect` は `undefined` を返します。\n\n#### 注意点 {/*caveats*/}\n\n* エフェクトはクライアント上でのみ実行されます。サーバレンダリング中には実行されません。\n* `useInsertionEffect` の内部から state を更新することはできません。\n* `useInsertionEffect` が実行される時点では、まだ ref はアタッチされていません。\n* `useInsertionEffect` は DOM の更新前に実行される場合も後に実行される場合もあります。タイミングに関わらず、DOM が更新されていることを前提としてはいけません。\n* 他の種類のエフェクトはすべてのエフェクトにクリーンアップを実行してからすべてのエフェクトにセットアップを行います。一方で `useInsertionEffect` はコンポーネントごとにクリーンアップとセットアップをまとめて行います。結果的にクリーンアップとセットアップが「交互に実行される」ような挙動になります。\n---\n\n## 使用法 {/*usage*/}\n\n### CSS-in-JS ライブラリからの動的スタイル注入 {/*injecting-dynamic-styles-from-css-in-js-libraries*/}\n\n従来、React コンポーネントのスタイル付けはプレーンな CSS を使用して行われていました。\n\n```js\n// In your JS file:\n<button className=\"success\" />\n\n// In your CSS file:\n.success { color: green; }\n```\n\nチームによっては、CSS ファイルを書く代わりに JavaScript コード内で直接スタイルを記述することを好む場合があります。これには通常、CSS-in-JS ライブラリやツールが必要です。CSS-in-JS には以下の 3 つの一般的なアプローチがあります。\n\n1. コンパイラを使用した CSS ファイルへの静的な抽出\n2. インラインスタイル、例えば `<div style={{ opacity: 1 }}>`\n3. `<style>` タグのランタイム時の注入\n\nCSS-in-JS を使用する場合、最初の 2 つのアプローチの組み合わせ（静的スタイルには CSS ファイル、動的スタイルにはインラインスタイル）を推奨します。**ランタイム時の `<style>` タグの注入は、以下の 2 つの理由から推奨しません：**\n\n1. ランタイム時の注入は、ブラウザにスタイルの再計算を頻繁に強制します。\n2. ランタイム時の注入は、React ライフサイクル中の間違ったタイミングで行われると非常に遅くなることがあります。\n\nこのうち最初の問題は解決不可能ですが、`useInsertionEffect` は 2 つ目の問題を解決するのに役立ちます。\n\nレイアウトエフェクトの発火前にスタイルを挿入するために `useInsertionEffect` を呼び出しましょう。\n\n```js {4-11}\n// Inside your CSS-in-JS library\nlet isInserted = new Set();\nfunction useCSS(rule) {\n  useInsertionEffect(() => {\n    // As explained earlier, we don't recommend runtime injection of <style> tags.\n    // But if you have to do it, then it's important to do in useInsertionEffect.\n    if (!isInserted.has(rule)) {\n      isInserted.add(rule);\n      document.head.appendChild(getStyleForRule(rule));\n    }\n  });\n  return rule;\n}\n\nfunction Button() {\n  const className = useCSS('...');\n  return <div className={className} />;\n}\n```\n\n`useEffect` と同様に、`useInsertionEffect` はサーバ上では実行されません。サーバ上でどの CSS ルールが使用されたかを収集する必要がある場合、レンダー中に行うことができます。\n\n```js {1,4-6}\nlet collectedRulesSet = new Set();\n\nfunction useCSS(rule) {\n  if (typeof window === 'undefined') {\n    collectedRulesSet.add(rule);\n  }\n  useInsertionEffect(() => {\n    // ...\n  });\n  return rule;\n}\n```\n\n[`useInsertionEffect` でランタイム時にスタイルを注入するよう CSS-in-JS ライブラリをアップグレードする場合の詳細](https://github.com/reactwg/react-18/discussions/110)\n\n<DeepDive>\n\n#### この手法がレンダー中や useLayoutEffect でスタイルを注入するより優れている理由 {/*how-is-this-better-than-injecting-styles-during-rendering-or-uselayouteffect*/}\n\nレンダー中、React が[非ブロッキング更新](/reference/react/useTransition#perform-non-blocking-updates-with-actions)を処理している最中にスタイルを挿入すると、ブラウザはコンポーネントツリーのレンダー中に毎フレームスタイルを再計算することになり、これは**非常に遅くなる**ことがあります。\n\n`useInsertionEffect` は、[`useLayoutEffect`](/reference/react/useLayoutEffect) や [`useEffect`](/reference/react/useEffect) でスタイルを挿入するよりも優れています。なぜなら、他のエフェクトがあなたのコンポーネントで実行される時点で `<style>` タグがすでに挿入されていることが保証されるからです。さもないと、古くなったスタイルにより、通常のエフェクトでのレイアウト計算が正しくなくなってしまいます。\n\n</DeepDive>\n"
  },
  {
    "path": "src/content/reference/react/useLayoutEffect.md",
    "content": "---\ntitle: useLayoutEffect\n---\n\n<Pitfall>\n\n`useLayoutEffect` はパフォーマンスを低下させる可能性があります。可能な限り [`useEffect`](/reference/react/useEffect) を使用することを推奨します。\n\n</Pitfall>\n\n<Intro>\n\n`useLayoutEffect` は [`useEffect`](/reference/react/useEffect) の一種ですが、ブラウザが画面を再描画する前に実行されます。\n\n```js\nuseLayoutEffect(setup, dependencies?)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useLayoutEffect(setup, dependencies?)` {/*useinsertioneffect*/}\n\nブラウザが画面を再描画する前にレイアウトの計測を行うために `useLayoutEffect` を呼び出します。\n\n```js\nimport { useState, useRef, useLayoutEffect } from 'react';\n\nfunction Tooltip() {\n  const ref = useRef(null);\n  const [tooltipHeight, setTooltipHeight] = useState(0);\n\n  useLayoutEffect(() => {\n    const { height } = ref.current.getBoundingClientRect();\n    setTooltipHeight(height);\n  }, []);\n  // ...\n```\n\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `setup`: エフェクトのロジックが記述された関数です。このセットアップ関数は、オプションで*クリーンアップ*関数を返すことができます。[コンポーネントがコミットされる](/learn/render-and-commit#step-3-react-commits-changes-to-the-dom)前に、React はセットアップ関数を実行します。依存配列 (dependencies) が変更された次のコミット時には、React はまず古い値を使ってクリーンアップ関数（あれば）を実行し、次に新しい値を使ってセットアップ関数を実行します。コンポーネントが DOM から削除される前に、React はクリーンアップ関数を実行します。\n\n* **省略可能** `dependencies`: `setup` コード内で参照されるすべてのリアクティブな値のリストです。リアクティブな値には、props、state、コンポーネント本体に直接宣言されたすべての変数および関数が含まれます。リンタが [React 用に設定されている場合](/learn/editor-setup#linting)、すべてのリアクティブな値が依存値として正しく指定されているか確認できます。依存値のリストは要素数が一定である必要があり、`[dep1, dep2, dep3]` のようにインラインで記述する必要があります。React は、[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) を使った比較で、それぞれの依存値を以前の値と比較します。この引数を省略すると、エフェクトはコンポーネントの毎回のコミット後に再実行されます。\n\n#### 返り値 {/*returns*/}\n\n`useLayoutEffect` は `undefined` を返します。\n\n#### 注意点 {/*caveats*/}\n\n* `useLayoutEffect` はフックであるため、**コンポーネントのトップレベル**やカスタムフック内でのみ呼び出すことができます。ループや条件文の中で呼び出すことはできません。これが必要な場合は、新しいコンポーネントを抽出し、その中にエフェクトを移動させてください。\n\n* Strict Mode が有効な場合、React は本物のセットアップの前に、**開発時専用のセットアップ+クリーンアップサイクルを 1 回追加で実行**します。これは、クリーンアップロジックがセットアップロジックと鏡のように対応しており、セットアップで行われたことを停止または元に戻していることを保証するためのストレステストです。問題が発生した場合は、[クリーンアップ関数を実装します](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development)。\n\n* 依存配列の一部にコンポーネント内で定義されたオブジェクトや関数がある場合、**エフェクトが必要以上に再実行される**可能性があります。これを修正するには、[オブジェクト型](/reference/react/useEffect#removing-unnecessary-object-dependencies)および[関数型](/reference/react/useEffect#removing-unnecessary-function-dependencies)の不要な依存値を削除します。また、エフェクトの外部に [state の更新](/reference/react/useEffect#updating-state-based-on-previous-state-from-an-effect)や[非リアクティブなロジック](/reference/react/useEffect#reading-the-latest-props-and-state-from-an-effect)を抽出することもできます。\n\n* エフェクトは**クライアント上でのみ実行されます**。サーバレンダリング中には実行されません。\n\n* `useLayoutEffect` 内のコードと、そこでスケジュールされたすべての state 更新は、**ブラウザによる画面の再描画をブロックします**。過度に使用すると、アプリが遅くなります。可能な限り [`useEffect` を使用してください](/reference/react/useEffect)。\n\n* `useLayoutEffect` 内で state の更新をトリガすると、React は `useEffect` も含む残りのエフェクトをすべて即座に実行します。\n\n---\n\n## 使用法 {/*usage*/}\n\n### ブラウザが画面を再描画する前にレイアウトを測定する {/*measuring-layout-before-the-browser-repaints-the-screen*/}\n\nほとんどのコンポーネントは、何をレンダーするかを決定するために、画面上での位置やサイズを知る必要はありません。単に JSX を返します。その後、ブラウザがその*レイアウト*（位置とサイズ）を計算し、画面を再描画します。\n\nしかし、それだけでは不十分な場合もあります。例えば、ある要素をホバーしたときに近くに表示されるツールチップを想像してみてください。十分なスペースがある場合、ツールチップは要素の上に表示されるべきですが、収まらない場合は下に表示されるとします。ツールチップを正しい最終位置にレンダーするためには、その高さ（つまり、上部に収まるかどうか）を知る必要があります。\n\nこれを行うには、2 パスでレンダーを行う必要があります：\n\n1. ツールチップを（位置が間違っていても良いので）どこかにレンダーします。\n2. ツールチップの高さを測定し、ツールチップを配置する場所を決定します。\n3. ツールチップを正しい場所で*再度*レンダーします。\n\n**これらすべては、ブラウザが画面を再描画する前に行わなければなりません**。ユーザにツールチップが移動するのを見せたくないのです。ブラウザが画面を再描画する前にレイアウトの測定を行うために `useLayoutEffect` を呼び出します。\n\n```js {5-8}\nfunction Tooltip() {\n  const ref = useRef(null);\n  const [tooltipHeight, setTooltipHeight] = useState(0); // You don't know real height yet\n\n  useLayoutEffect(() => {\n    const { height } = ref.current.getBoundingClientRect();\n    setTooltipHeight(height); // Re-render now that you know the real height\n  }, []);\n\n  // ...use tooltipHeight in the rendering logic below...\n}\n```\n\n動作をステップごとに説明します。\n\n1. `Tooltip` は初回は `tooltipHeight = 0` でレンダーされます（そのため、ツールチップは間違った位置に配置される可能性があります）。\n2. React はそれを DOM に配置し、`useLayoutEffect` のコードを実行します。\n3. `useLayoutEffect` はツールチップの内容の[高さを測定](https://developer.mozilla.org/ja/docs/Web/API/Element/getBoundingClientRect)し、即時の再レンダーをトリガします。\n4. `Tooltip` は実際の `tooltipHeight` で再度レンダーされます（ツールチップが正しく配置されます）。\n5. React が DOM 上で更新を行い、最終的にブラウザがツールチップを表示します。\n\n以下のボタンにホバーしてみて、ツールチップが収まるかどうかに応じて位置を調整する様子を観察してください。\n\n<Sandpack>\n\n```js\nimport ButtonWithTooltip from './ButtonWithTooltip.js';\n\nexport default function App() {\n  return (\n    <div>\n      <ButtonWithTooltip\n        tooltipContent={\n          <div>\n            This tooltip does not fit above the button.\n            <br />\n            This is why it's displayed below instead!\n          </div>\n        }\n      >\n        Hover over me (tooltip above)\n      </ButtonWithTooltip>\n      <div style={{ height: 50 }} />\n      <ButtonWithTooltip\n        tooltipContent={\n          <div>This tooltip fits above the button</div>\n        }\n      >\n        Hover over me (tooltip below)\n      </ButtonWithTooltip>\n      <div style={{ height: 50 }} />\n      <ButtonWithTooltip\n        tooltipContent={\n          <div>This tooltip fits above the button</div>\n        }\n      >\n        Hover over me (tooltip below)\n      </ButtonWithTooltip>\n    </div>\n  );\n}\n```\n\n```js src/ButtonWithTooltip.js\nimport { useState, useRef } from 'react';\nimport Tooltip from './Tooltip.js';\n\nexport default function ButtonWithTooltip({ tooltipContent, ...rest }) {\n  const [targetRect, setTargetRect] = useState(null);\n  const buttonRef = useRef(null);\n  return (\n    <>\n      <button\n        {...rest}\n        ref={buttonRef}\n        onPointerEnter={() => {\n          const rect = buttonRef.current.getBoundingClientRect();\n          setTargetRect({\n            left: rect.left,\n            top: rect.top,\n            right: rect.right,\n            bottom: rect.bottom,\n          });\n        }}\n        onPointerLeave={() => {\n          setTargetRect(null);\n        }}\n      />\n      {targetRect !== null && (\n        <Tooltip targetRect={targetRect}>\n          {tooltipContent}\n        </Tooltip>\n      )\n    }\n    </>\n  );\n}\n```\n\n```js src/Tooltip.js active\nimport { useRef, useLayoutEffect, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport TooltipContainer from './TooltipContainer.js';\n\nexport default function Tooltip({ children, targetRect }) {\n  const ref = useRef(null);\n  const [tooltipHeight, setTooltipHeight] = useState(0);\n\n  useLayoutEffect(() => {\n    const { height } = ref.current.getBoundingClientRect();\n    setTooltipHeight(height);\n    console.log('Measured tooltip height: ' + height);\n  }, []);\n\n  let tooltipX = 0;\n  let tooltipY = 0;\n  if (targetRect !== null) {\n    tooltipX = targetRect.left;\n    tooltipY = targetRect.top - tooltipHeight;\n    if (tooltipY < 0) {\n      // It doesn't fit above, so place below.\n      tooltipY = targetRect.bottom;\n    }\n  }\n\n  return createPortal(\n    <TooltipContainer x={tooltipX} y={tooltipY} contentRef={ref}>\n      {children}\n    </TooltipContainer>,\n    document.body\n  );\n}\n```\n\n```js src/TooltipContainer.js\nexport default function TooltipContainer({ children, x, y, contentRef }) {\n  return (\n    <div\n      style={{\n        position: 'absolute',\n        pointerEvents: 'none',\n        left: 0,\n        top: 0,\n        transform: `translate3d(${x}px, ${y}px, 0)`\n      }}\n    >\n      <div ref={contentRef} className=\"tooltip\">\n        {children}\n      </div>\n    </div>\n  );\n}\n```\n\n```css\n.tooltip {\n  color: white;\n  background: #222;\n  border-radius: 4px;\n  padding: 4px;\n}\n```\n\n</Sandpack>\n\n`Tooltip` コンポーネントを 2 パスで（初回は `tooltipHeight` が `0` で、2 回目は実際に測定した高さで）レンダーする必要があるにもかかわらず、最終結果だけが表示されることに注目してください。これが、この例で [`useEffect`](/reference/react/useEffect) の代わりに `useLayoutEffect` が必要な理由です。詳細な違いは以下の通りです。\n\n<Recipes titleText=\"useLayoutEffect vs useEffect\" titleId=\"examples\">\n\n#### `useLayoutEffect` はブラウザの再描画をブロックする {/*uselayouteffect-blocks-the-browser-from-repainting*/}\n\nReact は、`useLayoutEffect` 内のコードとその中でスケジュールされたすべての state 更新が、**ブラウザが画面を再描画する前に**処理されることを保証します。これにより、ツールチップをレンダーし、測定し、再度レンダーするという処理を、ユーザが最初の余分なレンダーに気付かないように行うことができます。言い換えると、`useLayoutEffect` はブラウザの描画をブロックします。\n\n<Sandpack>\n\n```js\nimport ButtonWithTooltip from './ButtonWithTooltip.js';\n\nexport default function App() {\n  return (\n    <div>\n      <ButtonWithTooltip\n        tooltipContent={\n          <div>\n            This tooltip does not fit above the button.\n            <br />\n            This is why it's displayed below instead!\n          </div>\n        }\n      >\n        Hover over me (tooltip above)\n      </ButtonWithTooltip>\n      <div style={{ height: 50 }} />\n      <ButtonWithTooltip\n        tooltipContent={\n          <div>This tooltip fits above the button</div>\n        }\n      >\n        Hover over me (tooltip below)\n      </ButtonWithTooltip>\n      <div style={{ height: 50 }} />\n      <ButtonWithTooltip\n        tooltipContent={\n          <div>This tooltip fits above the button</div>\n        }\n      >\n        Hover over me (tooltip below)\n      </ButtonWithTooltip>\n    </div>\n  );\n}\n```\n\n```js src/ButtonWithTooltip.js\nimport { useState, useRef } from 'react';\nimport Tooltip from './Tooltip.js';\n\nexport default function ButtonWithTooltip({ tooltipContent, ...rest }) {\n  const [targetRect, setTargetRect] = useState(null);\n  const buttonRef = useRef(null);\n  return (\n    <>\n      <button\n        {...rest}\n        ref={buttonRef}\n        onPointerEnter={() => {\n          const rect = buttonRef.current.getBoundingClientRect();\n          setTargetRect({\n            left: rect.left,\n            top: rect.top,\n            right: rect.right,\n            bottom: rect.bottom,\n          });\n        }}\n        onPointerLeave={() => {\n          setTargetRect(null);\n        }}\n      />\n      {targetRect !== null && (\n        <Tooltip targetRect={targetRect}>\n          {tooltipContent}\n        </Tooltip>\n      )\n    }\n    </>\n  );\n}\n```\n\n```js src/Tooltip.js active\nimport { useRef, useLayoutEffect, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport TooltipContainer from './TooltipContainer.js';\n\nexport default function Tooltip({ children, targetRect }) {\n  const ref = useRef(null);\n  const [tooltipHeight, setTooltipHeight] = useState(0);\n\n  useLayoutEffect(() => {\n    const { height } = ref.current.getBoundingClientRect();\n    setTooltipHeight(height);\n  }, []);\n\n  let tooltipX = 0;\n  let tooltipY = 0;\n  if (targetRect !== null) {\n    tooltipX = targetRect.left;\n    tooltipY = targetRect.top - tooltipHeight;\n    if (tooltipY < 0) {\n      // It doesn't fit above, so place below.\n      tooltipY = targetRect.bottom;\n    }\n  }\n\n  return createPortal(\n    <TooltipContainer x={tooltipX} y={tooltipY} contentRef={ref}>\n      {children}\n    </TooltipContainer>,\n    document.body\n  );\n}\n```\n\n```js src/TooltipContainer.js\nexport default function TooltipContainer({ children, x, y, contentRef }) {\n  return (\n    <div\n      style={{\n        position: 'absolute',\n        pointerEvents: 'none',\n        left: 0,\n        top: 0,\n        transform: `translate3d(${x}px, ${y}px, 0)`\n      }}\n    >\n      <div ref={contentRef} className=\"tooltip\">\n        {children}\n      </div>\n    </div>\n  );\n}\n```\n\n```css\n.tooltip {\n  color: white;\n  background: #222;\n  border-radius: 4px;\n  padding: 4px;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### `useEffect` はブラウザをブロックしない {/*useeffect-does-not-block-the-browser*/}\n\nこれは同じ例ですが、`useLayoutEffect` の代わりに [`useEffect`](/reference/react/useEffect) を使用しています。遅いデバイスを使用している場合、ツールチップが修正前の初期位置に一瞬「ちらついて」見えることがあります。\n\n<Sandpack>\n\n```js\nimport ButtonWithTooltip from './ButtonWithTooltip.js';\n\nexport default function App() {\n  return (\n    <div>\n      <ButtonWithTooltip\n        tooltipContent={\n          <div>\n            This tooltip does not fit above the button.\n            <br />\n            This is why it's displayed below instead!\n          </div>\n        }\n      >\n        Hover over me (tooltip above)\n      </ButtonWithTooltip>\n      <div style={{ height: 50 }} />\n      <ButtonWithTooltip\n        tooltipContent={\n          <div>This tooltip fits above the button</div>\n        }\n      >\n        Hover over me (tooltip below)\n      </ButtonWithTooltip>\n      <div style={{ height: 50 }} />\n      <ButtonWithTooltip\n        tooltipContent={\n          <div>This tooltip fits above the button</div>\n        }\n      >\n        Hover over me (tooltip below)\n      </ButtonWithTooltip>\n    </div>\n  );\n}\n```\n\n```js src/ButtonWithTooltip.js\nimport { useState, useRef } from 'react';\nimport Tooltip from './Tooltip.js';\n\nexport default function ButtonWithTooltip({ tooltipContent, ...rest }) {\n  const [targetRect, setTargetRect] = useState(null);\n  const buttonRef = useRef(null);\n  return (\n    <>\n      <button\n        {...rest}\n        ref={buttonRef}\n        onPointerEnter={() => {\n          const rect = buttonRef.current.getBoundingClientRect();\n          setTargetRect({\n            left: rect.left,\n            top: rect.top,\n            right: rect.right,\n            bottom: rect.bottom,\n          });\n        }}\n        onPointerLeave={() => {\n          setTargetRect(null);\n        }}\n      />\n      {targetRect !== null && (\n        <Tooltip targetRect={targetRect}>\n          {tooltipContent}\n        </Tooltip>\n      )\n    }\n    </>\n  );\n}\n```\n\n```js src/Tooltip.js active\nimport { useRef, useEffect, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport TooltipContainer from './TooltipContainer.js';\n\nexport default function Tooltip({ children, targetRect }) {\n  const ref = useRef(null);\n  const [tooltipHeight, setTooltipHeight] = useState(0);\n\n  useEffect(() => {\n    const { height } = ref.current.getBoundingClientRect();\n    setTooltipHeight(height);\n  }, []);\n\n  let tooltipX = 0;\n  let tooltipY = 0;\n  if (targetRect !== null) {\n    tooltipX = targetRect.left;\n    tooltipY = targetRect.top - tooltipHeight;\n    if (tooltipY < 0) {\n      // It doesn't fit above, so place below.\n      tooltipY = targetRect.bottom;\n    }\n  }\n\n  return createPortal(\n    <TooltipContainer x={tooltipX} y={tooltipY} contentRef={ref}>\n      {children}\n    </TooltipContainer>,\n    document.body\n  );\n}\n```\n\n```js src/TooltipContainer.js\nexport default function TooltipContainer({ children, x, y, contentRef }) {\n  return (\n    <div\n      style={{\n        position: 'absolute',\n        pointerEvents: 'none',\n        left: 0,\n        top: 0,\n        transform: `translate3d(${x}px, ${y}px, 0)`\n      }}\n    >\n      <div ref={contentRef} className=\"tooltip\">\n        {children}\n      </div>\n    </div>\n  );\n}\n```\n\n```css\n.tooltip {\n  color: white;\n  background: #222;\n  border-radius: 4px;\n  padding: 4px;\n}\n```\n\n</Sandpack>\n\nバグを再現しやすくするため、このバージョンではレンダー中に人為的な遅延を追加しています。React は、`useEffect` 内の state 更新を処理する前に、ブラウザに画面を描画させます。その結果、ツールチップがちらつきます：\n\n<Sandpack>\n\n```js\nimport ButtonWithTooltip from './ButtonWithTooltip.js';\n\nexport default function App() {\n  return (\n    <div>\n      <ButtonWithTooltip\n        tooltipContent={\n          <div>\n            This tooltip does not fit above the button.\n            <br />\n            This is why it's displayed below instead!\n          </div>\n        }\n      >\n        Hover over me (tooltip above)\n      </ButtonWithTooltip>\n      <div style={{ height: 50 }} />\n      <ButtonWithTooltip\n        tooltipContent={\n          <div>This tooltip fits above the button</div>\n        }\n      >\n        Hover over me (tooltip below)\n      </ButtonWithTooltip>\n      <div style={{ height: 50 }} />\n      <ButtonWithTooltip\n        tooltipContent={\n          <div>This tooltip fits above the button</div>\n        }\n      >\n        Hover over me (tooltip below)\n      </ButtonWithTooltip>\n    </div>\n  );\n}\n```\n\n```js src/ButtonWithTooltip.js\nimport { useState, useRef } from 'react';\nimport Tooltip from './Tooltip.js';\n\nexport default function ButtonWithTooltip({ tooltipContent, ...rest }) {\n  const [targetRect, setTargetRect] = useState(null);\n  const buttonRef = useRef(null);\n  return (\n    <>\n      <button\n        {...rest}\n        ref={buttonRef}\n        onPointerEnter={() => {\n          const rect = buttonRef.current.getBoundingClientRect();\n          setTargetRect({\n            left: rect.left,\n            top: rect.top,\n            right: rect.right,\n            bottom: rect.bottom,\n          });\n        }}\n        onPointerLeave={() => {\n          setTargetRect(null);\n        }}\n      />\n      {targetRect !== null && (\n        <Tooltip targetRect={targetRect}>\n          {tooltipContent}\n        </Tooltip>\n      )\n    }\n    </>\n  );\n}\n```\n\n```js {expectedErrors: {'react-compiler': [10, 11]}} src/Tooltip.js active\nimport { useRef, useEffect, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport TooltipContainer from './TooltipContainer.js';\n\nexport default function Tooltip({ children, targetRect }) {\n  const ref = useRef(null);\n  const [tooltipHeight, setTooltipHeight] = useState(0);\n\n  // This artificially slows down rendering\n  let now = performance.now();\n  while (performance.now() - now < 100) {\n    // Do nothing for a bit...\n  }\n\n  useEffect(() => {\n    const { height } = ref.current.getBoundingClientRect();\n    setTooltipHeight(height);\n  }, []);\n\n  let tooltipX = 0;\n  let tooltipY = 0;\n  if (targetRect !== null) {\n    tooltipX = targetRect.left;\n    tooltipY = targetRect.top - tooltipHeight;\n    if (tooltipY < 0) {\n      // It doesn't fit above, so place below.\n      tooltipY = targetRect.bottom;\n    }\n  }\n\n  return createPortal(\n    <TooltipContainer x={tooltipX} y={tooltipY} contentRef={ref}>\n      {children}\n    </TooltipContainer>,\n    document.body\n  );\n}\n```\n\n```js src/TooltipContainer.js\nexport default function TooltipContainer({ children, x, y, contentRef }) {\n  return (\n    <div\n      style={{\n        position: 'absolute',\n        pointerEvents: 'none',\n        left: 0,\n        top: 0,\n        transform: `translate3d(${x}px, ${y}px, 0)`\n      }}\n    >\n      <div ref={contentRef} className=\"tooltip\">\n        {children}\n      </div>\n    </div>\n  );\n}\n```\n\n```css\n.tooltip {\n  color: white;\n  background: #222;\n  border-radius: 4px;\n  padding: 4px;\n}\n```\n\n</Sandpack>\n\nこの例を `useLayoutEffect` に置き換えて、レンダーが遅くなっても描画をブロックすることを確認してください。\n\n<Solution />\n\n</Recipes>\n\n<Note>\n\n2 パスでレンダーしてブラウザをブロックすることはパフォーマンスを低下させます。できる限り避けてください。\n\n</Note>\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### \"`useLayoutEffect` does nothing on the server\" というエラーが出る {/*im-getting-an-error-uselayouteffect-does-nothing-on-the-server*/}\n\n`useLayoutEffect` の目的は、コンポーネントがレンダーのために[レイアウト情報を使用できるようにする](#measuring-layout-before-the-browser-repaints-the-screen)ことです。\n\n1. 初期コンテンツをレンダーする。\n2. ブラウザが画面を再描画する*前に*レイアウトを測定する。\n3. 読み取ったレイアウト情報を使用して最終コンテンツをレンダーする。\n\nあなた、またはあなたのフレームワークが[サーバレンダリング](/reference/react-dom/server)を使用している場合、React アプリは初期表示のためにサーバ上で HTML にレンダーされます。これにより、JavaScript コードがロードされる前に初期 HTML を表示できます。\n\n問題は、サーバ上にはレイアウト情報がないことです。\n\n[先ほどの例](#measuring-layout-before-the-browser-repaints-the-screen)では、`Tooltip` コンポーネント内での `useLayoutEffect` の呼び出しにより、高さに応じて自身を正しくコンテンツの上または下に配置することができていました。初期のサーバ HTML の一部として `Tooltip` をレンダーしようとすると、これは不可能になります。サーバ上ではまだレイアウトが存在しないからです！ したがって、サーバ上でレンダーしても、JavaScript がロードされ実行された際に、クライアント上で位置の「ジャンプ」が起こってしまいます。\n\n通常、レイアウト情報に依存するようなコンポーネントは、サーバ上でレンダーする必要はありません。例えば、初期レンダー中に `Tooltip` を表示することはおそらく無意味です。これはクライアントでのユーザ操作に応じてトリガされるものだからです。\n\nしかし、この問題に直面している場合、いくつかの選択肢があります。\n\n- `useLayoutEffect` を [`useEffect`](/reference/react/useEffect) に置き換えます。これにより React に対して、初期レンダー結果の表示を描画をブロックせずに行ってよいことを伝えます（元の HTML はエフェクトが実行される前に表示されるからです）。\n\n- あるいは、[コンポーネントをクライアント専用としてマークします](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content)。これにより React に対して、サーバレンダリング中に最も近い [`<Suspense>`](/reference/react/Suspense) バウンダリまで、コンテンツをロード中というフォールバック（例えば、スピナやグリマー）に置き換えるように指示します。\n\n- あるいは、`useLayoutEffect` を持つコンポーネントをハイドレーションの後にのみレンダーします。`false` に初期化されたブーリアン型の `isMounted` という state を保持しておき、`useEffect` の呼び出し内で `true` に設定します。そしてレンダーロジックは `return isMounted ? <RealContent /> : <FallbackContent />` のようにします。サーバ上およびハイドレーション中は、ユーザは `useLayoutEffect` を呼び出さない `FallbackContent` を見ることになります。その後、React はそれをクライアント専用の `RealContent` に置き換えますが、そこには `useLayoutEffect` の呼び出しを含むことができます。\n\n- コンポーネントを外部データストアと同期させており、レイアウト測定とは異なる理由で `useLayoutEffect` を使っている場合、代わりに [`useSyncExternalStore`](/reference/react/useSyncExternalStore) を検討してみてください。これは[サーバレンダリングをサポートしています](/reference/react/useSyncExternalStore#adding-support-for-server-rendering)。\n"
  },
  {
    "path": "src/content/reference/react/useMemo.md",
    "content": "---\ntitle: useMemo\n---\n\n<Intro>\n\n`useMemo` は、レンダー間で計算結果をキャッシュするための React フックです。\n\n```js\nconst cachedValue = useMemo(calculateValue, dependencies)\n```\n\n</Intro>\n\n<Note>\n\n[React Compiler](/learn/react-compiler) は値や関数の自動的なメモ化を行うことで、手作業による `useMemo` 呼び出しの必要性を軽減します。自動でメモ化を行うためにコンパイラを使用可能です。\n\n</Note>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useMemo(calculateValue, dependencies)` {/*usememo*/}\n\nコンポーネントのトップレベルで `useMemo` を呼び出して、レンダー間で計算をキャッシュします。\n\n```js\nimport { useMemo } from 'react';\n\nfunction TodoList({ todos, tab }) {\n  const visibleTodos = useMemo(\n    () => filterTodos(todos, tab),\n    [todos, tab]\n  );\n  // ...\n}\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `calculateValue`: キャッシュしたい値を計算する関数。純関数で、引数を取らず、任意の型の何らかの値を返す必要があります。React は初回レンダー中にこの関数を呼び出します。次回以降のレンダーでは、直前のレンダーと `dependencies` が変化していなければ、同じ値を再度返します。`dependencies` が変化していれば、`calculateValue` を呼び出してその結果を返し、同時に、後から再利用するためにその結果を保存します。\n\n* `dependencies`: `calculateValue` のコード内で参照されているすべてのリアクティブ値の配列。リアクティブ値には、props、state、およびコンポーネント本体で直接宣言されているすべての変数と関数が含まれます。リンタが [React 向けに設定されている](/learn/editor-setup#linting)場合は、すべてのリアクティブ値が正しく依存値として指定されているかを確認します。依存配列は、`[dep1, dep2, dep3]` のようにインラインで記述され、配列の長さは一定である必要があります。各依存値は、[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) を用いて、前回の値と比較されます。\n\n#### 返り値 {/*returns*/}\n\n初回のレンダーでは、引数なしで `calculateValue` を呼び出した結果が、`useMemo` の返り値となります。\n\n次回以降のレンダーでは、依存配列が変化していない場合は、以前のレンダーで保存された値を返します。変化している場合は、`calculateValue` を再度呼び出し、その結果をそのまま返します。\n\n#### 注意点 {/*caveats*/}\n\n* `useMemo` はフックなので、カスタムフックか**コンポーネントのトップレベル**でしか呼び出すことができません。ループや条件分岐の中で呼び出すことはできません。もしループや条件分岐の中で呼び出したい場合は、新しいコンポーネントに切り出して、その中に state を移動させてください。\n* Strict Mode では、[純粋でない関数を見つけやすくするために](#my-calculation-runs-twice-on-every-re-render)、**計算関数 (`calculateValue`) が 2 度呼び出されます**。これは、開発時のみの挙動で、本番では影響は与えません。もし、計算関数が純粋であれば（純粋であるべきです）、2 回呼び出されてもコードに影響はありません。2 回の呼び出しのうち、一方の呼び出し結果は無視されます。\n* **特別な理由がない限り、キャッシュされた値が破棄されることはありません**。キャッシュが破棄されるケースの例としては、開発時にコンポーネントのファイルを編集した場合があります。また、開発時および本番時に、初回マウント中にコンポーネントがサスペンドすると、キャッシュは破棄されます。将来的には、キャッシュが破棄されることを前提とした機能が React に追加される可能性があります。例えば、将来的に仮想リストが組み込みでサポートされた場合、仮想テーブルのビューポートからスクロールアウトした項目は、キャッシュを破棄するようになるかもしれません。このような挙動は、パフォーマンス最適化のみを目的として `useMemo` を使っている場合には問題ありません。しかし、他の目的で利用している場合は、[state 変数](/reference/react/useState#avoiding-recreating-the-initial-state) や [ref](/reference/react/useRef#avoiding-recreating-the-ref-contents) を利用した方が良いかもしれません。\n\n<Note>\n\nこのような返り値のキャッシュは、[*メモ化 (memoization)*](https://en.wikipedia.org/wiki/Memoization) として知られており、それがこのフックが `useMemo` という名前である理由です。\n\n</Note>\n\n---\n\n## 使用法 {/*usage*/}\n\n### 高コストな再計算を避ける {/*skipping-expensive-recalculations*/}\n\n複数レンダーを跨いで計算をキャッシュするには、コンポーネントのトップレベルで `useMemo` を呼び出し、計算をラップします。\n\n```js [[3, 4, \"visibleTodos\"], [1, 4, \"() => filterTodos(todos, tab)\"], [2, 4, \"[todos, tab]\"]]\nimport { useMemo } from 'react';\n\nfunction TodoList({ todos, tab, theme }) {\n  const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);\n  // ...\n}\n```\n\n`useMemo` には、2 つの引数を渡す必要があります。\n\n1. `() =>` のように、引数を取らず、求めたい計算結果を返す<CodeStep step={1}>計算関数</CodeStep>。\n2. コンポーネント内にある値のうち、計算関数内で使用されているすべての値を含む、<CodeStep step={2}>依存配列</CodeStep>。\n\n初回レンダーでは、`useMemo` から返される<CodeStep step={3}>値</CodeStep>は、<CodeStep step={1}>計算関数</CodeStep>を呼び出した結果になります。\n\n次回以降のレンダーでは、今回のレンダー時に渡した<CodeStep step={2}>依存配列</CodeStep>と、前回のレンダー時に渡した依存配列が比較されます。（[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) で比較します。）依存値のいずれも変化していない場合、`useMemo` は以前に計算した値を返します。変化している場合は、再度計算が実行され、新しい値が返されます。\n\nつまり `useMemo` は、依存配列が変化しない限り、複数のレンダーを跨いで計算結果をキャッシュします。\n\n**これが役に立つ場面を見てみましょう。**\n\nReact では通常、再レンダーが発生するたびに、コンポーネント関数全体が再実行されます。例えば、以下の `TodoList` で、state が更新されたり、親から新しい props を受け取ったりした場合、`filterTodos` 関数が再実行されます。\n\n```js {2}\nfunction TodoList({ todos, tab, theme }) {\n  const visibleTodos = filterTodos(todos, tab);\n  // ...\n}\n```\n\nほとんどの計算は非常に高速に処理されるため、何か問題になることは通常ありません。しかし、巨大な配列をフィルタリング・変換している場合や、高コストな計算を行っている場合には、データが変わっていなければこれらの計算をスキップしたくなるでしょう。`todos` と `tab` の値が前回のレンダー時と同じ場合、先ほどのように計算を `useMemo` でラップすることで、以前に計算した `visibleTodos` を再利用することができるのです。\n\nこのようなキャッシュのことを、[*メモ化*](https://en.wikipedia.org/wiki/Memoization)と呼びます。\n\n<Note>\n\n**`useMemo` はパフォーマンス最適化のためにのみ利用するべきです**。`useMemo` を外すとコードが動作しない場合、その根本的な問題を見つけて修正してください。その上で、パフォーマンスを向上させるために `useMemo` を追加してください。\n\n</Note>\n\n<DeepDive>\n\n#### 計算コストが高いかどうかを見分ける方法 {/*how-to-tell-if-a-calculation-is-expensive*/}\n\n一般的に、何千ものオブジェクトを作成したりループしたりしていない限り、おそらく高価ではありません。より確信を持ちたい場合は、コンソールログを追加して、コードの実行にかかった時間を計測することができます。\n\n```js {1,3}\nconsole.time('filter array');\nconst visibleTodos = filterTodos(todos, tab);\nconsole.timeEnd('filter array');\n```\n\n測定したいユーザ操作（例えば、入力フィールドへのタイプ）を実行します。その後、コンソールに `filter array: 0.15ms` のようなログが表示されます。全体のログ時間がかなりの量（例えば `1ms` 以上）になる場合、その計算をメモ化する意味があるかもしれません。実験として `useMemo` で計算をラップしてみて、その操作に対する合計時間が減少したかどうかをログで確認できます。\n\n```js\nconsole.time('filter array');\nconst visibleTodos = useMemo(() => {\n  return filterTodos(todos, tab); // Skipped if todos and tab haven't changed\n}, [todos, tab]);\nconsole.timeEnd('filter array');\n```\n\n`useMemo` は*初回*レンダーを高速化しません。更新時に不要な作業をスキップするときにのみ役立ちます。\n\nまた、ほとんどの場合に、あなたが使っているマシンは、ユーザのマシンより高速に動作するであろうことを忘れてはいけません。そのため、意図的に処理速度を低下させてパフォーマンスをテストするのが良いでしょう。例えば、Chrome では [CPU スロットリング](https://developer.chrome.com/blog/new-in-devtools-61/#throttling)オプションが提供されています。\n\nまた、開発環境でのパフォーマンス測定では完全に正確な結果は得られないことに注意してください。（例えば、[Strict Mode](/reference/react/StrictMode) がオンの場合、各コンポーネントが 1 度ではなく 2 度レンダーされることがあります。）最も正確にパフォーマンスを計測するためには、アプリを本番環境用にビルドし、ユーザが持っているようなデバイスでテストしてください。\n\n</DeepDive>\n\n<DeepDive>\n\n#### あらゆる場所に useMemo を追加すべきか？ {/*should-you-add-usememo-everywhere*/}\n\nあなたのアプリがこのサイトのように、ほとんどのインタラクションが大まかなもの（ページ全体やセクション全体の置き換えなど）である場合、メモ化は通常不要です。一方、あなたのアプリが描画エディタのようなもので、ほとんどのインタラクションが細かなもの（図形を移動させるなど）である場合、メモ化は非常に役に立つでしょう。\n\n`useMemo` を利用した最適化が力を発揮するのは、以下のような、ほんの一部のケースに限られます。\n\n- `useMemo` で行う計算が著しく遅く、かつ、その依存値がほとんど変化しない場合。\n- 計算した値を、[`memo`](/reference/react/memo) でラップされたコンポーネントの props に渡す場合。この場合は、値が変化していない場合には再レンダーをスキップしたいでしょう。メモ化することで、依存値が異なる場合にのみコンポーネントを再レンダーさせることができます。\n- その値が、後で何らかのフックの依存値として使用されるケース。例えば、別の `useMemo` の計算結果がその値に依存している場合や、[`useEffect`](/reference/react/useEffect) がその値に依存している場合などです。\n\nこれらのケース以外では、計算を `useMemo` でラップすることにメリットはありません。それを行っても重大な害はないため、個別のケースを考えずに、可能な限りすべてをメモ化するようにしているチームもあります。このアプローチのデメリットは、コードが読みにくくなるという点です。また、すべてのメモ化が有効であるわけではありません。例えば、毎回変化する値が 1 つ存在するだけで、コンポーネント全体のメモ化が無意味になってしまうこともあります。\n\n**実際には、以下のいくつかの原則に従うことで、多くのメモ化を不要にすることができます**。\n\n1. コンポーネントが他のコンポーネントを視覚的にラップするときは、それが[子として JSX を受け入れるようにします](/learn/passing-props-to-a-component#passing-jsx-as-children)。これにより、ラッパコンポーネントが自身の state を更新しても、React はその子を再レンダーする必要がないことを認識します。\n1. ローカル state を優先し、必要以上に [state のリフトアップ](/learn/sharing-state-between-components)を行わないようにします。フォームや、アイテムがホバーされているかどうか、といった頻繁に変化する state は、ツリーのトップやグローバルの状態ライブラリに保持しないでください。\n1. [レンダーロジックを純粋に](/learn/keeping-components-pure)保ちます。コンポーネントの再レンダーが問題を引き起こしたり、何らかの目に見える視覚的な結果を生じたりする場合、それはあなたのコンポーネントのバグです！ メモ化を追加するのではなく、バグを修正します。\n1. [state を更新する不要なエフェクトを避けてください](/learn/you-might-not-need-an-effect)。React アプリケーションのパフォーマンス問題の大部分は、エフェクト内での連鎖的な state 更新によってコンポーネントのレンダーが何度も引き起こされるために生じます。\n1. [エフェクトから不要な依存値をできるだけ削除します](/learn/removing-effect-dependencies)。例えば、メモ化する代わりに、オブジェクトや関数をエフェクトの中や外に移動させるだけで、簡単に解決できる場合があります。\n\nそれでも特定のインタラクションが遅いと感じる場合は、[React Developer Tools のプロファイラを使用して](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html)、どのコンポーネントでのメモ化が最も有効かを確認し、そこでメモ化を行いましょう。これらの原則を守ることで、コンポーネントのデバッグや理解が容易になるため、常に原則に従うことをおすすめします。長期的には、この問題を一挙に解決できる[自動的なメモ化](https://www.youtube.com/watch?v=lGEMwh32soc)について研究を行っています。\n\n</DeepDive>\n\n<Recipes titleText=\"useMemo と値を直接計算することの違い\" titleId=\"examples-recalculation\">\n\n#### `useMemo` を利用して再計算をスキップする {/*skipping-recalculation-with-usememo*/}\n\nこの例では `filterTodos` の実装には**人為的な遅延が入っています**。そのため、レンダー中に呼び出す JavaScript の関数の処理が著しく遅い場合に、どうなるかを確認できます。タブを切り替えたり、テーマを切り替えてみてください。\n\nタブの切り替えが遅く感じられるのは、切り替えによって、遅延が入っている `filterTodos` 関数を再実行させてしまっているからです。この挙動は考えてみれば当たり前で、`tab` が変化したのなら、計算全体を再実行する*必要があるはずです*。（なぜ 2 回実行されるのか気になる場合は、[こちら](#my-calculation-runs-twice-on-every-re-render)を参照してください）\n\n次に、テーマを切り替えてみましょう。**`useMemo` があるおかげで、人為的な遅延が入っているにも関わらず、高速に動作しています！** `todos` と `tab`（`useMemo` の依存配列として渡している）が、前回のレンダー時から変化していないため、遅延が入っている `filterTodos` の呼び出しがスキップされています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { createTodos } from './utils.js';\nimport TodoList from './TodoList.js';\n\nconst todos = createTodos();\n\nexport default function App() {\n  const [tab, setTab] = useState('all');\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <button onClick={() => setTab('all')}>\n        All\n      </button>\n      <button onClick={() => setTab('active')}>\n        Active\n      </button>\n      <button onClick={() => setTab('completed')}>\n        Completed\n      </button>\n      <br />\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Dark mode\n      </label>\n      <hr />\n      <TodoList\n        todos={todos}\n        tab={tab}\n        theme={isDark ? 'dark' : 'light'}\n      />\n    </>\n  );\n}\n\n```\n\n```js src/TodoList.js active\nimport { useMemo } from 'react';\nimport { filterTodos } from './utils.js'\n\nexport default function TodoList({ todos, theme, tab }) {\n  const visibleTodos = useMemo(\n    () => filterTodos(todos, tab),\n    [todos, tab]\n  );\n  return (\n    <div className={theme}>\n      <p><b>Note: <code>filterTodos</code> is artificially slowed down!</b></p>\n      <ul>\n        {visibleTodos.map(todo => (\n          <li key={todo.id}>\n            {todo.completed ?\n              <s>{todo.text}</s> :\n              todo.text\n            }\n          </li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n```js src/utils.js\nexport function createTodos() {\n  const todos = [];\n  for (let i = 0; i < 50; i++) {\n    todos.push({\n      id: i,\n      text: \"Todo \" + (i + 1),\n      completed: Math.random() > 0.5\n    });\n  }\n  return todos;\n}\n\nexport function filterTodos(todos, tab) {\n  console.log('[ARTIFICIALLY SLOW] Filtering ' + todos.length + ' todos for \"' + tab + '\" tab.');\n  let startTime = performance.now();\n  while (performance.now() - startTime < 500) {\n    // Do nothing for 500 ms to emulate extremely slow code\n  }\n\n  return todos.filter(todo => {\n    if (tab === 'all') {\n      return true;\n    } else if (tab === 'active') {\n      return !todo.completed;\n    } else if (tab === 'completed') {\n      return todo.completed;\n    }\n  });\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-top: 10px;\n}\n\n.dark {\n  background-color: black;\n  color: white;\n}\n\n.light {\n  background-color: white;\n  color: black;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### 常に値を再計算する {/*always-recalculating-a-value*/}\n\nこの例でも、`filterTodos` の実装には**人為的な遅延が入っています**。そのため、レンダー中に呼び出す JavaScript の関数の処理が著しく遅い場合に、どうなるかを確認できます。タブを切り替えたり、テーマを切り替えてみてください。\n\n先ほどの例とは異なり、今回はテーマを切り替えたときも遅くなっています！ **今回のコードでは `useMemo` が利用されていない**ためです。そのため、人為的な遅延が入っている `filterTodos` が、再レンダーのたびに呼び出されてしまいます。`theme` だけが変化した場合でも、`filterTodos` が呼び出されてしまいます。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { createTodos } from './utils.js';\nimport TodoList from './TodoList.js';\n\nconst todos = createTodos();\n\nexport default function App() {\n  const [tab, setTab] = useState('all');\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <button onClick={() => setTab('all')}>\n        All\n      </button>\n      <button onClick={() => setTab('active')}>\n        Active\n      </button>\n      <button onClick={() => setTab('completed')}>\n        Completed\n      </button>\n      <br />\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Dark mode\n      </label>\n      <hr />\n      <TodoList\n        todos={todos}\n        tab={tab}\n        theme={isDark ? 'dark' : 'light'}\n      />\n    </>\n  );\n}\n\n```\n\n```js src/TodoList.js active\nimport { filterTodos } from './utils.js'\n\nexport default function TodoList({ todos, theme, tab }) {\n  const visibleTodos = filterTodos(todos, tab);\n  return (\n    <div className={theme}>\n      <ul>\n        <p><b>Note: <code>filterTodos</code> is artificially slowed down!</b></p>\n        {visibleTodos.map(todo => (\n          <li key={todo.id}>\n            {todo.completed ?\n              <s>{todo.text}</s> :\n              todo.text\n            }\n          </li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n```js src/utils.js\nexport function createTodos() {\n  const todos = [];\n  for (let i = 0; i < 50; i++) {\n    todos.push({\n      id: i,\n      text: \"Todo \" + (i + 1),\n      completed: Math.random() > 0.5\n    });\n  }\n  return todos;\n}\n\nexport function filterTodos(todos, tab) {\n  console.log('[ARTIFICIALLY SLOW] Filtering ' + todos.length + ' todos for \"' + tab + '\" tab.');\n  let startTime = performance.now();\n  while (performance.now() - startTime < 500) {\n    // Do nothing for 500 ms to emulate extremely slow code\n  }\n\n  return todos.filter(todo => {\n    if (tab === 'all') {\n      return true;\n    } else if (tab === 'active') {\n      return !todo.completed;\n    } else if (tab === 'completed') {\n      return todo.completed;\n    }\n  });\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-top: 10px;\n}\n\n.dark {\n  background-color: black;\n  color: white;\n}\n\n.light {\n  background-color: white;\n  color: black;\n}\n```\n\n</Sandpack>\n\n一方、こちらは**意図的な遅延を取り除いた同じコード**です。`useMemo` が無いことで、動作に影響があるでしょうか？\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { createTodos } from './utils.js';\nimport TodoList from './TodoList.js';\n\nconst todos = createTodos();\n\nexport default function App() {\n  const [tab, setTab] = useState('all');\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <button onClick={() => setTab('all')}>\n        All\n      </button>\n      <button onClick={() => setTab('active')}>\n        Active\n      </button>\n      <button onClick={() => setTab('completed')}>\n        Completed\n      </button>\n      <br />\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Dark mode\n      </label>\n      <hr />\n      <TodoList\n        todos={todos}\n        tab={tab}\n        theme={isDark ? 'dark' : 'light'}\n      />\n    </>\n  );\n}\n\n```\n\n```js src/TodoList.js active\nimport { filterTodos } from './utils.js'\n\nexport default function TodoList({ todos, theme, tab }) {\n  const visibleTodos = filterTodos(todos, tab);\n  return (\n    <div className={theme}>\n      <ul>\n        {visibleTodos.map(todo => (\n          <li key={todo.id}>\n            {todo.completed ?\n              <s>{todo.text}</s> :\n              todo.text\n            }\n          </li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n```js src/utils.js\nexport function createTodos() {\n  const todos = [];\n  for (let i = 0; i < 50; i++) {\n    todos.push({\n      id: i,\n      text: \"Todo \" + (i + 1),\n      completed: Math.random() > 0.5\n    });\n  }\n  return todos;\n}\n\nexport function filterTodos(todos, tab) {\n  console.log('Filtering ' + todos.length + ' todos for \"' + tab + '\" tab.');\n\n  return todos.filter(todo => {\n    if (tab === 'all') {\n      return true;\n    } else if (tab === 'active') {\n      return !todo.completed;\n    } else if (tab === 'completed') {\n      return todo.completed;\n    }\n  });\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-top: 10px;\n}\n\n.dark {\n  background-color: black;\n  color: white;\n}\n\n.light {\n  background-color: white;\n  color: black;\n}\n```\n\n</Sandpack>\n\n大抵の場合、メモ化を行わなくともコードは問題なく動作します。ユーザの操作感が十分に高速であれば、メモ化は不要でしょう。\n\n`utils.js` で TODO アイテムの数を増やし、どのように挙動が変化するかを確認してみましょう。この計算自体は、最初はそれほどコストは高くないものの、TODO の数が増大すると、フィルタリングではなく再レンダーがオーバーヘッドの大半を占めるようになります。続く内容では、`useMemo` を使って、どのように再レンダーを最適化できるかを確認していきます。\n\n<Solution />\n\n</Recipes>\n\n---\n\n### コンポーネントの再レンダーをスキップする {/*skipping-re-rendering-of-components*/}\n\n`useMemo` は、子コンポーネントの再レンダーのパフォーマンスを最適化する際にも役に立つことがあります。これを説明するために、`TodoList` コンポーネントが、子コンポーネントの `List` の props として、`visibleTodos` を渡すことを考えます。\n\n```js {5}\nexport default function TodoList({ todos, tab, theme }) {\n  // ...\n  return (\n    <div className={theme}>\n      <List items={visibleTodos} />\n    </div>\n  );\n}\n```\n\nprops である `theme` を変化させると一瞬アプリがフリーズしますが、`<List />` を JSX から削除すると、高速に動作するようになったはずです。すなわち、この `List` コンポーネントには最適化する価値があるということです。\n\n**通常、あるコンポーネントが再レンダーされたときは、その子コンポーネントも再帰的にすべて再レンダーされます**。これが、`TodoList` が異なる `theme` の値で再レンダーされたとき、`List` コンポーネントも*一緒に*再レンダーされる理由です。この動作は、再レンダーにそれほど多くの計算コストを必要としないコンポーネントには適しています。しかし、もし再レンダーが遅いと分かった場合は、`List` コンポーネントを [`memo`](/reference/react/memo) で囲うことで、与えられた props が前回のレンダーと同じである場合に `List` の再レンダーをスキップすることができます。\n\n```js {3,5}\nimport { memo } from 'react';\n\nconst List = memo(function List({ items }) {\n  // ...\n});\n```\n\n**この変更によって、props の全項目が前回のレンダーと*等しい*場合には、`List` の再レンダーはスキップされるようになります**。これが、計算のキャッシュが重要になる理由です！ `useMemo` を使わずに `visibleTodos` の計算を行うことを想像してみてください。\n\n```js {2-3,6-7}\nexport default function TodoList({ todos, tab, theme }) {\n  // Every time the theme changes, this will be a different array...\n  const visibleTodos = filterTodos(todos, tab);\n  return (\n    <div className={theme}>\n      {/* ... so List's props will never be the same, and it will re-render every time */}\n      <List items={visibleTodos} />\n    </div>\n  );\n}\n```\n\n**上記の例では、`filterTodos` 関数が毎回*異なる*配列を生成します。**（これは、`{}` というオブジェクトリテラルが、毎回新しいオブジェクトを生成することと似ています。）通常これが問題になることはありませんが、今回の場合は、`List` の props が毎回別の値になってしまいます。そのため、`memo` による最適化が意味をなさなくなってしまうのです。ここで、`useMemo` が役に立ちます。\n\n```js {2-3,5,9-10}\nexport default function TodoList({ todos, tab, theme }) {\n  // Tell React to cache your calculation between re-renders...\n  const visibleTodos = useMemo(\n    () => filterTodos(todos, tab),\n    [todos, tab] // ...so as long as these dependencies don't change...\n  );\n  return (\n    <div className={theme}>\n      {/* ...List will receive the same props and can skip re-rendering */}\n      <List items={visibleTodos} />\n    </div>\n  );\n}\n```\n\n\n**`visibleTodos` の計算を `useMemo` でラップすることで、複数の再レンダーの間でその結果が同じになることを保証できます**（依存配列が変わらない限り）。通常、特別な理由がなければ、計算を `useMemo` でラップする*必要はありません*。この例では、[`memo`](/reference/react/memo) で囲われたコンポーネントに値を渡しておりレンダーのスキップができるということが、その特別な理由にあたります。他にも `useMemo` を追加する動機はいくつかあり、このページで詳しく解説していきます。\n\n<DeepDive>\n\n#### 個々の JSX ノードをメモ化する {/*memoizing-individual-jsx-nodes*/}\n\n`List` を [`memo`](/reference/react/memo) でラップする代わりに、`<List />` JSX ノード自体を `useMemo` でラップしても構いません。\n\n```js {3,6}\nexport default function TodoList({ todos, tab, theme }) {\n  const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);\n  const children = useMemo(() => <List items={visibleTodos} />, [visibleTodos]);\n  return (\n    <div className={theme}>\n      {children}\n    </div>\n  );\n}\n```\n\n挙動は同じになります。`visibleTodos` が変化していない場合は、`List` は再レンダーされません。\n\n`<List items={visibleTodos} />` のような JSX ノードは、`{ type: List, props: { items: visibleTodos } }` のようなオブジェクトと同じです。このオブジェクトを作成するコストは非常に小さいですが、React はその内容が前回の内容と同じかどうかは分かりません。そのため、React は、デフォルトで `List` コンポーネントを再レンダーするのです。\n\nしかし、React が前回のレンダー時と全く同じ JSX を見つけた場合、コンポーネントの再レンダーは行いません。これは、JSX ノードが[イミュータブル (immutable)](https://en.wikipedia.org/wiki/Immutable_object) であるためです。JSX ノードオブジェクトは時間が経過しても変化することはないため、再レンダーをスキップしてしまって問題ありません。しかし、これが機能するには、ノードが*真に全く同一のオブジェクトである必要があり*、コード上で同じように見えるだけでは不十分です。この例では、`useMemo` のおかげで、ノードが全く同じオブジェクトとなっているのです。\n\n`useMemo` を使って、JSX ノードを手動でラップするのは不便です。例えば、条件付きでラップすることはできません。そのため、通常は `useMemo` で JSX ノードをラップする代わりに、[`memo`](/reference/react/memo) でコンポーネントをでラップします。\n\n</DeepDive>\n\n<Recipes titleText=\"再レンダーをスキップする場合と毎回再レンダーを行う場合の違い\" titleId=\"examples-rerendering\">\n\n#### `useMemo` と `memo` を利用して再レンダーをスキップする {/*skipping-re-rendering-with-usememo-and-memo*/}\n\nこの例では、`List` コンポーネントには**人為的な遅延が入っています**。そのため、レンダー中に呼び出している React コンポーネントが著しく遅い場合の挙動を確認できます。タブを変更したり、テーマを切り替えたりしてみてください。\n\nタブの切り替えが遅く感じるのは、遅延が入っている `List` を再レンダーさせてしまっているからです。これは考えてみれば当然で、`tab` が変化したので、ユーザの新しい選択を画面に反映する必要があります。\n\n次に、テーマを切り替えてみましょう。**`useMemo` と [`memo`](/reference/react/memo) があるおかげで、人為的な遅延があるにも関わらず、高速に動作しています！** `visibleItems` 配列が前回のレンダー時から変化していないため、`List` は再レンダーをスキップしています。`visibleItems` 配列が変化していないのは、`todos` と `tab`（`useMemo` の依存配列として渡している）が、前回のレンダー時から変化していないからです。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { createTodos } from './utils.js';\nimport TodoList from './TodoList.js';\n\nconst todos = createTodos();\n\nexport default function App() {\n  const [tab, setTab] = useState('all');\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <button onClick={() => setTab('all')}>\n        All\n      </button>\n      <button onClick={() => setTab('active')}>\n        Active\n      </button>\n      <button onClick={() => setTab('completed')}>\n        Completed\n      </button>\n      <br />\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Dark mode\n      </label>\n      <hr />\n      <TodoList\n        todos={todos}\n        tab={tab}\n        theme={isDark ? 'dark' : 'light'}\n      />\n    </>\n  );\n}\n```\n\n```js src/TodoList.js active\nimport { useMemo } from 'react';\nimport List from './List.js';\nimport { filterTodos } from './utils.js'\n\nexport default function TodoList({ todos, theme, tab }) {\n  const visibleTodos = useMemo(\n    () => filterTodos(todos, tab),\n    [todos, tab]\n  );\n  return (\n    <div className={theme}>\n      <p><b>Note: <code>List</code> is artificially slowed down!</b></p>\n      <List items={visibleTodos} />\n    </div>\n  );\n}\n```\n\n```js {expectedErrors: {'react-compiler': [5, 6]}} src/List.js\nimport { memo } from 'react';\n\nconst List = memo(function List({ items }) {\n  console.log('[ARTIFICIALLY SLOW] Rendering <List /> with ' + items.length + ' items');\n  let startTime = performance.now();\n  while (performance.now() - startTime < 500) {\n    // Do nothing for 500 ms to emulate extremely slow code\n  }\n\n  return (\n    <ul>\n      {items.map(item => (\n        <li key={item.id}>\n          {item.completed ?\n            <s>{item.text}</s> :\n            item.text\n          }\n        </li>\n      ))}\n    </ul>\n  );\n});\n\nexport default List;\n```\n\n```js src/utils.js\nexport function createTodos() {\n  const todos = [];\n  for (let i = 0; i < 50; i++) {\n    todos.push({\n      id: i,\n      text: \"Todo \" + (i + 1),\n      completed: Math.random() > 0.5\n    });\n  }\n  return todos;\n}\n\nexport function filterTodos(todos, tab) {\n  return todos.filter(todo => {\n    if (tab === 'all') {\n      return true;\n    } else if (tab === 'active') {\n      return !todo.completed;\n    } else if (tab === 'completed') {\n      return todo.completed;\n    }\n  });\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-top: 10px;\n}\n\n.dark {\n  background-color: black;\n  color: white;\n}\n\n.light {\n  background-color: white;\n  color: black;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### 毎回コンポーネントを再レンダーする {/*always-re-rendering-a-component*/}\n\nこの例でも、`List` の実装には**人為的な遅延が入っています**。そのため、レンダー中に呼び出している React コンポーネントが著しく遅い場合の挙動を確認できます。タブを変更したり、テーマを切り替えたりしてみてください。\n\n先ほどの例とは異なり、今回はテーマの切り替え時も遅くなっています！ **今回のコードでは `useMemo` が利用されていない**ためです。そのため、`visibleTodos` は常に異なる配列となり、遅延が入っている `List` コンポーネントが、再レンダーをスキップすることができません。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { createTodos } from './utils.js';\nimport TodoList from './TodoList.js';\n\nconst todos = createTodos();\n\nexport default function App() {\n  const [tab, setTab] = useState('all');\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <button onClick={() => setTab('all')}>\n        All\n      </button>\n      <button onClick={() => setTab('active')}>\n        Active\n      </button>\n      <button onClick={() => setTab('completed')}>\n        Completed\n      </button>\n      <br />\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Dark mode\n      </label>\n      <hr />\n      <TodoList\n        todos={todos}\n        tab={tab}\n        theme={isDark ? 'dark' : 'light'}\n      />\n    </>\n  );\n}\n```\n\n```js src/TodoList.js active\nimport List from './List.js';\nimport { filterTodos } from './utils.js'\n\nexport default function TodoList({ todos, theme, tab }) {\n  const visibleTodos = filterTodos(todos, tab);\n  return (\n    <div className={theme}>\n      <p><b>Note: <code>List</code> is artificially slowed down!</b></p>\n      <List items={visibleTodos} />\n    </div>\n  );\n}\n```\n\n```js {expectedErrors: {'react-compiler': [5, 6]}} src/List.js\nimport { memo } from 'react';\n\nconst List = memo(function List({ items }) {\n  console.log('[ARTIFICIALLY SLOW] Rendering <List /> with ' + items.length + ' items');\n  let startTime = performance.now();\n  while (performance.now() - startTime < 500) {\n    // Do nothing for 500 ms to emulate extremely slow code\n  }\n\n  return (\n    <ul>\n      {items.map(item => (\n        <li key={item.id}>\n          {item.completed ?\n            <s>{item.text}</s> :\n            item.text\n          }\n        </li>\n      ))}\n    </ul>\n  );\n});\n\nexport default List;\n```\n\n```js src/utils.js\nexport function createTodos() {\n  const todos = [];\n  for (let i = 0; i < 50; i++) {\n    todos.push({\n      id: i,\n      text: \"Todo \" + (i + 1),\n      completed: Math.random() > 0.5\n    });\n  }\n  return todos;\n}\n\nexport function filterTodos(todos, tab) {\n  return todos.filter(todo => {\n    if (tab === 'all') {\n      return true;\n    } else if (tab === 'active') {\n      return !todo.completed;\n    } else if (tab === 'completed') {\n      return todo.completed;\n    }\n  });\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-top: 10px;\n}\n\n.dark {\n  background-color: black;\n  color: white;\n}\n\n.light {\n  background-color: white;\n  color: black;\n}\n```\n\n</Sandpack>\n\n一方、こちらは**意図的な遅延を取り除いた**コードです。`useMemo` が無いことで、動作に影響があるでしょうか？\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport { createTodos } from './utils.js';\nimport TodoList from './TodoList.js';\n\nconst todos = createTodos();\n\nexport default function App() {\n  const [tab, setTab] = useState('all');\n  const [isDark, setIsDark] = useState(false);\n  return (\n    <>\n      <button onClick={() => setTab('all')}>\n        All\n      </button>\n      <button onClick={() => setTab('active')}>\n        Active\n      </button>\n      <button onClick={() => setTab('completed')}>\n        Completed\n      </button>\n      <br />\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={isDark}\n          onChange={e => setIsDark(e.target.checked)}\n        />\n        Dark mode\n      </label>\n      <hr />\n      <TodoList\n        todos={todos}\n        tab={tab}\n        theme={isDark ? 'dark' : 'light'}\n      />\n    </>\n  );\n}\n```\n\n```js src/TodoList.js active\nimport List from './List.js';\nimport { filterTodos } from './utils.js'\n\nexport default function TodoList({ todos, theme, tab }) {\n  const visibleTodos = filterTodos(todos, tab);\n  return (\n    <div className={theme}>\n      <List items={visibleTodos} />\n    </div>\n  );\n}\n```\n\n```js src/List.js\nimport { memo } from 'react';\n\nfunction List({ items }) {\n  return (\n    <ul>\n      {items.map(item => (\n        <li key={item.id}>\n          {item.completed ?\n            <s>{item.text}</s> :\n            item.text\n          }\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nexport default memo(List);\n```\n\n```js src/utils.js\nexport function createTodos() {\n  const todos = [];\n  for (let i = 0; i < 50; i++) {\n    todos.push({\n      id: i,\n      text: \"Todo \" + (i + 1),\n      completed: Math.random() > 0.5\n    });\n  }\n  return todos;\n}\n\nexport function filterTodos(todos, tab) {\n  return todos.filter(todo => {\n    if (tab === 'all') {\n      return true;\n    } else if (tab === 'active') {\n      return !todo.completed;\n    } else if (tab === 'completed') {\n      return todo.completed;\n    }\n  });\n}\n```\n\n```css\nlabel {\n  display: block;\n  margin-top: 10px;\n}\n\n.dark {\n  background-color: black;\n  color: white;\n}\n\n.light {\n  background-color: white;\n  color: black;\n}\n```\n\n</Sandpack>\n\n大抵の場合、メモ化を行わなくともコードは問題なく動作します。ユーザの操作感が十分に高速であれば、メモ化は不要なのです。\n\nアプリが遅くなっている実際の要因が何なのか現実的に把握するためには、React を本番モードで実行し、[React Developer Tools](/learn/react-developer-tools) を無効にし、アプリのユーザが使用しているデバイスに近いデバイスを使用してください。\n\n<Solution />\n\n</Recipes>\n\n---\n\n### エフェクトが過度に実行されるのを抑制する {/*preventing-an-effect-from-firing-too-often*/}\n\n[エフェクト](/learn/synchronizing-with-effects)内で、以下のようにして何らかの値を使用したくなる場合があります。\n\n```js {4-7,10}\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  const options = {\n    serverUrl: 'https://localhost:1234',\n    roomId: roomId\n  }\n\n  useEffect(() => {\n    const connection = createConnection(options);\n    connection.connect();\n    // ...\n```\n\nしかしこれにより問題が生じます。[すべてのリアクティブ値はエフェクト内で依存値として宣言する必要があります](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency)。しかしこの `options` を依存値として宣言してしまうと、エフェクトがチャットルームへの再接続を繰り返すようになってしまいます。\n\n\n```js {5}\n  useEffect(() => {\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [options]); // 🔴 Problem: This dependency changes on every render\n  // ...\n```\n\nこれを修正するために、エフェクト内で使用されるオブジェクトを `useMemo` でラップすることができます。\n\n```js {4-9,16}\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  const options = useMemo(() => {\n    return {\n      serverUrl: 'https://localhost:1234',\n      roomId: roomId\n    };\n  }, [roomId]); // ✅ Only changes when roomId changes\n\n  useEffect(() => {\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [options]); // ✅ Only changes when options changes\n  // ...\n```\n\nこれで、`useMemo` がキャッシュ済みのオブジェクトを返している限り、`options` オブジェクトが再レンダー間で等しくなることが保証されます。\n\nしかし `useMemo` はパフォーマンス最適化のためのものであり、意味的な保証があるものではありません。[特定の理由](#caveats)がある場合は、React はキャッシュされた値を破棄することがあります。これによりエフェクトも再実行されるため、エフェクト*内*にオブジェクトを移動することで**このような依存値自体を不必要にする**方がより良いでしょう。\n\n```js {5-8,13}\nfunction ChatRoom({ roomId }) {\n  const [message, setMessage] = useState('');\n\n  useEffect(() => {\n    const options = { // ✅ No need for useMemo or object dependencies!\n      serverUrl: 'https://localhost:1234',\n      roomId: roomId\n    }\n\n    const connection = createConnection(options);\n    connection.connect();\n    return () => connection.disconnect();\n  }, [roomId]); // ✅ Only changes when roomId changes\n  // ...\n```\n\nこれでコードはよりシンプルになり、`useMemo` も不要となりました。[エフェクトから依存値を取り除く方法](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect)について参照してください。\n\n\n### 他のフックに渡す依存値をメモ化する {/*memoizing-a-dependency-of-another-hook*/}\n\nある計算が、コンポーネントの本体で直接作成されたオブジェクトに依存しているとしましょう。\n\n```js {2}\nfunction Dropdown({ allItems, text }) {\n  const searchOptions = { matchMode: 'whole-word', text };\n\n  const visibleItems = useMemo(() => {\n    return searchItems(allItems, searchOptions);\n  }, [allItems, searchOptions]); // 🚩 Caution: Dependency on an object created in the component body\n  // ...\n```\n\nこのようなオブジェクトを依存値として使うとメモ化の意味がなくなってしまいます。コンポーネントが再レンダーされたとき、コンポーネントの本体に含まれるコードはすべて再実行されます。**`searchOptions` オブジェクトを作成するコードも、毎回再実行されます。**`searchOptions` は `useMemo` の依存値であり、毎回異なる値となるため、依存値が変化したと判断され、`searchItems` が毎回再計算されます。\n\nこれを修正するには、`searchOptions` オブジェクトを依存配列に渡す前に、`searchOptions` オブジェクト自体をメモ化しましょう。\n\n```js {2-4}\nfunction Dropdown({ allItems, text }) {\n  const searchOptions = useMemo(() => {\n    return { matchMode: 'whole-word', text };\n  }, [text]); // ✅ Only changes when text changes\n\n  const visibleItems = useMemo(() => {\n    return searchItems(allItems, searchOptions);\n  }, [allItems, searchOptions]); // ✅ Only changes when allItems or searchOptions changes\n  // ...\n```\n\n上記の例では、`text` が変化しなければ、`searchOptions` オブジェクトも変化しません。しかし、さらに良い修正方法は、`searchOptions` オブジェクトの宣言を `useMemo` の計算関数の*中に*移動することです。\n\n```js {3}\nfunction Dropdown({ allItems, text }) {\n  const visibleItems = useMemo(() => {\n    const searchOptions = { matchMode: 'whole-word', text };\n    return searchItems(allItems, searchOptions);\n  }, [allItems, text]); // ✅ Only changes when allItems or text changes\n  // ...\n```\n\nこれで、計算が直接 `text` に依存するようになりました。（`text` は文字列なので「意図せず」変化してしまうことはありません。）\n\n---\n\n### 関数をメモ化する {/*memoizing-a-function*/}\n\n`Form` コンポーネントが [`memo`](/reference/react/memo) でラップされているとします。関数を props として渡してみましょう。\n\n```js {2-7}\nexport default function ProductPage({ productId, referrer }) {\n  function handleSubmit(orderDetails) {\n    post('/product/' + productId + '/buy', {\n      referrer,\n      orderDetails\n    });\n  }\n\n  return <Form onSubmit={handleSubmit} />;\n}\n```\n\n`{}` が異なるオブジェクトを生成するのと同様に、`function() {}` のような関数宣言や、`() => {}` のような関数式もまた、レンダーごとに*異なる*関数を生成します。新しい関数が生成されること自体は問題ではなく、避けるべきことでもありません。しかし、`Form` コンポーネントがメモ化されている状況では、`Form` の props に渡す値が変わっていない場合は `Form` の再レンダーをスキップしたいと考えるでしょう。毎回異なる値が props にあると、メモ化は無意味になってしまいます。\n\n`useMemo` で関数をメモ化する場合は、計算関数がさらに別の関数を返す必要があります。\n\n```js {2-3,8-9}\nexport default function Page({ productId, referrer }) {\n  const handleSubmit = useMemo(() => {\n    return (orderDetails) => {\n      post('/product/' + productId + '/buy', {\n        referrer,\n        orderDetails\n      });\n    };\n  }, [productId, referrer]);\n\n  return <Form onSubmit={handleSubmit} />;\n}\n```\n\nなんだか不恰好ですね！ **関数のメモ化はよくあることなので、それ専用の組み込みフックが提供されています**。余計な関数の入れ子を避けるには、**`useMemo` の代わりに [`useCallback`](/reference/react/useCallback) で関数をラップしましょう**。\n\n```js {2,7}\nexport default function Page({ productId, referrer }) {\n  const handleSubmit = useCallback((orderDetails) => {\n    post('/product/' + productId + '/buy', {\n      referrer,\n      orderDetails\n    });\n  }, [productId, referrer]);\n\n  return <Form onSubmit={handleSubmit} />;\n}\n```\n\n上記の 2 つの例は完全に等価です。`useCallback` のメリットは、余計な関数の入れ子が不要になることだけです。それ以外の違いは何もありません。[`useCallback` についての詳細は、こちらを参照してください。](/reference/react/useCallback)\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### 再レンダーのたびに計算が 2 回実行される {/*my-calculation-runs-twice-on-every-re-render*/}\n\n[Strict Mode](/reference/react/StrictMode) では、本来 1 回だけ関数が呼び出されるところで、2 回呼び出されることがあります。\n\n```js {2,5,6}\nfunction TodoList({ todos, tab }) {\n  // This component function will run twice for every render.\n\n  const visibleTodos = useMemo(() => {\n    // This calculation will run twice if any of the dependencies change.\n    return filterTodos(todos, tab);\n  }, [todos, tab]);\n\n  // ...\n```\n\nこれは想定通りの挙動であり、これでコードが壊れることがあってはいけません。\n\nこれは開発時のみの挙動で、開発者が[コンポーネントを純粋に保つ](/learn/keeping-components-pure)ために役立ちます。呼び出し結果のうちの 1 つが採用され、もう 1 つは無視されます。あなたが実装したコンポーネントと計算関数が純粋であれば、この挙動がロジックに影響を与えることはありません。しかし、もし意図せず純粋ではない関数になっていた場合は、この挙動によって間違いに気づき、修正することができます。\n\nたとえば、以下の計算関数は、props として受け取った配列の書き換え（ミューテーション）をしてしまっており、純粋ではありません。\n\n```js {2-3}\n  const visibleTodos = useMemo(() => {\n    // 🚩 Mistake: mutating a prop\n    todos.push({ id: 'last', text: 'Go for a walk!' });\n    const filtered = filterTodos(todos, tab);\n    return filtered;\n  }, [todos, tab]);\n```\n\nしかし、この関数は 2 度呼び出されるため、todo が 2 回追加されたことに気づくはずです。計算関数は、既存のオブジェクトを変更してはいけませんが、計算中に作成した*新しい*オブジェクトを変更することは問題ありません。たとえば、`filterTodos` 関数が常に*異なる配列*を返す場合は、*その配列*を変更しても問題ありません。\n\n```js {3,4}\n  const visibleTodos = useMemo(() => {\n    const filtered = filterTodos(todos, tab);\n    // ✅ Correct: mutating an object you created during the calculation\n    filtered.push({ id: 'last', text: 'Go for a walk!' });\n    return filtered;\n  }, [todos, tab]);\n```\n\n純関数について詳しく知るには、[コンポーネントを純粋に保つ](/learn/keeping-components-pure)を参照してください。\n\nまた、ミューテーションなしでオブジェクトを更新する方法については[オブジェクトの更新](/learn/updating-objects-in-state)を、ミューテーションなしで配列を更新する方法については[配列の更新](/learn/updating-arrays-in-state)を参照してください。\n\n---\n\n### `useMemo` の返り値が、オブジェクトではなく undefined になってしまう {/*my-usememo-call-is-supposed-to-return-an-object-but-returns-undefined*/}\n\n以下のコードはうまく動作しません。\n\n```js {1-2,5}\n  // 🔴 You can't return an object from an arrow function with () => {\n  const searchOptions = useMemo(() => {\n    matchMode: 'whole-word',\n    text: text\n  }, [text]);\n```\n\nJavaScript では、`() => {` というコードでアロー関数の本体を開始するため、`{` の波括弧はオブジェクトの一部にはなりません。したがってオブジェクトは返されず、ミスにつながります。`({` や `})` のように丸括弧を追加することで修正できます。\n\n```js {1-2,5}\n  // This works, but is easy for someone to break again\n  const searchOptions = useMemo(() => ({\n    matchMode: 'whole-word',\n    text: text\n  }), [text]);\n```\n\nしかし、これでもまだ混乱しやすく、誰かが丸括弧を削除してしまうと簡単に壊れてしまいます。\n\nこのミスを避けるために、明示的に `return` 文を書きましょう。\n\n```js {1-3,6-7}\n  // ✅ This works and is explicit\n  const searchOptions = useMemo(() => {\n    return {\n      matchMode: 'whole-word',\n      text: text\n    };\n  }, [text]);\n```\n\n---\n\n### コンポーネントがレンダーされるたびに `useMemo` 内の関数が再実行される {/*every-time-my-component-renders-the-calculation-in-usememo-re-runs*/}\n\n第 2 引数に依存配列を指定しているか確認してください！\n\n依存配列を忘れると、`useMemo` は毎回計算を再実行してしまいます。\n\n```js {2-3}\nfunction TodoList({ todos, tab }) {\n  // 🔴 Recalculates every time: no dependency array\n  const visibleTodos = useMemo(() => filterTodos(todos, tab));\n  // ...\n```\n\n第 2 引数に依存配列を渡した修正版は以下の通りです。\n\n```js {2-3}\nfunction TodoList({ todos, tab }) {\n  // ✅ Does not recalculate unnecessarily\n  const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);\n  // ...\n```\n\nこれで解決しない場合は、少なくとも 1 つの依存値が前回のレンダーと異なっていることが問題です。手動で依存値をコンソールに出力して、デバッグすることができます。\n\n```js\n  const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);\n  console.log([todos, tab]);\n```\n\nコンソール上で、別々の再レンダーによって表示された 2 つの配列を選びます。それぞれについて、配列を右クリックし、\"Store as a global variable（グローバル変数として保存）\" を選択することで、配列を保存することができます。1 回目に保存した配列が `temp1`、2 回目に保存した配列が `temp2` として保存されたとすると、ブラウザのコンソールを使用して、両方の配列の各依存値が同じかどうかを確認できます。\n\n```js\nObject.is(temp1[0], temp2[0]); // Is the first dependency the same between the arrays?\nObject.is(temp1[1], temp2[1]); // Is the second dependency the same between the arrays?\nObject.is(temp1[2], temp2[2]); // ... and so on for every dependency ...\n```\n\nメモ化を妨げている依存値を見つけたら、その依存値を削除する方法を探すか、その依存値も[メモ化](#memoizing-a-dependency-of-another-hook)しましょう。\n\n---\n\n### ループ内のリストの各項目について `useMemo` を呼び出したいが、禁止されている {/*i-need-to-call-usememo-for-each-list-item-in-a-loop-but-its-not-allowed*/}\n\n`Chart` コンポーネントが [`memo`](/reference/react/memo) でラップされているとします。`ReportList` コンポーネントが再レンダーされた場合でも、リスト内の各 `Chart` の再レンダーはスキップしたいです。ところが、以下のようにループ内で `useMemo` を呼び出すことはできません。\n\n```js {expectedErrors: {'react-compiler': [6]}} {5-11}\nfunction ReportList({ items }) {\n  return (\n    <article>\n      {items.map(item => {\n        // 🔴 You can't call useMemo in a loop like this:\n        const data = useMemo(() => calculateReport(item), [item]);\n        return (\n          <figure key={item.id}>\n            <Chart data={data} />\n          </figure>\n        );\n      })}\n    </article>\n  );\n}\n```\n\nその場合は、各アイテムをコンポーネントに切り出し、アイテムごとにデータをメモ化します。\n\n```js {5,12-18}\nfunction ReportList({ items }) {\n  return (\n    <article>\n      {items.map(item =>\n        <Report key={item.id} item={item} />\n      )}\n    </article>\n  );\n}\n\nfunction Report({ item }) {\n  // ✅ Call useMemo at the top level:\n  const data = useMemo(() => calculateReport(item), [item]);\n  return (\n    <figure>\n      <Chart data={data} />\n    </figure>\n  );\n}\n```\n\nあるいは、`useMemo` を削除し、`Report` 自体を [`memo`](/reference/react/memo) でラップすることでも解決できます。`item` が変化しない場合は、`Report` の再レンダーはスキップされ、`Chart` の再レンダーもスキップされます。\n\n```js {5,6,12}\nfunction ReportList({ items }) {\n  // ...\n}\n\nconst Report = memo(function Report({ item }) {\n  const data = calculateReport(item);\n  return (\n    <figure>\n      <Chart data={data} />\n    </figure>\n  );\n});\n```\n"
  },
  {
    "path": "src/content/reference/react/useOptimistic.md",
    "content": "---\ntitle: useOptimistic\n---\n\n<Intro>\n\n`useOptimistic` は、UI を楽観的 (optimistic) に更新するための React フックです。\n\n```js\nconst [optimisticState, setOptimistic] = useOptimistic(value, reducer?);\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useOptimistic(value, reducer?)` {/*useoptimistic*/}\n\nコンポーネントのトップレベルで `useOptimistic` を呼び出し、値に対する楽観的 state を作成します。\n\n```js\nimport { useOptimistic } from 'react';\n\nfunction MyComponent({name, todos}) {\n  const [optimisticAge, setOptimisticAge] = useOptimistic(28);\n  const [optimisticName, setOptimisticName] = useOptimistic(name);\n  const [optimisticTodos, setOptimisticTodos] = useOptimistic(todos, todoReducer);\n  // ...\n}\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `value`: 実行中 (pending) のアクションが存在しない場合に返される値。\n* **省略可能** `reducer(currentState, action)`: 楽観的 state の更新方法を定義するリデューサ関数。純関数である必要があり、現在の state とリデューサアクションを引数として受け取り、次の楽観的 state を返す。\n\n#### 返り値 {/*returns*/}\n\n`useOptimistic` は、厳密に 2 つの値を持つ配列を返します。\n\n1. `optimisticState`: 現在の楽観的 state。実行中のアクションがない場合は `value` と同じです。実行中のアクションがある場合は `reducer` が返した state（`reducer` を指定しなかった場合は set 関数に渡した値）と同じになります。\n2. [`set` 関数](#setoptimistic): アクション内で楽観的 state を別の値に更新できる関数。\n\n---\n\n### `set` 関数（`setOptimistic(optimisticState)` のように使う） {/*setoptimistic*/}\n\n`useOptimistic` が返す `set` 関数により、[アクション](reference/react/useTransition#functions-called-in-starttransition-are-called-actions)の実行中だけ state を更新できます。次の state を直接渡すことも、前の state から計算するための関数を渡すこともできます。\n\n```js\nconst [optimisticLike, setOptimisticLike] = useOptimistic(false);\nconst [optimisticSubs, setOptimisticSubs] = useOptimistic(subs);\n\nfunction handleClick() {\n  startTransition(async () => {\n    setOptimisticLike(true);\n    setOptimisticSubs(a => a + 1);\n    await saveChanges();\n  });\n}\n```\n\n#### 引数 {/*setoptimistic-parameters*/}\n\n* `optimisticState`: [アクション](reference/react/useTransition#functions-called-in-starttransition-are-called-actions)の実行中に楽観的 state として使いたい値。`useOptimistic` に `reducer` を渡している場合、この値は `reducer` の第 2 引数として渡されます。任意の型の値を渡せます。\n    * `optimisticState` に関数を渡した場合、それは*更新用関数 (updater function)* として扱われます。純関数である必要があり、楽観的 state を唯一の引数として受け取り、次の楽観的 state を返す必要があります。React は更新用関数をキューに積んでコンポーネントを再レンダーします。次回レンダー時に、React は [`useState` の更新用関数](/reference/react/useState#setstate-parameters) と同様の仕組みで、キューに積まれた更新用関数をひとつ前の state に順に適用していき次の state を計算します。\n\n#### 返り値 {/*setoptimistic-returns*/}\n\n`set` 関数に返り値はありません。\n\n#### 注意点 {/*setoptimistic-caveats*/}\n\n* `set` 関数は[アクション](reference/react/useTransition#functions-called-in-starttransition-are-called-actions)の内部で呼び出す必要があります。アクションの外でセッタ関数を呼ぶと、[React は警告を表示](#an-optimistic-state-update-occurred-outside-a-transition-or-action)し、楽観的 state が一瞬表示されます。\n\n<DeepDive>\n\n#### 楽観的 state の仕組み {/*how-optimistic-state-works*/}\n\n`useOptimistic` を使うと、アクションの実行中に一時的な値を表示できます。\n\n```js\nconst [value, setValue] = useState('a');\nconst [optimistic, setOptimistic] = useOptimistic(value);\n\nstartTransition(async () => {\n  setOptimistic('b');\n  const newValue = await saveChanges('b');\n  setValue(newValue);\n});\n```\n\nセッタ関数がアクションの内部で呼び出されると、`useOptimistic` は再レンダーをトリガし、アクションの実行中はその state を表示します。そうでない場合は、`useOptimistic` に渡した `value` が返されます。\n\nこの state は \"楽観的 (optimistic)\" と呼ばれます。実際にはアクションの完了まで時間がかかっているにもかかわらず、アクションの実行結果をユーザに即座に提示するために使われるからです。\n\n**更新の流れ**\n\n1. **即時更新**: `setOptimistic('b')` が呼ばれると、React は直ちに `'b'` でレンダーします。\n\n2. **（オプション）アクション内で await**: アクション内で await している間も、React は `'b'` を表示し続けます。\n\n3. **トランジションをスケジュール**: `setValue(newValue)` が本来の state への更新をスケジュールします。\n\n4. **（オプション）サスペンスを待機**: `newValue` がサスペンドした場合、React は `'b'` を表示し続けます。\n\n5. **単一レンダーでコミット**: 最終的に、`value` と `optimistic` の両方に `newValue` がコミットされます。\n\n楽観的 state を「クリア」するための余分なレンダーはありません。トランジションが完了すると、楽観的な state と本来の state が同一レンダー内で合流して一致するようになります。\n\n<Note>\n\n#### 楽観的 state は一時的なもの {/*optimistic-state-is-temporary*/}\n\n楽観的 state はアクションの実行中にのみレンダーされ、それ以外では `value` がレンダーされます。\n\n`saveChanges` が `'c'` を返した場合、`value` と `optimistic` はどちらも `'b'` ではなく `'c'` になります。\n\n</Note>\n\n**最終的な state が決まる仕組み**\n\nアクション終了後に何が表示されるかは、`useOptimistic` の `value` 引数で決まります。これは以下のどのパターンを使用するかによって変わります。\n\n- `useOptimistic(false)` のような**ハードコードされた値**: アクション終了後も `state` は `false` のままなので、UI は `false` を表示します。常に `false` から始まる保留中状態を表すのに有用です。\n\n- `useOptimistic(isLiked)` のように **props や state を渡すパターン**: アクション中に親が `isLiked` を更新すると、アクション完了後に新しい値が使われます。これにより UI がアクション結果を反映するようになります。\n\n- `useOptimistic(items, fn)` のような**リデューサパターン**: アクションの実行中に `items` が変化した場合、React は新しい `items` で `reducer` を再実行して state を再計算します。これにより、楽観的な追加が常に最新データに対して適用されます。\n\n**アクションが失敗したときの挙動**\n\nアクションがエラーをスローした場合にもトランザクションは終了し、React はその時点の `value` でレンダーを行います。通常、親は成功時にのみ `value` を更新するため、失敗時は `value` が変わらず、UI は楽観的更新前の表示に戻ります。エラーを捕捉してユーザにメッセージを表示することもできます。\n\n</DeepDive>\n\n---\n\n## 使用法 {/*usage*/}\n\n### コンポーネントに楽観的 state を追加する {/*adding-optimistic-state-to-a-component*/}\n\nコンポーネントのトップレベルで `useOptimistic` を呼び出し、1 つ以上の楽観的 state を宣言します。\n\n```js [[1, 4, \"age\"], [1, 5, \"name\"], [1, 6, \"todos\"], [2, 4, \"optimisticAge\"], [2, 5, \"optimisticName\"], [2, 6, \"optimisticTodos\"], [3, 4, \"setOptimisticAge\"], [3, 5, \"setOptimisticName\"], [3, 6, \"setOptimisticTodos\"], [4, 6, \"reducer\"]]\nimport { useOptimistic } from 'react';\n\nfunction MyComponent({age, name, todos}) {\n  const [optimisticAge, setOptimisticAge] = useOptimistic(age);\n  const [optimisticName, setOptimisticName] = useOptimistic(name);\n  const [optimisticTodos, setOptimisticTodos] = useOptimistic(todos, reducer);\n  // ...\n```\n\n`useOptimistic` は厳密に 2 つの値を持つ配列を返します。\n\n1. <CodeStep step={2}>楽観的 state</CodeStep>: 初期値は渡した <CodeStep step={1}>value</CodeStep> です。\n2. <CodeStep step={3}>set 関数</CodeStep>: [アクション](reference/react/useTransition#functions-called-in-starttransition-are-called-actions) の間だけ一時的に state を変更できます。\n   * <CodeStep step={4}>リデューサ</CodeStep>を渡した場合、楽観的 state を返す前に実行されます。\n\n<CodeStep step={2}>楽観的 state</CodeStep> を使うには、アクション内で `set` 関数を呼び出します。\n\nアクションとは `startTransition` 内で呼び出される関数です。\n\n```js {3}\nfunction onAgeChange(e) {\n  startTransition(async () => {\n    setOptimisticAge(42);\n    const newAge = await postAge(42);\n    setAge(newAge);\n  });\n}\n```\n\n`age` 自体は現在値のまま、React はまず楽観的 state である `42` を使ってレンダーします。アクションが POST を待機した後、`age` と `optimisticAge` の両方を `newAge` にしてレンダーします。\n\n[楽観的 state の仕組み](#how-optimistic-state-works)で詳細を確認できます。\n\n<Note>\n\n[アクションプロップ (Action props)](/reference/react/useTransition#exposing-action-props-from-components) を使う場合は、`startTransition` なしでセッタ関数を呼び出せます。\n\n```js [[3, 2, \"setOptimisticName\"]]\nasync function submitAction() {\n  setOptimisticName('Taylor');\n  await updateName('Taylor');\n}\n```\n\nこれが動作するのは、アクションプロップがすでに `startTransition` 内で呼び出されるようになっているためです。\n\n例は[アクションプロップで楽観的 state を使う](#using-optimistic-state-in-action-props)を参照してください。\n\n</Note>\n\n---\n\n### アクションプロップで楽観的 state を使う {/*using-optimistic-state-in-action-props*/}\n\n[アクションプロップ](/reference/react/useTransition#exposing-action-props-from-components)中では、`startTransition` なしで楽観的セッタ関数を直接呼び出せます。\n\n以下の例では、`<form>` の props である `submitAction` 内で楽観的 state を設定しています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState, startTransition } from 'react';\nimport EditName from './EditName';\n\nexport default function App() {\n  const [name, setName] = useState('Alice');\n  \n  return <EditName name={name} action={setName} />;\n}\n```\n\n```js src/EditName.js active\nimport { useOptimistic, startTransition } from 'react';\nimport { updateName } from './actions.js';\n\nexport default function EditName({ name, action }) {\n  const [optimisticName, setOptimisticName] = useOptimistic(name);\n\n  async function submitAction(formData) {\n    const newName = formData.get('name');\n    setOptimisticName(newName);\n    \n    const updatedName = await updateName(newName);\n    startTransition(() => {\n      action(updatedName);\n    })\n  }\n\n  return (\n    <form action={submitAction}>\n      <p>Your name is: {optimisticName}</p>\n      <p>\n        <label>Change it: </label>\n        <input\n          type=\"text\"\n          name=\"name\"\n          disabled={name !== optimisticName}\n        />\n      </p>\n    </form>\n  );\n}\n```\n\n```js src/actions.js hidden\nexport async function updateName(name) {\n  await new Promise((res) => setTimeout(res, 1000));\n  return name;\n}\n```\n\n</Sandpack>\n\nこの例では、ユーザがフォームのサブミット操作を行うと  `optimisticName` が即座に更新され、サーバリクエストが進行中の間、`newName` を楽観的に表示します。リクエストが完了すると、`name` と `optimisticName` がレスポンスの実際の `updatedName` となってレンダーされます。\n\n<DeepDive>\n\n#### この例で `startTransition` が不要である理由 {/*why-doesnt-this-need-starttransition*/}\n\n慣習として、`startTransition` の中で呼び出される props は \"Action\" を含む名前になります。\n\n`submitAction` が \"Action\" を含む名前なので、すでに `startTransition` の中で呼び出されていると分かるのです。\n\nアクションプロップパターンについては、[コンポーネントから `action` を props として公開する](/reference/react/useTransition#exposing-action-props-from-components) を参照してください。\n\n</DeepDive>\n\n---\n\n### アクションプロップに楽観的 state を追加する {/*adding-optimistic-state-to-action-props*/}\n\n[アクションプロップ](/reference/react/useTransition#exposing-action-props-from-components)を作るときは、`useOptimistic` を追加することで即時フィードバックを表示できます。\n\n以下は、`action` が実行中の間 \"Submitting...\" を表示するボタンです。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState, startTransition } from 'react';\nimport Button from './Button';\nimport { submitForm } from './actions.js';\n\nexport default function App() {\n  const [count, setCount] = useState(0);\n  return (\n    <div>\n      <Button action={async () => {         \n        await submitForm();\n        startTransition(() => {\n          setCount(c => c + 1);\n        });\n      }}>Increment</Button>\n      {count > 0 && <p>Submitted {count}!</p>}\n    </div>\n  );\n}\n```\n\n```js src/Button.js active\nimport { useOptimistic, startTransition } from 'react';\n\nexport default function Button({ action, children }) {\n  const [isPending, setIsPending] = useOptimistic(false);\n\n  return (\n    <button\n      disabled={isPending}\n      onClick={() => {\n        startTransition(async () => {\n          setIsPending(true);\n          await action();\n        });\n      }}\n    >\n      {isPending ? 'Submitting...' : children}\n    </button>\n  );\n}\n```\n\n```js src/actions.js hidden\nexport async function submitForm() {\n  await new Promise((res) => setTimeout(res, 1000));\n}\n```\n\n</Sandpack>\n\nボタンをクリックすると、`setIsPending(true)` が楽観的 state を使って即座に \"Submitting...\" を表示し、ボタンを無効化します。アクションが終わると、`isPending` が自動的に `false` となってレンダーされます。\n\nこのパターンを使うことで、props である `action` を `Button` とどのように組み合わせた場合でも保留中状態が自動で表示されます。\n\n```js\n// Show pending state for a state update\n<Button action={() => { setState(c => c + 1) }} />\n\n// Show pending state for a navigation\n<Button action={() => { navigate('/done') }} />\n\n// Show pending state for a POST\n<Button action={async () => { await fetch(/* ... */) }} />\n\n// Show pending state for any combination\n<Button action={async () => {\n  setState(c => c + 1);\n  await fetch(/* ... */);\n  navigate('/done');\n}} />\n```\n\n保留中状態は `action` 内のすべての処理が完了するまで表示されます。\n\n<Note>\n\n[`useTransition`](/reference/react/useTransition) を使って `isPending` 経由で保留中状態を取得することもできます。\n\n違いは、`useTransition` が `startTransition` 関数を提供する一方で、`useOptimistic` は任意のトランジションで動作することです。コンポーネントの要件に合う方を使ってください。\n\n</Note>\n\n---\n\n### props や state を楽観的に更新する {/*updating-props-or-state-optimistically*/}\n\nprops や state を `useOptimistic` でラップすることで、アクション実行中に即座に更新されるようにできます。\n\n以下の例では、`LikeButton` は `isLiked` を prop として受け取り、クリック時にそれを即座に切り替えます。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState, useOptimistic, startTransition } from 'react';\nimport { toggleLike } from './actions.js';\n\nexport default function App() {\n  const [isLiked, setIsLiked] = useState(false);\n  const [optimisticIsLiked, setOptimisticIsLiked] = useOptimistic(isLiked);\n\n  function handleClick() {\n    startTransition(async () => {\n      const newValue = !optimisticIsLiked\n      console.log('⏳ setting optimistic state: ' + newValue);\n      \n      setOptimisticIsLiked(newValue);\n      const updatedValue = await toggleLike(newValue);\n      \n      startTransition(() => {\n        console.log('⏳ setting real state: ' + updatedValue );\n        setIsLiked(updatedValue);\n      });\n    });\n  }\n\n  if (optimisticIsLiked !== isLiked) {\n    console.log('✅ rendering optimistic state: ' + optimisticIsLiked);  \n  } else {\n    console.log('✅ rendering real value: ' + optimisticIsLiked);\n  }\n  \n\n  return (\n    <button onClick={handleClick}>\n      {optimisticIsLiked ? '❤️ Unlike' : '🤍 Like'}\n    </button>\n  );\n}\n```\n\n```js src/actions.js hidden\nexport async function toggleLike(value) {\n  return await new Promise((res) => setTimeout(() => res(value), 1000));\n  // In a real app, this would update the server\n}\n```\n\n```js src/index.js hidden\nimport React from 'react';\nimport {createRoot} from 'react-dom/client';\nimport './styles.css';\n\nimport App from './App';\n\nconst root = createRoot(document.getElementById('root'));\n// Not using StrictMode so double render logs are not shown.\nroot.render(<App />);\n```\n\n</Sandpack>\n\nボタンがクリックされると、`setOptimisticIsLiked` が表示中の state を即座に更新し、ハートを「いいね済み」として表示します。その間、`await toggleLike` がバックグラウンドで実行されます。`await` が完了すると、親の `setIsLiked` が「本物」の state である `isLiked` を更新し、楽観的 state はこの新しい値に一致する形でレンダーされます。\n\n<Note>\n\nこの例では、次の値を計算するために `optimisticIsLiked` を読み取っています。これはベースの state が変化しない場合は機能しますが、アクションの実行中にベース state が変わる可能性がある場合は、state 更新用関数またはリデューサを使うほうがよいことがあります。\n\n例は[現在の state に基づいて state を更新する](#updating-state-based-on-current-state)を参照してください。\n\n</Note>\n\n---\n\n### 複数の値をまとめて更新する {/*updating-multiple-values-together*/}\n\n楽観的更新が複数の関連する値に影響する場合は、リデューサを使ってまとめて更新してください。これにより UI の一貫性を保つことができます。\n\n以下のフォローボタンでは、フォロー状態とフォロワー数を両方同時に更新します。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState, startTransition } from 'react';\nimport { followUser, unfollowUser } from './actions.js';\nimport FollowButton from './FollowButton';\n\nexport default function App() {\n  const [user, setUser] = useState({\n    name: 'React',\n    isFollowing: false,\n    followerCount: 10500\n  });\n\n  async function followAction(shouldFollow) {\n    if (shouldFollow) {\n      await followUser(user.name);\n    } else {\n      await unfollowUser(user.name);\n    }\n    startTransition(() => {\n      setUser(current => ({\n        ...current,\n        isFollowing: shouldFollow,\n        followerCount: current.followerCount + (shouldFollow ? 1 : -1)\n      }));\n    });\n  }\n\n  return <FollowButton user={user} followAction={followAction} />;\n}\n```\n\n```js src/FollowButton.js active\nimport { useOptimistic, startTransition } from 'react';\n\nexport default function FollowButton({ user, followAction }) {\n  const [optimisticState, updateOptimistic] = useOptimistic(\n    { isFollowing: user.isFollowing, followerCount: user.followerCount },\n    (current, isFollowing) => ({\n      isFollowing,\n      followerCount: current.followerCount + (isFollowing ? 1 : -1)\n    })\n  );\n\n  function handleClick() {\n    const newFollowState = !optimisticState.isFollowing;\n    startTransition(async () => {\n      updateOptimistic(newFollowState);\n      await followAction(newFollowState);\n    });\n  }\n\n  return (\n    <div>\n      <p><strong>{user.name}</strong></p>\n      <p>{optimisticState.followerCount} followers</p>\n      <button onClick={handleClick}>\n        {optimisticState.isFollowing ? 'Unfollow' : 'Follow'}\n      </button>\n    </div>\n  );\n}\n```\n\n```js src/actions.js hidden\nexport async function followUser(name) {\n  await new Promise((res) => setTimeout(res, 1000));\n}\n\nexport async function unfollowUser(name) {\n  await new Promise((res) => setTimeout(res, 1000));\n}\n```\n\n</Sandpack>\n\nリデューサは新しい `isFollowing` の値を受け取り、単一の更新で新しいフォロー状態と新しいフォロワー数の両方を計算します。これにより、ボタンテキストとフォロー数カウントが常に同期した状態を保てます。\n\n\n<DeepDive>\n\n#### 更新用関数とリデューサの使い分け {/*choosing-between-updaters-and-reducers*/}\n\n`useOptimistic` では、現在の state に基づいて state を計算するための 2 つのパターンがサポートされています。\n\n**更新用関数**は [useState の更新用関数](/reference/react/useState#updating-state-based-on-the-previous-state) と同様に動作します。セッタ関数に関数を渡してください。\n\n```js\nconst [optimistic, setOptimistic] = useOptimistic(value);\nsetOptimistic(current => !current);\n```\n\n**リデューサ**を使う場合、更新ロジックをセッタ呼び出しから分離できます。\n\n```js\nconst [optimistic, dispatch] = useOptimistic(value, (current, action) => {\n  // Calculate next state based on current and action\n});\ndispatch(action);\n```\n\n**更新用関数を使う**のは、セッタ呼び出し内だけで自然に更新内容を表現できる計算の場合です。これは `useState` で `setState(prev => ...)` を使うのに似ています。\n\n**リデューサを使う**のは、更新時にデータ（どの項目を追加するかなど）を渡す必要がある場合や、単一のフックで複数種類の更新を扱う場合です。\n\n**なぜリデューサを使うのでしょうか？**\n\nトランザクションの実行中にベースの state が変わる可能性がある場合、リデューサは不可欠です。add 処理の実行中に（たとえば別ユーザが todo を追加するなどで）`todos` が変化した場合、React は新しい `todos` でリデューサを再実行し、表示内容を再計算します。これにより、古くなったコピーではなく最新のリストに対して新しい todo を追加できるようになります。\n\n`setOptimistic(prev => [...prev, newItem])` のような更新用関数では、トランザクション開始時点の state しか見えないため、非同期処理中に発生した更新を取りこぼします。\n\n</DeepDive>\n\n---\n\n### 楽観的更新でリストに追加 {/*optimistically-adding-to-a-list*/}\n\nリストに項目を楽観的に追加したい場合、`reducer` を使用してください。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState, startTransition } from 'react';\nimport { addTodo } from './actions.js';\nimport TodoList from './TodoList';\n\nexport default function App() {\n  const [todos, setTodos] = useState([\n    { id: 1, text: 'Learn React' }\n  ]);\n\n  async function addTodoAction(newTodo) {\n    const savedTodo = await addTodo(newTodo);\n    startTransition(() => {\n      setTodos(todos => [...todos, savedTodo]);\n    });\n  }\n\n  return <TodoList todos={todos} addTodoAction={addTodoAction} />;\n}\n```\n\n```js src/TodoList.js active\nimport { useOptimistic, startTransition } from 'react';\n\nexport default function TodoList({ todos, addTodoAction }) {\n  const [optimisticTodos, addOptimisticTodo] = useOptimistic(\n    todos,\n    (currentTodos, newTodo) => [\n      ...currentTodos,\n      { id: newTodo.id, text: newTodo.text, pending: true }\n    ]\n  );\n\n  function handleAddTodo(text) {\n    const newTodo = { id: crypto.randomUUID(), text: text };\n    startTransition(async () => {\n      addOptimisticTodo(newTodo);\n      await addTodoAction(newTodo);\n    });\n  }\n\n  return (\n    <div>\n      <button onClick={() => handleAddTodo('New todo')}>Add Todo</button>\n      <ul>\n        {optimisticTodos.map(todo => (\n          <li key={todo.id}>\n            {todo.text} {todo.pending && \"(Adding...)\"}\n          </li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n```\n\n```js src/actions.js hidden\nexport async function addTodo(todo) {\n  await new Promise((res) => setTimeout(res, 1000));\n  // In a real app, this would save to the server\n  return { ...todo, pending: false };\n}\n```\n\n</Sandpack>\n\n`reducer` は現在の todo のリストと、追加対象の新しい todo を受け取ります。これが重要なのは、add 処理の実行中に（たとえば別ユーザが todo を追加するなどで）`todos` が変化した場合、React は更新後のリストでリデューサを再実行して楽観的 state を更新するからです。これにより、古くなったコピーではなく最新のリストに対して新しい todo を追加できるようになります。\n\n<Note>\n\n楽観的更新用のリストの各要素には `pending: true` フラグが含まれているため、要素ごとにローディング状態を表示できます。サーバが応答し、親が保存した要素を含んだ正規の `todos` リストで更新すると、楽観的 state は pending フラグのない確定済み項目に更新されます。\n\n</Note>\n\n---\n\n### 複数 `action` タイプの処理 {/*handling-multiple-action-types*/}\n\n処理すべき楽観的更新が複数ある（項目の追加と削除など）場合は、`action` オブジェクトを用いるリデューサパターンを使用してください。\n\n以下のショッピングカートの例は、単一のリデューサで追加と削除の両方を扱う方法を示しています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState, startTransition } from 'react';\nimport { addToCart, removeFromCart, updateQuantity } from './actions.js';\nimport ShoppingCart from './ShoppingCart';\n\nexport default function App() {\n  const [cart, setCart] = useState([]);\n\n  const cartActions = {\n    async add(item) {\n      await addToCart(item);\n      startTransition(() => {\n        setCart(current => {\n          const exists = current.find(i => i.id === item.id);\n          if (exists) {\n            return current.map(i =>\n              i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i\n            );\n          }\n          return [...current, { ...item, quantity: 1 }];\n        });\n      });\n    },\n    async remove(id) {\n      await removeFromCart(id);\n      startTransition(() => {\n        setCart(current => current.filter(item => item.id !== id));\n      });\n    },\n    async updateQuantity(id, quantity) {\n      await updateQuantity(id, quantity);\n      startTransition(() => {\n        setCart(current =>\n          current.map(item =>\n            item.id === id ? { ...item, quantity } : item\n          )\n        );\n      });\n    }\n  };\n\n  return <ShoppingCart cart={cart} cartActions={cartActions} />;\n}\n```\n\n```js src/ShoppingCart.js active\nimport { useOptimistic, startTransition } from 'react';\n\nexport default function ShoppingCart({ cart, cartActions }) {\n  const [optimisticCart, dispatch] = useOptimistic(\n    cart,\n    (currentCart, action) => {\n      switch (action.type) {\n        case 'add':\n          const exists = currentCart.find(item => item.id === action.item.id);\n          if (exists) {\n            return currentCart.map(item =>\n              item.id === action.item.id\n                ? { ...item, quantity: item.quantity + 1, pending: true }\n                : item\n            );\n          }\n          return [...currentCart, { ...action.item, quantity: 1, pending: true }];\n        case 'remove':\n          return currentCart.filter(item => item.id !== action.id);\n        case 'update_quantity':\n          return currentCart.map(item =>\n            item.id === action.id\n              ? { ...item, quantity: action.quantity, pending: true }\n              : item\n          );\n        default:\n          return currentCart;\n      }\n    }\n  );\n\n  function handleAdd(item) {\n    startTransition(async () => {\n      dispatch({ type: 'add', item });\n      await cartActions.add(item);\n    });\n  }\n\n  function handleRemove(id) {\n    startTransition(async () => {\n      dispatch({ type: 'remove', id });\n      await cartActions.remove(id);\n    });\n  }\n\n  function handleUpdateQuantity(id, quantity) {\n    startTransition(async () => {\n      dispatch({ type: 'update_quantity', id, quantity });\n      await cartActions.updateQuantity(id, quantity);\n    });\n  }\n\n  const total = optimisticCart.reduce(\n    (sum, item) => sum + item.price * item.quantity,\n    0\n  );\n\n  return (\n    <div>\n      <h2>Shopping Cart</h2>\n      <div style={{ marginBottom: 16 }}>\n        <button onClick={() => handleAdd({\n          id: 1, name: 'T-Shirt', price: 25\n        })}>\n          Add T-Shirt ($25)\n        </button>{' '}\n        <button onClick={() => handleAdd({\n          id: 2, name: 'Mug', price: 15\n        })}>\n          Add Mug ($15)\n        </button>\n      </div>\n      {optimisticCart.length === 0 ? (\n        <p>Your cart is empty</p>\n      ) : (\n        <ul>\n          {optimisticCart.map(item => (\n            <li key={item.id}>\n              {item.name} - ${item.price} ×\n              {item.quantity}\n              {' '}= ${item.price * item.quantity}\n              <button\n                onClick={() => handleRemove(item.id)}\n                style={{ marginLeft: 8 }}\n              >\n                Remove\n              </button>\n              {item.pending && ' ...'}\n            </li>\n          ))}\n        </ul>\n      )}\n      <p><strong>Total: ${total}</strong></p>\n    </div>\n  );\n}\n```\n\n```js src/actions.js hidden\nexport async function addToCart(item) {\n  await new Promise((res) => setTimeout(res, 800));\n}\n\nexport async function removeFromCart(id) {\n  await new Promise((res) => setTimeout(res, 800));\n}\n\nexport async function updateQuantity(id, quantity) {\n  await new Promise((res) => setTimeout(res, 800));\n}\n```\n\n</Sandpack>\n\nリデューサは 3 種類の `action` タイプ (`add`, `remove`, `update_quantity`) を処理し、それぞれについて新しい楽観的 state を返します。各 `action` は `pending: true` フラグを設定するため、[サーバ関数](/reference/rsc/server-functions)の実行中に視覚的なフィードバックを表示できます。\n\n---\n\n### エラーリカバリを伴う楽観的削除 {/*optimistic-delete-with-error-recovery*/}\n\n項目を楽観的に削除する場合、アクションが失敗するケースを扱う必要があります。\n\n以下の例では、削除に失敗したときにエラーメッセージを表示し、UI が自動でロールバックして項目が再表示される様子を示しています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState, startTransition } from 'react';\nimport { deleteItem } from './actions.js';\nimport ItemList from './ItemList';\n\nexport default function App() {\n  const [items, setItems] = useState([\n    { id: 1, name: 'Learn React' },\n    { id: 2, name: 'Build an app' },\n    { id: 3, name: 'Deploy to production' },\n  ]);\n\n  async function deleteAction(id) {\n    await deleteItem(id);\n    startTransition(() => {\n      setItems(current => current.filter(item => item.id !== id));\n    });\n  }\n\n  return <ItemList items={items} deleteAction={deleteAction} />;\n}\n```\n\n```js src/ItemList.js active\nimport { useState, useOptimistic, startTransition } from 'react';\n\nexport default function ItemList({ items, deleteAction }) {\n  const [error, setError] = useState(null);\n  const [optimisticItems, removeItem] = useOptimistic(\n    items,\n    (currentItems, idToRemove) =>\n      currentItems.map(item =>\n        item.id === idToRemove\n          ? { ...item, deleting: true }\n          : item\n      )\n  );\n\n  function handleDelete(id) {\n    setError(null);\n    startTransition(async () => {\n      removeItem(id);\n      try {\n        await deleteAction(id);\n      } catch (e) {\n        setError(e.message);\n      }\n    });\n  }\n\n  return (\n    <div>\n      <h2>Your Items</h2>\n      <ul>\n        {optimisticItems.map(item => (\n          <li\n            key={item.id}\n            style={{\n              opacity: item.deleting ? 0.5 : 1,\n              textDecoration: item.deleting ? 'line-through' : 'none',\n              transition: 'opacity 0.2s'\n            }}\n          >\n            {item.name}\n            <button\n              onClick={() => handleDelete(item.id)}\n              disabled={item.deleting}\n              style={{ marginLeft: 8 }}\n            >\n              {item.deleting ? 'Deleting...' : 'Delete'}\n            </button>\n          </li>\n        ))}\n      </ul>\n      {error && (\n        <p style={{ color: 'red', padding: 8, background: '#fee' }}>\n          {error}\n        </p>\n      )}\n    </div>\n  );\n}\n```\n\n```js src/actions.js hidden\nexport async function deleteItem(id) {\n  await new Promise((res) => setTimeout(res, 1000));\n  // Item 3 always fails to demonstrate error recovery\n  if (id === 3) {\n    throw new Error('Cannot delete. Permission denied.');\n  }\n}\n```\n\n</Sandpack>\n\n'Deploy to production' を削除してみてください。削除が失敗すると、該当項目が自動的にリスト内に再表示されます。\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### \"An optimistic state update occurred outside a Transition or Action\" というエラーが出る {/*an-optimistic-state-update-occurred-outside-a-transition-or-action*/}\n\n次のエラーが表示される場合があります：\n\n<ConsoleBlockMulti>\n\n<ConsoleLogLine level=\"error\">\n\nAn optimistic state update occurred outside a Transition or Action. To fix, move the update to an Action, or wrap with `startTransition`.\n\n</ConsoleLogLine>\n\n</ConsoleBlockMulti>\n\n楽観的セッタ関数は `startTransition` の中で呼び出す必要があります。\n\n```js\n// 🚩 Incorrect: outside a Transition\nfunction handleClick() {\n  setOptimistic(newValue);  // Warning!\n  // ...\n}\n\n// ✅ Correct: inside a Transition\nfunction handleClick() {\n  startTransition(async () => {\n    setOptimistic(newValue);\n    // ...\n  });\n}\n\n// ✅ Also correct: inside an Action prop\nfunction submitAction(formData) {\n  setOptimistic(newValue);\n  // ...\n}\n```\n\nセッタをアクション外で呼び出すと、楽観的 state が一瞬表示されたあと、すぐに元の値へ戻ります。これは、アクションの実行中に楽観的 state を「保持」するためのトランザクションが存在しないためです。\n\n### \"Cannot update optimistic state while rendering\" というエラーが出る {/*cannot-update-optimistic-state-while-rendering*/}\n\n以下のエラーが表示される場合があります。\n\n<ConsoleBlockMulti>\n\n<ConsoleLogLine level=\"error\">\n\nCannot update optimistic state while rendering.\n\n</ConsoleLogLine>\n\n</ConsoleBlockMulti>\n\nこのエラーは、コンポーネントのレンダーフェーズ中に楽観的セッタを呼び出したときに発生します。呼び出せるのはイベントハンドラ、エフェクト、またはその他のコールバックの中だけです。\n\n```js\n// 🚩 Incorrect: calling during render\nfunction MyComponent({ items }) {\n  const [isPending, setPending] = useOptimistic(false);\n\n  // This runs during render - not allowed!\n  setPending(true);\n  \n  // ...\n}\n\n// ✅ Correct: calling inside startTransition\nfunction MyComponent({ items }) {\n  const [isPending, setPending] = useOptimistic(false);\n\n  function handleClick() {\n    startTransition(() => {\n      setPending(true);\n      // ...\n    });\n  }\n\n  // ...\n}\n\n// ✅ Also correct: calling from an Action\nfunction MyComponent({ items }) {\n  const [isPending, setPending] = useOptimistic(false);\n\n  function action() {\n    setPending(true);\n    // ...\n  }\n\n  // ...\n}\n```\n\n### 楽観的更新で古い値が表示される {/*my-optimistic-updates-show-stale-values*/}\n\n楽観的 state が古いデータに基づいているように見える場合は、現在の state を基準に楽観的 state を計算するため、更新用関数またはリデューサの使用を検討してください。\n\n```js\n// May show stale data if state changes during Action\nconst [optimistic, setOptimistic] = useOptimistic(count);\nsetOptimistic(5);  // Always sets to 5, even if count changed\n\n// Better: relative updates handle state changes correctly\nconst [optimistic, adjust] = useOptimistic(count, (current, delta) => current + delta);\nadjust(1);  // Always adds 1 to whatever the current count is\n```\n\n詳しくは[現在の state に基づいて state を更新する](#updating-state-based-on-current-state)を参照してください。\n\n### 楽観的更新が実行中かどうか分からない {/*i-dont-know-if-my-optimistic-update-is-pending*/}\n\n`useOptimistic` が実行中 (pending) 状態かどうかを知る方法は 3 つあります。\n\n1. **`optimisticValue === value` を確認する**\n\n```js\nconst [optimistic, setOptimistic] = useOptimistic(value);\nconst isPending = optimistic !== value;\n```\n\n値が等しくない場合、進行中のトランザクションがあるということです。\n\n2. **`useTransition` を追加する**\n\n```js\nconst [isPending, startTransition] = useTransition();\nconst [optimistic, setOptimistic] = useOptimistic(value);\n\n//...\nstartTransition(() => {\n  setOptimistic(state);\n})\n```\n\n`useTransition` は内部的に `useOptimistic` を使用して `isPending` を取得しています。つまりこれは 1 の方法と等価です。\n\n3. **リデューサ内で `pending` フラグを追加する**\n\n```js\nconst [optimistic, addOptimistic] = useOptimistic(\n  items,\n  (state, newItem) => [...state, { ...newItem, isPending: true }]\n);\n```\n\nそれぞれの楽観的要素が独自のフラグを持つため、要素ごとにローディング状態を表示できます。\n"
  },
  {
    "path": "src/content/reference/react/useReducer.md",
    "content": "---\ntitle: useReducer\n---\n\n<Intro>\n\n`useReducer` は、[リデューサ (reducer)](/learn/extracting-state-logic-into-a-reducer) をコンポーネントに追加するための React フックです。\n\n```js\nconst [state, dispatch] = useReducer(reducer, initialArg, init?)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useReducer(reducer, initialArg, init?)` {/*usereducer*/}\n\n[リデューサ](/learn/extracting-state-logic-into-a-reducer) で state を管理するために、コンポーネントのトップレベルで `useReducer` を呼び出します。\n\n```js\nimport { useReducer } from 'react';\n\nfunction reducer(state, action) {\n  // ...\n}\n\nfunction MyComponent() {\n  const [state, dispatch] = useReducer(reducer, { age: 42 });\n  // ...\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `reducer`: state をどのように更新するかを指定するリデューサ関数です。純粋でなければならず、引数として state とアクションを取り、次の state を返します。state とアクションはどのような型でも大丈夫です。\n* `initialArg`: 初期 state が計算される元になる値です。任意の型の値を指定できます。どのように初期 state を計算するかは、次の `init` 引数に依存します。\n* **省略可能** `init`: 初期 state を返す初期化関数です。指定されていない場合、初期 state は `initialArg` そのものになります。指定されている場合、初期 state は `init(initialArg)` の結果になります。\n\n#### 返り値 {/*returns*/}\n\n`useReducer` は、2 つの値を持つ配列を返します：\n\n1. 現在の state。最初のレンダー中に、`init(initialArg)` または `initialArg`（`init` がない場合）が設定されます。\n2. state を別の値に更新し、再レンダーをトリガするための [`dispatch` 関数](#dispatch)。\n\n#### 注意点 {/*caveats*/}\n\n* `useReducer` はフックなので、**コンポーネントのトップレベル**または独自のカスタムフック内でのみ呼び出すことができます。ループや条件の中で呼び出すことはできません。必要な場合は、新しいコンポーネントとして抜き出し、その中に state を移動させてください。\n* `dispatch` 関数は常に同一のものとなるため、多くの場合エフェクトの依存配列では省略されますが、依存配列に含めてもエフェクトの再実行は起こりません。依存値を削除してもリンタがエラーを出さない場合、削除しても安全です。[エフェクトから依存値を取り除く方法](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect)を参照してください。\n* Strict Mode では、React は[純粋でない関数を見つけやすくするために](#my-reducer-or-initializer-function-runs-twice)、リデューサと初期化関数を 2 回呼び出します。これは開発時の動作であり、本番には影響しません。リデューサと初期化関数が純粋であれば（そうあるべきです）、これはロジックに影響しません。片方の呼び出しの結果は無視されます。\n\n---\n\n### `dispatch` 関数 {/*dispatch*/}\n\n`useReducer` によって返される `dispatch` 関数を使うことで、state を別の値に更新して再レンダーをトリガすることができます。`dispatch` 関数には、唯一の引数としてアクションを渡す必要があります。\n\n```js\nconst [state, dispatch] = useReducer(reducer, { age: 42 });\n\nfunction handleClick() {\n  dispatch({ type: 'incremented_age' });\n  // ...\n```\n\nReact がセットする次の state は、引数として `state` の現在値および `dispatch` に渡されたアクションを用いて `reducer` 関数を呼び出した結果になります。\n\n#### 引数 {/*dispatch-parameters*/}\n\n* `action`: ユーザによって実行されたアクションです。任意の型の値を指定できます。慣例として、アクションは通常オブジェクトであり、`type` プロパティで識別され、他のプロパティでオプションの追加情報を保持します。\n\n#### 返り値 {/*dispatch-returns*/}\n\n`dispatch` 関数には返り値はありません。\n\n#### 注意点 {/*setstate-caveats*/}\n\n* `dispatch` 関数は、**次のレンダーのための state 変数のみを更新**します。`dispatch` 関数を呼び出した後に state 変数を読み取ると、既に画面上に表示されている、`dispatch` 呼び出し前の[古い値を読み取ります](#ive-dispatched-an-action-but-logging-gives-me-the-old-state-value)。\n\n* 与えられた新しい値が、[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) の比較により、現在の `state` と同じと判断された場合、React は**コンポーネントとその子要素の再レンダーをスキップ**します。これは最適化です。React は結果を無視できる場合でも必要があってコンポーネントを呼び出すかもしれませんが、これがコードに影響してはいけません。\n\n* React は [state の更新のバッチ処理（batching, 束ね処理）](/learn/queueing-a-series-of-state-updates) を行います。これにより、**すべてのイベントハンドラが実行され**、それらの `set` 関数が呼び出された後に画面が更新されます。これにより、1 つのイベント中に複数回の再レンダーが発生するのを防ぐことができます。稀なケースとして、DOM にアクセスするなどの理由で React に画面を早期に強制的に更新させる必要がある場合は、[`flushSync`](/reference/react-dom/flushSync) を使用できます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### コンポーネントにリデューサを追加する {/*adding-a-reducer-to-a-component*/}\n\nコンポーネントのトップレベルで `useReducer` を呼び出して、[リデューサ](/learn/extracting-state-logic-into-a-reducer)を使って state を管理します。\n\n```js [[1, 8, \"state\"], [2, 8, \"dispatch\"], [4, 8, \"reducer\"], [3, 8, \"{ age: 42 }\"]]\nimport { useReducer } from 'react';\n\nfunction reducer(state, action) {\n  // ...\n}\n\nfunction MyComponent() {\n  const [state, dispatch] = useReducer(reducer, { age: 42 });\n  // ...\n```\n\n`useReducer` は、2 つの値を持つ配列を返します：\n\n1. この state 変数の<CodeStep step={1}>現在の state</CodeStep>。与えられた<CodeStep step={3}>初期 state</CodeStep> が初期値として設定されます。\n2. ユーザ操作に応じて state を変更するための<CodeStep step={2}>`dispatch` 関数</CodeStep>。\n\n画面上の内容を更新するには、*アクション*と呼ばれる、ユーザが行ったことを表すオブジェクトを引数として <CodeStep step={2}>`dispatch`</CodeStep> を呼び出します。\n\n```js [[2, 2, \"dispatch\"]]\nfunction handleClick() {\n  dispatch({ type: 'incremented_age' });\n}\n```\n\nReact は現在の state とアクションを<CodeStep step={4}>リデューサ関数</CodeStep>に渡します。リデューサは次の state を計算して返します。React はその次の state を保存するとともに、その state を使ってコンポーネントをレンダーし、UI を更新します。\n\n<Sandpack>\n\n```js\nimport { useReducer } from 'react';\n\nfunction reducer(state, action) {\n  if (action.type === 'incremented_age') {\n    return {\n      age: state.age + 1\n    };\n  }\n  throw Error('Unknown action.');\n}\n\nexport default function Counter() {\n  const [state, dispatch] = useReducer(reducer, { age: 42 });\n\n  return (\n    <>\n      <button onClick={() => {\n        dispatch({ type: 'incremented_age' })\n      }}>\n        Increment age\n      </button>\n      <p>Hello! You are {state.age}.</p>\n    </>\n  );\n}\n```\n\n```css\nbutton { display: block; margin-top: 10px; }\n```\n\n</Sandpack>\n\n`useReducer` は [`useState`](/reference/react/useState) と非常に似ていますが、イベントハンドラから state 更新ロジックをコンポーネントの外の単一の関数に移動することができます。詳しくは、[`useState` と `useReducer` の選び方](/learn/extracting-state-logic-into-a-reducer#comparing-usestate-and-usereducer)を参照ください。\n\n---\n\n### リデューサ関数の書き方 {/*writing-the-reducer-function*/}\n\nリデューサ関数は次のように宣言されます：\n\n```js\nfunction reducer(state, action) {\n  // ...\n}\n```\n\nその後、次の state を計算して返すコードを中に書いていきます。慣例として、[`switch` 文](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch)として書くことが一般的です。`switch` の各 `case` ごとに、次の state を計算して返します。\n\n```js {4-7,10-13}\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'incremented_age': {\n      return {\n        name: state.name,\n        age: state.age + 1\n      };\n    }\n    case 'changed_name': {\n      return {\n        name: action.nextName,\n        age: state.age\n      };\n    }\n  }\n  throw Error('Unknown action: ' + action.type);\n}\n```\n\nアクションの形はどのようなものでも構いません。慣例として、アクションを識別するための `type` プロパティを持ったオブジェクトを渡すことが一般的です。アクションにはリデューサが次の state を計算するために必要な最小限の情報を含めるべきです。\n\n```js {5,9-12}\nfunction Form() {\n  const [state, dispatch] = useReducer(reducer, { name: 'Taylor', age: 42 });\n  \n  function handleButtonClick() {\n    dispatch({ type: 'incremented_age' });\n  }\n\n  function handleInputChange(e) {\n    dispatch({\n      type: 'changed_name',\n      nextName: e.target.value\n    });\n  }\n  // ...\n```\n\nアクションのタイプ名はコンポーネント内にローカルなものです。[各アクションは、複数のデータの変更につながるものであっても、単一のユーザ操作を表すようにしてください](/learn/extracting-state-logic-into-a-reducer#writing-reducers-well)。state の形は任意ですが、通常はオブジェクトまたは配列になります。\n\n詳しくは、[リデューサへの state ロジックの抽出](/learn/extracting-state-logic-into-a-reducer)を参照ください。\n\n<Pitfall>\n\nstate は読み取り専用です。state 内のオブジェクトや配列を変更しないでください。\n\n```js {4,5}\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'incremented_age': {\n      // 🚩 Don't mutate an object in state like this:\n      state.age = state.age + 1;\n      return state;\n    }\n```\n\n代わりに、常に新しいオブジェクトをリデューサから返してください。\n\n```js {4-8}\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'incremented_age': {\n      // ✅ Instead, return a new object\n      return {\n        ...state,\n        age: state.age + 1\n      };\n    }\n```\n\n詳しくは、[state 内のオブジェクトの更新](/learn/updating-objects-in-state)と [state 内の配列の更新](/learn/updating-arrays-in-state)を参照ください。\n\n</Pitfall>\n\n<Recipes titleText=\"基本的な useReducer の例\" titleId=\"examples-basic\">\n\n#### フォーム（オブジェクト） {/*form-object*/}\n\nこの例では、リデューサが 2 つのフィールド（`name` と `age`）を持つ state オブジェクトを管理しています。\n\n<Sandpack>\n\n```js\nimport { useReducer } from 'react';\n\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'incremented_age': {\n      return {\n        name: state.name,\n        age: state.age + 1\n      };\n    }\n    case 'changed_name': {\n      return {\n        name: action.nextName,\n        age: state.age\n      };\n    }\n  }\n  throw Error('Unknown action: ' + action.type);\n}\n\nconst initialState = { name: 'Taylor', age: 42 };\n\nexport default function Form() {\n  const [state, dispatch] = useReducer(reducer, initialState);\n\n  function handleButtonClick() {\n    dispatch({ type: 'incremented_age' });\n  }\n\n  function handleInputChange(e) {\n    dispatch({\n      type: 'changed_name',\n      nextName: e.target.value\n    }); \n  }\n\n  return (\n    <>\n      <input\n        value={state.name}\n        onChange={handleInputChange}\n      />\n      <button onClick={handleButtonClick}>\n        Increment age\n      </button>\n      <p>Hello, {state.name}. You are {state.age}.</p>\n    </>\n  );\n}\n```\n\n```css\nbutton { display: block; margin-top: 10px; }\n```\n\n</Sandpack>\n\n<Solution />\n\n#### タスクリスト（配列） {/*todo-list-array*/}\n\nこの例では、リデューサがタスクの配列を管理しています。配列は[書き換えずに更新する](/learn/updating-arrays-in-state)必要があります。\n\n<Sandpack>\n\n```js src/App.js\nimport { useReducer } from 'react';\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\n\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case 'added': {\n      return [...tasks, {\n        id: action.id,\n        text: action.text,\n        done: false\n      }];\n    }\n    case 'changed': {\n      return tasks.map(t => {\n        if (t.id === action.task.id) {\n          return action.task;\n        } else {\n          return t;\n        }\n      });\n    }\n    case 'deleted': {\n      return tasks.filter(t => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nexport default function TaskApp() {\n  const [tasks, dispatch] = useReducer(\n    tasksReducer,\n    initialTasks\n  );\n\n  function handleAddTask(text) {\n    dispatch({\n      type: 'added',\n      id: nextId++,\n      text: text,\n    });\n  }\n\n  function handleChangeTask(task) {\n    dispatch({\n      type: 'changed',\n      task: task\n    });\n  }\n\n  function handleDeleteTask(taskId) {\n    dispatch({\n      type: 'deleted',\n      id: taskId\n    });\n  }\n\n  return (\n    <>\n      <h1>Prague itinerary</h1>\n      <AddTask\n        onAddTask={handleAddTask}\n      />\n      <TaskList\n        tasks={tasks}\n        onChangeTask={handleChangeTask}\n        onDeleteTask={handleDeleteTask}\n      />\n    </>\n  );\n}\n\nlet nextId = 3;\nconst initialTasks = [\n  { id: 0, text: 'Visit Kafka Museum', done: true },\n  { id: 1, text: 'Watch a puppet show', done: false },\n  { id: 2, text: 'Lennon Wall pic', done: false }\n];\n```\n\n```js src/AddTask.js hidden\nimport { useState } from 'react';\n\nexport default function AddTask({ onAddTask }) {\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button onClick={() => {\n        setText('');\n        onAddTask(text);\n      }}>Add</button>\n    </>\n  )\n}\n```\n\n```js src/TaskList.js hidden\nimport { useState } from 'react';\n\nexport default function TaskList({\n  tasks,\n  onChangeTask,\n  onDeleteTask\n}) {\n  return (\n    <ul>\n      {tasks.map(task => (\n        <li key={task.id}>\n          <Task\n            task={task}\n            onChange={onChangeTask}\n            onDelete={onDeleteTask}\n          />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ task, onChange, onDelete }) {\n  const [isEditing, setIsEditing] = useState(false);\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={e => {\n            onChange({\n              ...task,\n              text: e.target.value\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={e => {\n          onChange({\n            ...task,\n            done: e.target.checked\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => onDelete(task.id)}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\n<Solution />\n\n#### Immer を使って簡潔な更新ロジックを書く {/*writing-concise-update-logic-with-immer*/}\n\n配列やオブジェクトを書き換えずに更新するのが面倒な場合、[Immer](https://github.com/immerjs/use-immer#useimmerreducer) のようなライブラリを使用して冗長なコードを減らすことができます。Immer を使用すると、オブジェクトを書き換えているかのような簡潔なコードを書くことができますが、内部ではイミュータブルな更新が行われます。\n\n<Sandpack>\n\n```js src/App.js\nimport { useImmerReducer } from 'use-immer';\nimport AddTask from './AddTask.js';\nimport TaskList from './TaskList.js';\n\nfunction tasksReducer(draft, action) {\n  switch (action.type) {\n    case 'added': {\n      draft.push({\n        id: action.id,\n        text: action.text,\n        done: false\n      });\n      break;\n    }\n    case 'changed': {\n      const index = draft.findIndex(t =>\n        t.id === action.task.id\n      );\n      draft[index] = action.task;\n      break;\n    }\n    case 'deleted': {\n      return draft.filter(t => t.id !== action.id);\n    }\n    default: {\n      throw Error('Unknown action: ' + action.type);\n    }\n  }\n}\n\nexport default function TaskApp() {\n  const [tasks, dispatch] = useImmerReducer(\n    tasksReducer,\n    initialTasks\n  );\n\n  function handleAddTask(text) {\n    dispatch({\n      type: 'added',\n      id: nextId++,\n      text: text,\n    });\n  }\n\n  function handleChangeTask(task) {\n    dispatch({\n      type: 'changed',\n      task: task\n    });\n  }\n\n  function handleDeleteTask(taskId) {\n    dispatch({\n      type: 'deleted',\n      id: taskId\n    });\n  }\n\n  return (\n    <>\n      <h1>Prague itinerary</h1>\n      <AddTask\n        onAddTask={handleAddTask}\n      />\n      <TaskList\n        tasks={tasks}\n        onChangeTask={handleChangeTask}\n        onDeleteTask={handleDeleteTask}\n      />\n    </>\n  );\n}\n\nlet nextId = 3;\nconst initialTasks = [\n  { id: 0, text: 'Visit Kafka Museum', done: true },\n  { id: 1, text: 'Watch a puppet show', done: false },\n  { id: 2, text: 'Lennon Wall pic', done: false },\n];\n```\n\n```js src/AddTask.js hidden\nimport { useState } from 'react';\n\nexport default function AddTask({ onAddTask }) {\n  const [text, setText] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add task\"\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button onClick={() => {\n        setText('');\n        onAddTask(text);\n      }}>Add</button>\n    </>\n  )\n}\n```\n\n```js src/TaskList.js hidden\nimport { useState } from 'react';\n\nexport default function TaskList({\n  tasks,\n  onChangeTask,\n  onDeleteTask\n}) {\n  return (\n    <ul>\n      {tasks.map(task => (\n        <li key={task.id}>\n          <Task\n            task={task}\n            onChange={onChangeTask}\n            onDelete={onDeleteTask}\n          />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ task, onChange, onDelete }) {\n  const [isEditing, setIsEditing] = useState(false);\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={e => {\n            onChange({\n              ...task,\n              text: e.target.value\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={task.done}\n        onChange={e => {\n          onChange({\n            ...task,\n            done: e.target.checked\n          });\n        }}\n      />\n      {taskContent}\n      <button onClick={() => onDelete(task.id)}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"use-immer\": \"0.5.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n---\n\n### 初期 state の再作成を避ける {/*avoiding-recreating-the-initial-state*/}\n\nReact は初期 state を一度保存したのち、次回以降のレンダーでは無視します。\n\n```js\nfunction createInitialState(username) {\n  // ...\n}\n\nfunction TodoList({ username }) {\n  const [state, dispatch] = useReducer(reducer, createInitialState(username));\n  // ...\n```\n\n`createInitialState(username)` の結果は初期レンダーでのみ使用されますが、レンダーの度に毎回この関数を呼び出しています。これは、大きな配列を作成したり、高コストな計算を行ったりしている場合に無駄になる可能性があります。\n\nこれを解決するために、`useReducer` の 3 番目の引数として**初期化関数を渡す**ことができます：\n\n```js {6}\nfunction createInitialState(username) {\n  // ...\n}\n\nfunction TodoList({ username }) {\n  const [state, dispatch] = useReducer(reducer, username, createInitialState);\n  // ...\n```\n\n`createInitialState` を渡していることに注意してください。これは*関数そのもの*であり、`createInitialState()` ではありません。これにより、初期 state は初期化後に再作成されなくなります。\n\n上記の例では、`createInitialState` は `username` 引数を受け取ります。初期化関数が初期 state を計算するために情報を必要としない場合は、`useReducer` に対して 2 番目の引数として `null` を渡すことができます。\n\n<Recipes titleText=\"初期化関数と初期 state を直接渡す方法の違い\" titleId=\"examples-initializer\">\n\n#### 初期化関数を渡す {/*passing-the-initializer-function*/}\n\nこの例では、初期化関数を渡しているため、`createInitialState` 関数は初期化時にのみ実行されます。初期化関数は入力フィールドに文字を入力するなどでコンポーネントが再レンダーされたとしても実行されません。\n\n<Sandpack>\n\n```js src/App.js hidden\nimport TodoList from './TodoList.js';\n\nexport default function App() {\n  return <TodoList username=\"Taylor\" />;\n}\n```\n\n```js src/TodoList.js active\nimport { useReducer } from 'react';\n\nfunction createInitialState(username) {\n  const initialTodos = [];\n  for (let i = 0; i < 50; i++) {\n    initialTodos.push({\n      id: i,\n      text: username + \"'s task #\" + (i + 1)\n    });\n  }\n  return {\n    draft: '',\n    todos: initialTodos,\n  };\n}\n\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'changed_draft': {\n      return {\n        draft: action.nextDraft,\n        todos: state.todos,\n      };\n    };\n    case 'added_todo': {\n      return {\n        draft: '',\n        todos: [{\n          id: state.todos.length,\n          text: state.draft\n        }, ...state.todos]\n      }\n    }\n  }\n  throw Error('Unknown action: ' + action.type);\n}\n\nexport default function TodoList({ username }) {\n  const [state, dispatch] = useReducer(\n    reducer,\n    username,\n    createInitialState\n  );\n  return (\n    <>\n      <input\n        value={state.draft}\n        onChange={e => {\n          dispatch({\n            type: 'changed_draft',\n            nextDraft: e.target.value\n          })\n        }}\n      />\n      <button onClick={() => {\n        dispatch({ type: 'added_todo' });\n      }}>Add</button>\n      <ul>\n        {state.todos.map(item => (\n          <li key={item.id}>\n            {item.text}\n          </li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### 初期 state を直接渡す {/*passing-the-initial-state-directly*/}\n\nこの例では、初期化関数を渡して**いない**ため、`createInitialState` 関数は入力フィールドに文字を入力するなど、レンダーの度に毎回実行されます。動作には目に見える違いはありませんが、効率が下がります。\n\n<Sandpack>\n\n```js src/App.js hidden\nimport TodoList from './TodoList.js';\n\nexport default function App() {\n  return <TodoList username=\"Taylor\" />;\n}\n```\n\n```js src/TodoList.js active\nimport { useReducer } from 'react';\n\nfunction createInitialState(username) {\n  const initialTodos = [];\n  for (let i = 0; i < 50; i++) {\n    initialTodos.push({\n      id: i,\n      text: username + \"'s task #\" + (i + 1)\n    });\n  }\n  return {\n    draft: '',\n    todos: initialTodos,\n  };\n}\n\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'changed_draft': {\n      return {\n        draft: action.nextDraft,\n        todos: state.todos,\n      };\n    };\n    case 'added_todo': {\n      return {\n        draft: '',\n        todos: [{\n          id: state.todos.length,\n          text: state.draft\n        }, ...state.todos]\n      }\n    }\n  }\n  throw Error('Unknown action: ' + action.type);\n}\n\nexport default function TodoList({ username }) {\n  const [state, dispatch] = useReducer(\n    reducer,\n    createInitialState(username)\n  );\n  return (\n    <>\n      <input\n        value={state.draft}\n        onChange={e => {\n          dispatch({\n            type: 'changed_draft',\n            nextDraft: e.target.value\n          })\n        }}\n      />\n      <button onClick={() => {\n        dispatch({ type: 'added_todo' });\n      }}>Add</button>\n      <ul>\n        {state.todos.map(item => (\n          <li key={item.id}>\n            {item.text}\n          </li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### アクションをディスパッチしたがログには古い state の値が表示される {/*ive-dispatched-an-action-but-logging-gives-me-the-old-state-value*/}\n\n`dispatch` 関数を呼び出しても、**実行中のコード内の state は変更されません**：\n\n```js {4,5,8}\nfunction handleClick() {\n  console.log(state.age);  // 42\n\n  dispatch({ type: 'incremented_age' }); // Request a re-render with 43\n  console.log(state.age);  // Still 42!\n\n  setTimeout(() => {\n    console.log(state.age); // Also 42!\n  }, 5000);\n}\n```\n\nこれは [state がスナップショットのように振る舞う](/learn/state-as-a-snapshot)ためです。state を更新すると、新しい state の値で再レンダーが要求されますが、既に実行中のイベントハンドラ内の `state` JavaScript 変数には影響を与えません。\n\n次の state の値を推測する必要がある場合は、リデューサを自分で呼び出すことで手動で計算することができます：\n\n```js\nconst action = { type: 'incremented_age' };\ndispatch(action);\n\nconst nextState = reducer(state, action);\nconsole.log(state);     // { age: 42 }\nconsole.log(nextState); // { age: 43 }\n```\n\n---\n\n### アクションをディスパッチしたが画面が更新されない {/*ive-dispatched-an-action-but-the-screen-doesnt-update*/}\n\nReact は、[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) の比較により、**次の state が前の state と等しいと判断される場合、更新を無視します**。これは通常、state 内のオブジェクトや配列を直接書き換えた場合に発生します：\n\n```js {4-5,9-10}\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'incremented_age': {\n      // 🚩 Wrong: mutating existing object\n      state.age++;\n      return state;\n    }\n    case 'changed_name': {\n      // 🚩 Wrong: mutating existing object\n      state.name = action.nextName;\n      return state;\n    }\n    // ...\n  }\n}\n```\n\n既存の `state` オブジェクトを書き換えて返しているため、React は更新を無視します。これを修正するには、常に [state 内のオブジェクトの更新](/learn/updating-objects-in-state)や [state 内の配列の更新](/learn/updating-arrays-in-state)を正しく行い、これらの書き換えを避ける必要があります。\n\n```js {4-8,11-15}\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'incremented_age': {\n      // ✅ Correct: creating a new object\n      return {\n        ...state,\n        age: state.age + 1\n      };\n    }\n    case 'changed_name': {\n      // ✅ Correct: creating a new object\n      return {\n        ...state,\n        name: action.nextName\n      };\n    }\n    // ...\n  }\n}\n```\n\n---\n\n### ディスパッチ後にリデューサからの state の一部が undefined になる {/*a-part-of-my-reducer-state-becomes-undefined-after-dispatching*/}\n\n新しい state を返す際に、すべての `case` の分岐において**すべての既存のフィールドをコピーする**ようになっているか確認してください：\n\n```js {5}\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'incremented_age': {\n      return {\n        ...state, // Don't forget this!\n        age: state.age + 1\n      };\n    }\n    // ...\n```\n\n上記の `...state` を省略すると、返される次の state には `age` フィールドのみが含まれ、他のフィールドは何も含まれなくなってしまいます。\n\n---\n\n### ディスパッチ後にリデューサからの state 全体が undefined になる {/*my-entire-reducer-state-becomes-undefined-after-dispatching*/}\n\nstate が予期せず `undefined` になる場合、おそらくいずれかの `case` で state を `return` し忘れているか、アクションのタイプがいずれの `case` とも一致していないからです。原因を見つけるために、`switch` の外でエラーをスローしてみましょう。\n\n```js {10}\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'incremented_age': {\n      // ...\n    }\n    case 'edited_name': {\n      // ...\n    }\n  }\n  throw Error('Unknown action: ' + action.type);\n}\n```\n\nまた、このようなミスをキャッチするために、TypeScript のような静的型チェッカを使用することもできます。\n\n---\n\n### \"Too many re-renders\" というエラーが発生する {/*im-getting-an-error-too-many-re-renders*/}\n\n`Too many re-renders. React limits the number of renders to prevent an infinite loop.` というエラーが表示されることがあります。通常、これはレンダー中にアクションを無条件でディスパッチしているため、コンポーネントがループに入っていることを意味します：レンダー、ディスパッチ（これにより再度レンダーが発生）、レンダー、ディスパッチ（これにより再度レンダーが発生）、といった繰り返しです。非常に多くの場合、これはイベントハンドラの指定方法のミスによって引き起こされます。\n\n```js {1-2}\n// 🚩 Wrong: calls the handler during render\nreturn <button onClick={handleClick()}>Click me</button>\n\n// ✅ Correct: passes down the event handler\nreturn <button onClick={handleClick}>Click me</button>\n\n// ✅ Correct: passes down an inline function\nreturn <button onClick={(e) => handleClick(e)}>Click me</button>\n```\n\nこのエラーの原因がわからない場合は、コンソールのエラーの横にある矢印をクリックし、JavaScript スタックを調べてエラーの原因となる具体的な `dispatch` 関数呼び出しを見つけてください。\n\n---\n\n### リデューサまたは初期化関数が 2 回実行される {/*my-reducer-or-initializer-function-runs-twice*/}\n\n[Strict Mode](/reference/react/StrictMode) では、React はリデューサと初期化関数を 2 回呼び出します。これによってコードが壊れることがあってはなりません。\n\nこの**開発専用**の振る舞いは、[コンポーネントを純粋に保つ](/learn/keeping-components-pure)ために役立ちます。React は 2 回の呼び出しのうちの 1 つの結果を使用し、もう 1 つの結果は無視します。コンポーネント、イニシャライザ、およびリデューサ関数が純粋である限り、これはロジックに影響を与えません。ただし、これらの関数が誤っていて不純である場合、これによりミスに気付くことができます。\n\n例えば、以下の純粋でないリデューサ関数はステート内の配列を書き換えています：\n\n```js {4-6}\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'added_todo': {\n      // 🚩 Mistake: mutating state\n      state.todos.push({ id: nextId++, text: action.text });\n      return state;\n    }\n    // ...\n  }\n}\n```\n\nReact がリデューサ関数を 2 回呼び出すため、todo が 2 回追加されたことがわかり、ミスがあることがわかります。この例では、[配列の書き換えではなく置き換えを行う](/learn/updating-arrays-in-state#adding-to-an-array)ことでミスを修正できます：\n\n```js {4-11}\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'added_todo': {\n      // ✅ Correct: replacing with new state\n      return {\n        ...state,\n        todos: [\n          ...state.todos,\n          { id: nextId++, text: action.text }\n        ]\n      };\n    }\n    // ...\n  }\n}\n```\n\nこれでリデューサ関数は純粋になったため、1 回余分に呼び出されても動作に影響はありません。これが React が 2 回呼び出すことでミスを発見できる理由です。**コンポーネント、初期化関数、およびリデューサ関数のみが純粋である必要があります**。イベントハンドラは純粋である必要はないため、React はイベントハンドラを 2 回呼び出すことはありません。\n\n詳細は、[コンポーネントを純粋に保つ](/learn/keeping-components-pure)を参照してください。\n"
  },
  {
    "path": "src/content/reference/react/useRef.md",
    "content": "---\ntitle: useRef\n---\n\n<Intro>\n\n`useRef` は、レンダー時には不要な値を参照するための React フックです。\n\n```js\nconst ref = useRef(initialValue)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useRef(initialValue)` {/*useref*/}\n\nコンポーネントのトップレベルで `useRef` を呼び出して、[ref](/learn/referencing-values-with-refs) を宣言します。\n\n```js\nimport { useRef } from 'react';\n\nfunction MyComponent() {\n  const intervalRef = useRef(0);\n  const inputRef = useRef(null);\n  // ...\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `initialValue`: ref オブジェクトの `current` プロパティの初期値として設定する値です。任意の型の値を指定できます。この引数は 2 回目以降のレンダーでは無視されます。\n\n#### 返り値 {/*returns*/}\n\n`useRef` は、以下の 1 つのプロパティだけを持つオブジェクトを返します。\n\n* `current`: 渡した `initialValue` が初期値に設定されます。あとから別の値に変更することができます。ref オブジェクトを JSX ノードの `ref` 属性として React に渡すと、React は `current` プロパティの値を設定します。\n\n2 回目以降のレンダーでは、`useRef` は同じオブジェクトを返します。\n\n#### 注意点 {/*caveats*/}\n\n* `ref.current` プロパティは書き換えが可能です。つまり state と違いミュータブル (mutable) です。ただし、レンダーに利用されるオブジェクト（state の一部など）を保持している場合は、変更すべきではありません。\n* `ref.current` プロパティを変更しても、React はコンポーネントを再レンダーしません。ref はただの JavaScript オブジェクトですので、変更されたとしても、それを React が知ることはできないのです。\n* [初期化](#avoiding-recreating-the-ref-contents)時を除いて、レンダー中に `ref.current` の値を*読み取ったり*書き込んだりしないでください。コンポーネントの振る舞いが予測不能になります。\n* Strict Mode では、[純粋でない関数を見つけやすくするために](/reference/react/useState#my-initializer-or-updater-function-runs-twice)、コンポーネント関数が **2 回呼び出されます**。これは開発時のみの振る舞いであり、本番には影響しません。各 ref オブジェクトは 2 回生成されますが、そのうちの 1 つは破棄されます。コンポーネント関数が純粋であれば（そうであるべきです）、この振る舞いはロジックに影響しません。\n\n---\n\n## 使用法 {/*usage*/}\n\n### ref を使用して値を参照する {/*referencing-a-value-with-a-ref*/}\n\nコンポーネントのトップレベルで `useRef` を呼び出し、1 つ以上の [ref](/learn/referencing-values-with-refs) を宣言します。\n\n```js [[1, 4, \"intervalRef\"], [3, 4, \"0\"]]\nimport { useRef } from 'react';\n\nfunction Stopwatch() {\n  const intervalRef = useRef(0);\n  // ...\n```\n\n`useRef` は、唯一のプロパティである<CodeStep step={2}>`current`</CodeStep>に、指定された<CodeStep step={3}>初期値</CodeStep>が設定された状態の <CodeStep step={1}>ref オブジェクト</CodeStep>を返します。\n\n次回以降のレンダーでも、`useRef` は同じオブジェクトを返します。このオブジェクトの `current` プロパティを書き換えることで情報を保存しておき、あとからその値を読み出すことができます。これは [state](/reference/react/useState) と似ていますが、大きく違う点があります。\n\nそれは、**ref を変更しても、再レンダーはトリガされない**ということです。このことから、ref は、出力されるコンポーネントの外見に影響しないデータを保存するのに適しています。例えば、[インターバルの ID](https://developer.mozilla.org/en-US/docs/Web/API/setInterval) を保持しておき、あとから利用したい場合、ref に保存することができます。ref 内の値を更新するには、<CodeStep step={2}>`current` プロパティ</CodeStep>を手動で変更します。\n\n```js [[2, 5, \"intervalRef.current\"]]\nfunction handleStartClick() {\n  const intervalId = setInterval(() => {\n    // ...\n  }, 1000);\n  intervalRef.current = intervalId;\n}\n```\n\nそして、あとから、ref に保存されているインターバル ID を読み出すことができます。これで[インターバルをクリアする関数](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval)を呼び出し、インターバルを削除できます。\n\n```js [[2, 2, \"intervalRef.current\"]]\nfunction handleStopClick() {\n  const intervalId = intervalRef.current;\n  clearInterval(intervalId);\n}\n```\n\nref を使用することで、次のことが保証されます。\n\n- レンダーを跨いで**情報を保存**できます（通常の変数は、レンダーごとにリセットされます）。\n- 変更しても**再レンダーはトリガされません**（state 変数は、変更すると再レンダーがトリガされます）。\n- 保存された情報は、コンポーネントのインスタンスごとに**固有**です（コンポーネントの外側で定義された変数は、コンポーネントのインスタンス間で共有されます）。\n\nref を変更しても再レンダーはトリガされないため、ref は画面に表示したい情報を保存するのには適していません。そのような場合は、代わりに useState を使用してください。`useRef` と `useState` の使い分けに関しては、[ref と state の違い](/learn/referencing-values-with-refs#differences-between-refs-and-state)を参照してください。\n\n<Recipes titleText=\"useRef を用いて値を参照する使用例\" titleId=\"examples-value\">\n\n#### クリックカウンタ {/*click-counter*/}\n\nこのコンポーネントは、ボタンがクリックされた回数を保存するために ref を使用しています。この例では、クリック回数の読み書きはイベントハンドラ内でのみ行われているため、state の代わりに ref を使用して構いません。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\n\nexport default function Counter() {\n  let ref = useRef(0);\n\n  function handleClick() {\n    ref.current = ref.current + 1;\n    alert('You clicked ' + ref.current + ' times!');\n  }\n\n  return (\n    <button onClick={handleClick}>\n      Click me!\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\nJSX 内で `{ref.current}` を表示すると、クリックしても回数の表示は更新されません。これは、`ref.current` に新しい値を設定しても、再レンダーがトリガされないためです。レンダーに利用したい値は、代わりに state に保存してください。\n\n<Solution />\n\n#### ストップウォッチ {/*a-stopwatch*/}\n\nこの例では、state と ref を組み合わせて使用しています。`startTime` と `now` はレンダーに利用されるため、state 変数となっています。また、ボタンを押したときにインターバルを停止するために、[インターバル ID](https://developer.mozilla.org/en-US/docs/Web/API/setInterval) を保持しておく必要があります。インターバル ID はレンダー時には利用されないため、ref に保存し、手動で更新するのが適切です。\n\n<Sandpack>\n\n```js\nimport { useState, useRef } from 'react';\n\nexport default function Stopwatch() {\n  const [startTime, setStartTime] = useState(null);\n  const [now, setNow] = useState(null);\n  const intervalRef = useRef(null);\n\n  function handleStart() {\n    setStartTime(Date.now());\n    setNow(Date.now());\n\n    clearInterval(intervalRef.current);\n    intervalRef.current = setInterval(() => {\n      setNow(Date.now());\n    }, 10);\n  }\n\n  function handleStop() {\n    clearInterval(intervalRef.current);\n  }\n\n  let secondsPassed = 0;\n  if (startTime != null && now != null) {\n    secondsPassed = (now - startTime) / 1000;\n  }\n\n  return (\n    <>\n      <h1>Time passed: {secondsPassed.toFixed(3)}</h1>\n      <button onClick={handleStart}>\n        Start\n      </button>\n      <button onClick={handleStop}>\n        Stop\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n<Pitfall>\n\n**レンダー中に `ref.current` の値を*読み取ったり*書き込んだりしないでください**。\n\nReact は、コンポーネント本体の関数が[純関数のように振る舞う](/learn/keeping-components-pure)ことを期待しています。\n\n- 入力値（[props](/learn/passing-props-to-a-component)、[state](/learn/state-a-components-memory)、[context](/learn/passing-data-deeply-with-context)）が同じなら、常に同じ JSX を返さなければなりません。\n- 呼び出し順が変わったり、引数を変えて呼び出されたりしても、他の呼び出し結果に影響を与えてはいけません。\n\n**レンダー中に** ref を読み書きすると、これらに違反してしまいます。\n\n```js {expectedErrors: {'react-compiler': [4]}} {3-4,6-7}\nfunction MyComponent() {\n  // ...\n  // 🚩 Don't write a ref during rendering\n  myRef.current = 123;\n  // ...\n  // 🚩 Don't read a ref during rendering\n  return <h1>{myOtherRef.current}</h1>;\n}\n```\n\n代わりに、**イベントハンドラやエフェクトから** ref を読み書きしてください。\n\n```js {4-5,9-10}\nfunction MyComponent() {\n  // ...\n  useEffect(() => {\n    // ✅ You can read or write refs in effects\n    myRef.current = 123;\n  });\n  // ...\n  function handleClick() {\n    // ✅ You can read or write refs in event handlers\n    doSomething(myOtherRef.current);\n  }\n  // ...\n}\n```\n\nもし、レンダー中に何かを読み出したり[書き込んだり](/reference/react/useState#storing-information-from-previous-renders)*しなければならない*場合は、代わりに [useState](/reference/react/useState) を使用してください。\n\nこれらのルールを破っていても、コンポーネントは正常に動作し続けるかもしれません。しかし、近いうちに React に追加される新機能の多くは、これらのルールが守られることを前提としています。詳しくは[コンポーネントを純粋に保つ](/learn/keeping-components-pure#where-you-_can_-cause-side-effects)を参照してください。\n\n</Pitfall>\n\n---\n\n### ref で DOM を操作する {/*manipulating-the-dom-with-a-ref*/}\n\n[DOM](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API) を操作したい場合、ref を利用することが非常に多いです。React は、DOM へのアクセスを組み込みでサポートしています。\n\n最初に、<CodeStep step={3}>初期値</CodeStep>を `null` に設定した <CodeStep step={1}>ref オブジェクト</CodeStep> を宣言します。\n\n```js [[1, 4, \"inputRef\"], [3, 4, \"null\"]]\nimport { useRef } from 'react';\n\nfunction MyComponent() {\n  const inputRef = useRef(null);\n  // ...\n```\n\n次に、操作したい DOM ノードの JSX の `ref` 属性に、ref オブジェクトを渡します。\n\n```js [[1, 2, \"inputRef\"]]\n  // ...\n  return <input ref={inputRef} />;\n```\n\nこの DOM ノードが生成され、画面に配置されると、ref オブジェクトの <CodeStep step={2}>`current` プロパティ</CodeStep>にその DOM ノードが設定されます。これで、`<input>` の DOM ノードにアクセスして、[`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) のようなメソッドを呼び出すことができるようになります。\n\n```js [[2, 2, \"inputRef.current\"]]\n  function handleClick() {\n    inputRef.current.focus();\n  }\n```\n\nReact は、ノードが画面から削除されると `current` プロパティを `null` に戻します。\n\n詳しくは、[ref を使用して DOM を操作する](/learn/manipulating-the-dom-with-refs)を参照してください。\n\n<Recipes titleText=\"useRef を使用して DOM を操作する例\" titleId=\"examples-dom\">\n\n#### input 要素をフォーカス {/*focusing-a-text-input*/}\n\nこの例では、ボタンをクリックすると input にフォーカスが当たります。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\n\nexport default function Form() {\n  const inputRef = useRef(null);\n\n  function handleClick() {\n    inputRef.current.focus();\n  }\n\n  return (\n    <>\n      <input ref={inputRef} />\n      <button onClick={handleClick}>\n        Focus the input\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### スクロールして画像を表示 {/*scrolling-an-image-into-view*/}\n\nこの例では、ボタンがクリックされると、その画像が画面に表示されるようにスクロールします。ref を使用してリストの DOM ノードを取得し、DOM の [`querySelectorAll`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) API を呼び出して、スクロール先の画像を探しています。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\n\nexport default function CatFriends() {\n  const listRef = useRef(null);\n\n  function scrollToIndex(index) {\n    const listNode = listRef.current;\n    // This line assumes a particular DOM structure:\n    const imgNode = listNode.querySelectorAll('li > img')[index];\n    imgNode.scrollIntoView({\n      behavior: 'smooth',\n      block: 'nearest',\n      inline: 'center'\n    });\n  }\n\n  return (\n    <>\n      <nav>\n        <button onClick={() => scrollToIndex(0)}>\n          Neo\n        </button>\n        <button onClick={() => scrollToIndex(1)}>\n          Millie\n        </button>\n        <button onClick={() => scrollToIndex(2)}>\n          Bella\n        </button>\n      </nav>\n      <div>\n        <ul ref={listRef}>\n          <li>\n            <img\n              src=\"https://placecats.com/neo/300/200\"\n              alt=\"Neo\"\n            />\n          </li>\n          <li>\n            <img\n              src=\"https://placecats.com/millie/200/200\"\n              alt=\"Millie\"\n            />\n          </li>\n          <li>\n            <img\n              src=\"https://placecats.com/bella/199/200\"\n              alt=\"Bella\"\n            />\n          </li>\n        </ul>\n      </div>\n    </>\n  );\n}\n```\n\n```css\ndiv {\n  width: 100%;\n  overflow: hidden;\n}\n\nnav {\n  text-align: center;\n}\n\nbutton {\n  margin: .25rem;\n}\n\nul,\nli {\n  list-style: none;\n  white-space: nowrap;\n}\n\nli {\n  display: inline;\n  padding: 0.5rem;\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### 動画の再生・停止 {/*playing-and-pausing-a-video*/}\n\nこの例では、ref を使用して `<video>` の DOM ノードの [`play()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play) と [`pause()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause) を呼び出します。\n\n<Sandpack>\n\n```js\nimport { useState, useRef } from 'react';\n\nexport default function VideoPlayer() {\n  const [isPlaying, setIsPlaying] = useState(false);\n  const ref = useRef(null);\n\n  function handleClick() {\n    const nextIsPlaying = !isPlaying;\n    setIsPlaying(nextIsPlaying);\n\n    if (nextIsPlaying) {\n      ref.current.play();\n    } else {\n      ref.current.pause();\n    }\n  }\n\n  return (\n    <>\n      <button onClick={handleClick}>\n        {isPlaying ? 'Pause' : 'Play'}\n      </button>\n      <video\n        width=\"250\"\n        ref={ref}\n        onPlay={() => setIsPlaying(true)}\n        onPause={() => setIsPlaying(false)}\n      >\n        <source\n          src=\"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4\"\n          type=\"video/mp4\"\n        />\n      </video>\n    </>\n  );\n}\n```\n\n```css\nbutton { display: block; margin-bottom: 20px; }\n```\n\n</Sandpack>\n\n<Solution />\n\n#### 独自コンポーネントの ref を公開 {/*exposing-a-ref-to-your-own-component*/}\n\n親コンポーネントから、独自コンポーネント内の DOM を操作したい場合があります。たとえば、`MyInput` コンポーネントを作成しているとして、親コンポーネントが input にフォーカスを当てたい場合などです（親コンポーネントは input にはアクセスできません）。この場合は、親側で `ref` を作成して、その `ref` を props として子コンポーネントに渡すようにします。[こちらの詳細な説明](/learn/manipulating-the-dom-with-refs#accessing-another-components-dom-nodes)を参照してください。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\n\nfunction MyInput({ ref }) {\n  return <input ref={ref} />;\n};\n\nexport default function Form() {\n  const inputRef = useRef(null);\n\n  function handleClick() {\n    inputRef.current.focus();\n  }\n\n  return (\n    <>\n      <MyInput ref={inputRef} />\n      <button onClick={handleClick}>\n        Focus the input\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n---\n\n### ref の値の再生成を防ぐ {/*avoiding-recreating-the-ref-contents*/}\n\nReact は、初回に渡された ref の値を保存しますが、それ以降のレンダーではその値は無視します。\n\n```js\nfunction Video() {\n  const playerRef = useRef(new VideoPlayer());\n  // ...\n```\n\n`new VideoPlayer()` の結果は初回レンダーでのみ利用されますが、すべてのレンダーで呼び出し自体は発生しています。これは、計算コストの高いオブジェクトを作成している場合に、無駄が多くなります。\n\nこれを解決するために、次のように ref を初期化することができます。\n\n```js\nfunction Video() {\n  const playerRef = useRef(null);\n  if (playerRef.current === null) {\n    playerRef.current = new VideoPlayer();\n  }\n  // ...\n```\n\n通常、レンダー中に `ref.current` の値を読み取ったり書き込んだりすることは許されていません。しかし、今回の場合は問題ありません。なぜなら、呼び出し結果は常に同じであり、条件分岐により書き込みは初期化時にのみ実行されるため、コンポーネントの振る舞いが完全に予測可能となるからです。\n\n<DeepDive>\n\n#### useRef を遅延して初期化する場合に null チェックを回避する {/*how-to-avoid-null-checks-when-initializing-use-ref-later*/}\n\n型チェッカを使用しており `null` チェックを何度も行うのが煩わしい場合は、次のようなパターンを試してみてください。\n\n```js\nfunction Video() {\n  const playerRef = useRef(null);\n\n  function getPlayer() {\n    if (playerRef.current !== null) {\n      return playerRef.current;\n    }\n    const player = new VideoPlayer();\n    playerRef.current = player;\n    return player;\n  }\n\n  // ...\n```\n\nこの例では、`playerRef` 自体は null 許容です。しかし型チェッカに、`getPlayer()` は `null` を返す場合がないと判断させられるはずです。そこでイベントハンドラなどで `getPlayer()` を使用できます。\n\n</DeepDive>\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### 独自コンポーネントへの ref を取得できない {/*i-cant-get-a-ref-to-a-custom-component*/}\n\n以下のようにして、独自コンポーネントに `ref` を渡そうとしている場合、\n\n```js\nconst inputRef = useRef(null);\n\nreturn <MyInput ref={inputRef} />;\n```\n\nコンソールにこのようなエラーが表示されるかもしれません。\n\n<ConsoleBlock level=\"error\">\n\nTypeError: Cannot read properties of null\n\n</ConsoleBlock>\n\nデフォルトでは、独自コンポーネントは、内部の DOM ノードへの ref を公開していません。\n\nこれを修正するには、まず、ref を取得したいコンポーネントを探します。\n\n```js\nexport default function MyInput({ value, onChange }) {\n  return (\n    <input\n      value={value}\n      onChange={onChange}\n    />\n  );\n}\n```\n\n次にコンポーネントが受け取る props のリストに `ref` を追加し、その `ref` を、対応する[組み込み](/reference/react-dom/components/common)子コンポーネントに以下のようにして渡します。\n\n```js {1,6}\nfunction MyInput({ value, onChange, ref }) {\n  return (\n    <input\n      value={value}\n      onChange={onChange}\n      ref={ref}\n    />\n  );\n};\n\nexport default MyInput;\n```\n\nこれで、親コンポーネントから ref を取得できるようになります。\n\n詳しくは、[別のコンポーネントの DOM ノードにアクセスする](/learn/manipulating-the-dom-with-refs#accessing-another-components-dom-nodes)を参照してください。\n"
  },
  {
    "path": "src/content/reference/react/useState.md",
    "content": "---\ntitle: useState\n---\n\n<Intro>\n\n`useState` は、コンポーネントに [state 変数](/learn/state-a-components-memory) を追加するための React フックです。\n\n```js\nconst [state, setState] = useState(initialState)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useState(initialState)` {/*usestate*/}\n\nコンポーネントのトップレベルで `useState` を呼び出して、[state 変数](/learn/state-a-components-memory)を宣言します。\n\n```js\nimport { useState } from 'react';\n\nfunction MyComponent() {\n  const [age, setAge] = useState(28);\n  const [name, setName] = useState('Taylor');\n  const [todos, setTodos] = useState(() => createTodos());\n  // ...\n```\n\nstate 変数は慣習として、[分割代入](https://javascript.info/destructuring-assignment)を利用して `[something, setSomething]` のように命名します。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `initialState`: state の初期値です。どんな型の値でも渡すことができますが、関数を渡した場合は特別な振る舞いをします。この引数は初回レンダー後は無視されます。\n  * `initialState` に関数を渡した場合、その関数は*初期化関数 (initializer function)* として扱われます。初期化関数は、純粋で、引数を取らず、何らかの型の値を返す必要があります。React はコンポーネントを初期化するときに初期化関数を呼び出し、その返り値を初期 state として保存します。[例を見る](#avoiding-recreating-the-initial-state)\n\n#### 返り値 {/*returns*/}\n\n`useState` は以下の 2 つの値を持つ配列を返します。\n\n1. 現在の state。初回レンダー中では、`initialState` と同じ値になります。\n2. state を別の値に更新し、再レンダーをトリガする [`set` 関数](#setstate)。\n\n#### 注意点 {/*caveats*/}\n\n* `useState` はフックであるため、**コンポーネントのトップレベル**またはカスタムフック内でのみ呼び出すことができます。ループや条件文の中で呼び出すことはできません。これが必要な場合は、新しいコンポーネントを抽出し、state を移動させてください。\n* Strict Mode では、[純粋でない関数を見つけやすくするために](#my-initializer-or-updater-function-runs-twice)、**初期化関数が 2 回呼び出されます**。これは開発時のみの振る舞いであり、本番には影響しません。初期化関数が純粋であれば（そうであるべきです）、2 回呼び出されてもコードに影響はありません。2 回の呼び出しのうち 1 回の呼び出し結果は無視されます。\n\n---\n\n### `setSomething(nextState)` のように利用する `set` 関数 {/*setstate*/}\n\n`useState` が返す `set` 関数を利用して、state を別の値に更新し、再レンダーをトリガすることができます。直接次の state を渡すか、前の state から次の state を導出する関数を渡します。\n\n```js\nconst [name, setName] = useState('Edward');\n\nfunction handleClick() {\n  setName('Taylor');\n  setAge(a => a + 1);\n  // ...\n```\n\n#### 引数 {/*setstate-parameters*/}\n\n* `nextState`: 次に state にセットしたい値です。どんな型の値でも渡すことができますが、関数を渡した場合は特別な振る舞いをします。\n  * `nextState` に関数を渡した場合、その関数は*更新用関数 (updater function)* として扱われます。更新用関数は、純粋で、処理中の state の値を唯一の引数として受け取り、次の state を返す必要があります。React は更新用関数をキューに入れ、コンポーネントを再レンダーします。次のレンダーで、React はキューに入れられたすべての更新用関数を前の state に対して適用し、次の state を導出します。[例を見る](#updating-state-based-on-the-previous-state)\n\n#### 返り値 {/*setstate-returns*/}\n\n`set` 関数は返り値を持ちません。\n\n#### 注意点 {/*setstate-caveats*/}\n\n* `set` 関数は***次の*レンダーのための state 変数のみを更新**します。`set` 関数を呼び出した後に state 変数を読み取っても、呼び出し前の画面に表示されていた[古い値が返されます](#ive-updated-the-state-but-logging-gives-me-the-old-value)。\n\n* 新しい値が現在の `state` と同一の場合、React は最適化のために、**コンポーネントとその子コンポーネントの再レンダーをスキップ**します。state の同一性の比較は、[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) によって行われます。一部のケースでは、React は子コンポーネントをスキップする前にコンポーネントを呼び出す必要がありますが、あなたのコードに影響を与えることはないはずです。\n\n* React は [state の更新をまとめて行います（バッチ処理）](/learn/queueing-a-series-of-state-updates)。**すべてのイベントハンドラを実行し終え**、`set` 関数が呼び出された後に、画面を更新します。これにより、1 つのイベント中に複数回の再レンダーが発生することはありません。まれに、早期に画面を更新する必要がある場合（例えば DOM にアクセスする場合など）がありますが、その場合は [`flushSync`](/reference/react-dom/flushSync) を利用できます。\n\n* `set` 関数は常に同一のものとなるため、多くの場合エフェクトの依存配列では省略されますが、依存配列に含めてもエフェクトの再実行は起こりません。依存値を削除してもリンタがエラーを出さない場合、削除しても安全です。[エフェクトから依存値を取り除く方法](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect)を参照してください。\n\n* レンダー中に `set` 関数を呼び出すことは、*現在レンダー中の*コンポーネント内からのみ許されています。その場合、React はその出力を破棄し、新しい state で再レンダーを試みます。このパターンが必要になることはほとんどありませんが、**前回のレンダーからの情報を保存**するために使用できます。[例を見る](#storing-information-from-previous-renders)\n\n* Strict Mode では、[純粋でない関数を見つけやすくするために](#my-initializer-or-updater-function-runs-twice)**更新用関数が 2 回呼び出されます**。これは開発時のみの振る舞いであり、本番には影響しません。更新用関数が純粋であれば（そうであるべきです）、2 回呼び出されてもコードに影響はありません。2 回の呼び出しのうち 1 回の呼び出し結果は無視されます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### state をコンポーネントに追加する {/*adding-state-to-a-component*/}\n\nコンポーネントのトップレベルで `useState` を呼び出し、1 つ以上の [state 変数](/learn/state-a-components-memory)を宣言します。\n\n```js [[1, 4, \"age\"], [2, 4, \"setAge\"], [3, 4, \"42\"], [1, 5, \"name\"], [2, 5, \"setName\"], [3, 5, \"'Taylor'\"]]\nimport { useState } from 'react';\n\nfunction MyComponent() {\n  const [age, setAge] = useState(42);\n  const [name, setName] = useState('Taylor');\n  // ...\n```\n\nstate 変数は慣習として、[分割代入](https://javascript.info/destructuring-assignment)を利用して `[something, setSomething]` のように命名します。\n\n`useState` は、以下の 2 つの値を持つ配列を返します。\n\n1. この state 変数の<CodeStep step={1}>現在の値</CodeStep>。最初は、<CodeStep step={3}>初期 state</CodeStep> に設定されます。\n2. インタラクションに応じて、state を他の値に変更するための<CodeStep step={2}>`set` 関数</CodeStep>。\n\nスクリーン上の表示を更新するには、次の state を引数として `set` 関数を呼び出します。\n\n```js [[2, 2, \"setName\"]]\nfunction handleClick() {\n  setName('Robin');\n}\n```\n\nReact は次の state を保存したあと、新しい値でコンポーネントを再レンダーし、UI を更新します。\n\n<Pitfall>\n\n`set` 関数の呼び出しは、[既に実行中のコードの現在の state を変更するわけでは**ありません**](#ive-updated-the-state-but-logging-gives-me-the-old-value)。\n\n```js {3}\nfunction handleClick() {\n  setName('Robin');\n  console.log(name); // Still \"Taylor\"!\n}\n```\n\nこの呼び出しは、*次の*レンダー以降に `useState` が返す値にのみ影響を与えます。\n\n</Pitfall>\n\n<Recipes titleText=\"useState の基本的な使用例\" titleId=\"examples-basic\">\n\n#### カウンタ (number) {/*counter-number*/}\n\nこの例では、`count` state 変数が数値 (number) を保持しています。ボタンをクリックすることで、数値が増加します。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [count, setCount] = useState(0);\n\n  function handleClick() {\n    setCount(count + 1);\n  }\n\n  return (\n    <button onClick={handleClick}>\n      You pressed me {count} times\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### テキストフィールド (string) {/*text-field-string*/}\n\nこの例では、`text` state 変数が文字列 (string) を保持しています。ブラウザの DOM の input 要素に文字を入力すると、`handleChange` は input 要素から最新の値を読み出し、`setText` を呼び出して state を更新します。これにより、下部に、現在の `text` を表示できます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function MyInput() {\n  const [text, setText] = useState('hello');\n\n  function handleChange(e) {\n    setText(e.target.value);\n  }\n\n  return (\n    <>\n      <input value={text} onChange={handleChange} />\n      <p>You typed: {text}</p>\n      <button onClick={() => setText('hello')}>\n        Reset\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### チェックボックス (boolean) {/*checkbox-boolean*/}\n\nこの例では、`liked` state 変数が真偽値 (boolean) を保持しています。input をクリックすると、`setLiked` はブラウザのチェックボックスの input がチェックされているかどうかで、`liked` state 変数を更新します。`liked` 変数は、チェックボックスの下のテキストをレンダーするために使用されます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function MyCheckbox() {\n  const [liked, setLiked] = useState(true);\n\n  function handleChange(e) {\n    setLiked(e.target.checked);\n  }\n\n  return (\n    <>\n      <label>\n        <input\n          type=\"checkbox\"\n          checked={liked}\n          onChange={handleChange}\n        />\n        I liked this\n      </label>\n      <p>You {liked ? 'liked' : 'did not like'} this.</p>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### フォーム（2 つの変数） {/*form-two-variables*/}\n\n同じコンポーネントで、複数の state 変数を宣言することができます。それぞれの state 変数は、完全に独立しています。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [name, setName] = useState('Taylor');\n  const [age, setAge] = useState(42);\n\n  return (\n    <>\n      <input\n        value={name}\n        onChange={e => setName(e.target.value)}\n      />\n      <button onClick={() => setAge(age + 1)}>\n        Increment age\n      </button>\n      <p>Hello, {name}. You are {age}.</p>\n    </>\n  );\n}\n```\n\n```css\nbutton { display: block; margin-top: 10px; }\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n---\n\n### 直前の state に応じて更新する {/*updating-state-based-on-the-previous-state*/}\n\n`age` が `42` である場合を考えましょう。このハンドラは、`setAge(age + 1)` を 3 回呼び出します。\n\n```js\nfunction handleClick() {\n  setAge(age + 1); // setAge(42 + 1)\n  setAge(age + 1); // setAge(42 + 1)\n  setAge(age + 1); // setAge(42 + 1)\n}\n```\n\nしかし、1 回クリックしたあと、`age` は `45` ではなく `43` になります！ これは、`set` 関数を呼び出しても、既に実行されているコードの `age` state 変数を[更新するわけではない](/learn/state-as-a-snapshot)ためです。そのため、`setAge(age + 1)` の呼び出しはすべて `setAge(43)` になります。\n\nこの問題を解消するため、次の state の代わりに、***更新用関数*を `setAge` に渡す**ことができます。\n\n```js [[1, 2, \"a\", 0], [2, 2, \"a + 1\"], [1, 3, \"a\", 0], [2, 3, \"a + 1\"], [1, 4, \"a\", 0], [2, 4, \"a + 1\"]]\nfunction handleClick() {\n  setAge(a => a + 1); // setAge(42 => 43)\n  setAge(a => a + 1); // setAge(43 => 44)\n  setAge(a => a + 1); // setAge(44 => 45)\n}\n```\n\nここで、`a => a + 1` は更新用関数です。更新用関数は、<CodeStep step={1}>処理中の state の値</CodeStep>を受け取り、そこから<CodeStep step={2}>次の state</CodeStep> を導出します。\n\nReact は更新用関数を[キュー](/learn/queueing-a-series-of-state-updates)に入れます。そして、次のレンダー中に、同じ順番で更新用関数を呼び出します。\n\n1. `a => a + 1` は処理中の state の値として `42` を受け取り、次の state として `43` を返します。\n1. `a => a + 1` は処理中の state の値として `43` を受け取り、次の state として `44` を返します。\n1. `a => a + 1` は処理中の state の値として `44` を受け取り、次の state として `45` を返します。\n\nキューにはこれ以上の更新用関数はないので、React は最終的に `45` を現在の state として保存します。\n\n慣習として、処理中の state の引数名には、state 変数名の頭文字 1 文字を利用することが一般的です（例えば、`age` という state 変数に対して、`a` という引数名）。しかし、`prevAge` など、他の分かりやすい名前を使うこともできます。\n\n開発時に[更新用関数が 2 回呼び出される](#my-initializer-or-updater-function-runs-twice)ことがあります。これは、更新用関数が[純粋](/learn/keeping-components-pure)であることを確認するためです。\n\n<DeepDive>\n\n#### 常に更新用関数を利用すべきか {/*is-using-an-updater-always-preferred*/}\n\n新しくセットする値が直前の state から導出される場合、常に `setAge(a => a + 1)` という書き方をすべきだという意見があります。悪いことではありませんが、必ずしも必要なわけではありません。\n\nほとんどのケースでは、どちらのアプローチでも違いはありません。React は、クリックなどのユーザの意図的なアクションに対して、`age` state 変数の更新が次のクリックの前に発生することを保証しています。すなわち、イベントハンドラの開始時に、クリックハンドラが「古い」`age` を参照してしまうことはありません。\n\n一方で、同じイベント内で複数回の更新を行う場合、更新用関数が役に立ちます。また、state 変数自身を参照することが難しいケースにも有用です（再レンダーの発生を最適化する際に、このケースに遭遇することがあります）。\n\nわずかな文法の冗長性よりも一貫性を優先するのであれば、state が直前の state から導出される場合には、常に更新用関数を書くようにすることは合理的です。もし、state が、他の state 変数の直前の値から導出される場合は、それらを 1 つのオブジェクトにまとめて[リデューサ (reducer) を利用する](/learn/extracting-state-logic-into-a-reducer)ことを検討してください。\n\n</DeepDive>\n\n<Recipes titleText=\"更新用関数を渡す場合と次の state を直接渡す場合の違い\" titleId=\"examples-updater\">\n\n#### 更新用関数を渡す {/*passing-the-updater-function*/}\n\nこの例では更新用関数を渡しているため、\"+3\" ボタンは想定通りに動きます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [age, setAge] = useState(42);\n\n  function increment() {\n    setAge(a => a + 1);\n  }\n\n  return (\n    <>\n      <h1>Your age: {age}</h1>\n      <button onClick={() => {\n        increment();\n        increment();\n        increment();\n      }}>+3</button>\n      <button onClick={() => {\n        increment();\n      }}>+1</button>\n    </>\n  );\n}\n```\n\n```css\nbutton { display: block; margin: 10px; font-size: 20px; }\nh1 { display: block; margin: 10px; }\n```\n\n</Sandpack>\n\n<Solution />\n\n#### 次の state を直接渡す {/*passing-the-next-state-directly*/}\n\nこの例では更新用関数を渡して**いません**。そのため \"+3\" ボタンは**意図した通りには動きません**。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Counter() {\n  const [age, setAge] = useState(42);\n\n  function increment() {\n    setAge(age + 1);\n  }\n\n  return (\n    <>\n      <h1>Your age: {age}</h1>\n      <button onClick={() => {\n        increment();\n        increment();\n        increment();\n      }}>+3</button>\n      <button onClick={() => {\n        increment();\n      }}>+1</button>\n    </>\n  );\n}\n```\n\n```css\nbutton { display: block; margin: 10px; font-size: 20px; }\nh1 { display: block; margin: 10px; }\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n---\n\n### オブジェクトや配列の state を更新する {/*updating-objects-and-arrays-in-state*/}\n\nstate にオブジェクトや配列をセットすることができます。ただし React では、state は読み取り専用として扱う必要があります。そのため、state を更新する場合は、**既存のオブジェクトを直接*書き換える (mutate)* のではなく、*置き換える (replace)* 必要があります**。例えば、state として `form` オブジェクトを保持している場合、以下のように書き換えを行ってはいけません。\n\n```js\n// 🚩 Don't mutate an object in state like this:\nform.firstName = 'Taylor';\n```\n\n代わりに、新しいオブジェクトを作成してオブジェクト全体を置き換えてください。\n\n```js\n// ✅ Replace state with a new object\nsetForm({\n  ...form,\n  firstName: 'Taylor'\n});\n```\n\n詳しくは、[state 内のオブジェクトの更新](/learn/updating-objects-in-state)や、[state 内の配列の更新](/learn/updating-arrays-in-state)を参照してください。\n\n<Recipes titleText=\"オブジェクトや配列を state にする例\" titleId=\"examples-objects\">\n\n#### フォーム（オブジェクト） {/*form-object*/}\n\nこの例では、`form` state 変数はオブジェクトを保持しています。それぞれの input 要素は change ハンドラを持っており、新しい `form` オブジェクトを引数として `setForm` を呼び出します。`{ ...form }` のようにスプレッド構文を用いることで、state オブジェクトを（書き換えではなく）確実に置き換えることができます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [form, setForm] = useState({\n    firstName: 'Barbara',\n    lastName: 'Hepworth',\n    email: 'bhepworth@sculpture.com',\n  });\n\n  return (\n    <>\n      <label>\n        First name:\n        <input\n          value={form.firstName}\n          onChange={e => {\n            setForm({\n              ...form,\n              firstName: e.target.value\n            });\n          }}\n        />\n      </label>\n      <label>\n        Last name:\n        <input\n          value={form.lastName}\n          onChange={e => {\n            setForm({\n              ...form,\n              lastName: e.target.value\n            });\n          }}\n        />\n      </label>\n      <label>\n        Email:\n        <input\n          value={form.email}\n          onChange={e => {\n            setForm({\n              ...form,\n              email: e.target.value\n            });\n          }}\n        />\n      </label>\n      <p>\n        {form.firstName}{' '}\n        {form.lastName}{' '}\n        ({form.email})\n      </p>\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 5px; }\n```\n\n</Sandpack>\n\n<Solution />\n\n#### フォーム（ネストされたオブジェクト） {/*form-nested-object*/}\n\nこの例では、state がネストされたオブジェクトになっています。ネストされたオブジェクトの state を更新する場合、更新するオブジェクトのコピーを作成する必要があります。さらに、そのオブジェクトを内包する上位のオブジェクトも同様に、コピーを作成する必要があります。詳しくは、[ネストされたオブジェクトの更新](/learn/updating-objects-in-state#updating-a-nested-object)を参照してください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [person, setPerson] = useState({\n    name: 'Niki de Saint Phalle',\n    artwork: {\n      title: 'Blue Nana',\n      city: 'Hamburg',\n      image: 'https://i.imgur.com/Sd1AgUOm.jpg',\n    }\n  });\n\n  function handleNameChange(e) {\n    setPerson({\n      ...person,\n      name: e.target.value\n    });\n  }\n\n  function handleTitleChange(e) {\n    setPerson({\n      ...person,\n      artwork: {\n        ...person.artwork,\n        title: e.target.value\n      }\n    });\n  }\n\n  function handleCityChange(e) {\n    setPerson({\n      ...person,\n      artwork: {\n        ...person.artwork,\n        city: e.target.value\n      }\n    });\n  }\n\n  function handleImageChange(e) {\n    setPerson({\n      ...person,\n      artwork: {\n        ...person.artwork,\n        image: e.target.value\n      }\n    });\n  }\n\n  return (\n    <>\n      <label>\n        Name:\n        <input\n          value={person.name}\n          onChange={handleNameChange}\n        />\n      </label>\n      <label>\n        Title:\n        <input\n          value={person.artwork.title}\n          onChange={handleTitleChange}\n        />\n      </label>\n      <label>\n        City:\n        <input\n          value={person.artwork.city}\n          onChange={handleCityChange}\n        />\n      </label>\n      <label>\n        Image:\n        <input\n          value={person.artwork.image}\n          onChange={handleImageChange}\n        />\n      </label>\n      <p>\n        <i>{person.artwork.title}</i>\n        {' by '}\n        {person.name}\n        <br />\n        (located in {person.artwork.city})\n      </p>\n      <img \n        src={person.artwork.image} \n        alt={person.artwork.title}\n      />\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 5px; margin-bottom: 5px; }\nimg { width: 200px; height: 200px; }\n```\n\n</Sandpack>\n\n<Solution />\n\n#### リスト（配列） {/*list-array*/}\n\nこの例では、`todos` state 変数が配列を保持しています。各ボタンのハンドラは、`todos` 配列の新しい値を引数として `setTodos` を呼び出します。スプレッド構文 (`[...todos]`) や、`todos.map()`、`todos.filter()` などを利用すると、state の配列を（書き換えではなく）確実に置き換えることができます。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport AddTodo from './AddTodo.js';\nimport TaskList from './TaskList.js';\n\nlet nextId = 3;\nconst initialTodos = [\n  { id: 0, title: 'Buy milk', done: true },\n  { id: 1, title: 'Eat tacos', done: false },\n  { id: 2, title: 'Brew tea', done: false },\n];\n\nexport default function TaskApp() {\n  const [todos, setTodos] = useState(initialTodos);\n\n  function handleAddTodo(title) {\n    setTodos([\n      ...todos,\n      {\n        id: nextId++,\n        title: title,\n        done: false\n      }\n    ]);\n  }\n\n  function handleChangeTodo(nextTodo) {\n    setTodos(todos.map(t => {\n      if (t.id === nextTodo.id) {\n        return nextTodo;\n      } else {\n        return t;\n      }\n    }));\n  }\n\n  function handleDeleteTodo(todoId) {\n    setTodos(\n      todos.filter(t => t.id !== todoId)\n    );\n  }\n\n  return (\n    <>\n      <AddTodo\n        onAddTodo={handleAddTodo}\n      />\n      <TaskList\n        todos={todos}\n        onChangeTodo={handleChangeTodo}\n        onDeleteTodo={handleDeleteTodo}\n      />\n    </>\n  );\n}\n```\n\n```js src/AddTodo.js\nimport { useState } from 'react';\n\nexport default function AddTodo({ onAddTodo }) {\n  const [title, setTitle] = useState('');\n  return (\n    <>\n      <input\n        placeholder=\"Add todo\"\n        value={title}\n        onChange={e => setTitle(e.target.value)}\n      />\n      <button onClick={() => {\n        setTitle('');\n        onAddTodo(title);\n      }}>Add</button>\n    </>\n  )\n}\n```\n\n```js src/TaskList.js\nimport { useState } from 'react';\n\nexport default function TaskList({\n  todos,\n  onChangeTodo,\n  onDeleteTodo\n}) {\n  return (\n    <ul>\n      {todos.map(todo => (\n        <li key={todo.id}>\n          <Task\n            todo={todo}\n            onChange={onChangeTodo}\n            onDelete={onDeleteTodo}\n          />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction Task({ todo, onChange, onDelete }) {\n  const [isEditing, setIsEditing] = useState(false);\n  let todoContent;\n  if (isEditing) {\n    todoContent = (\n      <>\n        <input\n          value={todo.title}\n          onChange={e => {\n            onChange({\n              ...todo,\n              title: e.target.value\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    todoContent = (\n      <>\n        {todo.title}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={todo.done}\n        onChange={e => {\n          onChange({\n            ...todo,\n            done: e.target.checked\n          });\n        }}\n      />\n      {todoContent}\n      <button onClick={() => onDelete(todo.id)}>\n        Delete\n      </button>\n    </label>\n  );\n}\n```\n\n```css\nbutton { margin: 5px; }\nli { list-style-type: none; }\nul, li { margin: 0; padding: 0; }\n```\n\n</Sandpack>\n\n<Solution />\n\n#### Immer で簡潔な更新ロジックを書く {/*writing-concise-update-logic-with-immer*/}\n\n配列やオブジェクトの書き換えを行わずに state を更新することが煩雑に感じる場合、[Immer](https://github.com/immerjs/use-immer) のようなライブラリを用いて繰り返しのコードを減らすことができます。Immer を利用することで、オブジェクトを書き換えているかのような簡潔なコードを書くことができます。しかし内部では、イミュータブル（不変, immutable）な更新が実行されます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport { useImmer } from 'use-immer';\n\nlet nextId = 3;\nconst initialList = [\n  { id: 0, title: 'Big Bellies', seen: false },\n  { id: 1, title: 'Lunar Landscape', seen: false },\n  { id: 2, title: 'Terracotta Army', seen: true },\n];\n\nexport default function BucketList() {\n  const [list, updateList] = useImmer(initialList);\n\n  function handleToggle(artworkId, nextSeen) {\n    updateList(draft => {\n      const artwork = draft.find(a =>\n        a.id === artworkId\n      );\n      artwork.seen = nextSeen;\n    });\n  }\n\n  return (\n    <>\n      <h1>Art Bucket List</h1>\n      <h2>My list of art to see:</h2>\n      <ItemList\n        artworks={list}\n        onToggle={handleToggle} />\n    </>\n  );\n}\n\nfunction ItemList({ artworks, onToggle }) {\n  return (\n    <ul>\n      {artworks.map(artwork => (\n        <li key={artwork.id}>\n          <label>\n            <input\n              type=\"checkbox\"\n              checked={artwork.seen}\n              onChange={e => {\n                onToggle(\n                  artwork.id,\n                  e.target.checked\n                );\n              }}\n            />\n            {artwork.title}\n          </label>\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"immer\": \"1.7.3\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"use-immer\": \"0.5.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n---\n\n### 初期 state が再生成されることを防ぐ {/*avoiding-recreating-the-initial-state*/}\n\nReact は一度だけ初期 state を保存し、2 回目以降のレンダーではそれを無視します。\n\n```js\nfunction TodoList() {\n  const [todos, setTodos] = useState(createInitialTodos());\n  // ...\n```\n\n`createInitialTodos()` は毎レンダーで呼び出されるものの、その結果は初回レンダーでのみ利用されます。これは、`createInitialTodos()`が巨大な配列の生成やコストの高い計算を行っている場合に、無駄が多くなります。\n\nこれを解決するため、以下のように***初期化関数*を渡す**ことができます。\n\n```js\nfunction TodoList() {\n  const [todos, setTodos] = useState(createInitialTodos);\n  // ...\n```\n\n関数を呼び出した結果である `createInitialTodos()` ではなく、`createInitialTodos` という*関数それ自体*を渡していることに注意してください。`useState` に関数が渡された場合、React は初期化時に、関数を一度だけ呼び出します。\n\n初期化関数が[純粋](/learn/keeping-components-pure)であることを確認するため、開発時に[初期化関数が 2 回呼び出される](#my-initializer-or-updater-function-runs-twice)ことがあります。\n\n<Recipes titleText=\"初期化関数を渡す場合と初期値を直接渡す場合の違い\" titleId=\"examples-initializer\">\n\n#### 初期化関数を渡す {/*passing-the-initializer-function*/}\n\nこの例では、初期化関数を利用しています。そのため、`createInitialTodos` 関数は初期化時のみ実行されます。入力フィールドに文字を入力した場合などの、コンポーネントの再レンダー時には実行されません。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nfunction createInitialTodos() {\n  const initialTodos = [];\n  for (let i = 0; i < 50; i++) {\n    initialTodos.push({\n      id: i,\n      text: 'Item ' + (i + 1)\n    });\n  }\n  return initialTodos;\n}\n\nexport default function TodoList() {\n  const [todos, setTodos] = useState(createInitialTodos);\n  const [text, setText] = useState('');\n\n  return (\n    <>\n      <input\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button onClick={() => {\n        setText('');\n        setTodos([{\n          id: todos.length,\n          text: text\n        }, ...todos]);\n      }}>Add</button>\n      <ul>\n        {todos.map(item => (\n          <li key={item.id}>\n            {item.text}\n          </li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n#### 初期 state を直接渡す {/*passing-the-initial-state-directly*/}\n\nこの例では、初期化関数を利用して**いません**。そのため、`createInitialTodos` 関数は、入力フィールドに文字を入力したときなどのすべてのレンダーで実行されます。挙動に目に見える違いはありませんが、少し効率が悪くなります。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nfunction createInitialTodos() {\n  const initialTodos = [];\n  for (let i = 0; i < 50; i++) {\n    initialTodos.push({\n      id: i,\n      text: 'Item ' + (i + 1)\n    });\n  }\n  return initialTodos;\n}\n\nexport default function TodoList() {\n  const [todos, setTodos] = useState(createInitialTodos());\n  const [text, setText] = useState('');\n\n  return (\n    <>\n      <input\n        value={text}\n        onChange={e => setText(e.target.value)}\n      />\n      <button onClick={() => {\n        setText('');\n        setTodos([{\n          id: todos.length,\n          text: text\n        }, ...todos]);\n      }}>Add</button>\n      <ul>\n        {todos.map(item => (\n          <li key={item.id}>\n            {item.text}\n          </li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n<Solution />\n\n</Recipes>\n\n---\n\n### key を利用して state をリセットする {/*resetting-state-with-a-key*/}\n\n`key` 属性は、[リストをレンダーする場合](/learn/rendering-lists)によく利用します。しかし、もう 1 つの使い道があります。\n\n**コンポーネントに異なる `key` を渡すことで、コンポーネントの state をリセットすることができます**。この例では、`version` state 変数を `Form` に `key` として渡しています。\"Reset\" ボタンをクリックすると、`version` state 変数が変化します。`key` が変化したとき、React は `Form` コンポーネント（と、そのすべての子コンポーネント）を一から再生成するため、`Form` の state がリセットされます。\n\n詳しくは、[state の保持とリセット](/learn/preserving-and-resetting-state)を参照してください。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\n\nexport default function App() {\n  const [version, setVersion] = useState(0);\n\n  function handleReset() {\n    setVersion(version + 1);\n  }\n\n  return (\n    <>\n      <button onClick={handleReset}>Reset</button>\n      <Form key={version} />\n    </>\n  );\n}\n\nfunction Form() {\n  const [name, setName] = useState('Taylor');\n\n  return (\n    <>\n      <input\n        value={name}\n        onChange={e => setName(e.target.value)}\n      />\n      <p>Hello, {name}.</p>\n    </>\n  );\n}\n```\n\n```css\nbutton { display: block; margin-bottom: 20px; }\n```\n\n</Sandpack>\n\n---\n\n### 直前のレンダーの情報を保存する {/*storing-information-from-previous-renders*/}\n\n通常、state の更新はイベントハンドラの中で行われます。しかし、レンダーに応じて state を設定したい場合があります。例えば、prop が変化したときに state 変数を変化させたい場合です。\n\n以下に示すように、ほとんどのケースでは不要です。\n\n* **もし必要な値が現在の props と他の state のみから導出される場合、[冗長な state を削除してください](/learn/choosing-the-state-structure#avoid-redundant-state)**。もし何度も再計算されることが気になる場合は、[`useMemo` フック](/reference/react/useMemo)が役に立ちます。\n* もしコンポーネントツリーの state 全体をリセットしたい場合、[コンポーネントに異なる `key` を渡してください](#resetting-state-with-a-key)。\n* 可能であれば、関連するすべての state をイベントハンドラの中で更新してください。\n\nこれらがどれも適用できない稀なケースでは、コンポーネントのレンダー中に `set` 関数を呼び出し、それまでにレンダーされた値に基づいて state を更新するパターンが利用できます。\n\n以下の例では、`CountLabel` コンポーネントは、渡された `count` プロパティを表示しています。\n\n```js src/CountLabel.js\nexport default function CountLabel({ count }) {\n  return <h1>{count}</h1>\n}\n```\n\n直近の変更で、counter の値が*増えたのか減ったのか*を表示したいとします。`count` プロパティだけでは知ることができないため、前回の値を保持し続ける必要があります。前回の値を保持するために、`prevCount` state 変数を追加します。さらに、`trend` state 変数を追加し、count が増えたのか減ったのかを保持します。`prevCount` と `count` を比較し、もしこれらが一致しない場合に、`prevCount` と `trend` を更新します。これで、現在の count プロパティと、*前回のレンダーからどのように変化したのか*の両方を表示することができます。\n\n<Sandpack>\n\n```js src/App.js\nimport { useState } from 'react';\nimport CountLabel from './CountLabel.js';\n\nexport default function App() {\n  const [count, setCount] = useState(0);\n  return (\n    <>\n      <button onClick={() => setCount(count + 1)}>\n        Increment\n      </button>\n      <button onClick={() => setCount(count - 1)}>\n        Decrement\n      </button>\n      <CountLabel count={count} />\n    </>\n  );\n}\n```\n\n```js src/CountLabel.js active\nimport { useState } from 'react';\n\nexport default function CountLabel({ count }) {\n  const [prevCount, setPrevCount] = useState(count);\n  const [trend, setTrend] = useState(null);\n  if (prevCount !== count) {\n    setPrevCount(count);\n    setTrend(count > prevCount ? 'increasing' : 'decreasing');\n  }\n  return (\n    <>\n      <h1>{count}</h1>\n      {trend && <p>The count is {trend}</p>}\n    </>\n  );\n}\n```\n\n```css\nbutton { margin-bottom: 10px; }\n```\n\n</Sandpack>\n\nレンダー中に `set` 関数を呼び出す場合は、`prevCount !== count` のような条件文の中でなければならず、その中には `setPrevCount(count)` のような呼び出しが書かれなければならないことに注意してください。さもないと、再レンダーのループに陥り、コンポーネントがクラッシュします。また、例のように、*現在レンダーしている*コンポーネントの state のみ更新することができます。レンダー中に*別の*コンポーネントの `set` 関数を呼び出すとエラーになります。最後に、`set` 関数の呼び出しは、[書き換えなしで state を更新](#updating-objects-and-arrays-in-state)する必要があります。これは、[純関数](/learn/keeping-components-pure)の他のルールを破ることができないことを意味します。\n\nこのパターンは理解するのが難しいため、通常は避けるべきです。しかし、エフェクト内で state を更新するよりは良い方法です。レンダー中に `set` 関数を呼び出すと、コンポーネントが `return` 文で終了した直後、子コンポーネントをレンダーする前に再レンダーが行われます。このため、子コンポーネントが 2 回レンダーされずに済みます。コンポーネント関数の残りの部分は引き続き実行されます（結果は破棄されますが）。もし、`set` 関数の呼び出しを含む条件分岐が、すべてのフックの呼び出しより下にある場合、早期 `return;` を追加して、再レンダーを早めることができます。\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### state を更新したのに古い値がログに表示される {/*ive-updated-the-state-but-logging-gives-me-the-old-value*/}\n\n`set` 関数の呼び出しは、実行中のコードの state を変化**させません**。\n\n```js {4,5,8}\nfunction handleClick() {\n  console.log(count);  // 0\n\n  setCount(count + 1); // Request a re-render with 1\n  console.log(count);  // Still 0!\n\n  setTimeout(() => {\n    console.log(count); // Also 0!\n  }, 5000);\n}\n```\n\nこれは、[state がスナップショットのように振る舞う](/learn/state-as-a-snapshot)ためです。state の更新は、新しい state の値での再レンダーをリクエストします。すでに実行中のイベントハンドラ内の `count` という JavaScript 変数には影響を与えません。\n\n次の state が必要な場合は、`set` 関数に渡す前に一度変数に保存することができます。\n\n```js\nconst nextCount = count + 1;\nsetCount(nextCount);\n\nconsole.log(count);     // 0\nconsole.log(nextCount); // 1\n```\n\n---\n\n### state を更新したのに画面が更新されない {/*ive-updated-the-state-but-the-screen-doesnt-update*/}\n\nReact では、**更新の前後で state の値が変化しない場合、その変更は無視されます**。state の値の変化は、[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) によって判断されます。この現象は、state のオブジェクトや配列を直接書き換えた場合によく起こります。\n\n```js\nobj.x = 10;  // 🚩 Wrong: mutating existing object\nsetObj(obj); // 🚩 Doesn't do anything\n```\n\n既存の `obj` オブジェクトを書き換えて、`setObj` に戻したため、この更新は無視されます。修正するには、state のオブジェクトや配列を[*書き換える*のではなく、*置き換える*](#updating-objects-and-arrays-in-state)必要があります。\n\n```js\n// ✅ Correct: creating a new object\nsetObj({\n  ...obj,\n  x: 10\n});\n```\n\n---\n\n### \"Too many re-renders\" というエラーが出る {/*im-getting-an-error-too-many-re-renders*/}\n\n`Too many re-renders. React limits the number of renders to prevent an infinite loop.` というエラーが出ることがあります。これは通常、*レンダー中に*無条件に `set` 関数を呼び出しているため、コンポーネントがループに入っていることを意味します。レンダー、`set` 関数の呼び出し（レンダーを引き起こす）、レンダー、`set` 関数の呼び出し（レンダーを引き起こす）、というように続きます。大抵の場合、これはイベントハンドラの指定を間違ったことによるものです。\n\n```js {1-2}\n// 🚩 Wrong: calls the handler during render\nreturn <button onClick={handleClick()}>Click me</button>\n\n// ✅ Correct: passes down the event handler\nreturn <button onClick={handleClick}>Click me</button>\n\n// ✅ Correct: passes down an inline function\nreturn <button onClick={(e) => handleClick(e)}>Click me</button>\n```\n\nこのエラーの原因がわからない場合は、コンソールのエラーの横にある矢印をクリックして、JavaScript スタックを調べ、エラーの原因となる `set` 関数の呼び出しを特定してください。\n\n---\n\n### 初期化関数や更新用関数が 2 度呼ばれる {/*my-initializer-or-updater-function-runs-twice*/}\n\n[Strict Mode](/reference/react/StrictMode) では、いくつかの関数が、本来 1 回のところを 2 回呼び出されることがあります。\n\n```js {2,5-6,11-12}\nfunction TodoList() {\n  // This component function will run twice for every render.\n\n  const [todos, setTodos] = useState(() => {\n    // This initializer function will run twice during initialization.\n    return createTodos();\n  });\n\n  function handleClick() {\n    setTodos(prevTodos => {\n      // This updater function will run twice for every click.\n      return [...prevTodos, createTodo()];\n    });\n  }\n  // ...\n```\n\nこれは予想される動作であり、あなたのコードを壊すものではありません。\n\nこれは**開発時のみ**の挙動で、[コンポーネントを純粋に保つ](/learn/keeping-components-pure)ために役立ちます。React は、呼び出し結果の 1 つを利用し、もう 1 つを無視します。コンポーネント、初期化関数、更新用関数が純粋であれば、この挙動があなたのロジックに影響を与えることはありません。ただし、誤って純粋でない関数を指定した場合は、これにより間違いに気付くことができるでしょう。\n\n例えば以下の更新用関数は、state の配列を書き換えるため純粋ではありません。\n\n```js {2,3}\nsetTodos(prevTodos => {\n  // 🚩 Mistake: mutating state\n  prevTodos.push(createTodo());\n});\n```\n\nReact は更新用関数を 2 回呼び出すため、todo が 2 つ追加されてしまい、間違いに気付くことができます。この例では、配列を[書き換えるのではなく、置き換える](#updating-objects-and-arrays-in-state)ことで間違いを修正できます。\n\n```js {2,3}\nsetTodos(prevTodos => {\n  // ✅ Correct: replacing with new state\n  return [...prevTodos, createTodo()];\n});\n```\n\n更新用関数が純粋になったため、複数回呼び出されても動作に影響しません。これが、2 回呼び出されることで間違いに気付くことができる理由です。**コンポーネント、初期化関数、更新用関数のみが純粋である必要があります**。イベントハンドラは、純粋である必要がないため、2 回呼び出されることはありません。\n\n詳しくは、[コンポーネントを純粋に保つ](/learn/keeping-components-pure)を参照してください。\n\n---\n\n### 関数を state にセットしようとすると、その関数が呼び出されてしまう {/*im-trying-to-set-state-to-a-function-but-it-gets-called-instead*/}\n\nこのような形で関数を state に設定することはできません。\n\n```js\nconst [fn, setFn] = useState(someFunction);\n\nfunction handleClick() {\n  setFn(someOtherFunction);\n}\n```\n\n関数を渡すと、React は `someFunction` を[初期化関数](#avoiding-recreating-the-initial-state)、`someOtherFunction` を[更新用関数](#updating-state-based-on-the-previous-state)として扱います。そのため、それらを呼び出し、その結果を保存しようとします。関数を実行するのではなく*保存*するには、どちらの場合も `() =>` を前に付ける必要があります。こうすると、React は関数自体を保存します。\n\n```js {1,4}\nconst [fn, setFn] = useState(() => someFunction);\n\nfunction handleClick() {\n  setFn(() => someOtherFunction);\n}\n```\n"
  },
  {
    "path": "src/content/reference/react/useSyncExternalStore.md",
    "content": "---\ntitle: useSyncExternalStore\n---\n\n<Intro>\n\n`useSyncExternalStore` は、外部ストアへのサブスクライブを可能にする React のフックです。\n\n```js\nconst snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)` {/*usesyncexternalstore*/}\n\n外部データストアから値を読み取るために、コンポーネントのトップレベルで useSyncExternalStore を呼び出します。\n\n```js\nimport { useSyncExternalStore } from 'react';\nimport { todosStore } from './todoStore.js';\n\nfunction TodosApp() {\n  const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot);\n  // ...\n}\n```\n\nこれは、ストアにあるデータのスナップショットを返します。引数として 2 つの関数を渡す必要があります：\n\n1. `subscribe` 関数はストアへのサブスクライブを開始します。サブスクライブを解除する関数を返す必要があります。 \n2. `getSnapshot` 関数は、ストアからデータのスナップショットを読み取る必要があります。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `subscribe`: ストアにサブスクライブを開始し、また callback 引数を受け取る関数。この関数はストアが変更された際に渡された callback を呼び出す必要があります。これにより React は `getSnapshot` を呼び出し、（必要に応じて）コンポーネントを再レンダーします。`subscribe` 関数は、サブスクリプションをクリーンアップする関数を返す必要があります。\n\n* `getSnapshot`: コンポーネントが必要とするストアにあるデータのスナップショットを返す関数。ストアが変更されていない場合、`getSnapshot` への再呼び出しは同じ値を返す必要があります。ストアが変更されて返された値が（[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) で比較して）異なる場合、React はコンポーネントを再レンダーします。\n\n* **省略可能** `getServerSnapshot`: ストアのデータの初期スナップショットを返す関数。これはサーバレンダリング中、およびクライアント上でのサーバレンダリングされたコンテンツのハイドレーション中にのみ使用されます。サーバスナップショットはクライアントとサーバ間で同一でなければならず、通常はサーバからクライアントに渡されるシリアライズされたものです。この引数を省略すると、サーバ上でのコンポーネントのレンダリングはエラーを発生させます。\n\n#### 返り値 {/*returns*/}\n\nレンダリングロジックで使用できるストアの現在のスナップショット。\n\n#### 注意点 {/*caveats*/}\n\n* `getSnapshot` によって返されるストアのスナップショットはイミュータブル（immutable; 書き換え不能）でなければなりません。背後で使っているストアがミュータブルなデータを持っている場合、データが変更された場合は新しいイミュータブルなスナップショットを返し、それ以外の場合はキャッシュされた最後のスナップショットを返すようにします。\n\n* 再レンダー中に異なる `subscribe` 関数が渡された場合、React は新しく渡された `subscribe` 関数を使ってストアに再サブスクライブします。これを防ぐには、`subscribe` をコンポーネントの外で宣言します。\n\n* [ノンブロッキング型のトランジション更新](/reference/react/useTransition)の最中にストアの書き換えが発生した場合、React はその更新をブロッキング型で行うようにフォールバックします。具体的には、トランザクションによる更新のたびに、React は DOM に更新を適用する前に `getSnapshot` を再度呼び出します。そこで最初の値とは異なる値が返された場合、React は更新を最初からやり直しますが、再試行時にはブロッキング型の更新を行うことで、画面上の全コンポーネントがストアからの同一バージョンの値を反映していることを保証します。\n\n* `useSyncExternalStore` から返される値に基づいてレンダーを*サスペンド*させることは推奨されていません。外部ストアで起きた変更は[ノンブロッキング型のトランジション更新](/reference/react/useTransition)としてマークすることができないため、直近の [`Suspense` フォールバック](/reference/react/Suspense)が起動してしまいます。既に画面上に表示されているコンテンツがローディングスピナで隠れてしまうため、通常は望ましくないユーザ体験につながります。\n\n  例えば以下のようなコードは推奨されません。\n\n  ```js\n  const LazyProductDetailPage = lazy(() => import('./ProductDetailPage.js'));\n\n  function ShoppingApp() {\n    const selectedProductId = useSyncExternalStore(...);\n\n    // ❌ Calling `use` with a Promise dependent on `selectedProductId`\n    const data = use(fetchItem(selectedProductId))\n\n    // ❌ Conditionally rendering a lazy component based on `selectedProductId`\n    return selectedProductId != null ? <LazyProductDetailPage /> : <FeaturedProducts />;\n  }\n  ```\n\n---\n\n## 使用法 {/*usage*/}\n\n### 外部ストアへのサブスクライブ {/*subscribing-to-an-external-store*/}\n\nReact コンポーネントのほとんどは、[props](/learn/passing-props-to-a-component)、[state](/reference/react/useState) および[コンテクスト](/reference/react/useContext)からのみデータを読み取ります。しかし、コンポーネントは時間と共に変化する React 外のストアからデータを読み取る必要がある場合があります。これには以下のようなものが含まれます：\n\n* React の外部で状態を保持するサードパーティの状態管理ライブラリ。\n* 可変の値を、その変更にサブスクライブするためのイベントともに公開するブラウザ API。\n\n外部データストアから値を読み取るために、コンポーネントの最上位で `useSyncExternalStore` を呼び出します。\n\n```js [[1, 5, \"todosStore.subscribe\"], [2, 5, \"todosStore.getSnapshot\"], [3, 5, \"todos\", 0]]\nimport { useSyncExternalStore } from 'react';\nimport { todosStore } from './todoStore.js';\n\nfunction TodosApp() {\n  const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot);\n  // ...\n}\n```\n\nこれはストア内のデータの<CodeStep step={3}>スナップショット</CodeStep>を返します。引数として 2 つの関数を渡す必要があります：\n\n1. <CodeStep step={1}>`subscribe` 関数</CodeStep>は、ストアへのサブスクライブを行い、またサブスクライブを解除する関数を返します。\n2. <CodeStep step={2}>`getSnapshot` 関数</CodeStep>は、ストアからデータのスナップショットを読み取ります。\n\nReact はこれらの関数を使ってコンポーネントをストアにサブスクライブされた状態に保ち、変更があるたびに再レンダーします。\n\n例えば、以下のサンドボックスでは、`todosStore` は React の外部にデータを保存する外部ストアとして実装されています。`TodosApp` コンポーネントは、`useSyncExternalStore` フックを使ってその外部ストアに接続します。 \n\n<Sandpack>\n\n```js\nimport { useSyncExternalStore } from 'react';\nimport { todosStore } from './todoStore.js';\n\nexport default function TodosApp() {\n  const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot);\n  return (\n    <>\n      <button onClick={() => todosStore.addTodo()}>Add todo</button>\n      <hr />\n      <ul>\n        {todos.map(todo => (\n          <li key={todo.id}>{todo.text}</li>\n        ))}\n      </ul>\n    </>\n  );\n}\n```\n\n```js src/todoStore.js\n// This is an example of a third-party store\n// that you might need to integrate with React.\n\n// If your app is fully built with React,\n// we recommend using React state instead.\n\nlet nextId = 0;\nlet todos = [{ id: nextId++, text: 'Todo #1' }];\nlet listeners = [];\n\nexport const todosStore = {\n  addTodo() {\n    todos = [...todos, { id: nextId++, text: 'Todo #' + nextId }]\n    emitChange();\n  },\n  subscribe(listener) {\n    listeners = [...listeners, listener];\n    return () => {\n      listeners = listeners.filter(l => l !== listener);\n    };\n  },\n  getSnapshot() {\n    return todos;\n  }\n};\n\nfunction emitChange() {\n  for (let listener of listeners) {\n    listener();\n  }\n}\n```\n\n</Sandpack>\n\n<Note>\n\n可能であれば、React 組み込みの state 管理機能である [`useState`](/reference/react/useState) および [`useReducer`](/reference/react/useReducer) を代わりに使用することをお勧めします。`useSyncExternalStore` API は、既存の非 React コードと統合する必要がある場合に主に役立ちます。\n\n</Note>\n\n---\n\n### ブラウザ API へのサブスクライブ {/*subscribing-to-a-browser-api*/}\n\n`useSyncExternalStore` を追加するもう 1 つの理由は、時間とともに変化する、ブラウザが公開する値にサブスクライブしたい場合です。たとえば、コンポーネントがネットワーク接続がアクティブかどうかを表示したいとします。ブラウザは、この情報を [`navigator.onLine`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine) というプロパティを介して公開します。\n\nこの値は React の知らないところで変更される可能性があるので、`useSyncExternalStore` でそれを読み取るべきです。\n\n```js\nimport { useSyncExternalStore } from 'react';\n\nfunction ChatIndicator() {\n  const isOnline = useSyncExternalStore(subscribe, getSnapshot);\n  // ...\n}\n```\n\n`getSnapshot` 関数を実装するためには、ブラウザ API から現在の値を読み取ることが必要です：\n\n```js\nfunction getSnapshot() {\n  return navigator.onLine;\n}\n```\n\n次に、`subscribe` 関数を実装する必要があります。例えば、`navigator.onLine` が変化すると、ブラウザは `window` オブジェクト上で [`online`](https://developer.mozilla.org/en-US/docs/Web/API/Window/online_event) および [`offline`](https://developer.mozilla.org/en-US/docs/Web/API/Window/offline_event) というイベントを発火します。これら対応するイベントに `callback` 引数を登録し、それを解除する関数を返す必要があります：\n\n```js\nfunction subscribe(callback) {\n  window.addEventListener('online', callback);\n  window.addEventListener('offline', callback);\n  return () => {\n    window.removeEventListener('online', callback);\n    window.removeEventListener('offline', callback);\n  };\n}\n```\n\nこれで React は、外部の `navigator.onLine` API から値を読み取る方法と、その変更にサブスクライブする方法を知ることができます。ネットワークからデバイスを切断すると、コンポーネントが反応して再レンダーされることに注目してください：\n\n<Sandpack>\n\n```js\nimport { useSyncExternalStore } from 'react';\n\nexport default function ChatIndicator() {\n  const isOnline = useSyncExternalStore(subscribe, getSnapshot);\n  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;\n}\n\nfunction getSnapshot() {\n  return navigator.onLine;\n}\n\nfunction subscribe(callback) {\n  window.addEventListener('online', callback);\n  window.addEventListener('offline', callback);\n  return () => {\n    window.removeEventListener('online', callback);\n    window.removeEventListener('offline', callback);\n  };\n}\n```\n\n</Sandpack>\n\n---\n\n### ロジックをカスタムフックに抽出する {/*extracting-the-logic-to-a-custom-hook*/}\n\n通常、`useSyncExternalStore` を直接コンポーネント内に記述することはありません。代わりに、自分自身のカスタムフックから呼び出すことが一般的です。これにより、異なるコンポーネントから同じ外部ストアを使用できます。\n\n例えば、このカスタム `useOnlineStatus` フックはネットワークがオンラインであるかどうかを追跡します：\n\n```js {3,6}\nimport { useSyncExternalStore } from 'react';\n\nexport function useOnlineStatus() {\n  const isOnline = useSyncExternalStore(subscribe, getSnapshot);\n  return isOnline;\n}\n\nfunction getSnapshot() {\n  // ...\n}\n\nfunction subscribe(callback) {\n  // ...\n}\n```\n\nこれで、異なるコンポーネントが、基本的な実装を繰り返すことなく `useOnlineStatus` を呼び出せるようになりました：\n\n<Sandpack>\n\n```js\nimport { useOnlineStatus } from './useOnlineStatus.js';\n\nfunction StatusBar() {\n  const isOnline = useOnlineStatus();\n  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;\n}\n\nfunction SaveButton() {\n  const isOnline = useOnlineStatus();\n\n  function handleSaveClick() {\n    console.log('✅ Progress saved');\n  }\n\n  return (\n    <button disabled={!isOnline} onClick={handleSaveClick}>\n      {isOnline ? 'Save progress' : 'Reconnecting...'}\n    </button>\n  );\n}\n\nexport default function App() {\n  return (\n    <>\n      <SaveButton />\n      <StatusBar />\n    </>\n  );\n}\n```\n\n```js src/useOnlineStatus.js\nimport { useSyncExternalStore } from 'react';\n\nexport function useOnlineStatus() {\n  const isOnline = useSyncExternalStore(subscribe, getSnapshot);\n  return isOnline;\n}\n\nfunction getSnapshot() {\n  return navigator.onLine;\n}\n\nfunction subscribe(callback) {\n  window.addEventListener('online', callback);\n  window.addEventListener('offline', callback);\n  return () => {\n    window.removeEventListener('online', callback);\n    window.removeEventListener('offline', callback);\n  };\n}\n```\n\n</Sandpack>\n\n---\n\n### サーバレンダリングのサポートを追加する {/*adding-support-for-server-rendering*/}\n\nReact アプリが[サーバレンダリング](/reference/react-dom/server)を使用している場合、React コンポーネントは初期 HTML を生成するためにブラウザ環境外でも実行されます。これにより、外部ストアへの接続に関するいくつかの課題が生じます。\n\n- ブラウザ専用の API に接続している場合、それはサーバ上では存在しないため動作しません。\n- サードパーティのデータストアに接続している場合、サーバとクライアント間でそのデータを一致させる必要があります。\n\nこれらの問題を解決するために、`useSyncExternalStore` に `getServerSnapshot` 関数を第 3 引数として渡します：\n\n```js {4,12-14}\nimport { useSyncExternalStore } from 'react';\n\nexport function useOnlineStatus() {\n  const isOnline = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n  return isOnline;\n}\n\nfunction getSnapshot() {\n  return navigator.onLine;\n}\n\nfunction getServerSnapshot() {\n  return true; // Always show \"Online\" for server-generated HTML\n}\n\nfunction subscribe(callback) {\n  // ...\n}\n```\n\n`getServerSnapshot` 関数は `getSnapshot` と似ていますが、以下の 2 つの状況でのみ実行されます：\n\n- サーバ上で、HTML を生成する際に実行される。\n- クライアント上で、React がサーバ HTML をインタラクティブにするとき、つまり[ハイドレーション](/reference/react-dom/client/hydrateRoot)中に実行される。\n\nこれにより、アプリがインタラクティブになる前に使用される初期のスナップショット値を指定できます。サーバレンダリング中に意味のある初期値が存在しない場合は、この引数を省略して、[強制的にクライアントでレンダーする](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content)ようにします。\n\n<Note>\n\n初回のクライアントレンダリングでは、`getServerSnapshot` はサーバで返したものと必ず正確に同一のデータを返すようにしてください。例えば、`getServerSnapshot` がサーバ上で事前に準備されたストアコンテンツを返した場合、このコンテンツをクライアントに転送する必要があります。これを行う 1 つの方法は、サーバレンダリング中に `window.MY_STORE_DATA` のようなグローバル変数を設定する `<script>` タグを発行しておき、クライアントの `getServerSnapshot` でそのグローバル変数から読み込むことです。あなたが使う外部ストアにその方法が記載されているはずです。\n\n</Note>\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### \"The result of `getSnapshot` should be cached\" というエラーが出る {/*im-getting-an-error-the-result-of-getsnapshot-should-be-cached*/}\n\nこのエラーは、`getSnapshot` 関数が呼ばれるたびに新しいオブジェクトを返していることを意味します。例えば：\n\n```js {2-5}\nfunction getSnapshot() {\n  // 🔴 Do not return always different objects from getSnapshot\n  return {\n    todos: myStore.todos\n  };\n}\n```\n\n`getSnapshot` の返り値が前回と異なる場合、React はコンポーネントを再レンダーします。このため、常に異なる値を返すと無限ループに入り、このエラーが発生します。\n\n`getSnapshot` オブジェクトは、実際に何かが変更された場合にのみ、別のオブジェクトを返す必要があります。ストアにイミュータブルなデータが含まれている場合は、そのデータを直接返すことができます：\n\n```js {2-3}\nfunction getSnapshot() {\n  // ✅ You can return immutable data\n  return myStore.todos;\n}\n```\n\nストアデータがミュータブルな場合、`getSnapshot` 関数はそのイミュータブルなスナップショットを返す必要があります。つまり、新しいオブジェクトを作成する必要は**あります**が、毎回作成してはいけないということです。その代わりに、最後に計算されたスナップショットを保存しておき、ストア内のデータが変更されていない場合は前回と同じスナップショットを返すようにします。ミュータブルなデータが変更されたかどうかを判断する方法は、ミュータブルなストアによって異なります。\n\n---\n\n### `subscribe` が毎レンダーごとに呼び出される {/*my-subscribe-function-gets-called-after-every-re-render*/}\n\nこの `subscribe` 関数はコンポーネントの**内部**で定義されているため、再レンダーするたびに異なった値になります：\n\n```js {2-5}\nfunction ChatIndicator() {\n  // 🚩 Always a different function, so React will resubscribe on every re-render\n  function subscribe() {\n    // ...\n  }\n  \n  const isOnline = useSyncExternalStore(subscribe, getSnapshot);\n\n  // ...\n}\n```\n\nReact は、再レンダー間で異なる `subscribe` 関数を渡すと、ストアに再サブスクライブします。これがパフォーマンスの問題を引き起こし、再サブスクライブを避けたい場合は、`subscribe` 関数を外部に移動してください：\n\n```js {1-4}\n// ✅ Always the same function, so React won't need to resubscribe\nfunction subscribe() {\n  // ...\n}\n\nfunction ChatIndicator() {\n  const isOnline = useSyncExternalStore(subscribe, getSnapshot);\n  // ...\n}\n```\n\nあるいは、`subscribe` を [`useCallback`](/reference/react/useCallback) でラップすることで、引数が変更されたときのみ再サブスクライブすることができます：\n\n```js {2-5}\nfunction ChatIndicator({ userId }) {\n  // ✅ Same function as long as userId doesn't change\n  const subscribe = useCallback(() => {\n    // ...\n  }, [userId]);\n  \n  const isOnline = useSyncExternalStore(subscribe, getSnapshot);\n\n  // ...\n}\n```\n"
  },
  {
    "path": "src/content/reference/react/useTransition.md",
    "content": "---\ntitle: useTransition\n---\n\n<Intro>\n\n`useTransition` は、UI を部分的にバックグラウンドでレンダーするための React フックです。\n\n```js\nconst [isPending, startTransition] = useTransition()\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useTransition()` {/*usetransition*/}\n\nコンポーネントのトップレベルで `useTransition` を呼び出し、state 更新の一部をトランジションとしてマークします。\n\n```js\nimport { useTransition } from 'react';\n\nfunction TabContainer() {\n  const [isPending, startTransition] = useTransition();\n  // ...\n}\n```\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n`useTransition` には引数はありません。\n\n#### 返り値 {/*returns*/}\n\n`useTransition` は常に 2 つの要素を含む配列を返します。\n\n1. トランジションが保留中であるかどうかを示す `isPending` フラグ。\n2. 更新をトランジションとしてマークするための [`startTransition` 関数](#starttransition)。\n\n---\n\n### `startTransition(action)` {/*starttransition*/}\n\n`useTransition` によって返される `startTransition` 関数により、更新をトランジションとしてマークすることができます。\n\n```js {6,8}\nfunction TabContainer() {\n  const [isPending, startTransition] = useTransition();\n  const [tab, setTab] = useState('about');\n\n  function selectTab(nextTab) {\n    startTransition(() => {\n      setTab(nextTab);\n    });\n  }\n  // ...\n}\n```\n\n<Note>\n#### `startTransition` で呼び出される関数は「アクション」と呼ばれる。 {/*functions-called-in-starttransition-are-called-actions*/}\n\n`startTransition` に渡される関数は「アクション」と呼ばれます。慣習として、（コールバック props のように）`startTransition` 内で呼び出されるコールバックは `action` という名前にするか、末尾に \"Action\" というサフィックスを含めるとよいでしょう。\n\n```js {1,9}\nfunction SubmitButton({ submitAction }) {\n  const [isPending, startTransition] = useTransition();\n\n  return (\n    <button\n      disabled={isPending}\n      onClick={() => {\n        startTransition(async () => {\n          await submitAction();\n        });\n      }}\n    >\n      Submit\n    </button>\n  );\n}\n\n```\n\n</Note>\n\n\n\n#### 引数 {/*starttransition-parameters*/}\n\n* `action`: 1 つ以上の [`set` 関数](/reference/react/useState#setstate)を呼び出して state を更新する関数。React は引数なしで直ちに `action` を呼び出し、`action` 関数呼び出し中に同期的にスケジュールされたすべての state 更新をトランジションとしてマークします。`action` 内で await されている非同期関数のコールもトランジションの一部ではありますが、現時点では `await` の後に来る `set` 関数は別の `startTransition` にラップする必要があります（[トラブルシューティング](#react-doesnt-treat-my-state-update-after-await-as-a-transition)参照）。トランジションとしてマークされた state の更新は[ノンブロッキング](#perform-non-blocking-updates-with-actions)になり、[不要なローディングインジケータを表示しない](#preventing-unwanted-loading-indicators)ようになります。\n\n#### 返り値 {/*starttransition-returns*/}\n\n`startTransition` は何も返しません。\n\n#### 注意点 {/*starttransition-caveats*/}\n\n* `useTransition` はフックであるため、コンポーネント内かカスタムフック内でのみ呼び出すことができます。他の場所（例えば、データライブラリ）でトランジションを開始する必要がある場合は、代わりにスタンドアロンの [`startTransition`](/reference/react/startTransition) を呼び出してください。\n\n* state の `set` 関数にアクセスできる場合にのみ、state 更新をトランジションにラップできます。ある props やカスタムフックの値に反応してトランジションを開始したい場合は、代わりに [`useDeferredValue`](/reference/react/useDeferredValue) を試してみてください。\n\n* `startTransition` に渡された関数は即座に呼び出され、その関数の実行中に発生するすべての state 更新がトランジションとしてマークされます。しかし例えば、`setTimeout` 内で state を更新しようとした場合は、それはトランジションとしてマークされません。\n\n* 非同期リクエスト後に state 更新を行いたい場合は、トランジションとしてマークするために別の `startTransition` でラップする必要があります。これは既知の制限であり、将来的に修正される予定です（詳細は[トラブルシューティング](#react-doesnt-treat-my-state-update-after-await-as-a-transition)を参照してください）。\n\n* `startTransition` 関数は常に同一のものとなるため、多くの場合エフェクトの依存配列では省略されますが、依存配列に含めてもエフェクトの再実行は起こりません。依存値を削除してもリンタがエラーを出さない場合、削除しても安全です。[エフェクトから依存値を取り除く方法](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect)を参照してください。\n\n* トランジションとしてマークされた state 更新は、他の state 更新によって中断されます。例えば、トランジション内でチャートコンポーネントを更新した後、チャートの再レンダーの途中で入力フィールドに入力を始めた場合、React は入力欄の更新の処理後にチャートコンポーネントのレンダー作業を再開します。\n\n* トランジションによる更新はテキスト入力欄の制御には使用できません。\n\n* 進行中のトランジションが複数ある場合、React は現在それらをひとつに束ねる処理を行います。この制限は将来のリリースでは削除される可能性があります。\n\n## 使用法 {/*usage*/}\n\n### ノンブロッキングな更新をアクションを使って実行する {/*perform-non-blocking-updates-with-actions*/}\n\nコンポーネントのトップレベルで `useTransition` を呼び出してアクション (Action) を作成し、保留中 (pending) 状態にアクセスします。\n\n```js [[1, 4, \"isPending\"], [2, 4, \"startTransition\"]]\nimport {useState, useTransition} from 'react';\n\nfunction CheckoutForm() {\n  const [isPending, startTransition] = useTransition();\n  // ...\n}\n```\n\n`useTransition` は正確に 2 つの項目を含む配列を返します：\n\n1. トランジションが保留中であるかどうかを示す <CodeStep step={1}>`isPending` フラグ</CodeStep>。\n2. アクションを作成するための <CodeStep step={2}>`startTransition` 関数</CodeStep>。\n\nトランジションを開始するには、以下のようにして `startTransition` に関数を渡します。\n\n```js\nimport {useState, useTransition} from 'react';\nimport {updateQuantity} from './api';\n\nfunction CheckoutForm() {\n  const [isPending, startTransition] = useTransition();\n  const [quantity, setQuantity] = useState(1);\n\n  function onSubmit(newQuantity) {\n    startTransition(async function () {\n      const savedQuantity = await updateQuantity(newQuantity);\n      startTransition(() => {\n        setQuantity(savedQuantity);\n      });\n    });\n  }\n  // ...\n}\n```\n\n`startTransition` に渡される関数が \"アクション (Action)\" と呼ばれるものです。アクション内では state を更新したり、（必要に応じて）副作用を実行したりすることができます。その作業はバックグラウンドで、ページ上のユーザ操作をブロックすることなく行われます。ひとつのトランジションが複数のアクションを含むことができ、トランジションが進行中でも UI の応答性は保たれます。例えば、ユーザがタブをクリックしたあとに気が変わって別のタブをクリックした場合でも、最初の更新が終了するのを待つことなく、2 回目のクリックが即座に処理されます。\n\nトランジションの進行中状態に関するフィードバックをユーザに提供するために、`startTransition` が最初に呼び出されると `isPending` state が `true` に切り替わり、すべてのアクションが完了して最終的な状態がユーザに表示されるまで `true` のままになります。トランジションによりアクション内の副作用が順番に完了することが保証され、[不要なローディングインジケータが抑止](#preventing-unwanted-loading-indicators)されます。また、`useOptimistic` を使用することで、トランジションが進行中の間にも即時のフィードバックを提供することができます。\n\n<Recipes titleText=\"useTransition と通常の state 更新の違い\">\n\n#### アクションで数量を更新 {/*updating-the-quantity-in-an-action*/}\n\nこの例では、`updateQuantity` 関数がカート内の商品の数量を更新するリクエストをサーバに送信する部分をシミュレーションしています。この関数は、リクエストの完了に少なくとも 1 秒かかるように**意図的に遅延**させられています。\n\n数量欄を素早く複数回更新してみてください。リクエストが進行中の間、\"Total\" 欄には保留中状態が表示され、最後のリクエストが完了した後にのみ \"Total\" が更新されることに注意してください。更新がアクション内で行われるため、リクエストの進行中でも \"quantity\" 欄を更新し続けることが可能です。\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"beta\",\n    \"react-dom\": \"beta\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js src/App.js\nimport { useState, useTransition } from \"react\";\nimport { updateQuantity } from \"./api\";\nimport Item from \"./Item\";\nimport Total from \"./Total\";\n\nexport default function App({}) {\n  const [quantity, setQuantity] = useState(1);\n  const [isPending, startTransition] = useTransition();\n\n  const updateQuantityAction = async newQuantity => {\n    // To access the pending state of a transition,\n    // call startTransition again.\n    startTransition(async () => {\n      const savedQuantity = await updateQuantity(newQuantity);\n      startTransition(() => {\n        setQuantity(savedQuantity);\n      });\n    });\n  };\n\n  return (\n    <div>\n      <h1>Checkout</h1>\n      <Item action={updateQuantityAction}/>\n      <hr />\n      <Total quantity={quantity} isPending={isPending} />\n    </div>\n  );\n}\n```\n\n```js src/Item.js\nimport { startTransition } from \"react\";\n\nexport default function Item({action}) {\n  function handleChange(event) {\n    // To expose an action prop, await the callback in startTransition.\n    startTransition(async () => {\n      await action(event.target.value);\n    })\n  }\n  return (\n    <div className=\"item\">\n      <span>Eras Tour Tickets</span>\n      <label htmlFor=\"name\">Quantity: </label>\n      <input\n        type=\"number\"\n        onChange={handleChange}\n        defaultValue={1}\n        min={1}\n      />\n    </div>\n  )\n}\n```\n\n```js src/Total.js\nconst intl = new Intl.NumberFormat(\"en-US\", {\n  style: \"currency\",\n  currency: \"USD\"\n});\n\nexport default function Total({quantity, isPending}) {\n  return (\n    <div className=\"total\">\n      <span>Total:</span>\n      <span>\n        {isPending ? \"🌀 Updating...\" : `${intl.format(quantity * 9999)}`}\n      </span>\n    </div>\n  )\n}\n```\n\n```js src/api.js\nexport async function updateQuantity(newQuantity) {\n  return new Promise((resolve, reject) => {\n    // Simulate a slow network request.\n    setTimeout(() => {\n      resolve(newQuantity);\n    }, 2000);\n  });\n}\n```\n\n```css\n.item {\n  display: flex;\n  align-items: center;\n  justify-content: start;\n}\n\n.item label {\n  flex: 1;\n  text-align: right;\n}\n\n.item input {\n  margin-left: 4px;\n  width: 60px;\n  padding: 4px;\n}\n\n.total {\n  height: 50px;\n  line-height: 25px;\n  display: flex;\n  align-content: center;\n  justify-content: space-between;\n}\n```\n\n</Sandpack>\n\nこれはアクションの動作を示す基本的な例となっていますが、この例ではリクエストが順番通り完了しなかった場合の問題を処理していません。数量を複数回更新すると、後続のリクエストの後で以前のリクエストが完了するために、数量がおかしな順番で更新されてしまう可能性があります。これは既知の制限であり、将来的に修正される予定です（詳細は[トラブルシューティング](#my-state-updates-in-transitions-are-out-of-order)を参照してください）。\n\nReact は、一般的なユースケースに対応する以下のような組み込みの抽象化を提供しています。\n- [`useActionState`](/reference/react/useActionState)\n- [`<form>` アクション](/reference/react-dom/components/form)\n- [サーバ関数](/reference/rsc/server-functions)\n\nこれらのソリューションはリクエスト順序の問題を自動的に管理します。トランジションを使って非同期の state 遷移を管理するカスタムフックやライブラリを構築する場合、リクエスト順序をより高度に制御可能ですが、問題を手動で管理する必要があります。\n\n<Solution />\n\n#### アクションなしで数量を更新 {/*updating-the-users-name-without-an-action*/}\n\n今回も、`updateQuantity` 関数がカート内の商品の数量を更新するリクエストをサーバに送信する部分をシミュレーションしています。この関数は、リクエストの完了に少なくとも 1 秒かかるように**意図的に遅延**させられています。\n\n数量欄を素早く複数回更新してみてください。\"Total\" の保留中状態がリクエストの進行中に表示されますが、\"quantity\" のクリック回数に応じて \"Total\" が複数回更新されることに注意してください。\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"beta\",\n    \"react-dom\": \"beta\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js src/App.js\nimport { useState } from \"react\";\nimport { updateQuantity } from \"./api\";\nimport Item from \"./Item\";\nimport Total from \"./Total\";\n\nexport default function App({}) {\n  const [quantity, setQuantity] = useState(1);\n  const [isPending, setIsPending] = useState(false);\n\n  const onUpdateQuantity = async newQuantity => {\n    // Manually set the isPending State.\n    setIsPending(true);\n    const savedQuantity = await updateQuantity(newQuantity);\n    setIsPending(false);\n    setQuantity(savedQuantity);\n  };\n\n  return (\n    <div>\n      <h1>Checkout</h1>\n      <Item onUpdateQuantity={onUpdateQuantity}/>\n      <hr />\n      <Total quantity={quantity} isPending={isPending} />\n    </div>\n  );\n}\n\n```\n\n```js src/Item.js\nexport default function Item({onUpdateQuantity}) {\n  function handleChange(event) {\n    onUpdateQuantity(event.target.value);\n  }\n  return (\n    <div className=\"item\">\n      <span>Eras Tour Tickets</span>\n      <label htmlFor=\"name\">Quantity: </label>\n      <input\n        type=\"number\"\n        onChange={handleChange}\n        defaultValue={1}\n        min={1}\n      />\n    </div>\n  )\n}\n```\n\n```js src/Total.js\nconst intl = new Intl.NumberFormat(\"en-US\", {\n  style: \"currency\",\n  currency: \"USD\"\n});\n\nexport default function Total({quantity, isPending}) {\n  return (\n    <div className=\"total\">\n      <span>Total:</span>\n      <span>\n        {isPending ? \"🌀 Updating...\" : `${intl.format(quantity * 9999)}`}\n      </span>\n    </div>\n  )\n}\n```\n\n```js src/api.js\nexport async function updateQuantity(newQuantity) {\n  return new Promise((resolve, reject) => {\n    // Simulate a slow network request.\n    setTimeout(() => {\n      resolve(newQuantity);\n    }, 2000);\n  });\n}\n```\n\n```css\n.item {\n  display: flex;\n  align-items: center;\n  justify-content: start;\n}\n\n.item label {\n  flex: 1;\n  text-align: right;\n}\n\n.item input {\n  margin-left: 4px;\n  width: 60px;\n  padding: 4px;\n}\n\n.total {\n  height: 50px;\n  line-height: 25px;\n  display: flex;\n  align-content: center;\n  justify-content: space-between;\n}\n```\n\n</Sandpack>\n\nこのような問題に対するよくある解決法は、数量の更新中にユーザがさらなる変更を行えないようにしてしまうことです。\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"beta\",\n    \"react-dom\": \"beta\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js src/App.js\nimport { useState, useTransition } from \"react\";\nimport { updateQuantity } from \"./api\";\nimport Item from \"./Item\";\nimport Total from \"./Total\";\n\nexport default function App({}) {\n  const [quantity, setQuantity] = useState(1);\n  const [isPending, setIsPending] = useState(false);\n\n  const onUpdateQuantity = async event => {\n    const newQuantity = event.target.value;\n    // Manually set the isPending state.\n    setIsPending(true);\n    const savedQuantity = await updateQuantity(newQuantity);\n    setIsPending(false);\n    setQuantity(savedQuantity);\n  };\n\n  return (\n    <div>\n      <h1>Checkout</h1>\n      <Item isPending={isPending} onUpdateQuantity={onUpdateQuantity}/>\n      <hr />\n      <Total quantity={quantity} isPending={isPending} />\n    </div>\n  );\n}\n\n```\n\n```js src/Item.js\nexport default function Item({isPending, onUpdateQuantity}) {\n  return (\n    <div className=\"item\">\n      <span>Eras Tour Tickets</span>\n      <label htmlFor=\"name\">Quantity: </label>\n      <input\n        type=\"number\"\n        disabled={isPending}\n        onChange={onUpdateQuantity}\n        defaultValue={1}\n        min={1}\n      />\n    </div>\n  )\n}\n```\n\n```js src/Total.js\nconst intl = new Intl.NumberFormat(\"en-US\", {\n  style: \"currency\",\n  currency: \"USD\"\n});\n\nexport default function Total({quantity, isPending}) {\n  return (\n    <div className=\"total\">\n      <span>Total:</span>\n      <span>\n        {isPending ? \"🌀 Updating...\" : `${intl.format(quantity * 9999)}`}\n      </span>\n    </div>\n  )\n}\n```\n\n```js src/api.js\nexport async function updateQuantity(newQuantity) {\n  return new Promise((resolve, reject) => {\n    // Simulate a slow network request.\n    setTimeout(() => {\n      resolve(newQuantity);\n    }, 2000);\n  });\n}\n```\n\n```css\n.item {\n  display: flex;\n  align-items: center;\n  justify-content: start;\n}\n\n.item label {\n  flex: 1;\n  text-align: right;\n}\n\n.item input {\n  margin-left: 4px;\n  width: 60px;\n  padding: 4px;\n}\n\n.total {\n  height: 50px;\n  line-height: 25px;\n  display: flex;\n  align-content: center;\n  justify-content: space-between;\n}\n```\n\n</Sandpack>\n\nですがこのソリューションでは、数量を更新するたびにユーザが待つ必要があるため、アプリが遅く感じられます。数量の更新中もユーザが UI を操作できるよう、手動でより複雑な処理を追加することも可能ですが、アクションを使用すれば、シンプルな組み込み API でこのケースに対処できます。\n\n<Solution />\n\n</Recipes>\n\n---\n\n### コンポーネントから `action` を props として公開する {/*exposing-action-props-from-components*/}\n\nコンポーネントが props として `action` を公開することで、親がアクションを呼び出せるようにできます。\n\n例えばこの `TabButton` コンポーネントは `onClick` 時のロジックを `action` 内にラップしています。\n\n```js {8-12}\nexport default function TabButton({ action, children, isActive }) {\n  const [isPending, startTransition] = useTransition();\n  if (isActive) {\n    return <b>{children}</b>\n  }\n  return (\n    <button onClick={() => {\n      startTransition(async () => {\n        // await the action that's passed in.\n        // This allows it to be either sync or async.\n        await action();\n      });\n    }}>\n      {children}\n    </button>\n  );\n}\n```\n\nこれで親コンポーネントは state を `action` 内で更新するようになるため、この state 更新はトランジションとしてマークされます。つまり、\"Posts\" をクリックした直後に \"Contact\" をクリックしても、ユーザ操作がブロックされないようになるということです。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport TabButton from './TabButton.js';\nimport AboutTab from './AboutTab.js';\nimport PostsTab from './PostsTab.js';\nimport ContactTab from './ContactTab.js';\n\nexport default function TabContainer() {\n  const [tab, setTab] = useState('about');\n  return (\n    <>\n      <TabButton\n        isActive={tab === 'about'}\n        action={() => setTab('about')}\n      >\n        About\n      </TabButton>\n      <TabButton\n        isActive={tab === 'posts'}\n        action={() => setTab('posts')}\n      >\n        Posts (slow)\n      </TabButton>\n      <TabButton\n        isActive={tab === 'contact'}\n        action={() => setTab('contact')}\n      >\n        Contact\n      </TabButton>\n      <hr />\n      {tab === 'about' && <AboutTab />}\n      {tab === 'posts' && <PostsTab />}\n      {tab === 'contact' && <ContactTab />}\n    </>\n  );\n}\n```\n\n```js src/TabButton.js active\nimport { useTransition } from 'react';\n\nexport default function TabButton({ action, children, isActive }) {\n  const [isPending, startTransition] = useTransition();\n  if (isActive) {\n    return <b>{children}</b>\n  }\n  if (isPending) {\n    return <b className=\"pending\">{children}</b>;\n  }\n  return (\n    <button onClick={async () => {\n      startTransition(async () => {\n        // await the action that's passed in.\n        // This allows it to be either sync or async.\n        await action();\n      });\n    }}>\n      {children}\n    </button>\n  );\n}\n```\n\n```js src/AboutTab.js\nexport default function AboutTab() {\n  return (\n    <p>Welcome to my profile!</p>\n  );\n}\n```\n\n```js {expectedErrors: {'react-compiler': [19, 20]}} src/PostsTab.js\nimport { memo } from 'react';\n\nconst PostsTab = memo(function PostsTab() {\n  // Log once. The actual slowdown is inside SlowPost.\n  console.log('[ARTIFICIALLY SLOW] Rendering 500 <SlowPost />');\n\n  let items = [];\n  for (let i = 0; i < 500; i++) {\n    items.push(<SlowPost key={i} index={i} />);\n  }\n  return (\n    <ul className=\"items\">\n      {items}\n    </ul>\n  );\n});\n\nfunction SlowPost({ index }) {\n  let startTime = performance.now();\n  while (performance.now() - startTime < 1) {\n    // Do nothing for 1 ms per item to emulate extremely slow code\n  }\n\n  return (\n    <li className=\"item\">\n      Post #{index + 1}\n    </li>\n  );\n}\n\nexport default PostsTab;\n```\n\n```js src/ContactTab.js\nexport default function ContactTab() {\n  return (\n    <>\n      <p>\n        You can find me online here:\n      </p>\n      <ul>\n        <li>admin@mysite.com</li>\n        <li>+123456789</li>\n      </ul>\n    </>\n  );\n}\n```\n\n```css\nbutton { margin-right: 10px }\nb { display: inline-block; margin-right: 10px; }\n.pending { color: #777; }\n```\n\n</Sandpack>\n\n<Note>\n\nコンポーネント内から props として `action` を公開する場合は、トランジション内で `await` するべきです。\n\nこれにより `action` コールバックが同期的な場合でも非同期の場合でも、アクションを `await` でラップするために余分に `startTransition` を用いる必要がなくなります。\n\n</Note>\n\n---\n\n### 保留中状態を視覚的に表示する {/*displaying-a-pending-visual-state*/}\n\n`useTransition` によって返される `isPending` ブーリアン値を使用して、ユーザにトランジションが進行中であることを示すことができます。例えば、タブボタンは特別な \"pending\" という視覚状態を持つことができます。\n\n```js {4-6}\nfunction TabButton({ action, children, isActive }) {\n  const [isPending, startTransition] = useTransition();\n  // ...\n  if (isPending) {\n    return <b className=\"pending\">{children}</b>;\n  }\n  // ...\n```\n\n\"Posts\" をクリックすると、タブボタン自体がすぐに更新されるため、より反応が良く感じられることに着目してください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport TabButton from './TabButton.js';\nimport AboutTab from './AboutTab.js';\nimport PostsTab from './PostsTab.js';\nimport ContactTab from './ContactTab.js';\n\nexport default function TabContainer() {\n  const [tab, setTab] = useState('about');\n  return (\n    <>\n      <TabButton\n        isActive={tab === 'about'}\n        action={() => setTab('about')}\n      >\n        About\n      </TabButton>\n      <TabButton\n        isActive={tab === 'posts'}\n        action={() => setTab('posts')}\n      >\n        Posts (slow)\n      </TabButton>\n      <TabButton\n        isActive={tab === 'contact'}\n        action={() => setTab('contact')}\n      >\n        Contact\n      </TabButton>\n      <hr />\n      {tab === 'about' && <AboutTab />}\n      {tab === 'posts' && <PostsTab />}\n      {tab === 'contact' && <ContactTab />}\n    </>\n  );\n}\n```\n\n```js src/TabButton.js active\nimport { useTransition } from 'react';\n\nexport default function TabButton({ action, children, isActive }) {\n  const [isPending, startTransition] = useTransition();\n  if (isActive) {\n    return <b>{children}</b>\n  }\n  if (isPending) {\n    return <b className=\"pending\">{children}</b>;\n  }\n  return (\n    <button onClick={() => {\n      startTransition(async () => {\n        await action();\n      });\n    }}>\n      {children}\n    </button>\n  );\n}\n```\n\n```js src/AboutTab.js\nexport default function AboutTab() {\n  return (\n    <p>Welcome to my profile!</p>\n  );\n}\n```\n\n```js {expectedErrors: {'react-compiler': [19, 20]}} src/PostsTab.js\nimport { memo } from 'react';\n\nconst PostsTab = memo(function PostsTab() {\n  // Log once. The actual slowdown is inside SlowPost.\n  console.log('[ARTIFICIALLY SLOW] Rendering 500 <SlowPost />');\n\n  let items = [];\n  for (let i = 0; i < 500; i++) {\n    items.push(<SlowPost key={i} index={i} />);\n  }\n  return (\n    <ul className=\"items\">\n      {items}\n    </ul>\n  );\n});\n\nfunction SlowPost({ index }) {\n  let startTime = performance.now();\n  while (performance.now() - startTime < 1) {\n    // Do nothing for 1 ms per item to emulate extremely slow code\n  }\n\n  return (\n    <li className=\"item\">\n      Post #{index + 1}\n    </li>\n  );\n}\n\nexport default PostsTab;\n```\n\n```js src/ContactTab.js\nexport default function ContactTab() {\n  return (\n    <>\n      <p>\n        You can find me online here:\n      </p>\n      <ul>\n        <li>admin@mysite.com</li>\n        <li>+123456789</li>\n      </ul>\n    </>\n  );\n}\n```\n\n```css\nbutton { margin-right: 10px }\nb { display: inline-block; margin-right: 10px; }\n.pending { color: #777; }\n```\n\n</Sandpack>\n\n---\n\n### 望ましくないローディングインジケータの防止 {/*preventing-unwanted-loading-indicators*/}\n\nこの例では、`PostsTab` コンポーネントは [use](/reference/react/use) を使用していくつかのデータをフェッチしています。\"Posts\" タブをクリックすると、`PostsTab` コンポーネントが*サスペンド*し、その結果、最も近いローディングフォールバックが表示されます：\n\n<Sandpack>\n\n```js\nimport { Suspense, useState } from 'react';\nimport TabButton from './TabButton.js';\nimport AboutTab from './AboutTab.js';\nimport PostsTab from './PostsTab.js';\nimport ContactTab from './ContactTab.js';\n\nexport default function TabContainer() {\n  const [tab, setTab] = useState('about');\n  return (\n    <Suspense fallback={<h1>🌀 Loading...</h1>}>\n      <TabButton\n        isActive={tab === 'about'}\n        action={() => setTab('about')}\n      >\n        About\n      </TabButton>\n      <TabButton\n        isActive={tab === 'posts'}\n        action={() => setTab('posts')}\n      >\n        Posts\n      </TabButton>\n      <TabButton\n        isActive={tab === 'contact'}\n        action={() => setTab('contact')}\n      >\n        Contact\n      </TabButton>\n      <hr />\n      {tab === 'about' && <AboutTab />}\n      {tab === 'posts' && <PostsTab />}\n      {tab === 'contact' && <ContactTab />}\n    </Suspense>\n  );\n}\n```\n\n```js src/TabButton.js\nexport default function TabButton({ action, children, isActive }) {\n  if (isActive) {\n    return <b>{children}</b>\n  }\n  return (\n    <button onClick={() => {\n      action();\n    }}>\n      {children}\n    </button>\n  );\n}\n```\n\n```js src/AboutTab.js hidden\nexport default function AboutTab() {\n  return (\n    <p>Welcome to my profile!</p>\n  );\n}\n```\n\n```js src/PostsTab.js hidden\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nfunction PostsTab() {\n  const posts = use(fetchData('/posts'));\n  return (\n    <ul className=\"items\">\n      {posts.map(post =>\n        <Post key={post.id} title={post.title} />\n      )}\n    </ul>\n  );\n}\n\nfunction Post({ title }) {\n  return (\n    <li className=\"item\">\n      {title}\n    </li>\n  );\n}\n\nexport default PostsTab;\n```\n\n```js src/ContactTab.js hidden\nexport default function ContactTab() {\n  return (\n    <>\n      <p>\n        You can find me online here:\n      </p>\n      <ul>\n        <li>admin@mysite.com</li>\n        <li>+123456789</li>\n      </ul>\n    </>\n  );\n}\n```\n\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url.startsWith('/posts')) {\n    return await getPosts();\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getPosts() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 1000);\n  });\n  let posts = [];\n  for (let i = 0; i < 500; i++) {\n    posts.push({\n      id: i,\n      title: 'Post #' + (i + 1)\n    });\n  }\n  return posts;\n}\n```\n\n```css\nbutton { margin-right: 10px }\nb { display: inline-block; margin-right: 10px; }\n.pending { color: #777; }\n```\n\n</Sandpack>\n\nローディングインジケータを表示するためにタブのコンテナ全体が隠れることは不快なユーザ体験となってしまいます。`TabButton` に `useTransition` を追加すると、代わりにタブボタン内に保留状態を表示することができます。\n\n\"Posts\" をクリックしても、もはやタブコンテナ全体がスピナに置き換わることはなくなったことに注目してください。\n\n<Sandpack>\n\n```js\nimport { Suspense, useState } from 'react';\nimport TabButton from './TabButton.js';\nimport AboutTab from './AboutTab.js';\nimport PostsTab from './PostsTab.js';\nimport ContactTab from './ContactTab.js';\n\nexport default function TabContainer() {\n  const [tab, setTab] = useState('about');\n  return (\n    <Suspense fallback={<h1>🌀 Loading...</h1>}>\n      <TabButton\n        isActive={tab === 'about'}\n        action={() => setTab('about')}\n      >\n        About\n      </TabButton>\n      <TabButton\n        isActive={tab === 'posts'}\n        action={() => setTab('posts')}\n      >\n        Posts\n      </TabButton>\n      <TabButton\n        isActive={tab === 'contact'}\n        action={() => setTab('contact')}\n      >\n        Contact\n      </TabButton>\n      <hr />\n      {tab === 'about' && <AboutTab />}\n      {tab === 'posts' && <PostsTab />}\n      {tab === 'contact' && <ContactTab />}\n    </Suspense>\n  );\n}\n```\n\n```js src/TabButton.js active\nimport { useTransition } from 'react';\n\nexport default function TabButton({ action, children, isActive }) {\n  const [isPending, startTransition] = useTransition();\n  if (isActive) {\n    return <b>{children}</b>\n  }\n  if (isPending) {\n    return <b className=\"pending\">{children}</b>;\n  }\n  return (\n    <button onClick={() => {\n      startTransition(async () => {\n        await action();\n      });\n    }}>\n      {children}\n    </button>\n  );\n}\n```\n\n```js src/AboutTab.js hidden\nexport default function AboutTab() {\n  return (\n    <p>Welcome to my profile!</p>\n  );\n}\n```\n\n```js src/PostsTab.js hidden\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nfunction PostsTab() {\n  const posts = use(fetchData('/posts'));\n  return (\n    <ul className=\"items\">\n      {posts.map(post =>\n        <Post key={post.id} title={post.title} />\n      )}\n    </ul>\n  );\n}\n\nfunction Post({ title }) {\n  return (\n    <li className=\"item\">\n      {title}\n    </li>\n  );\n}\n\nexport default PostsTab;\n```\n\n```js src/ContactTab.js hidden\nexport default function ContactTab() {\n  return (\n    <>\n      <p>\n        You can find me online here:\n      </p>\n      <ul>\n        <li>admin@mysite.com</li>\n        <li>+123456789</li>\n      </ul>\n    </>\n  );\n}\n```\n\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url.startsWith('/posts')) {\n    return await getPosts();\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getPosts() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 1000);\n  });\n  let posts = [];\n  for (let i = 0; i < 500; i++) {\n    posts.push({\n      id: i,\n      title: 'Post #' + (i + 1)\n    });\n  }\n  return posts;\n}\n```\n\n```css\nbutton { margin-right: 10px }\nb { display: inline-block; margin-right: 10px; }\n.pending { color: #777; }\n```\n\n</Sandpack>\n\n[サスペンスとトランジションの詳細はこちらをご覧ください](/reference/react/Suspense#preventing-already-revealed-content-from-hiding)。\n\n<Note>\n\nトランジションは（今回のタブコンテナのような）*すでに表示されている*コンテンツを隠さない範囲で「待機」を行います。もし Posts タブに[ネストした `<Suspense>` バウンダリ](/reference/react/Suspense#revealing-nested-content-as-it-loads)がある場合、トランジションはそれを「待機」することはありません。\n\n</Note>\n\n---\n\n### サスペンス対応ルータの構築 {/*building-a-suspense-enabled-router*/}\n\nReact のフレームワークやルータを構築している場合、ページのナビゲーションをトランジションとしてマークすることをお勧めします。\n\n```js {3,6,8}\nfunction Router() {\n  const [page, setPage] = useState('/');\n  const [isPending, startTransition] = useTransition();\n\n  function navigate(url) {\n    startTransition(() => {\n      setPage(url);\n    });\n  }\n  // ...\n```\n\nこれが推奨されるのは以下の 3 つの理由からです：\n\n- [トランジションは中断可能](#perform-non-blocking-updates-with-actions)であるため、ユーザは再レンダーの完了を待たずにクリックしてページから離れることができます。\n- [トランジションは不要なローディングインジケータを防ぐ](#preventing-unwanted-loading-indicators)ため、ユーザがナビゲーション時の不快なちらつきを避けることができます。\n- [トランジションはすべての保留中のアクションを待機します](#perform-non-blocking-updates-with-actions)。これにより、新しいページが表示される前に副作用の完了をユーザが待つことができます。\n\n以下は、ナビゲーションにトランジションを使用した簡易的なルータの例です。\n\n<Sandpack>\n\n```js src/App.js\nimport { Suspense, useState, useTransition } from 'react';\nimport IndexPage from './IndexPage.js';\nimport ArtistPage from './ArtistPage.js';\nimport Layout from './Layout.js';\n\nexport default function App() {\n  return (\n    <Suspense fallback={<BigSpinner />}>\n      <Router />\n    </Suspense>\n  );\n}\n\nfunction Router() {\n  const [page, setPage] = useState('/');\n  const [isPending, startTransition] = useTransition();\n\n  function navigate(url) {\n    startTransition(() => {\n      setPage(url);\n    });\n  }\n\n  let content;\n  if (page === '/') {\n    content = (\n      <IndexPage navigate={navigate} />\n    );\n  } else if (page === '/the-beatles') {\n    content = (\n      <ArtistPage\n        artist={{\n          id: 'the-beatles',\n          name: 'The Beatles',\n        }}\n      />\n    );\n  }\n  return (\n    <Layout isPending={isPending}>\n      {content}\n    </Layout>\n  );\n}\n\nfunction BigSpinner() {\n  return <h2>🌀 Loading...</h2>;\n}\n```\n\n```js src/Layout.js\nexport default function Layout({ children, isPending }) {\n  return (\n    <div className=\"layout\">\n      <section className=\"header\" style={{\n        opacity: isPending ? 0.7 : 1\n      }}>\n        Music Browser\n      </section>\n      <main>\n        {children}\n      </main>\n    </div>\n  );\n}\n```\n\n```js src/IndexPage.js\nexport default function IndexPage({ navigate }) {\n  return (\n    <button onClick={() => navigate('/the-beatles')}>\n      Open The Beatles artist page\n    </button>\n  );\n}\n```\n\n```js src/ArtistPage.js\nimport { Suspense } from 'react';\nimport Albums from './Albums.js';\nimport Biography from './Biography.js';\nimport Panel from './Panel.js';\n\nexport default function ArtistPage({ artist }) {\n  return (\n    <>\n      <h1>{artist.name}</h1>\n      <Biography artistId={artist.id} />\n      <Suspense fallback={<AlbumsGlimmer />}>\n        <Panel>\n          <Albums artistId={artist.id} />\n        </Panel>\n      </Suspense>\n    </>\n  );\n}\n\nfunction AlbumsGlimmer() {\n  return (\n    <div className=\"glimmer-panel\">\n      <div className=\"glimmer-line\" />\n      <div className=\"glimmer-line\" />\n      <div className=\"glimmer-line\" />\n    </div>\n  );\n}\n```\n\n```js src/Albums.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Albums({ artistId }) {\n  const albums = use(fetchData(`/${artistId}/albums`));\n  return (\n    <ul>\n      {albums.map(album => (\n        <li key={album.id}>\n          {album.title} ({album.year})\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n```js src/Biography.js\nimport {use} from 'react';\nimport { fetchData } from './data.js';\n\nexport default function Biography({ artistId }) {\n  const bio = use(fetchData(`/${artistId}/bio`));\n  return (\n    <section>\n      <p className=\"bio\">{bio}</p>\n    </section>\n  );\n}\n```\n\n```js src/Panel.js\nexport default function Panel({ children }) {\n  return (\n    <section className=\"panel\">\n      {children}\n    </section>\n  );\n}\n```\n\n```js src/data.js hidden\n// Note: the way you would do data fetching depends on\n// the framework that you use together with Suspense.\n// Normally, the caching logic would be inside a framework.\n\nlet cache = new Map();\n\nexport function fetchData(url) {\n  if (!cache.has(url)) {\n    cache.set(url, getData(url));\n  }\n  return cache.get(url);\n}\n\nasync function getData(url) {\n  if (url === '/the-beatles/albums') {\n    return await getAlbums();\n  } else if (url === '/the-beatles/bio') {\n    return await getBio();\n  } else {\n    throw Error('Not implemented');\n  }\n}\n\nasync function getBio() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 500);\n  });\n\n  return `The Beatles were an English rock band,\n    formed in Liverpool in 1960, that comprised\n    John Lennon, Paul McCartney, George Harrison\n    and Ringo Starr.`;\n}\n\nasync function getAlbums() {\n  // Add a fake delay to make waiting noticeable.\n  await new Promise(resolve => {\n    setTimeout(resolve, 3000);\n  });\n\n  return [{\n    id: 13,\n    title: 'Let It Be',\n    year: 1970\n  }, {\n    id: 12,\n    title: 'Abbey Road',\n    year: 1969\n  }, {\n    id: 11,\n    title: 'Yellow Submarine',\n    year: 1969\n  }, {\n    id: 10,\n    title: 'The Beatles',\n    year: 1968\n  }, {\n    id: 9,\n    title: 'Magical Mystery Tour',\n    year: 1967\n  }, {\n    id: 8,\n    title: 'Sgt. Pepper\\'s Lonely Hearts Club Band',\n    year: 1967\n  }, {\n    id: 7,\n    title: 'Revolver',\n    year: 1966\n  }, {\n    id: 6,\n    title: 'Rubber Soul',\n    year: 1965\n  }, {\n    id: 5,\n    title: 'Help!',\n    year: 1965\n  }, {\n    id: 4,\n    title: 'Beatles For Sale',\n    year: 1964\n  }, {\n    id: 3,\n    title: 'A Hard Day\\'s Night',\n    year: 1964\n  }, {\n    id: 2,\n    title: 'With The Beatles',\n    year: 1963\n  }, {\n    id: 1,\n    title: 'Please Please Me',\n    year: 1963\n  }];\n}\n```\n\n```css\nmain {\n  min-height: 200px;\n  padding: 10px;\n}\n\n.layout {\n  border: 1px solid black;\n}\n\n.header {\n  background: #222;\n  padding: 10px;\n  text-align: center;\n  color: white;\n}\n\n.bio { font-style: italic; }\n\n.panel {\n  border: 1px solid #aaa;\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n}\n\n.glimmer-panel {\n  border: 1px dashed #aaa;\n  background: linear-gradient(90deg, rgba(221,221,221,1) 0%, rgba(255,255,255,1) 100%);\n  border-radius: 6px;\n  margin-top: 20px;\n  padding: 10px;\n}\n\n.glimmer-line {\n  display: block;\n  width: 60%;\n  height: 20px;\n  margin: 10px;\n  border-radius: 4px;\n  background: #f0f0f0;\n}\n```\n\n</Sandpack>\n\n<Note>\n\n[サスペンス対応](/reference/react/Suspense)のルータは、デフォルトでナビゲーションの更新をトランジションにラップすることが期待されます。\n\n</Note>\n\n---\n\n### エラーバウンダリでユーザにエラーを表示する {/*displaying-an-error-to-users-with-error-boundary*/}\n\n`startTransition` に渡された関数がエラーをスローした場合、[エラーバウンダリ](/reference/react/Component#catching-rendering-errors-with-an-error-boundary)を使用してユーザにエラーを表示することができます。エラーバウンダリを使用するには、`useTransition` を呼び出しているコンポーネントをエラーバウンダリで囲みます。`startTransition` に渡された関数がエラーになった場合、エラーバウンダリに指定されているフォールバックが表示されます。\n\n<Sandpack>\n\n```js src/AddCommentContainer.js active\nimport { useTransition } from \"react\";\nimport { ErrorBoundary } from \"react-error-boundary\";\n\nexport function AddCommentContainer() {\n  return (\n    <ErrorBoundary fallback={<p>⚠️Something went wrong</p>}>\n      <AddCommentButton />\n    </ErrorBoundary>\n  );\n}\n\nfunction addComment(comment) {\n  // For demonstration purposes to show Error Boundary\n  if (comment == null) {\n    throw new Error(\"Example Error: An error thrown to trigger error boundary\");\n  }\n}\n\nfunction AddCommentButton() {\n  const [pending, startTransition] = useTransition();\n\n  return (\n    <button\n      disabled={pending}\n      onClick={() => {\n        startTransition(() => {\n          // Intentionally not passing a comment\n          // so error gets thrown\n          addComment();\n        });\n      }}\n    >\n      Add comment\n    </button>\n  );\n}\n```\n\n```js src/App.js hidden\nimport { AddCommentContainer } from \"./AddCommentContainer.js\";\n\nexport default function App() {\n  return <AddCommentContainer />;\n}\n```\n\n```js src/index.js hidden\nimport React, { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\nimport App from './App';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"19.0.0-rc-3edc000d-20240926\",\n    \"react-dom\": \"19.0.0-rc-3edc000d-20240926\",\n    \"react-scripts\": \"^5.0.0\",\n    \"react-error-boundary\": \"4.0.3\"\n  },\n  \"main\": \"/index.js\"\n}\n```\n</Sandpack>\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### トランジション中に入力フィールドを更新できない {/*updating-an-input-in-a-transition-doesnt-work*/}\n\n入力フィールドを制御する state 変数に対してトランジションを使用することはできません。\n\n```js {4,10}\nconst [text, setText] = useState('');\n// ...\nfunction handleChange(e) {\n  // ❌ Can't use Transitions for controlled input state\n  startTransition(() => {\n    setText(e.target.value);\n  });\n}\n// ...\nreturn <input value={text} onChange={handleChange} />;\n```\n\nこれは、トランジションが非ブロッキングである一方、change イベントへの応答として入力を更新する処理は同期的である必要があるためです。タイピングに応じてトランジションを実行したい場合、2 つの選択肢があります：\n\n1. 入力フィールド用の state（常に同期的に更新される）と、トランジションで更新する state を別々に宣言する。これにより、同期的な state を使用して入力フィールドを制御しつつ、トランジション state 変数（入力欄より「遅れる」ことになる）をレンダーロジックの残りの部分に渡すことができます。\n2. あるいは、保持する state 変数は 1 つにし、実際の値より「遅れる」ことのできる [`useDeferredValue`](/reference/react/useDeferredValue) を追加することができます。これにより、ノンブロッキングな再レンダーを始めて、それが自動的に新しい値に「追いつく」ようにできます。\n\n---\n\n### React が state 更新をトランジションとして扱わない {/*react-doesnt-treat-my-state-update-as-a-transition*/}\n\nstate 更新をトランジションでラップするとき、更新が `startTransition` の呼び出しの*最中*に行われていることを確認してください：\n\n```js\nstartTransition(() => {\n  // ✅ Setting state *during* startTransition call\n  setPage('/about');\n});\n```\n\n`startTransition` に渡す関数は同期的でなければなりません。以下のような形で更新をトランジションとしてマークすることはできません。\n\n```js\nstartTransition(() => {\n  // ❌ Setting state *after* startTransition call\n  setTimeout(() => {\n    setPage('/about');\n  }, 1000);\n});\n```\n\n代わりに、以下は可能です。\n\n```js\nsetTimeout(() => {\n  startTransition(() => {\n    // ✅ Setting state *during* startTransition call\n    setPage('/about');\n  });\n}, 1000);\n```\n\n---\n\n### `await` 後の state 更新がトランジションにならない {/*react-doesnt-treat-my-state-update-after-await-as-a-transition*/}\n\n`startTransition` 関数内で `await` を使用した場合、`await` の後に行われる state 更新はトランジションとしてマークされません。各 `await` 後の state 更新をそれぞれ `startTransition` 呼び出しでラップする必要があります。\n\n```js\nstartTransition(async () => {\n  await someAsyncFunction();\n  // ❌ Not using startTransition after await\n  setPage('/about');\n});\n```\n\n一方で、以下は動作します。\n\n```js\nstartTransition(async () => {\n  await someAsyncFunction();\n  // ✅ Using startTransition *after* await\n  startTransition(() => {\n    setPage('/about');\n  });\n});\n```\n\nこれは JavaScript の制限により React が非同期コンテクストのスコープを失うために発生する問題です。将来的に [AsyncContext](https://github.com/tc39/proposal-async-context) が利用可能になれば、この制限は解消される予定です。\n\n---\n\n### コンポーネントの外部から `useTransition` を呼び出したい {/*i-want-to-call-usetransition-from-outside-a-component*/}\n\n`useTransition` はフックであるため、コンポーネント外で呼び出すことはできません。この場合、代わりにスタンドアロンの [`startTransition`](/reference/react/startTransition) メソッドを使用してください。同じように機能しますが、`isPending` インジケータは提供されません。\n\n---\n\n### `startTransition` に渡す関数がすぐに実行される {/*the-function-i-pass-to-starttransition-executes-immediately*/}\n\nこのコードを実行すると、1、2、3 が出力されます：\n\n```js {1,3,6}\nconsole.log(1);\nstartTransition(() => {\n  console.log(2);\n  setPage('/about');\n});\nconsole.log(3);\n```\n\n**1、2、3 が出力されるのは期待通りの動作です**。`startTransition` に渡す関数は遅延されません。ブラウザの `setTimeout` を使う場合とは異なり、コールバックは後で実行されるのではありません。React はあなたの関数をすぐに実行しますが、*それが実行されている間*にスケジュールされた state 更新をトランジションとしてマークします。以下のように動作していると考えることができます。\n\n```js\n// A simplified version of how React works\n\nlet isInsideTransition = false;\n\nfunction startTransition(scope) {\n  isInsideTransition = true;\n  scope();\n  isInsideTransition = false;\n}\n\nfunction setState() {\n  if (isInsideTransition) {\n    // ... schedule a Transition state update ...\n  } else {\n    // ... schedule an urgent state update ...\n  }\n}\n```\n\n### トランジション内で state 更新の順番がおかしくなる {/*my-state-updates-in-transitions-are-out-of-order*/}\n\n`startTransition` 内で `await` を使用すると、更新が順不同で発生する可能性があります。\n\n以下の例では、`updateQuantity` 関数がカート内の商品の数量を更新するリクエストをサーバに送信する部分をシミュレーションしています。この関数は、ネットワークリクエストの競合状態をシミュレートするため、*初回リクエストの結果が常に後続リクエストの結果より後に返ってくる*ようになっています。\n\n数量を一度だけ更新した場合と、素早く複数回更新した場合を試してみてください。誤った合計が表示される場合があることに気付くでしょう。\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"beta\",\n    \"react-dom\": \"beta\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js src/App.js\nimport { useState, useTransition } from \"react\";\nimport { updateQuantity } from \"./api\";\nimport Item from \"./Item\";\nimport Total from \"./Total\";\n\nexport default function App({}) {\n  const [quantity, setQuantity] = useState(1);\n  const [isPending, startTransition] = useTransition();\n  // Store the actual quantity in separate state to show the mismatch.\n  const [clientQuantity, setClientQuantity] = useState(1);\n\n  const updateQuantityAction = newQuantity => {\n    setClientQuantity(newQuantity);\n\n    // Access the pending state of the transition,\n    // by wrapping in startTransition again.\n    startTransition(async () => {\n      const savedQuantity = await updateQuantity(newQuantity);\n      startTransition(() => {\n        setQuantity(savedQuantity);\n      });\n    });\n  };\n\n  return (\n    <div>\n      <h1>Checkout</h1>\n      <Item action={updateQuantityAction}/>\n      <hr />\n      <Total clientQuantity={clientQuantity} savedQuantity={quantity} isPending={isPending} />\n    </div>\n  );\n}\n\n```\n\n```js src/Item.js\nimport {startTransition} from 'react';\n\nexport default function Item({action}) {\n  function handleChange(e) {\n    // Update the quantity in an Action.\n    startTransition(async () => {\n      await action(e.target.value);\n    });\n  }\n  return (\n    <div className=\"item\">\n      <span>Eras Tour Tickets</span>\n      <label htmlFor=\"name\">Quantity: </label>\n      <input\n        type=\"number\"\n        onChange={handleChange}\n        defaultValue={1}\n        min={1}\n      />\n    </div>\n  )\n}\n```\n\n```js src/Total.js\nconst intl = new Intl.NumberFormat(\"en-US\", {\n  style: \"currency\",\n  currency: \"USD\"\n});\n\nexport default function Total({ clientQuantity, savedQuantity, isPending }) {\n  return (\n    <div className=\"total\">\n      <span>Total:</span>\n      <div>\n        <div>\n          {isPending\n            ? \"🌀 Updating...\"\n            : `${intl.format(savedQuantity * 9999)}`}\n        </div>\n        <div className=\"error\">\n          {!isPending &&\n            clientQuantity !== savedQuantity &&\n            `Wrong total, expected: ${intl.format(clientQuantity * 9999)}`}\n        </div>\n      </div>\n    </div>\n  );\n}\n```\n\n```js src/api.js\nlet firstRequest = true;\nexport async function updateQuantity(newName) {\n  return new Promise((resolve, reject) => {\n    if (firstRequest === true) {\n      firstRequest = false;\n      setTimeout(() => {\n        firstRequest = true;\n        resolve(newName);\n        // Simulate every other request being slower\n      }, 1000);\n    } else {\n      setTimeout(() => {\n        resolve(newName);\n      }, 50);\n    }\n  });\n}\n```\n\n```css\n.item {\n  display: flex;\n  align-items: center;\n  justify-content: start;\n}\n\n.item label {\n  flex: 1;\n  text-align: right;\n}\n\n.item input {\n  margin-left: 4px;\n  width: 60px;\n  padding: 4px;\n}\n\n.total {\n  height: 50px;\n  line-height: 25px;\n  display: flex;\n  align-content: center;\n  justify-content: space-between;\n}\n\n.total div {\n  display: flex;\n  flex-direction: column;\n  align-items: flex-end;\n}\n\n.error {\n  color: red;\n}\n```\n\n</Sandpack>\n\n\n複数回のクリックがあると、後続のリクエストが完了した後で古いリクエストが完了する場合があります。この場合、現在の React は意図した順序を認識できません。これは、更新が非同期的にスケジュールされ、非同期の境界を越えると React が順序の情報を保持できないからです。\n\nトランジション内のアクションは実行順序を保証しないため、これは想定された動作です。一般的なユースケースのために、React は [`useActionState`](/reference/react/useActionState) や [`<form>` アクション](/reference/react-dom/components/form) のような高レベルの抽象化を提供しており、順序の管理を自動化します。高度なユースケースでは、独自のキューイングや中断ロジックを実装して、実行順序を管理する必要があります。\n\nExample of `useActionState` handling execution order:\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"beta\",\n    \"react-dom\": \"beta\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js src/App.js\nimport { useState, useActionState } from \"react\";\nimport { updateQuantity } from \"./api\";\nimport Item from \"./Item\";\nimport Total from \"./Total\";\n\nexport default function App({}) {\n  // Store the actual quantity in separate state to show the mismatch.\n  const [clientQuantity, setClientQuantity] = useState(1);\n  const [quantity, updateQuantityAction, isPending] = useActionState(\n    async (prevState, payload) => {\n      setClientQuantity(payload);\n      const savedQuantity = await updateQuantity(payload);\n      return savedQuantity; // Return the new quantity to update the state\n    },\n    1 // Initial quantity\n  );\n\n  return (\n    <div>\n      <h1>Checkout</h1>\n      <Item action={updateQuantityAction}/>\n      <hr />\n      <Total clientQuantity={clientQuantity} savedQuantity={quantity} isPending={isPending} />\n    </div>\n  );\n}\n\n```\n\n```js src/Item.js\nimport {startTransition} from 'react';\n\nexport default function Item({action}) {\n  function handleChange(e) {\n    // Update the quantity in an Action.\n    startTransition(() => {\n      action(e.target.value);\n    });\n  }\n  return (\n    <div className=\"item\">\n      <span>Eras Tour Tickets</span>\n      <label htmlFor=\"name\">Quantity: </label>\n      <input\n        type=\"number\"\n        onChange={handleChange}\n        defaultValue={1}\n        min={1}\n      />\n    </div>\n  )\n}\n```\n\n```js src/Total.js\nconst intl = new Intl.NumberFormat(\"en-US\", {\n  style: \"currency\",\n  currency: \"USD\"\n});\n\nexport default function Total({ clientQuantity, savedQuantity, isPending }) {\n  return (\n    <div className=\"total\">\n      <span>Total:</span>\n      <div>\n        <div>\n          {isPending\n            ? \"🌀 Updating...\"\n            : `${intl.format(savedQuantity * 9999)}`}\n        </div>\n        <div className=\"error\">\n          {!isPending &&\n            clientQuantity !== savedQuantity &&\n            `Wrong total, expected: ${intl.format(clientQuantity * 9999)}`}\n        </div>\n      </div>\n    </div>\n  );\n}\n```\n\n```js src/api.js\nlet firstRequest = true;\nexport async function updateQuantity(newName) {\n  return new Promise((resolve, reject) => {\n    if (firstRequest === true) {\n      firstRequest = false;\n      setTimeout(() => {\n        firstRequest = true;\n        resolve(newName);\n        // Simulate every other request being slower\n      }, 1000);\n    } else {\n      setTimeout(() => {\n        resolve(newName);\n      }, 50);\n    }\n  });\n}\n```\n\n```css\n.item {\n  display: flex;\n  align-items: center;\n  justify-content: start;\n}\n\n.item label {\n  flex: 1;\n  text-align: right;\n}\n\n.item input {\n  margin-left: 4px;\n  width: 60px;\n  padding: 4px;\n}\n\n.total {\n  height: 50px;\n  line-height: 25px;\n  display: flex;\n  align-content: center;\n  justify-content: space-between;\n}\n\n.total div {\n  display: flex;\n  flex-direction: column;\n  align-items: flex-end;\n}\n\n.error {\n  color: red;\n}\n```\n\n</Sandpack>\n"
  },
  {
    "path": "src/content/reference/react-compiler/compilationMode.md",
    "content": "---\ntitle: compilationMode\n---\n\n<Intro>\n\n`compilationMode` オプションは、React Compiler がコンパイル対象の関数をどのように選択するか制御します。\n\n</Intro>\n\n```js\n{\n  compilationMode: 'infer' // or 'annotation', 'syntax', 'all'\n}\n```\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `compilationMode` {/*compilationmode*/}\n\nReact Compiler が最適化する関数を決定する方法を制御します。\n\n#### 型 {/*type*/}\n\n```\n'infer' | 'syntax' | 'annotation' | 'all'\n```\n\n#### デフォルト値 {/*default-value*/}\n\n`'infer'`\n\n#### 指定可能な値 {/*options*/}\n\n- **`'infer'`**（デフォルト）: コンパイラは高度なヒューリスティックを使用して React コンポーネントとフックを識別します。\n  - `\"use memo\"` ディレクティブで明示的にアノテーションされた関数\n  - コンポーネント（パスカルケース）やフック（`use` プレフィックス）の規約で命名され、かつ、JSX の作成あるいは他のフックの呼び出しを行っている関数\n\n- **`'annotation'`**: `\"use memo\"` ディレクティブで明示的にマークされた関数のみをコンパイルします。段階的導入に最適です。\n\n- **`'syntax'`**: Flow の [component](https://flow.org/en/docs/react/component-syntax/) および [hook](https://flow.org/en/docs/react/hook-syntax/) 構文を使用するコンポーネントとフックのみをコンパイルします。\n\n- **`'all'`**: すべてのトップレベル関数をコンパイルします。非 React 関数もコンパイルする可能性があるため推奨されません。\n\n#### 注意点 {/*caveats*/}\n\n- `'infer'` モードでは、関数が検出されるために React の命名規則に従う必要があります。\n- `'all'` モードを使用すると、ユーティリティ関数がコンパイルされるためにパフォーマンスに悪影響を与える可能性があります。\n- `'syntax'` モードでは Flow が必要で、TypeScript では動作しません。\n- モードに関係なく、`\"use no memo\"` ディレクティブを持つ関数は常にスキップされます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### デフォルト推論モード {/*default-inference-mode*/}\n\nデフォルトの `'infer'` モードは、React の慣習に従う大抵のコードベースでうまく動作します。\n\n```js\n{\n  compilationMode: 'infer'\n}\n```\n\nこのモードでは、以下の関数がコンパイルされます。\n\n```js\n// ✅ Compiled: Named like a component + returns JSX\nfunction Button(props) {\n  return <button>{props.label}</button>;\n}\n\n// ✅ Compiled: Named like a hook + calls hooks\nfunction useCounter() {\n  const [count, setCount] = useState(0);\n  return [count, setCount];\n}\n\n// ✅ Compiled: Explicit directive\nfunction expensiveCalculation(data) {\n  \"use memo\";\n  return data.reduce(/* ... */);\n}\n\n// ❌ Not compiled: Not a component/hook pattern\nfunction calculateTotal(items) {\n  return items.reduce((a, b) => a + b, 0);\n}\n```\n\n### アノテーションモードを使用した段階的な導入 {/*incremental-adoption*/}\n\n段階的な移行では、マークされた関数のみをコンパイルするために `'annotation'` モードを使用してください。\n\n```js\n{\n  compilationMode: 'annotation'\n}\n```\n\nその後に、コンパイルする関数を明示的にマークしていきます。\n\n```js\n// Only this function will be compiled\nfunction ExpensiveList(props) {\n  \"use memo\";\n  return (\n    <ul>\n      {props.items.map(item => (\n        <li key={item.id}>{item.name}</li>\n      ))}\n    </ul>\n  );\n}\n\n// This won't be compiled without the directive\nfunction NormalComponent(props) {\n  return <div>{props.content}</div>;\n}\n```\n\n### Flow syntax モードの使用方法 {/*flow-syntax-mode*/}\n\nコードベースで TypeScript ではなく Flow を使用している場合は、以下のようにします。\n\n```js\n{\n  compilationMode: 'syntax'\n}\n```\n\n次に、Flow のコンポーネント構文を使用します。\n\n```js\n// Compiled: Flow component syntax\ncomponent Button(label: string) {\n  return <button>{label}</button>;\n}\n\n// Compiled: Flow hook syntax\nhook useCounter(initial: number) {\n  const [count, setCount] = useState(initial);\n  return [count, setCount];\n}\n\n// Not compiled: Regular function syntax\nfunction helper(data) {\n  return process(data);\n}\n```\n\n### 特定の関数のオプトアウト {/*opting-out*/}\n\nコンパイルモードに関係なく、`\"use no memo\"` を使用してコンパイルをスキップすることができます。\n\n```js\nfunction ComponentWithSideEffects() {\n  \"use no memo\"; // Prevent compilation\n\n  // This component has side effects that shouldn't be memoized\n  logToAnalytics('component_rendered');\n\n  return <div>Content</div>;\n}\n```\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### infer モードでコンポーネントがコンパイルされない {/*component-not-compiled-infer*/}\n\n`'infer'` モードでは、コンポーネントが React の慣習に従っていることを確認してください。\n\n```js\n// ❌ Won't be compiled: lowercase name\nfunction button(props) {\n  return <button>{props.label}</button>;\n}\n\n// ✅ Will be compiled: PascalCase name\nfunction Button(props) {\n  return <button>{props.label}</button>;\n}\n\n// ❌ Won't be compiled: doesn't create JSX or call hooks\nfunction useData() {\n  return window.localStorage.getItem('data');\n}\n\n// ✅ Will be compiled: calls a hook\nfunction useData() {\n  const [data] = useState(() => window.localStorage.getItem('data'));\n  return data;\n}\n```\n"
  },
  {
    "path": "src/content/reference/react-compiler/compiling-libraries.md",
    "content": "---\ntitle: Compiling Libraries\n---\n\n<Intro>\nThis guide helps library authors understand how to use React Compiler to ship optimized library code to their users.\n</Intro>\n\n<InlineToc />\n\n## Why Ship Compiled Code? {/*why-ship-compiled-code*/}\n\nAs a library author, you can compile your library code before publishing to npm. This provides several benefits:\n\n- **Performance improvements for all users** - Your library users get optimized code even if they aren't using React Compiler yet\n- **No configuration required by users** - The optimizations work out of the box\n- **Consistent behavior** - All users get the same optimized version regardless of their build setup\n\n## Setting Up Compilation {/*setting-up-compilation*/}\n\nAdd React Compiler to your library's build process:\n\n<TerminalBlock>\nnpm install -D babel-plugin-react-compiler@latest\n</TerminalBlock>\n\nConfigure your build tool to compile your library. For example, with Babel:\n\n```js\n// babel.config.js\nmodule.exports = {\n  plugins: [\n    'babel-plugin-react-compiler',\n  ],\n  // ... other config\n};\n```\n\n## Backwards Compatibility {/*backwards-compatibility*/}\n\nIf your library supports React versions below 19, you'll need additional configuration:\n\n### 1. Install the runtime package {/*install-runtime-package*/}\n\nWe recommend installing react-compiler-runtime as a direct dependency:\n\n<TerminalBlock>\nnpm install react-compiler-runtime@latest\n</TerminalBlock>\n\n```json\n{\n  \"dependencies\": {\n    \"react-compiler-runtime\": \"^1.0.0\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"^17.0.0 || ^18.0.0 || ^19.0.0\"\n  }\n}\n```\n\n### 2. Configure the target version {/*configure-target-version*/}\n\nSet the minimum React version your library supports:\n\n```js\n{\n  target: '17', // Minimum supported React version\n}\n```\n\n## Testing Strategy {/*testing-strategy*/}\n\nTest your library both with and without compilation to ensure compatibility. Run your existing test suite against the compiled code, and also create a separate test configuration that bypasses the compiler. This helps catch any issues that might arise from the compilation process and ensures your library works correctly in all scenarios.\n\n## Troubleshooting {/*troubleshooting*/}\n\n### Library doesn't work with older React versions {/*library-doesnt-work-with-older-react-versions*/}\n\nIf your compiled library throws errors in React 17 or 18:\n\n1. Verify you've installed `react-compiler-runtime` as a dependency\n2. Check that your `target` configuration matches your minimum supported React version\n3. Ensure the runtime package is included in your published bundle\n\n### Compilation conflicts with other Babel plugins {/*compilation-conflicts-with-other-babel-plugins*/}\n\nSome Babel plugins may conflict with React Compiler:\n\n1. Place `babel-plugin-react-compiler` early in your plugin list\n2. Disable conflicting optimizations in other plugins\n3. Test your build output thoroughly\n\n### Runtime module not found {/*runtime-module-not-found*/}\n\nIf users see \"Cannot find module 'react-compiler-runtime'\":\n\n1. Ensure the runtime is listed in `dependencies`, not `devDependencies`\n2. Check that your bundler includes the runtime in the output\n3. Verify the package is published to npm with your library\n\n## Next Steps {/*next-steps*/}\n\n- Learn about [debugging techniques](/learn/react-compiler/debugging) for compiled code\n- Check the [configuration options](/reference/react-compiler/configuration) for all compiler options\n- Explore [compilation modes](/reference/react-compiler/compilationMode) for selective optimization"
  },
  {
    "path": "src/content/reference/react-compiler/configuration.md",
    "content": "---\ntitle: 設定\n---\n\n<Intro>\n\nこのページでは、React Compiler で利用可能な設定オプションをすべてリストアップしています。\n\n</Intro>\n\n<Note>\n\nほとんどのアプリでは、デフォルトの設定で問題なく動作します。特別な要件がある場合は、後述する詳細な設定を利用できます。\n\n</Note>\n\n```js\n// babel.config.js\nmodule.exports = {\n  plugins: [\n    [\n      'babel-plugin-react-compiler', {\n        // compiler options\n      }\n    ]\n  ]\n};\n```\n\n---\n\n## コンパイル制御 {/*compilation-control*/}\n\nこれらのオプションは、コンパイラが*何を*最適化し、*どのように*コンポーネントとフックを選択してコンパイルするかを制御します。\n\n* [`compilationMode`](/reference/react-compiler/compilationMode) は、コンパイルする関数を選択する方法を制御します（例：すべての関数、アノテーション付きのもののみ、インテリジェント検出など）。\n\n```js\n{\n  compilationMode: 'annotation' // Only compile \"use memo\" functions\n}\n```\n\n---\n\n## バージョン互換性 {/*version-compatibility*/}\n\nReact バージョンの設定により、使用中の React バージョンと互換性のあるコードをコンパイラが生成することが保証されます。\n\n[`target`](/reference/react-compiler/target) は、使用中の React バージョン（17、18、19）を指定します。\n\n```js\n// For React 18 projects\n{\n  target: '18' // Also requires react-compiler-runtime package\n}\n```\n\n---\n\n## エラーハンドリング {/*error-handling*/}\n\nこれらのオプションは、コンパイラが [React のルール](/reference/rules)に従わないコードをどのように処理するか制御します。\n\n[`panicThreshold`](/reference/react-compiler/panicThreshold) は、ビルドを失敗させるか、問題のあるコンポーネントをスキップするかを決定します。\n\n```js\n// Recommended for production\n{\n  panicThreshold: 'none' // Skip components with errors instead of failing the build\n}\n```\n\n---\n\n## デバッグ {/*debugging*/}\n\nログと解析オプションは、コンパイラが何を行っているのか理解するのに役立ちます。\n\n[`logger`](/reference/react-compiler/logger) は、コンパイルイベントに対するカスタムのロギング手段を指定します。\n\n```js\n{\n  logger: {\n    logEvent(filename, event) {\n      if (event.kind === 'CompileSuccess') {\n        console.log('Compiled:', filename);\n      }\n    }\n  }\n}\n```\n\n---\n\n## フィーチャーフラグ {/*feature-flags*/}\n\n条件付きコンパイルにより、最適化されたコードがいつ使用されるか制御することができます。\n\n[`gating`](/reference/react-compiler/gating) は、A/B テストや段階的ロールアウトのためのランタイムフィーチャーフラグを有効にします。\n\n```js\n{\n  gating: {\n    source: 'my-feature-flags',\n    importSpecifierName: 'isCompilerEnabled'\n  }\n}\n```\n\n---\n\n## 一般的な設定パターン {/*common-patterns*/}\n\n### デフォルト設定 {/*default-configuration*/}\n\nほとんどの React 19 アプリケーションで、コンパイラは設定なしで動作します。\n\n```js\n// babel.config.js\nmodule.exports = {\n  plugins: [\n    'babel-plugin-react-compiler'\n  ]\n};\n```\n\n### React 17/18 プロジェクト {/*react-17-18*/}\n\n古い React バージョンでは、ランタイムパッケージとターゲット設定が必要です。\n\n```bash\nnpm install react-compiler-runtime@latest\n```\n\n```js\n{\n  target: '18' // or '17'\n}\n```\n\n### 段階的な導入 {/*incremental-adoption*/}\n\n特定のディレクトリから始めて、段階的に拡張することができます。\n\n```js\n{\n  compilationMode: 'annotation' // Only compile \"use memo\" functions\n}\n```\n\n"
  },
  {
    "path": "src/content/reference/react-compiler/directives/use-memo.md",
    "content": "---\ntitle: \"use memo\"\ntitleForTitleTag: \"'use memo' directive\"\n---\n\n<Intro>\n\n`\"use memo\"` marks a function for React Compiler optimization.\n\n</Intro>\n\n<Note>\n\nIn most cases, you don't need `\"use memo\"`. It's primarily needed in `annotation` mode where you must explicitly mark functions for optimization. In `infer` mode, the compiler automatically detects components and hooks by their naming patterns (PascalCase for components, `use` prefix for hooks). If a component or hook isn't being compiled in `infer` mode, you should fix its naming convention rather than forcing compilation with `\"use memo\"`.\n\n</Note>\n\n<InlineToc />\n\n---\n\n## Reference {/*reference*/}\n\n### `\"use memo\"` {/*use-memo*/}\n\nAdd `\"use memo\"` at the beginning of a function to mark it for React Compiler optimization.\n\n```js {1}\nfunction MyComponent() {\n  \"use memo\";\n  // ...\n}\n```\n\nWhen a function contains `\"use memo\"`, the React Compiler will analyze and optimize it during build time. The compiler will automatically memoize values and components to prevent unnecessary re-computations and re-renders.\n\n#### Caveats {/*caveats*/}\n\n* `\"use memo\"` must be at the very beginning of a function body, before any imports or other code (comments are OK).\n* The directive must be written with double or single quotes, not backticks.\n* The directive must exactly match `\"use memo\"`.\n* Only the first directive in a function is processed; additional directives are ignored.\n* The effect of the directive depends on your [`compilationMode`](/reference/react-compiler/compilationMode) setting.\n\n### How `\"use memo\"` marks functions for optimization {/*how-use-memo-marks*/}\n\nIn a React app that uses the React Compiler, functions are analyzed at build time to determine if they can be optimized. By default, the compiler automatically infers which components to memoize, but this can depend on your [`compilationMode`](/reference/react-compiler/compilationMode) setting if you've set it.\n\n`\"use memo\"` explicitly marks a function for optimization, overriding the default behavior:\n\n* In `annotation` mode: Only functions with `\"use memo\"` are optimized\n* In `infer` mode: The compiler uses heuristics, but `\"use memo\"` forces optimization\n* In `all` mode: Everything is optimized by default, making `\"use memo\"` redundant\n\nThe directive creates a clear boundary in your codebase between optimized and non-optimized code, giving you fine-grained control over the compilation process.\n\n### When to use `\"use memo\"` {/*when-to-use*/}\n\nYou should consider using `\"use memo\"` when:\n\n#### You're using annotation mode {/*annotation-mode-use*/}\nIn `compilationMode: 'annotation'`, the directive is required for any function you want optimized:\n\n```js\n// ✅ This component will be optimized\nfunction OptimizedList() {\n  \"use memo\";\n  // ...\n}\n\n// ❌ This component won't be optimized\nfunction SimpleWrapper() {\n  // ...\n}\n```\n\n#### You're gradually adopting React Compiler {/*gradual-adoption*/}\nStart with `annotation` mode and selectively optimize stable components:\n\n```js\n// Start by optimizing leaf components\nfunction Button({ onClick, children }) {\n  \"use memo\";\n  // ...\n}\n\n// Gradually move up the tree as you verify behavior\nfunction ButtonGroup({ buttons }) {\n  \"use memo\";\n  // ...\n}\n```\n\n---\n\n## Usage {/*usage*/}\n\n### Working with different compilation modes {/*compilation-modes*/}\n\nThe behavior of `\"use memo\"` changes based on your compiler configuration:\n\n```js\n// babel.config.js\nmodule.exports = {\n  plugins: [\n    ['babel-plugin-react-compiler', {\n      compilationMode: 'annotation' // or 'infer' or 'all'\n    }]\n  ]\n};\n```\n\n#### Annotation mode {/*annotation-mode-example*/}\n```js\n// ✅ Optimized with \"use memo\"\nfunction ProductCard({ product }) {\n  \"use memo\";\n  // ...\n}\n\n// ❌ Not optimized (no directive)\nfunction ProductList({ products }) {\n  // ...\n}\n```\n\n#### Infer mode (default) {/*infer-mode-example*/}\n```js\n// Automatically memoized because this is named like a Component\nfunction ComplexDashboard({ data }) {\n  // ...\n}\n\n// Skipped: Is not named like a Component\nfunction simpleDisplay({ text }) {\n  // ...\n}\n```\n\nIn `infer` mode, the compiler automatically detects components and hooks by their naming patterns (PascalCase for components, `use` prefix for hooks). If a component or hook isn't being compiled in `infer` mode, you should fix its naming convention rather than forcing compilation with `\"use memo\"`.\n\n---\n\n## Troubleshooting {/*troubleshooting*/}\n\n### Verifying optimization {/*verifying-optimization*/}\n\nTo confirm your component is being optimized:\n\n1. Check the compiled output in your build\n2. Use React DevTools to check for Memo ✨ badge\n\n### See also {/*see-also*/}\n\n* [`\"use no memo\"`](/reference/react-compiler/directives/use-no-memo) - Opt out of compilation\n* [`compilationMode`](/reference/react-compiler/compilationMode) - Configure compilation behavior\n* [React Compiler](/learn/react-compiler) - Getting started guide"
  },
  {
    "path": "src/content/reference/react-compiler/directives/use-no-memo.md",
    "content": "---\ntitle: \"use no memo\"\ntitleForTitleTag: \"'use no memo' directive\"\n---\n\n<Intro>\n\n`\"use no memo\"` prevents a function from being optimized by React Compiler.\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## Reference {/*reference*/}\n\n### `\"use no memo\"` {/*use-no-memo*/}\n\nAdd `\"use no memo\"` at the beginning of a function to prevent React Compiler optimization.\n\n```js {1}\nfunction MyComponent() {\n  \"use no memo\";\n  // ...\n}\n```\n\nWhen a function contains `\"use no memo\"`, the React Compiler will skip it entirely during optimization. This is useful as a temporary escape hatch when debugging or when dealing with code that doesn't work correctly with the compiler.\n\n#### Caveats {/*caveats*/}\n\n* `\"use no memo\"` must be at the very beginning of a function body, before any imports or other code (comments are OK).\n* The directive must be written with double or single quotes, not backticks.\n* The directive must exactly match `\"use no memo\"` or its alias `\"use no forget\"`.\n* This directive takes precedence over all compilation modes and other directives.\n* It's intended as a temporary debugging tool, not a permanent solution.\n\n### How `\"use no memo\"` opts-out of optimization {/*how-use-no-memo-opts-out*/}\n\nReact Compiler analyzes your code at build time to apply optimizations. `\"use no memo\"` creates an explicit boundary that tells the compiler to skip a function entirely.\n\nThis directive takes precedence over all other settings:\n* In `all` mode: The function is skipped despite the global setting\n* In `infer` mode: The function is skipped even if heuristics would optimize it\n\nThe compiler treats these functions as if the React Compiler wasn't enabled, leaving them exactly as written.\n\n### When to use `\"use no memo\"` {/*when-to-use*/}\n\n`\"use no memo\"` should be used sparingly and temporarily. Common scenarios include:\n\n#### Debugging compiler issues {/*debugging-compiler*/}\nWhen you suspect the compiler is causing issues, temporarily disable optimization to isolate the problem:\n\n```js\nfunction ProblematicComponent({ data }) {\n  \"use no memo\"; // TODO: Remove after fixing issue #123\n\n  // Rules of React violations that weren't statically detected\n  // ...\n}\n```\n\n#### Third-party library integration {/*third-party*/}\nWhen integrating with libraries that might not be compatible with the compiler:\n\n```js\nfunction ThirdPartyWrapper() {\n  \"use no memo\";\n\n  useThirdPartyHook(); // Has side effects that compiler might optimize incorrectly\n  // ...\n}\n```\n\n---\n\n## Usage {/*usage*/}\n\nThe `\"use no memo\"` directive is placed at the beginning of a function body to prevent React Compiler from optimizing that function:\n\n```js\nfunction MyComponent() {\n  \"use no memo\";\n  // Function body\n}\n```\n\nThe directive can also be placed at the top of a file to affect all functions in that module:\n\n```js\n\"use no memo\";\n\n// All functions in this file will be skipped by the compiler\n```\n\n`\"use no memo\"` at the function level overrides the module level directive.\n\n---\n\n## Troubleshooting {/*troubleshooting*/}\n\n### Directive not preventing compilation {/*not-preventing*/}\n\nIf `\"use no memo\"` isn't working:\n\n```js\n// ❌ Wrong - directive after code\nfunction Component() {\n  const data = getData();\n  \"use no memo\"; // Too late!\n}\n\n// ✅ Correct - directive first\nfunction Component() {\n  \"use no memo\";\n  const data = getData();\n}\n```\n\nAlso check:\n* Spelling - must be exactly `\"use no memo\"`\n* Quotes - must use single or double quotes, not backticks\n\n### Best practices {/*best-practices*/}\n\n**Always document why** you're disabling optimization:\n\n```js\n// ✅ Good - clear explanation and tracking\nfunction DataProcessor() {\n  \"use no memo\"; // TODO: Remove after fixing rule of react violation\n  // ...\n}\n\n// ❌ Bad - no explanation\nfunction Mystery() {\n  \"use no memo\";\n  // ...\n}\n```\n\n### See also {/*see-also*/}\n\n* [`\"use memo\"`](/reference/react-compiler/directives/use-memo) - Opt into compilation\n* [React Compiler](/learn/react-compiler) - Getting started guide"
  },
  {
    "path": "src/content/reference/react-compiler/directives.md",
    "content": "---\ntitle: Directives\n---\n\n<Intro>\nReact Compiler directives are special string literals that control whether specific functions are compiled.\n</Intro>\n\n```js\nfunction MyComponent() {\n  \"use memo\"; // Opt this component into compilation\n  return <div>{/* ... */}</div>;\n}\n```\n\n<InlineToc />\n\n---\n\n## Overview {/*overview*/}\n\nReact Compiler directives provide fine-grained control over which functions are optimized by the compiler. They are string literals placed at the beginning of a function body or at the top of a module.\n\n### Available directives {/*available-directives*/}\n\n* **[`\"use memo\"`](/reference/react-compiler/directives/use-memo)** - Opts a function into compilation\n* **[`\"use no memo\"`](/reference/react-compiler/directives/use-no-memo)** - Opts a function out of compilation\n\n### Quick comparison {/*quick-comparison*/}\n\n| Directive | Purpose | When to use |\n|-----------|---------|-------------|\n| [`\"use memo\"`](/reference/react-compiler/directives/use-memo) | Force compilation | When using `annotation` mode or to override `infer` mode heuristics |\n| [`\"use no memo\"`](/reference/react-compiler/directives/use-no-memo) | Prevent compilation | Debugging issues or working with incompatible code |\n\n---\n\n## Usage {/*usage*/}\n\n### Function-level directives {/*function-level*/}\n\nPlace directives at the beginning of a function to control its compilation:\n\n```js\n// Opt into compilation\nfunction OptimizedComponent() {\n  \"use memo\";\n  return <div>This will be optimized</div>;\n}\n\n// Opt out of compilation\nfunction UnoptimizedComponent() {\n  \"use no memo\";\n  return <div>This won't be optimized</div>;\n}\n```\n\n### Module-level directives {/*module-level*/}\n\nPlace directives at the top of a file to affect all functions in that module:\n\n```js\n// At the very top of the file\n\"use memo\";\n\n// All functions in this file will be compiled\nfunction Component1() {\n  return <div>Compiled</div>;\n}\n\nfunction Component2() {\n  return <div>Also compiled</div>;\n}\n\n// Can be overridden at function level\nfunction Component3() {\n  \"use no memo\"; // This overrides the module directive\n  return <div>Not compiled</div>;\n}\n```\n\n### Compilation modes interaction {/*compilation-modes*/}\n\nDirectives behave differently depending on your [`compilationMode`](/reference/react-compiler/compilationMode):\n\n* **`annotation` mode**: Only functions with `\"use memo\"` are compiled\n* **`infer` mode**: Compiler decides what to compile, directives override decisions\n* **`all` mode**: Everything is compiled, `\"use no memo\"` can exclude specific functions\n\n---\n\n## Best practices {/*best-practices*/}\n\n### Use directives sparingly {/*use-sparingly*/}\n\nDirectives are escape hatches. Prefer configuring the compiler at the project level:\n\n```js\n// ✅ Good - project-wide configuration\n{\n  plugins: [\n    ['babel-plugin-react-compiler', {\n      compilationMode: 'infer'\n    }]\n  ]\n}\n\n// ⚠️ Use directives only when needed\nfunction SpecialCase() {\n  \"use no memo\"; // Document why this is needed\n  // ...\n}\n```\n\n### Document directive usage {/*document-usage*/}\n\nAlways explain why a directive is used:\n\n```js\n// ✅ Good - clear explanation\nfunction DataGrid() {\n  \"use no memo\"; // TODO: Remove after fixing issue with dynamic row heights (JIRA-123)\n  // Complex grid implementation\n}\n\n// ❌ Bad - no explanation\nfunction Mystery() {\n  \"use no memo\";\n  // ...\n}\n```\n\n### Plan for removal {/*plan-removal*/}\n\nOpt-out directives should be temporary:\n\n1. Add the directive with a TODO comment\n2. Create a tracking issue\n3. Fix the underlying problem\n4. Remove the directive\n\n```js\nfunction TemporaryWorkaround() {\n  \"use no memo\"; // TODO: Remove after upgrading ThirdPartyLib to v2.0\n  return <ThirdPartyComponent />;\n}\n```\n\n---\n\n## Common patterns {/*common-patterns*/}\n\n### Gradual adoption {/*gradual-adoption*/}\n\nWhen adopting the React Compiler in a large codebase:\n\n```js\n// Start with annotation mode\n{\n  compilationMode: 'annotation'\n}\n\n// Opt in stable components\nfunction StableComponent() {\n  \"use memo\";\n  // Well-tested component\n}\n\n// Later, switch to infer mode and opt out problematic ones\nfunction ProblematicComponent() {\n  \"use no memo\"; // Fix issues before removing\n  // ...\n}\n```\n\n\n---\n\n## Troubleshooting {/*troubleshooting*/}\n\nFor specific issues with directives, see the troubleshooting sections in:\n\n* [`\"use memo\"` troubleshooting](/reference/react-compiler/directives/use-memo#troubleshooting)\n* [`\"use no memo\"` troubleshooting](/reference/react-compiler/directives/use-no-memo#troubleshooting)\n\n### Common issues {/*common-issues*/}\n\n1. **Directive ignored**: Check placement (must be first) and spelling\n2. **Compilation still happens**: Check `ignoreUseNoForget` setting\n3. **Module directive not working**: Ensure it's before all imports\n\n---\n\n## See also {/*see-also*/}\n\n* [`compilationMode`](/reference/react-compiler/compilationMode) - Configure how the compiler chooses what to optimize\n* [`Configuration`](/reference/react-compiler/configuration) - Full compiler configuration options\n* [React Compiler documentation](https://react.dev/learn/react-compiler) - Getting started guide"
  },
  {
    "path": "src/content/reference/react-compiler/gating.md",
    "content": "---\ntitle: gating\n---\n\n<Intro>\n\n`gating` オプションは条件付きコンパイルを有効にし、最適化されたコードがランタイムでいつ使用されるか制御できるようにします。\n\n</Intro>\n\n```js\n{\n  gating: {\n    source: 'my-feature-flags',\n    importSpecifierName: 'shouldUseCompiler'\n  }\n}\n```\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `gating` {/*gating*/}\n\nコンパイルされた関数に対する、ランタイムのフィーチャーフラグによる制御を設定します。\n\n#### 型 {/*type*/}\n\n```\n{\n  source: string;\n  importSpecifierName: string;\n} | null\n```\n\n#### デフォルト値 {/*default-value*/}\n\n`null`\n\n#### プロパティ {/*properties*/}\n\n- **`source`**: フィーチャーフラグをインポートするモジュールパス\n- **`importSpecifierName`**: インポートするエクスポート済み関数の名前\n\n#### 注意点 {/*caveats*/}\n\n- ゲーティング関数はブーリアンを返す必要があります。\n- コンパイル済みバージョンと元のバージョンの両方を含めるため、バンドルサイズが増加します。\n- コンパイルされた関数を含むすべてのファイルにインポート文が付加されます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 基本的なフィーチャーフラグのセットアップ {/*basic-setup*/}\n\n1. フィーチャーフラグモジュールを作成します。\n\n```js\n// src/utils/feature-flags.js\nexport function shouldUseCompiler() {\n  // your logic here\n  return getFeatureFlag('react-compiler-enabled');\n}\n```\n\n2. コンパイラを設定します。\n\n```js\n{\n  gating: {\n    source: './src/utils/feature-flags',\n    importSpecifierName: 'shouldUseCompiler'\n  }\n}\n```\n\n3. コンパイラがゲーティング済みのコードを生成します。\n\n```js\n// Input\nfunction Button(props) {\n  return <button>{props.label}</button>;\n}\n\n// Output (simplified)\nimport { shouldUseCompiler } from './src/utils/feature-flags';\n\nconst Button = shouldUseCompiler()\n  ? function Button_optimized(props) { /* compiled version */ }\n  : function Button_original(props) { /* original version */ };\n```\n\nゲーティング関数はモジュール評価時に一度だけ評価されることに注意してください。JS バンドルがパース・評価された時点でコンポーネントの選択が固定され、ブラウザセッションが持続する間、静的に維持されます。\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### フィーチャーフラグが動作しない {/*flag-not-working*/}\n\nフラグモジュールが正しい関数をエクスポートしているか確認してください。\n\n```js\n// ❌ Wrong: Default export\nexport default function shouldUseCompiler() {\n  return true;\n}\n\n// ✅ Correct: Named export matching importSpecifierName\nexport function shouldUseCompiler() {\n  return true;\n}\n```\n\n### インポートエラーが発生する {/*import-errors*/}\n\nソースのパスが正しいことを確認してください。\n\n```js\n// ❌ Wrong: Relative to babel.config.js\n{\n  source: './src/flags',\n  importSpecifierName: 'flag'\n}\n\n// ✅ Correct: Module resolution path\n{\n  source: '@myapp/feature-flags',\n  importSpecifierName: 'flag'\n}\n\n// ✅ Also correct: Absolute path from project root\n{\n  source: './src/utils/flags',\n  importSpecifierName: 'flag'\n}\n```\n"
  },
  {
    "path": "src/content/reference/react-compiler/logger.md",
    "content": "---\ntitle: logger\n---\n\n<Intro>\n\n`logger` オプションで、コンパイル時に起こる React Compiler のイベントに対するカスタムロガーを指定します。\n\n</Intro>\n\n```js\n{\n  logger: {\n    logEvent(filename, event) {\n      console.log(`[Compiler] ${event.kind}: ${filename}`);\n    }\n  }\n}\n```\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `logger` {/*logger*/}\n\nコンパイラの動作を追跡しエラーをデバッグするための、カスタムのロギング方法を設定します。\n\n#### 型 {/*type*/}\n\n```\n{\n  logEvent: (filename: string | null, event: LoggerEvent) => void;\n} | null\n```\n\n#### デフォルト値 {/*default-value*/}\n\n`null`\n\n#### メソッド {/*methods*/}\n\n- **`logEvent`**: 各コンパイライベントに対して、ファイル名およびイベント詳細を引数にして呼び出される。\n\n#### イベントタイプ {/*event-types*/}\n\n- **`CompileSuccess`**: 関数が正常にコンパイルされた\n- **`CompileError`**: エラーにより関数がスキップされた\n- **`CompileDiagnostic`**: 致命的でない診断情報\n- **`CompileSkip`**: その他の理由で関数がスキップされた\n- **`PipelineError`**: 予期しないコンパイルエラー\n- **`Timing`**: パフォーマンスの測定情報\n\n#### 注意点 {/*caveats*/}\n\n- イベントの構造はバージョン間で変更される可能性があります。\n- 大きなコードベースだと、多くのログエントリが生成されます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 基本的なログ {/*basic-logging*/}\n\nコンパイルの成功と失敗を追跡します。\n\n```js\n{\n  logger: {\n    logEvent(filename, event) {\n      switch (event.kind) {\n        case 'CompileSuccess': {\n          console.log(`✅ Compiled: ${filename}`);\n          break;\n        }\n        case 'CompileError': {\n          console.log(`❌ Skipped: ${filename}`);\n          break;\n        }\n        default: {}\n      }\n    }\n  }\n}\n```\n\n### 詳細なエラーログ {/*detailed-error-logging*/}\n\nコンパイル失敗に関する詳細な情報を取得します。\n\n```js\n{\n  logger: {\n    logEvent(filename, event) {\n      if (event.kind === 'CompileError') {\n        console.error(`\\nCompilation failed: ${filename}`);\n        console.error(`Reason: ${event.detail.reason}`);\n\n        if (event.detail.description) {\n          console.error(`Details: ${event.detail.description}`);\n        }\n\n        if (event.detail.loc) {\n          const { line, column } = event.detail.loc.start;\n          console.error(`Location: Line ${line}, Column ${column}`);\n        }\n\n        if (event.detail.suggestions) {\n          console.error('Suggestions:', event.detail.suggestions);\n        }\n      }\n    }\n  }\n}\n```\n\n"
  },
  {
    "path": "src/content/reference/react-compiler/panicThreshold.md",
    "content": "---\ntitle: panicThreshold\n---\n\n<Intro>\n\n`panicThreshold` オプションは、コンパイル時のエラーを React Compiler がどのように処理するかを制御します。\n\n</Intro>\n\n```js\n{\n  panicThreshold: 'none' // Recommended\n}\n```\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `panicThreshold` {/*panicthreshold*/}\n\nコンパイルエラーでビルドを失敗させるか、最適化をスキップするかを決定します。\n\n#### 型 {/*type*/}\n\n```\n'none' | 'critical_errors' | 'all_errors'\n```\n\n#### デフォルト値 {/*default-value*/}\n\n`'none'`\n\n#### 指定可能な値 {/*options*/}\n\n- **`'none'`**（デフォルト、推奨）: コンパイルできないコンポーネントをスキップしてビルドを継続する。\n- **`'critical_errors'`**: クリティカルなコンパイラエラーの場合のみビルドを失敗させる。\n- **`'all_errors'`**: コンパイラの診断情報がある場合常にビルドを失敗させる。\n\n#### 注意点 {/*caveats*/}\n\n- 本番ビルドでは常に `'none'` を使用してください。\n- ビルドが失敗すると、アプリケーションのビルドも失敗します。\n- `'none'` の場合、コンパイラは問題のあるコードを自動的に検出してスキップします。\n- より高い閾値は開発中のデバッグ時にのみ有用です。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 本番設定（推奨） {/*production-configuration*/}\n\n本番ビルドでは常に `'none'` を使用します。これがデフォルト値です。\n\n```js\n{\n  panicThreshold: 'none'\n}\n```\n\nこれにより以下が保証されます。\n- コンパイラの問題でビルドが失敗することはありません。\n- 最適化できないコンポーネントは通常通り実行されます。\n- 最大数のコンポーネントが最適化されます。\n- 安定した本番デプロイが行われます。\n\n### 開発時のデバッグ {/*development-debugging*/}\n\n問題を見つけるために、一時的により厳しい閾値を使用します。\n\n```js\nconst isDevelopment = process.env.NODE_ENV === 'development';\n\n{\n  panicThreshold: isDevelopment ? 'critical_errors' : 'none',\n  logger: {\n    logEvent(filename, event) {\n      if (isDevelopment && event.kind === 'CompileError') {\n        // ...\n      }\n    }\n  }\n}\n```"
  },
  {
    "path": "src/content/reference/react-compiler/target.md",
    "content": "---\ntitle: target\n---\n\n<Intro>\n\n`target` オプションは、コンパイラがどの React バージョン向けにコードを生成するか指定します。\n\n</Intro>\n\n```js\n{\n  target: '19' // or '18', '17'\n}\n```\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `target` {/*target*/}\n\nコンパイル出力の React バージョンの互換性を設定します。\n\n#### 型 {/*type*/}\n\n```\n'17' | '18' | '19'\n```\n\n#### デフォルト値 {/*default-value*/}\n\n`'19'`\n\n#### 有効な値 {/*valid-values*/}\n\n- **`'19'`**: React 19 がターゲット（デフォルト）。追加のランタイムパッケージは不要です。\n- **`'18'`**: React 18 がターゲット。`react-compiler-runtime` パッケージが必要です。\n- **`'17'`**: React 17 がターゲット。`react-compiler-runtime` パッケージが必要です。\n\n#### 注意点 {/*caveats*/}\n\n- 数値ではなく文字列値を使用してください。（例：`17` ではなく `'17'`）\n- パッチバージョンを含めないでください。（例：`'18.2.0'` ではなく `'18'`）\n- React 19 にはコンパイラランタイム API が組み込みで含まれています。\n- React 17 と 18 では `react-compiler-runtime@latest` のインストールが必要です。\n\n---\n\n## 使用法 {/*usage*/}\n\n### React 19 がターゲットの場合（デフォルト） {/*targeting-react-19*/}\n\nReact 19 では特別な設定は不要です。\n\n```js\n{\n  // defaults to target: '19'\n}\n```\n\nコンパイラは React 19 組み込みのランタイム API を使用します。\n\n```js\n// Compiled output uses React 19's native APIs\nimport { c as _c } from 'react/compiler-runtime';\n```\n\n### React 17 または 18 が対象の場合 {/*targeting-react-17-or-18*/}\n\nReact 17 と React 18 のプロジェクトでは、2 つのステップが必要です。\n\n1. ランタイムパッケージをインストールします。\n\n```bash\nnpm install react-compiler-runtime@latest\n```\n\n2. ターゲットを設定します。\n\n```js\n// For React 18\n{\n  target: '18'\n}\n\n// For React 17\n{\n  target: '17'\n}\n```\n\nコンパイラは両バージョンでポリフィルランタイムを使用します。\n\n```js\n// Compiled output uses the polyfill\nimport { c as _c } from 'react-compiler-runtime';\n```\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### コンパイラのランタイムが不足していることに関するランタイムエラー {/*missing-runtime*/}\n\n\"Cannot find module 'react/compiler-runtime'\" のようなエラーが表示される場合は以下のようにします。\n\n1. React バージョンを確認してください。\n   ```bash\n   npm why react\n   ```\n\n2. React 17 または 18 を使用している場合は、ランタイムをインストールしてください。\n   ```bash\n   npm install react-compiler-runtime@latest\n   ```\n\n3. ターゲットが React バージョンと一致していることを確認してください。\n   ```js\n   {\n     target: '18' // Must match your React major version\n   }\n   ```\n\n### ランタイムパッケージが動作しない {/*runtime-not-working*/}\n\nランタイムパッケージが以下を満たしていることを確認してください。\n\n1. プロジェクト内にインストールされていること（グローバルではないこと）。\n2. `package.json` の依存ライブラリとして記載されていること。\n3. 正しいバージョンであること。（`@latest` タグ）\n4. `devDependencies` に含まれていないこと（ランタイム時に必要なため）。\n\n### コンパイル済み出力の確認 {/*checking-output*/}\n\n正しくランタイムが使用されていることを確認するには、インポートが変わっていることに注目してください（組み込み版は `react/compiler-runtime`、React 17/18 のスタンドアロンパッケージ版では `react-compiler-runtime`）。\n\n```js\n// For React 19 (built-in runtime)\nimport { c } from 'react/compiler-runtime'\n//                      ^\n\n// For React 17/18 (polyfill runtime)\nimport { c } from 'react-compiler-runtime'\n//                      ^\n```"
  },
  {
    "path": "src/content/reference/react-dom/client/createRoot.md",
    "content": "---\ntitle: createRoot\n---\n\n<Intro>\n\n`createRoot` は、ブラウザの DOM ノード内に React コンポーネントを表示するためのルートを作成します。\n\n```js\nconst root = createRoot(domNode, options?)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `createRoot(domNode, options?)` {/*createroot*/}\n\n`createRoot` を呼び出して、ブラウザの DOM 要素内にコンテンツを表示するための React ルートを作成します。\n\n```js\nimport { createRoot } from 'react-dom/client';\n\nconst domNode = document.getElementById('root');\nconst root = createRoot(domNode);\n```\n\nReact は `domNode` に対応するルートを作成し、その内部の DOM を管理します。ルートを作成した後、その内部に React コンポーネントを表示するために [`root.render`](#root-render) を呼び出す必要があります。\n\n```js\nroot.render(<App />);\n```\n\nReact で完全に構築されたアプリには、ルートコンポーネントのための `createRoot` 呼び出しが通常 1 つのみ存在します。ページ内に React を「散りばめて」使用するページの場合は、必要なだけ複数のルートを持つことができます。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `domNode`: [DOM 要素](https://developer.mozilla.org/en-US/docs/Web/API/Element)。React はこの DOM 要素に対応するルートを作成し、レンダーされた React コンテンツを表示するための `render` など、関数をルート上で呼び出せるようにします。\n\n* **省略可能** `options`: この React ルートに関するオプションが含まれたオブジェクト。\n\n  * **省略可能** `onCaughtError`: エラーバウンダリ内で React がエラーをキャッチしたときに呼び出されるコールバック。エラーバウンダリにキャッチされた `error` と、`componentStack` を含んだ `errorInfo` を引数にして呼び出されます。\n  * **省略可能** `onUncaughtError`: エラーがスローされたがエラーバウンダリでキャッチされなかったときに呼び出されるコールバック。スローされた `error` と、`componentStack` を含んだ `errorInfo` を引数にして呼び出されます。\n  * **省略可能** `onRecoverableError`: React が自動的にエラーから回復したときに呼び出されるコールバック。React がスローする `error` と、`componentStack` を含んだ `errorInfo` を引数にして呼び出されます。復帰可能なエラーの一部は元のエラーを `error.cause` として含んでいます。\n  * **省略可能** `identifierPrefix`: React が [`useId`](/reference/react/useId) によって生成する ID に使用する文字列プレフィックス。同じページ上に複数のルートを使用する際に、競合を避けるために用います。\n\n#### 返り値 {/*returns*/}\n\n`createRoot` は、[`render`](#root-render) と [`unmount`](#root-unmount) の 2 つのメソッドを持つオブジェクトを返します。\n\n#### 注意点 {/*caveats*/}\n* アプリがサーバレンダリングを使用している場合、`createRoot()` の使用はサポートされていません。代わりに [`hydrateRoot()`](/reference/react-dom/client/hydrateRoot) を使用してください。\n* アプリ内で `createRoot` を呼び出すのは通常 1 回だけです。フレームワークを使用している場合、この呼び出しはフレームワークが代わりに行う可能性があります。\n* DOM ツリー内の、コンポーネントの子ではない別の部分に JSX をレンダーしたい場合（例えばモーダルやツールチップ）、`createRoot` の代わりに [`createPortal`](/reference/react-dom/createPortal) を使用してください。\n\n---\n\n### `root.render(reactNode)` {/*root-render*/}\n\n`root.render` を呼び出して、React ルートのブラウザ DOM ノードに [JSX](/learn/writing-markup-with-jsx)（\"React ノード\"）を表示します。\n\n```js\nroot.render(<App />);\n```\n\nReact は `root` に `<App />` を表示し、その内部の DOM の管理を行います。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*root-render-parameters*/}\n\n* `reactNode`: 表示したい *React ノード*。通常は `<App />` のような JSX ですが、[`createElement()`](/reference/react/createElement) で構築した React 要素や、文字列、数値、`null`、または `undefined` を渡すこともできます。\n\n\n#### 返り値 {/*root-render-returns*/}\n\n`root.render` は `undefined` を返します。\n\n#### 注意点 {/*root-render-caveats*/}\n\n* `root.render` を初めて呼び出したとき、React は React ルート内の既存の HTML コンテンツをすべてクリアしてから、React コンポーネントをレンダーします。\n\n* ルートの DOM ノードがサーバやビルド中に React によって生成された HTML を含んでいる場合は、既存の HTML にイベントハンドラをアタッチできる [`hydrateRoot()`](/reference/react-dom/client/hydrateRoot) を使用してください。\n\n* 同じルートに対して `render` を複数回呼び出すと、React は最新の JSX を反映するために必要なだけの DOM の更新を行います。React は、渡された JSX を以前にレンダーしたツリーと[「マッチング」](/learn/preserving-and-resetting-state)して、DOM のどの部分が再利用でき、どの部分を再作成する必要があるのかを決定します。同じルートに対して複数回 `render` を呼び出すことは、ルートコンポーネントで [`set` 関数](/reference/react/useState#setstate)を呼び出すことに似ており、React は不必要な DOM 更新を回避します。\n\n* レンダーは一度始まると同期的に実行されますが、`root.render(...)` 自体は同期的ではありません。つまり、`root.render()` の後に書かれたコードが、そのレンダーで発生するエフェクト（`useLayoutEffect` や `useEffect`）よりも先に実行される場合があります。これは通常問題なく、調整が必要となることは滅多にありません。エフェクトのタイミングが重要となる稀なケースでは、[`flushSync`](https://react.dev/reference/react-dom/flushSync) で `root.render(...)` をラップすることで、初回レンダーを完全に同期的に実行できます。\n  \n  ```js\n  const root = createRoot(document.getElementById('root'));\n  root.render(<App />);\n  // 🚩 The HTML will not include the rendered <App /> yet:\n  console.log(document.body.innerHTML);\n  ```\n\n---\n\n### `root.unmount()` {/*root-unmount*/}\n\n`root.unmount` を呼び出して、React ルート内にレンダーされたツリーを破棄します。\n\n```js\nroot.unmount();\n```\n\nReact で完全に構築されたアプリには、通常、`root.unmount` の呼び出しは一切ありません。\n\nこれは主に、React ルートの DOM ノード（またはその祖先のいずれか）が他のコードによって DOM から削除される可能性がある場合に有用です。例えば、非アクティブなタブを DOM から削除する jQuery のタブパネルがあると想像してみてください。タブが削除されると、（React ルートを含んだ）内部のすべてが DOM から削除されます。その場合、削除されたルートのコンテンツの管理を「停止」するよう React に伝えるために `root.unmount` を呼び出す必要があります。そうしないと、削除されたルート内のコンポーネントは、データ購読などのグローバルリソースをクリーンアップして解放する必要があるということが分からないままになります。\n\n`root.unmount` を呼び出すことで、ルート内のすべてのコンポーネントがアンマウントされ、React がルート DOM ノードから「切り離され」ます。これには、ツリー内のイベントハンドラや state の削除も含まれます。\n\n\n#### 引数 {/*root-unmount-parameters*/}\n\n`root.unmount` は引数を受け付けません。\n\n\n#### 返り値 {/*root-unmount-returns*/}\n\n`root.unmount` は `undefined` を返します。\n\n#### 注意点 {/*root-unmount-caveats*/}\n\n* `root.unmount` を呼び出すと、ツリー内のすべてのコンポーネントがアンマウントされ、React がルート DOM ノードから「切り離され」ます。\n\n* `root.unmount` を呼び出した後、同一ルートで再度 `root.render` を呼び出すことはできません。アンマウント済みのルートで `root.render` を呼び出そうとすると、\"Cannot update an unmounted root\" というエラーがスローされます。ただし、ある DOM ノードに対する以前のルートがアンマウントされた後で、同じ DOM ノードに対して新しいルートを作成することは可能です。\n\n---\n\n## 使用法 {/*usage*/}\n\n### React で完全に構築されたアプリのレンダー {/*rendering-an-app-fully-built-with-react*/}\n\nアプリが完全に React で構築されている場合は、アプリ全体のための単一のルートを作成します。\n\n```js [[1, 3, \"document.getElementById('root')\"], [2, 4, \"<App />\"]]\nimport { createRoot } from 'react-dom/client';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(<App />);\n```\n\n通常、このコードは起動時に一度だけ実行する必要があります。このコードは以下のことを行います。\n\n1. HTML で定義されている<CodeStep step={1}>ブラウザの DOM ノード</CodeStep>を見つけます。\n2. アプリの <CodeStep step={2}>React コンポーネント</CodeStep>をその内部に表示します。\n\n<Sandpack>\n\n```html public/index.html\n<!DOCTYPE html>\n<html>\n  <head><title>My app</title></head>\n  <body>\n    <!-- This is the DOM node -->\n    <div id=\"root\"></div>\n  </body>\n</html>\n```\n\n```js src/index.js active\nimport { createRoot } from 'react-dom/client';\nimport App from './App.js';\nimport './styles.css';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(<App />);\n```\n\n```js src/App.js\nimport { useState } from 'react';\n\nexport default function App() {\n  return (\n    <>\n      <h1>Hello, world!</h1>\n      <Counter />\n    </>\n  );\n}\n\nfunction Counter() {\n  const [count, setCount] = useState(0);\n  return (\n    <button onClick={() => setCount(count + 1)}>\n      You clicked me {count} times\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\n**アプリが完全に React で構築されている場合、さらにルートを作成したり、[`root.render`](#root-render) を再度呼び出したりする必要はありません**。\n\nこの時点から、React がアプリ全体の DOM を管理します。さらにコンポーネントを追加するには、[`App` コンポーネントの中にネストします](/learn/importing-and-exporting-components)。UI を更新する必要がある場合、各コンポーネントが [state を使用して行います](/reference/react/useState)。モーダルやツールチップなどの追加コンテンツをこの DOM ノードの外部に表示する必要がある場合、[ポータルを使ってレンダーします](/reference/react-dom/createPortal)。\n\n<Note>\n\nHTML が空の場合、アプリの JavaScript コードが読み込まれて実行されるまで、ユーザには空白のページが見え続けることになります。\n\n```html\n<div id=\"root\"></div>\n```\n\nこれは非常に遅く感じられることがあります！ これを解決するために、[サーバサイドで、あるいはビルド時に](/reference/react-dom/server)初期 HTML をコンポーネントから生成することができます。これにより、訪問者は JavaScript コードが読み込まれる前にテキストを読んだり、画像を見たり、リンクをクリックしたりすることができます。この最適化を自動で行う[フレームワークの使用](/learn/creating-a-react-app#full-stack-frameworks)を推奨します。実行タイミングにより、この技術は*サーバサイドレンダリング (server-side rendering; SSR)* または *静的サイト生成 (static site generation; SSG)* と呼ばれます。\n\n</Note>\n\n<Pitfall>\n\n**サーバレンダリングまたは静的生成を使用するアプリは、`createRoot` の代わりに [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を呼び出す必要があります**。これにより React は、HTML に書かれた DOM ノードを破棄して再作成するのではなく、ハイドレーション（再利用）するようになります。\n\n</Pitfall>\n\n---\n\n### React で部分的に構築されたページのレンダー {/*rendering-a-page-partially-built-with-react*/}\n\nページが[完全には React で構築されていない](/learn/add-react-to-an-existing-project#using-react-for-a-part-of-your-existing-page)場合、`createRoot` を複数回呼び出して、React に管理させたいトップレベルの各 UI パーツに対してルートを作成することができます。各ルートで [`root.render`](#root-render) を呼び出して、それぞれに異なるコンテンツを表示することができます。\n\n以下では、`index.html` ファイルに定義されている 2 つの異なる DOM ノードに 2 つの異なる React コンポーネントがレンダーされています。\n\n<Sandpack>\n\n```html public/index.html\n<!DOCTYPE html>\n<html>\n  <head><title>My app</title></head>\n  <body>\n    <nav id=\"navigation\"></nav>\n    <main>\n      <p>This paragraph is not rendered by React (open index.html to verify).</p>\n      <section id=\"comments\"></section>\n    </main>\n  </body>\n</html>\n```\n\n```js src/index.js active\nimport './styles.css';\nimport { createRoot } from 'react-dom/client';\nimport { Comments, Navigation } from './Components.js';\n\nconst navDomNode = document.getElementById('navigation');\nconst navRoot = createRoot(navDomNode); \nnavRoot.render(<Navigation />);\n\nconst commentDomNode = document.getElementById('comments');\nconst commentRoot = createRoot(commentDomNode); \ncommentRoot.render(<Comments />);\n```\n\n```js src/Components.js\nexport function Navigation() {\n  return (\n    <ul>\n      <NavLink href=\"/\">Home</NavLink>\n      <NavLink href=\"/about\">About</NavLink>\n    </ul>\n  );\n}\n\nfunction NavLink({ href, children }) {\n  return (\n    <li>\n      <a href={href}>{children}</a>\n    </li>\n  );\n}\n\nexport function Comments() {\n  return (\n    <>\n      <h2>Comments</h2>\n      <Comment text=\"Hello!\" author=\"Sophie\" />\n      <Comment text=\"How are you?\" author=\"Sunil\" />\n    </>\n  );\n}\n\nfunction Comment({ text, author }) {\n  return (\n    <p>{text} — <i>{author}</i></p>\n  );\n}\n```\n\n```css\nnav ul { padding: 0; margin: 0; }\nnav ul li { display: inline-block; margin-right: 20px; }\n```\n\n</Sandpack>\n\nまた、[`document.createElement()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement) を使用して新しい DOM ノードを作成し、それを手動でドキュメントに追加することもできます。\n\n```js\nconst domNode = document.createElement('div');\nconst root = createRoot(domNode); \nroot.render(<Comment />);\ndocument.body.appendChild(domNode); // You can add it anywhere in the document\n```\n\nDOM ノードから React ツリーを削除し、それによって使用されたすべてのリソースをクリーンアップするには、[`root.unmount` を呼び出します](#root-unmount)。\n\n```js\nroot.unmount();\n```\n\nこれは主に、React コンポーネントが別のフレームワークで書かれたアプリの中にある場合に役立ちます。\n\n---\n\n### ルートコンポーネントの更新 {/*updating-a-root-component*/}\n\n同じルートに対して `render` を複数回呼び出すことができます。コンポーネントツリーの構造が以前にレンダーされたものと一致していれば、React は [state を保持します](/learn/preserving-and-resetting-state)。以下の例で入力フィールドにタイプできることに着目してください。つまり毎秒 `render` が繰り返し呼び出されていますが、更新により DOM が破壊されていないということです。\n\n<Sandpack>\n\n```js src/index.js active\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\nimport App from './App.js';\n\nconst root = createRoot(document.getElementById('root'));\n\nlet i = 0;\nsetInterval(() => {\n  root.render(<App counter={i} />);\n  i++;\n}, 1000);\n```\n\n```js src/App.js\nexport default function App({counter}) {\n  return (\n    <>\n      <h1>Hello, world! {counter}</h1>\n      <input placeholder=\"Type something here\" />\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n`render` を複数回呼び出すことは滅多にありません。通常、コンポーネントは代わりに [state の更新](/reference/react/useState)を行います。\n\n### 本番環境でのエラーのロギング {/*error-logging-in-production*/}\n\nデフォルトでは、React はすべてのエラーをコンソールに記録します。独自のエラーレポートの仕組みを実装するには、省略可能なルートオプションとして `onUncaughtError`、`onCaughtError`、`onRecoverableError` のエラーハンドラを提供することができます。\n\n```js [[1, 6, \"onCaughtError\"], [2, 6, \"error\", 1], [3, 6, \"errorInfo\"], [4, 10, \"componentStack\", 15]]\nimport { createRoot } from \"react-dom/client\";\nimport { reportCaughtError } from \"./reportError\";\n\nconst container = document.getElementById(\"root\");\nconst root = createRoot(container, {\n  onCaughtError: (error, errorInfo) => {\n    if (error.message !== \"Known error\") {\n      reportCaughtError({\n        error,\n        componentStack: errorInfo.componentStack,\n      });\n    }\n  },\n});\n```\n\n<CodeStep step={1}>onCaughtError</CodeStep> は以下の 2 つの引数で呼びされる関数です。\n\n1. スローされた <CodeStep step={2}>error</CodeStep>。\n2. <CodeStep step={3}>errorInfo</CodeStep> オブジェクト。エラーの <CodeStep step={4}>componentStack</CodeStep> を含んでいる。\n\n`onUncaughtError` と `onRecoverableError` を組み合わせて、独自のエラーレポーティングのシステムを実装できます。\n\n<Sandpack>\n\n```js src/reportError.js\nfunction reportError({ type, error, errorInfo }) {\n  // The specific implementation is up to you.\n  // `console.error()` is only used for demonstration purposes.\n  console.error(type, error, \"Component Stack: \");\n  console.error(\"Component Stack: \", errorInfo.componentStack);\n}\n\nexport function onCaughtErrorProd(error, errorInfo) {\n  if (error.message !== \"Known error\") {\n    reportError({ type: \"Caught\", error, errorInfo });\n  }\n}\n\nexport function onUncaughtErrorProd(error, errorInfo) {\n  reportError({ type: \"Uncaught\", error, errorInfo });\n}\n\nexport function onRecoverableErrorProd(error, errorInfo) {\n  reportError({ type: \"Recoverable\", error, errorInfo });\n}\n```\n\n```js src/index.js active\nimport { createRoot } from \"react-dom/client\";\nimport App from \"./App.js\";\nimport {\n  onCaughtErrorProd,\n  onRecoverableErrorProd,\n  onUncaughtErrorProd,\n} from \"./reportError\";\n\nconst container = document.getElementById(\"root\");\nconst root = createRoot(container, {\n  // Keep in mind to remove these options in development to leverage\n  // React's default handlers or implement your own overlay for development.\n  // The handlers are only specfied unconditionally here for demonstration purposes.\n  onCaughtError: onCaughtErrorProd,\n  onRecoverableError: onRecoverableErrorProd,\n  onUncaughtError: onUncaughtErrorProd,\n});\nroot.render(<App />);\n```\n\n```js src/App.js\nimport { Component, useState } from \"react\";\n\nfunction Boom() {\n  foo.bar = \"baz\";\n}\n\nclass ErrorBoundary extends Component {\n  state = { hasError: false };\n\n  static getDerivedStateFromError(error) {\n    return { hasError: true };\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Something went wrong.</h1>;\n    }\n    return this.props.children;\n  }\n}\n\nexport default function App() {\n  const [triggerUncaughtError, settriggerUncaughtError] = useState(false);\n  const [triggerCaughtError, setTriggerCaughtError] = useState(false);\n\n  return (\n    <>\n      <button onClick={() => settriggerUncaughtError(true)}>\n        Trigger uncaught error\n      </button>\n      {triggerUncaughtError && <Boom />}\n      <button onClick={() => setTriggerCaughtError(true)}>\n        Trigger caught error\n      </button>\n      {triggerCaughtError && (\n        <ErrorBoundary>\n          <Boom />\n        </ErrorBoundary>\n      )}\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### ルートを作成したが何も表示されない {/*ive-created-a-root-but-nothing-is-displayed*/}\n\nアプリを実際にルートに*レンダー*するのを忘れていないか確認してください。\n\n```js {5}\nimport { createRoot } from 'react-dom/client';\nimport App from './App.js';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(<App />);\n```\n\nこれを行うまでは何も表示されません。\n\n---\n\n### \"You passed a second argument to root.render\" というエラーが出る {/*im-getting-an-error-you-passed-a-second-argument-to-root-render*/}\n\nよくある間違いとして、`createRoot` 用のオプションを `root.render(...)` に渡してしまうことが挙げられます。\n\n<ConsoleBlock level=\"error\">\n\nWarning: You passed a second argument to root.render(...) but it only accepts one argument.\n\n</ConsoleBlock>\n\n修正するには、ルートオプションを `createRoot(...)` に渡すようにしてください。`root.render(...)` ではありません。\n```js {2,5}\n// 🚩 Wrong: root.render only takes one argument.\nroot.render(App, {onUncaughtError});\n\n// ✅ Correct: pass options to createRoot.\nconst root = createRoot(container, {onUncaughtError}); \nroot.render(<App />);\n```\n\n---\n\n### \"Target container is not a DOM element\" というエラーが出る {/*im-getting-an-error-target-container-is-not-a-dom-element*/}\n\nこのエラーは、`createRoot` に渡しているものが DOM ノードでないことを意味します。\n\n何が起こっているのかわからない場合は、ログを出力してみてください。\n\n```js {2}\nconst domNode = document.getElementById('root');\nconsole.log(domNode); // ???\nconst root = createRoot(domNode);\nroot.render(<App />);\n```\n\n例えば、`domNode` が `null` の場合、[`getElementById`](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById) が `null` を返したことを意味します。これは、呼び出し時点でドキュメント内に指定した ID のノードが存在しない場合に発生します。こうなる理由はいくつか考えられます。\n\n1. 探している ID が HTML ファイルで使用した ID と異なっている。タイプミスをチェックしてください！\n2. DOM ノードがバンドルの `<script>` タグより*後ろ*にあるため、スクリプトから「見え」ない。\n\nこのエラーが発生するもうひとつの一般的な理由は、`createRoot(domNode)` ではなく `createRoot(<App />)` と書いてしまっていることです。\n\n---\n\n### \"Functions are not valid as a React child.\" というエラーが出る {/*im-getting-an-error-functions-are-not-valid-as-a-react-child*/}\n\nこのエラーは、`root.render` に渡しているものが React コンポーネントでないことを意味します。\n\nこれは、`root.render` を `<Component />` ではなく `Component` のように呼び出した場合に発生することがあります。\n\n```js {2,5}\n// 🚩 Wrong: App is a function, not a Component.\nroot.render(App);\n\n// ✅ Correct: <App /> is a component.\nroot.render(<App />);\n```\n\nまたは、`root.render` に関数を呼び出した結果ではなく関数自体を渡してしまった場合にも発生します。\n\n```js {2,5}\n// 🚩 Wrong: createApp is a function, not a component.\nroot.render(createApp);\n\n// ✅ Correct: call createApp to return a component.\nroot.render(createApp());\n```\n\n---\n\n### サーバレンダリングされた HTML が再作成される {/*my-server-rendered-html-gets-re-created-from-scratch*/}\n\nアプリがサーバでレンダーする形式であり、React によって生成された初期 HTML がある場合、ルートを作成して `root.render` を呼び出すと、その HTML がすべて削除されて DOM ノードがゼロから再作成されることに気付かれるかもしれません。これにより処理が遅くなり、フォーカスやスクロール位置がリセットされ、ユーザ入力が失われる可能性があります。\n\nサーバでレンダーされたアプリは、`createRoot` の代わりに [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を使用する必要があります。\n\n```js {1,4-7}\nimport { hydrateRoot } from 'react-dom/client';\nimport App from './App.js';\n\nhydrateRoot(\n  document.getElementById('root'),\n  <App />\n);\n```\n\nAPI が異なることに注意してください。特に、通常はこの後さらに `root.render` を呼び出すことはありません。\n"
  },
  {
    "path": "src/content/reference/react-dom/client/hydrateRoot.md",
    "content": "---\ntitle: hydrateRoot\n---\n\n<Intro>\n\n`hydrateRoot` を使用すると、[`react-dom/server`](/reference/react-dom/server) によって事前生成した HTML コンテンツが含まれるブラウザ DOM ノード内に、React コンポーネントを表示できます。\n\n```js\nconst root = hydrateRoot(domNode, reactNode, options?)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `hydrateRoot(domNode, reactNode, options?)` {/*hydrateroot*/}\n\n`hydrateRoot` を呼び出して、サーバ環境で事前に React がレンダーした HTML に対して React を「アタッチ」します。\n\n```js\nimport { hydrateRoot } from 'react-dom/client';\n\nconst domNode = document.getElementById('root');\nconst root = hydrateRoot(domNode, reactNode);\n```\n\nReact は、`domNode` 内に存在する HTML にアタッチし、その内部の DOM の管理を引き継ぎます。React で完全に構築されたアプリには、ルートコンポーネントを引数にした `hydrateRoot` 呼び出しが通常 1 つのみ存在します。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `domNode`: サーバ上でルート要素としてレンダーされた [DOM 要素](https://developer.mozilla.org/en-US/docs/Web/API/Element)。\n\n* `reactNode`: 既存の初期 HTML をレンダーするために使用された \"React ノード\"。これは通常、`ReactDOM Server` のメソッド（例：`renderToPipeableStream(<App />)`）でレンダーされた JSX、例えば `<App />` になります。\n\n* **省略可能** `options`: この React ルートのオプションを含むオブジェクト。\n\n  * **省略可能** `onCaughtError`: エラーバウンダリ内で React がエラーをキャッチしたときに呼び出されるコールバック。エラーバウンダリにキャッチされた `error` と、`componentStack` を含んだ `errorInfo` を引数にして呼び出されます。\n  * **省略可能** `onUncaughtError`: エラーがスローされたがエラーバウンダリでキャッチされなかったときに呼び出されるコールバック。スローされた `error` と、`componentStack` を含んだ `errorInfo` を引数にして呼び出されます。\n  * **optional** `onRecoverableError`: React が自動的にエラーから回復したときに呼び出されるコールバック。React がスローする `error` と、`componentStack` を含んだ `errorInfo` を引数にして呼び出されます。復帰可能なエラーの一部は元のエラーを `error.cause` として含んでいます。\n  * **省略可能** `identifierPrefix`: React が [`useId`](/reference/react/useId) によって生成する ID に使用する文字列プレフィックス。同じページ上に複数のルートを使用する際に、競合を避けるために用います。サーバ上で使用されたものと同じプレフィックスでなければなりません。\n\n\n#### 返り値 {/*returns*/}\n\n`hydrateRoot` は、[`render`](#root-render) と [`unmount`](#root-unmount) の 2 つのメソッドを持つオブジェクトを返します。\n\n#### 注意点 {/*caveats*/}\n\n* `hydrateRoot()` は、レンダーされたコンテンツがサーバでレンダーされたコンテンツと同一であることを期待しています。不一致はバグとして扱い修正する必要があります。\n* 開発モードでは、React はハイドレーション中の不一致について警告します。不一致が発生した場合、属性の違いが修正される保証はありません。これはパフォーマンス上の理由から重要です。なぜならほとんどのアプリでは、不一致はまれであり、すべてのマークアップを検証することは非常に高コストになるからです。\n* アプリには通常、`hydrateRoot` 呼び出しは 1 つだけ存在します。フレームワークを使用している場合、フレームワークがこの呼び出しを行うかもしれません。\n* アプリがクライアントでレンダーする形式であり、事前レンダーされた HTML がない場合、`hydrateRoot()` は使用できません。代わりに [`createRoot()`](/reference/react-dom/client/createRoot) を使用してください。\n\n---\n\n### `root.render(reactNode)` {/*root-render*/}\n\n`root.render` を呼び出して、ブラウザ DOM 要素内にハイドレーションされた React ルート内の React コンポーネントを更新します。\n\n```js\nroot.render(<App />);\n```\n\nReact はハイドレーションされた `root` 内の `<App />` を更新します。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*root-render-parameters*/}\n\n* `reactNode`: 更新したい \"React ノード\"。通常は `<App />` のような JSX ですが、[`createElement()`](/reference/react/createElement) で構築した React 要素や、文字列、数値、`null`、または `undefined` を渡すこともできます。\n\n\n#### 返り値 {/*root-render-returns*/}\n\n`root.render` は `undefined` を返します。\n\n#### 注意点 {/*root-render-caveats*/}\n\n* ルートがハイドレーションを完了する前に `root.render` を呼び出すと、React は既存のサーバレンダリングされた HTML コンテンツをクリアし、ルート全体をクライアントでのレンダーに切り替えます。\n\n---\n\n### `root.unmount()` {/*root-unmount*/}\n\n`root.unmount` を呼び出して、React ルート内にレンダーされたツリーを破棄します。\n\n```js\nroot.unmount();\n```\n\nReact で完全に構築されたアプリには、通常、`root.unmount` の呼び出しは一切ありません。\n\nこれは主に、React ルートの DOM ノード（またはその祖先のいずれか）が他のコードによって DOM から削除される可能性がある場合に有用です。例えば、非アクティブなタブを DOM から削除する jQuery のタブパネルがあると想像してみてください。タブが削除されると、（React ルートを含んだ）内部のすべてが DOM から削除されます。その場合、削除されたルートのコンテンツの管理を「停止」するよう React に伝えるために `root.unmount` を呼び出す必要があります。そうしないと、削除されたルート内のコンポーネントは、データ購読などのグローバルリソースをクリーンアップして解放する必要があるということが分からないままになります。\n\n`root.unmount` を呼び出すことで、ルート内のすべてのコンポーネントがアンマウントされ、React がルート DOM ノードから「切り離され」ます。これには、ツリー内のイベントハンドラや state の削除も含まれます。\n\n\n#### 引数 {/*root-unmount-parameters*/}\n\n`root.unmount` は引数を受け付けません。\n\n\n#### 返り値 {/*root-unmount-returns*/}\n\n`root.unmount` は `undefined` を返します。\n\n#### 注意点 {/*root-unmount-caveats*/}\n\n* `root.unmount` を呼び出すと、ツリー内のすべてのコンポーネントがアンマウントされ、React がルート DOM ノードから「切り離され」ます。\n\n* `root.unmount` を呼び出した後、同一ルートで再度 `root.render` を呼び出すことはできません。アンマウント済みのルートで `root.render` を呼び出そうとすると、\"Cannot update an unmounted root\" というエラーがスローされます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### サーバでレンダーされた HTML のハイドレーション {/*hydrating-server-rendered-html*/}\n\nあなたのアプリの HTML が [`react-dom/server`](/reference/react-dom/client/createRoot) によって生成されている場合、クライアント上それに対する*ハイドレーション*を行う必要があります。\n\n```js [[1, 3, \"document.getElementById('root')\"], [2, 3, \"<App />\"]]\nimport { hydrateRoot } from 'react-dom/client';\n\nhydrateRoot(document.getElementById('root'), <App />);\n```\n\nこれにより、<CodeStep step={1}>ブラウザ DOM ノード</CodeStep>内にあるサーバからの HTML がハイドレーションされ、あなたのアプリの <CodeStep step={2}>React コンポーネント</CodeStep>となります。通常、これはスタートアップ時に一度のみ行います。フレームワークを使用している場合はあなたの代わりにこの呼び出しが自動で行われるかもしれません。\n\nアプリのハイドレーション時に React は、サーバで生成された初期 HTML に、あなたのコンポーネントのロジックを「アタッチ」します。ハイドレーションにより、サーバからの初期 HTML スナップショットが、ブラウザで動作する完全にインタラクティブなアプリに変化します。\n\n<Sandpack>\n\n```html public/index.html\n<!--\n  HTML content inside <div id=\"root\">...</div>\n  was generated from App by react-dom/server.\n-->\n<div id=\"root\"><h1>Hello, world!</h1><button>You clicked me <!-- -->0<!-- --> times</button></div>\n```\n\n```js src/index.js active\nimport './styles.css';\nimport { hydrateRoot } from 'react-dom/client';\nimport App from './App.js';\n\nhydrateRoot(\n  document.getElementById('root'),\n  <App />\n);\n```\n\n```js src/App.js\nimport { useState } from 'react';\n\nexport default function App() {\n  return (\n    <>\n      <h1>Hello, world!</h1>\n      <Counter />\n    </>\n  );\n}\n\nfunction Counter() {\n  const [count, setCount] = useState(0);\n  return (\n    <button onClick={() => setCount(count + 1)}>\n      You clicked me {count} times\n    </button>\n  );\n}\n```\n\n</Sandpack>\n\n通常、`hydrateRoot` を再度呼び出したり、複数の場所で呼び出したりする必要はありません。ここから先は、React がアプリケーションの DOM を管理しています。UI を更新するには、コンポーネントは [state を使うことになるでしょう](/reference/react/useState)。\n\n<Pitfall>\n\n`hydrateRoot` に渡す React ツリーは、サーバ上で行った出力と**同じ出力**を生成する必要があります。\n\nこれはユーザ体験にとって重要です。ユーザはあなたの JavaScript コードがロードされる前に、サーバが生成した HTML を一定時間見ています。サーバレンダリングによりアプリの出力の HTML スナップショットを表示することで、アプリが素早くロードされているという錯覚が作り出されます。突然異なるコンテンツを表示すると、その錯覚が壊れてしまいます。このため、サーバからのレンダーの出力はクライアント上での初期レンダーの出力と一致する必要があるのです。\n\nハイドレーションエラーを引き起こす最も一般的な原因としては以下のようなものがあります。\n\n* ルートノード内の React が生成した HTML の周囲に余分な空白（改行など）がある。\n* レンダーのロジックで `typeof window !== 'undefined'` のようなチェックを使用している。\n* レンダーのロジックで [`window.matchMedia`](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) のようなブラウザ専用の API を使用している。\n* サーバとクライアントで異なるデータをレンダーしている。\n\nReact は一部のハイドレーションエラーから回復できますが、**他のバグと同様にそれらを修正する必要があります**。運がよければアプリが遅くなるだけですが、最悪の場合、イベントハンドラが誤った要素にアタッチされる可能性があります。\n\n</Pitfall>\n\n---\n\n### ドキュメント全体のハイドレーション {/*hydrating-an-entire-document*/}\n\nReact で完全に構築されたアプリケーションは、[`<html>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html) タグを含むドキュメント全体を JSX としてレンダーすることができます。\n\n```js {3,13}\nfunction App() {\n  return (\n    <html>\n      <head>\n        <meta charSet=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        <link rel=\"stylesheet\" href=\"/styles.css\"></link>\n        <title>My app</title>\n      </head>\n      <body>\n        <Router />\n      </body>\n    </html>\n  );\n}\n```\n\nドキュメント全体をハイドレートするには、`hydrateRoot` の最初の引数として [`document`](https://developer.mozilla.org/en-US/docs/Web/API/Window/document) グローバル変数を渡します。\n\n```js {4}\nimport { hydrateRoot } from 'react-dom/client';\nimport App from './App.js';\n\nhydrateRoot(document, <App />);\n```\n\n---\n\n### やむを得ないハイドレーション不一致エラーの抑制 {/*suppressing-unavoidable-hydration-mismatch-errors*/}\n\nサーバとクライアントの間で、単一の要素の属性やテキストコンテンツがやむを得ない理由で異なる場合（たとえば、タイムスタンプなど）、ハイドレーションの不一致警告を抑制することができます。\n\n要素のハイドレーション警告を抑制するには、`suppressHydrationWarning={true}` を追加します。\n\n<Sandpack>\n\n```html public/index.html\n<!--\n  HTML content inside <div id=\"root\">...</div>\n  was generated from App by react-dom/server.\n-->\n<div id=\"root\"><h1>Current Date: <!-- -->01/01/2020</h1></div>\n```\n\n```js src/index.js\nimport './styles.css';\nimport { hydrateRoot } from 'react-dom/client';\nimport App from './App.js';\n\nhydrateRoot(document.getElementById('root'), <App />);\n```\n\n```js src/App.js active\nexport default function App() {\n  return (\n    <h1 suppressHydrationWarning={true}>\n      Current Date: {new Date().toLocaleDateString()}\n    </h1>\n  );\n}\n```\n\n</Sandpack>\n\nこれは単一レベルの深さまでしか機能せず、避難ハッチとしての使用を意図しています。過度に使用しないでください。これを使用しても React はテキストコンテンツの不一致を修正しようとは**しません**。\n\n---\n\n### クライアントとサーバで異なるコンテンツの処理 {/*handling-different-client-and-server-content*/}\n\nサーバとクライアントで意図的に異なるものをレンダーする必要がある場合、2 回に分けたレンダーを行うことができます。クライアントで異なるものをレンダーするコンポーネントは、`isClient` のような [state 変数](/reference/react/useState)を読み取るようにし、この変数を[エフェクト](/reference/react/useEffect)内で `true` に設定することができます。\n\n<Sandpack>\n\n```html public/index.html\n<!--\n  HTML content inside <div id=\"root\">...</div>\n  was generated from App by react-dom/server.\n-->\n<div id=\"root\"><h1>Is Server</h1></div>\n```\n\n```js src/index.js\nimport './styles.css';\nimport { hydrateRoot } from 'react-dom/client';\nimport App from './App.js';\n\nhydrateRoot(document.getElementById('root'), <App />);\n```\n\n{/* kind of an edge case, seems fine to use this hack here */}\n```js {expectedErrors: {'react-compiler': [7]}} src/App.js active\nimport { useState, useEffect } from \"react\";\n\nexport default function App() {\n  const [isClient, setIsClient] = useState(false);\n\n  useEffect(() => {\n    setIsClient(true);\n  }, []);\n\n  return (\n    <h1>\n      {isClient ? 'Is Client' : 'Is Server'}\n    </h1>\n  );\n}\n```\n\n</Sandpack>\n\nこの方法では、初回のレンダーはサーバと同じコンテンツをレンダーし、不一致を回避しますが、追加のレンダーがハイドレーションの直後に同期的に行われます。\n\n<Pitfall>\n\nこのアプローチではコンポーネントを 2 回レンダーする必要があるためハイドレーションが遅くなります。低速な接続におけるユーザ体験に注意してください。JavaScript コードは初期レンダーされた HTML よりもかなり遅く読み込まれる場合があるため、ハイドレーション直後に異なる UI をレンダーするとユーザに不快感を与えるかもしれません。\n\n</Pitfall>\n\n---\n\n### ハイドレーションされたルートコンポーネントの更新 {/*updating-a-hydrated-root-component*/}\n\nルートのハイドレーションが完了した後、[`root.render`](#root-render) を呼び出してルートの React コンポーネントを更新することができます。**[`createRoot`](/reference/react-dom/client/createRoot) の場合とは異なり、初期コンテンツはすでに HTML としてレンダーされているため、通常はこれを行う必要はありません**。\n\nハイドレーションの後のどこかのタイミングで `root.render` を呼び出し、コンポーネントツリーの構造が以前にレンダーされたものと一致していれば、React は [state を保持します](/learn/preserving-and-resetting-state)。以下の例で入力フィールドにタイプできることに着目してください。つまり毎秒 `render` が繰り返し呼び出されていますが、更新により DOM が破壊されていないということです。\n\n<Sandpack>\n\n```html public/index.html\n<!--\n  All HTML content inside <div id=\"root\">...</div> was\n  generated by rendering <App /> with react-dom/server.\n-->\n<div id=\"root\"><h1>Hello, world! <!-- -->0</h1><input placeholder=\"Type something here\"/></div>\n```\n\n```js src/index.js active\nimport { hydrateRoot } from 'react-dom/client';\nimport './styles.css';\nimport App from './App.js';\n\nconst root = hydrateRoot(\n  document.getElementById('root'),\n  <App counter={0} />\n);\n\nlet i = 0;\nsetInterval(() => {\n  root.render(<App counter={i} />);\n  i++;\n}, 1000);\n```\n\n```js src/App.js\nexport default function App({counter}) {\n  return (\n    <>\n      <h1>Hello, world! {counter}</h1>\n      <input placeholder=\"Type something here\" />\n    </>\n  );\n}\n```\n\n</Sandpack>\n\nハイドレーションされたルートで [`root.render`](#root-render) を呼び出すことは滅多にありません。通常、代わりにコンポーネントの中で [state を更新](/reference/react/useState) します。\n\n### 本番環境でのエラーのロギング {/*error-logging-in-production*/}\n\nデフォルトでは、React はすべてのエラーをコンソールに記録します。独自のエラーレポートの仕組みを実装するには、省略可能なルートオプションとして `onUncaughtError`、`onCaughtError`、`onRecoverableError` のエラーハンドラを提供することができます。\n\n```js [[1, 7, \"onCaughtError\"], [2, 7, \"error\", 1], [3, 7, \"errorInfo\"], [4, 11, \"componentStack\", 15]]\nimport { hydrateRoot } from \"react-dom/client\";\nimport App from \"./App.js\";\nimport { reportCaughtError } from \"./reportError\";\n\nconst container = document.getElementById(\"root\");\nconst root = hydrateRoot(container, <App />, {\n  onCaughtError: (error, errorInfo) => {\n    if (error.message !== \"Known error\") {\n      reportCaughtError({\n        error,\n        componentStack: errorInfo.componentStack,\n      });\n    }\n  },\n});\n```\n\n<CodeStep step={1}>onCaughtError</CodeStep> は以下の 2 つの引数で呼びされる関数です。\n\n1. スローされた <CodeStep step={2}>error</CodeStep>。\n2. <CodeStep step={3}>errorInfo</CodeStep> オブジェクト。エラーの <CodeStep step={4}>componentStack</CodeStep> を含んでいる。\n\n`onUncaughtError` と `onRecoverableError` を組み合わせて、独自のエラーレポーティングのシステムを実装できます。\n\n<Sandpack>\n\n```js src/reportError.js\nfunction reportError({ type, error, errorInfo }) {\n  // The specific implementation is up to you.\n  // `console.error()` is only used for demonstration purposes.\n  console.error(type, error, \"Component Stack: \");\n  console.error(\"Component Stack: \", errorInfo.componentStack);\n}\n\nexport function onCaughtErrorProd(error, errorInfo) {\n  if (error.message !== \"Known error\") {\n    reportError({ type: \"Caught\", error, errorInfo });\n  }\n}\n\nexport function onUncaughtErrorProd(error, errorInfo) {\n  reportError({ type: \"Uncaught\", error, errorInfo });\n}\n\nexport function onRecoverableErrorProd(error, errorInfo) {\n  reportError({ type: \"Recoverable\", error, errorInfo });\n}\n```\n\n```js src/index.js active\nimport { hydrateRoot } from \"react-dom/client\";\nimport App from \"./App.js\";\nimport {\n  onCaughtErrorProd,\n  onRecoverableErrorProd,\n  onUncaughtErrorProd,\n} from \"./reportError\";\n\nconst container = document.getElementById(\"root\");\nhydrateRoot(container, <App />, {\n  // Keep in mind to remove these options in development to leverage\n  // React's default handlers or implement your own overlay for development.\n  // The handlers are only specfied unconditionally here for demonstration purposes.\n  onCaughtError: onCaughtErrorProd,\n  onRecoverableError: onRecoverableErrorProd,\n  onUncaughtError: onUncaughtErrorProd,\n});\n```\n\n```js src/App.js\nimport { Component, useState } from \"react\";\n\nfunction Boom() {\n  foo.bar = \"baz\";\n}\n\nclass ErrorBoundary extends Component {\n  state = { hasError: false };\n\n  static getDerivedStateFromError(error) {\n    return { hasError: true };\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Something went wrong.</h1>;\n    }\n    return this.props.children;\n  }\n}\n\nexport default function App() {\n  const [triggerUncaughtError, settriggerUncaughtError] = useState(false);\n  const [triggerCaughtError, setTriggerCaughtError] = useState(false);\n\n  return (\n    <>\n      <button onClick={() => settriggerUncaughtError(true)}>\n        Trigger uncaught error\n      </button>\n      {triggerUncaughtError && <Boom />}\n      <button onClick={() => setTriggerCaughtError(true)}>\n        Trigger caught error\n      </button>\n      {triggerCaughtError && (\n        <ErrorBoundary>\n          <Boom />\n        </ErrorBoundary>\n      )}\n    </>\n  );\n}\n```\n\n```html public/index.html hidden\n<!DOCTYPE html>\n<html>\n<head>\n  <title>My app</title>\n</head>\n<body>\n<!--\n  Purposefully using HTML content that differs from the server-rendered content to trigger recoverable errors.\n-->\n<div id=\"root\">Server content before hydration.</div>\n</body>\n</html>\n```\n</Sandpack>\n\n## トラブルシューティング {/*troubleshooting*/}\n\n\n### \"You passed a second argument to root.render\" というエラーが出る {/*im-getting-an-error-you-passed-a-second-argument-to-root-render*/}\n\nよくある間違いとして、`hydrateRoot` 用のオプションを `root.render(...)` に渡してしまうことが挙げられます。\n\n<ConsoleBlock level=\"error\">\n\nWarning: You passed a second argument to root.render(...) but it only accepts one argument.\n\n</ConsoleBlock>\n\n修正するには、ルートオプションを `hydrateRoot(...)` に渡すようにしてください。`root.render(...)` ではありません。\n```js {2,5}\n// 🚩 Wrong: root.render only takes one argument.\nroot.render(App, {onUncaughtError});\n\n// ✅ Correct: pass options to createRoot.\nconst root = hydrateRoot(container, <App />, {onUncaughtError});\n```\n"
  },
  {
    "path": "src/content/reference/react-dom/client/index.md",
    "content": "---\ntitle: クライアント用 React DOM API\n---\n\n<Intro>\n\n`react-dom/client` の API を用いて、クライアント（ブラウザ）上で React コンポーネントをレンダーすることができます。これらの API は通常、React ツリーを初期化するためにアプリのトップレベルで使用されます。[フレームワーク](/learn/creating-a-react-app#full-stack-frameworks)はこれらをあなたの代わりに呼び出すことがあります。ほとんどのコンポーネントは、これらをインポートしたり使用したりする必要はありません。\n\n</Intro>\n\n---\n\n## クライアント API {/*client-apis*/}\n\n* [`createRoot`](/reference/react-dom/client/createRoot) は、ブラウザの DOM ノード内に React コンポーネントを表示するためのルートを作成します。\n* [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) は、[`react-dom/server`](/reference/react-dom/server) によって事前生成した HTML コンテンツが含まれるブラウザ DOM ノード内に、React コンポーネントを表示します。\n\n---\n\n## ブラウザサポート {/*browser-support*/}\n\nReact は、Internet Explorer 9 以上を含むすべての一般的なブラウザをサポートしています。IE 9 や IE 10 などの古いブラウザにはいくつかのポリフィルが必要です。"
  },
  {
    "path": "src/content/reference/react-dom/components/common.md",
    "content": "---\ntitle: \"<div> などの一般的なコンポーネント\"\n---\n\n<Intro>\n\n[`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div) などすべての組み込みブラウザコンポーネントは、いくつかの共通の props とイベントをサポートしています。\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<div>` などの一般的なコンポーネント {/*common*/}\n\n```js\n<div className=\"wrapper\">Some content</div>\n```\n\n[さらに例を見る](#usage)\n\n#### props {/*common-props*/}\n\nこれらの特別な React の props はすべての組み込みコンポーネントでサポートされています。\n\n* `children`: React ノード（要素、文字列、数値、[ポータル](/reference/react-dom/createPortal)、`null` や `undefined` やブーリアンのような空ノード、あるいは他の React ノードの配列）。コンポーネントの内容を指定します。JSX を使用する場合、通常は `<div><span /></div>` のようにタグをネストすることで props として暗黙的に `children` を指定します。\n\n* `dangerouslySetInnerHTML`: `{ __html: '<p>some html</p>' }` という形式の、内部に生の HTML 文字列を含んだオブジェクト。DOM ノードの [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) プロパティを上書きし、渡された HTML を表示します。これは最大限に注意して使用する必要があります！ 内部の HTML が信頼できない場合（例えば、ユーザデータに基づいている場合）、[XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) 脆弱性を導入するリスクがあります。[`dangerouslySetInnerHTML` の使用について詳しく読む](#dangerously-setting-the-inner-html)\n\n* `ref`: [`useRef`](/reference/react/useRef) または [`createRef`](/reference/react/createRef) から得られる ref オブジェクト、または [`ref` コールバック関数](#ref-callback)、または[レガシー ref](https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs) 用の文字列。指定された ref にこのノードの DOM 要素が渡されます。[ref を使った DOM の操作について詳しく読む](#manipulating-a-dom-node-with-a-ref)\n\n* `suppressContentEditableWarning`: ブーリアン値。`true` の場合、React が `children` と `contentEditable={true}` を両方持つ要素（通常、これらは一緒に動作しません）に対して表示する警告を抑止します。`contentEditable` の内容を手動で管理するテキスト入力ライブラリを作成している場合に使用します。\n\n* `suppressHydrationWarning`: ブーリアン値。[サーバレンダリング](/reference/react-dom/server)を使用する場合、通常、サーバとクライアントが異なる内容をレンダーすると警告が表示されます。一部の稀なケース（タイムスタンプなど）では、完全な一致を保証することが非常に困難または不可能です。`suppressHydrationWarning` を `true` に設定すると、React はその要素の属性と内容の不一致について警告しなくなります。これは 1 レベルの深さまでしか機能せず、避難ハッチとして使用することを目的としています。過度な使用はしないでください。[ハイドレーションエラーの抑制について読む](/reference/react-dom/client/hydrateRoot#suppressing-unavoidable-hydration-mismatch-errors)\n\n* `style`: CSS スタイルを持つオブジェクト。例えば `{ fontWeight: 'bold', margin: 20 }` のようなものです。DOM の [`style`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) プロパティと同様に、CSS プロパティ名は `camelCase` で記述する必要があります。例えば `font-weight` ではなく `fontWeight` と書きます。値として文字列や数値を渡すことができます。数値を渡す場合、例えば `width: 100` のようにすると、React は自動的に `px`（\"ピクセル\"）を値に追加します。ただし、それが[単位のないプロパティ](https://github.com/facebook/react/blob/81d4ee9ca5c405dce62f64e61506b8e155f38d8d/packages/react-dom-bindings/src/shared/CSSProperty.js#L8-L57)の場合は除きます。`style` は、スタイルの値が事前に分からない動的なスタイルに対してのみ使用することを推奨します。他の場合は、`className` を用いてプレーンな CSS クラスを適用する方が効率的です。[`className` と `style` について詳しく読む](#applying-css-styles)\n\n以下の標準的な DOM プロパティは、すべての組み込みコンポーネントでサポートされています。\n\n* [`accessKey`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey): 文字列。要素のキーボードショートカットを指定します。[一般的には推奨されません](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey#accessibility_concerns)。\n* [`aria-*`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes): ARIA 属性を使用すると、この要素のアクセシビリティツリー情報を指定できます。完全なリファレンスについては [ARIA 属性](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes)を参照してください。React では、すべての ARIA 属性名は HTML と全く同じです。\n* [`autoCapitalize`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autocapitalize): 文字列。ユーザ入力の大文字への変換方法を制御します。\n* [`className`](https://developer.mozilla.org/en-US/docs/Web/API/Element/className): 文字列。要素の CSS クラス名を指定します。[CSS スタイルの適用についてもっと読む](#applying-css-styles)\n* [`contentEditable`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable): ブーリアン。`true` の場合、ブラウザはユーザがレンダーされた要素を直接編集することを許可します。これは [Lexical](https://lexical.dev/) のようなリッチテキスト入力ライブラリを実装するために使用されます。React は、`contentEditable={true}` の要素に React の子を渡そうとすると警告します。なぜなら、ユーザの編集後に React がその内容を更新できなくなるからです。\n* [`data-*`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*): データ属性を使用すると、要素にいくつかの文字列データを添付できます。例えば `data-fruit=\"banana\"` のようなものです。React では通常は props や state からデータを読み取るため、あまり使用されません。\n* [`dir`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir): `'ltr'` または `'rtl'` のいずれか。要素のテキスト方向を指定します。\n* [`draggable`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable): ブーリアン。要素がドラッグ可能かどうかを指定します。[HTML Drag and Drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) の一部です。\n* [`enterKeyHint`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/enterKeyHint): 文字列。仮想キーボードのエンターキーに対してどのアクションを表示するかを指定します。\n* [`htmlFor`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor): 文字列。[`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label) と [`<output>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output) に対して、[ラベルを何らかのコントロールに関連付ける](/reference/react-dom/components/input#providing-a-label-for-an-input) ために使います。[HTML の `for` 属性](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/for)と同じです。React では HTML 属性名ではなく、標準の DOM プロパティ名 (`htmlFor`) の方を使用します。\n* [`hidden`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden): ブール値または文字列。要素を非表示にするかどうかを指定します。\n* [`id`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id): 文字列。この要素の一意の識別子を指定します。これは後でこの要素を見つけたり他の要素と接続したりするために使用できます。同じコンポーネントの複数のインスタンス間での衝突を避けるため、[`useId`](/reference/react/useId) で生成してください。\n* [`is`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/is): 文字列。指定された場合、コンポーネントは[カスタム要素](/reference/react-dom/components#custom-html-elements)のように動作します。\n* [`inputMode`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode): 文字列。表示するキーボードの種類（例えばテキスト、数値、電話番号）を指定します。\n* [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop): 文字列。構造化データクローラに対して、この要素がどのプロパティを表しているのかを指定します。\n* [`lang`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang): 文字列。要素の言語を指定します。\n* [`onAnimationEnd`](https://developer.mozilla.org/en-US/docs/Web/API/Element/animationend_event): [`AnimationEvent` ハンドラ](#animationevent-handler)関数。CSS アニメーションが完了したときに発火します。\n* `onAnimationEndCapture`: `onAnimationEnd` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onAnimationIteration`](https://developer.mozilla.org/en-US/docs/Web/API/Element/animationiteration_event): [`AnimationEvent` ハンドラ](#animationevent-handler)関数。CSS アニメーションのイテレーションが終了し、別のイテレーションが始まるときに発火します。\n* `onAnimationIterationCapture`: `onAnimationIteration` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onAnimationStart`](https://developer.mozilla.org/en-US/docs/Web/API/Element/animationstart_event): [`AnimationEvent` ハンドラ](#animationevent-handler)関数。CSS アニメーションが開始するときに発火します。\n* `onAnimationStartCapture`: `onAnimationStart` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onAuxClick`](https://developer.mozilla.org/en-US/docs/Web/API/Element/auxclick_event): [`MouseEvent` ハンドラ](#mouseevent-handler)関数。非プライマリポインタボタンがクリックされたときに発火します。\n* `onAuxClickCapture`: `onAuxClick` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* `onBeforeInput`: [`InputEvent` ハンドラ](#inputevent-handler)関数。編集可能な要素の値が変更される前に発火します。React はまだネイティブの [`beforeinput`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/beforeinput_event) イベントを*使用しておらず*、他のイベントを使用してポリフィルを試みます。\n* `onBeforeInputCapture`: `onBeforeInput` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* `onBlur`: [`FocusEvent` ハンドラ](#focusevent-handler)関数。要素がフォーカスを失ったときに発火します。React の `onBlur` イベントは、ブラウザの組み込み [`blur`](https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event) イベントとは異なり、バブルします。\n* `onBlurCapture`: `onBlur` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onClick`](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event): [`MouseEvent` ハンドラ](#mouseevent-handler)関数。ポインティングデバイスのプライマリボタンがクリックされたときに発火します。\n* `onClickCapture`: `onClick` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onCompositionStart`](https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionstart_event): [`CompositionEvent` ハンドラ](#compositionevent-handler)関数。[インプットメソッドエディタ (IME)](https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor) が新しいコンポジションセッションを開始するときに発火します。\n* `onCompositionStartCapture`: `onCompositionStart` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onCompositionEnd`](https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionend_event): [`CompositionEvent` ハンドラ](#compositionevent-handler)関数。[インプットメソッドエディタ](https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor)がコンポジションセッションを完了またはキャンセルするときに発火します。\n* `onCompositionEndCapture`: `onCompositionEnd` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onCompositionUpdate`](https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionupdate_event): [`CompositionEvent` ハンドラ](#compositionevent-handler)関数。[インプットメソッドエディタ](https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor)が新しい文字を受け取るときに発火します。\n* `onCompositionUpdateCapture`: `onCompositionUpdate` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onContextMenu`](https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event): [`MouseEvent` ハンドラ](#mouseevent-handler)関数。ユーザがコンテクストメニューを開こうとすると発火します。\n* `onContextMenuCapture`: `onContextMenu` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onCopy`](https://developer.mozilla.org/en-US/docs/Web/API/Element/copy_event): [`ClipboardEvent` ハンドラ](#clipboardevent-handler)関数。ユーザが何かをクリップボードにコピーしようとすると発火します。\n* `onCopyCapture`: `onCopy` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onCut`](https://developer.mozilla.org/en-US/docs/Web/API/Element/cut_event): [`ClipboardEvent` ハンドラ](#clipboardevent-handler)関数。ユーザが何かをクリップボードに切り取ろうとすると発火します。\n* `onCutCapture`: `onCut` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* `onDoubleClick`: [`MouseEvent` ハンドラ](#mouseevent-handler)関数。ユーザがダブルクリックすると発火します。ブラウザの [`dblclick` イベント](https://developer.mozilla.org/en-US/docs/Web/API/Element/dblclick_event)に対応します。\n* `onDoubleClickCapture`: `onDoubleClick` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onDrag`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drag_event): [`DragEvent` ハンドラ](#dragevent-handler)関数。ユーザが何かをドラッグしている間発火します。\n* `onDragCapture`: `onDrag` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onDragEnd`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragend_event): [`DragEvent` ハンドラ](#dragevent-handler)関数。ユーザが何かのドラッグを止めると発火します。\n* `onDragEndCapture`: `onDragEnd` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onDragEnter`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragenter_event): [`DragEvent` ハンドラ](#dragevent-handler)関数。ドラッグされたコンテンツが有効なドロップターゲットに入ると発火します。\n* `onDragEnterCapture`: `onDragEnter` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onDragOver`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragover_event): [`DragEvent` ハンドラ](#dragevent-handler)関数。ドラッグされたコンテンツが有効なドロップターゲット上でドラッグされている間発火します。ドロップを許可するためにはここで `e.preventDefault()` を呼び出す必要があります。\n* `onDragOverCapture`: `onDragOver` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onDragStart`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragstart_event): [`DragEvent` ハンドラ](#dragevent-handler)関数。ユーザが要素のドラッグを開始すると発火します。\n* `onDragStartCapture`: `onDragStart` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onDrop`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drop_event): [`DragEvent` ハンドラ](#dragevent-handler)関数。何かが有効なドロップターゲットにドロップされたときに発火します。\n* `onDropCapture`: `onDrop` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* `onFocus`: [`FocusEvent` ハンドラ](#focusevent-handler)関数。要素がフォーカスを受け取ったときに発火します。ブラウザの組み込み [`focus`](https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event) イベントとは異なり、React の `onFocus` イベントはバブルします。\n* `onFocusCapture`: `onFocus` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onGotPointerCapture`](https://developer.mozilla.org/en-US/docs/Web/API/Element/gotpointercapture_event): [`PointerEvent` ハンドラ](#pointerevent-handler)関数。要素がプログラム的にポインタをキャプチャしたときに発火します。\n* `onGotPointerCaptureCapture`: `onGotPointerCapture` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onKeyDown`](https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event): [`KeyboardEvent` ハンドラ](#keyboardevent-handler)関数。キーが押されたときに発火します。\n* `onKeyDownCapture`: `onKeyDown` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onKeyPress`](https://developer.mozilla.org/en-US/docs/Web/API/Element/keypress_event): [`KeyboardEvent` ハンドラ](#keyboardevent-handler)関数。非推奨です。代わりに `onKeyDown` または `onBeforeInput` を使用してください。\n* `onKeyPressCapture`: `onKeyPress` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onKeyUp`](https://developer.mozilla.org/en-US/docs/Web/API/Element/keyup_event): [`KeyboardEvent` ハンドラ](#keyboardevent-handler)関数。キーが離されたときに発火します。\n* `onKeyUpCapture`: `onKeyUp` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onLostPointerCapture`](https://developer.mozilla.org/en-US/docs/Web/API/Element/lostpointercapture_event): [`PointerEvent` ハンドラ](#pointerevent-handler)関数。要素がポインタのキャプチャを停止したときに発火します。\n* `onLostPointerCaptureCapture`: `onLostPointerCapture` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onMouseDown`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mousedown_event): [`MouseEvent` ハンドラ](#mouseevent-handler)関数。ポインタが押されたときに発火します。\n* `onMouseDownCapture`: `onMouseDown` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onMouseEnter`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseenter_event): [`MouseEvent` ハンドラ](#mouseevent-handler)関数。ポインタが要素の内部に移動したときに発火します。キャプチャフェーズはありません。代わりに、`onMouseLeave` と `onMouseEnter` は、離れる要素から入る要素へと伝播します。\n* [`onMouseLeave`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event): [`MouseEvent` ハンドラ](#mouseevent-handler)関数。ポインタが要素の外に移動したときに発火します。キャプチャフェーズはありません。代わりに、`onMouseLeave` と `onMouseEnter` は、離れる要素から入る要素へと伝播します。\n* [`onMouseMove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event): [`MouseEvent` ハンドラ](#mouseevent-handler)関数。ポインタの座標が変わったときに発火します。\n* `onMouseMoveCapture`: `onMouseMove` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onMouseOut`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseout_event): [`MouseEvent` ハンドラ](#mouseevent-handler)関数。ポインタが要素の外に移動したとき、または子要素に移動したときに発火します。\n* `onMouseOutCapture`: `onMouseOut` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onMouseUp`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseup_event): [`MouseEvent` ハンドラ](#mouseevent-handler)関数。ポインタがリリースされたときに発火します。\n* `onMouseUpCapture`: `onMouseUp` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onPointerCancel`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointercancel_event): [`PointerEvent` ハンドラ](#pointerevent-handler)関数。ブラウザがポインタによるインタラクションをキャンセルしたときに発火します。\n* `onPointerCancelCapture`: `onPointerCancel` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onPointerDown`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerdown_event): [`PointerEvent` ハンドラ](#pointerevent-handler)関数。ポインタがアクティブになったときに発火します。\n* `onPointerDownCapture`: `onPointerDown` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onPointerEnter`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerenter_event): [`PointerEvent` ハンドラ](#pointerevent-handler)関数。ポインタが要素の内部に移動したときに発火します。キャプチャフェーズはありません。代わりに、`onPointerLeave` と `onPointerEnter` は、離れる要素から入る要素へと伝播します。\n* [`onPointerLeave`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerleave_event): [`PointerEvent` ハンドラ](#pointerevent-handler)関数。ポインタが要素の外に移動したときに発火します。キャプチャフェーズはありません。代わりに、`onPointerLeave` と `onPointerEnter` は、離れる要素から入る要素へと伝播します。\n* [`onPointerMove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointermove_event): [`PointerEvent` ハンドラ](#pointerevent-handler)関数。ポインタの座標が変わったときに発火します。\n* `onPointerMoveCapture`: `onPointerMove` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onPointerOut`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerout_event): [`PointerEvent` ハンドラ](#pointerevent-handler)関数。ポインタが要素の外に移動したとき、ポインタのインタラクションがキャンセルされたとき、その他[いくつかの他の理由](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerout_event)で発火します。\n* `onPointerOutCapture`: `onPointerOut` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onPointerUp`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerup_event): [`PointerEvent` ハンドラ](#pointerevent-handler)関数。ポインタがもはやアクティブでなくなったときに発火します。\n* `onPointerUpCapture`: `onPointerUp` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onPaste`](https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event): [`ClipboardEvent` ハンドラ](#clipboardevent-handler)関数。ユーザがクリップボードから何かを貼り付けようとすると発火します。\n* `onPasteCapture`: `onPaste` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onScroll`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scroll_event): [`Event` ハンドラ](#event-handler)関数。要素がスクロールされたときに発火します。このイベントはバブルしません。\n* `onScrollCapture`: `onScroll` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onSelect`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/select_event): [`Event` ハンドラ](#event-handler)関数。入力フィールドなどの編集可能な要素内の選択が変更された後に発火します。React は `onSelect` イベントを `contentEditable={true}` の要素でも動作するように拡張します。さらに、React は空の選択と編集（選択に影響を与える可能性があります）に対しても発火するように拡張します。\n* `onSelectCapture`: `onSelect` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onTouchCancel`](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchcancel_event): [`TouchEvent` ハンドラ](#touchevent-handler)関数。ブラウザがタッチインタラクションをキャンセルすると発火します。\n* `onTouchCancelCapture`: `onTouchCancel` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onTouchEnd`](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchend_event): [`TouchEvent` ハンドラ](#touchevent-handler)関数。1 つ以上のタッチポイントが削除されると発火します。\n* `onTouchEndCapture`: `onTouchEnd` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onTouchMove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchmove_event): [`TouchEvent` ハンドラ](#touchevent-handler)関数。1 つ以上のタッチポイントが移動すると発火します。\n* `onTouchMoveCapture`: `onTouchMove` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onTouchStart`](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchstart_event): [`TouchEvent` ハンドラ](#touchevent-handler)関数。1 つ以上のタッチポイントが配置されると発火します。\n* `onTouchStartCapture`: `onTouchStart` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onTransitionEnd`](https://developer.mozilla.org/en-US/docs/Web/API/Element/transitionend_event): [`TransitionEvent` ハンドラ](#transitionevent-handler)関数。CSS のトランジションが完了すると発火します。\n* `onTransitionEndCapture`: `onTransitionEnd` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onWheel`](https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event): [`WheelEvent` ハンドラ](#wheelevent-handler)関数。ユーザがホイールボタンを回転させると発火します。\n* `onWheelCapture`: `onWheel` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`role`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles): 文字列。支援技術に対して要素の役割を明示的に指定します。\n* [`slot`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles): 文字列。シャドウ DOM を使用する際のスロット名を指定します。React では、同等のパターンは通常 JSX を props として渡すことで達成されます。例えば `<Layout left={<Sidebar />} right={<Content />} />` のようになります。\n* [`spellCheck`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/spellcheck): ブーリアンまたは null。明示的に `true` または `false` に設定すると、スペルチェックを有効または無効にします。\n* [`tabIndex`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex): 数値。デフォルトの Tab ボタンの動作を上書きします。[`-1` と `0` 以外の値の使用は避けてください](https://www.tpgi.com/using-the-tabindex-attribute/)。\n* [`title`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title): 文字列。要素のツールチップテキストを指定します。\n* [`translate`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/translate): `'yes'` または `'no'` のいずれか。`'no'` を渡すと、要素の内容の翻訳が除外されます。\n\nカスタム属性も props として渡すことができます。例えば `mycustomprop=\"someValue\"` のようになります。これはサードパーティのライブラリと統合する際に便利です。カスタム属性名は小文字でなければならず、`on` で始まってはいけません。値は文字列に変換されます。`null` または `undefined` を渡すと、カスタム属性は削除されます。\n\n以下のイベントは [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) 要素のみで発火します：\n\n* [`onReset`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset_event): [`Event` ハンドラ](#event-handler)関数。フォームがリセットされたときに発火します。\n* `onResetCapture`: `onReset` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onSubmit`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event): [`Event` ハンドラ](#event-handler)関数。フォームが送信されたときに発火します。\n* `onSubmitCapture`: `onSubmit` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n\n以下のイベントは、[`<dialog>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) 要素に対してのみ発火します。ブラウザのイベントとは異なり、React ではバブルします。\n\n* [`onCancel`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/cancel_event): [`Event` ハンドラ](#event-handler)関数。ユーザがダイアログをキャンセルしようとしたときに発火します。\n* `onCancelCapture`: `onCancel` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onClose`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/close_event): [`Event` ハンドラ](#event-handler)関数。ダイアログが閉じられたときに発火します。\n* `onCloseCapture`: `onClose` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n\n以下のイベントは、[`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details) 要素に対してのみ発火します。ブラウザのイベントとは異なり、React ではバブルします。\n\n* [`onToggle`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDetailsElement/toggle_event): [`Event` ハンドラ](#event-handler)関数。ユーザが詳細を切り替えたときに発火します。\n* `onToggleCapture`: `onToggle` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n\nこれらのイベントは、[`<img>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img)、[`<iframe>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe)、[`<object>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object)、[`<embed>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed)、[`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)、および [SVG の `<image>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_Image_Tag) 要素に対して発火します。ブラウザのイベントとは異なり、React ではバブルします。\n\n* `onLoad`: [`Event` ハンドラ](#event-handler)関数。リソースが読み込まれたときに発火します。\n* `onLoadCapture`: `onLoad` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onError`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error_event): [`Event` ハンドラ](#event-handler)関数。リソースが読み込めなかったときに発火します。\n* `onErrorCapture`: `onError` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n\n以下のイベントは、[`<audio>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio) や [`<video>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video) などのリソースに対して発火します。ブラウザのイベントとは異なり、React ではこれらのイベントはバブルします。\n\n* [`onAbort`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/abort_event): [`Event` ハンドラ](#event-handler)関数。リソースが完全には読み込まれなかったが、エラーによるものではない場合に発火します。\n* `onAbortCapture`: `onAbort` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onCanPlay`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplay_event): [`Event` ハンドラ](#event-handler)関数。再生を開始するのに十分なデータがあるが、バッファリングのための停止なしに最後まで再生するのに十分なデータはない場合に発火します。\n* `onCanPlayCapture`: `onCanPlay` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onCanPlayThrough`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplaythrough_event): [`Event` ハンドラ](#event-handler)関数。バッファリングのための停止をせずとも再生を開始して最後まで再生できると考えられる十分なデータがある場合に発火します。\n* `onCanPlayThroughCapture`: `onCanPlayThrough` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onDurationChange`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/durationchange_event)：[`Event` ハンドラ](#event-handler)関数。メディアの再生時間が更新されたときに発火します。\n* `onDurationChangeCapture`: `onDurationChange` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onEmptied`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/emptied_event): [`Event` ハンドラ](#event-handler)関数。メディアが空になったときに発火します。\n* `onEmptiedCapture`: `onEmptied` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onEncrypted`](https://w3c.github.io/encrypted-media/#dom-evt-encrypted): [`Event` ハンドラ](#event-handler)関数。ブラウザが暗号化されたメディアに遭遇したときに発火します。\n* `onEncryptedCapture`: `onEncrypted` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onEnded`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ended_event): [`Event` ハンドラ](#event-handler)関数。再生するものが何も残っていないために再生が停止したときに発火します。\n* `onEndedCapture`: `onEnded` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onError`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error_event): [`Event` ハンドラ](#event-handler)関数。リソースが読み込めなかったときに発火します。\n* `onErrorCapture`: `onError` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onLoadedData`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadeddata_event): [`Event` ハンドラ](#event-handler)関数。現在の再生フレームが読み込まれたときに発火します。\n* `onLoadedDataCapture`: `onLoadedData` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onLoadedMetadata`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadedmetadata_event): [`Event` ハンドラ](#event-handler)関数。メタデータが読み込まれたときに発火します。\n* `onLoadedMetadataCapture`: `onLoadedMetadata` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onLoadStart`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadstart_event): [`Event` ハンドラ](#event-handler)関数。ブラウザがリソースの読み込みを開始したときに発火します。\n* `onLoadStartCapture`: `onLoadStart` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onPause`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause_event): [`Event` ハンドラ](#event-handler)関数。メディアが一時停止したときに発火します。\n* `onPauseCapture`: `onPause` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onPlay`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play_event): [`Event` ハンドラ](#event-handler)関数。メディアが一時停止を解除したときに発火します。\n* `onPlayCapture`: `onPlay` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onPlaying`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/playing_event): [`Event` ハンドラ](#event-handler)関数。メディアが再生または再開したときに発火します。\n* `onPlayingCapture`: `onPlaying` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onProgress`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/progress_event): [`Event` ハンドラ](#event-handler)関数。リソースの読み込み中に定期的に発火します。\n* `onProgressCapture`: `onProgress` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onRateChange`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ratechange_event): [`Event` ハンドラ](#event-handler)関数。再生レートが変更されたときに発火します。\n* `onRateChangeCapture`: `onRateChange` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* `onResize`: [`Event` ハンドラ](#event-handler)関数。ビデオのサイズが変更されたときに発火します。\n* `onResizeCapture`: `onResize` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onSeeked`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeked_event): [`Event` ハンドラ](#event-handler)関数。シーク操作が完了したときに発火します。\n* `onSeekedCapture`: `onSeeked` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onSeeking`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeking_event): [`Event` ハンドラ](#event-handler)関数。シーク操作を開始したときに発火します。\n* `onSeekingCapture`: `onSeeking` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onStalled`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/stalled_event): [`Event` ハンドラ](#event-handler)関数。ブラウザがデータを待っているが、ロードが進まないときに発火します。\n* `onStalledCapture`: `onStalled` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onSuspend`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/suspend_event): [`Event` ハンドラ](#event-handler)関数。リソースのロードが中断されたときに発火します。\n* `onSuspendCapture`: `onSuspend` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onTimeUpdate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/timeupdate_event): [`Event` ハンドラ](#event-handler)関数。現在の再生時間が更新されたときに発火します。\n* `onTimeUpdateCapture`: `onTimeUpdate` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onVolumeChange`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/volumechange_event): [`Event` ハンドラ](#event-handler)関数。ボリュームが変更されたときに発火します。\n* `onVolumeChangeCapture`: `onVolumeChange` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onWaiting`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/waiting_event): [`Event` ハンドラ](#event-handler)関数。一時的なデータ不足により再生が停止したときに発火します。\n* `onWaitingCapture`: `onWaiting` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n\n#### 注意点 {/*common-caveats*/}\n\n- `children` と `dangerouslySetInnerHTML` を同時に渡すことはできません。\n- 一部のイベント（`onAbort` や `onLoad` など）はブラウザではバブルしませんが、React ではバブルします。\n\n---\n\n### `ref` コールバック関数 {/*ref-callback*/}\n\n[`useRef`](/reference/react/useRef#manipulating-the-dom-with-a-ref) などが返す ref オブジェクトの代わりに、`ref` 属性に関数を渡すことができます。\n\n```js\n<div ref={(node) => {\n  console.log('Attached', node);\n\n  return () => {\n    console.log('Clean up', node)\n  }\n}}>\n```\n\n[`ref` コールバックを使用した例を見る](/learn/manipulating-the-dom-with-refs#how-to-manage-a-list-of-refs-using-a-ref-callback)\n\n`<div>` DOM ノードが画面に追加されると、React はその DOM `node` を引数として `ref` コールバックを呼び出します。`<div>` DOM ノードが削除されると、React はコールバックが返したクリーンアップ関数を呼び出します。\n\nReact は、*異なる* `ref` コールバックが渡された場合も `ref` コールバックを呼び出します。上記の例では、`(node) => { ... }` は毎回のレンダーで異なる関数です。コンポーネントが再レンダーされると、*前*の関数が `null` を引数として呼び出され、*次*の関数が DOM ノードを引数として呼び出されます。\n\n#### 引数 {/*ref-callback-parameters*/}\n\n* `node`: DOM ノード。ref がアタッチされるときに React は DOM ノードを渡します。毎回のレンダーで `ref` コールバックに同じ関数参照を渡さない限り、コンポーネントのレンダー毎に一時的なクリーンアップと再作成が繰り返されることになります。\n\n<Note>\n\n#### `ref` コールバックのクリーンアップは React 19 で追加されました {/*react-19-added-cleanup-functions-for-ref-callbacks*/}\n\n後方互換性をサポートするため、`ref` コールバックからクリーンアップ関数が返されない場合、`ref` がデタッチされると `node` を `null` としてコールバックが呼び出されます。この動作は将来のバージョンで削除される予定です。\n\n</Note>\n\n#### 返り値 {/*returns*/}\n\n* **省略可能** クリーンアップ関数：`ref` がデタッチされると、React はクリーンアップ関数を呼び出します。ref コールバックが関数を返さない場合、ref がデタッチされたときに、React は null を引数として再度コールバックを呼び出しますが、この動作は将来のバージョンで削除される予定です。\n\n#### 注意事項 {/*caveats*/}\n\n* Strict Mode が有効の場合、React は最終的なセットアップの前に、**開発時専用のセットアップ＋クリーンアップのサイクルを追加で 1 回実行します**。これはクリーンアップのロジックがセットアップロジックに「鏡のように対応」しており、セットアップが行っていることが何であれそれの停止ないし取り消しを行っている、ということを保証するために行う、ストレステストです。問題が生じている場合は正しくクリーンアップ関数を実装してください。\n* *異なる* `ref` のコールバックを渡した場合、React は*古い*コールバックのクリーンアップが存在する場合それをまず実行します。クリーンアップ関数が定義されていない場合は、`ref` コールバック自体が `null` を引数にして呼び出されます。その後*新しい*関数が、DOM ノードを引数にして呼び出されます。\n\n---\n\n### React イベントオブジェクト {/*react-event-object*/}\n\nイベントハンドラは *React イベントオブジェクト*を受け取ります。これは \"合成イベント (synthetic event)\" とも呼ばれることがあります。\n\n```js\n<button onClick={e => {\n  console.log(e); // React event object\n}} />\n```\n\nこれは対応する元の DOM イベントと同じ標準に準拠していますが、一部のブラウザ間の非一致を修正したものです。\n\n一部の React イベントはブラウザのネイティブイベントに直接マッピングされません。例えば `onMouseLeave` においては `e.nativeEvent` は `mouseout` イベントになっています。具体的なマッピングは公開 API の一部ではなく、将来変更される可能性があります。何らかの理由で元となるブラウザイベントが必要な場合は、`e.nativeEvent` から読み取ってください。\n\n#### プロパティ {/*react-event-object-properties*/}\n\nReact イベントオブジェクトは、標準の [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) プロパティの一部を実装しています。\n\n* [`bubbles`](https://developer.mozilla.org/en-US/docs/Web/API/Event/bubbles): ブーリアン。イベントが DOM をバブルするかどうかを返します。\n* [`cancelable`](https://developer.mozilla.org/en-US/docs/Web/API/Event/cancelable): ブーリアン。イベントがキャンセル可能かどうかを返します。\n* [`currentTarget`](https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget): DOM ノード。React ツリー内で現在のハンドラがアタッチされているノードを返します。\n* [`defaultPrevented`](https://developer.mozilla.org/en-US/docs/Web/API/Event/defaultPrevented): ブーリアン。`preventDefault` が呼び出されたかどうかを返します。\n* [`eventPhase`](https://developer.mozilla.org/en-US/docs/Web/API/Event/eventPhase): 数値。イベントが現在どのフェーズにあるかを返します。\n* [`isTrusted`](https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted): ブーリアン。イベントの発生理由がユーザによるものかどうかを返します。\n* [`target`](https://developer.mozilla.org/en-US/docs/Web/API/Event/target): DOM ノード。イベントが発生したノード（遠い子孫要素のこともある）を返します。\n* [`timeStamp`](https://developer.mozilla.org/en-US/docs/Web/API/Event/timeStamp): 数値。イベントが発生した時間を返します。\n\nさらに、React イベントオブジェクトは以下のプロパティを提供します。\n\n* `nativeEvent`: DOM の [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event)。オリジナルのブラウザイベントオブジェクト。\n\n#### メソッド {/*react-event-object-methods*/}\n\nReact イベントオブジェクトは、標準の [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) メソッドの一部を実装しています。\n\n* [`preventDefault()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault): イベントのデフォルトのブラウザアクションを防ぎます。\n* [`stopPropagation()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation): React ツリーを通じたイベントの伝播を停止します。\n\nさらに、React イベントオブジェクトは以下のメソッドを提供します。\n\n* `isDefaultPrevented()`: `preventDefault` が呼び出されたかどうかを示すブーリアンを返します。\n* `isPropagationStopped()`: `stopPropagation` が呼び出されたかどうかを示すブーリアンを返します。\n* `persist()`: React DOM では使用されません。React Native では、イベントのプロパティを読み取る際にこのイベントを呼び出します。\n* `isPersistent()`: React DOM では使用されません。React Native では、`persist` が呼び出されたかどうかを返します。\n\n#### 注意点 {/*react-event-object-caveats*/}\n\n* `currentTarget`、`eventPhase`、`target`、`type` の値は、あなたの React コードで期待される通りの値を反映しています。内部的には、React はルートにイベントハンドラをアタッチするのですが、この事実は React イベントオブジェクトには反映されません。例えば、`e.currentTarget` は元となる `e.nativeEvent.currentTarget` とは同じでないかもしれません。ポリフィルされたイベントでは、`e.type`（React イベントのタイプ）は `e.nativeEvent.type`（元イベントのタイプ）と異なることがあります。\n\n---\n\n### `AnimationEvent` ハンドラ関数 {/*animationevent-handler*/}\n\n[CSS アニメーション](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations)イベントのイベントハンドラタイプです。\n\n```js\n<div\n  onAnimationStart={e => console.log('onAnimationStart')}\n  onAnimationIteration={e => console.log('onAnimationIteration')}\n  onAnimationEnd={e => console.log('onAnimationEnd')}\n/>\n```\n\n#### 引数 {/*animationevent-handler-parameters*/}\n\n* `e`: 以下の追加の [`AnimationEvent`](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent) プロパティを持つ [React イベントオブジェクト](#react-event-object)。\n  * [`animationName`](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent/animationName)\n  * [`elapsedTime`](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent/elapsedTime)\n  * [`pseudoElement`](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent/pseudoElement)\n\n---\n\n### `ClipboardEvent` ハンドラ関数 {/*clipboadevent-handler*/}\n\n[Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API) イベントのイベントハンドラタイプです。\n\n```js\n<input\n  onCopy={e => console.log('onCopy')}\n  onCut={e => console.log('onCut')}\n  onPaste={e => console.log('onPaste')}\n/>\n```\n\n#### 引数 {/*clipboadevent-handler-parameters*/}\n\n* `e`: 以下の追加の [`ClipboardEvent`](https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent) プロパティを持つ [React イベントオブジェクト](#react-event-object)。\n\n* [`clipboardData`](https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent/clipboardData)\n\n---\n\n### `CompositionEvent` ハンドラ関数 {/*compositionevent-handler*/}\n\n[インプットメソッドエディタ (IME)](https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor) イベントのためのイベントハンドラタイプです。\n\n```js\n<input\n  onCompositionStart={e => console.log('onCompositionStart')}\n  onCompositionUpdate={e => console.log('onCompositionUpdate')}\n  onCompositionEnd={e => console.log('onCompositionEnd')}\n/>\n```\n\n#### 引数 {/*compositionevent-handler-parameters*/}\n\n* `e`: 以下の追加の [`CompositionEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent) プロパティを持つ [React イベントオブジェクト](#react-event-object)。\n  * [`data`](https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent/data)\n\n---\n\n### `DragEvent` ハンドラ関数 {/*dragevent-handler*/}\n\n[HTML ドラッグ & ドロップ API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) イベントのためのイベントハンドラタイプです。\n\n```js\n<>\n  <div\n    draggable={true}\n    onDragStart={e => console.log('onDragStart')}\n    onDragEnd={e => console.log('onDragEnd')}\n  >\n    Drag source\n  </div>\n\n  <div\n    onDragEnter={e => console.log('onDragEnter')}\n    onDragLeave={e => console.log('onDragLeave')}\n    onDragOver={e => { e.preventDefault(); console.log('onDragOver'); }}\n    onDrop={e => console.log('onDrop')}\n  >\n    Drop target\n  </div>\n</>\n```\n\n#### 引数 {/*dragevent-handler-parameters*/}\n\n* `e`: 以下の追加の [`DragEvent`](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent) プロパティを持つ [React イベントオブジェクト](#react-event-object)。\n  * [`dataTransfer`](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/dataTransfer)\n\n  また、継承元である [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) のプロパティも含みます：\n\n  * [`altKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/altKey)\n  * [`button`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button)\n  * [`buttons`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons)\n  * [`ctrlKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/ctrlKey)\n  * [`clientX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX)\n  * [`clientY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientY)\n  * [`getModifierState(key)`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/getModifierState)\n  * [`metaKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/metaKey)\n  * [`movementX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX)\n  * [`movementY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementY)\n  * [`pageX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX)\n  * [`pageY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageY)\n  * [`relatedTarget`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/relatedTarget)\n  * [`screenX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenX)\n  * [`screenY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenY)\n  * [`shiftKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/shiftKey)\n\n  また、継承元である [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) のプロパティも含みます：\n\n  * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail)\n  * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view)\n\n---\n\n### `FocusEvent` ハンドラ関数 {/*focusevent-handler*/}\n\nフォーカスイベントのためのイベントハンドラタイプです。\n\n```js\n<input\n  onFocus={e => console.log('onFocus')}\n  onBlur={e => console.log('onBlur')}\n/>\n```\n\n[例を見る](#handling-focus-events)\n\n#### 引数 {/*focusevent-handler-parameters*/}\n\n* `e`: 以下の追加の [`FocusEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent) プロパティを持つ [React イベントオブジェクト](#react-event-object)。\n  * [`relatedTarget`](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget)\n\n  また、継承元である [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) のプロパティも含みます：\n\n  * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail)\n  * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view)\n\n---\n\n### `Event` ハンドラ関数 {/*event-handler*/}\n\n一般的なイベントのためのイベントハンドラタイプです。\n\n#### 引数 {/*event-handler-parameters*/}\n\n* `e`: 追加のプロパティがない [React イベントオブジェクト](#react-event-object)。\n\n---\n\n### `InputEvent` ハンドラ関数 {/*inputevent-handler*/}\n\n`onBeforeInput` イベントのためのイベントハンドラタイプです。\n\n```js\n<input onBeforeInput={e => console.log('onBeforeInput')} />\n```\n\n#### 引数 {/*inputevent-handler-parameters*/}\n\n* `e`: 以下の追加の [`InputEvent`](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent) プロパティを持つ [React イベントオブジェクト](#react-event-object)。\n  * [`data`](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/data)\n\n---\n\n### `KeyboardEvent` ハンドラ関数 {/*keyboardevent-handler*/}\n\nキーボードイベントのためのイベントハンドラタイプです。\n\n```js\n<input\n  onKeyDown={e => console.log('onKeyDown')}\n  onKeyUp={e => console.log('onKeyUp')}\n/>\n```\n\n[例を見る](#handling-keyboard-events)\n\n#### 引数 {/*keyboardevent-handler-parameters*/}\n\n* `e`: 以下の追加の [`KeyboardEvent`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent) プロパティを持つ [React イベントオブジェクト](#react-event-object)。\n  * [`altKey`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/altKey)\n  * [`charCode`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/charCode)\n  * [`code`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code)\n  * [`ctrlKey`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/ctrlKey)\n  * [`getModifierState(key)`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState)\n  * [`key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key)\n  * [`keyCode`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode)\n  * [`locale`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/locale)\n  * [`metaKey`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/metaKey)\n  * [`location`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/location)\n  * [`repeat`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat)\n  * [`shiftKey`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/shiftKey)\n  * [`which`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/which)\n\n  また、継承元である [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) のプロパティも含みます：\n\n  * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail)\n  * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view)\n\n---\n\n### `MouseEvent` ハンドラ関数 {/*mouseevent-handler*/}\n\nマウスイベントのためのイベントハンドラタイプです。\n\n```js\n<div\n  onClick={e => console.log('onClick')}\n  onMouseEnter={e => console.log('onMouseEnter')}\n  onMouseOver={e => console.log('onMouseOver')}\n  onMouseDown={e => console.log('onMouseDown')}\n  onMouseUp={e => console.log('onMouseUp')}\n  onMouseLeave={e => console.log('onMouseLeave')}\n/>\n```\n\n[例を見る](#handling-mouse-events)\n\n#### 引数 {/*mouseevent-handler-parameters*/}\n\n* `e`: 以下の追加の [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) プロパティを持つ [React イベントオブジェクト](#react-event-object)。\n  * [`altKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/altKey)\n  * [`button`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button)\n  * [`buttons`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons)\n  * [`ctrlKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/ctrlKey)\n  * [`clientX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX)\n  * [`clientY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientY)\n  * [`getModifierState(key)`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/getModifierState)\n  * [`metaKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/metaKey)\n  * [`movementX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX)\n  * [`movementY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementY)\n  * [`pageX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX)\n  * [`pageY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageY)\n  * [`relatedTarget`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/relatedTarget)\n  * [`screenX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenX)\n  * [`screenY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenY)\n  * [`shiftKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/shiftKey)\n\n  また、継承元である [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) のプロパティも含みます：\n\n  * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail)\n  * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view)\n\n---\n\n### `PointerEvent` ハンドラ関数 {/*pointerevent-handler*/}\n\n[ポインタイベント](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events)のためのイベントハンドラタイプです。\n\n```js\n<div\n  onPointerEnter={e => console.log('onPointerEnter')}\n  onPointerMove={e => console.log('onPointerMove')}\n  onPointerDown={e => console.log('onPointerDown')}\n  onPointerUp={e => console.log('onPointerUp')}\n  onPointerLeave={e => console.log('onPointerLeave')}\n/>\n```\n\n[例を見る](#handling-pointer-events)。\n\n#### 引数 {/*pointerevent-handler-parameters*/}\n\n* `e`: 以下の追加の [`PointerEvent`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) プロパティを持つ [React イベントオブジェクト](#react-event-object)。\n  * [`height`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height)\n  * [`isPrimary`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary)\n  * [`pointerId`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId)\n  * [`pointerType`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType)\n  * [`pressure`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure)\n  * [`tangentialPressure`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tangentialPressure)\n  * [`tiltX`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX)\n  * [`tiltY`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY)\n  * [`twist`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/twist)\n  * [`width`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width)\n\n  また、継承元である [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) のプロパティも含みます：\n\n  * [`altKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/altKey)\n  * [`button`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button)\n  * [`buttons`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons)\n  * [`ctrlKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/ctrlKey)\n  * [`clientX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX)\n  * [`clientY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientY)\n  * [`getModifierState(key)`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/getModifierState)\n  * [`metaKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/metaKey)\n  * [`movementX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX)\n  * [`movementY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementY)\n  * [`pageX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX)\n  * [`pageY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageY)\n  * [`relatedTarget`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/relatedTarget)\n  * [`screenX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenX)\n  * [`screenY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenY)\n  * [`shiftKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/shiftKey)\n\n  また、継承元である [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) のプロパティも含みます：\n\n  * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail)\n  * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view)\n\n---\n\n### `TouchEvent` ハンドラ関数 {/*touchevent-handler*/}\n\n[タッチイベント](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events)のためのイベントハンドラタイプです。\n\n```js\n<div\n  onTouchStart={e => console.log('onTouchStart')}\n  onTouchMove={e => console.log('onTouchMove')}\n  onTouchEnd={e => console.log('onTouchEnd')}\n  onTouchCancel={e => console.log('onTouchCancel')}\n/>\n```\n\n#### 引数 {/*touchevent-handler-parameters*/}\n\n* `e`: 以下の追加の [`TouchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent) プロパティを持つ [React イベントオブジェクト](#react-event-object)。\n  * [`altKey`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/altKey)\n  * [`ctrlKey`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/ctrlKey)\n  * [`changedTouches`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/changedTouches)\n  * [`getModifierState(key)`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/getModifierState)\n  * [`metaKey`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/metaKey)\n  * [`shiftKey`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/shiftKey)\n  * [`touches`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/touches)\n  * [`targetTouches`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/targetTouches)\n  \n  また、継承元である [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) のプロパティも含みます：\n\n  * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail)\n  * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view)\n\n---\n\n### `TransitionEvent` ハンドラ関数 {/*transitionevent-handler*/}\n\nCSS トランジションイベントのためのイベントハンドラタイプです。\n\n```js\n<div\n  onTransitionEnd={e => console.log('onTransitionEnd')}\n/>\n```\n\n#### 引数 {/*transitionevent-handler-parameters*/}\n\n* `e`: 以下の追加の [`TransitionEvent`](https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent) プロパティを持つ [React イベントオブジェクト](#react-event-object)。\n  * [`elapsedTime`](https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent/elapsedTime)\n  * [`propertyName`](https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent/propertyName)\n  * [`pseudoElement`](https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent/pseudoElement)\n\n---\n\n### `UIEvent` ハンドラ関数 {/*uievent-handler*/}\n\n一般的な UI イベントのためのイベントハンドラタイプです。\n\n```js\n<div\n  onScroll={e => console.log('onScroll')}\n/>\n```\n\n#### 引数 {/*uievent-handler-parameters*/}\n\n* `e`: 以下の追加の [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) プロパティを持つ [React イベントオブジェクト](#react-event-object)。\n  * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail)\n  * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view)\n\n---\n\n### `WheelEvent` ハンドラ関数 {/*wheelevent-handler*/}\n\n`onWheel` イベントのためのイベントハンドラタイプです。\n\n```js\n<div\n  onWheel={e => console.log('onWheel')}\n/>\n```\n\n#### 引数 {/*wheelevent-handler-parameters*/}\n\n* `e`: 以下の追加の [`WheelEvent`](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent) プロパティを持つ [React イベントオブジェクト](#react-event-object)。\n  * [`deltaMode`](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaMode)\n  * [`deltaX`](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaX)\n  * [`deltaY`](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY)\n  * [`deltaZ`](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaZ)\n\n\n  また、継承元である [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) のプロパティも含みます：\n\n  * [`altKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/altKey)\n  * [`button`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button)\n  * [`buttons`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons)\n  * [`ctrlKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/ctrlKey)\n  * [`clientX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX)\n  * [`clientY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientY)\n  * [`getModifierState(key)`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/getModifierState)\n  * [`metaKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/metaKey)\n  * [`movementX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX)\n  * [`movementY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementY)\n  * [`pageX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX)\n  * [`pageY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageY)\n  * [`relatedTarget`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/relatedTarget)\n  * [`screenX`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenX)\n  * [`screenY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenY)\n  * [`shiftKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/shiftKey)\n\n  また、継承元である [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) のプロパティも含みます：\n\n  * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail)\n  * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view)\n\n---\n\n## 使用法 {/*usage*/}\n\n### CSS スタイルの適用 {/*applying-css-styles*/}\n\nReact では、CSS クラスを [`className`](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) で指定します。これは HTML の `class` 属性と同様に動作します。\n\n```js\n<img className=\"avatar\" />\n```\n\nそして別の CSS ファイルでそれに対する CSS ルールを記述します。\n\n```css\n/* In your CSS */\n.avatar {\n  border-radius: 50%;\n}\n```\n\nReact は CSS ファイルの追加方法を規定しません。最も単純なケースでは、HTML に [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) タグを追加します。ビルドツールやフレームワークを使用している場合は、そのドキュメンテーションを参照して、プロジェクトに CSS ファイルを追加する方法を学んでください。\n\n時には、スタイルの値がデータに依存することがあります。`style` 属性を使用して、一部のスタイルを動的に渡します。\n\n```js {3-6}\n<img\n  className=\"avatar\"\n  style={{\n    width: user.imageSize,\n    height: user.imageSize\n  }}\n/>\n```\n\n\n上記の例では、`style={{}}` は特別な構文ではなく、`style={ }` という [JSX の波括弧](/learn/javascript-in-jsx-with-curly-braces)内の通常の `{}` オブジェクトです。スタイルが JavaScript 変数に依存する場合にのみ `style` 属性を使用することをお勧めします。\n\n<Sandpack>\n\n```js src/App.js\nimport Avatar from './Avatar.js';\n\nconst user = {\n  name: 'Hedy Lamarr',\n  imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',\n  imageSize: 90,\n};\n\nexport default function App() {\n  return <Avatar user={user} />;\n}\n```\n\n```js src/Avatar.js active\nexport default function Avatar({ user }) {\n  return (\n    <img\n      src={user.imageUrl}\n      alt={'Photo of ' + user.name}\n      className=\"avatar\"\n      style={{\n        width: user.imageSize,\n        height: user.imageSize\n      }}\n    />\n  );\n}\n```\n\n```css src/styles.css\n.avatar {\n  border-radius: 50%;\n}\n```\n\n</Sandpack>\n\n<DeepDive>\n\n#### 複数の CSS クラスを条件付きで適用する方法 {/*how-to-apply-multiple-css-classes-conditionally*/}\n\nCSS クラスを条件付きで適用するには、JavaScript を使用して `className` 文字列を自分で生成する必要があります。\n\n例えば、`className={'row ' + (isSelected ? 'selected': '')}` は `isSelected` が `true` かどうかによって、`className=\"row\"` または `className=\"row selected\"` になります。\n\nこれをより読みやすくするために、[`classnames`](https://github.com/JedWatson/classnames) のような小さなヘルパライブラリを使用できます。\n\n```js\nimport cn from 'classnames';\n\nfunction Row({ isSelected }) {\n  return (\n    <div className={cn('row', isSelected && 'selected')}>\n      ...\n    </div>\n  );\n}\n```\n\nこれは条件付きクラスが複数ある場合に特に便利です。\n\n```js\nimport cn from 'classnames';\n\nfunction Row({ isSelected, size }) {\n  return (\n    <div className={cn('row', {\n      selected: isSelected,\n      large: size === 'large',\n      small: size === 'small',\n    })}>\n      ...\n    </div>\n  );\n}\n```\n\n</DeepDive>\n\n---\n\n### ref を使って DOM ノードを操作する {/*manipulating-a-dom-node-with-a-ref*/}\n\nJSX タグに対応するブラウザの DOM ノードを取得する必要がある場合があります。例えば、ボタンがクリックされたときに `<input>` にフォーカスを当てたい場合、ブラウザの `<input>` DOM ノードに対して [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) を呼び出す必要があります。\n\nあるタグに対応するブラウザ DOM ノードを取得するには、[ref を宣言](/reference/react/useRef)し、それをそのタグの `ref` 属性として渡します。\n\n```js {7}\nimport { useRef } from 'react';\n\nexport default function Form() {\n  const inputRef = useRef(null);\n  // ...\n  return (\n    <input ref={inputRef} />\n    // ...\n```\n\nReact は、画面にレンダーされた後に、DOM ノードを `inputRef.current` に代入します。\n\n<Sandpack>\n\n```js\nimport { useRef } from 'react';\n\nexport default function Form() {\n  const inputRef = useRef(null);\n\n  function handleClick() {\n    inputRef.current.focus();\n  }\n\n  return (\n    <>\n      <input ref={inputRef} />\n      <button onClick={handleClick}>\n        Focus the input\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n[ref を使った DOM の操作](/learn/manipulating-the-dom-with-refs)に詳しい解説があります。[こちらに他の例があります](/reference/react/useRef#usage)。\n\nより高度なユースケースのために、`ref` 属性は[コールバック関数](#ref-callback)も受け入れます。\n\n---\n\n### 危険を冒して内部 HTML をセットする {/*dangerously-setting-the-inner-html*/}\n\n以下のように、要素に対して生の HTML 文字列を渡すことができます。\n\n```js\nconst markup = { __html: '<p>some raw html</p>' };\nreturn <div dangerouslySetInnerHTML={markup} />;\n```\n\n**これは危険です。元の DOM の [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) プロパティも同様ですが、最大限に注意を払ってください！ マークアップが完全に信頼できるソースから来ていない限り、この方法を使うといとも簡単に [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) 脆弱性が発生します**。\n\n例えば、Markdown を HTML に変換する Markdown ライブラリを使用しており、そのパーサにバグがないと信頼でき、ユーザは本人が入力したものしか見ない、という場合、結果 HTML を以下のように表示することができます。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport MarkdownPreview from './MarkdownPreview.js';\n\nexport default function MarkdownEditor() {\n  const [postContent, setPostContent] = useState('_Hello,_ **Markdown**!');\n  return (\n    <>\n      <label>\n        Enter some markdown:\n        <textarea\n          value={postContent}\n          onChange={e => setPostContent(e.target.value)}\n        />\n      </label>\n      <hr />\n      <MarkdownPreview markdown={postContent} />\n    </>\n  );\n}\n```\n\n```js src/MarkdownPreview.js active\nimport { Remarkable } from 'remarkable';\n\nconst md = new Remarkable();\n\nfunction renderMarkdownToHTML(markdown) {\n  // This is ONLY safe because the output HTML\n  // is shown to the same user, and because you\n  // trust this Markdown parser to not have bugs.\n  const renderedHTML = md.render(markdown);\n  return {__html: renderedHTML};\n}\n\nexport default function MarkdownPreview({ markdown }) {\n  const markup = renderMarkdownToHTML(markdown);\n  return <div dangerouslySetInnerHTML={markup} />;\n}\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"remarkable\": \"2.0.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```css\ntextarea { display: block; margin-top: 5px; margin-bottom: 10px; }\n```\n\n</Sandpack>\n\nこの `{__html}` というオブジェクトは、HTML が生成される場所にできるだけ近いところで作成するようにしてください。例えば上記の例の `renderMarkdownToHTML` のようにします。これにより、あなたのコード内で使われる生 HTML が生 HTML であるとマークされるようになり、HTML を含んでいると思われる変数だけが `dangerouslySetInnerHTML` に渡されるようになります。`<div dangerouslySetInnerHTML={{__html: markup}} />` のようにしてこのオブジェクトをインラインで作成することは推奨されません。\n\nなぜ任意の HTML をレンダーすることが危険なのかを理解するために、上記のコードを以下のように置き換えてみてください。\n\n```js {1-4,7,8}\nconst post = {\n  // Imagine this content is stored in the database.\n  content: `<img src=\"\" onerror='alert(\"you were hacked\")'>`\n};\n\nexport default function MarkdownPreview() {\n  // 🔴 SECURITY HOLE: passing untrusted input to dangerouslySetInnerHTML\n  const markup = { __html: post.content };\n  return <div dangerouslySetInnerHTML={markup} />;\n}\n```\n\nHTML に埋め込まれたコードが実行されます。ハッカーはこのセキュリティホールを利用してユーザ情報を盗んだり、ユーザに代わって行動したりすることができます。**`dangerouslySetInnerHTML` は信頼できる、そしてサニタイズされたデータでのみ使用してください**。\n\n---\n\n### マウスイベントの処理 {/*handling-mouse-events*/}\n\nこの例では、一般的な[マウスイベント](#mouseevent-handler)とそれらの発火タイミングを示しています。\n\n<Sandpack>\n\n```js\nexport default function MouseExample() {\n  return (\n    <div\n      onMouseEnter={e => console.log('onMouseEnter (parent)')}\n      onMouseLeave={e => console.log('onMouseLeave (parent)')}\n    >\n      <button\n        onClick={e => console.log('onClick (first button)')}\n        onMouseDown={e => console.log('onMouseDown (first button)')}\n        onMouseEnter={e => console.log('onMouseEnter (first button)')}\n        onMouseLeave={e => console.log('onMouseLeave (first button)')}\n        onMouseOver={e => console.log('onMouseOver (first button)')}\n        onMouseUp={e => console.log('onMouseUp (first button)')}\n      >\n        First button\n      </button>\n      <button\n        onClick={e => console.log('onClick (second button)')}\n        onMouseDown={e => console.log('onMouseDown (second button)')}\n        onMouseEnter={e => console.log('onMouseEnter (second button)')}\n        onMouseLeave={e => console.log('onMouseLeave (second button)')}\n        onMouseOver={e => console.log('onMouseOver (second button)')}\n        onMouseUp={e => console.log('onMouseUp (second button)')}\n      >\n        Second button\n      </button>\n    </div>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 10px; }\n```\n\n</Sandpack>\n\n---\n\n### ポインタイベントの処理 {/*handling-pointer-events*/}\n\nこの例では、一般的な[ポインタイベント](#pointerevent-handler)とそれらの発火タイミングを示しています。\n\n<Sandpack>\n\n```js\nexport default function PointerExample() {\n  return (\n    <div\n      onPointerEnter={e => console.log('onPointerEnter (parent)')}\n      onPointerLeave={e => console.log('onPointerLeave (parent)')}\n      style={{ padding: 20, backgroundColor: '#ddd' }}\n    >\n      <div\n        onPointerDown={e => console.log('onPointerDown (first child)')}\n        onPointerEnter={e => console.log('onPointerEnter (first child)')}\n        onPointerLeave={e => console.log('onPointerLeave (first child)')}\n        onPointerMove={e => console.log('onPointerMove (first child)')}\n        onPointerUp={e => console.log('onPointerUp (first child)')}\n        style={{ padding: 20, backgroundColor: 'lightyellow' }}\n      >\n        First child\n      </div>\n      <div\n        onPointerDown={e => console.log('onPointerDown (second child)')}\n        onPointerEnter={e => console.log('onPointerEnter (second child)')}\n        onPointerLeave={e => console.log('onPointerLeave (second child)')}\n        onPointerMove={e => console.log('onPointerMove (second child)')}\n        onPointerUp={e => console.log('onPointerUp (second child)')}\n        style={{ padding: 20, backgroundColor: 'lightblue' }}\n      >\n        Second child\n      </div>\n    </div>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 10px; }\n```\n\n</Sandpack>\n\n---\n\n### フォーカスイベントの処理 {/*handling-focus-events*/}\n\nReact では、[フォーカスイベント](#focusevent-handler)はバブルします。`currentTarget` と `relatedTarget` を使用して、フォーカスまたはブラー（フォーカス喪失）イベントが親要素の外部から発生したかどうかを判断できます。この例では、子要素へのフォーカス、親要素へのフォーカス、および全体のサブツリーへのフォーカスの入出を検出する方法を示しています。\n\n<Sandpack>\n\n```js\nexport default function FocusExample() {\n  return (\n    <div\n      tabIndex={1}\n      onFocus={(e) => {\n        if (e.currentTarget === e.target) {\n          console.log('focused parent');\n        } else {\n          console.log('focused child', e.target.name);\n        }\n        if (!e.currentTarget.contains(e.relatedTarget)) {\n          // Not triggered when swapping focus between children\n          console.log('focus entered parent');\n        }\n      }}\n      onBlur={(e) => {\n        if (e.currentTarget === e.target) {\n          console.log('unfocused parent');\n        } else {\n          console.log('unfocused child', e.target.name);\n        }\n        if (!e.currentTarget.contains(e.relatedTarget)) {\n          // Not triggered when swapping focus between children\n          console.log('focus left parent');\n        }\n      }}\n    >\n      <label>\n        First name:\n        <input name=\"firstName\" />\n      </label>\n      <label>\n        Last name:\n        <input name=\"lastName\" />\n      </label>\n    </div>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 10px; }\n```\n\n</Sandpack>\n\n---\n\n### キーボードイベントの処理 {/*handling-keyboard-events*/}\n\nこの例では、一般的な[キーボードイベント](#keyboardevent-handler)とそれらの発火タイミングを示しています。\n\n<Sandpack>\n\n```js\nexport default function KeyboardExample() {\n  return (\n    <label>\n      First name:\n      <input\n        name=\"firstName\"\n        onKeyDown={e => console.log('onKeyDown:', e.key, e.code)}\n        onKeyUp={e => console.log('onKeyUp:', e.key, e.code)}\n      />\n    </label>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin-left: 10px; }\n```\n\n</Sandpack>\n"
  },
  {
    "path": "src/content/reference/react-dom/components/form.md",
    "content": "---\ntitle: \"<form>\"\n---\n\n<Intro>\n\n[ブラウザ組み込みの `<form>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)を利用することで、情報を送信するためのインタラクティブなコントロールを作成できます。\n\n```js\n<form action={search}>\n    <input name=\"query\" />\n    <button type=\"submit\">Search</button>\n</form>\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<form>` {/*form*/}\n\n情報を送信するためのインタラクティブなコントロールを作成するには、[ビルトインのブラウザ `<form>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)をレンダーします。\n\n```js\n<form action={search}>\n    <input name=\"query\" />\n    <button type=\"submit\">Search</button>\n</form>\n```\n\n[さらに例を見る](#usage)\n\n#### props {/*props*/}\n\n`<form>` は、[一般的な要素の props](/reference/react-dom/components/common#common-props) をすべてサポートしています。\n\n[`action`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action)：URL または関数。`action` として URL が渡された場合、フォームは HTML の form コンポーネントと同様に動作します。`action` として関数が渡された場合、その関数がフォームの送信を [Action prop パターン](/reference/react/useTransition#exposing-action-props-from-components)を用いてトランジション内で処理します。`action` に渡された関数は非同期でもよく、送信されたフォームの [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) を唯一の引数として呼び出されます。`action` は、`<button>`、`<input type=\"submit\">`、または `<input type=\"image\">` コンポーネントの `formAction` プロパティによって上書きされることがあります。\n\n#### 注意点 {/*caveats*/}\n\n* `action` または `formAction` に関数が渡された場合、HTTP メソッドは `method` の値に関わらず POST になります。\n\n---\n\n## 使用法 {/*usage*/}\n\n### クライアント上でフォーム送信を処理する {/*handle-form-submission-on-the-client*/}\n\nフォームの `action` プロパティに関数を渡すことで、フォームが送信されたときにその関数が実行されるようにします。この関数には [`formData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) が引数として渡されるため、フォームが送信したデータにアクセスできます。これは URL のみを受け付ける本来の [HTML action](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action) とは異なる、独自の動作です。`action` に指定された関数が成功した後、非制御のフィールド要素はすべてリセットされます。\n\n<Sandpack>\n\n```js src/App.js\nexport default function Search() {\n  function search(formData) {\n    const query = formData.get(\"query\");\n    alert(`You searched for '${query}'`);\n  }\n  return (\n    <form action={search}>\n      <input name=\"query\" />\n      <button type=\"submit\">Search</button>\n    </form>\n  );\n}\n```\n\n</Sandpack>\n\n### サーバ関数を使ってフォームの送信を処理する {/*handle-form-submission-with-a-server-function*/}\n\n`<form>` をレンダーし、入力フィールドと送信ボタンを配置します。フォームが送信されたときに関数を実行するために、サーバ関数（Server Function; [`'use server'`](/reference/rsc/use-server) でマークされた関数）を form の `action` に渡します。\n\n`<form action>` にサーバ関数を渡すことで、JavaScript が無効あるいはコードがロードされる前の状態でも、ユーザがフォームを送信できるようになります。これは、接続やデバイスが遅い、または JavaScript が無効になっているユーザにとって有益であり、`action` に URL を渡したフォームと同様に動作します。\n\n`<form>` のアクションには hidden となっているフォームフィールドを使ってデータを送信することもできます。サーバ関数は、hidden フィールドのデータも [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) インスタンスに含まれた状態で呼び出されます。\n\n```jsx\nimport { updateCart } from './lib.js';\n\nfunction AddToCart({productId}) {\n  async function addToCart(formData) {\n    'use server'\n    const productId = formData.get('productId')\n    await updateCart(productId)\n  }\n  return (\n    <form action={addToCart}>\n        <input type=\"hidden\" name=\"productId\" value={productId} />\n        <button type=\"submit\">Add to Cart</button>\n    </form>\n\n  );\n}\n```\n\nhidden フィールドを使用して `<form>` アクションにデータを渡す代わりに、<CodeStep step={1}>`bind`</CodeStep> メソッドを呼び出して追加の引数を渡すこともできます。これにより、渡された引数である <CodeStep step={3}>`formData`</CodeStep> に加えて新しい引数 (<CodeStep step={2}>`productId`</CodeStep>) がバインドされます。\n\n```jsx [[1, 8, \"bind\"], [2,8, \"productId\"], [2,4, \"productId\"], [3,4, \"formData\"]]\nimport { updateCart } from './lib.js';\n\nfunction AddToCart({productId}) {\n  async function addToCart(productId, formData) {\n    \"use server\";\n    await updateCart(productId)\n  }\n  const addProductToCart = addToCart.bind(null, productId);\n  return (\n    <form action={addProductToCart}>\n      <button type=\"submit\">Add to Cart</button>\n    </form>\n  );\n}\n```\n\n[サーバコンポーネント](/reference/rsc/use-client) によって `<form>` をレンダーし、`<form>` の `action` に[サーバ関数](/reference/rsc/server-functions)を渡すことで、フォームの[プログレッシブエンハンスメント](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement)が有効になります。\n\n### フォームの送信中状態を表示する {/*display-a-pending-state-during-form-submission*/}\nフォームが送信されている間に保留 (pending) 状態を表示するには、`<form>` 内でレンダーされるコンポーネントで `useFormStatus` フックを呼び出して、返された `pending` プロパティを読み取ります。\n\n以下では、フォームが送信中であることを表示するために `pending` プロパティを使用しています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useFormStatus } from \"react-dom\";\nimport { submitForm } from \"./actions.js\";\n\nfunction Submit() {\n  const { pending } = useFormStatus();\n  return (\n    <button type=\"submit\" disabled={pending}>\n      {pending ? \"Submitting...\" : \"Submit\"}\n    </button>\n  );\n}\n\nfunction Form({ action }) {\n  return (\n    <form action={action}>\n      <Submit />\n    </form>\n  );\n}\n\nexport default function App() {\n  return <Form action={submitForm} />;\n}\n```\n\n```js src/actions.js hidden\nexport async function submitForm(query) {\n    await new Promise((res) => setTimeout(res, 1000));\n}\n```\n\n</Sandpack>\n\n`useFormStatus` フックの詳細は[リファレンスドキュメント](/reference/react-dom/hooks/useFormStatus)を参照してください。\n\n### フォームデータの楽観的更新 {/*optimistically-updating-form-data*/}\n`useOptimistic` フックは、ネットワークリクエストのようなバックグラウンド作業が完了する前に、ユーザインターフェースを楽観的に更新する方法を提供します。フォームにおいては、この技術はアプリをよりレスポンシブに感じさせるために役立ちます。ユーザがフォームを送信した際に、サーバのレスポンスを待たずに、予想される結果を用いてインターフェースを即座に更新しておきます。\n\n例えば、ユーザがフォームにメッセージを入力して送信ボタンを押すと、`useOptimistic` フックにより、メッセージが実際にサーバに送信される前であっても、リストに \"Sending...\" というラベル付きでメッセージを即座に表示できるようになります。この「楽観的」アプローチにより、アプリの印象が高速でレスポンシブになります。その後フォームはバックグラウンドでメッセージの実際の送信を試みます。サーバにメッセージが到着したことを確認すると、\"Sending...\" ラベルが取り除かれます。\n\n<Sandpack>\n\n\n```js src/App.js\nimport { useOptimistic, useState, useRef } from \"react\";\nimport { deliverMessage } from \"./actions.js\";\n\nfunction Thread({ messages, sendMessage }) {\n  const formRef = useRef();\n  async function formAction(formData) {\n    addOptimisticMessage(formData.get(\"message\"));\n    formRef.current.reset();\n    await sendMessage(formData);\n  }\n  const [optimisticMessages, addOptimisticMessage] = useOptimistic(\n    messages,\n    (state, newMessage) => [\n      ...state,\n      {\n        text: newMessage,\n        sending: true\n      }\n    ]\n  );\n\n  return (\n    <>\n      {optimisticMessages.map((message, index) => (\n        <div key={index}>\n          {message.text}\n          {!!message.sending && <small> (Sending...)</small>}\n        </div>\n      ))}\n      <form action={formAction} ref={formRef}>\n        <input type=\"text\" name=\"message\" placeholder=\"Hello!\" />\n        <button type=\"submit\">Send</button>\n      </form>\n    </>\n  );\n}\n\nexport default function App() {\n  const [messages, setMessages] = useState([\n    { text: \"Hello there!\", sending: false, key: 1 }\n  ]);\n  async function sendMessage(formData) {\n    const sentMessage = await deliverMessage(formData.get(\"message\"));\n    setMessages((messages) => [...messages, { text: sentMessage }]);\n  }\n  return <Thread messages={messages} sendMessage={sendMessage} />;\n}\n```\n\n```js src/actions.js\nexport async function deliverMessage(message) {\n  await new Promise((res) => setTimeout(res, 1000));\n  return message;\n}\n```\n\n</Sandpack>\n\n[//]: # 'Uncomment the next line, and delete this line after the `useOptimistic` reference documentation page is published'\n[//]: # 'To learn more about the `useOptimistic` Hook see the [reference documentation](/reference/react/useOptimistic).'\n\n### フォーム送信エラーの処理 {/*handling-form-submission-errors*/}\n\n場合によっては、`<form>` の `action` で呼び出された関数がエラーをスローすることがあります。このようなエラーを処理するには、`<form>` をエラーバウンダリでラップします。`<form>` の `action` で呼び出される関数がエラーをスローすると、エラーバウンダリのフォールバックが表示されます。\n\n<Sandpack>\n\n```js src/App.js\nimport { ErrorBoundary } from \"react-error-boundary\";\n\nexport default function Search() {\n  function search() {\n    throw new Error(\"search error\");\n  }\n  return (\n    <ErrorBoundary\n      fallback={<p>There was an error while submitting the form</p>}\n    >\n      <form action={search}>\n        <input name=\"query\" />\n        <button type=\"submit\">Search</button>\n      </form>\n    </ErrorBoundary>\n  );\n}\n\n```\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"react\": \"19.0.0-rc-3edc000d-20240926\",\n    \"react-dom\": \"19.0.0-rc-3edc000d-20240926\",\n    \"react-scripts\": \"^5.0.0\",\n    \"react-error-boundary\": \"4.0.3\"\n  },\n  \"main\": \"/index.js\",\n  \"devDependencies\": {}\n}\n```\n\n</Sandpack>\n\n### JavaScript を使わずにフォーム送信エラーを表示する {/*display-a-form-submission-error-without-javascript*/}\n\nプログレッシブエンハンスメントの実現のため JavaScript バンドルが読み込まれる前にフォーム送信エラーメッセージを表示できるようにするには、以下の条件を満たす必要があります。\n\n1. `<form>` が [クライアントコンポーネント](/reference/rsc/use-client)によってレンダーされている\n1. `<form>` の `action` プロパティに渡される関数が[サーバ関数](/reference/rsc/server-functions)である\n1. `useActionState` フックを使用してエラーメッセージを表示している\n\n`useActionState` は[サーバ関数](/reference/rsc/server-functions)と初期 state の 2 つの引数を受け取り、state 変数とアクションの 2 つの値を返します。`useActionState` が返したアクションは、フォームの `action` プロパティに渡します。`useActionState` が返した state 変数は、エラーメッセージを表示するために使用できます。`useActionState` に渡すサーバ関数が返す値は、state 変数を更新するために使用されます。\n\n<Sandpack>\n\n```js src/App.js\nimport { useActionState } from \"react\";\nimport { signUpNewUser } from \"./api\";\n\nexport default function Page() {\n  async function signup(prevState, formData) {\n    \"use server\";\n    const email = formData.get(\"email\");\n    try {\n      await signUpNewUser(email);\n      alert(`Added \"${email}\"`);\n    } catch (err) {\n      return err.toString();\n    }\n  }\n  const [message, signupAction] = useActionState(signup, null);\n  return (\n    <>\n      <h1>Signup for my newsletter</h1>\n      <p>Signup with the same email twice to see an error</p>\n      <form action={signupAction} id=\"signup-form\">\n        <label htmlFor=\"email\">Email: </label>\n        <input name=\"email\" id=\"email\" placeholder=\"react@example.com\" />\n        <button>Sign up</button>\n        {!!message && <p>{message}</p>}\n      </form>\n    </>\n  );\n}\n```\n\n```js src/api.js hidden\nlet emails = [];\n\nexport async function signUpNewUser(newEmail) {\n  if (emails.includes(newEmail)) {\n    throw new Error(\"This email address has already been added\");\n  }\n  emails.push(newEmail);\n}\n```\n\n</Sandpack>\n\nフォームアクションから state を更新する方法については、[`useActionState`](/reference/react/useActionState) のドキュメントを参照してください。\n\n### 複数の送信タイプを処理する {/*handling-multiple-submission-types*/}\n\nフォームは、ユーザが押したボタンに基づいて複数の送信アクションを処理するように設計することができます。フォーム内の各ボタンは、props である `formAction` を指定することで、異なるアクションや振る舞いに関連付けることができます。\n\nユーザが特定のボタンをタップしてフォームを送信すると、そのボタンの `formAction` によって定義された対応するアクションが実行されます。例えば、デフォルトでは書いた記事をレビュー用に送信するが、記事を下書きとして保存するための `formAction` がセットされた別のボタンもある、というフォームを作ることができます。\n\n<Sandpack>\n\n```js src/App.js\nexport default function Search() {\n  function publish(formData) {\n    const content = formData.get(\"content\");\n    const button = formData.get(\"button\");\n    alert(`'${content}' was published with the '${button}' button`);\n  }\n\n  function save(formData) {\n    const content = formData.get(\"content\");\n    alert(`Your draft of '${content}' has been saved!`);\n  }\n\n  return (\n    <form action={publish}>\n      <textarea name=\"content\" rows={4} cols={40} />\n      <br />\n      <button type=\"submit\" name=\"button\" value=\"submit\">Publish</button>\n      <button formAction={save}>Save draft</button>\n    </form>\n  );\n}\n```\n\n</Sandpack>\n"
  },
  {
    "path": "src/content/reference/react-dom/components/index.md",
    "content": "---\ntitle: \"React DOM コンポーネント\"\n---\n\n<Intro>\n\nReact は、ブラウザ組み込みのすべての [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML/Element) と [SVG](https://developer.mozilla.org/en-US/docs/Web/SVG/Element) コンポーネントをサポートしています。\n\n</Intro>\n\n---\n\n## 一般的なコンポーネント {/*common-components*/}\n\nすべてのブラウザ組み込みコンポーネントは、いくつかの props とイベントをサポートしています。\n\n* [`<div>` などの一般的なコンポーネント](/reference/react-dom/components/common)\n\nこれには、`ref` や `dangerouslySetInnerHTML` のような React 固有の props も含みます。\n\n---\n\n## フォームコンポーネント {/*form-components*/}\n\n以下のブラウザ組み込みコンポーネントはユーザからの入力を受け付けます。\n\n* [`<input>`](/reference/react-dom/components/input)\n* [`<select>`](/reference/react-dom/components/select)\n* [`<textarea>`](/reference/react-dom/components/textarea)\n\nこれらは React では特別です。なぜなら props として `value` を渡すと[*制御されたコンポーネント*](/reference/react-dom/components/input#controlling-an-input-with-a-state-variable)になるからです。\n\n---\n\n## リソース・メタデータ関連コンポーネント {/*resource-and-metadata-components*/}\n\n以下のブラウザ組み込みコンポーネントを用いて、外部リソースを読み込んだり、ドキュメントにメタデータを付与したりすることができます。\n\n* [`<link>`](/reference/react-dom/components/link)\n* [`<meta>`](/reference/react-dom/components/meta)\n* [`<script>`](/reference/react-dom/components/script)\n* [`<style>`](/reference/react-dom/components/style)\n* [`<title>`](/reference/react-dom/components/title)\n\nこれらが React にとって特別なのは、React がこれらをドキュメントの head 要素に入れることができ、リソースのロード中にサスペンドができ、また個々のコンポーネントのリファレンスページで説明されているような特別な挙動が有効になるからです。\n\n---\n\n## すべての HTML コンポーネント {/*all-html-components*/}\n\nReact はブラウザ組み込みのすべての HTML コンポーネントをサポートしています。これには以下が含まれます。\n\n* [`<aside>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside)\n* [`<audio>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio)\n* [`<b>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/b)\n* [`<base>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base)\n* [`<bdi>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdi)\n* [`<bdo>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdo)\n* [`<blockquote>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote)\n* [`<body>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body)\n* [`<br>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br)\n* [`<button>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button)\n* [`<canvas>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas)\n* [`<caption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption)\n* [`<cite>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite)\n* [`<code>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code)\n* [`<col>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/col)\n* [`<colgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/colgroup)\n* [`<data>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/data)\n* [`<datalist>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist)\n* [`<dd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd)\n* [`<del>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/del)\n* [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)\n* [`<dfn>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dfn)\n* [`<dialog>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog)\n* [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div)\n* [`<dl>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl)\n* [`<dt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt)\n* [`<em>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em)\n* [`<embed>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed)\n* [`<fieldset>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset)\n* [`<figcaption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption)\n* [`<figure>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure)\n* [`<footer>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/footer)\n* [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)\n* [`<h1>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1)\n* [`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)\n* [`<header>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header)\n* [`<hgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hgroup)\n* [`<hr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr)\n* [`<html>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html)\n* [`<i>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i)\n* [`<iframe>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe)\n* [`<img>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img)\n* [`<input>`](/reference/react-dom/components/input)\n* [`<ins>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins)\n* [`<kbd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/kbd)\n* [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label)\n* [`<legend>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend)\n* [`<li>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li)\n* [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)\n* [`<main>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main)\n* [`<map>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map)\n* [`<mark>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark)\n* [`<menu>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menu)\n* [`<meta>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)\n* [`<meter>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter)\n* [`<nav>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav)\n* [`<noscript>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript)\n* [`<object>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object)\n* [`<ol>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol)\n* [`<optgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup)\n* [`<option>`](/reference/react-dom/components/option)\n* [`<output>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output)\n* [`<p>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p)\n* [`<picture>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture)\n* [`<pre>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre)\n* [`<progress>`](/reference/react-dom/components/progress)\n* [`<q>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q)\n* [`<rp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rp)\n* [`<rt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rt)\n* [`<ruby>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby)\n* [`<s>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/s)\n* [`<samp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/samp)\n* [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)\n* [`<section>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section)\n* [`<select>`](/reference/react-dom/components/select)\n* [`<slot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot)\n* [`<small>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/small)\n* [`<source>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source)\n* [`<span>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span)\n* [`<strong>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong)\n* [`<style>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style)\n* [`<sub>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub)\n* [`<summary>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary)\n* [`<sup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup)\n* [`<table>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table)\n* [`<tbody>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody)\n* [`<td>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td)\n* [`<template>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template)\n* [`<textarea>`](/reference/react-dom/components/textarea)\n* [`<tfoot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tfoot)\n* [`<th>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/th)\n* [`<thead>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead)\n* [`<time>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time)\n* [`<title>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title)\n* [`<tr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr)\n* [`<track>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track)\n* [`<u>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/u)\n* [`<ul>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul)\n* [`<var>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/var)\n* [`<video>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video)\n* [`<wbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/wbr)\n\n<Note>\n\n[DOM 標準](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model)と同様に、React では props の名前として `camelCase` 規則を使用します。例えば、`tabindex` ではなく `tabIndex` と書きます。既存の HTML を JSX に変換するための[オンラインコンバータ](https://transform.tools/html-to-jsx)を使用できます。\n\n</Note>\n\n---\n\n### カスタム HTML 要素 {/*custom-html-elements*/}\n\nダッシュを含むタグ、例えば `<my-element>` をレンダーする場合、React は[カスタム HTML 要素](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements)をレンダーしていると想定します。\n\n組み込みのブラウザ HTML 要素を [`is`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/is) 属性を用いてレンダーする場合も、カスタム要素として扱われます。\n\n#### カスタム要素に値を渡す {/*attributes-vs-properties*/}\n\nカスタム要素にデータを渡す方法は 2 種類あります。\n\n1) 属性 (attribute) として：マークアップ内に現れ、文字列型の値しかとれない\n2) プロパティ (property) として：マークアップ内には直接現れず、任意の JavaScript 型をとれる\n\nデフォルトでは、React は JSX に書かれた値を属性として渡します。\n\n```jsx\n<my-element value=\"Hello, world!\"></my-element>\n```\n\nカスタム要素に文字列ではない JavaScript の値が渡されると、デフォルトではシリアライズされます。\n\n```jsx\n// Will be passed as `\"1,2,3\"` as the output of `[1,2,3].toString()`\n<my-element value={[1,2,3]}></my-element>\n```\n\nただし React は、対応するクラスのコンストラクタ内に当該プロパティ名が出現する場合、カスタム要素のプロパティに任意の値を渡すことができる、と認識します。\n\n<Sandpack>\n\n```js src/index.js hidden\nimport {MyElement} from './MyElement.js';\nimport { createRoot } from 'react-dom/client';\nimport {App} from \"./App.js\";\n\ncustomElements.define('my-element', MyElement);\n\nconst root = createRoot(document.getElementById('root'))\nroot.render(<App />);\n```\n\n```js src/MyElement.js active\nexport class MyElement extends HTMLElement {\n  constructor() {\n    super();\n    // The value here will be overwritten by React \n    // when initialized as an element\n    this.value = undefined;\n  }\n\n  connectedCallback() {\n    this.innerHTML = this.value.join(\", \");\n  }\n}\n```\n\n```js src/App.js\nexport function App() {\n  return <my-element value={[1,2,3]}></my-element>\n}\n```\n\n</Sandpack>\n\n#### カスタム要素でのイベントのリッスン {/*custom-element-events*/}\n\nカスタム要素においては、イベントが起こったときに呼び出すための関数を受け取るのではなく、要素自体が [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) をディスパッチするというのが一般的なパターンです。このようなイベントは、`on` 接頭辞をつけることで JSX 経由でリッスンすることができます。\n\n<Sandpack>\n\n```js src/index.js hidden\nimport {MyElement} from './MyElement.js';\nimport { createRoot } from 'react-dom/client';\nimport {App} from \"./App.js\";\n\ncustomElements.define('my-element', MyElement);\n\nconst root = createRoot(document.getElementById('root'))\nroot.render(<App />);\n```\n\n```javascript src/MyElement.js\nexport class MyElement extends HTMLElement {\n  constructor() {\n    super();\n    this.test = undefined;\n    this.emitEvent = this._emitEvent.bind(this);\n  }\n\n  _emitEvent() {\n    const event = new CustomEvent('speak', {\n      detail: {\n        message: 'Hello, world!',\n      },\n    });\n    this.dispatchEvent(event);\n  }\n\n  connectedCallback() {\n    this.el = document.createElement('button');\n    this.el.innerText = 'Say hi';\n    this.el.addEventListener('click', this.emitEvent);\n    this.appendChild(this.el);\n  }\n\n  disconnectedCallback() {\n    this.el.removeEventListener('click', this.emitEvent);\n  }\n}\n```\n\n```jsx src/App.js active\nexport function App() {\n  return (\n    <my-element\n      onspeak={e => console.log(e.detail.message)}\n    ></my-element>\n  )\n}\n```\n\n</Sandpack>\n\n<Note>\n\nイベント名は大文字・小文字を区別し、ダッシュ (`-`) をサポートします。カスタム要素のイベントをリッスンする際は、大文字・小文字の区別やダッシュを保持するようにしてください。\n\n```jsx\n// Listens for `say-hi` events\n<my-element onsay-hi={console.log}></my-element>\n// Listens for `sayHi` events\n<my-element onsayHi={console.log}></my-element>\n```\n\n</Note>\n---\n\n## すべての SVG コンポーネント {/*all-svg-components*/}\n\nReact は、組み込みのブラウザ SVG コンポーネントをすべてサポートしています。以下が含まれます。\n\n* [`<a>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/a)\n* [`<animate>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate)\n* [`<animateMotion>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animateMotion)\n* [`<animateTransform>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animateTransform)\n* [`<circle>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle)\n* [`<clipPath>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/clipPath)\n* [`<defs>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs)\n* [`<desc>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/desc)\n* [`<discard>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/discard)\n* [`<ellipse>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/ellipse)\n* [`<feBlend>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feBlend)\n* [`<feColorMatrix>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feColorMatrix)\n* [`<feComponentTransfer>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feComponentTransfer)\n* [`<feComposite>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feComposite)\n* [`<feConvolveMatrix>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feConvolveMatrix)\n* [`<feDiffuseLighting>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feDiffuseLighting)\n* [`<feDisplacementMap>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feDisplacementMap)\n* [`<feDistantLight>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feDistantLight)\n* [`<feDropShadow>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feDropShadow)\n* [`<feFlood>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feFlood)\n* [`<feFuncA>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feFuncA)\n* [`<feFuncB>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feFuncB)\n* [`<feFuncG>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feFuncG)\n* [`<feFuncR>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feFuncR)\n* [`<feGaussianBlur>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feGaussianBlur)\n* [`<feImage>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feImage)\n* [`<feMerge>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feMerge)\n* [`<feMergeNode>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feMergeNode)\n* [`<feMorphology>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feMorphology)\n* [`<feOffset>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feOffset)\n* [`<fePointLight>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/fePointLight)\n* [`<feSpecularLighting>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feSpecularLighting)\n* [`<feSpotLight>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feSpotLight)\n* [`<feTile>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feTile)\n* [`<feTurbulence>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feTurbulence)\n* [`<filter>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/filter)\n* [`<foreignObject>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/foreignObject)\n* [`<g>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g)\n* `<hatch>`\n* `<hatchpath>`\n* [`<image>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image)\n* [`<line>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/line)\n* [`<linearGradient>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient)\n* [`<marker>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/marker)\n* [`<mask>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/mask)\n* [`<metadata>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/metadata)\n* [`<mpath>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/mpath)\n* [`<path>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path)\n* [`<pattern>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/pattern)\n* [`<polygon>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polygon)\n* [`<polyline>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polyline)\n* [`<radialGradient>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient)\n* [`<rect>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect)\n* [`<script>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/script)\n* [`<set>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/set)\n* [`<stop>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop)\n* [`<style>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/style)\n* [`<svg>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg)\n* [`<switch>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/switch)\n* [`<symbol>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/symbol)\n* [`<text>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/text)\n* [`<textPath>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/textPath)\n* [`<title>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title)\n* [`<tspan>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/tspan)\n* [`<use>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use)\n* [`<view>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/view)\n\n<Note>\n\n[DOM 標準](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model)と同様に、React では props の名前として `camelCase` 規則を使用します。例えば、`tabindex` ではなく `tabIndex` と書きます。既存の SVG を JSX に変換するための[オンラインコンバータ](https://transform.tools/html-to-jsx)を使用できます。\n\n名前空間付きの属性もコロンなしで書かなければなりません。\n\n* `xlink:actuate` は `xlinkActuate` になります。\n* `xlink:arcrole` は `xlinkArcrole` になります。\n* `xlink:href` は `xlinkHref` になります。\n* `xlink:role` は `xlinkRole` になります。\n* `xlink:show` は `xlinkShow` になります。\n* `xlink:title` は `xlinkTitle` になります。\n* `xlink:type` は `xlinkType` になります。\n* `xml:base` は `xmlBase` になります。\n* `xml:lang` は `xmlLang` になります。\n* `xml:space` は `xmlSpace` になります。\n* `xmlns:xlink` は `xmlnsXlink` になります。\n\n</Note>\n"
  },
  {
    "path": "src/content/reference/react-dom/components/input.md",
    "content": "---\ntitle: \"<input>\"\n---\n\n<Intro>\n\n[ブラウザ組み込みの `<input>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)を利用することで、さまざまな種類のフォーム入力をレンダーすることができます。\n\n```js\n<input />\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<input>` {/*input*/}\n\n入力欄を表示するには、[組み込みのブラウザ `<input>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) コンポーネントをレンダーします。\n\n```js\n<input name=\"myInput\" />\n```\n\n[さらに例を見る](#usage)\n\n#### props {/*props*/}\n\n`<input>` は[一般的な要素の props](/reference/react-dom/components/common#common-props) をすべてサポートしています。\n\n- [`formAction`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formaction): 文字列または関数。`type=\"submit\"` and `type=\"image\"` の場合に親の `<form action>` を上書きする。`action` に URL が渡された場合はフォームは標準的な HTML フォームとして動作する。関数が渡された場合はその関数がフォームの送信を処理する。[`<form action>`](/reference/react-dom/components/form#props) を参照。\n\n以下の props を渡すことで、[入力欄を制御されたコンポーネント (controlled component)](#controlling-an-input-with-a-state-variable) にできます。\n\n* [`checked`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#checked): ブーリアン。チェックボックスまたはラジオボタンの場合、選択されているかどうかを制御します。\n* [`value`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#value): 文字列。テキスト入力の場合、そのテキストを制御します。（ラジオボタンの場合は、フォームデータを指定します。）\n\nこれらのいずれかを渡す場合は、渡された値を更新する `onChange` ハンドラも渡す必要があります。\n\n以下の `<input>` の props は、非制御 (uncontrolled) の入力欄にのみ使用されます。\n\n* [`defaultChecked`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#defaultChecked): ブーリアン。`type=\"checkbox\"` および `type=\"radio\"` の入力欄の場合に、[初期値](#providing-an-initial-value-for-an-input)を指定します。 \n* [`defaultValue`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#defaultValue): 文字列。テキスト入力の場合に、[初期値](#providing-an-initial-value-for-an-input)を指定します。\n\n以下の `<input>` の props は、非制御の入力欄と制御された入力欄の両方で用いられます。\n\n* [`accept`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#accept): 文字列。`type=\"file\"` である入力欄が受け付けるファイルの種類を指定します。\n* [`alt`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#alt): 文字列。`type=\"image\"` である入力欄の場合に、代替画像テキストを指定します。\n* [`capture`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#capture): 文字列。`type=\"file\"` である入力欄がキャプチャするメディア（マイク、ビデオ、またはカメラ）を指定します。\n* [`autoComplete`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#autocomplete): 文字列。可能な[オートコンプリート動作](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values)の 1 つを指定します。\n* [`autoFocus`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#autofocus): ブーリアン。`true` の場合、React はマウント時にその要素をフォーカスします。\n* [`dirname`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#dirname): 文字列。要素の文字方向に対するフォームフィールド名を指定します。\n* [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#disabled): ブール値。`true` の場合、入力はインタラクティブではなくなり、薄暗く表示されます。\n* `children`: `<input>` は子要素を受け取りません。\n* [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#form): 文字列。この入力が属する `<form>` の `id` を指定します。省略された場合、最も近い親フォームが対象となります。\n* [`formAction`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formaction): 文字列。`type=\"submit\"` および `type=\"image\"` の場合、親要素の `<form action>` を上書きします。\n* [`formEnctype`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formenctype): 文字列。`type=\"submit\"` および `type=\"image\"` の場合、親要素の `<form enctype>` を上書きします。\n* [`formMethod`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formmethod): 文字列。`type=\"submit\"` および `type=\"image\"` の場合、親要素の `<form method>` を上書きします。\n* [`formNoValidate`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formnovalidate): 文字列。`type=\"submit\"` および `type=\"image\"` の場合、親要素の `<form noValidate>` を上書きします。\n* [`formTarget`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formtarget): 文字列。`type=\"submit\"` および `type=\"image\"` の場合、`<form target>` を上書きします。\n* [`height`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#height): 文字列。`type=\"image\"` の場合、画像の高さを指定します。\n* [`list`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#list): 文字列。オートコンプリートの選択肢を指定する `<datalist>` の `id` を指定します。\n* [`max`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#max): 数値。数値および日時タイプの入力欄において最大値を指定します。\n* [`maxLength`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#maxlength): 数値。テキストなどのタイプの入力欄において最大文字数を指定します。\n* [`min`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#min): 数値。数値および日時タイプの入力欄において最小値を指定します。\n* [`minLength`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#minlength): 数値。テキストなどのタイプの入力欄において最小文字数を指定します。\n* [`multiple`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#multiple): ブール値。`type=\"file\"` および `type=\"email\"` の場合、複数の値を許可するかどうかを指定します。\n* [`name`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#name): 文字列。[フォームで送信される](#reading-the-input-values-when-submitting-a-form)際に使われるこの入力欄の名前を指定します。\n* `onChange`: [`Event` ハンドラ](/reference/react-dom/components/common#event-handler)関数。[制御された入力](#controlling-an-input-with-a-state-variable)の場合は必須。ユーザが入力の値を変更するとすぐに発火します（例えば、キーストロークごとに発火します）。ブラウザの [`input` イベント](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event)と同じように動作します。\n* `onChangeCapture`: `onChange` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onInput`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event): [`Event` ハンドラ](/reference/react-dom/components/common#event-handler)関数。ユーザが値を変更するとすぐに発火します。歴史的な理由から、React では代わりに同様の動作をする `onChange` を使用するのが慣習となっています。\n* `onInputCapture`: `onInput` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onInvalid`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/invalid_event): [`Event` ハンドラ](/reference/react-dom/components/common#event-handler)関数。フォームの送信時に入力の検証に失敗した場合に発火します。組み込みの `invalid` イベントとは異なり、React の `onInvalid` イベントはバブルします。\n* `onInvalidCapture`: `onInvalid` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onSelect`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/select_event): [`Event` ハンドラ](/reference/react-dom/components/common#event-handler)関数。`<input>` 内で選択テキストが変更された後に発火します。React は `onSelect` イベントを拡張しており、空の選択やテキストの編集（選択に影響を与える可能性がある）でも発火します。\n* `onSelectCapture`: `onSelect` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`pattern`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#pattern): 文字列。`value` がマッチする必要のあるパターンを指定します。\n* [`placeholder`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#placeholder): 文字列。入力値が空の場合、これが薄い色で表示されます。\n* [`readOnly`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#readonly): ブーリアン。`true` の場合、入力欄はユーザによって編集できなくなります。\n* [`required`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#required): ブーリアン。`true` の場合、フォームを送信するためには値が必須となります。\n* [`size`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#size): 数値。width の設定と似ていますが、入力欄によって異なる単位で指定します。\n* [`src`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#src): 文字列。`type=\"image\"` の入力の場合、画像ファイルを指定します。\n* [`step`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#step): 正の数値、または文字列の `'any'`。有効な値の間隔を指定します。\n* [`type`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#type): 文字列。[入力タイプ](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types)の 1 つ。\n* [`width`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#width): 文字列。`type=\"image\"` の場合、画像の幅を指定します。\n\n#### 注意点 {/*caveats*/}\n\n- チェックボックスの場合 `value`（や `defaultValue`）ではなく、`checked`（や `defaultChecked`）が必要です。\n- テキスト入力欄が props として文字列型の `value` プロパティを受け取ると、[制御されたものとして扱われます](#controlling-an-input-with-a-state-variable)。\n- チェックボックスまたはラジオボタンが props としてブーリアン型の `checked` を受け取ると、[制御されたものとして扱われます](#controlling-an-input-with-a-state-variable)。\n- 入力欄は制御されたコンポーネントと非制御コンポーネントに同時になることはできません。。\n- 入力欄は、ライフタイム中に制御されたコンポーネントから非制御コンポーネント、またはその逆に切り替えることはできません。\n- すべての制御された入力欄には、制御に使っている state を同期的に更新するための `onChange` イベントハンドラが必要です。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 各種の入力欄を表示する {/*displaying-inputs-of-different-types*/}\n\n入力欄を表示するには、`<input>` コンポーネントをレンダーします。デフォルトではテキスト入力になります。チェックボックスの場合は `type=\"checkbox\"` を、ラジオボタンの場合は `type=\"radio\"` を、[または他の入力タイプのいずれか](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types)を渡すことができます。\n\n<Sandpack>\n\n```js\nexport default function MyForm() {\n  return (\n    <>\n      <label>\n        Text input: <input name=\"myInput\" />\n      </label>\n      <hr />\n      <label>\n        Checkbox: <input type=\"checkbox\" name=\"myCheckbox\" />\n      </label>\n      <hr />\n      <p>\n        Radio buttons:\n        <label>\n          <input type=\"radio\" name=\"myRadio\" value=\"option1\" />\n          Option 1\n        </label>\n        <label>\n          <input type=\"radio\" name=\"myRadio\" value=\"option2\" />\n          Option 2\n        </label>\n        <label>\n          <input type=\"radio\" name=\"myRadio\" value=\"option3\" />\n          Option 3\n        </label>\n      </p>\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin: 5px; }\n```\n\n</Sandpack>\n\n---\n\n### 入力欄にラベルを付ける {/*providing-a-label-for-an-input*/}\n\n通常、すべての `<input>` は [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label) タグ内に配置します。これにより、ブラウザに対してこのラベルがその入力欄に関連付けられていることが伝わります。ユーザがラベルをクリックすると、ブラウザは自動的に入力欄にフォーカスします。これはアクセシビリティの観点からも重要です。ユーザが入力欄にフォーカスすると、スクリーンリーダがラベルのキャプションを読み上げます。\n\nもし `<label>` 内に `<input>` をネストできない場合は、同じ ID を `<input id>` と [`<label htmlFor>`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor) に渡すことで関連付けることができます。同一コンポーネントの複数のインスタンス間での競合を避けるために、[`useId`](/reference/react/useId) を使用してそのような ID を生成してください。\n\n<Sandpack>\n\n```js\nimport { useId } from 'react';\n\nexport default function Form() {\n  const ageInputId = useId();\n  return (\n    <>\n      <label>\n        Your first name:\n        <input name=\"firstName\" />\n      </label>\n      <hr />\n      <label htmlFor={ageInputId}>Your age:</label>\n      <input id={ageInputId} name=\"age\" type=\"number\" />\n    </>\n  );\n}\n```\n\n```css\ninput { margin: 5px; }\n```\n\n</Sandpack>\n\n---\n\n### 入力欄に初期値を指定する {/*providing-an-initial-value-for-an-input*/}\n\nオプションで、入力値の初期値を指定することができます。テキスト入力の場合は、`defaultValue` として文字列を渡してください。チェックボックスとラジオボタンの場合は、代わりにブーリアンの `defaultChecked` を使用して初期値を指定してください。\n\n<Sandpack>\n\n```js\nexport default function MyForm() {\n  return (\n    <>\n      <label>\n        Text input: <input name=\"myInput\" defaultValue=\"Some initial value\" />\n      </label>\n      <hr />\n      <label>\n        Checkbox: <input type=\"checkbox\" name=\"myCheckbox\" defaultChecked={true} />\n      </label>\n      <hr />\n      <p>\n        Radio buttons:\n        <label>\n          <input type=\"radio\" name=\"myRadio\" value=\"option1\" />\n          Option 1\n        </label>\n        <label>\n          <input\n            type=\"radio\"\n            name=\"myRadio\"\n            value=\"option2\"\n            defaultChecked={true} \n          />\n          Option 2\n        </label>\n        <label>\n          <input type=\"radio\" name=\"myRadio\" value=\"option3\" />\n          Option 3\n        </label>\n      </p>\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin: 5px; }\n```\n\n</Sandpack>\n\n---\n\n### フォーム送信時に入力欄から値を読み取る {/*reading-the-input-values-when-submitting-a-form*/}\n\n入力欄を [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) で囲み、その中に [`<button type=\"submit\">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) を配置します。これにより、`<form onSubmit>` イベントハンドラが呼び出されます。デフォルトでは、ブラウザはフォームデータを現在の URL に送信し、ページを更新します。`e.preventDefault()` を呼び出すことで、その振る舞いをオーバーライドできます。[`new FormData(e.target)`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) を使用してフォームデータを読み込みます。\n<Sandpack>\n\n```js\nexport default function MyForm() {\n  function handleSubmit(e) {\n    // Prevent the browser from reloading the page\n    e.preventDefault();\n\n    // Read the form data\n    const form = e.target;\n    const formData = new FormData(form);\n\n    // You can pass formData as a fetch body directly:\n    fetch('/some-api', { method: form.method, body: formData });\n\n    // Or you can work with it as a plain object:\n    const formJson = Object.fromEntries(formData.entries());\n    console.log(formJson);\n  }\n\n  return (\n    <form method=\"post\" onSubmit={handleSubmit}>\n      <label>\n        Text input: <input name=\"myInput\" defaultValue=\"Some initial value\" />\n      </label>\n      <hr />\n      <label>\n        Checkbox: <input type=\"checkbox\" name=\"myCheckbox\" defaultChecked={true} />\n      </label>\n      <hr />\n      <p>\n        Radio buttons:\n        <label><input type=\"radio\" name=\"myRadio\" value=\"option1\" /> Option 1</label>\n        <label><input type=\"radio\" name=\"myRadio\" value=\"option2\" defaultChecked={true} /> Option 2</label>\n        <label><input type=\"radio\" name=\"myRadio\" value=\"option3\" /> Option 3</label>\n      </p>\n      <hr />\n      <button type=\"reset\">Reset form</button>\n      <button type=\"submit\">Submit form</button>\n    </form>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin: 5px; }\n```\n\n</Sandpack>\n\n<Note>\n\nすべての `<input>` に `name` を指定してください。例えば、`<input name=\"firstName\" defaultValue=\"Taylor\" />` のように指定します。指定した `name` は、フォームデータ内のキーとして使用されます。例えば、`{ firstName: \"Taylor\" }` のようになります。\n\n</Note>\n\n<Pitfall>\n\nデフォルトでは、`<form>` 内にあり `type` 属性のない `<button>` はフォームの送信を行います。これは予想外の挙動かもしれません！ 独自のカスタム `Button` React コンポーネントを利用している場合は、（type を指定しない）`<button>` の代わりに [`<button type=\"button\">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/button) を使うようにすることを考慮してください。そしてフォームを送信することが*意図されている*ボタンには明示的に `<button type=\"submit\">` を使用してください。\n\n</Pitfall>\n\n---\n\n### state 変数を使用して入力要素を制御する {/*controlling-an-input-with-a-state-variable*/}\n\n`<input />` のような入力欄は*非制御*です。`<input defaultValue=\"Initial text\" />` のように[デフォルト値を指定](#providing-an-initial-value-for-an-input)している場合でも、この JSX で指定しているのはあくまで初期値であって現在の値ではありません。\n\n***制御された*入力欄をレンダーするには、`value` プロパティ（チェックボックスやラジオボタンの場合は `checked`）を渡してください**。React は入力欄が常に渡した `value` を反映するようにします。通常、[state 変数](/reference/react/useState)を宣言することで入力欄を制御します。\n\n```js {2,6,7}\nfunction Form() {\n  const [firstName, setFirstName] = useState(''); // Declare a state variable...\n  // ...\n  return (\n    <input\n      value={firstName} // ...force the input's value to match the state variable...\n      onChange={e => setFirstName(e.target.value)} // ... and update the state variable on any edits!\n    />\n  );\n}\n```\n\n例えば編集のたびに UI を再レンダーする必要があるなどの理由でいずれにせよ state が必要な場合、制御された入力欄は特に有用です。\n\n```js {2,9}\nfunction Form() {\n  const [firstName, setFirstName] = useState('');\n  return (\n    <>\n      <label>\n        First name:\n        <input value={firstName} onChange={e => setFirstName(e.target.value)} />\n      </label>\n      {firstName !== '' && <p>Your name is {firstName}.</p>}\n      ...\n```\n\nまた、制御された入力欄は、入力 state を変更する方法（例えばボタンクリックなど）を複数提供したい場合にも役立ちます。\n\n```js {3-4,10-11,14}\nfunction Form() {\n  // ...\n  const [age, setAge] = useState('');\n  const ageAsNumber = Number(age);\n  return (\n    <>\n      <label>\n        Age:\n        <input\n          value={age}\n          onChange={e => setAge(e.target.value)}\n          type=\"number\"\n        />\n        <button onClick={() => setAge(ageAsNumber + 10)}>\n          Add 10 years\n        </button>\n```\n\n制御されたコンポーネントに渡す `value` は、`undefined` や `null` であってはなりません。初期値を空にしたい場合（例えば、以下の `firstName` フィールドのような場合）、state 変数を空の文字列 (`''`) で初期化してください。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function Form() {\n  const [firstName, setFirstName] = useState('');\n  const [age, setAge] = useState('20');\n  const ageAsNumber = Number(age);\n  return (\n    <>\n      <label>\n        First name:\n        <input\n          value={firstName}\n          onChange={e => setFirstName(e.target.value)}\n        />\n      </label>\n      <label>\n        Age:\n        <input\n          value={age}\n          onChange={e => setAge(e.target.value)}\n          type=\"number\"\n        />\n        <button onClick={() => setAge(ageAsNumber + 10)}>\n          Add 10 years\n        </button>\n      </label>\n      {firstName !== '' &&\n        <p>Your name is {firstName}.</p>\n      }\n      {ageAsNumber > 0 &&\n        <p>Your age is {ageAsNumber}.</p>\n      }\n    </>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin: 5px; }\np { font-weight: bold; }\n```\n\n</Sandpack>\n\n<Pitfall>\n\n**`onChange` を指定せずに `value` を渡すと、入力欄に入力することができなくなります**。入力欄に `value` を渡して制御を行うと、渡した値で入力欄を*強制的に固定*することになります。したがって、state 変数を `value` として渡しても、`onChange` イベントハンドラ内でその state 変数を同期的に更新するのを忘れた場合、React は入力欄を、キーストローク毎に指定した `value` に戻してしまいます。\n\n</Pitfall>\n\n---\n\n### キーストロークごとの再レンダーを最適化する {/*optimizing-re-rendering-on-every-keystroke*/}\n\n制御された入力欄を使用する場合、キーストロークごとに state のセットが行われます。state を含んだコンポーネントが大きなツリーを再レンダーする場合、速度が遅くなる可能性があります。再レンダー時のパフォーマンスを最適化する方法がいくつかあります。\n\n例えば、キーストロークごとにページ内コンテンツをすべて再レンダーするフォームがあるとしましょう。\n\n```js {5-8}\nfunction App() {\n  const [firstName, setFirstName] = useState('');\n  return (\n    <>\n      <form>\n        <input value={firstName} onChange={e => setFirstName(e.target.value)} />\n      </form>\n      <PageContent />\n    </>\n  );\n}\n```\n\n`<PageContent />` は入力値の state に依存していないため、入力値 state を独立したコンポーネントに移動できます。\n\n```js {4,10-17}\nfunction App() {\n  return (\n    <>\n      <SignupForm />\n      <PageContent />\n    </>\n  );\n}\n\nfunction SignupForm() {\n  const [firstName, setFirstName] = useState('');\n  return (\n    <form>\n      <input value={firstName} onChange={e => setFirstName(e.target.value)} />\n    </form>\n  );\n}\n```\n\nこれにより、キーストロークごとに `SignupForm` のみが再レンダーされるため、パフォーマンスが大幅に向上します。\n\n再レンダーを回避する方法がない場合（例えば、`PageContent` が検索ボックスの入力値に依存する場合）、[`useDeferredValue`](/reference/react/useDeferredValue#deferring-re-rendering-for-a-part-of-the-ui) を使用することで、巨大な再レンダーの途中でも、制御された入力欄の応答性を維持できます。\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### テキスト入力欄にタイプしても内容が更新されない {/*my-text-input-doesnt-update-when-i-type-into-it*/}\n\n`value` があるが `onChange` のない入力欄をレンダーすると、コンソールにエラーが表示されます。\n\n```js\n// 🔴 Bug: controlled text input with no onChange handler\n<input value={something} />\n```\n\n<ConsoleBlock level=\"error\">\n\nYou provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.\n\n</ConsoleBlock>\n\nエラーメッセージが示すように、[*初期値*を指定したいだけの場合](#providing-an-initial-value-for-a-text-area)は、代わりに `defaultValue` を渡すようにしてください。\n\n```js\n// ✅ Good: uncontrolled input with an initial value\n<input defaultValue={something} />\n```\n\n[state 変数でこの入力欄を制御したい場合](#controlling-an-input-with-a-state-variable)は、`onChange` ハンドラを指定してください。\n\n```js\n// ✅ Good: controlled input with onChange\n<input value={something} onChange={e => setSomething(e.target.value)} />\n```\n\n値を意図的に読み取り専用にしたい場合は、エラーを抑制するために props として `readOnly` を追加してください。\n\n```js\n// ✅ Good: readonly controlled input without on change\n<input value={something} readOnly={true} />\n```\n\n---\n\n### チェックボックスをクリックしても更新されない {/*my-checkbox-doesnt-update-when-i-click-on-it*/}\n\n`checked` があるが `onChange` のないチェックボックスをレンダーすると、コンソールにエラーが表示されます。\n\n```js\n// 🔴 Bug: controlled checkbox with no onChange handler\n<input type=\"checkbox\" checked={something} />\n```\n\n<ConsoleBlock level=\"error\">\n\nYou provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`.\n\n</ConsoleBlock>\n\nエラーメッセージが示すように、[*初期値*を指定したいだけの場合](#providing-an-initial-value-for-an-input)は、代わりに `defaultChecked` を渡すようにしてください。\n\n```js\n// ✅ Good: uncontrolled checkbox with an initial value\n<input type=\"checkbox\" defaultChecked={something} />\n```\n\n[state 変数でこのチェックボックスを制御したい場合](#controlling-an-input-with-a-state-variable)は、`onChange` ハンドラを指定してください。\n\n```js\n// ✅ Good: controlled checkbox with onChange\n<input type=\"checkbox\" checked={something} onChange={e => setSomething(e.target.checked)} />\n```\n\n<Pitfall>\n\nチェックボックスの場合は、`e.target.value` ではなく `e.target.checked` を読み取る必要があります。\n\n</Pitfall>\n\nチェックボックスを意図的に読み取り専用にしたい場合は、エラーを抑制するために props として `readOnly` を追加してください。\n\n```js\n// ✅ Good: readonly controlled input without on change\n<input type=\"checkbox\" checked={something} readOnly={true} />\n```\n\n---\n\n### キーストロークごとに入力欄のカーソルが先頭に戻る {/*my-input-caret-jumps-to-the-beginning-on-every-keystroke*/}\n\n入力欄を[制御する](#controlling-a-text-area-with-a-state-variable)場合、`onChange` 中でその state 変数を DOM からやってくる入力欄の値に更新する必要があります。\n\nstate を `e.target.value`（チェックボックスの場合は `e.target.checked`）以外のものに更新することはできません。\n\n```js\nfunction handleChange(e) {\n  // 🔴 Bug: updating an input to something other than e.target.value\n  setFirstName(e.target.value.toUpperCase());\n}\n```\n\nまた、非同期に更新することもできません。\n\n```js\nfunction handleChange(e) {\n  // 🔴 Bug: updating an input asynchronously\n  setTimeout(() => {\n    setFirstName(e.target.value);\n  }, 100);\n}\n```\n\nコードを修正するには、state を `e.target.value` の値に同期的に更新します。\n\n```js\nfunction handleChange(e) {\n  // ✅ Updating a controlled input to e.target.value synchronously\n  setFirstName(e.target.value);\n}\n```\n\nこれで問題が解決しない場合、入力欄がキーストロークごとに DOM から削除・再追加されている可能性があります。これは、再レンダーごとに state を誤って[リセット](/learn/preserving-and-resetting-state)している場合に起こります。例えば、入力欄またはその親が常に異なる `key` 属性を受け取っている可能性や、コンポーネント定義をネストしている（これは React では許されておらず、「内側」のコンポーネントがレンダー時に再マウントさえることになります）可能性があります。\n\n---\n\n### \"A component is changing an uncontrolled input to be controlled\" というエラーが発生する {/*im-getting-an-error-a-component-is-changing-an-uncontrolled-input-to-be-controlled*/}\n\n\nコンポーネントに `value` を渡す場合、そのライフサイクル全体を通じて文字列型でなければなりません。\n\n最初に `value={undefined}` を渡しておき、後で `value=\"some string\"` を渡すようなことはできません。なぜなら、React はあなたがコンポーネントを非制御コンポーネントと制御されたコンポーネントのどちらにしたいのか分からなくなるからです。制御されたコンポーネントは常に文字列の `value` を受け取るべきであり、`null` や `undefined` であってはいけません。\n\nあなたの `value` が API や state 変数から来ている場合、それが `null` や `undefined` に初期化されているかもしれません。その場合、まず空の文字列（`''`）にセットするか、`value` が文字列であることを保証するために `value={someValue ?? ''}` を渡すようにしてください。\n\n同様に、チェックボックスに `checked` を渡す場合は、常にブーリアン型であることを確認してください。\n"
  },
  {
    "path": "src/content/reference/react-dom/components/link.md",
    "content": "---\nlink: \"<link>\"\n---\n\n<Intro>\n\n[ブラウザ組み込みの `<link>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)を利用することで、スタイルシートのような外部リソースを使用したり、リンクメタデータでドキュメントへのアノテーション（ラベル付け）を行えます。\n\n```js\n<link rel=\"icon\" href=\"favicon.ico\" />\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<link>` {/*link*/}\n\nスタイルシート、フォント、アイコンなどの外部リソースにリンクしたり、リンクメタデータを使ってドキュメントにアノテーションを行うためには、[ブラウザ組み込みの `<link>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)をレンダーします。任意のコンポーネントから `<link>` をレンダーでき、React は[ほとんどの場合](#special-rendering-behavior)対応する DOM 要素をドキュメントの head に配置します。\n\n```js\n<link rel=\"icon\" href=\"favicon.ico\" />\n```\n\n[さらに例を見る](#usage)\n\n#### props {/*props*/}\n\n`<link>` は、[一般的な要素の props](/reference/react-dom/components/common#common-props) をすべてサポートしています。\n\n* `rel`: 文字列、必須。[リソースとの関係](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel)を指定します。React は [`rel=\"stylesheet\"` となっているリンク](#special-rendering-behavior)を他のリンクとは異なる方法で扱います。\n\n以下の props は `rel=\"stylesheet\"` の場合に適用されます。\n\n* `precedence`: 文字列。React がドキュメントの `<head>` 内で `<link>` DOM ノードを他と比較してどのように順序付けるかを指定します。これによりどのスタイルシートが他のスタイルシートを上書きできるかが決まります。React は、最初に見つけた優先順位の値を「低い」と見なし、後に見つけた優先順位の値を「高い」と見なします。多くのスタイルシステムは、スタイルルールがアトミックであるため、単一の優先順位の値を使用しても問題なく機能します。同じ優先順位を持つスタイルシートは、`<link>` の場合でもインライン `<style>` タグの場合でも、あるいは [`preinit`](/reference/react-dom/preinit) 関数を使用してロードされた場合でも、一緒に配置されます。\n* `media`: 文字列。スタイルシートの適用を特定の[メディアクエリ](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries)に制限します。\n* `title`: 文字列。[代替スタイルシート](https://developer.mozilla.org/en-US/docs/Web/CSS/Alternative_style_sheets)の名前を指定します。\n\n以下の props は `rel=\"stylesheet\"` の場合に適用されますが、React の[スタイルシートに関する特別な扱い](#special-rendering-behavior)を無効にします。\n\n* `disabled`: ブール値。スタイルシートを無効にします。\n* `onError`: 関数。スタイルシートの読み込みに失敗したときに呼び出されます。\n* `onLoad`: 関数。スタイルシートの読み込みが完了したときに呼び出されます。\n\n以下の props は `rel=\"preload\"` または `rel=\"modulepreload\"` の場合に適用されます。\n\n* `as`: 文字列。リソースの種別。可能な値は `audio`、`document`、`embed`、`fetch`、`font`、`image`、`object`、`script`、`style`、`track`、`video`、`worker` です。\n* `imageSrcSet`: 文字列。`as=\"image\"` の場合にのみ適用されます。[画像のソースセット](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)を指定します。\n* `imageSizes`: 文字列。`as=\"image\"` の場合にのみ適用されます。[画像のサイズ](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)を指定します。\n\n以下の props は、`rel=\"icon\"` または `rel=\"apple-touch-icon\"` の場合に適用されます。\n\n* `sizes`: 文字列。アイコンの[サイズ](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)。\n\n以下の props はすべての場合に適用されます。\n\n* `href`: 文字列。リンクするリソースの URL。\n* `crossOrigin`: 文字列。使用する [CORS ポリシー](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin)。可能な値は `anonymous` と `use-credentials` です。`as` が `\"fetch\"` に設定されている場合は必須です。\n* `referrerPolicy`: 文字列。フェッチ時に送信する [Referrer ヘッダ](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#referrerpolicy)。可能な値は `no-referrer-when-downgrade`（デフォルト）、`no-referrer`、`origin`、`origin-when-cross-origin`、および `unsafe-url` です。\n* `fetchPriority`: 文字列。リソースのフェッチに対する相対的な優先度のヒントです。可能な値は `auto`（デフォルト）、`high`、および `low` です。\n* `hrefLang`: 文字列。リンクするリソースの言語。\n* `integrity`: 文字列。[真正性を検証する](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)ために使用するリソースの暗号化ハッシュ。\n* `type`：文字列。リンクされるリソースの MIME タイプ。\n\n以下は React での使用が**推奨されない** props です。\n\n* `blocking`: 文字列。`\"render\"` と設定されている場合、スタイルシートがロードされるまでページを描画しないようブラウザに指示します。React ではサスペンスを通じてより細かい制御を提供します。\n\n#### 特別なレンダー動作 {/*special-rendering-behavior*/}\n\n`<link>` コンポーネントが React ツリー内のどこでレンダーされていても、React は対応する DOM 要素を常にドキュメントの `<head>` 内に配置します。`<head>` は DOM 内で `<link>` が存在できる唯一の有効な場所ですが、ある特定のページを表すコンポーネントが自分自身で `<link>` コンポーネントをレンダーできれば有用であり、コンポーネントの組み合わせやすさが保たれます。\n\nこれにはいくつかの例外があります。\n\n* `<link>` に props として `rel=\"stylesheet\"` がある場合、この特別な動作を得るために props として `precedence` も必要です。これは、ドキュメント内におけるスタイルシートの順序は重要であり、このスタイルシートを他のスタイルシートに対してどのような順序で配置するか React が知る必要があるためです。`precedence` が指定されていない場合、特別な動作は起きなくなります。\n* `<link>` に props として [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop) が存在する場合、特別な動作は発生しません。この場合リンクはドキュメントに適用されるのではなく、ページの特定の部分に関するメタデータを表すことになるからです。\n* `<link>` に props として `onLoad` または `onError` がある場合も同様です。この場合リンクされたリソースのロードを React コンポーネント内で手動で管理しようとしているということだからです。\n\n#### スタイルシートの特別な動作 {/*special-behavior-for-stylesheets*/}\n\nさらに、`<link>` がスタイルシートへのリンクである（つまり props として `rel=\"stylesheet\"` が含まれている）場合、React はそれを以下のように特別に扱います。\n\n* `<link>` をレンダーしているコンポーネントは、スタイルシートが読み込まれている間、[サスペンド](/reference/react/Suspense)します。\n* 複数のコンポーネントが同じスタイルシートへのリンクをレンダーしている場合、React は重複が起きないよう、DOM にリンクをひとつだけ配置します。2 つのリンクは同じ `href` プロパティを持っている場合に同じものと見なされます。\n\nこの特別な動作には、以下の 2 つの例外があります。\n\n* リンクに props として `precedence` がない場合、特別な動作は発生しません。ドキュメント内のスタイルシートの順序は重要であり、React がこのスタイルシートを他のスタイルシートに対してどのような順序で配置するのか、`precedence` プロパティを使用して指定する必要があるからです。\n* props として `onLoad`、`onError`、または `disabled` のいずれかを指定した場合、特別な動作は発生しません。これらの props は、コンポーネント内でスタイルシートの読み込みを手動で管理してしていることを意味するからです。\n\nこの特別な動作に関して、以下の 2 つの注意点があります。\n\n* リンクがレンダーされた後、React は props に変更があってもそれを無視します（開発中にこれが起きた場合は React が警告を発します）。\n* コンポーネントがアンマウントされた後も、React は DOM にリンクを残すことがあります。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 関連リソースへのリンク {/*linking-to-related-resources*/}\n\nアイコン、推奨 (canonical) URL、またはピンバック (pingback) といった関連リソースへのリンクを、ドキュメントにアノテーション（注記）として与えることができます。React ツリー内のどこにレンダーされている場合でも、React はこのようなメタデータをドキュメントの `<head>` 内に配置します。\n\n<SandpackWithHTMLOutput>\n\n```js src/App.js active\nimport ShowRenderedHTML from './ShowRenderedHTML.js';\n\nexport default function BlogPage() {\n  return (\n    <ShowRenderedHTML>\n      <link rel=\"icon\" href=\"favicon.ico\" />\n      <link rel=\"pingback\" href=\"http://www.example.com/xmlrpc.php\" />\n      <h1>My Blog</h1>\n      <p>...</p>\n    </ShowRenderedHTML>\n  );\n}\n```\n\n</SandpackWithHTMLOutput>\n\n### スタイルシートへのリンク {/*linking-to-a-stylesheet*/}\n\nコンポーネントが正しく表示されるために特定のスタイルシートに依存している場合、そのスタイルシートへのリンクを当該コンポーネント内でレンダーできます。スタイルシートが読み込まれている間、コンポーネントは[サスペンド](/reference/react/Suspense)します。props として `precedence` を指定する必要があり、これにより React が他のスタイルシートに対してこのスタイルシートを相対的にどのように配置するか指示します。優先度が高いスタイルシートは、優先度が低いものをオーバーライドできます。\n\n<Note>\nスタイルシートを使用する場合、[preinit](/reference/react-dom/preinit) 関数を呼び出すことが有用です。この関数を呼び出すことで、たとえば [HTTP Early Hints レスポンス](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103)を送信でき、ブラウザがスタイルシートのフェッチをより早く開始できるかもしれません。\n</Note>\n\n<SandpackWithHTMLOutput>\n\n```js src/App.js active\nimport ShowRenderedHTML from './ShowRenderedHTML.js';\n\nexport default function SiteMapPage() {\n  return (\n    <ShowRenderedHTML>\n      <link rel=\"stylesheet\" href=\"sitemap.css\" precedence=\"medium\" />\n      <p>...</p>\n    </ShowRenderedHTML>\n  );\n}\n```\n\n</SandpackWithHTMLOutput>\n\n### スタイルシートの優先度の制御 {/*controlling-stylesheet-precedence*/}\n\nスタイルシートは互いに競合することがあり、その場合ブラウザはドキュメント内で後に来るものを採用します。React では props である `precedence` を使用してスタイルシートの順序を制御できます。以下の例では 2 つのコンポーネントがスタイルシートをレンダーしています。優先度の高いリンクをレンダーしているコンポーネントが先に来ていますが、ドキュメント内では後に配置されます。\n\n<SandpackWithHTMLOutput>\n\n```js src/App.js active\nimport ShowRenderedHTML from './ShowRenderedHTML.js';\n\nexport default function HomePage() {\n  return (\n    <ShowRenderedHTML>\n      <FirstComponent />\n      <SecondComponent />\n      <ThirdComponent/>\n      ...\n    </ShowRenderedHTML>\n  );\n}\n\nfunction FirstComponent() {\n  return <link rel=\"stylesheet\" href=\"first.css\" precedence=\"first\" />;\n}\n\nfunction SecondComponent() {\n  return <link rel=\"stylesheet\" href=\"second.css\" precedence=\"second\" />;\n}\n\nfunction ThirdComponent() {\n  return <link rel=\"stylesheet\" href=\"third.css\" precedence=\"first\" />;\n}\n\n```\n\n</SandpackWithHTMLOutput>\n\n`precedence` として指定する値自体は任意であり、何を指定するかは自由であることに注意してください。React は最初に現れた値を「低優先度」と考え、後に現れたものを「高優先度」と見なします。\n\n### スタイルシートレンダーの重複解消処理 {/*deduplicated-stylesheet-rendering*/}\n\n複数のコンポーネントが同じスタイルシートをレンダーする場合、React はドキュメントの head 内に `<link>` をひとつだけ配置します。\n\n<SandpackWithHTMLOutput>\n\n```js src/App.js active\nimport ShowRenderedHTML from './ShowRenderedHTML.js';\n\nexport default function HomePage() {\n  return (\n    <ShowRenderedHTML>\n      <Component />\n      <Component />\n      ...\n    </ShowRenderedHTML>\n  );\n}\n\nfunction Component() {\n  return <link rel=\"stylesheet\" href=\"styles.css\" precedence=\"medium\" />;\n}\n```\n\n</SandpackWithHTMLOutput>\n\n### リンクでドキュメント内の特定の項目にアノテーションを行う {/*annotating-specific-items-within-the-document-with-links*/}\n\n`<link>` コンポーネントの props として `itemProp` を使用することで、ドキュメント内の特定の項目に、関連リソースへのリンクをアノテーションできます。この場合、React はこれらのアノテーションをドキュメントの `<head>` 内に配置するのではなく、他の React コンポーネントと同様に配置します。\n\n```js\n<section itemScope>\n  <h3>Annotating specific items</h3>\n  <link itemProp=\"author\" href=\"http://example.com/\" />\n  <p>...</p>\n</section>\n```\n"
  },
  {
    "path": "src/content/reference/react-dom/components/meta.md",
    "content": "---\nmeta: \"<meta>\"\n---\n\n<Intro>\n\n[ブラウザ組み込みの `<meta>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)を利用することで、ドキュメントにメタデータを追加できます。\n\n```js\n<meta name=\"keywords\" content=\"React, JavaScript, semantic markup, html\" />\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<meta>` {/*meta*/}\n\nドキュメントにメタデータを追加するためには、[ブラウザ組み込みの `<meta>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)をレンダーします。任意のコンポーネントから `<meta>` をレンダーでき、React は対応する DOM 要素を常にドキュメントの head 内に配置します。\n\n```js\n<meta name=\"keywords\" content=\"React, JavaScript, semantic markup, html\" />\n```\n\n[さらに例を見る](#usage)\n\n#### props {/*props*/}\n\n`<meta>` は、[一般的な要素の props](/reference/react-dom/components/common#common-props) をすべてサポートしています。\n\n`name`、`httpEquiv`、`charset`、`itemProp` のうち、props として*どれかひとつだけ*を指定しなければなりません。これらの props のうちどれが指定されているかによって、`<meta>` コンポーネントの動作は異なります。\n\n* `name`: 文字列。ドキュメントに添付される[メタデータの種類](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name)を指定します。\n* `charset`: 文字列。ドキュメントで使用される文字セットを指定します。有効な値は `\"utf-8\"` のみです。\n* `httpEquiv`: 文字列。ドキュメントを処理するためのディレクティブを指定します。\n* `itemProp`: 文字列。ドキュメント全体ではなく、ドキュメント内の特定のアイテムに関するメタデータを指定する際に用います。\n* `content`: 文字列。`name` や `itemProp` と共に使用される場合はメタデータの内容を指定し、`httpEquiv` と共に使用される場合はディレクティブの動作を指定します。\n\n#### 特別なレンダー動作 {/*special-rendering-behavior*/}\n\n`<meta>` コンポーネントが React ツリー内のどこでレンダーされていても、React は対応する DOM 要素を常にドキュメントの `<head>` 内に配置します。`<head>` は DOM 内で `<meta>` が存在できる唯一の有効な場所ですが、ある特定のページを表すコンポーネントが自分自身で `<meta>` コンポーネントをレンダーできれば有用であり、コンポーネントの組み合わせやすさが保たれます。\n\nただし、例外がひとつあります。`<meta>` に props として [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop) がある場合、特別な動作は発生しません。この場合ドキュメントに関するメタデータではなく、ページの特定の部分に関するメタデータを表しているためです。\n\n---\n\n## 使用法 {/*usage*/}\n\n### ドキュメントにメタデータによるアノテーションを加える {/*annotating-the-document-with-metadata*/}\n\nキーワード、概要文、著者名といったメタデータを用いて、ドキュメントにアノテーション（ラベル付け）が行えます。React ツリー内のどこでレンダーされている場合でも、React はこのメタデータをドキュメントの `<head>` 内に配置します。\n\n```html\n<meta name=\"author\" content=\"John Smith\" />\n<meta name=\"keywords\" content=\"React, JavaScript, semantic markup, html\" />\n<meta name=\"description\" content=\"API reference for the <meta> component in React DOM\" />\n```\n\n任意のコンポーネントから `<meta>` コンポーネントをレンダーできます。React は `<meta>` DOM ノードをドキュメントの `<head>` に配置します。\n\n<SandpackWithHTMLOutput>\n\n```js src/App.js active\nimport ShowRenderedHTML from './ShowRenderedHTML.js';\n\nexport default function SiteMapPage() {\n  return (\n    <ShowRenderedHTML>\n      <meta name=\"keywords\" content=\"React\" />\n      <meta name=\"description\" content=\"A site map for the React website\" />\n      <h1>Site Map</h1>\n      <p>...</p>\n    </ShowRenderedHTML>\n  );\n}\n```\n\n</SandpackWithHTMLOutput>\n\n### メタデータでドキュメント内の特定の項目にアノテーションを行う {/*annotating-specific-items-within-the-document-with-metadata*/}\n\n`<meta>` コンポーネントの props として `itemProp` を使用することで、ドキュメント内の特定の項目に、メタデータをアノテーションできます。この場合、React はこれらのアノテーションをドキュメントの `<head>` 内に配置するのではなく、他の React コンポーネントと同様に配置します。\n\n```js\n<section itemScope>\n  <h3>Annotating specific items</h3>\n  <meta itemProp=\"description\" content=\"API reference for using <meta> with itemProp\" />\n  <p>...</p>\n</section>\n```\n"
  },
  {
    "path": "src/content/reference/react-dom/components/option.md",
    "content": "---\ntitle: \"<option>\"\n---\n\n<Intro>\n\n[ブラウザ組み込みの `<option>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)を利用することで、[`<select>`](/reference/react-dom/components/select) ボックス内に選択肢をレンダーすることができます。\n\n```js\n<select>\n  <option value=\"someOption\">Some option</option>\n  <option value=\"otherOption\">Other option</option>\n</select>\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<option>` {/*option*/}\n\n[ブラウザ組み込みの `<option>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option) を利用することで、[`<select>`](/reference/react-dom/components/select) ボックス内にオプションをレンダーすることができます。\n\n```js\n<select>\n  <option value=\"someOption\">Some option</option>\n  <option value=\"otherOption\">Other option</option>\n</select>\n```\n\n[さらに例を見る](#usage)\n\n#### props {/*props*/}\n\n`<option>` は、[一般的な要素の props](/reference/react-dom/components/common#common-props) をすべてサポートしています。\n\nさらに、`<option>` は以下の props をサポートしています：\n\n* [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#disabled): ブーリアン。`true` の場合、オプションは選択できなくなり、薄暗く表示されます。\n* [`label`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#label): 文字列。オプションの意味を指定します。指定しない場合、オプション内のテキストが使用されます。\n* [`value`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#value): このオプションが選択された場合に、[親の `<select>` をフォームで送信する](/reference/react-dom/components/select#reading-the-select-box-value-when-submitting-a-form)際に使用される値。\n\n#### 注意点 {/*caveats*/}\n\n* React は `<option>` の `selected` 属性をサポートしていません。代わりに、このオプションの `value` を親の [`<select defaultValue>`](/reference/react-dom/components/select#providing-an-initially-selected-option) に渡して非制御のセレクトボックスを作成するか、[`<select value>`](/reference/react-dom/components/select#controlling-a-select-box-with-a-state-variable) に渡して制御されたセレクトボックスを作成します。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 選択肢を含むセレクトボックスを表示する {/*displaying-a-select-box-with-options*/}\n\n`<option>` コンポーネントのリストを内部に含む `<select>` をレンダーして、セレクトボックスを表示します。各 `<option>` に、フォームで送信されるデータを表す `value` を指定してください。\n\n`<select>` を `<option>` コンポーネントのリストと共に表示する方法についての詳細は、[こちらをご覧ください](/reference/react-dom/components/select)。\n\n<Sandpack>\n\n```js\nexport default function FruitPicker() {\n  return (\n    <label>\n      Pick a fruit:\n      <select name=\"selectedFruit\">\n        <option value=\"apple\">Apple</option>\n        <option value=\"banana\">Banana</option>\n        <option value=\"orange\">Orange</option>\n      </select>\n    </label>\n  );\n}\n```\n\n```css\nselect { margin: 5px; }\n```\n\n</Sandpack>  \n\n"
  },
  {
    "path": "src/content/reference/react-dom/components/progress.md",
    "content": "---\ntitle: \"<progress>\"\n---\n\n<Intro>\n\n[ブラウザ組み込みの `<progress>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress)を利用することで、進行状況のインジケータをレンダーすることができます。\n\n```js\n<progress value={0.5} />\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<progress>` {/*progress*/}\n\n進行状況のインジケータを表示するためには、[ブラウザ組み込みの `<progress>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress)をレンダーします。\n\n```js\n<progress value={0.5} />\n```\n\n[さらに例を見る](#usage)\n\n#### props {/*props*/}\n\n`<progress>` は[一般的な要素の props](/reference/react-dom/components/common#common-props) をすべてサポートしています。\n\nさらに、`<progress>` は以下の props もサポートしています：\n\n* [`max`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress#max)： 数値。`value` の最大値を指定します。デフォルトは `1` です。\n* [`value`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress#value)： `0` から `max` までの数値、または進行状況が不定 (indeterminate) であることを表す `null`。完了した量を指定します。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 進行状況のインジケータの制御 {/*controlling-a-progress-indicator*/}\n\n進行状況のインジケータを表示するためには、`<progress>` コンポーネントをレンダーします。`0` から `max` 値までの数値を `value` として渡すことができます。`max` 値を渡さない場合、デフォルトで `1` とみなされます。\n\n進行状況のインジケータを不定状態にするには `value={null}` を渡します。\n\n<Sandpack>\n\n```js\nexport default function App() {\n  return (\n    <>\n      <progress value={0} />\n      <progress value={0.5} />\n      <progress value={0.7} />\n      <progress value={75} max={100} />\n      <progress value={1} />\n      <progress value={null} />\n    </>\n  );\n}\n```\n\n```css\nprogress { display: block; }\n```\n\n</Sandpack>\n"
  },
  {
    "path": "src/content/reference/react-dom/components/script.md",
    "content": "---\nscript: \"<script>\"\n---\n\n<Intro>\n\n[ブラウザ組み込みの `<script>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)を利用することで、ドキュメントにスクリプトを追加できます。\n\n```js\n<script> alert(\"hi!\") </script>\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<script>` {/*script*/}\n\nドキュメントにインラインスクリプトまたは外部スクリプトを追加するためには、[ブラウザ組み込みの `<script>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)をレンダーします。任意のコンポーネントから `<script>` をレンダーでき、React は[特定の場合](#special-rendering-behavior)に対応する DOM 要素をドキュメントの head 内に配置し、同一スクリプトの重複解消処理を行います。\n\n```js\n<script> alert(\"hi!\") </script>\n<script src=\"script.js\" />\n```\n\n[さらに例を見る](#usage)\n\n#### props {/*props*/}\n\n`<script>` は、[一般的な要素の props](/reference/react-dom/components/common#common-props) をすべてサポートしています。\n\nprops として `children` または `src` のいずれかが必要です。\n\n* `children`: 文字列。インラインスクリプトのソースコード。\n* `src`: 文字列。外部スクリプトの URL。\n\n他に、以下の props がサポートされます。\n\n* `async`: ブーリアン。ドキュメントの残りの部分の処理を完了するまでブラウザにスクリプトの実行を遅延させることを許可します。これはパフォーマンスのために推奨される動作です。\n* `crossOrigin`: 文字列。使用する [CORS ポリシー](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin)。可能な値は `anonymous` と `use-credentials` です。\n* `fetchPriority`: 文字列。複数のスクリプトを同時にフェッチする際に、ブラウザにスクリプトの優先付けを行わせます。`\"high\"`、`\"low\"`、`\"auto\"`（デフォルト）のいずれかです。\n* `integrity`: 文字列。文字列。[真正性を検証する](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)ために使用するスクリプトの暗号化ハッシュ。\n* `noModule`: ブーリアン。ES モジュールをサポートするブラウザでこのスクリプトを無効にすることで、サポートしていないブラウザ用のフォールバックスクリプトとして利用できるようにします。\n* `nonce`: 文字列。厳格なコンテンツセキュリティポリシーを使用する際に[リソースを許可するための暗号化 nonce](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce)。\n* `referrer`: 文字列。スクリプトをフェッチする際、およびそのスクリプトが更にリソースフェッチする際に送信する [Referer ヘッダー](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#referrerpolicy)を指定します。\n* `type`: 文字列。スクリプトが[従来のスクリプト、ES モジュール、またはインポートマップ](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type)のいずれであるかを指定します。\n\n以下の props は React による[スクリプトの特別な扱い](#special-rendering-behavior)を無効にします。\n\n* `onError`: 関数。スクリプトのロードに失敗したときに呼び出されます。\n* `onLoad`: 関数。スクリプトのロードが完了したときに呼び出されます。\n\n以下は React での使用が**推奨されない** props です。\n\n* `blocking`: 文字列。`\"render\"` と設定されている場合、スクリプトがロードされるまでページを描画しないようブラウザに指示します。React ではサスペンスを通じてより細かい制御を提供します。\n* `defer`: 文字列。ドキュメントの読み込みが完了するまでブラウザによるスクリプトの実行を防ぎます。これはストリーミングでサーバレンダーされるコンポーネントと互換性がありません。代わりに `async` を使用してください。\n\n#### 特別なレンダー動作 {/*special-rendering-behavior*/}\n\nReact は `<script>` コンポーネントをドキュメントの `<head>` に移動させ、同一スクリプトの重複解消処理を行います。\n\nこの動作を有効にするには、props として `src` と `async={true}` の props を指定してください。React は同じ `src` を持つスクリプトが重複しないようにします。スクリプトを安全に移動させるために `async` が true である必要があります。\n\nこの特別な動作に関して、以下の 2 つの注意点があります。\n\n* スクリプトがレンダーされた後、React は props に変更があってもそれを無視します（開発中にこれが起きた場合は React が警告を発します）。\n* スクリプトをレンダーしていたコンポーネントがアンマウントされた後も、React は DOM にスクリプトを残すことがあります。（スクリプトは DOM に挿入された際に一度だけ実行されるものなので、これによる影響はありません。）\n\n---\n\n## 使用法 {/*usage*/}\n\n### 外部スクリプトのレンダー {/*rendering-an-external-script*/}\n\nコンポーネントが正しく表示されるために特定のスクリプトに依存している場合、当該コンポーネント内で `<script>` をレンダーできます。\nただしコンポーネントはスクリプトのロードが完了する前にコミットされる可能性があります。\n`onLoad` プロパティを使うなどで `load` イベントが発火したのを確認してからスクリプトの内容に依存するコードを使用するようにしてください。\n\nReact 重複解消処理を行うため、複数のコンポーネントが同じ `src` を持つスクリプトをレンダーしている場合でも、React はそれらのうちひとつだけを DOM に挿入します。\n\n<SandpackWithHTMLOutput>\n\n```js src/App.js active\nimport ShowRenderedHTML from './ShowRenderedHTML.js';\n\nfunction Map({lat, long}) {\n  return (\n    <>\n      <script async src=\"map-api.js\" onLoad={() => console.log('script loaded')} />\n      <div id=\"map\" data-lat={lat} data-long={long} />\n    </>\n  );\n}\n\nexport default function Page() {\n  return (\n    <ShowRenderedHTML>\n      <Map />\n    </ShowRenderedHTML>\n  );\n}\n```\n\n</SandpackWithHTMLOutput>\n\n<Note>\nスクリプトを使用する場合、[preinit](/reference/react-dom/preinit) 関数を呼び出すことが有用です。この関数を呼び出すことで、たとえば [HTTP Early Hints レスポンス](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103)を送信でき、ブラウザがスクリプトのフェッチをより早く開始できるかもしれません。\n</Note>\n\n### インラインスクリプトのレンダー {/*rendering-an-inline-script*/}\n\nインラインスクリプトを含めるには、`<script>` コンポーネントをレンダーする際に children としてスクリプトのソースコードを指定します。インラインスクリプトは重複解消処理が行われず、ドキュメントの `<head>` に移動されません。\n\n<SandpackWithHTMLOutput>\n\n```js src/App.js active\nimport ShowRenderedHTML from './ShowRenderedHTML.js';\n\nfunction Tracking() {\n  return (\n    <script>\n      ga('send', 'pageview');\n    </script>\n  );\n}\n\nexport default function Page() {\n  return (\n    <ShowRenderedHTML>\n      <h1>My Website</h1>\n      <Tracking />\n      <p>Welcome</p>\n    </ShowRenderedHTML>\n  );\n}\n```\n\n</SandpackWithHTMLOutput>\n"
  },
  {
    "path": "src/content/reference/react-dom/components/select.md",
    "content": "---\ntitle: \"<select>\"\n---\n\n<Intro>\n\n[ブラウザ組み込みの `<select>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)を利用することで、選択肢を含むセレクトボックスをレンダーすることができます。\n\n```js\n<select>\n  <option value=\"someOption\">Some option</option>\n  <option value=\"otherOption\">Other option</option>\n</select>\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<select>` {/*select*/}\n\nセレクトボックスを表示するためには、[ブラウザ組み込みの `<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select) コンポーネントをレンダーします。\n\n```js\n<select>\n  <option value=\"someOption\">Some option</option>\n  <option value=\"otherOption\">Other option</option>\n</select>\n```\n\n[さらに例を見る](#usage)\n\n#### props {/*props*/}\n\n`<select>` は[一般的な要素の props](/reference/react-dom/components/common#common-props) をすべてサポートしています。\n\n`value` プロパティを渡すことで、[セレクトボックスを制御されたコンポーネント (controlled component)](#controlling-a-select-box-with-a-state-variable) にできます。\n\n* `value`: 文字列（または [`multiple={true}`](#enabling-multiple-selection) の場合は文字列の配列）。どのオプションが選択されているかを制御します。それぞれの value 文字列は、`<select>` 内にネストされたいずれかの `<option>` の `value` と一致します。\n\n`value` を渡す場合は、渡された値を更新する `onChange` ハンドラも渡す必要があります。\n\nもし `<select>` を非制御コンポーネント (uncontrolled component) として使用する場合は、代わりに `defaultValue` プロパティを渡すことができます。\n\n* `defaultValue`: 文字列（または [`multiple={true}`](#enabling-multiple-selection) の場合は文字列の配列）。[デフォルトのオプションを指定](#providing-an-initially-selected-option) します。\n\n以下の `<select>` の props は、非制御セレクトボックスと制御されたセレクトボックスの両方で用いられます。\n\n* [`autoComplete`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#autocomplete): 文字列。可能な[オートコンプリート動作](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values)の 1 つを指定します。\n* [`autoFocus`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#autofocus): ブーリアン。`true` の場合、React は要素をマウント時にフォーカスします。\n* `children`: `<select>` は [`<option>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)、[`<optgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup)、[`<datalist>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist) コンポーネントを子として受け入れます。カスタムコンポーネントを渡しても構いませんが、その場合は最終的に上記のコンポーネントのいずれかにレンダーされる必要があります。`<option>` タグを最終的にレンダーする独自のコンポーネントを渡す場合、レンダーする各 `<option>` には `value` が必要です。\n* [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#disabled): ブーリアン。`true` の場合、セレクトボックスはインタラクティブではなくなり、薄暗く表示されます。 \n* [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#form): 文字列。このセレクトボックスが属する `<form>` 要素の `id` を指定します。省略した場合、最も近い親フォームが対象となります。\n* [`multiple`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#multiple): ブーリアン。`true` の場合、ブラウザで[複数選択](#enabling-multiple-selection)が可能になります。\n* [`name`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#name): 文字列。[フォームとともに送信される](#reading-the-select-box-value-when-submitting-a-form)この選択ボックスの名前を指定します。 \n* `onChange`: [`Event` ハンドラ](/reference/react-dom/components/common#event-handler)関数。[制御されたセレクトボックス](#controlling-a-select-box-with-a-state-variable)では必要です。ユーザが別のオプションを選択したときに即座に発火します。ブラウザの [`input` イベント](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event)と同様に動作します。\n* `onChangeCapture`: `onChange` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョンです。\n* [`onInput`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event): [`Event` ハンドラ](/reference/react-dom/components/common#event-handler)関数。ユーザによって値が変更されたときに即座に発火します。歴史的な理由から、React では代わりに同様の動作をする `onChange` を使用するのが慣習となっています。\n* `onInputCapture`: `onInput` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョンです。\n* [`onInvalid`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/invalid_event): [`Event` ハンドラ](/reference/react-dom/components/common#event-handler)関数。フォームの送信時に入力の検証に失敗した場合に発火します。組み込みの `invalid` イベントとは異なり、React の `onInvalid` イベントはバブルします。\n* `onInvalidCapture`: `onInvalid` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョンです。\n* [`required`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#required): ブーリアン。`true` の場合、フォームを送信する際に値の指定を必須にします。\n* [`size`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#size): 数値。`multiple={true}` となっている選択ボックスにおける最初に表示させたい項目の数を指定します。\n\n#### 注意点 {/*caveats*/}\n\n- HTML とは異なり、`<option>` に `selected` 属性を渡すことはサポートされていません。代わりに、非制御のセレクトボックスには [`<select defaultValue>`](#providing-an-initially-selected-option) を使用し、制御されたセレクトボックスには [`<select value>`](#controlling-a-select-box-with-a-state-variable) を使用します。\n- セレクトボックスに `value` プロパティが渡されると、[制御されたコンポーネントとして扱われます](#controlling-a-select-box-with-a-state-variable)。\n- セレクトボックスは制御されたコンポーネントと非制御コンポーネントに同時になることはできません。\n- セレクトボックスは、ライフタイム中に制御されたコンポーネントから非制御コンポーネント、またはその逆に切り替えることはできません。\n- すべての制御されたセレクトボックスには、制御に使っている state を同期的に更新するための `onChange` イベントハンドラが必要です。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 選択肢を含むセレクトボックスを表示する {/*displaying-a-select-box-with-options*/}\n\n`<option>` コンポーネントのリストを内部に含む `<select>` をレンダーして、セレクトボックスを表示します。各 `<option>` に、フォームで送信されるデータを表す `value` を指定してください。\n\n<Sandpack>\n\n```js\nexport default function FruitPicker() {\n  return (\n    <label>\n      Pick a fruit:\n      <select name=\"selectedFruit\">\n        <option value=\"apple\">Apple</option>\n        <option value=\"banana\">Banana</option>\n        <option value=\"orange\">Orange</option>\n      </select>\n    </label>\n  );\n}\n```\n\n```css\nselect { margin: 5px; }\n```\n\n</Sandpack>  \n\n---\n\n### セレクトボックスにラベルを付ける {/*providing-a-label-for-a-select-box*/}\n\n通常、すべての `<select>` は [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label) タグ内に配置します。これにより、ブラウザに対してこのラベルがそのセレクトボックスに関連付けられていることが伝わります。ユーザがラベルをクリックすると、ブラウザは自動的にセレクトボックスにフォーカスします。これはアクセシビリティの観点からも重要です。ユーザがセレクトボックスにフォーカスすると、スクリーンリーダがラベルのキャプションを読み上げます。\n\nもし `<label>` 内に `<select>` をネストできない場合は、同じ ID を `<select id>` と [`<label htmlFor>`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor) に渡すことで関連付けることができます。同一コンポーネントの複数のインスタンス間での競合を避けるために、[`useId`](/reference/react/useId) を使用してそのような ID を生成してください。\n\n<Sandpack>\n\n```js\nimport { useId } from 'react';\n\nexport default function Form() {\n  const vegetableSelectId = useId();\n  return (\n    <>\n      <label>\n        Pick a fruit:\n        <select name=\"selectedFruit\">\n          <option value=\"apple\">Apple</option>\n          <option value=\"banana\">Banana</option>\n          <option value=\"orange\">Orange</option>\n        </select>\n      </label>\n      <hr />\n      <label htmlFor={vegetableSelectId}>\n        Pick a vegetable:\n      </label>\n      <select id={vegetableSelectId} name=\"selectedVegetable\">\n        <option value=\"cucumber\">Cucumber</option>\n        <option value=\"corn\">Corn</option>\n        <option value=\"tomato\">Tomato</option>\n      </select>\n    </>\n  );\n}\n```\n\n```css\nselect { margin: 5px; }\n```\n\n</Sandpack>\n\n\n---\n\n### デフォルトのオプションを指定する {/*providing-an-initially-selected-option*/}\n\nデフォルトでは、ブラウザはリスト内の最初の `<option>` を選択します。異なるオプションをデフォルトで選択する場合は、その `<option>` の `value` を `<select>` 要素の `defaultValue` として渡します。\n\n<Sandpack>\n\n```js\nexport default function FruitPicker() {\n  return (\n    <label>\n      Pick a fruit:\n      <select name=\"selectedFruit\" defaultValue=\"orange\">\n        <option value=\"apple\">Apple</option>\n        <option value=\"banana\">Banana</option>\n        <option value=\"orange\">Orange</option>\n      </select>\n    </label>\n  );\n}\n```\n\n```css\nselect { margin: 5px; }\n```\n\n</Sandpack>  \n\n<Pitfall>\n\nHTML とは異なり、個々の `<option>` に `selected` 属性を渡すことはサポートされていません。\n\n</Pitfall>\n\n---\n\n### 複数選択を有効にする {/*enabling-multiple-selection*/}\n\n複数のオプションを選択できるようにするには、`<select>` に `multiple={true}` を渡します。さらに初期選択オプションを指定するために `defaultValue` を渡したい場合は、配列にする必要があります。\n\n<Sandpack>\n\n```js\nexport default function FruitPicker() {\n  return (\n    <label>\n      Pick some fruits:\n      <select\n        name=\"selectedFruit\"\n        defaultValue={['orange', 'banana']}\n        multiple={true}\n      >\n        <option value=\"apple\">Apple</option>\n        <option value=\"banana\">Banana</option>\n        <option value=\"orange\">Orange</option>\n      </select>\n    </label>\n  );\n}\n```\n\n```css\nselect { display: block; margin-top: 10px; width: 200px; }\n```\n\n</Sandpack>\n\n---\n\n### フォーム送信時にセレクトボックスから値を読み取る {/*reading-the-select-box-value-when-submitting-a-form*/}\n\nセレクトボックスを [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) で囲み、その中に [`<button type=\"submit\">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) を配置します。これにより、`<form onSubmit>` イベントハンドラが呼び出されます。デフォルトでは、ブラウザはフォームデータを現在の URL に送信し、ページを更新します。`e.preventDefault()` を呼び出すことで、その振る舞いをオーバーライドできます。[`new FormData(e.target)`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) を使用してフォームデータを読み込みます。\n<Sandpack>\n\n```js\nexport default function EditPost() {\n  function handleSubmit(e) {\n    // Prevent the browser from reloading the page\n    e.preventDefault();\n    // Read the form data\n    const form = e.target;\n    const formData = new FormData(form);\n    // You can pass formData as a fetch body directly:\n    fetch('/some-api', { method: form.method, body: formData });\n    // You can generate a URL out of it, as the browser does by default:\n    console.log(new URLSearchParams(formData).toString());\n    // You can work with it as a plain object.\n    const formJson = Object.fromEntries(formData.entries());\n    console.log(formJson); // (!) This doesn't include multiple select values\n    // Or you can get an array of name-value pairs.\n    console.log([...formData.entries()]);\n  }\n\n  return (\n    <form method=\"post\" onSubmit={handleSubmit}>\n      <label>\n        Pick your favorite fruit:\n        <select name=\"selectedFruit\" defaultValue=\"orange\">\n          <option value=\"apple\">Apple</option>\n          <option value=\"banana\">Banana</option>\n          <option value=\"orange\">Orange</option>\n        </select>\n      </label>\n      <label>\n        Pick all your favorite vegetables:\n        <select\n          name=\"selectedVegetables\"\n          multiple={true}\n          defaultValue={['corn', 'tomato']}\n        >\n          <option value=\"cucumber\">Cucumber</option>\n          <option value=\"corn\">Corn</option>\n          <option value=\"tomato\">Tomato</option>\n        </select>\n      </label>\n      <hr />\n      <button type=\"reset\">Reset</button>\n      <button type=\"submit\">Submit</button>\n    </form>\n  );\n}\n```\n\n```css\nlabel, select { display: block; }\nlabel { margin-bottom: 20px; }\n```\n\n</Sandpack>\n\n<Note>\n\n`<select>` に `name` を指定してください。例えば `<select name=\"selectedFruit\" />` のように指定します。指定した `name` はフォームデータ内のキーとして使用されます。例えば、`{ selectedFruit: \"orange\" }` のようになります。\n\n`<select multiple={true}>` を使用すると、フォームから読み取った [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) には、選択されたそれぞれの値が個別に名前と値のペアとして含まれます。上記の例のコンソールログをよく確認してください。\n\n</Note>\n\n<Pitfall>\n\nデフォルトでは、`<form>` 内の*あらゆる* `<button>` はフォームの送信を行います。これは予想外の挙動かもしれません！ 独自のカスタム `Button` React コンポーネントを利用している場合は、`<button>` の代わりに [`<button type=\"button\">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/button) を返すようにすることを考慮してください。そしてフォームを送信することが*意図されている*ボタンには明示的に `<button type=\"submit\">` を使用してください。\n\n</Pitfall>\n\n---\n\n### state 変数を使ってセレクトボックスを制御する {/*controlling-a-select-box-with-a-state-variable*/}\n\n`<select />` のようなセレクトボックスは*非制御*です。たとえ `<select defaultValue=\"orange\" />` のように[デフォルトのオプションを指定](#providing-an-initially-selected-option)している場合でも、この JSX で指定しているのはあくまで初期値であって現在の値ではありません。\n\n***制御された*セレクトボックスをレンダーするには、`value` プロパティを渡してください**。React はセレクトボックスが常に渡した `value` を反映するようにします。通常、[state 変数](/reference/react/useState)を宣言することでセレクトボックスを制御します。\n\n```js {2,6,7}\nfunction FruitPicker() {\n  const [selectedFruit, setSelectedFruit] = useState('orange'); // Declare a state variable...\n  // ...\n  return (\n    <select\n      value={selectedFruit} // ...force the select's value to match the state variable...\n      onChange={e => setSelectedFruit(e.target.value)} // ... and update the state variable on any change!\n    >\n      <option value=\"apple\">Apple</option>\n      <option value=\"banana\">Banana</option>\n      <option value=\"orange\">Orange</option>\n    </select>\n  );\n}\n```\n\nこれは、選択毎に UI の一部を再レンダーしたい場合に便利です。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\n\nexport default function FruitPicker() {\n  const [selectedFruit, setSelectedFruit] = useState('orange');\n  const [selectedVegs, setSelectedVegs] = useState(['corn', 'tomato']);\n  return (\n    <>\n      <label>\n        Pick a fruit:\n        <select\n          value={selectedFruit}\n          onChange={e => setSelectedFruit(e.target.value)}\n        >\n          <option value=\"apple\">Apple</option>\n          <option value=\"banana\">Banana</option>\n          <option value=\"orange\">Orange</option>\n        </select>\n      </label>\n      <hr />\n      <label>\n        Pick all your favorite vegetables:\n        <select\n          multiple={true}\n          value={selectedVegs}\n          onChange={e => {\n            const options = [...e.target.selectedOptions];\n            const values = options.map(option => option.value);\n            setSelectedVegs(values);\n          }}\n        >\n          <option value=\"cucumber\">Cucumber</option>\n          <option value=\"corn\">Corn</option>\n          <option value=\"tomato\">Tomato</option>\n        </select>\n      </label>\n      <hr />\n      <p>Your favorite fruit: {selectedFruit}</p>\n      <p>Your favorite vegetables: {selectedVegs.join(', ')}</p>\n    </>\n  );\n}\n```\n\n```css\nselect { margin-bottom: 10px; display: block; }\n```\n\n</Sandpack>\n\n<Pitfall>\n\n**`onChange` を指定せずに `value` を渡すと、オプションを選択することができなくなります**。セレクトボックスに `value` を渡して制御を行うと、渡した値でセレクトボックスを*強制的に固定*することになります。したがって、state 変数を `value` として渡しても、`onChange` イベントハンドラ内でその state 変数を同期的に更新するのを忘れた場合、React はセレクトボックスを、キーストローク毎に指定した `value` に戻してしまいます。\n\nHTML とは異なり、個々の `<option>` に `selected` 属性を渡すことはサポートされていません。\n\n</Pitfall>\n"
  },
  {
    "path": "src/content/reference/react-dom/components/style.md",
    "content": "---\nstyle: \"<style>\"\n---\n\n<Intro>\n\n[ブラウザ組み込みの `<style>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style)を利用することで、ドキュメントにインラインの CSS スタイルシートを追加できます。\n\n```js\n<style>{` p { color: red; } `}</style>\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<style>` {/*style*/}\n\nドキュメントにインラインスタイルを追加するためには、[ブラウザ組み込みの `<style>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style)をレンダーします。任意のコンポーネントから `<style>` をレンダーでき、React は[特定の場合](#special-rendering-behavior)に対応する DOM 要素をドキュメントの head に配置し、同一のスタイルの重複解消処理を行います。\n\n```js\n<style>{` p { color: red; } `}</style>\n```\n\n[さらに例を見る](#usage)\n\n#### props {/*props*/}\n\n`<style>` は、[一般的な要素の props](/reference/react-dom/components/common#common-props) をすべてサポートしています。\n\n* `children`: 文字列、必須。スタイルシートの内容です。\n* `precedence`: 文字列。React がドキュメントの `<head>` 内で `<style>` DOM ノードを他と比較してどのように順序付けるかを指定します。これによりどのスタイルシートが他を上書きできるかが決まります。React は、最初に見つけた優先順位の値を「低い」と見なし、後で見つけた優先順位の値を「高い」と見なします。多くのスタイルシステムは、スタイルルールがアトミックであるため、単一の優先順位の値を使用しても問題なく機能します。同じ優先順位を持つスタイルシートは、`<link>` の場合でもインライン `<style>` タグの場合でも、あるいは [`preinit`](/reference/react-dom/preinit) 関数を使用してロードされた場合でも、一緒に配置されます。\n* `href`: 文字列。同じ `href` を持つスタイルに対して React が[重複解消処理](#special-rendering-behavior)を行えるようにします。\n* `media`: 文字列。スタイルシートの適用を特定の[メディアクエリ](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries)に制限します。\n* `nonce`: 文字列。厳格なコンテンツセキュリティポリシーを使用する際に[リソースを許可するための暗号化 nonce](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce)。\n* `title`: 文字列。[代替スタイルシート](https://developer.mozilla.org/en-US/docs/Web/CSS/Alternative_style_sheets)の名前を指定します。\n\n以下は React での使用が**推奨されない** props です。\n\n* `blocking`: 文字列。`\"render\"` と設定されている場合、スタイルシートがロードされるまでページを描画しないようブラウザに指示します。React ではサスペンスを通じてより細かい制御を提供します。\n\n#### 特別なレンダー動作 {/*special-rendering-behavior*/}\n\nReact は `<style>` コンポーネントをドキュメントの `<head>` に移動し、同一のスタイルシートの重複解消処理を行い、スタイルシートがロードされている間に[サスペンド](/reference/react/Suspense)します。\n\nこの動作を有効にするには、props として `href` と `precedence` を指定してください。React は同じ `href` を持つスタイルの重複解消処理を行います。`precedence` はドキュメントの `<head>` 内における他の `<style>` DOM ノードとの相対ランクを React に指示することで、どのスタイルシートが他を上書きできるかを決定できるようにします。\n\nこの特別な動作に関して、3 つの注意点があります。\n\n* スタイルがレンダーされた後、React は props に変更があってもそれを無視します（開発中にこれが起きた場合は React が警告を発します）。\n* `precedence` が指定されている場合、React は（`href` と `precedence` 以外の）無関係な props をすべて削除します。\n* コンポーネントがアンマウントされた後も、React は DOM にスタイルを残すことがあります。\n\n---\n\n## 使用法 {/*usage*/}\n\n### インライン CSS スタイルシートのレンダー {/*rendering-an-inline-css-stylesheet*/}\n\nコンポーネントが正しく表示されるために特定の CSS スタイルに依存している場合、インラインスタイルシートを当該コンポーネント内でレンダーできます。\n\nprops である `href` は、このスタイルシートを一意に識別する必要があります。React は同じ `href` を持つスタイルシートに対して重複解消処理を行うからです。\nprops として `precedence` を与えた場合、React は各々の値がコンポーネントツリーに現れた順番を基準にして、インラインのスタイルシートを並び替えます。\n\nインラインのスタイルシートはロード中にサスペンスバウンダリをトリガしません。\nこれは内部でフォントや画像などのリソースを非同期的に読み込んでいる場合でも同様です。\n\n<SandpackWithHTMLOutput>\n\n```js src/App.js active\nimport ShowRenderedHTML from './ShowRenderedHTML.js';\nimport { useId } from 'react';\n\nfunction PieChart({data, colors}) {\n  const id = useId();\n  const stylesheet = colors.map((color, index) =>\n    `#${id} .color-${index}: \\{ color: \"${color}\"; \\}`\n  ).join();\n  return (\n    <>\n      <style href={\"PieChart-\" + JSON.stringify(colors)} precedence=\"medium\">\n        {stylesheet}\n      </style>\n      <svg id={id}>\n        …\n      </svg>\n    </>\n  );\n}\n\nexport default function App() {\n  return (\n    <ShowRenderedHTML>\n      <PieChart data=\"...\" colors={['red', 'green', 'blue']} />\n    </ShowRenderedHTML>\n  );\n}\n```\n\n</SandpackWithHTMLOutput>\n"
  },
  {
    "path": "src/content/reference/react-dom/components/textarea.md",
    "content": "---\ntitle: \"<textarea>\"\n---\n\n<Intro>\n\n[ブラウザ組み込みの `<textarea>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)を利用することで、複数行のテキスト入力エリアをレンダーすることができます。\n\n```js\n<textarea />\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<textarea>` {/*textarea*/}\n\nテキストエリアを表示するためには、[ブラウザ組み込みの `<textarea>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea) コンポーネントをレンダーします。\n\n```js\n<textarea name=\"postContent\" />\n```\n\n[さらに例を見る](#usage)\n\n#### props {/*props*/}\n\n`<textarea>` は[一般的な要素の props](/reference/react-dom/components/common#common-props) をすべてサポートしています。\n\n`value` プロパティを渡すことで、[テキストエリアを制御されたコンポーネント (controlled component)](#controlling-a-text-area-with-a-state-variable) にできます。\n\n* `value`: 文字列。テキストエリア内のテキストを制御します。\n\n`value` を渡す場合は、渡された値を更新する `onChange` ハンドラも渡す必要があります。\n\nもし `<textarea>` を非制御コンポーネント (uncontrolled component) として使用する場合は、代わりに `defaultValue` プロパティを渡すことができます。\n\n* `defaultValue`: 文字列。テキストエリアの[初期値](#providing-an-initial-value-for-a-text-area)を指定します。\n\nこれらの `<textarea>` の props は、非制御のテキストエリアと制御されたテキストエリアの両方で用いられます。\n\n* [`autoComplete`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#autocomplete): `'on'` または `'off'`。オートコンプリートの挙動を指定します。\n* [`autoFocus`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#autofocus): ブーリアン。`true` の場合、React はマウント時にこの要素にフォーカスします。\n* `children`: `<textarea>` には子要素を指定できません。初期値を設定するには `defaultValue` を使用します。\n* [`cols`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#cols): 数値。デフォルトの幅を平均文字幅で指定します。デフォルトは `20` です。\n* [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#disabled): ブーリアン。`true` の場合、入力エリアは操作不可能になり、表示が暗くなります。\n* [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#form): 文字列。この入力が属する `<form>` の `id` を指定します。省略された場合、最も近い親のフォームとなります。\n* [`maxLength`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#maxlength): 数値。テキストの最大長を指定します。\n* [`minLength`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#minlength): 数値。テキストの最小長を指定します。\n* [`name`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#name): 文字列。[フォームで送信される](#reading-the-textarea-value-when-submitting-a-form)この入力エリアの名前を指定します。\n* `onChange`: [`Event` ハンドラ](/reference/react-dom/components/common#event-handler) 関数。[制御されたテキストエリア](#controlling-a-text-area-with-a-state-variable)では必須。ユーザによって入力値が変更されるとすぐに発火します（例えば、各キーストロークで発火します）。ブラウザの [`input` イベント](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event) と同様に動作します。\n* `onChangeCapture`: `onChange` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onInput`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event): [`Event` ハンドラ](/reference/react-dom/components/common#event-handler)関数。ユーザによって値が変更されるとすぐに発火します。歴史的な理由から、React では同様に動作する `onChange` を使用するのが慣例です。\n* `onInputCapture`: `onInput` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onInvalid`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/invalid_event): [`Event` ハンドラ](/reference/react-dom/components/common#event-handler)関数。フォームの送信時に入力が検証に失敗した場合に発火します。組み込みの `invalid` イベントとは異なり、React の `onInvalid` イベントはバブリングします。\n* `onInvalidCapture`: `onInvalid` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`onSelect`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement/select_event): [`Event` ハンドラ](/reference/react-dom/components/common#event-handler)関数。`<textarea>` 内で選択テキストが変更された後に発火します。React は `onSelect` イベントを拡張しており、空の選択やテキストの編集（選択に影響を与える可能性がある）でも発火します。\n* `onSelectCapture`: `onSelect` の[キャプチャフェーズ](/learn/responding-to-events#capture-phase-events)で発火するバージョン。\n* [`placeholder`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#placeholder): 文字列。テキストエリアの値が空の場合、これが薄い色で表示されます。\n* [`readOnly`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#readonly): ブーリアン。`true` の場合、テキストエリアはユーザによって編集できなくなります。\n* [`required`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#required): ブーリアン。`true` の場合、フォームを送信するためには値が必須となります。\n* [`rows`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#rows): 数値。デフォルトの高さを平均文字高での指定します。デフォルトは `2` です。\n* [`wrap`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#wrap): `'hard'`、`'soft'`、または `'off'`。フォームを送信するときにテキストがどのように折り返されるかを指定します。\n\n#### 注意点 {/*caveats*/}\n\n- `<textarea>something</textarea>` のように子要素を渡すことはできません。[初期内容の指定には `defaultValue` を使用してください](#providing-an-initial-value-for-a-text-area)。\n- テキストエリアが props として文字列型の `value` を受け取ると、[制御されたテキストエリアとして扱われます](#controlling-a-text-area-with-a-state-variable)。\n- テキストエリアは制御されたコンポーネントと非制御コンポーネントに同時になることはできません。。\n- テキストエリアは、ライフタイム中に制御されたコンポーネントから非制御コンポーネント、またはその逆に切り替えることはできません。\n- すべての制御されたテキストエリアには、制御に使っている state を同期的に更新するための `onChange` イベントハンドラが必要です。\n\n---\n\n## 使用法 {/*usage*/}\n\n### テキストエリアを表示する {/*displaying-a-text-area*/}\n\nテキストエリアを表示するには、`<textarea>` をレンダーします。そのデフォルトのサイズは [`rows`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#rows) と [`cols`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#cols) 属性で指定できますが、デフォルトではユーザがそれをリサイズできます。リサイズを無効にするには、CSS で `resize: none` を指定します。\n\n<Sandpack>\n\n```js\nexport default function NewPost() {\n  return (\n    <label>\n      Write your post:\n      <textarea name=\"postContent\" rows={4} cols={40} />\n    </label>\n  );\n}\n```\n\n```css\ninput { margin-left: 5px; }\ntextarea { margin-top: 10px; }\nlabel { margin: 10px; }\nlabel, textarea { display: block; }\n```\n\n</Sandpack>\n\n---\n\n### テキストエリアにラベルを付ける {/*providing-a-label-for-a-text-area*/}\n\n通常、すべての `<textarea>` は [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label) タグ内に配置します。これにより、ブラウザに対してこのラベルがそのテキストエリアに関連付けられていることが伝わります。ユーザがラベルをクリックすると、ブラウザは自動的にテキストエリアにフォーカスします。これはアクセシビリティの観点からも重要です。ユーザがテキストエリアにフォーカスすると、スクリーンリーダがラベルのキャプションを読み上げます。\n\nもし `<label>` 内に `<textarea>` をネストできない場合は、同じ ID を `<textarea id>` と [`<label htmlFor>`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor) に渡すことで関連付けることができます。同一コンポーネントの複数のインスタンス間での競合を避けるために、[`useId`](/reference/react/useId) を使用してそのような ID を生成してください。\n\n<Sandpack>\n\n```js\nimport { useId } from 'react';\n\nexport default function Form() {\n  const postTextAreaId = useId();\n  return (\n    <>\n      <label htmlFor={postTextAreaId}>\n        Write your post:\n      </label>\n      <textarea\n        id={postTextAreaId}\n        name=\"postContent\"\n        rows={4}\n        cols={40}\n      />\n    </>\n  );\n}\n```\n\n```css\ninput { margin: 5px; }\n```\n\n</Sandpack>\n\n---\n\n### テキストエリアに初期値を指定する {/*providing-an-initial-value-for-a-text-area*/}\n\nオプションで、テキストエリアの初期値を指定することができます。`defaultValue` 文字列として渡してください。\n\n<Sandpack>\n\n```js\nexport default function EditPost() {\n  return (\n    <label>\n      Edit your post:\n      <textarea\n        name=\"postContent\"\n        defaultValue=\"I really enjoyed biking yesterday!\"\n        rows={4}\n        cols={40}\n      />\n    </label>\n  );\n}\n```\n\n```css\ninput { margin-left: 5px; }\ntextarea { margin-top: 10px; }\nlabel { margin: 10px; }\nlabel, textarea { display: block; }\n```\n\n</Sandpack>\n\n<Pitfall>\n\nHTML とは異なり、初期テキストを `<textarea>Some content</textarea>` のようにして渡すことはサポートされていません。\n\n</Pitfall>\n\n---\n\n### フォーム送信時にテキストエリアから値を読み取る {/*reading-the-text-area-value-when-submitting-a-form*/}\n\nテキストエリアを [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) で囲み、その中に [`<button type=\"submit\">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) を配置します。これにより、`<form onSubmit>` イベントハンドラが呼び出されます。デフォルトでは、ブラウザはフォームデータを現在の URL に送信し、ページを更新します。`e.preventDefault()` を呼び出すことで、その振る舞いをオーバーライドできます。[`new FormData(e.target)`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) を使用してフォームデータを読み込みます。\n<Sandpack>\n\n```js\nexport default function EditPost() {\n  function handleSubmit(e) {\n    // Prevent the browser from reloading the page\n    e.preventDefault();\n\n    // Read the form data\n    const form = e.target;\n    const formData = new FormData(form);\n\n    // You can pass formData as a fetch body directly:\n    fetch('/some-api', { method: form.method, body: formData });\n\n    // Or you can work with it as a plain object:\n    const formJson = Object.fromEntries(formData.entries());\n    console.log(formJson);\n  }\n\n  return (\n    <form method=\"post\" onSubmit={handleSubmit}>\n      <label>\n        Post title: <input name=\"postTitle\" defaultValue=\"Biking\" />\n      </label>\n      <label>\n        Edit your post:\n        <textarea\n          name=\"postContent\"\n          defaultValue=\"I really enjoyed biking yesterday!\"\n          rows={4}\n          cols={40}\n        />\n      </label>\n      <hr />\n      <button type=\"reset\">Reset edits</button>\n      <button type=\"submit\">Save post</button>\n    </form>\n  );\n}\n```\n\n```css\nlabel { display: block; }\ninput { margin: 5px; }\n```\n\n</Sandpack>\n\n<Note>\n\n`<textarea>` に `name` を指定してください。例えば `<textarea name=\"postContent\" />` のように指定します。指定した `name` はフォームデータ内のキーとして使用されます。例えば、`{ postContent: \"Your post\" }` のようになります。\n\n</Note>\n\n<Pitfall>\n\nデフォルトでは、`<form>` 内の*あらゆる* `<button>` はフォームの送信を行います。これは予想外の挙動かもしれません！ 独自のカスタム `Button` React コンポーネントを利用している場合は、`<button>` の代わりに [`<button type=\"button\">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/button) を返すようにすることを考慮してください。そしてフォームを送信することが*意図されている*ボタンには明示的に `<button type=\"submit\">` を使用してください。\n\n</Pitfall>\n\n---\n\n### state 変数を使ってテキストエリアを制御する {/*controlling-a-text-area-with-a-state-variable*/}\n\n`<textarea />` のようなテキストエリアは*非制御*です。たとえ `<textarea defaultValue=\"Initial text\" />` のように[デフォルト値を指定](#providing-an-initial-value-for-a-text-area)している場合でも、この JSX で指定しているのはあくまで初期値であって現在の値ではありません。\n\n***制御された*テキストエリアをレンダーするには、`value` プロパティを渡してください**。React はテキストエリアが常に渡した `value` を反映するようにします。通常、[state 変数](/reference/react/useState)を宣言することでテキストエリアを制御します。\n\n```js {2,6,7}\nfunction NewPost() {\n  const [postContent, setPostContent] = useState(''); // Declare a state variable...\n  // ...\n  return (\n    <textarea\n      value={postContent} // ...force the input's value to match the state variable...\n      onChange={e => setPostContent(e.target.value)} // ... and update the state variable on any edits!\n    />\n  );\n}\n```\n\nこれは、キーストロークごとに UI の一部を再レンダーしたい場合に便利です。\n\n<Sandpack>\n\n```js\nimport { useState } from 'react';\nimport MarkdownPreview from './MarkdownPreview.js';\n\nexport default function MarkdownEditor() {\n  const [postContent, setPostContent] = useState('_Hello,_ **Markdown**!');\n  return (\n    <>\n      <label>\n        Enter some markdown:\n        <textarea\n          value={postContent}\n          onChange={e => setPostContent(e.target.value)}\n        />\n      </label>\n      <hr />\n      <MarkdownPreview markdown={postContent} />\n    </>\n  );\n}\n```\n\n```js src/MarkdownPreview.js\nimport { Remarkable } from 'remarkable';\n\nconst md = new Remarkable();\n\nexport default function MarkdownPreview({ markdown }) {\n  const renderedHTML = md.render(markdown);\n  return <div dangerouslySetInnerHTML={{__html: renderedHTML}} />;\n}\n```\n\n```json package.json\n{\n  \"dependencies\": {\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"remarkable\": \"2.0.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```css\ntextarea { display: block; margin-top: 5px; margin-bottom: 10px; }\n```\n\n</Sandpack>\n\n<Pitfall>\n\n**`onChange` を指定せずに `value` を渡すと、テキストエリアに入力することができなくなります**。テキストエリアに `value` を渡して制御を行うと、渡した値でテキストエリアを*強制的に固定*することになります。したがって、state 変数を `value` として渡しても、`onChange` イベントハンドラ内でその state 変数を同期的に更新するのを忘れた場合、React はテキストエリアを、キーストローク毎に指定した `value` に戻してしまいます。\n\n</Pitfall>\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### テキストエリアにタイプしても内容が更新されない {/*my-text-area-doesnt-update-when-i-type-into-it*/}\n\n`value` があるが `onChange` のないテキストエリアをレンダーすると、コンソールにエラーが表示されます。\n\n```js\n// 🔴 Bug: controlled text area with no onChange handler\n<textarea value={something} />\n```\n\n<ConsoleBlock level=\"error\">\n\nYou provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.\n\n</ConsoleBlock>\n\nエラーメッセージが示すように、[*初期値*を指定したいだけの場合](#providing-an-initial-value-for-a-text-area)は、代わりに `defaultValue` を渡すようにしてください。\n\n```js\n// ✅ Good: uncontrolled text area with an initial value\n<textarea defaultValue={something} />\n```\n\n[state 変数でこのテキストエリアを制御したい場合](#controlling-a-text-area-with-a-state-variable)は、`onChange` ハンドラを指定してください。\n\n```js\n// ✅ Good: controlled text area with onChange\n<textarea value={something} onChange={e => setSomething(e.target.value)} />\n```\n\n値を意図的に読み取り専用にしたい場合は、エラーを抑制するために props として `readOnly` を追加してください。\n\n```js\n// ✅ Good: readonly controlled text area without on change\n<textarea value={something} readOnly={true} />\n```\n\n---\n\n### キーストロークごとにテキストエリアのカーソルが先頭に戻る {/*my-text-area-caret-jumps-to-the-beginning-on-every-keystroke*/}\n\nテキストエリアを[制御する](#controlling-a-text-area-with-a-state-variable)場合、`onChange` 中でその state 変数を DOM からやってくるテキストエリアの値に更新する必要があります。\n\nstate を `e.target.value` 以外のものに更新することはできません。\n\n```js\nfunction handleChange(e) {\n  // 🔴 Bug: updating an input to something other than e.target.value\n  setFirstName(e.target.value.toUpperCase());\n}\n```\n\nまた、非同期に更新することもできません。\n\n```js\nfunction handleChange(e) {\n  // 🔴 Bug: updating an input asynchronously\n  setTimeout(() => {\n    setFirstName(e.target.value);\n  }, 100);\n}\n```\n\nコードを修正するには、state を `e.target.value` の値に同期的に更新します。\n\n```js\nfunction handleChange(e) {\n  // ✅ Updating a controlled input to e.target.value synchronously\n  setFirstName(e.target.value);\n}\n```\n\nこれで問題が解決しない場合、テキストエリアがキーストロークごとに DOM から削除・再追加されている可能性があります。これは、再レンダーごとに state を誤って[リセット](/learn/preserving-and-resetting-state)している場合に起こります。例えば、テキストエリアまたはその親が常に異なる `key` 属性を受け取っている可能性や、コンポーネント定義をネストしている（これは React では許されておらず、「内側」のコンポーネントがレンダー時に再マウントさえることになります）可能性があります。\n\n---\n\n### \"A component is changing an uncontrolled input to be controlled\" というエラーが発生する {/*im-getting-an-error-a-component-is-changing-an-uncontrolled-input-to-be-controlled*/}\n\n\nコンポーネントに `value` を渡す場合、そのライフサイクル全体を通じて文字列型でなければなりません。\n\n最初に `value={undefined}` を渡しておき、後で `value=\"some string\"` を渡すようなことはできません。なぜなら、React はあなたがコンポーネントを非制御コンポーネントと制御されたコンポーネントのどちらにしたいのか分からなくなるからです。制御されたコンポーネントは常に文字列の `value` を受け取るべきであり、`null` や `undefined` であってはいけません。\n\nあなたの `value` が API や state 変数から来ている場合、それが `null` や `undefined` に初期化されているかもしれません。その場合、まず空の文字列（`''`）にセットするか、`value` が文字列であることを保証するために `value={someValue ?? ''}` を渡すようにしてください。\n"
  },
  {
    "path": "src/content/reference/react-dom/components/title.md",
    "content": "---\ntitle: \"<title>\"\n---\n\n<Intro>\n\n[ブラウザ組み込みの `<title>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title)を利用することで、ドキュメントのタイトルを指定できます。\n\n```js\n<title>My Blog</title>\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `<title>` {/*title*/}\n\nドキュメントのタイトルを指定するには、[ブラウザ組み込みの `<title>` コンポーネント](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title)をレンダーします。任意のコンポーネントから `<title>` をレンダーでき、React は常に対応する DOM 要素をドキュメントの head 内に配置します。\n\n```js\n<title>My Blog</title>\n```\n\n[さらに例を見る](#usage)\n\n#### props {/*props*/}\n\n`<title>` は、[一般的な要素の props](/reference/react-dom/components/common#common-props) をすべてサポートしています。\n\n* `children`: `<title>` は子としてテキストのみを受け入れます。このテキストがドキュメントのタイトルになります。それがテキストのみをレンダーする限り、カスタムのコンポーネントを渡すこともできます。\n\n#### 特別なレンダー動作 {/*special-rendering-behavior*/}\n\n`<title>` コンポーネントが React ツリー内のどこでレンダーされていても、React は対応する DOM 要素を常にドキュメントの `<head>` 内に配置します。`<head>` は DOM 内で `<title>` が存在できる唯一の有効な場所ですが、ある特定のページを表すコンポーネントが自分自身で `<title>` コンポーネントをレンダーできれば有用であり、コンポーネントの組み合わせやすさが保たれます。\n\nこれには 2 つの例外があります。\n* `<title>` が `<svg>` コンポーネント内にある場合、特別な動作は発生しません。その文脈ではドキュメントのタイトルではなく[当該 SVG グラフィックに対するアクセシビリティ用の説明](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title)を表すことになるからです。\n* `<title>` に props として [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop) がある場合、特別な動作は発生しません。その場合はドキュメントのタイトルではなく、ページの特定の部分に関するメタデータを表すことになるからです。\n\n<Pitfall>\n\n一度にひとつの `<title>` だけがレンダーされるようにしてください。複数のコンポーネントが同時に `<title>` タグをレンダーすると、React はそれらのタイトルをすべてドキュメントの head に配置します。このような場合のブラウザや検索エンジンの動作は定義されていません。\n\n</Pitfall>\n\n---\n\n## 使用法 {/*usage*/}\n\n### ドキュメントのタイトルを設定する {/*set-the-document-title*/}\n\n任意のコンポーネントから、テキストを children として持つ `<title>` コンポーネントをレンダーします。React は `<title>` DOM ノードをドキュメントの `<head>` に配置します。\n\n<SandpackWithHTMLOutput>\n\n```js src/App.js active\nimport ShowRenderedHTML from './ShowRenderedHTML.js';\n\nexport default function ContactUsPage() {\n  return (\n    <ShowRenderedHTML>\n      <title>My Site: Contact Us</title>\n      <h1>Contact Us</h1>\n      <p>Email us at support@example.com</p>\n    </ShowRenderedHTML>\n  );\n}\n```\n\n</SandpackWithHTMLOutput>\n\n### タイトルに変数を使用する {/*use-variables-in-the-title*/}\n\n`<title>` コンポーネントの children は、単一の文字列（あるいは単一の数値、または `toString` メソッドを持つ単一のオブジェクト）でなければなりません。気付きにくいかもしれませんが、以下のように JSX で波括弧を使用した場合：\n\n```js\n<title>Results page {pageNumber}</title> // 🔴 Problem: This is not a single string\n```\n\n...実際には、`<title>` コンポーネントは children として 2 つの要素（`\"Results page\"` という文字列と、`pageNumber` の値）を持つ配列を受け取ることになります。これはエラーを引き起こします。代わりに、文字列補間を使用することで `<title>` に単一の文字列を渡すようにしてください。\n\n```js\n<title>{`Results page ${pageNumber}`}</title>\n```\n\n"
  },
  {
    "path": "src/content/reference/react-dom/createPortal.md",
    "content": "---\ntitle: createPortal\n---\n\n<Intro>\n\n`createPortal` を使うことで、DOM 上の別の場所に子要素をレンダーすることができます。\n\n\n```js\n<div>\n  <SomeComponent />\n  {createPortal(children, domNode, key?)}\n</div>\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `createPortal(children, domNode, key?)` {/*createportal*/}\n\nポータルを作成するには、`createPortal` を呼び出し、JSX とそれをレンダーする先の DOM ノードを渡します。\n\n```js\nimport { createPortal } from 'react-dom';\n\n// ...\n\n<div>\n  <p>This child is placed in the parent div.</p>\n  {createPortal(\n    <p>This child is placed in the document body.</p>,\n    document.body\n  )}\n</div>\n```\n\n[さらに例を見る](#usage)\n\nポータルは DOM ノードの物理的な配置だけを変更します。それ以外のすべての点で、ポータルにレンダーする JSX は、レンダー元の React コンポーネントの子ノードとして機能します。例えば、子は親ツリーが提供するコンテクストにアクセスでき、イベントは React ツリーに従って子から親へとバブルアップします。\n\n#### 引数 {/*parameters*/}\n\n* `children`: React でレンダーできるあらゆるもの、例えば JSX（`<div />` や `<SomeComponent />` など）、[フラグメント](/reference/react/Fragment) (`<>...</>`)、文字列や数値、またはこれらの配列。 \n\n* `domNode`: `document.getElementById()` によって返されるような DOM ノード。ノードはすでに存在している必要があります。更新中に異なる DOM ノードを渡すと、ポータルの内容が再作成されます。\n\n* **省略可能** `key`: ポータルの [key](/learn/rendering-lists#keeping-list-items-in-order-with-key) として使用される一意の文字列または数値。\n\n#### 返り値 {/*returns*/}\n\n`createPortal` は、JSX に含めたり React コンポーネントから返したりすることができる React ノードを返します。React がレンダー出力内でそのようなものを検出すると、渡された `children` を渡された `domNode` の内部に配置します。\n\n#### 注意点 {/*caveats*/}\n\n* ポータルからのイベントは、DOM ツリーではなく React ツリーに沿って伝播します。例えば、ポータル内部でクリックが起き、ポータルが `<div onClick>` でラップされている場合、その `onClick` ハンドラが発火します。これが問題を引き起こす場合、ポータル内部からイベント伝播を停止するか、ポータル自体を React ツリー内で上に移動します。\n\n---\n\n## 使用法 {/*usage*/}\n\n### DOM 内の別部分へのレンダー {/*rendering-to-a-different-part-of-the-dom*/}\n\n*ポータル*により、コンポーネントが子の一部を DOM 内の別の場所にレンダーできるようになります。これにより、コンポーネントの出力の一部を、自身の含まれているコンテナの外に「脱出」させることが可能です。例えばコンポーネントから、ページの他の要素の上部や外部に表示されるモーダルダイアログやツールチップを表示することができます。\n\nポータルを作成するには、<CodeStep step={1}>何らかの JSX</CodeStep> と <CodeStep step={2}>行き先の DOM ノード</CodeStep> を渡した `createPortal` の呼び出し結果をレンダーします。\n\n```js [[1, 8, \"<p>This child is placed in the document body.</p>\"], [2, 9, \"document.body\"]]\nimport { createPortal } from 'react-dom';\n\nfunction MyComponent() {\n  return (\n    <div style={{ border: '2px solid black' }}>\n      <p>This child is placed in the parent div.</p>\n      {createPortal(\n        <p>This child is placed in the document body.</p>,\n        document.body\n      )}\n    </div>\n  );\n}\n```\n\nReact は、<CodeStep step={1}>渡した JSX</CodeStep> に対応する DOM ノードを <CodeStep step={2}>渡した DOM ノード</CodeStep> の内部に配置します。\n\nポータルがなければ 2 つ目の `<p>` は親の `<div>` の内部に配置されますが、ポータルはそれを [`document.body`](https://developer.mozilla.org/en-US/docs/Web/API/Document/body) に「テレポート」させます。\n\n<Sandpack>\n\n```js\nimport { createPortal } from 'react-dom';\n\nexport default function MyComponent() {\n  return (\n    <div style={{ border: '2px solid black' }}>\n      <p>This child is placed in the parent div.</p>\n      {createPortal(\n        <p>This child is placed in the document body.</p>,\n        document.body\n      )}\n    </div>\n  );\n}\n```\n\n</Sandpack>\n\n2 つ目の段落が親の `<div>` の境界線の外側に表示されていることに注意してください。開発者ツールで DOM 構造を調べると、2 つ目の `<p>` が直接 `<body>` に配置されていることがわかります。\n\n```html {4-6,9}\n<body>\n  <div id=\"root\">\n    ...\n      <div style=\"border: 2px solid black\">\n        <p>This child is placed inside the parent div.</p>\n      </div>\n    ...\n  </div>\n  <p>This child is placed in the document body.</p>\n</body>\n```\n\nポータルは DOM ノードの物理的な配置だけを変更します。それ以外のすべての点で、ポータルにレンダーする JSX は、レンダー元の React コンポーネントの子ノードとして機能します。例えば、子は親ツリーが提供するコンテクストにアクセスでき、イベントは React ツリーに従って子から親へとバブルアップします。\n\n---\n\n### ポータルを使ったモーダルダイアログのレンダー {/*rendering-a-modal-dialog-with-a-portal*/}\n\nポータルを使用して、ページ内の他の要素上に浮かんで表示されるモーダルダイアログを作成することができます。ダイアログを呼び出すコンポーネントが `overflow: hidden` やダイアログに干渉する他のスタイルを持つコンテナ内にあっても問題ありません。\n\nこの例では、2 つのコンテナはモーダルダイアログの表示を妨げるようなスタイルを持っていますが、ポータルを使ってレンダーされた方は影響を受けていません。DOM 内ではモーダルは親 JSX 要素の中に含まれていないからです。\n\n<Sandpack>\n\n```js src/App.js active\nimport NoPortalExample from './NoPortalExample';\nimport PortalExample from './PortalExample';\n\nexport default function App() {\n  return (\n    <>\n      <div className=\"clipping-container\">\n        <NoPortalExample  />\n      </div>\n      <div className=\"clipping-container\">\n        <PortalExample />\n      </div>\n    </>\n  );\n}\n```\n\n```js src/NoPortalExample.js\nimport { useState } from 'react';\nimport ModalContent from './ModalContent.js';\n\nexport default function NoPortalExample() {\n  const [showModal, setShowModal] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShowModal(true)}>\n        Show modal without a portal\n      </button>\n      {showModal && (\n        <ModalContent onClose={() => setShowModal(false)} />\n      )}\n    </>\n  );\n}\n```\n\n```js src/PortalExample.js active\nimport { useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport ModalContent from './ModalContent.js';\n\nexport default function PortalExample() {\n  const [showModal, setShowModal] = useState(false);\n  return (\n    <>\n      <button onClick={() => setShowModal(true)}>\n        Show modal using a portal\n      </button>\n      {showModal && createPortal(\n        <ModalContent onClose={() => setShowModal(false)} />,\n        document.body\n      )}\n    </>\n  );\n}\n```\n\n```js src/ModalContent.js\nexport default function ModalContent({ onClose }) {\n  return (\n    <div className=\"modal\">\n      <div>I'm a modal dialog</div>\n      <button onClick={onClose}>Close</button>\n    </div>\n  );\n}\n```\n\n\n```css src/styles.css\n.clipping-container {\n  position: relative;\n  border: 1px solid #aaa;\n  margin-bottom: 12px;\n  padding: 12px;\n  width: 250px;\n  height: 80px;\n  overflow: hidden;\n}\n\n.modal {\n  display: flex;\n  justify-content: space-evenly;\n  align-items: center;\n  box-shadow: rgba(100, 100, 111, 0.3) 0px 7px 29px 0px;\n  background-color: white;\n  border: 2px solid rgb(240, 240, 240);\n  border-radius: 12px;\n  position:  absolute;\n  width: 250px;\n  top: 70px;\n  left: calc(50% - 125px);\n  bottom: 70px;\n}\n```\n\n</Sandpack>\n\n<Pitfall>\n\nポータルを使用する際には、アプリを正しくアクセシブルにすることが重要です。例えば、ユーザが自然にポータルの内または外へフォーカスを移動できるよう、キーボードフォーカスを管理する必要があるかもしれません。\n\nモーダルを作成する際には、[WAI-ARIA のモーダル作成実践ガイド](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal)に従ってください。コミュニティパッケージを使用する場合は、それがアクセシブルであり、このガイドラインに従っていることを確認してください。\n\n</Pitfall>\n\n---\n\n### 非 React のサーバマークアップに React コンポーネントをレンダー {/*rendering-react-components-into-non-react-server-markup*/}\n\nReact で構築されていない静的ページあるいはサーバレンダーされたページの一部に React ルートがある場合にも、ポータルは有用です。例えば、ページが Rails のようなサーバフレームワークで構築されている場合、サイドバーなどの静的な部位の内部に操作可能なエリアを作成することができます。[別々の React ルート](/reference/react-dom/client/createRoot#rendering-a-page-partially-built-with-react)を複数用いる場合と異なり、ポータルを使うとアプリを単一の React ツリーとして扱うことができ、部位ごとに DOM 内の異なる場所にレンダーさせつつも state を共有可能です。\n\n<Sandpack>\n\n```html public/index.html\n<!DOCTYPE html>\n<html>\n  <head><title>My app</title></head>\n  <body>\n    <h1>Welcome to my hybrid app</h1>\n    <div class=\"parent\">\n      <div class=\"sidebar\">\n        This is server non-React markup\n        <div id=\"sidebar-content\"></div>\n      </div>\n      <div id=\"root\"></div>\n    </div>\n  </body>\n</html>\n```\n\n```js src/index.js\nimport { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport App from './App.js';\nimport './styles.css';\n\nconst root = createRoot(document.getElementById('root'));\nroot.render(\n  <StrictMode>\n    <App />\n  </StrictMode>\n);\n```\n\n```js src/App.js active\nimport { createPortal } from 'react-dom';\n\nconst sidebarContentEl = document.getElementById('sidebar-content');\n\nexport default function App() {\n  return (\n    <>\n      <MainContent />\n      {createPortal(\n        <SidebarContent />,\n        sidebarContentEl\n      )}\n    </>\n  );\n}\n\nfunction MainContent() {\n  return <p>This part is rendered by React</p>;\n}\n\nfunction SidebarContent() {\n  return <p>This part is also rendered by React!</p>;\n}\n```\n\n```css\n.parent {\n  display: flex;\n  flex-direction: row;\n}\n\n#root {\n  margin-top: 12px;\n}\n\n.sidebar {\n  padding:  12px;\n  background-color: #eee;\n  width: 200px;\n  height: 200px;\n  margin-right: 12px;\n}\n\n#sidebar-content {\n  margin-top: 18px;\n  display: block;\n  background-color: white;\n}\n\np {\n  margin: 0;\n}\n```\n\n</Sandpack>\n\n---\n\n### 非 React DOM ノードに React コンポーネントをレンダー {/*rendering-react-components-into-non-react-dom-nodes*/}\n\nReact 外で管理されている DOM ノードの内容を管理するためにポータルを使用することもできます。例えば、非 React のマップウィジェットと統合していて、ポップアップ内に React のコンテンツをレンダーしたいとします。これを行うには、レンダーする DOM ノードを格納するための `popupContainer` state 変数を宣言します。\n\n```js\nconst [popupContainer, setPopupContainer] = useState(null);\n```\n\nサードパーティのウィジェットを作成する際に、ウィジェットが返す DOM ノードを格納しておき、その内部にレンダーが行えるようにします。\n\n```js {5-6}\nuseEffect(() => {\n  if (mapRef.current === null) {\n    const map = createMapWidget(containerRef.current);\n    mapRef.current = map;\n    const popupDiv = addPopupToMapWidget(map);\n    setPopupContainer(popupDiv);\n  }\n}, []);\n```\n\nこのようにしておけば、`popupContainer` が利用可能になったところでその中に `createPortal` を使って React コンテンツをレンダーすることができるようになります。\n\n```js {3-6}\nreturn (\n  <div style={{ width: 250, height: 250 }} ref={containerRef}>\n    {popupContainer !== null && createPortal(\n      <p>Hello from React!</p>,\n      popupContainer\n    )}\n  </div>\n);\n```\n\n以下に、試してみることができる完全な例を示します。\n\n<Sandpack>\n\n```json package.json hidden\n{\n  \"dependencies\": {\n    \"leaflet\": \"1.9.1\",\n    \"react\": \"latest\",\n    \"react-dom\": \"latest\",\n    \"react-scripts\": \"latest\",\n    \"remarkable\": \"2.0.1\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n```\n\n```js src/App.js\nimport { useRef, useEffect, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { createMapWidget, addPopupToMapWidget } from './map-widget.js';\n\nexport default function Map() {\n  const containerRef = useRef(null);\n  const mapRef = useRef(null);\n  const [popupContainer, setPopupContainer] = useState(null);\n\n  useEffect(() => {\n    if (mapRef.current === null) {\n      const map = createMapWidget(containerRef.current);\n      mapRef.current = map;\n      const popupDiv = addPopupToMapWidget(map);\n      setPopupContainer(popupDiv);\n    }\n  }, []);\n\n  return (\n    <div style={{ width: 250, height: 250 }} ref={containerRef}>\n      {popupContainer !== null && createPortal(\n        <p>Hello from React!</p>,\n        popupContainer\n      )}\n    </div>\n  );\n}\n```\n\n```js src/map-widget.js\nimport 'leaflet/dist/leaflet.css';\nimport * as L from 'leaflet';\n\nexport function createMapWidget(containerDomNode) {\n  const map = L.map(containerDomNode);\n  map.setView([0, 0], 0);\n  L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {\n    maxZoom: 19,\n    attribution: '© OpenStreetMap'\n  }).addTo(map);\n  return map;\n}\n\nexport function addPopupToMapWidget(map) {\n  const popupDiv = document.createElement('div');\n  L.popup()\n    .setLatLng([0, 0])\n    .setContent(popupDiv)\n    .openOn(map);\n  return popupDiv;\n}\n```\n\n```css\nbutton { margin: 5px; }\n```\n\n</Sandpack>\n"
  },
  {
    "path": "src/content/reference/react-dom/flushSync.md",
    "content": "---\ntitle: flushSync\n---\n\n<Pitfall>\n\n`flushSync` の使用は一般的ではなく、アプリのパフォーマンスを低下させる可能性があります。\n\n</Pitfall>\n\n<Intro>\n\n`flushSync` は、渡されたコールバック関数内のあらゆる更新作業を強制的かつ同期的にフラッシュ (flush) するように React に指示します。これにより、DOM が直ちに更新されることが保証されます。\n\n```js\nflushSync(callback)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `flushSync(callback)` {/*flushsync*/}\n\n`flushSync` を呼び出して、保留中の作業をフラッシュし、DOM を同期的に更新するよう React に強制します。\n\n```js\nimport { flushSync } from 'react-dom';\n\nflushSync(() => {\n  setSomething(123);\n});\n```\n\nほとんどの場合、`flushSync` の使用は避けることができます。`flushSync` は最後の手段として使用してください。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n\n* `callback`: 関数。React はこのコールバックを直ちに呼び出し、そこに含まれるすべての更新作業を同期的にフラッシュします。また、保留中の更新、エフェクト、エフェクト内の更新もフラッシュすることがあります。この `flushSync` 呼び出しの結果として更新のサスペンドが起きると、フォールバックが再表示される可能性があります。\n\n#### 返り値 {/*returns*/}\n\n`flushSync` は `undefined` を返します。\n\n#### 注意点 {/*caveats*/}\n\n* `flushSync` はパフォーマンスを大幅に低下させることがあります。慎重に使用してください。\n* `flushSync` により、保留中のサスペンスバウンダリが強制的に `fallback` 状態で表示される可能性があります。\n* `flushSync` は、保留中のエフェクトを実行し、リターンする前にそれらに含まれる更新を同期的に適用することがあります。\n* `flushSync` は、コールバック内の更新をフラッシュするために必要な場合、コールバックの外にある更新もフラッシュすることがあります。例えば、クリックに起因する保留中の更新がある場合、React はコールバック内の更新をフラッシュする前にそれらをフラッシュする可能性があります。\n\n---\n\n## 使用法 {/*usage*/}\n\n### サードパーティコードとの統合のために更新作業をフラッシュ {/*flushing-updates-for-third-party-integrations*/}\n\nブラウザ API や UI ライブラリなどのサードパーティのコードと統合作業を行う際には、React に更新をフラッシュするように強制する必要があるかもしれません。コールバック内の任意の <CodeStep step={1}>state 更新</CodeStep> を同期的にフラッシュするよう React に強制するために `flushSync` を使用します。\n\n```js [[1, 2, \"setSomething(123)\"]]\nflushSync(() => {\n  setSomething(123);\n});\n// By this line, the DOM is updated.\n```\n\nこれにより、コードの次の行が実行される時点で、React はすでに DOM を更新しているということが保証されます。\n\n**`flushSync` の使用は一般的ではなく、頻繁に使用するとアプリのパフォーマンスが大幅に低下する可能性があります**。アプリが React の API のみを使用し、サードパーティのライブラリとの結合がない場合、`flushSync` は不要のはずです。\n\nしかし、これはブラウザの API などのサードパーティのコードとの統合に役立つことがあります。\n\n一部のブラウザの API は、コールバック内の結果がコールバックの終了時点までに同期的に DOM に書き込まれ、ブラウザがレンダーされた DOM を操作できるようになることを期待しています。ほとんどの場合 React はこれを自動的に処理します。しかし、一部のケースでは同期的な更新を強制する必要があるかもしれません。\n\n例えば、ブラウザの `onbeforeprint` API を用いると、印刷ダイアログが開く直前にページを変更することができます。これは、ドキュメントが印刷用により良く表示されるよう、カスタム印刷スタイルを適用する際に有用です。以下の例では、`onbeforeprint` コールバック内で `flushSync` を使用して、React の state を即座に DOM に「フラッシュ」します。これにより、印刷ダイアログが開いた時点では、`isPrinting` として \"yes\" が表示されます。\n\n<Sandpack>\n\n```js src/App.js active\nimport { useState, useEffect } from 'react';\nimport { flushSync } from 'react-dom';\n\nexport default function PrintApp() {\n  const [isPrinting, setIsPrinting] = useState(false);\n\n  useEffect(() => {\n    function handleBeforePrint() {\n      flushSync(() => {\n        setIsPrinting(true);\n      })\n    }\n\n    function handleAfterPrint() {\n      setIsPrinting(false);\n    }\n\n    window.addEventListener('beforeprint', handleBeforePrint);\n    window.addEventListener('afterprint', handleAfterPrint);\n    return () => {\n      window.removeEventListener('beforeprint', handleBeforePrint);\n      window.removeEventListener('afterprint', handleAfterPrint);\n    }\n  }, []);\n\n  return (\n    <>\n      <h1>isPrinting: {isPrinting ? 'yes' : 'no'}</h1>\n      <button onClick={() => window.print()}>\n        Print\n      </button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n`flushSync` がない場合、印刷ダイアログが表示される時点での `isPrinting` は \"no\" になります。これは、React が更新を非同期的にバッチ（束ね）処理するため、state の更新処理がなされる前に印刷ダイアログが表示されるからです。\n\n<Pitfall>\n\n`flushSync` はパフォーマンスを大幅に低下させ、保留中のサスペンスバウンダリのフォールバックが予期せず表示されてしまう可能性があります。\n\nほとんどの場合、`flushSync` の使用は避けることができるので、`flushSync` は最後の手段として使用してください。\n\n</Pitfall>\n\n---\n\n## Troubleshooting {/*troubleshooting*/}\n\n### I'm getting an error: \"flushSync was called from inside a lifecycle method\" {/*im-getting-an-error-flushsync-was-called-from-inside-a-lifecycle-method*/}\n\n\nReact cannot `flushSync` in the middle of a render. If you do, it will noop and warn:\n\n<ConsoleBlock level=\"error\">\n\nWarning: flushSync was called from inside a lifecycle method. React cannot flush when React is already rendering. Consider moving this call to a scheduler task or micro task.\n\n</ConsoleBlock>\n\nThis includes calling `flushSync` inside:\n\n- rendering a component.\n- `useLayoutEffect` or `useEffect` hooks.\n- Class component lifecycle methods.\n\nFor example, calling `flushSync` in an Effect will noop and warn:\n\n```js\nimport { useEffect } from 'react';\nimport { flushSync } from 'react-dom';\n\nfunction MyComponent() {\n  useEffect(() => {\n    // 🚩 Wrong: calling flushSync inside an effect\n    flushSync(() => {\n      setSomething(newValue);\n    });\n  }, []);\n\n  return <div>{/* ... */}</div>;\n}\n```\n\nTo fix this, you usually want to move the `flushSync` call to an event:\n\n```js\nfunction handleClick() {\n  // ✅ Correct: flushSync in event handlers is safe\n  flushSync(() => {\n    setSomething(newValue);\n  });\n}\n```\n\n\nIf it's difficult to move to an event, you can defer `flushSync` in a microtask:\n\n```js {3,7}\nuseEffect(() => {\n  // ✅ Correct: defer flushSync to a microtask\n  queueMicrotask(() => {\n    flushSync(() => {\n      setSomething(newValue);\n    });\n  });\n}, []);\n```\n\nThis will allow the current render to finish and schedule another syncronous render to flush the updates.\n\n<Pitfall>\n\n`flushSync` can significantly hurt performance, but this particular pattern is even worse for performance. Exhaust all other options before calling `flushSync` in a microtask as an escape hatch.\n\n</Pitfall>\n"
  },
  {
    "path": "src/content/reference/react-dom/hooks/index.md",
    "content": "---\ntitle: \"組み込みの React DOM フック\"\n---\n\n<Intro>\n\n`react-dom` パッケージには、（ブラウザ DOM 環境で実行される）ウェブアプリケーション専用のフックが含まれています。これらのフックは、iOS、Android、Windows アプリケーションといった非ブラウザ環境ではサポートされていません。ウェブブラウザと*その他の環境*の両方でサポートされるフックを探している場合は、[React のフック一覧ページ](/reference/react/hooks)をご覧ください。このページは、`react-dom` パッケージのすべてのフックの一覧です。\n\n</Intro>\n\n---\n\n## フォーム関連フック {/*form-hooks*/}\n\n*フォーム*により、情報を送信するためのインタラクティブなコントロールを作成できます。コンポーネント内でフォームを管理するために以下のフックを使用できます。\n\n* [`useFormStatus`](/reference/react-dom/hooks/useFormStatus) によりフォームのステータスに基づいて UI を更新できます。\n\n```js\nfunction Form({ action }) {\n  async function increment(n) {\n    return n + 1;\n  }\n  const [count, incrementFormAction] = useActionState(increment, 0);\n  return (\n    <form action={action}>\n      <button formAction={incrementFormAction}>Count: {count}</button>\n      <Button />\n    </form>\n  );\n}\n\nfunction Button() {\n  const { pending } = useFormStatus();\n  return (\n    <button disabled={pending} type=\"submit\">\n      Submit\n    </button>\n  );\n}\n```\n"
  },
  {
    "path": "src/content/reference/react-dom/hooks/useFormStatus.md",
    "content": "---\ntitle: useFormStatus\n---\n\n<Intro>\n\n`useFormStatus` は、直近のフォーム送信に関するステータス情報を提供するフックです。\n\n```js\nconst { pending, data, method, action } = useFormStatus();\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `useFormStatus()` {/*use-form-status*/}\n\n`useFormStatus` フックは、直近のフォーム送信に関するステータス情報を提供します。\n\n```js {5},[[1, 6, \"status.pending\"]]\nimport { useFormStatus } from \"react-dom\";\nimport action from './actions';\n\nfunction Submit() {\n  const status = useFormStatus();\n  return <button disabled={status.pending}>Submit</button>\n}\n\nexport default function App() {\n  return (\n    <form action={action}>\n      <Submit />\n    </form>\n  );\n}\n```\n\nステータス情報を取得するには、この `Submit` コンポーネントが `<form>` 内でレンダーされている必要があります。このフックは、フォームが送信中かどうかを示す <CodeStep step={1}>`pending`</CodeStep> プロパティなどの情報を返します。\n\n上記の例では、`Submit` がこの情報を使用して、フォームが送信中の間 `<button>` を無効にして押せなくしています。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n`useFormStatus` は引数を受け取りません。\n\n#### 返り値 {/*returns*/}\n\n以下のプロパティを持つ `status` オブジェクト。\n\n* `pending`: ブーリアン。`true` の場合、親 `<form>` で送信が進行中であることを意味します。それ以外の場合は `false` となります。\n\n* `data`: [`FormData` インターフェース](https://developer.mozilla.org/en-US/docs/Web/API/FormData)を実装したオブジェクト。親 `<form>` が送信中のデータを含んでいます。送信がアクティブでない場合や親 `<form>` がない場合は `null` になります。\n\n* `method`: `'get'` または `'post'` のいずれかの文字列。親 `<form>` が `GET` と `POST` [HTTP メソッド](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods)のどちらで送信されているかを表します。デフォルトでは、`<form>` は `GET` メソッドを使用しますが、[`method`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#method) によって指定することができます。\n\n[//]: # (Link to `<form>` documentation. \"Read more on the `action` prop on `<form>`.\")\n* `action`: 親 `<form>` の props である `action` に渡された関数への参照。親 `<form>` がない場合、このプロパティは `null` です。`action` プロパティに URI 値が渡された場合や `action` プロパティが指定されていない場合も、`status.action` は `null` になります。\n\n#### 注意点 {/*caveats*/}\n\n* `useFormStatus` フックは、`<form>` 内でレンダーされるコンポーネントから呼び出す必要があります。\n* `useFormStatus` は親 `<form>` のステータス情報のみを返します。同じコンポーネントや子コンポーネント内でレンダーされた `<form>` のステータス情報は返しません。\n\n---\n\n## 使用法 {/*usage*/}\n\n### フォーム送信中にステータスを表示 {/*display-a-pending-state-during-form-submission*/}\nフォームの送信中にそのステータスを表示するには、`<form>` 内でレンダーされるコンポーネントで `useFormStatus` フックを呼び出し、返された `pending` プロパティを読み取ります。\n\n以下では、フォームが送信中であることを示すために `pending` プロパティを使用しています。\n\n<Sandpack>\n\n```js src/App.js\nimport { useFormStatus } from \"react-dom\";\nimport { submitForm } from \"./actions.js\";\n\nfunction Submit() {\n  const { pending } = useFormStatus();\n  return (\n    <button type=\"submit\" disabled={pending}>\n      {pending ? \"Submitting...\" : \"Submit\"}\n    </button>\n  );\n}\n\nfunction Form({ action }) {\n  return (\n    <form action={action}>\n      <Submit />\n    </form>\n  );\n}\n\nexport default function App() {\n  return <Form action={submitForm} />;\n}\n```\n\n```js src/actions.js hidden\nexport async function submitForm(query) {\n    await new Promise((res) => setTimeout(res, 1000));\n}\n```\n</Sandpack>  \n\n<Pitfall>\n\n##### `useFormStatus` は同じコンポーネントでレンダーされた `<form>` のステータス情報を返さない {/*useformstatus-will-not-return-status-information-for-a-form-rendered-in-the-same-component*/}\n\n`useFormStatus` フックは親の `<form>` に対するステータス情報のみを返します。フックを呼び出しているのと同じコンポーネントや子コンポーネントでレンダーされる `<form>` には対応していません。\n\n```js\nfunction Form() {\n  // 🚩 `pending` will never be true\n  // useFormStatus does not track the form rendered in this component\n  const { pending } = useFormStatus();\n  return <form action={submit}></form>;\n}\n```\n\nこうするのではなく、`useFormStatus` を `<form>` の内部にあるコンポーネントから呼び出してください。\n\n```js\nfunction Submit() {\n  // ✅ `pending` will be derived from the form that wraps the Submit component\n  const { pending } = useFormStatus(); \n  return <button disabled={pending}>...</button>;\n}\n\nfunction Form() {\n  // This is the <form> `useFormStatus` tracks\n  return (\n    <form action={submit}>\n      <Submit />\n    </form>\n  );\n}\n```\n\n</Pitfall>\n\n### ユーザが送信中のフォームデータを読み取る {/*read-form-data-being-submitted*/}\n\n`useFormStatus` から返されるステータス情報の `data` プロパティを使用して、ユーザが送信しているデータを表示できます。\n\n以下の例は、ユーザが自分の欲しいユーザネームを要求できるフォームです。`useFormStatus` を使用することで、ユーザがどんなユーザネームを要求したのか確認できる一時的なステータスメッセージを表示できます。\n\n<Sandpack>\n\n```js src/UsernameForm.js active\nimport {useState, useMemo, useRef} from 'react';\nimport {useFormStatus} from 'react-dom';\n\nexport default function UsernameForm() {\n  const {pending, data} = useFormStatus();\n\n  return (\n    <div>\n      <h3>Request a Username: </h3>\n      <input type=\"text\" name=\"username\" disabled={pending}/>\n      <button type=\"submit\" disabled={pending}>\n        Submit\n      </button>\n      <br />\n      <p>{data ? `Requesting ${data?.get(\"username\")}...`: ''}</p>\n    </div>\n  );\n}\n```\n\n```js src/App.js\nimport UsernameForm from './UsernameForm';\nimport { submitForm } from \"./actions.js\";\nimport {useRef} from 'react';\n\nexport default function App() {\n  const ref = useRef(null);\n  return (\n    <form ref={ref} action={async (formData) => {\n      await submitForm(formData);\n      ref.current.reset();\n    }}>\n      <UsernameForm />\n    </form>\n  );\n}\n```\n\n```js src/actions.js hidden\nexport async function submitForm(query) {\n    await new Promise((res) => setTimeout(res, 2000));\n}\n```\n\n```css\np {\n    height: 14px;\n    padding: 0;\n    margin: 2px 0 0 0 ;\n    font-size: 14px\n}\n\nbutton {\n    margin-left: 2px;\n}\n\n```\n\n</Sandpack>  \n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### `status.pending` が `true` にならない {/*pending-is-never-true*/}\n\n`useFormStatus` は親の `<form>` に対するステータス情報のみを返します。\n\n`useFormStatus` を呼び出しているコンポーネントが `<form>` の中にネストされていない場合、`status.pending` は常に `false` を返します。`useFormStatus` が `<form>` 要素の子コンポーネント内で呼び出されていることを確認してください。\n\n`useFormStatus` は、同じコンポーネントでレンダーされた `<form>` の状態は追跡しません。詳細は[落とし穴](#useformstatus-will-not-return-status-information-for-a-form-rendered-in-the-same-component)欄を参照してください。\n"
  },
  {
    "path": "src/content/reference/react-dom/index.md",
    "content": "---\ntitle: React DOM API\n---\n\n<Intro>\n\n`react-dom` パッケージには、ウェブアプリケーション（ブラウザの DOM 環境で動作する）でのみサポートされるメソッドが含まれています。これらは React Native ではサポートされません。\n\n</Intro>\n\n---\n\n## API {/*apis*/}\n\nこれらの API はインポートしてコンポーネントで使用できます。これらはあまり使用されません。\n\n* [`createPortal`](/reference/react-dom/createPortal) は、DOM ツリーの別の場所に子コンポーネントをレンダーできるようにします。\n* [`flushSync`](/reference/react-dom/flushSync) は、React に state の更新を強制的にフラッシュさせ、DOM を同期的に更新させます。\n\n## リソースプリロード関連の API {/*resource-preloading-apis*/}\n\nこれらの API は、例えばスクリプト、スタイルシート、フォントなどのリソースを必要とする別のページに実際に遷移する前に、それらのリソースが必要となることが判明した時点で即座にプリロードを開始することで、アプリを高速化するためのものです。\n\n[React ベースのフレームワーク](/learn/creating-a-react-app)は、多くの場合リソースの読み込みを自動で処理してくれるため、この API を直接呼び出す必要はないかもしれません。詳細はフレームワークのドキュメントを参照してください。\n\n* [`prefetchDNS`](/reference/react-dom/prefetchDNS) は、接続予定の DNS ドメインネームに対応する IP アドレスをプリフェッチします。\n* [`preconnect`](/reference/react-dom/preconnect) は、具体的なリソースが不明な場合でも事前にリクエスト先のサーバへの接続を確立します。\n* [`preload`](/reference/react-dom/preload) は、使用予定のスタイルシート、フォント、画像や外部スクリプトのフェッチを行います。\n* [`preloadModule`](/reference/react-dom/preloadModule) は、使用予定の ESM モジュールのフェッチを行います。\n* [`preinit`](/reference/react-dom/preinit) は、外部スクリプトのフェッチと実行、またはスタイルシートのフェッチと挿入を行います。\n* [`preinitModule`](/reference/react-dom/preinitModule) は、ESM モジュールのフェッチと実行を行います。\n\n---\n\n## エントリポイント {/*entry-points*/}\n\n`react-dom` パッケージは、2 つの追加のエントリポイントを提供します。\n\n* [`react-dom/client`](/reference/react-dom/client) は、クライアント（ブラウザ内）で React コンポーネントをレンダーするための API を含んでいます。\n* [`react-dom/server`](/reference/react-dom/server) は、サーバ上で React コンポーネントをレンダーするための API を含んでいます。\n\n---\n\n## 削除済み API {/*removed-apis*/}\n\n以下の API は React 19 で削除されています。\n\n* [`findDOMNode`](https://18.react.dev/reference/react-dom/findDOMNode): [代替手段](https://18.react.dev/reference/react-dom/findDOMNode#alternatives)を参照してください。\n* [`hydrate`](https://18.react.dev/reference/react-dom/hydrate): 代わりに [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を使用してください。\n* [`render`](https://18.react.dev/reference/react-dom/render): 代わりに [`createRoot`](/reference/react-dom/client/createRoot) を使用してください。\n* [`unmountComponentAtNode`](/reference/react-dom/unmountComponentAtNode): 代わりに [`root.unmount()`](/reference/react-dom/client/createRoot#root-unmount) を使用してください。\n* [`renderToNodeStream`](https://18.react.dev/reference/react-dom/server/renderToNodeStream): 代わりに [`react-dom/server`](/reference/react-dom/server) の API を使用してください。\n* [`renderToStaticNodeStream`](https://18.react.dev/reference/react-dom/server/renderToStaticNodeStream): 代わりに [`react-dom/server`](/reference/react-dom/server) の API を使用してください。\n"
  },
  {
    "path": "src/content/reference/react-dom/preconnect.md",
    "content": "---\ntitle: preconnect\n---\n\n<Intro>\n\n`preconnect` を使用して、リソースをロードする予定のサーバにあらかじめ接続しておくことができます。\n\n```js\npreconnect(\"https://example.com\");\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `preconnect(href)` {/*preconnect*/}\n\nホストに事前接続を行うためには、`react-dom` の `preconnect` 関数を呼び出します。\n\n```js\nimport { preconnect } from 'react-dom';\n\nfunction AppRoot() {\n  preconnect(\"https://example.com\");\n  // ...\n}\n\n```\n\n[さらに例を見る](#usage)\n\n`preconnect` 関数は、指定されたサーバへの接続を開くよう、ブラウザに対してヒントを与えます。ブラウザがそのヒントに従うと、そのサーバからのリソースのロードが素早く行える可能性があります。\n\n#### 引数 {/*parameters*/}\n\n* `href`: 文字列。接続したいサーバの URL。\n\n\n#### 返り値 {/*returns*/}\n\n`preconnect` は何も返しません。\n\n#### 注意点 {/*caveats*/}\n\n* 同じサーバに対して `preconnect` を複数回呼び出した場合の効果は、一度のみ呼び出した場合と同様です。\n* ブラウザからは、コンポーネントのレンダー中、エフェクト内、イベントハンドラ内も含むどんな状況においても `preconnect` の呼び出しが可能です。\n* サーバサイドレンダリングやサーバコンポーネントのレンダー時には、コンポーネントのレンダー中やレンダーから派生した非同期処理の中で `preconnect` を呼び出した場合にのみ効果があります。それ以外の呼び出しは無視されます。\n* どのリソースが必要か具体的に分かっている場合は、リソースのロードを即座に開始する[他の関数](/reference/react-dom/#resource-preloading-apis)を利用することができます。\n* ウェブページ自体がホストされているのと同じサーバに事前接続する利点はありません。接続のヒントが与えられた時点で既に接続が完了しているからです。\n\n---\n\n## 使用法 {/*usage*/}\n\n### レンダー時の事前接続 {/*preconnecting-when-rendering*/}\n\nコンポーネントをレンダーする際に子コンポーネントがホストから外部リソースをロードすると分かっている場合に、`preconnect` を呼び出しておきます。\n\n```js\nimport { preconnect } from 'react-dom';\n\nfunction AppRoot() {\n  preconnect(\"https://example.com\");\n  return ...;\n}\n```\n\n### イベントハンドラ内での事前接続 {/*preconnecting-in-an-event-handler*/}\n\n外部リソースを必要とするページ遷移や状態遷移を行う前に、イベントハンドラで `preconnect` を呼び出しておきます。これにより、新しいページや状態がレンダーされる時点で読み込むのと比べ、早期に処理を開始できます。\n\n```js\nimport { preconnect } from 'react-dom';\n\nfunction CallToAction() {\n  const onClick = () => {\n    preconnect('http://example.com');\n    startWizard();\n  }\n  return (\n    <button onClick={onClick}>Start Wizard</button>\n  );\n}\n```\n"
  },
  {
    "path": "src/content/reference/react-dom/prefetchDNS.md",
    "content": "---\ntitle: prefetchDNS\n---\n\n<Intro>\n\n`prefetchDNS` を使用して、リソースをロードする予定のサーバに対して IP ルックアップを事前に行うことができます。\n\n```js\nprefetchDNS(\"https://example.com\");\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `prefetchDNS(href)` {/*prefetchdns*/}\n\nホストのルックアップを行うには、`react-dom` の `prefetchDNS` 関数を呼び出します。\n\n```js\nimport { prefetchDNS } from 'react-dom';\n\nfunction AppRoot() {\n  prefetchDNS(\"https://example.com\");\n  // ...\n}\n\n```\n\n[さらに例を見る](#usage)\n\nprefetchDNS 関数は、指定されたサーバの IP アドレスを調べるようブラウザに対してヒントを与えます。ブラウザがそのヒントに従うと、そのサーバからのリソースのロードが素早く行える可能性があります。\n\n#### 引数 {/*parameters*/}\n\n* `href`: 文字列。接続したいサーバの URL。\n\n#### 返り値 {/*returns*/}\n\n`prefetchDNS` は何も返しません。\n\n#### 注意点 {/*caveats*/}\n\n* 同じサーバに対して `prefetchDNS` を複数回呼び出した場合の効果は、一度のみ呼び出した場合と同様です。\n* ブラウザからは、コンポーネントのレンダー中、エフェクト内、イベントハンドラ内も含むどんな状況においても `prefetchDNS` の呼び出しが可能です。\n* サーバサイドレンダリングやサーバコンポーネントのレンダー時には、コンポーネントのレンダー中やレンダーから派生した非同期処理の中で `prefetchDNS` を呼び出した場合にのみ効果があります。それ以外の呼び出しは無視されます。\n* どのリソースが必要か具体的に分かっている場合は、リソースのロードを即座に開始する[他の関数](/reference/react-dom/#resource-preloading-apis)を利用することができます。\n* ウェブページ自体がホストされているのと同じサーバにプリフェッチを行う利点はありません。ヒントが与えられた時点で既にルックアップが完了しているからです。\n* [`preconnect`](/reference/react-dom/preconnect) と比較して、大量のドメインに投機的に接続する場合は `prefetchDNS` の方が適しているかもしれません。大量に事前接続するとそのオーバーヘッドが利益を上回る可能性があるためです。\n\n---\n\n## 使用法 {/*usage*/}\n\n### レンダー時の DNS プリフェッチ {/*prefetching-dns-when-rendering*/}\n\nコンポーネントをレンダーする際に子コンポーネントがホストから外部リソースをロードすると分かっている場合に、`prefetchDNS` を呼び出しておきます。\n\n```js\nimport { prefetchDNS } from 'react-dom';\n\nfunction AppRoot() {\n  prefetchDNS(\"https://example.com\");\n  return ...;\n}\n```\n\n### イベントハンドラ内での DNS プリフェッチ {/*prefetching-dns-in-an-event-handler*/}\n\n外部リソースを必要とするページ遷移や状態遷移を行う前に、イベントハンドラで `prefetchDNS` を呼び出しておきます。これにより、新しいページや状態がレンダーされる時点で読み込むのと比べ、早期に処理を開始できます。\n\n```js\nimport { prefetchDNS } from 'react-dom';\n\nfunction CallToAction() {\n  const onClick = () => {\n    prefetchDNS('http://example.com');\n    startWizard();\n  }\n  return (\n    <button onClick={onClick}>Start Wizard</button>\n  );\n}\n```\n"
  },
  {
    "path": "src/content/reference/react-dom/preinit.md",
    "content": "---\ntitle: preinit\n---\n\n<Note>\n\n[React ベースのフレームワーク](/learn/creating-a-react-app)は、多くの場合リソースの読み込みを自動で処理してくれるため、この API を直接呼び出す必要はないかもしれません。詳細はフレームワークのドキュメントを参照してください。\n\n</Note>\n\n<Intro>\n\n`preinit` を使用して、スタイルシートや外部スクリプトを事前にフェッチして評価することができます。\n\n```js\npreinit(\"https://example.com/script.js\", {as: \"script\"});\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `preinit(href, options)` {/*preinit*/}\n\nスクリプトやスタイルシートを事前初期化するためには、`react-dom` の `preinit` 関数を呼び出します。\n\n```js\nimport { preinit } from 'react-dom';\n\nfunction AppRoot() {\n  preinit(\"https://example.com/script.js\", {as: \"script\"});\n  // ...\n}\n\n```\n\n[さらに例を見る](#usage)\n\n`preinit` 関数は、指定されたリソースのダウンロードと実行を開始するようブラウザに対してヒントを与えます。これにより時間を節約できる可能性があります。`preinit` されたスクリプトは、ダウンロードが完了すると実行されます。preinit されたスタイルシートはドキュメントに挿入され、すぐに効果が現れます。\n\n#### 引数 {/*parameters*/}\n\n* `href`: 文字列。ダウンロードして実行したいリソースの URL。\n* `options`: オブジェクト。以下のプロパティを含みます。\n  * `as`: 必須の文字列。リソースの種別。可能な値は `script` と `style` です。\n  * `precedence`: 文字列。スタイルシートの場合は必須。他のスタイルシートに対する相対的な挿入位置を指定します。優先度が高いスタイルシートは、低いものをオーバーライドできます。指定可能な値は `reset`、`low`、`medium`、`high` です。\n  * `crossOrigin`: 文字列。使用する [CORS ポリシー](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin)。可能な値は `anonymous` と `use-credentials` です。\n  * `integrity`: 文字列。[真正性を検証する](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)ために使用するリソースの暗号化ハッシュ。\n  * `nonce`: 文字列。厳格なコンテンツセキュリティポリシーを使用する際に[リソースを許可するための暗号化 nonce](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce)。\n  * `fetchPriority`: 文字列。リソースの相対的なフェッチ優先度のヒントです。指定可能な値は `auto`（デフォルト）、`high`、`low` です。\n\n#### 返り値 {/*returns*/}\n\n`preinit` は何も返しません。\n\n#### 注意点 {/*caveats*/}\n\n* 同じ `href` で `preinit` を複数回呼び出した場合の効果は、一度のみ呼び出した場合と同様です。\n* ブラウザからは、コンポーネントのレンダー中、エフェクト内、イベントハンドラ内も含むどんな状況においても `preinit` の呼び出しが可能です。\n* サーバサイドレンダリングやサーバコンポーネントのレンダー時には、コンポーネントのレンダー中やレンダーから派生した非同期処理の中で `preinit` を呼び出した場合にのみ効果があります。それ以外の呼び出しは無視されます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### レンダー時の事前初期化 {/*preiniting-when-rendering*/}\n\nコンポーネントをレンダーする際に自身あるいはその子が特定のリソースを使用することが分かっており、かつそのリソースがダウンロードされた直後に評価され効果が現れても問題ないという場合、`preinit` を呼び出します。\n\n<Recipes titleText=\"事前初期化の例\">\n\n#### 外部スクリプトの事前初期化 {/*preiniting-an-external-script*/}\n\n```js\nimport { preinit } from 'react-dom';\n\nfunction AppRoot() {\n  preinit(\"https://example.com/script.js\", {as: \"script\"});\n  return ...;\n}\n```\n\nブラウザにスクリプトをダウンロードさせたいが、すぐに実行させたくない場合は、代わりに [`preload`](/reference/react-dom/preload) を使用してください。ESM モジュールをロードしたい場合は、[`preinitModule`](/reference/react-dom/preinitModule) を使用してください。\n\n<Solution />\n\n#### スタイルシートの事前初期化 {/*preiniting-a-stylesheet*/}\n\n```js\nimport { preinit } from 'react-dom';\n\nfunction AppRoot() {\n  preinit(\"https://example.com/style.css\", {as: \"style\", precedence: \"medium\"});\n  return ...;\n}\n```\n\n`precedence` オプション（必須）を使用することで、ドキュメント内でのスタイルシートの順序を制御します。優先度が高いスタイルシートは、優先度が低いものをオーバーライドできます。\n\nスタイルシートをダウンロードしたいがすぐにはドキュメントに挿入したくないという場合、代わりに [`preload`](/reference/react-dom/preload) を使用してください。\n\n<Solution />\n\n</Recipes>\n\n### イベントハンドラ内での事前初期化 {/*preiniting-in-an-event-handler*/}\n\n外部リソースを必要とするページ遷移や状態遷移を行う前に、イベントハンドラで `preinit` を呼び出しておきます。これにより、新しいページや状態がレンダーされる時点で読み込むのと比べ、早期に処理を開始できます。\n\n```js\nimport { preinit } from 'react-dom';\n\nfunction CallToAction() {\n  const onClick = () => {\n    preinit(\"https://example.com/wizardStyles.css\", {as: \"style\"});\n    startWizard();\n  }\n  return (\n    <button onClick={onClick}>Start Wizard</button>\n  );\n}\n```\n"
  },
  {
    "path": "src/content/reference/react-dom/preinitModule.md",
    "content": "---\ntitle: preinitModule\n---\n\n<Note>\n\n[React ベースのフレームワーク](/learn/creating-a-react-app)は、多くの場合リソースの読み込みを自動で処理してくれるため、この API を直接呼び出す必要はないかもしれません。詳細はフレームワークのドキュメントを参照してください。\n\n</Note>\n\n<Intro>\n\n`preinitModule` は、ESM モジュールを事前にフェッチして評価することができます。\n\n```js\npreinitModule(\"https://example.com/module.js\", {as: \"script\"});\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `preinitModule(href, options)` {/*preinitmodule*/}\n\nESM モジュールを事前初期化するためには、`react-dom` の `preinitModule` 関数を呼び出します。\n\n```js\nimport { preinitModule } from 'react-dom';\n\nfunction AppRoot() {\n  preinitModule(\"https://example.com/module.js\", {as: \"script\"});\n  // ...\n}\n\n```\n\n[さらに例を見る](#usage)\n\n`preinitModule` 関数は、指定されたモジュールのダウンロードと実行を開始するようブラウザに対してヒントを与えます。これにより時間を節約できる可能性があります。事前初期化されたスクリプトは、ダウンロードが完了すると実行されます。\n\n#### 引数 {/*parameters*/}\n\n* `href`: 文字列。ダウンロードして実行したいモジュールの URL。\n* `options`: オブジェクト。以下のプロパティを含みます。\n  *  `as`: 必須の文字列。`'script'` である必要があります。\n  *  `crossOrigin`: 文字列。使用する [CORS ポリシー](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin)。可能な値は `anonymous` と `use-credentials` です。\n  *  `integrity`: 文字列。[真正性を検証する](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)ために使用するリソースの暗号化ハッシュ。\n  *  `nonce`: 文字列。厳格なコンテンツセキュリティポリシーを使用する際に[リソースを許可するための暗号化 nonce](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce)。\n\n#### 返り値 {/*returns*/}\n\n`preinitModule` は何も返しません。\n\n#### 注意点 {/*caveats*/}\n\n* 同じ `href` で `preinitModule` を複数回呼び出した場合の効果は、一度のみ呼び出した場合と同様です。\n* ブラウザからは、コンポーネントのレンダー中、エフェクト内、イベントハンドラ内も含むどんな状況においても `preinitModule` の呼び出しが可能です。\n* サーバサイドレンダリングやサーバコンポーネントのレンダー時には、コンポーネントのレンダー中やレンダーから派生した非同期処理の中で `preinitModule` を呼び出した場合にのみ効果があります。それ以外の呼び出しは無視されます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### レンダー時のプリロード {/*preloading-when-rendering*/}\n\nコンポーネントをレンダーする際に自身あるいはその子が特定のモジュールを使用することが分かっており、かつそのモジュールがダウンロードされた直後に評価され効果が現れても問題ないという場合、`preinitModule` を呼び出します。\n\n```js\nimport { preinitModule } from 'react-dom';\n\nfunction AppRoot() {\n  preinitModule(\"https://example.com/module.js\", {as: \"script\"});\n  return ...;\n}\n```\n\nブラウザにモジュールをダウンロードさせたいが、すぐに実行させたくない場合は、代わりに [`preloadModule`](/reference/react-dom/preloadModule) を使用してください。ESM モジュールではないスクリプトを事前初期化したい場合は、[`preinit`](/reference/react-dom/preinit) を使用してください。\n\n### イベントハンドラ内でのプリロード {/*preloading-in-an-event-handler*/}\n\n外部リソースを必要とするページ遷移や状態遷移を行う前に、イベントハンドラで `preinitModule` を呼び出しておきます。これにより、新しいページや状態がレンダーされる時点で読み込むのと比べ、早期に処理を開始できます。\n\n```js\nimport { preinitModule } from 'react-dom';\n\nfunction CallToAction() {\n  const onClick = () => {\n    preinitModule(\"https://example.com/module.js\", {as: \"script\"});\n    startWizard();\n  }\n  return (\n    <button onClick={onClick}>Start Wizard</button>\n  );\n}\n```\n"
  },
  {
    "path": "src/content/reference/react-dom/preload.md",
    "content": "---\ntitle: preload\n---\n\n<Note>\n\n[React ベースのフレームワーク](/learn/creating-a-react-app)は、多くの場合リソースの読み込みを自動で処理してくれるため、この API を直接呼び出す必要はないかもしれません。詳細はフレームワークのドキュメントを参照してください。\n\n</Note>\n\n<Intro>\n\n`preload` を使用して、後で使用する予定のスタイルシート、フォント、外部スクリプトなどのリソースを事前にフェッチすることができます。\n\n```js\npreload(\"https://example.com/font.woff2\", {as: \"font\"});\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `preload(href, options)` {/*preload*/}\n\nリソースを事前に読み込むためには、`react-dom` の `preload` 関数を呼び出します。\n\n```js\nimport { preload } from 'react-dom';\n\nfunction AppRoot() {\n  preload(\"https://example.com/font.woff2\", {as: \"font\"});\n  // ...\n}\n\n```\n\n[さらに例を見る](#usage)\n\n`preload` 関数は、指定されたリソースのダウンロードを開始するようブラウザに対してヒントを与えます。これにより時間を節約できる可能性があります。\n\n#### 引数 {/*parameters*/}\n\n* `href`: 文字列。ダウンロードしたいリソースの URL。\n* `options`: オブジェクト。以下のプロパティを含みます。\n  *  `as`: 必須の文字列。リソースの種別。[指定可能な値](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#as)は `audio`、`document`、`embed`、`fetch`、`font`、`image`、`object`、`script`、`style`、`track`、`video`、`worker` です。\n  *  `crossOrigin`: 文字列。使用する [CORS ポリシー](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin)。可能な値は `anonymous` と `use-credentials` です。`as` が `\"fetch\"` に設定されている場合は必須です。\n  *  `referrerPolicy`: 文字列。フェッチ時に送信する [Referrer ヘッダー](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#referrerpolicy)。指定可能な値は `no-referrer-when-downgrade`（デフォルト）、`no-referrer`, `origin`, `origin-when-cross-origin`, `unsafe-url` です。\n  *  `integrity`: 文字列。[真正性を検証する](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)ために使用するリソースの暗号化ハッシュ。\n  *  `type`: 文字列。リソースの MIME タイプ。\n  *  `nonce`: 文字列。厳格なコンテンツセキュリティポリシーを使用する際に[リソースを許可するための暗号化 nonce](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce)。\n  *  `fetchPriority`: 文字列。リソースの相対的なフェッチ優先度のヒントです。指定可能な値は `auto`（デフォルト）、`high`、`low` です。\n  *  `imageSrcSet`: 文字列。`as: \"image\"` の場合にのみ使用します。[画像のソースセット](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)を指定します。\n  *  `imageSizes`: 文字列。`as: image\"` の場合にのみ使用します。[画像のサイズ](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)を指定します。\n\n#### 返り値 {/*returns*/}\n\n`preload` は何も返しません。\n\n#### 注意点 {/*caveats*/}\n\n* 同等の `preload` を複数回呼び出した場合の効果は、一度のみ呼び出した場合と同様です。`preload` の呼び出しが同等であるかどうかの判断は以下のルールに従います。\n  * `href` が同じであれば、2 つの呼び出しは同等とみなす。\n  * ただし `as` が `image` に設定されている場合、`href`、`imageSrcSet`、`imageSizes` がすべて同一である場合、2 つの呼び出しを同等とみなす。\n* ブラウザからは、コンポーネントのレンダー中、エフェクト内、イベントハンドラ内も含むどんな状況においても `preload` の呼び出しが可能です。\n* サーバサイドレンダリングやサーバコンポーネントのレンダー時には、コンポーネントのレンダー中やレンダーから派生した非同期処理の中で `preload` を呼び出した場合にのみ効果があります。それ以外の呼び出しは無視されます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### レンダー時のプリロード {/*preloading-when-rendering*/}\n\nコンポーネントをレンダーする際に自身あるいはその子が特定のリソースを使用することが分かっている場合、`preload` を呼び出します。\n\n<Recipes titleText=\"プリロードの例\">\n\n#### 外部スクリプトのプリロード {/*preloading-an-external-script*/}\n\n```js\nimport { preload } from 'react-dom';\n\nfunction AppRoot() {\n  preload(\"https://example.com/script.js\", {as: \"script\"});\n  return ...;\n}\n```\n\nブラウザに（ダウンロードのみではなく）スクリプトの実行も即座に開始させたい場合、代わりに [`preinit`](/reference/react-dom/preinit) を使用してください。ESM モジュールをロードしたい場合は、[`preloadModule`](/reference/react-dom/preloadModule) を使用してください。\n\n<Solution />\n\n#### スタイルシートのプリロード {/*preloading-a-stylesheet*/}\n\n```js\nimport { preload } from 'react-dom';\n\nfunction AppRoot() {\n  preload(\"https://example.com/style.css\", {as: \"style\"});\n  return ...;\n}\n```\n\nスタイルシートを文書に即座に挿入したい場合（つまり、ブラウザにダウンロードのみではなく内容の解析も即座にさせたい場合）、代わりに [`preinit`](/reference/react-dom/preinit) を使用してください。\n\n<Solution />\n\n#### フォントのプリロード {/*preloading-a-font*/}\n\n```js\nimport { preload } from 'react-dom';\n\nfunction AppRoot() {\n  preload(\"https://example.com/style.css\", {as: \"style\"});\n  preload(\"https://example.com/font.woff2\", {as: \"font\"});\n  return ...;\n}\n```\n\nスタイルシートをプリロードする場合、スタイルシートが参照するフォントもプリロードすると良いでしょう。これにより、ブラウザはスタイルシートのダウンロードと解析を行う前に、フォントのダウンロードを開始できます。\n\n<Solution />\n\n#### 画像のプリロード {/*preloading-an-image*/}\n\n```js\nimport { preload } from 'react-dom';\n\nfunction AppRoot() {\n  preload(\"/banner.png\", {\n    as: \"image\",\n    imageSrcSet: \"/banner512.png 512w, /banner1024.png 1024w\",\n    imageSizes: \"(max-width: 512px) 512px, 1024px\",\n  });\n  return ...;\n}\n```\n\n画像をプリロードする際、`imageSrcSet` と `imageSizes` オプションは、ブラウザが[画面のサイズに適した画像を取得する](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)のに役立ちます。\n\n<Solution />\n\n</Recipes>\n\n### イベントハンドラでのプリロード {/*preloading-in-an-event-handler*/}\n\n外部リソースを必要とするページ遷移や状態遷移を行う前に、イベントハンドラで `preload` を呼び出しておきます。これにより、新しいページや状態がレンダーされる時点で読み込むのと比べ、早期に処理を開始できます。\n\n```js\nimport { preload } from 'react-dom';\n\nfunction CallToAction() {\n  const onClick = () => {\n    preload(\"https://example.com/wizardStyles.css\", {as: \"style\"});\n    startWizard();\n  }\n  return (\n    <button onClick={onClick}>Start Wizard</button>\n  );\n}\n```\n"
  },
  {
    "path": "src/content/reference/react-dom/preloadModule.md",
    "content": "---\ntitle: preloadModule\n---\n\n<Note>\n\n[React ベースのフレームワーク](/learn/creating-a-react-app)は、多くの場合リソースの読み込みを自動で処理してくれるため、この API を直接呼び出す必要はないかもしれません。詳細はフレームワークのドキュメントを参照してください。\n\n</Note>\n\n<Intro>\n\n`preloadModule` は、使用予定の ESM モジュールを事前にフェッチすることができます。\n\n```js\npreloadModule(\"https://example.com/module.js\", {as: \"script\"});\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `preloadModule(href, options)` {/*preloadmodule*/}\n\nESM モジュールをプリロードするためには、`react-dom` の `preloadModule` 関数を呼び出します。\n\n```js\nimport { preloadModule } from 'react-dom';\n\nfunction AppRoot() {\n  preloadModule(\"https://example.com/module.js\", {as: \"script\"});\n  // ...\n}\n\n```\n\n[さらに例を見る](#usage)\n\n`preloadModule` 関数は、指定されたモジュールのダウンロードを開始するようブラウザに対してヒントを与えます。これにより時間を節約できる可能性があります。\n\n#### 引数 {/*parameters*/}\n\n* `href`: 文字列。ダウンロードしたいモジュールの URL。\n* `options`: オブジェクト。以下のプロパティを含みます。\n  *  `as`: 必須の文字列。`'script'` である必要があります。\n  *  `crossOrigin`: 文字列。使用する [CORS ポリシー](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin)。可能な値は `anonymous` と `use-credentials` です。\n  *  `integrity`: 文字列。[真正性を検証する](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)ために使用するリソースの暗号化ハッシュ。\n  *  `nonce`: 文字列。厳格なコンテンツセキュリティポリシーを使用する際に[リソースを許可するための暗号化 nonce](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce)。\n\n\n#### 返り値 {/*returns*/}\n\n`preloadModule` は何も返しません。\n\n#### 注意点 {/*caveats*/}\n\n* 同じ `href` で `preloadModule` を複数回呼び出した場合の効果は、一度のみ呼び出した場合と同様です。\n* ブラウザからは、コンポーネントのレンダー中、エフェクト内、イベントハンドラ内も含むどんな状況においても `preloadModule` の呼び出しが可能です。\n* サーバサイドレンダリングやサーバコンポーネントのレンダー時には、コンポーネントのレンダー中やレンダーから派生した非同期処理の中で `preloadModule` を呼び出した場合にのみ効果があります。それ以外の呼び出しは無視されます。\n\n---\n\n## 使用法 {/*usage*/}\n\n### レンダー時のプリロード {/*preloading-when-rendering*/}\n\nコンポーネントをレンダーする際に自身あるいはその子が特定のモジュールを使用することが分かっている場合、`preloadModule` を呼び出します。\n\n```js\nimport { preloadModule } from 'react-dom';\n\nfunction AppRoot() {\n  preloadModule(\"https://example.com/module.js\", {as: \"script\"});\n  return ...;\n}\n```\n\nブラウザにモジュールのダウンロードだけでなく実行もさせたい場合は、代わりに [`preinitModule`](/reference/react-dom/preinitModule) を使用します。ESM モジュールではないスクリプトを読み込みたい場合は、[`preload`](/reference/react-dom/preload) を使用します。\n\n### イベントハンドラ内でのプリロード {/*preloading-in-an-event-handler*/}\n\n外部リソースを必要とするページ遷移や状態遷移を行う前に、イベントハンドラで `preloadModule` を呼び出しておきます。これにより、新しいページや状態がレンダーされる時点で読み込むのと比べ、早期に処理を開始できます。\n\n```js\nimport { preloadModule } from 'react-dom';\n\nfunction CallToAction() {\n  const onClick = () => {\n    preloadModule(\"https://example.com/module.js\", {as: \"script\"});\n    startWizard();\n  }\n  return (\n    <button onClick={onClick}>Start Wizard</button>\n  );\n}\n```\n"
  },
  {
    "path": "src/content/reference/react-dom/server/index.md",
    "content": "---\ntitle: サーバ用 React DOM API\n---\n\n<Intro>\n\n`react-dom/server` の API を用いて、サーバ上で React コンポーネントを HTML にレンダーすることができます。これらの API は、アプリケーションの最上位で初期 HTML を生成するために、サーバ上でのみ使用されます。[フレームワーク](/learn/creating-a-react-app#full-stack-frameworks)はこれらをあなたの代わりに呼び出すことがあります。ほとんどのコンポーネントは、これらをインポートしたり使用したりする必要はありません。\n\n</Intro>\n\n---\n\n## Web Stream 用のサーバ API {/*server-apis-for-web-streams*/}\n\n以下のメソッドは、[Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) が利用可能な環境でのみ使用できます。これには、ブラウザ、Deno、および一部のモダンなエッジランタイムが含まれます。\n\n* [`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream) は React ツリーを[読み取り可能な Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) にレンダーします。\n* [`resume`](/reference/react-dom/server/renderToPipeableStream) は [`prerender`](/reference/react-dom/static/prerender) の結果を再開して[読み取り可能な Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) に流します。\n\n\n<Note>\n\nNode.js でも互換性のためこれらのメソッドが使用可能ですが、パフォーマンスが劣化するため推奨されません。代わりに [Node.js 専用の API](#server-apis-for-nodejs-streams) を使用してください。\n\n</Note>\n---\n\n## Node.js ストリーム用のサーバ API {/*server-apis-for-nodejs-streams*/}\n\n以下のメソッドは、[Node.js ストリーム](https://nodejs.org/api/stream.html)が利用可能な環境でのみ使用できます。\n\n* [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream) は React ツリーをパイプ可能な [Node.js ストリーム](https://nodejs.org/api/stream.html)にレンダーします。\n* [`resumeToPipeableStream`](/reference/react-dom/server/renderToPipeableStream) は [`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream) の結果を再開してパイプ可能な [Node.js ストリーム](https://nodejs.org/api/stream.html)に流します。\n\n---\n\n## 非ストリーム環境向けのレガシーサーバ API {/*legacy-server-apis-for-non-streaming-environments*/}\n\n以下のメソッドは、ストリームをサポートしていない環境で使用できます。\n\n* [`renderToString`](/reference/react-dom/server/renderToString) は React ツリーを文字列にレンダーします。\n* [`renderToStaticMarkup`](/reference/react-dom/server/renderToStaticMarkup) は非インタラクティブな React ツリーを文字列にレンダーします。\n\nこれらは、ストリーミング API に比べて機能が限定されています。\n"
  },
  {
    "path": "src/content/reference/react-dom/server/renderToPipeableStream.md",
    "content": "---\ntitle: renderToPipeableStream\n---\n\n<Intro>\n\n`renderToPipeableStream` は React ツリーをパイプ可能な [Node.js ストリーム](https://nodejs.org/api/stream.html)にレンダーします。\n\n```js\nconst { pipe, abort } = renderToPipeableStream(reactNode, options?)\n```\n\n</Intro>\n\n<InlineToc />\n\n<Note>\n\nこの API は Node.js 専用です。Deno やモダンなエッジランタイムなどの [Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) を用いる環境では、[`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream) を代わりに使用してください。\n\n</Note>\n\n---\n\n## リファレンス {/*reference*/}\n\n### `renderToPipeableStream(reactNode, options?)` {/*rendertopipeablestream*/}\n\n`renderToPipeableStream` を呼び出して、React ツリーを HTML として [Node.js ストリーム](https://nodejs.org/api/stream.html#writable-streams)にレンダーします。\n\n```js\nimport { renderToPipeableStream } from 'react-dom/server';\n\nconst { pipe } = renderToPipeableStream(<App />, {\n  bootstrapScripts: ['/main.js'],\n  onShellReady() {\n    response.setHeader('content-type', 'text/html');\n    pipe(response);\n  }\n});\n```\n\nクライアント側では、このようにサーバ生成された HTML を操作可能にするために [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を用います。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `reactNode`: HTML へとレンダーしたい React ノード。例えば、`<App />` のような JSX 要素です。これはドキュメント全体を表すことが期待されているため、`App` コンポーネントは `<html>` タグをレンダーする必要があります。\n\n* **省略可能** `options`: ストリーム関連のオプションが含まれたオブジェクト。\n  * **省略可能** `bootstrapScriptContent`: 指定された場合、この文字列がインラインの `<script>` タグ内に配置されます。\n  * **省略可能** `bootstrapScripts`: ページ上に出力する `<script>` タグに対応する URL 文字列の配列。これを使用して、[`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を呼び出す `<script>` を含めます。クライアントで React をまったく実行したくない場合は省略します。\n  * **省略可能** `bootstrapModules`: `bootstrapScripts` と同様ですが、代わりに [`<script type=\"module\">`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) を出力します。\n  * **省略可能** `identifierPrefix`: React が [`useId`](/reference/react/useId) によって生成する ID に使用する文字列プレフィックス。同じページ上に複数のルートを使用する際に、競合を避けるために用います。[`hydrateRoot`](/reference/react-dom/client/hydrateRoot#parameters) にも同じプレフィックスを渡す必要があります。\n  * **省略可能** `namespaceURI`: このストリームのルート[ネームスペース URI](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#important_namespace_uris) 文字列。デフォルトでは通常の HTML です。SVG の場合は `'http://www.w3.org/2000/svg'`、MathML の場合は `'http://www.w3.org/1998/Math/MathML'` を渡します。\n  * **省略可能** `nonce`: [`script-src` Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src) を用いてスクリプトを許可するための [`nonce`](http://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#nonce) 文字列。\n  * **省略可能** `onAllReady`: [シェル](#specifying-what-goes-into-the-shell)とすべての追加[コンテンツ](#streaming-more-content-as-it-loads)の両方を含むすべてのレンダーが完了したときに呼び出されるコールバック。[クローラや静的生成向け](#waiting-for-all-content-to-load-for-crawlers-and-static-generation)の場合に、`onShellReady` の代わりに利用できます。ここからストリーミングを開始する場合、プログレッシブなローディングがなくなり、ストリームには最終的な HTML が含まれるようになります。\n  * **省略可能** `onError`: サーバエラーが発生するたびに発火するコールバック。[復帰可能なエラー](#recovering-from-errors-outside-the-shell)の場合も[そうでないエラー](#recovering-from-errors-inside-the-shell)の場合もあります。デフォルトでは `console.error` のみを呼び出します。これを上書きして[クラッシュレポートをログに記録する](#logging-crashes-on-the-server)場合でも `console.error` を呼び出すようにしてください。また、シェルが出力される前に[ステータスコードを調整する](#setting-the-status-code)ためにも使用できます。\n  * **省略可能** `onShellReady`: [初期シェル](#specifying-what-goes-into-the-shell)がレンダーされた直後に呼び出されるコールバック。ここで[ステータスコードのセット](#setting-the-status-code)を行い、`pipe` をコールしてストリーミングを開始できます。React はシェルを送信した後に、[追加コンテンツ](#streaming-more-content-as-it-loads)と、ローディングフォールバックを追加コンテンツで置換するためのインライン `<script>` タグをストリーミングします。\n  * **省略可能** `onShellError`: 初期シェルのレンダー中にエラーが発生すると呼び出されるコールバック。引数としてエラーを受け取ります。ストリームからのバイト列送信はまだ起きておらず、`onShellReady` や `onAllReady` はコールされなくなるため、[フォールバック用の HTML シェルを出力](#recovering-from-errors-inside-the-shell)することができます。\n  * **省略可能** `progressiveChunkSize`: チャンクのバイト数。[デフォルトの推論方法についてはこちらを参照してください](https://github.com/facebook/react/blob/14c2be8dac2d5482fda8a0906a31d239df8551fc/packages/react-server/src/ReactFizzServer.js#L210-L225)。\n\n\n#### 返り値 {/*returns*/}\n\n`renderToPipeableStream` は以下の 2 つのメソッドを含んだオブジェクトを返します。\n\n* `pipe`: HTML を [Node.js の Writable ストリーム](https://nodejs.org/api/stream.html#writable-streams)に出力します。`pipe` の呼び出しは、ストリーミングを有効にしたい場合は `onShellReady` で、クローラや静的生成向けの出力を行いたい場合は `onAllReady` で行ってください。\n* `abort`: [サーバでのレンダーを中止](#aborting-server-rendering)してクライアントで残りをレンダーするために使用します。\n\n---\n\n## 使用法 {/*usage*/}\n\n### React ツリーを HTML として Node.js ストリームにレンダーする {/*rendering-a-react-tree-as-html-to-a-nodejs-stream*/}\n\n`renderToPipeableStream` を呼び出して、React ツリーを HTML として [Node.js ストリーム](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) にレンダーします。\n\n```js [[1, 5, \"<App />\"], [2, 6, \"['/main.js']\"]]\nimport { renderToPipeableStream } from 'react-dom/server';\n\n// The route handler syntax depends on your backend framework\napp.use('/', (request, response) => {\n  const { pipe } = renderToPipeableStream(<App />, {\n    bootstrapScripts: ['/main.js'],\n    onShellReady() {\n      response.setHeader('content-type', 'text/html');\n      pipe(response);\n    }\n  });\n});\n```\n\n<CodeStep step={1}>ルートコンポーネント</CodeStep> と <CodeStep step={2}>ブートストラップ `<script>` パス</CodeStep>のリストを指定する必要があります。ルートコンポーネントは、**ルートの `<html>` タグを含んだドキュメント全体**を返すようにします。\n\n例えば以下のような形になるでしょう。\n\n```js [[1, 1, \"App\"]]\nexport default function App() {\n  return (\n    <html>\n      <head>\n        <meta charSet=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        <link rel=\"stylesheet\" href=\"/styles.css\"></link>\n        <title>My app</title>\n      </head>\n      <body>\n        <Router />\n      </body>\n    </html>\n  );\n}\n```\n\nReact は [doctype](https://developer.mozilla.org/en-US/docs/Glossary/Doctype) とあなたが指定した<CodeStep step={2}>ブートストラップ `<script>` タグ</CodeStep>を結果の HTML ストリームに注入します。\n\n```html [[2, 5, \"/main.js\"]]\n<!DOCTYPE html>\n<html>\n  <!-- ... HTML from your components ... -->\n</html>\n<script src=\"/main.js\" async=\"\"></script>\n```\n\nクライアント側では、ブートストラップスクリプトは [`hydrateRoot` を呼び出して `document` 全体のハイドレーションを行う](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document)必要があります。\n\n```js [[1, 4, \"<App />\"]]\nimport { hydrateRoot } from 'react-dom/client';\nimport App from './App.js';\n\nhydrateRoot(document, <App />);\n```\n\nこれにより、サーバで生成された HTML にイベントリスナが追加され、操作可能になります。\n\n<DeepDive>\n\n#### ビルド出力から CSS と JS のアセットパスを読み取る {/*reading-css-and-js-asset-paths-from-the-build-output*/}\n\nビルド後に、最終的なアセット URL（JavaScript や CSS ファイルなど）にはよくハッシュ化が行われます。例えば、`styles.css` が `styles.123456.css` になることがあります。静的なアセットのファイル名をハッシュ化することで、同じアセットがビルドごとに異なるファイル名になることが保証されます。これが有用なのは、ある特定の名前を持つファイルの内容が不変になり、静的なアセットの長期的なキャッシングを安全に行えるようになるためです。\n\nしかし、ビルド後までアセット URL が分からない場合、それらをソースコードに含めることができません。例えば、先ほどのように JSX に `\"/styles.css\"` をハードコーディングする方法は動作しません。ソースコードにそれらを含めないようにするため、ルートコンポーネントが、props 経由で渡されたマップから実際のファイル名を読み取るようにすることができます。\n\n```js {1,6}\nexport default function App({ assetMap }) {\n  return (\n    <html>\n      <head>\n        ...\n        <link rel=\"stylesheet\" href={assetMap['styles.css']}></link>\n        ...\n      </head>\n      ...\n    </html>\n  );\n}\n```\n\nサーバ上では、`<App assetMap={assetMap} />` のようにレンダーし、アセット URL を含む `assetMap` を渡します。\n\n```js {1-5,8,9}\n// You'd need to get this JSON from your build tooling, e.g. read it from the build output.\nconst assetMap = {\n  'styles.css': '/styles.123456.css',\n  'main.js': '/main.123456.js'\n};\n\napp.use('/', (request, response) => {\n  const { pipe } = renderToPipeableStream(<App assetMap={assetMap} />, {\n    bootstrapScripts: [assetMap['main.js']],\n    onShellReady() {\n      response.setHeader('content-type', 'text/html');\n      pipe(response);\n    }\n  });\n});\n```\n\nサーバで `<App assetMap={assetMap} />` のようにレンダーしているので、クライアントでも `assetMap` を使ってレンダーしてハイドレーションエラーを避ける必要があります。このためには以下のように `assetMap` をシリアライズしてクライアントに渡します。\n\n```js {9-10}\n// You'd need to get this JSON from your build tooling.\nconst assetMap = {\n  'styles.css': '/styles.123456.css',\n  'main.js': '/main.123456.js'\n};\n\napp.use('/', (request, response) => {\n  const { pipe } = renderToPipeableStream(<App assetMap={assetMap} />, {\n    // Careful: It's safe to stringify() this because this data isn't user-generated.\n    bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`,\n    bootstrapScripts: [assetMap['main.js']],\n    onShellReady() {\n      response.setHeader('content-type', 'text/html');\n      pipe(response);\n    }\n  });\n});\n```\n\n上記の例では、`bootstrapScriptContent` オプションを使って`<script>` タグを追加して、クライアント上でグローバル `window.assetMap` 変数をセットしています。これにより、クライアントのコードが同じ `assetMap` を読み取れるようになります。\n\n```js {4}\nimport { hydrateRoot } from 'react-dom/client';\nimport App from './App.js';\n\nhydrateRoot(document, <App assetMap={window.assetMap} />);\n```\n\nクライアントとサーバの両方が props として同じ `assetMap` を使って `App` をレンダーするため、ハイドレーションエラーは発生しません。\n\n</DeepDive>\n\n---\n\n### ロードが進むにつれてコンテンツをストリーミングする {/*streaming-more-content-as-it-loads*/}\n\nストリーミングにより、サーバ上ですべてのデータがロードされる前に、ユーザがコンテンツを見始められるようにすることができます。例えば以下のようなプロフィールページがあり、カバー、フレンド・写真が含まれたサイドバー、投稿のリストを表示しているところを考えましょう。\n\n```js\nfunction ProfilePage() {\n  return (\n    <ProfileLayout>\n      <ProfileCover />\n      <Sidebar>\n        <Friends />\n        <Photos />\n      </Sidebar>\n      <Posts />\n    </ProfileLayout>\n  );\n}\n```\n\nここで、`<Posts />` のデータを読み込むのに時間がかかるとしましょう。理想的には、投稿の読み込みを待つことなく、プロフィールページの残りのコンテンツをユーザに表示したいでしょう。これを実現するには、[`Posts` を `<Suspense>` バウンダリで囲みます](/reference/react/Suspense#displaying-a-fallback-while-content-is-loading)。\n\n```js {9,11}\nfunction ProfilePage() {\n  return (\n    <ProfileLayout>\n      <ProfileCover />\n      <Sidebar>\n        <Friends />\n        <Photos />\n      </Sidebar>\n      <Suspense fallback={<PostsGlimmer />}>\n        <Posts />\n      </Suspense>\n    </ProfileLayout>\n  );\n}\n```\n\nこれにより React に、`Posts` のデータが読み込まれる前に HTML をストリーミング開始するよう指示します。React はまず、ローディングフォールバック (`PostsGlimmer`) の HTML を送信します。次に `Posts` のデータ読み込みが完了したら、残りの HTML と、ローディングフォールバックをそれで置換するためのインライン `<script>` タグを送信します。ユーザから見ると、ページにはまず `PostsGlimmer` が表示され、後からそれが `Posts` に置き換わることになります。\n\nさらに、より細かく読み込みシーケンスを制御するために[`<Suspense>` バウンダリをネストさせることもできます](/reference/react/Suspense#revealing-nested-content-as-it-loads)。\n\n```js {5,13}\nfunction ProfilePage() {\n  return (\n    <ProfileLayout>\n      <ProfileCover />\n      <Suspense fallback={<BigSpinner />}>\n        <Sidebar>\n          <Friends />\n          <Photos />\n        </Sidebar>\n        <Suspense fallback={<PostsGlimmer />}>\n          <Posts />\n        </Suspense>\n      </Suspense>\n    </ProfileLayout>\n  );\n}\n```\n\nこの例では、React はページのストリーミングをさらに素早く開始できます。最初にレンダーが完了している必要があるのは、`<Suspense>` バウンダリで囲まれていない `ProfileLayout` と `ProfileCover` だけです。`Sidebar`、`Friends`、`Photos` がデータを読み込む必要がある場合、React は `BigSpinner` のフォールバック HTML を代わりに送信します。その後、より多くのデータが利用可能になるにつれ、より多くのコンテンツが表示されていき、最終的にすべてが表示されます。\n\nストリーミングでは、ブラウザで React 自体が読み込まれるのを待つ必要も、アプリが操作可能になるのを待つ必要もありません。サーバからの HTML コンテンツは、あらゆる `<script>` タグが読み込まれる前にプログレッシブに表示されます。\n\n[HTML ストリーミングの動作について詳しく読む](https://github.com/reactwg/react-18/discussions/37)\n\n<Note>\n\n**サスペンスコンポーネントをアクティブ化できるのはサスペンス対応のデータソースだけです**。これには以下が含まれます：\n\n- [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) や [Next.js](https://nextjs.org/docs/getting-started/react-essentials) のようなサスペンス対応のフレームワークでのデータフェッチ\n- [`lazy`](/reference/react/lazy) を用いたコンポーネントコードの遅延ロード\n- [`use`](/reference/react/use) を用いたプロミス (Promise) からの値の読み取り\n\nサスペンスはエフェクトやイベントハンドラ内でデータフェッチが行われた場合にはそれを**検出しません**。\n\n上記の `Posts` コンポーネントで実際にデータをロードする方法は、使用するフレームワークによって異なります。サスペンス対応のフレームワークを使用している場合、詳細はデータフェッチに関するドキュメンテーション内に記載されているはずです。\n\n使い方の規約のある (opinionated) フレームワークを使用せずにサスペンスを使ったデータフェッチを行うことは、まだサポートされていません。サスペンス対応のデータソースを実装するための要件はまだ不安定であり、ドキュメント化されていません。データソースをサスペンスと統合するための公式な API は、React の将来のバージョンでリリースされる予定です。\n\n</Note>\n\n---\n\n### シェルに何を含めるかの指定 {/*specifying-what-goes-into-the-shell*/}\n\nアプリの全 `<Suspense>` バウンダリより外にある部分のことを*シェル (shell)* と呼びます。\n\n```js {3-5,13,14}\nfunction ProfilePage() {\n  return (\n    <ProfileLayout>\n      <ProfileCover />\n      <Suspense fallback={<BigSpinner />}>\n        <Sidebar>\n          <Friends />\n          <Photos />\n        </Sidebar>\n        <Suspense fallback={<PostsGlimmer />}>\n          <Posts />\n        </Suspense>\n      </Suspense>\n    </ProfileLayout>\n  );\n}\n```\n\nこれが、ユーザに見える最初のローディング中状態を決定します。\n\n```js {3-5,13\n<ProfileLayout>\n  <ProfileCover />\n  <BigSpinner />\n</ProfileLayout>\n```\n\nもしルート部分でアプリ全体を `<Suspense>` バウンダリでラップしてしまうと、シェルとしてはそのスピナだけが含まれることになります。しかしこれはあまり快適なユーザ体験にはなりません。大きなスピナが画面に表示されることは、もう少しだけ待ってから実際のレイアウトを表示することよりも遅く不快に感じられるためです。したがって、`<Suspense>` 境界は適切に配置して、シェルが*ミニマルかつ完全*に感じられるように必要があるでしょう。例えばページレイアウト全体のスケルトンのようなものです。\n\nシェル全体のレンダーが完了したときに `onShellReady` コールバックが呼び出されます。通常、ここでストリーミングを開始します。\n\n```js {3-6}\nconst { pipe } = renderToPipeableStream(<App />, {\n  bootstrapScripts: ['/main.js'],\n  onShellReady() {\n    response.setHeader('content-type', 'text/html');\n    pipe(response);\n  }\n});\n```\n\n`onShellReady` が呼び出される時点では、ネストされた `<Suspense>` バウンダリ内のコンポーネントはまだデータをロード中かもしれません。\n\n---\n\n### サーバ上でのクラッシュログの記録 {/*logging-crashes-on-the-server*/}\n\nデフォルトでは、サーバ上のすべてのエラーはコンソールにログとして記録されます。この挙動をオーバーライドして、クラッシュレポートをログとして記録することができます。\n\n```js {7-10}\nconst { pipe } = renderToPipeableStream(<App />, {\n  bootstrapScripts: ['/main.js'],\n  onShellReady() {\n    response.setHeader('content-type', 'text/html');\n    pipe(response);\n  },\n  onError(error) {\n    console.error(error);\n    logServerCrashReport(error);\n  }\n});\n```\n\nカスタムの `onError` 実装を提供する場合、上記のようにエラーをコンソールにもログとして記録することを忘れないでください。\n\n---\n\n### シェル内のエラーからの復帰 {/*recovering-from-errors-inside-the-shell*/}\n\nこの例では、シェルとして `ProfileLayout`、`ProfileCover`、および `PostsGlimmer` が含まれています。\n\n```js {3-5,7-8}\nfunction ProfilePage() {\n  return (\n    <ProfileLayout>\n      <ProfileCover />\n      <Suspense fallback={<PostsGlimmer />}>\n        <Posts />\n      </Suspense>\n    </ProfileLayout>\n  );\n}\n```\n\nこれらのコンポーネントをレンダーする際にエラーが発生した場合、React はクライアントに送信できる意味のある HTML を提供できません。最終手段として、`onShellError` をオーバーライドして、サーバレンダリングに依存しないフォールバック HTML を送信しましょう。\n\n```js {7-11}\nconst { pipe } = renderToPipeableStream(<App />, {\n  bootstrapScripts: ['/main.js'],\n  onShellReady() {\n    response.setHeader('content-type', 'text/html');\n    pipe(response);\n  },\n  onShellError(error) {\n    response.statusCode = 500;\n    response.setHeader('content-type', 'text/html');\n    response.send('<h1>Something went wrong</h1>'); \n  },\n  onError(error) {\n    console.error(error);\n    logServerCrashReport(error);\n  }\n});\n```\n\nシェルの生成中にエラーが発生した場合、`onError` と `onShellError` の両方が発火します。エラーレポートには `onError` を使用し、フォールバックの HTML ドキュメントを送信するためには `onShellError` を使用します。フォールバック HTML はエラーページである必要はありません。代わりに、クライアントのみでアプリをレンダーするための代替シェルを含めることも可能です。\n\n---\n\n### シェル外のエラーからの復帰 {/*recovering-from-errors-outside-the-shell*/}\n\nこの例では、`<Posts />` コンポーネントは `<Suspense>` でラップされているため、シェルの一部では*ありません*。\n\n```js {6}\nfunction ProfilePage() {\n  return (\n    <ProfileLayout>\n      <ProfileCover />\n      <Suspense fallback={<PostsGlimmer />}>\n        <Posts />\n      </Suspense>\n    </ProfileLayout>\n  );\n}\n```\n\n`Posts` コンポーネントまたはその内部のどこかでエラーが発生した場合、React はそこからの[復帰を試みます](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content)。\n\n1. 最も近い `<Suspense>` バウンダリ (`PostsGlimmer`) のローディングフォールバックを HTML として出力します。\n2. サーバ上で `Posts` のコンテンツをレンダーしようとするのを諦めます。\n3. JavaScript コードがクライアント上でロードされると、React はクライアント上で `Posts` のレンダーを*再試行*します。\n\nクライアント上で `Posts` のレンダーを再試行して*再度*失敗した場合、React はクライアント上でエラーをスローします。レンダー中にスローされる他のすべてのエラーと同様に、[最も近い親のエラーバウンダリ](/reference/react/Component#static-getderivedstatefromerror)がユーザにエラーをどのように提示するかを決定します。つまり、エラーが復帰不能であることが確定するまで、ユーザにはローディングインジケータが見えることになります。\n\nクライアント上での `Posts` のレンダー再試行が成功した場合、サーバからのローディングフォールバックはクライアントでのレンダー出力で置き換えられます。ユーザにはサーバエラーが発生したことは分かりません。ただし、サーバの `onError` コールバックとクライアントの [`onRecoverableError`](/reference/react-dom/client/hydrateRoot#hydrateroot) コールバックが発火するため、エラーについて通知を受け取ることができます。\n\n---\n\n### ステータスコードの設定 {/*setting-the-status-code*/}\n\nストリーミングにはトレードオフも存在します。ユーザがコンテンツを早く見ることができるように、できるだけ早くページのストリーミングを開始したいでしょう。一方で、ストリーミングを開始すると、レスポンスのステータスコードを設定することができなくなります。\n\nシェル（すべての `<Suspense>` バウンダリより上の部分）とそれ以外のコンテンツに[アプリを分割する](#specifying-what-goes-into-the-shell)ことで、この問題はすでに部分的に解決されています。シェルでエラーが発生した場合、`onShellError` コールバックが呼び出され、エラーのステータスコードをセットすることができます。それ以外の場合は、アプリがクライアント上で復帰できる可能性があるため、\"OK\" を送信できるのです。\n\n```js {4}\nconst { pipe } = renderToPipeableStream(<App />, {\n  bootstrapScripts: ['/main.js'],\n  onShellReady() {\n    response.statusCode = 200;\n    response.setHeader('content-type', 'text/html');\n    pipe(response);\n  },\n  onShellError(error) {\n    response.statusCode = 500;\n    response.setHeader('content-type', 'text/html');\n    response.send('<h1>Something went wrong</h1>'); \n  },\n  onError(error) {\n    console.error(error);\n    logServerCrashReport(error);\n  }\n});\n```\n\nシェルの*外側*（つまり `<Suspense>` バウンダリの内側）のコンポーネントでエラーが発生した場合、React はレンダーを停止しません。これは、`onError` コールバックは発火するものの、その後 `onShellError` ではなく `onShellReady` が発火することを意味します。これは[上記で説明したように](#recovering-from-errors-outside-the-shell)、React がそのエラーをクライアント上で復帰しようとするからです。\n\nただしお望みであれば、何らかのエラーが起きたという事実に基づいたステータスコードを設定することもできます。\n\n```js {1,6,16}\nlet didError = false;\n\nconst { pipe } = renderToPipeableStream(<App />, {\n  bootstrapScripts: ['/main.js'],\n  onShellReady() {\n    response.statusCode = didError ? 500 : 200;\n    response.setHeader('content-type', 'text/html');\n    pipe(response);\n  },\n  onShellError(error) {\n    response.statusCode = 500;\n    response.setHeader('content-type', 'text/html');\n    response.send('<h1>Something went wrong</h1>'); \n  },\n  onError(error) {\n    didError = true;\n    console.error(error);\n    logServerCrashReport(error);\n  }\n});\n```\n\nこれは、初期のシェルコンテンツの生成中に既にシェルの外側で発生したエラーが捕捉できるだけなので、完全ではありません。あるコンテンツでエラーが発生したかどうかを知ることが重要であれば、それをシェルに移動させることができます。\n\n---\n\n### エラーの種類によって処理を分ける {/*handling-different-errors-in-different-ways*/}\n\nカスタムの `Error` サブクラスを[作成](https://javascript.info/custom-errors)し、[`instanceof`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) 演算子を使用してどんなエラーがスローされたかをチェックすることができます。例えば、カスタムの `NotFoundError` を定義し、コンポーネントからそれをスローすることができます。その後、`onError`、`onShellReady`, `onShellError` のコールバック中でエラーの種類に応じて異なる処理を行うことができます。\n\n```js {2,4-14,19,24,30}\nlet didError = false;\nlet caughtError = null;\n\nfunction getStatusCode() {\n  if (didError) {\n    if (caughtError instanceof NotFoundError) {\n      return 404;\n    } else {\n      return 500;\n    }\n  } else {\n    return 200;\n  }\n}\n\nconst { pipe } = renderToPipeableStream(<App />, {\n  bootstrapScripts: ['/main.js'],\n  onShellReady() {\n    response.statusCode = getStatusCode();\n    response.setHeader('content-type', 'text/html');\n    pipe(response);\n  },\n  onShellError(error) {\n   response.statusCode = getStatusCode();\n   response.setHeader('content-type', 'text/html');\n   response.send('<h1>Something went wrong</h1>'); \n  },\n  onError(error) {\n    didError = true;\n    caughtError = error;\n    console.error(error);\n    logServerCrashReport(error);\n  }\n});\n```\n\nしかしシェルを出力してストリーミングを開始してしまうと、ステータスコードを変更できなくなりますので注意してください。\n\n---\n\n### クローラや静的生成向けに全コンテンツの読み込みを待機する {/*waiting-for-all-content-to-load-for-crawlers-and-static-generation*/}\n\nストリーミングにより、利用可能になった順でコンテンツをユーザが見えるようになるため、ユーザ体験が向上します。\n\nしかし、クローラがページを訪れた場合や、ビルド時にページを生成している場合には、コンテンツを徐々に表示するのではなく、すべてのコンテンツを最初にロードしてから最終的な HTML 出力を生成したいでしょう。\n\n`onAllReady` コールバックを用いることで、すべてのコンテンツが読み込まれるまで待機を行うことができます。\n\n\n```js {2,7,11,18-24}\nlet didError = false;\nlet isCrawler = // ... depends on your bot detection strategy ...\n\nconst { pipe } = renderToPipeableStream(<App />, {\n  bootstrapScripts: ['/main.js'],\n  onShellReady() {\n    if (!isCrawler) {\n      response.statusCode = didError ? 500 : 200;\n      response.setHeader('content-type', 'text/html');\n      pipe(response);\n    }\n  },\n  onShellError(error) {\n    response.statusCode = 500;\n    response.setHeader('content-type', 'text/html');\n    response.send('<h1>Something went wrong</h1>'); \n  },\n  onAllReady() {\n    if (isCrawler) {\n      response.statusCode = didError ? 500 : 200;\n      response.setHeader('content-type', 'text/html');\n      pipe(response);      \n    }\n  },\n  onError(error) {\n    didError = true;\n    console.error(error);\n    logServerCrashReport(error);\n  }\n});\n```\n\n通常のユーザは、ストリームで読み込まれるコンテンツを段階的に受け取ります。クローラは、全データが読み込まれた後の最終的な HTML 出力を受け取ります。しかし、これはクローラが*すべての*データを待つ必要があることを意味し、その中には読み込みが遅いものやエラーが発生するものも含まれるかもしれません。アプリケーションによっては、クローラにもシェルを送信することを選択しても構いません。\n\n---\n\n### サーバレンダリングの中止 {/*aborting-server-rendering*/}\n\nタイムアウト後にサーバでのレンダーを「諦める」ように強制することが可能です。\n\n```js {1,5-7}\nconst { pipe, abort } = renderToPipeableStream(<App />, {\n  // ...\n});\n\nsetTimeout(() => {\n  abort();\n}, 10000);\n```\n\nReact は、残りのローディング中フォールバックを HTML として直ちに出力し、クライアント上で残りをレンダーしようと試みます。\n"
  },
  {
    "path": "src/content/reference/react-dom/server/renderToReadableStream.md",
    "content": "---\ntitle: renderToReadableStream\n---\n\n<Intro>\n\n`renderToReadableStream` は React ツリーを[読み取り可能な Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) にレンダーします。\n\n```js\nconst stream = await renderToReadableStream(reactNode, options?)\n```\n\n</Intro>\n\n<InlineToc />\n\n<Note>\n\nこの API は [Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) に依存しています。Node.js では、代わりに [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream) を使用してください。\n\n</Note>\n\n---\n\n## リファレンス {/*reference*/}\n\n### `renderToReadableStream(reactNode, options?)` {/*rendertoreadablestream*/}\n\n`renderToReadableStream` を呼び出して、React ツリーを HTML として[読み取り可能な Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) にレンダーします。\n\n```js\nimport { renderToReadableStream } from 'react-dom/server';\n\nasync function handler(request) {\n  const stream = await renderToReadableStream(<App />, {\n    bootstrapScripts: ['/main.js']\n  });\n  return new Response(stream, {\n    headers: { 'content-type': 'text/html' },\n  });\n}\n```\n\nクライアント側では、このようにサーバ生成された HTML を操作可能にするために [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を用います。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `reactNode`: HTML へとレンダーしたい React ノード。例えば、`<App />` のような JSX 要素です。これはドキュメント全体を表すことが期待されているため、`App` コンポーネントは `<html>` タグをレンダーする必要があります。\n\n* **省略可能** `options`: ストリーム関連のオプションが含まれたオブジェクト。\n  * **省略可能** `bootstrapScriptContent`: 指定された場合、この文字列がインラインの `<script>` タグ内に配置されます。\n  * **省略可能** `bootstrapScripts`: ページ上に出力する `<script>` タグに対応する URL 文字列の配列。これを使用して、[`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を呼び出す `<script>` を含めます。クライアントで React をまったく実行したくない場合は省略します。\n  * **省略可能** `bootstrapModules`: `bootstrapScripts` と同様ですが、代わりに [`<script type=\"module\">`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) を出力します。\n  * **省略可能** `identifierPrefix`: React が [`useId`](/reference/react/useId) によって生成する ID に使用する文字列プレフィックス。同じページ上に複数のルートを使用する際に、競合を避けるために用います。[`hydrateRoot`](/reference/react-dom/client/hydrateRoot#parameters) にも同じプレフィックスを渡す必要があります。\n  * **省略可能** `namespaceURI`: このストリームのルート[ネームスペース URI](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#important_namespace_uris) 文字列。デフォルトでは通常の HTML です。SVG の場合は `'http://www.w3.org/2000/svg'`、MathML の場合は `'http://www.w3.org/1998/Math/MathML'` を渡します。\n  * **省略可能** `nonce`: [`script-src` Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src) を用いてスクリプトを許可するための [`nonce`](http://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#nonce) 文字列。\n  * **省略可能** `onError`: サーバエラーが発生するたびに発火するコールバック。[復帰可能なエラー](#recovering-from-errors-outside-the-shell)の場合も[そうでないエラー](#recovering-from-errors-inside-the-shell)の場合もあります。デフォルトでは `console.error` のみを呼び出します。これを上書きして[クラッシュレポートをログに記録する](#logging-crashes-on-the-server)場合でも `console.error` を呼び出すようにしてください。また、シェルが出力される前に[ステータスコードを調整する](#setting-the-status-code)ためにも使用できます。\n  * **省略可能** `progressiveChunkSize`: チャンクのバイト数。[デフォルトの推論方法についてはこちらを参照してください](https://github.com/facebook/react/blob/14c2be8dac2d5482fda8a0906a31d239df8551fc/packages/react-server/src/ReactFizzServer.js#L210-L225)。\n  * **省略可能** `signal`: [サーバでのレンダーを中止](#aborting-server-rendering)してクライアントで残りをレンダーするために使用できる [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)。\n\n\n#### 返り値 {/*returns*/}\n\n`renderToReadableStream` は Promise を返します。\n\n- [シェル](#specifying-what-goes-into-the-shell)のレンダーが成功した場合、Promise は[読み取り可能な Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) に解決 (resolve) されます。\n- シェルのレンダーが失敗した場合、Promise は拒否 (reject) されます。[これを使用してフォールバックシェルを出力します](#recovering-from-errors-inside-the-shell)。\n\n返されるストリームには以下の追加のプロパティが存在します。\n\n* `allReady`: [シェル](#specifying-what-goes-into-the-shell)とすべての追加[コンテンツ](#streaming-more-content-as-it-loads)の両方を含むすべてのレンダーが完了したときに解決される Promise。[クローラや静的生成向け](#waiting-for-all-content-to-load-for-crawlers-and-static-generation)の場合、レスポンスを返す前に `stream.allReady` を await できます。これを行うとプログレッシブなローディングがなくなり、ストリームには最終的な HTML が含まれるようになります。\n\n---\n\n## 使用法 {/*usage*/}\n\n### React ツリーを HTML として読み取り可能な Web Stream にレンダーする {/*rendering-a-react-tree-as-html-to-a-readable-web-stream*/}\n\n`renderToReadableStream` を呼び出して、React ツリーを HTML として[読み取り可能な Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) にレンダーします。\n\n```js [[1, 4, \"<App />\"], [2, 5, \"['/main.js']\"]]\nimport { renderToReadableStream } from 'react-dom/server';\n\nasync function handler(request) {\n  const stream = await renderToReadableStream(<App />, {\n    bootstrapScripts: ['/main.js']\n  });\n  return new Response(stream, {\n    headers: { 'content-type': 'text/html' },\n  });\n}\n```\n\n<CodeStep step={1}>ルートコンポーネント</CodeStep> と <CodeStep step={2}>ブートストラップ `<script>` パス</CodeStep>のリストを指定する必要があります。ルートコンポーネントは、**ルートの `<html>` タグを含んだドキュメント全体**を返すようにします。\n\n例えば以下のような形になるでしょう。\n\n```js [[1, 1, \"App\"]]\nexport default function App() {\n  return (\n    <html>\n      <head>\n        <meta charSet=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        <link rel=\"stylesheet\" href=\"/styles.css\"></link>\n        <title>My app</title>\n      </head>\n      <body>\n        <Router />\n      </body>\n    </html>\n  );\n}\n```\n\nReact は [doctype](https://developer.mozilla.org/en-US/docs/Glossary/Doctype) とあなたが指定した<CodeStep step={2}>ブートストラップ `<script>` タグ</CodeStep>を結果の HTML ストリームに注入します。\n\n```html [[2, 5, \"/main.js\"]]\n<!DOCTYPE html>\n<html>\n  <!-- ... HTML from your components ... -->\n</html>\n<script src=\"/main.js\" async=\"\"></script>\n```\n\nクライアント側では、ブートストラップスクリプトは [`hydrateRoot` を呼び出して `document` 全体のハイドレーションを行う](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document)必要があります。\n\n```js [[1, 4, \"<App />\"]]\nimport { hydrateRoot } from 'react-dom/client';\nimport App from './App.js';\n\nhydrateRoot(document, <App />);\n```\n\nこれにより、サーバで生成された HTML にイベントリスナが追加され、操作可能になります。\n\n<DeepDive>\n\n#### ビルド出力から CSS と JS のアセットパスを読み取る {/*reading-css-and-js-asset-paths-from-the-build-output*/}\n\nビルド後に、最終的なアセット URL（JavaScript や CSS ファイルなど）にはよくハッシュ化が行われます。例えば、`styles.css` が `styles.123456.css` になることがあります。静的なアセットのファイル名をハッシュ化することで、同じアセットがビルドごとに異なるファイル名になることが保証されます。これが有用なのは、ある特定の名前を持つファイルの内容が不変になり、静的なアセットの長期的なキャッシングを安全に行えるようになるためです。\n\nしかし、ビルド後までアセット URL が分からない場合、それらをソースコードに含めることができません。例えば、先ほどのように JSX に `\"/styles.css\"` をハードコーディングする方法は動作しません。ソースコードにそれらを含めないようにするため、ルートコンポーネントが、props 経由で渡されたマップから実際のファイル名を読み取るようにすることができます。\n\n```js {1,6}\nexport default function App({ assetMap }) {\n  return (\n    <html>\n      <head>\n        <title>My app</title>\n        <link rel=\"stylesheet\" href={assetMap['styles.css']}></link>\n      </head>\n      ...\n    </html>\n  );\n}\n```\n\nサーバ上では、`<App assetMap={assetMap} />` のようにレンダーし、アセット URL を含む `assetMap` を渡します。\n\n```js {1-5,8,9}\n// You'd need to get this JSON from your build tooling, e.g. read it from the build output.\nconst assetMap = {\n  'styles.css': '/styles.123456.css',\n  'main.js': '/main.123456.js'\n};\n\nasync function handler(request) {\n  const stream = await renderToReadableStream(<App assetMap={assetMap} />, {\n    bootstrapScripts: [assetMap['/main.js']]\n  });\n  return new Response(stream, {\n    headers: { 'content-type': 'text/html' },\n  });\n}\n```\n\nサーバで `<App assetMap={assetMap} />` のようにレンダーしているので、クライアントでも `assetMap` を使ってレンダーしてハイドレーションエラーを避ける必要があります。このためには以下のように `assetMap` をシリアライズしてクライアントに渡します。\n\n```js {9-10}\n// You'd need to get this JSON from your build tooling.\nconst assetMap = {\n  'styles.css': '/styles.123456.css',\n  'main.js': '/main.123456.js'\n};\n\nasync function handler(request) {\n  const stream = await renderToReadableStream(<App assetMap={assetMap} />, {\n    // Careful: It's safe to stringify() this because this data isn't user-generated.\n    bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`,\n    bootstrapScripts: [assetMap['/main.js']],\n  });\n  return new Response(stream, {\n    headers: { 'content-type': 'text/html' },\n  });\n}\n```\n\n上記の例では、`bootstrapScriptContent` オプションを使って`<script>` タグを追加して、クライアント上でグローバル `window.assetMap` 変数をセットしています。これにより、クライアントのコードが同じ `assetMap` を読み取れるようになります。\n\n```js {4}\nimport { hydrateRoot } from 'react-dom/client';\nimport App from './App.js';\n\nhydrateRoot(document, <App assetMap={window.assetMap} />);\n```\n\nクライアントとサーバの両方が props として同じ `assetMap` を使って `App` をレンダーするため、ハイドレーションエラーは発生しません。\n\n</DeepDive>\n\n---\n\n### ロードが進むにつれてコンテンツをストリーミングする {/*streaming-more-content-as-it-loads*/}\n\nストリーミングにより、サーバ上ですべてのデータがロードされる前に、ユーザがコンテンツを見始められるようにすることができます。例えば以下のようなプロフィールページがあり、カバー、フレンド・写真が含まれたサイドバー、投稿のリストを表示しているところを考えましょう。\n\n```js\nfunction ProfilePage() {\n  return (\n    <ProfileLayout>\n      <ProfileCover />\n      <Sidebar>\n        <Friends />\n        <Photos />\n      </Sidebar>\n      <Posts />\n    </ProfileLayout>\n  );\n}\n```\n\nここで、`<Posts />` のデータを読み込むのに時間がかかるとしましょう。理想的には、投稿の読み込みを待つことなく、プロフィールページの残りのコンテンツをユーザに表示したいでしょう。これを実現するには、[`Posts` を `<Suspense>` バウンダリで囲みます](/reference/react/Suspense#displaying-a-fallback-while-content-is-loading)。\n\n```js {9,11}\nfunction ProfilePage() {\n  return (\n    <ProfileLayout>\n      <ProfileCover />\n      <Sidebar>\n        <Friends />\n        <Photos />\n      </Sidebar>\n      <Suspense fallback={<PostsGlimmer />}>\n        <Posts />\n      </Suspense>\n    </ProfileLayout>\n  );\n}\n```\n\nこれにより React に、`Posts` のデータが読み込まれる前に HTML をストリーミング開始するよう指示します。React はまず、ローディングフォールバック (`PostsGlimmer`) の HTML を送信します。次に `Posts` のデータ読み込みが完了したら、残りの HTML と、ローディングフォールバックをそれで置換するためのインライン `<script>` タグを送信します。ユーザから見ると、ページにはまず `PostsGlimmer` が表示され、後からそれが `Posts` に置き換わることになります。\n\nさらに、より細かく読み込みシーケンスを制御するために[`<Suspense>` バウンダリをネストさせることもできます](/reference/react/Suspense#revealing-nested-content-as-it-loads)。\n\n```js {5,13}\nfunction ProfilePage() {\n  return (\n    <ProfileLayout>\n      <ProfileCover />\n      <Suspense fallback={<BigSpinner />}>\n        <Sidebar>\n          <Friends />\n          <Photos />\n        </Sidebar>\n        <Suspense fallback={<PostsGlimmer />}>\n          <Posts />\n        </Suspense>\n      </Suspense>\n    </ProfileLayout>\n  );\n}\n```\n\n\nこの例では、React はページのストリーミングをさらに素早く開始できます。最初にレンダーが完了している必要があるのは、`<Suspense>` バウンダリで囲まれていない `ProfileLayout` と `ProfileCover` だけです。`Sidebar`、`Friends`、`Photos` がデータを読み込む必要がある場合、React は `BigSpinner` のフォールバック HTML を代わりに送信します。その後、より多くのデータが利用可能になるにつれ、より多くのコンテンツが表示されていき、最終的にすべてが表示されます。\n\nストリーミングでは、ブラウザで React 自体が読み込まれるのを待つ必要も、アプリが操作可能になるのを待つ必要もありません。サーバからの HTML コンテンツは、あらゆる `<script>` タグが読み込まれる前にプログレッシブに表示されます。\n\n[HTML ストリーミングの動作について詳しく読む](https://github.com/reactwg/react-18/discussions/37)\n\n<Note>\n\n**サスペンスコンポーネントをアクティブ化できるのはサスペンス対応のデータソースだけです**。これには以下が含まれます：\n\n- [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) や [Next.js](https://nextjs.org/docs/getting-started/react-essentials) のようなサスペンス対応のフレームワークでのデータフェッチ\n- [`lazy`](/reference/react/lazy) を用いたコンポーネントコードの遅延ロード\n- [`use`](/reference/react/use) を用いたプロミス (Promise) からの値の読み取り\n\nサスペンスはエフェクトやイベントハンドラ内でデータフェッチが行われた場合にはそれを**検出しません**。\n\n上記の `Posts` コンポーネントで実際にデータをロードする方法は、使用するフレームワークによって異なります。サスペンス対応のフレームワークを使用している場合、詳細はデータフェッチに関するドキュメンテーション内に記載されているはずです。\n\n使い方の規約のある (opinionated) フレームワークを使用せずにサスペンスを使ったデータフェッチを行うことは、まだサポートされていません。サスペンス対応のデータソースを実装するための要件はまだ不安定であり、ドキュメント化されていません。データソースをサスペンスと統合するための公式な API は、React の将来のバージョンでリリースされる予定です。\n\n</Note>\n\n---\n\n### シェルに何を含めるかの指定 {/*specifying-what-goes-into-the-shell*/}\n\nアプリの全 `<Suspense>` バウンダリより外にある部分のことを*シェル (shell)* と呼びます。\n\n```js {3-5,13,14}\nfunction ProfilePage() {\n  return (\n    <ProfileLayout>\n      <ProfileCover />\n      <Suspense fallback={<BigSpinner />}>\n        <Sidebar>\n          <Friends />\n          <Photos />\n        </Sidebar>\n        <Suspense fallback={<PostsGlimmer />}>\n          <Posts />\n        </Suspense>\n      </Suspense>\n    </ProfileLayout>\n  );\n}\n```\n\nこれが、ユーザに見える最初のローディング中状態を決定します。\n\n```js {3-5,13\n<ProfileLayout>\n  <ProfileCover />\n  <BigSpinner />\n</ProfileLayout>\n```\n\nもしルート部分でアプリ全体を `<Suspense>` バウンダリでラップしてしまうと、シェルとしてはそのスピナだけが含まれることになります。しかしこれはあまり快適なユーザ体験にはなりません。大きなスピナが画面に表示されることは、もう少しだけ待ってから実際のレイアウトを表示することよりも遅く不快に感じられるためです。したがって、`<Suspense>` 境界は適切に配置して、シェルが*ミニマルかつ完全*に感じられるように必要があるでしょう。例えばページレイアウト全体のスケルトンのようなものです。\n\n非同期の `renderToReadableStream` 呼び出しが `stream` に解決されるのは、シェル全体のレンダーが終了した直後です。通常、その `stream` を使ってレスポンスを作成して返すことで、ストリーミングを開始します。\n\n```js {5}\nasync function handler(request) {\n  const stream = await renderToReadableStream(<App />, {\n    bootstrapScripts: ['/main.js']\n  });\n  return new Response(stream, {\n    headers: { 'content-type': 'text/html' },\n  });\n}\n```\n\nこの `stream` が返される時点では、ネストされた `<Suspense>` バウンダリ内のコンポーネントはまだデータをロード中かもしれません。\n\n---\n\n### サーバ上でのクラッシュログの記録 {/*logging-crashes-on-the-server*/}\n\nデフォルトでは、サーバ上のすべてのエラーはコンソールにログとして記録されます。この挙動をオーバーライドして、クラッシュレポートをログとして記録することができます。\n\n```js {4-7}\nasync function handler(request) {\n  const stream = await renderToReadableStream(<App />, {\n    bootstrapScripts: ['/main.js'],\n    onError(error) {\n      console.error(error);\n      logServerCrashReport(error);\n    }\n  });\n  return new Response(stream, {\n    headers: { 'content-type': 'text/html' },\n  });\n}\n```\n\nカスタムの `onError` 実装を提供する場合、上記のようにエラーをコンソールにもログとして記録することを忘れないでください。\n\n---\n\n### シェル内のエラーからの復帰 {/*recovering-from-errors-inside-the-shell*/}\n\nこの例では、シェルとして `ProfileLayout`、`ProfileCover`、および `PostsGlimmer` が含まれています。\n\n```js {3-5,7-8}\nfunction ProfilePage() {\n  return (\n    <ProfileLayout>\n      <ProfileCover />\n      <Suspense fallback={<PostsGlimmer />}>\n        <Posts />\n      </Suspense>\n    </ProfileLayout>\n  );\n}\n```\n\nこれらのコンポーネントをレンダーする際にエラーが発生した場合、React はクライアントに送信できる意味のある HTML を提供できません。最終手段として、`renderToReadableStream` 呼び出しを `try...catch` でラップして、サーバレンダリングに依存しないフォールバック HTML を送信しましょう。\n\n```js {2,13-18}\nasync function handler(request) {\n  try {\n    const stream = await renderToReadableStream(<App />, {\n      bootstrapScripts: ['/main.js'],\n      onError(error) {\n        console.error(error);\n        logServerCrashReport(error);\n      }\n    });\n    return new Response(stream, {\n      headers: { 'content-type': 'text/html' },\n    });\n  } catch (error) {\n    return new Response('<h1>Something went wrong</h1>', {\n      status: 500,\n      headers: { 'content-type': 'text/html' },\n    });\n  }\n}\n```\n\nシェルの生成中にエラーが発生した場合、`onError` と `catch` ブロックの両方が発火します。エラーレポートには `onError` を使用し、フォールバックの HTML ドキュメントを送信するためには `catch` ブロックを使用します。フォールバック HTML はエラーページである必要はありません。代わりに、クライアントのみでアプリをレンダーするための代替シェルを含めることも可能です。\n\n---\n\n### シェル外のエラーからの復帰 {/*recovering-from-errors-outside-the-shell*/}\n\nこの例では、`<Posts />` コンポーネントは `<Suspense>` でラップされているため、シェルの一部では*ありません*。\n\n```js {6}\nfunction ProfilePage() {\n  return (\n    <ProfileLayout>\n      <ProfileCover />\n      <Suspense fallback={<PostsGlimmer />}>\n        <Posts />\n      </Suspense>\n    </ProfileLayout>\n  );\n}\n```\n\n`Posts` コンポーネントまたはその内部のどこかでエラーが発生した場合、React はそこからの[復帰を試みます](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content)。\n\n1. 最も近い `<Suspense>` バウンダリ (`PostsGlimmer`) のローディングフォールバックを HTML として出力します。\n2. サーバ上で `Posts` のコンテンツをレンダーしようとするのを諦めます。\n3. JavaScript コードがクライアント上でロードされると、React はクライアント上で `Posts` のレンダーを*再試行*します。\n\nクライアント上で `Posts` のレンダーを再試行して*再度*失敗した場合、React はクライアント上でエラーをスローします。レンダー中にスローされる他のすべてのエラーと同様に、[最も近い親のエラーバウンダリ](/reference/react/Component#static-getderivedstatefromerror)がユーザにエラーをどのように提示するかを決定します。つまり、エラーが復帰不能であることが確定するまで、ユーザにはローディングインジケータが見えることになります。\n\nクライアント上での `Posts` のレンダー再試行が成功した場合、サーバからのローディングフォールバックはクライアントでのレンダー出力で置き換えられます。ユーザにはサーバエラーが発生したことは分かりません。ただし、サーバの `onError` コールバックとクライアントの [`onRecoverableError`](/reference/react-dom/client/hydrateRoot#hydrateroot) コールバックが発火するため、エラーについて通知を受け取ることができます。\n\n---\n\n### ステータスコードの設定 {/*setting-the-status-code*/}\n\nストリーミングにはトレードオフも存在します。ユーザがコンテンツを早く見ることができるように、できるだけ早くページのストリーミングを開始したいでしょう。一方で、ストリーミングを開始すると、レスポンスのステータスコードを設定することができなくなります。\n\nシェル（すべての `<Suspense>` バウンダリより上の部分）とそれ以外のコンテンツに[アプリを分割する](#specifying-what-goes-into-the-shell)ことで、この問題はすでに部分的に解決されています。シェルでエラーが発生した場合、`catch` ブロックが実行され、エラーのステータスコードをセットすることができます。それ以外の場合は、アプリがクライアント上で復帰できる可能性があるため、\"OK\" を送信できるのです。\n\n```js {11}\nasync function handler(request) {\n  try {\n    const stream = await renderToReadableStream(<App />, {\n      bootstrapScripts: ['/main.js'],\n      onError(error) {\n        console.error(error);\n        logServerCrashReport(error);\n      }\n    });\n    return new Response(stream, {\n      status: 200,\n      headers: { 'content-type': 'text/html' },\n    });\n  } catch (error) {\n    return new Response('<h1>Something went wrong</h1>', {\n      status: 500,\n      headers: { 'content-type': 'text/html' },\n    });\n  }\n}\n```\n\nシェルの*外側*（つまり `<Suspense>` バウンダリの内側）のコンポーネントでエラーが発生した場合、React はレンダーを停止しません。これは、`onError` コールバックが発火するものの、コードは `catch` ブロックに入らずに実行を続けることを意味します。これは[上記で説明したように](#recovering-from-errors-outside-the-shell)、React がそのエラーをクライアント上で復帰しようとするからです。\n\nただしお望みであれば、何らかのエラーが起きたという事実に基づいたステータスコードを設定することもできます。\n\n```js {3,7,13}\nasync function handler(request) {\n  try {\n    let didError = false;\n    const stream = await renderToReadableStream(<App />, {\n      bootstrapScripts: ['/main.js'],\n      onError(error) {\n        didError = true;\n        console.error(error);\n        logServerCrashReport(error);\n      }\n    });\n    return new Response(stream, {\n      status: didError ? 500 : 200,\n      headers: { 'content-type': 'text/html' },\n    });\n  } catch (error) {\n    return new Response('<h1>Something went wrong</h1>', {\n      status: 500,\n      headers: { 'content-type': 'text/html' },\n    });\n  }\n}\n```\n\nこれは、初期のシェルコンテンツの生成中に既にシェルの外側で発生したエラーが捕捉できるだけなので、完全ではありません。あるコンテンツでエラーが発生したかどうかを知ることが重要であれば、それをシェルに移動させることができます。\n\n---\n\n### エラーの種類によって処理を分ける {/*handling-different-errors-in-different-ways*/}\n\nカスタムの `Error` サブクラスを[作成](https://javascript.info/custom-errors)し、[`instanceof`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) 演算子を使用してどんなエラーがスローされたかをチェックすることができます。例えば、カスタムの `NotFoundError` を定義し、コンポーネントからそれをスローすることができます。その後、`onError` でエラーを保存しておき、エラーの種類に応じてレスポンスを返す前に何か異なる処理を行うことができます。\n\n```js {2-3,5-15,22,28,33}\nasync function handler(request) {\n  let didError = false;\n  let caughtError = null;\n\n  function getStatusCode() {\n    if (didError) {\n      if (caughtError instanceof NotFoundError) {\n        return 404;\n      } else {\n        return 500;\n      }\n    } else {\n      return 200;\n    }\n  }\n\n  try {\n    const stream = await renderToReadableStream(<App />, {\n      bootstrapScripts: ['/main.js'],\n      onError(error) {\n        didError = true;\n        caughtError = error;\n        console.error(error);\n        logServerCrashReport(error);\n      }\n    });\n    return new Response(stream, {\n      status: getStatusCode(),\n      headers: { 'content-type': 'text/html' },\n    });\n  } catch (error) {\n    return new Response('<h1>Something went wrong</h1>', {\n      status: getStatusCode(),\n      headers: { 'content-type': 'text/html' },\n    });\n  }\n}\n```\n\nしかしシェルを出力してストリーミングを開始してしまうと、ステータスコードを変更できなくなりますので注意してください。\n\n---\n\n### クローラや静的生成向けに全コンテンツの読み込みを待機する {/*waiting-for-all-content-to-load-for-crawlers-and-static-generation*/}\n\nストリーミングにより、利用可能になった順でコンテンツをユーザが見えるようになるため、ユーザ体験が向上します。\n\nしかし、クローラがページを訪れた場合や、ビルド時にページを生成している場合には、コンテンツを徐々に表示するのではなく、すべてのコンテンツを最初にロードしてから最終的な HTML 出力を生成したいでしょう。\n\nPromise である `stream.allReady` を await することで、すべてのコンテンツが読み込まれるまで待機を行うことができます。\n\n```js {12-15}\nasync function handler(request) {\n  try {\n    let didError = false;\n    const stream = await renderToReadableStream(<App />, {\n      bootstrapScripts: ['/main.js'],\n      onError(error) {\n        didError = true;\n        console.error(error);\n        logServerCrashReport(error);\n      }\n    });\n    let isCrawler = // ... depends on your bot detection strategy ...\n    if (isCrawler) {\n      await stream.allReady;\n    }\n    return new Response(stream, {\n      status: didError ? 500 : 200,\n      headers: { 'content-type': 'text/html' },\n    });\n  } catch (error) {\n    return new Response('<h1>Something went wrong</h1>', {\n      status: 500,\n      headers: { 'content-type': 'text/html' },\n    });\n  }\n}\n```\n\n通常のユーザは、ストリームで読み込まれるコンテンツを段階的に受け取ります。クローラは、全データが読み込まれた後の最終的な HTML 出力を受け取ります。しかし、これはクローラが*すべての*データを待つ必要があることを意味し、その中には読み込みが遅いものやエラーが発生するものも含まれるかもしれません。アプリケーションによっては、クローラにもシェルを送信することを選択しても構いません。\n\n---\n\n### サーバレンダリングの中止 {/*aborting-server-rendering*/}\n\nタイムアウト後にサーバレンダリングを「諦める」ように強制することができます。\n\n```js {3,4-6,9}\nasync function handler(request) {\n  try {\n    const controller = new AbortController();\n    setTimeout(() => {\n      controller.abort();\n    }, 10000);\n\n    const stream = await renderToReadableStream(<App />, {\n      signal: controller.signal,\n      bootstrapScripts: ['/main.js'],\n      onError(error) {\n        didError = true;\n        console.error(error);\n        logServerCrashReport(error);\n      }\n    });\n    // ...\n```\n\nReact は、残りのローディング中フォールバックを HTML として直ちに出力し、クライアント上で残りをレンダーしようと試みます。\n"
  },
  {
    "path": "src/content/reference/react-dom/server/renderToStaticMarkup.md",
    "content": "---\ntitle: renderToStaticMarkup\n---\n\n<Intro>\n\n`renderToStaticMarkup` は、非インタラクティブな React ツリーを HTML 文字列にレンダーします。\n\n```js\nconst html = renderToStaticMarkup(reactNode, options?)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `renderToStaticMarkup(reactNode, options?)` {/*rendertostaticmarkup*/}\n\nサーバ上において、`renderToStaticMarkup` を呼び出してアプリを HTML にレンダーします。\n\n```js\nimport { renderToStaticMarkup } from 'react-dom/server';\n\nconst html = renderToStaticMarkup(<Page />);\n```\n\nこれにより、React コンポーネントの非インタラクティブな HTML 出力が生成されます。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `reactNode`: HTML にレンダーしたい React ノード。例えば、`<Page />` のような JSX ノード。\n* **省略可能** `options`: サーバレンダー用のオプションが含まれたオブジェクト。\n  * **省略可能** `identifierPrefix`: React が [`useId`](/reference/react/useId) によって生成する ID に使用する文字列プレフィックス。同じページ上に複数のルートを使用する際に、競合を避けるために用います。\n\n#### 返り値 {/*returns*/}\n\nHTML 文字列。\n\n#### 注意点 {/*caveats*/}\n\n* `renderToStaticMarkup` の出力に対してハイドレーションは行えません。\n\n* `renderToStaticMarkup` のサスペンスに対するサポートは限定的です。コンポーネントがサスペンドすると、`renderToStaticMarkup` はそのフォールバックを HTML として直ちに出力します。\n\n* `renderToStaticMarkup` はブラウザで動作しますが、クライアントコードでの使用は推奨されません。ブラウザでコンポーネントを HTML にレンダーする必要がある場合は、[DOM ノードにレンダーしてその HTML を取得してください](/reference/react-dom/server/renderToString#removing-rendertostring-from-the-client-code)。\n\n---\n\n## 使用法 {/*usage*/}\n\n### 非インタラクティブな React ツリーを HTML として文字列にレンダーする {/*rendering-a-non-interactive-react-tree-as-html-to-a-string*/}\n\n`renderToStaticMarkup` を呼び出して、あなたのアプリを、サーバからのレスポンスとして送信できる HTML 文字列にレンダーします。\n\n```js {5-6}\nimport { renderToStaticMarkup } from 'react-dom/server';\n\n// The route handler syntax depends on your backend framework\napp.use('/', (request, response) => {\n  const html = renderToStaticMarkup(<Page />);\n  response.send(html);\n});\n```\n\nこれにより、React コンポーネントの非インタラクティブな初期 HTML 出力が生成されます。\n\n<Pitfall>\n\nこのメソッドは、**ハイドレーションができない非インタラクティブな HTML をレンダーします**。これは、React をシンプルな静的ページジェネレータとして使用したい場合や、メールのような完全に静的なコンテンツをレンダーする場合に有用です。\n\nインタラクティブなアプリでは、サーバ上で [`renderToString`](/reference/react-dom/server/renderToString) を、クライアント上で [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を使用すべきです。\n\n</Pitfall>\n"
  },
  {
    "path": "src/content/reference/react-dom/server/renderToString.md",
    "content": "---\ntitle: renderToString\n---\n\n<Pitfall>\n\n`renderToString` はストリーミングやデータ待機をサポートしていません。[代替手段を見る](#alternatives)。\n\n</Pitfall>\n\n<Intro>\n\n`renderToString` は React ツリーを HTML 文字列にレンダーします。\n\n```js\nconst html = renderToString(reactNode, options?)\n```\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `renderToString(reactNode, options?)` {/*rendertostring*/}\n\nサーバ上において、`renderToString` を呼び出してあなたのアプリを HTML にレンダーします。\n\n```js\nimport { renderToString } from 'react-dom/server';\n\nconst html = renderToString(<App />);\n```\n\nクライアント側では、このようにサーバ生成された HTML を操作可能にするために [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を用います。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `reactNode`: HTML にレンダーしたい React ノード。例えば、`<App />` のような JSX ノード。\n\n* **省略可能** `options`: サーバレンダー用のオプションが含まれたオブジェクト。\n  * **省略可能** `identifierPrefix`: React が [`useId`](/reference/react/useId) によって生成する ID に使用する文字列プレフィックス。同じページ上に複数のルートを使用する際に、競合を避けるために用います。[`hydrateRoot`](/reference/react-dom/client/hydrateRoot#parameters) にも同じプレフィックスを渡す必要があります。\n\n#### 返り値 {/*returns*/}\n\nHTML 文字列。\n\n#### 注意点 {/*caveats*/}\n\n* `renderToString` のサスペンスに対するサポートは限定的です。コンポーネントがサスペンドすると、`renderToString` はそのフォールバックを HTML として直ちに送信します。\n\n* `renderToString` はブラウザでも動作しますが、クライアントコードでの使用は[推奨されません](#removing-rendertostring-from-the-client-code)。\n\n---\n\n## 使用法 {/*usage*/}\n\n### React ツリーを HTML として文字列にレンダーする {/*rendering-a-react-tree-as-html-to-a-string*/}\n\n`renderToString` を呼び出して、あなたのアプリを、サーバからのレスポンスとして送信できる HTML 文字列にレンダーします。\n\n```js {5-6}\nimport { renderToString } from 'react-dom/server';\n\n// The route handler syntax depends on your backend framework\napp.use('/', (request, response) => {\n  const html = renderToString(<App />);\n  response.send(html);\n});\n```\n\nこれにより、React コンポーネントの初期の非インタラクティブな HTML 出力が生成されます。クライアント側では、サーバが生成した HTML の*ハイドレーション*を行い操作可能にするために、[`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を呼び出す必要があります。\n\n\n<Pitfall>\n\n`renderToString` はストリーミングやデータ待機をサポートしていません。[代替手段を見る](#alternatives)。\n\n</Pitfall>\n\n---\n\n## 代替手段 {/*alternatives*/}\n\n### サーバ上で `renderToString` からストリーム対応レンダーへの移行 {/*migrating-from-rendertostring-to-a-streaming-method-on-the-server*/}\n\n`renderToString` は直ちに文字列を返すため、コンテンツをロードしながらのストリーミングをサポートしません。\n\n可能な場合、全機能を備えた以下の代替手段の使用を推奨します。\n\n* Node.js を使用している場合は、[`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream) を使用します。\n* Deno や、[Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) をサポートするモダンなエッジランタイムを使用している場合は、[`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream) を使用します。\n\nサーバ環境がストリームをサポートしていない場合は、`renderToString` の使用を続けても構いません。\n\n---\n\n### サーバ上で `renderToString` から静的なプリレンダーへの移行 {/*migrating-from-rendertostring-to-a-static-prerender-on-the-server*/}\n\n`renderToString` は直ちに文字列を返すため、静的 HTML 生成時にデータの待機を行うことをサポートしてません。\n\n全機能を備えた以下の代替手段の使用を推奨します。\n\n* Node.js を使用している場合は、[`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream) を使用します。\n* Deno や、[Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) をサポートするモダンなエッジランタイムを使用している場合は、[`prerender`](/reference/react-dom/static/prerender) を使用します。\n\n静的サイトを生成する環境でストリームがサポートされていない場合、`renderToString` の使用を続けても構いません。\n\n---\n\n### クライアントコードから `renderToString` を削除する {/*removing-rendertostring-from-the-client-code*/}\n\n時に、`renderToString` はクライアント上で何らかのコンポーネントを HTML に変換するために使用されることがあります。\n\n```js {1-2}\n// 🚩 Unnecessary: using renderToString on the client\nimport { renderToString } from 'react-dom/server';\n\nconst html = renderToString(<MyIcon />);\nconsole.log(html); // For example, \"<svg>...</svg>\"\n```\n\n**クライアント上で** `react-dom/server` をインポートすることは、不必要にバンドルサイズが増加するため避けるべきです。ブラウザで何らかのコンポーネントを HTML にレンダーする必要がある場合は、[`createRoot`](/reference/react-dom/client/createRoot) を使用し、DOM から HTML を読み取ります：\n\n```js\nimport { createRoot } from 'react-dom/client';\nimport { flushSync } from 'react-dom';\n\nconst div = document.createElement('div');\nconst root = createRoot(div);\nflushSync(() => {\n  root.render(<MyIcon />);\n});\nconsole.log(div.innerHTML); // For example, \"<svg>...</svg>\"\n```\n\n[`flushSync`](/reference/react-dom/flushSync) の呼び出しは、[`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) プロパティを読み取る前に DOM が更新されるようにするために必要です。\n\n---\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### コンポーネントがサスペンドすると HTML に常にフォールバックが含まれる {/*when-a-component-suspends-the-html-always-contains-a-fallback*/}\n\n`renderToString` はサスペンスを完全にはサポートしていません。\n\n何らかのコンポーネントが（[`lazy`](/reference/react/lazy) で定義されている、データをフェッチしているなどの理由で）サスペンドした場合、`renderToString` はそのコンテンツがロードされるのを待ちません。代わりに、`renderToString` はその上にある最も近い [`<Suspense>`](/reference/react/Suspense) バウンダリを見つけ、その `fallback` を HTML にレンダーします。コンテンツは、クライアントでコードがロードされるまで表示されなくなります。\n\nこれを解決するには、[ストリーミング対応の推奨ソリューション](#alternatives)のいずれかを使用します。サーバサイドレンダリングの場合、サーバ上でコンテンツがロードされるにつれて分割してコンテンツをストリームするため、クライアントコードがロードされる前から、ユーザはページが徐々に埋まっていくところを見ることができるようになります。静的サイト生成の場合、静的な HTML が作成される前にすべてのコンテンツがロードされるのを待機できます。\n\n"
  },
  {
    "path": "src/content/reference/react-dom/server/resume.md",
    "content": "---\ntitle: resume\n---\n\n<Intro>\n\n`resume` streams a pre-rendered React tree to a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)\n\n```js\nconst stream = await resume(reactNode, postponedState, options?)\n```\n\n</Intro>\n\n<InlineToc />\n\n<Note>\n\nThis API depends on [Web Streams.](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) For Node.js, use [`resumeToNodeStream`](/reference/react-dom/server/renderToPipeableStream) instead.\n\n</Note>\n\n---\n\n## Reference {/*reference*/}\n\n### `resume(node, postponedState, options?)` {/*resume*/}\n\nCall `resume` to resume rendering a pre-rendered React tree as HTML into a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)\n\n```js\nimport { resume } from 'react-dom/server';\nimport {getPostponedState} from './storage';\n\nasync function handler(request, writable) {\n  const postponed = await getPostponedState(request);\n  const resumeStream = await resume(<App />, postponed);\n  return resumeStream.pipeTo(writable)\n}\n```\n\n[See more examples below.](#usage)\n\n#### Parameters {/*parameters*/}\n\n* `reactNode`: The React node you called `prerender` with. For example, a JSX element like `<App />`. It is expected to represent the entire document, so the `App` component should render the `<html>` tag.\n* `postponedState`: The opaque `postpone` object returned from a [prerender API](/reference/react-dom/static/index), loaded from wherever you stored it (e.g. redis, a file, or S3).\n* **optional** `options`: An object with streaming options.\n  * **optional** `nonce`: A [`nonce`](http://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#nonce) string to allow scripts for [`script-src` Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src).\n  * **optional** `signal`: An [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that lets you [abort server rendering](#aborting-server-rendering) and render the rest on the client.\n  * **optional** `onError`: A callback that fires whenever there is a server error, whether [recoverable](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-outside-the-shell) or [not.](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-inside-the-shell) By default, this only calls `console.error`. If you override it to [log crash reports,](/reference/react-dom/server/renderToReadableStream#logging-crashes-on-the-server) make sure that you still call `console.error`.\n\n\n#### Returns {/*returns*/}\n\n`resume` returns a Promise:\n\n- If `resume` successfully produced a [shell](/reference/react-dom/server/renderToReadableStream#specifying-what-goes-into-the-shell), that Promise will resolve to a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) that can be piped to a [Writable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream).\n- If an error happens in the shell, the Promise will reject with that error.\n\nThe returned stream has an additional property:\n\n* `allReady`: A Promise that resolves when all rendering is complete. You can `await stream.allReady` before returning a response [for crawlers and static generation.](/reference/react-dom/server/renderToReadableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation) If you do that, you won't get any progressive loading. The stream will contain the final HTML.\n\n#### Caveats {/*caveats*/}\n\n- `resume` does not accept options for `bootstrapScripts`, `bootstrapScriptContent`, or `bootstrapModules`. Instead, you need to pass these options to the `prerender` call that generates the `postponedState`. You can also inject bootstrap content into the writable stream manually.\n- `resume` does not accept `identifierPrefix` since the prefix needs to be the same in both `prerender` and `resume`.\n- Since `nonce` cannot be provided to prerender, you should only provide `nonce` to `resume` if you're not providing scripts to prerender.\n- `resume` re-renders from the root until it finds a component that was not fully pre-rendered. Only fully prerendered Components (the Component and its children finished prerendering) are skipped entirely.\n\n## Usage {/*usage*/}\n\n### Resuming a prerender {/*resuming-a-prerender*/}\n\n<Sandpack>\n\n```js src/App.js hidden \n```\n\n```html public/index.html\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Document</title>\n</head>\n<body>\n  <iframe id=\"container\"></iframe>\n</body>\n</html>\n```\n\n```js src/index.js\nimport {\n  flushReadableStreamToFrame,\n  getUser,\n  Postponed,\n  sleep,\n} from \"./demo-helpers\";\nimport { StrictMode, Suspense, use, useEffect } from \"react\";\nimport { prerender } from \"react-dom/static\";\nimport { resume } from \"react-dom/server\";\nimport { hydrateRoot } from \"react-dom/client\";\n\nfunction Header() {\n  return <header>Me and my descendants can be prerendered</header>;\n}\n\nconst { promise: cookies, resolve: resolveCookies } = Promise.withResolvers();\n\nfunction Main() {\n  const { sessionID } = use(cookies);\n  const user = getUser(sessionID);\n\n  useEffect(() => {\n    console.log(\"reached interactivity!\");\n  }, []);\n\n  return (\n    <main>\n      Hello, {user.name}!\n      <button onClick={() => console.log(\"hydrated!\")}>\n        Clicking me requires hydration.\n      </button>\n    </main>\n  );\n}\n\nfunction Shell({ children }) {\n  // In a real app, this is where you would put your html and body.\n  // We're just using tags here we can include in an existing body for demonstration purposes\n  return (\n    <html>\n      <body>{children}</body>\n    </html>\n  );\n}\n\nfunction App() {\n  return (\n    <Shell>\n      <Suspense fallback=\"loading header\">\n        <Header />\n      </Suspense>\n      <Suspense fallback=\"loading main\">\n        <Main />\n      </Suspense>\n    </Shell>\n  );\n}\n\nasync function main(frame) {\n  // Layer 1\n  const controller = new AbortController();\n  const prerenderedApp = prerender(<App />, {\n    signal: controller.signal,\n    onError(error) {\n      if (error instanceof Postponed) {\n      } else {\n        console.error(error);\n      }\n    },\n  });\n  // We're immediately aborting in a macrotask.\n  // Any data fetching that's not available synchronously, or in a microtask, will not have finished.\n  setTimeout(() => {\n    controller.abort(new Postponed());\n  });\n\n  const { prelude, postponed } = await prerenderedApp;\n  await flushReadableStreamToFrame(prelude, frame);\n\n  // Layer 2\n  // Just waiting here for demonstration purposes.\n  // In a real app, the prelude and postponed state would've been serialized in Layer 1 and Layer would deserialize them.\n  // The prelude content could be flushed immediated as plain HTML while\n  // React is continuing to render from where the prerender left off.\n  await sleep(2000);\n\n  // You would get the cookies from the incoming HTTP request\n  resolveCookies({ sessionID: \"abc\" });\n\n  const stream = await resume(<App />, postponed);\n\n  await flushReadableStreamToFrame(stream, frame);\n\n  // Layer 3\n  // Just waiting here for demonstration purposes.\n  await sleep(2000);\n\n  hydrateRoot(frame.contentWindow.document, <App />);\n}\n\nmain(document.getElementById(\"container\"));\n\n```\n\n```js src/demo-helpers.js\nexport async function flushReadableStreamToFrame(readable, frame) {\n  const document = frame.contentWindow.document;\n  const decoder = new TextDecoder();\n  for await (const chunk of readable) {\n    const partialHTML = decoder.decode(chunk);\n    document.write(partialHTML);\n  }\n}\n\n// This doesn't need to be an error.\n// You can use any other means to check if an error during prerender was\n// from an intentional abort or a real error.\nexport class Postponed extends Error {}\n\n// We're just hardcoding a session here.\nexport function getUser(sessionID) {\n  return {\n    name: \"Alice\",\n  };\n}\n\nexport function sleep(timeoutMS) {\n  return new Promise((resolve) => {\n    setTimeout(() => {\n      resolve();\n    }, timeoutMS);\n  });\n}\n```\n\n</Sandpack>\n\n### Further reading {/*further-reading*/}\n\nResuming behaves like `renderToReadableStream`. For more examples, check out the [usage section of `renderToReadableStream`](/reference/react-dom/server/renderToReadableStream#usage).\nThe [usage section of `prerender`](/reference/react-dom/static/prerender#usage) includes examples of how to use `prerender` specifically."
  },
  {
    "path": "src/content/reference/react-dom/server/resumeToPipeableStream.md",
    "content": "---\ntitle: resumeToPipeableStream\n---\n\n<Intro>\n\n`resumeToPipeableStream` streams a pre-rendered React tree  to a pipeable [Node.js Stream.](https://nodejs.org/api/stream.html)\n\n```js\nconst {pipe, abort} = await resumeToPipeableStream(reactNode, postponedState, options?)\n```\n\n</Intro>\n\n<InlineToc />\n\n<Note>\n\nThis API is specific to Node.js. Environments with [Web Streams,](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) like Deno and modern edge runtimes, should use [`resume`](/reference/react-dom/server/renderToReadableStream) instead.\n\n</Note>\n\n---\n\n## Reference {/*reference*/}\n\n### `resumeToPipeableStream(node, postponed, options?)` {/*resume-to-pipeable-stream*/}\n\nCall `resume` to resume rendering a pre-rendered React tree as HTML into a [Node.js Stream.](https://nodejs.org/api/stream.html#writable-streams)\n\n```js\nimport { resume } from 'react-dom/server';\nimport {getPostponedState} from './storage';\n\nasync function handler(request, response) {\n  const postponed = await getPostponedState(request);\n  const {pipe} = resumeToPipeableStream(<App />, postponed, {\n    onShellReady: () => {\n      pipe(response);\n    }\n  });\n}\n```\n\n[See more examples below.](#usage)\n\n#### Parameters {/*parameters*/}\n\n* `reactNode`: The React node you called `prerender` with. For example, a JSX element like `<App />`. It is expected to represent the entire document, so the `App` component should render the `<html>` tag.\n* `postponedState`: The opaque `postpone` object returned from a [prerender API](/reference/react-dom/static/index), loaded from wherever you stored it (e.g. redis, a file, or S3).\n* **optional** `options`: An object with streaming options.\n  * **optional** `nonce`: A [`nonce`](http://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#nonce) string to allow scripts for [`script-src` Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src).\n  * **optional** `signal`: An [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that lets you [abort server rendering](#aborting-server-rendering) and render the rest on the client.\n  * **optional** `onError`: A callback that fires whenever there is a server error, whether [recoverable](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-outside-the-shell) or [not.](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-inside-the-shell) By default, this only calls `console.error`. If you override it to [log crash reports,](/reference/react-dom/server/renderToReadableStream#logging-crashes-on-the-server) make sure that you still call `console.error`.\n  * **optional** `onShellReady`: A callback that fires right after the [shell](#specifying-what-goes-into-the-shell) has finished. You can call `pipe` here to start streaming. React will [stream the additional content](#streaming-more-content-as-it-loads) after the shell along with the inline `<script>` tags that replace the HTML loading fallbacks with the content.\n  * **optional** `onShellError`: A callback that fires if there was an error rendering the shell. It receives the error as an argument. No bytes were emitted from the stream yet, and neither `onShellReady` nor `onAllReady` will get called, so you can [output a fallback HTML shell](#recovering-from-errors-inside-the-shell) or use the prelude.\n\n\n#### Returns {/*returns*/}\n\n`resume` returns an object with two methods:\n\n* `pipe` outputs the HTML into the provided [Writable Node.js Stream.](https://nodejs.org/api/stream.html#writable-streams) Call `pipe` in `onShellReady` if you want to enable streaming, or in `onAllReady` for crawlers and static generation.\n* `abort` lets you [abort server rendering](#aborting-server-rendering) and render the rest on the client.\n\n#### Caveats {/*caveats*/}\n\n- `resumeToPipeableStream` does not accept options for `bootstrapScripts`, `bootstrapScriptContent`, or `bootstrapModules`. Instead, you need to pass these options to the `prerender` call that generates the `postponedState`. You can also inject bootstrap content into the writable stream manually.\n- `resumeToPipeableStream` does not accept `identifierPrefix` since the prefix needs to be the same in both `prerender` and `resumeToPipeableStream`.\n- Since `nonce` cannot be provided to prerender, you should only provide `nonce` to `resumeToPipeableStream` if you're not providing scripts to prerender.\n- `resumeToPipeableStream` re-renders from the root until it finds a component that was not fully pre-rendered. Only fully prerendered Components (the Component and its children finished prerendering) are skipped entirely.\n\n## Usage {/*usage*/}\n\n### Further reading {/*further-reading*/}\n\nResuming behaves like `renderToReadableStream`. For more examples, check out the [usage section of `renderToReadableStream`](/reference/react-dom/server/renderToReadableStream#usage).\nThe [usage section of `prerender`](/reference/react-dom/static/prerender#usage) includes examples of how to use `prerenderToNodeStream` specifically."
  },
  {
    "path": "src/content/reference/react-dom/static/index.md",
    "content": "---\ntitle: 静的サイト用 React DOM API\n---\n\n<Intro>\n\n`react-dom/static` の API を用いて、React コンポーネントを静的な HTML にレンダーすることができます。これらの API はストリーミング API とよりも機能が限られています。[フレームワーク](/learn/creating-a-react-app#full-stack-frameworks)がこれらあなたの代わりに呼び出すことがあります。ほとんどのコンポーネントは、これらをインポートしたり使用したりする必要はありません。\n\n</Intro>\n\n---\n\n## Web ストリーム用の静的 API {/*static-apis-for-web-streams*/}\n\n以下のメソッドは、[Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) が利用可能な環境でのみ使用できます。これには、ブラウザ、Deno、および一部のモダンなエッジランタイムが含まれます。\n\n* [`prerender`](/reference/react-dom/static/prerender) は React ツリーを[読み取り可能な Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) を用いて静的な HTML にレンダーします。\n* <ExperimentalBadge /> [`resumeAndPrerender`](/reference/react-dom/static/resumeAndPrerender) はプリンレンダー済みの React ツリーを再開して、[読み取り可能な Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) に静的な HTML として流します。\n\nNode.js でも互換性のためこれらのメソッドが使用可能ですが、パフォーマンスが劣化するため推奨されません。代わりに [Node.js 専用の API](#static-apis-for-nodejs-streams) を使用してください。\n\n---\n\n## Node.js ストリーム用の静的 API {/*static-apis-for-nodejs-streams*/}\n\n以下のメソッドは、[Node.js ストリーム](https://nodejs.org/api/stream.html)が利用可能な環境でのみ使用できます。\n\n* [`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream) は React ツリーを [Node.js ストリーム](https://nodejs.org/api/stream.html)を用いて静的な HTML にレンダーします。\n* <ExperimentalBadge /> [`resumeAndPrerenderToNodeStream`](/reference/react-dom/static/resumeAndPrerenderToNodeStream) はプリレンダー済みの React ツリーを再開して、[Node.js ストリーム](https://nodejs.org/api/stream.html)に静的な HTML として流します。\n"
  },
  {
    "path": "src/content/reference/react-dom/static/prerender.md",
    "content": "---\ntitle: prerender\n---\n\n<Intro>\n\n`prerender` は React ツリーを [Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) を用いて静的な HTML 文字列にレンダーします。\n\n```js\nconst {prelude, postponed} = await prerender(reactNode, options?)\n```\n\n</Intro>\n\n<InlineToc />\n\n<Note>\n\nこの API は [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) に依存しています。Node.js では、代わりに [`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream) を使用してください。\n\n</Note>\n\n---\n\n## リファレンス {/*reference*/}\n\n### `prerender(reactNode, options?)` {/*prerender*/}\n\n`prerender` を呼び出して、アプリを静的な HTML にレンダーします。\n\n```js\nimport { prerender } from 'react-dom/static';\n\nasync function handler(request, response) {\n  const {prelude} = await prerender(<App />, {\n    bootstrapScripts: ['/main.js']\n  });\n  return new Response(prelude, {\n    headers: { 'content-type': 'text/html' },\n  });\n}\n```\n\nクライアント側では、このようにサーバ生成された HTML を操作可能にするために [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を用います。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `reactNode`: HTML へとレンダーしたい React ノード。例えば、`<App />` のような JSX 要素です。これはドキュメント全体を表すことが期待されているため、`App` コンポーネントは `<html>` タグをレンダーする必要があります。\n\n* **省略可能** `options`: 静的生成関連のオプションが含まれたオブジェクト。\n  * **省略可能** `bootstrapScriptContent`: 指定された場合、この文字列がインラインの `<script>` タグ内に配置されます。\n  * **省略可能** `bootstrapScripts`: ページ上に出力する `<script>` タグに対応する URL 文字列の配列。これを使用して、[`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を呼び出す `<script>` を含めます。クライアントで React をまったく実行したくない場合は省略します。\n  * **省略可能** `bootstrapModules`: `bootstrapScripts` と同様ですが、代わりに [`<script type=\"module\">`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) を出力します。\n  * **省略可能** `identifierPrefix`: React が [`useId`](/reference/react/useId) によって生成する ID に使用する文字列プレフィックス。同じページ上に複数のルートを使用する際に、競合を避けるために用います。[`hydrateRoot`](/reference/react-dom/client/hydrateRoot#parameters) にも同じプレフィックスを渡す必要があります。\n  * **省略可能** `namespaceURI`: このストリームのルート[ネームスペース URI](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#important_namespace_uris) 文字列。デフォルトでは通常の HTML です。SVG の場合は `'http://www.w3.org/2000/svg'`、MathML の場合は `'http://www.w3.org/1998/Math/MathML'` を渡します。\n  * **省略可能** `onError`: サーバエラーが発生するたびに発火するコールバック。[復帰可能なエラー](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-outside-the-shell)の場合も[そうでないエラー](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-inside-the-shell)の場合もあります。デフォルトでは `console.error` のみを呼び出します。これを上書きして[クラッシュレポートをログに記録する](/reference/react-dom/server/renderToReadableStream#logging-crashes-on-the-server)場合でも `console.error` を呼び出すようにしてください。また、シェルが出力される前に[ステータスコードを調整する](/reference/react-dom/server/renderToReadableStream#setting-the-status-code)ためにも使用できます。\n  * **省略可能** `progressiveChunkSize`: チャンクのバイト数。[デフォルトの推論方法についてはこちらを参照してください](https://github.com/facebook/react/blob/14c2be8dac2d5482fda8a0906a31d239df8551fc/packages/react-server/src/ReactFizzServer.js#L210-L225)。\n  * **省略可能** `signal`: [プリレンダーを中止](#aborting-prerendering)してクライアントで残りをレンダーするために使用できる [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)。\n\n#### 返り値 {/*returns*/}\n\n`prerender` はプロミスを返します。\n- レンダーが成功した場合、プロミスは以下を含んだオブジェクトに解決 (resolve) されます。\n  - `prelude`: HTML の [Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API)。このストリームを使ってレスポンスを送信したり、ストリームを文字列に一括して読み出したりできます。\n  - `postponed`: `prerender` が終了しなかった場合には、[`resume`](/reference/react-dom/server/resume) に渡すために用いる、JSON シリアライズ可能な非公開のオブジェクト。そうでない場合は `null` で、これは `predule` に必要なすべてのコンテンツが入っており `resume` が必要ないことを表す。\n- レンダーが失敗した場合は、Promise は拒否 (reject) されます。[これを使用してフォールバックシェルを出力します](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-inside-the-shell)。\n\n#### 注意点 {/*caveats*/}\n\nプリレンダー中に `nonce` オプションは利用できません。nonce はリクエストごとに一意である必要があり、[CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP) でアプリケーションを保護するために nonce を使用する場合、プリレンダー自体に nonce 値を含めることは不適切かつ危険です。\n\n<Note>\n\n### `prerender` をいつ使うのか {/*when-to-use-prerender*/}\n\n`prerender` API は、静的なサーバサイド生成 (server-side generation; SSG) に使用するものです。`renderToString` とは異なり、`prerender` はすべてのデータの読み込みが完了するまで待機してから解決されます。このため、サスペンスを使用して取得するデータを含む、ページ全体の静的な HTML を生成するのに適しています。読み込み中のコンテンツをストリーミングする場合は、[renderToReadableStream](/reference/react-dom/server/renderToReadableStream) のようなストリーミング付きサーバサイドレンダリング (SSR) API を使用してください。\n\n部分プリレンダリング (partial pre-rendering) をサポートするため、`prerender` は中断可能です。あとで `resumeAndPrerender` でプリレンダーを継続することも、`resume` で再開することも可能です。\n\n</Note>\n\n---\n\n## 使用法 {/*usage*/}\n\n### React ツリーを静的な HTML としてストリームにレンダーする {/*rendering-a-react-tree-to-a-stream-of-static-html*/}\n\n`prerender` を呼び出して、React ツリーを静的な HTML として[読み取り可能な Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) にレンダーします。\n\n```js [[1, 4, \"<App />\"], [2, 5, \"['/main.js']\"]]\nimport { prerender } from 'react-dom/static';\n\nasync function handler(request) {\n  const {prelude} = await prerender(<App />, {\n    bootstrapScripts: ['/main.js']\n  });\n  return new Response(prelude, {\n    headers: { 'content-type': 'text/html' },\n  });\n}\n```\n\n<CodeStep step={1}>ルートコンポーネント</CodeStep> と <CodeStep step={2}>ブートストラップ `<script>` パス</CodeStep>のリストを指定する必要があります。ルートコンポーネントは、**ルートの `<html>` タグを含んだドキュメント全体**を返すようにします。\n\n例えば以下のような形になるでしょう。\n\n```js [[1, 1, \"App\"]]\nexport default function App() {\n  return (\n    <html>\n      <head>\n        <meta charSet=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        <link rel=\"stylesheet\" href=\"/styles.css\"></link>\n        <title>My app</title>\n      </head>\n      <body>\n        <Router />\n      </body>\n    </html>\n  );\n}\n```\n\nReact は [doctype](https://developer.mozilla.org/en-US/docs/Glossary/Doctype) とあなたが指定した<CodeStep step={2}>ブートストラップ `<script>` タグ</CodeStep>を結果の HTML ストリームに注入します。\n\n```html [[2, 5, \"/main.js\"]]\n<!DOCTYPE html>\n<html>\n  <!-- ... HTML from your components ... -->\n</html>\n<script src=\"/main.js\" async=\"\"></script>\n```\n\nクライアント側では、ブートストラップスクリプトは [`hydrateRoot` を呼び出して `document` 全体のハイドレーションを行う](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document)必要があります。\n\n```js [[1, 4, \"<App />\"]]\nimport { hydrateRoot } from 'react-dom/client';\nimport App from './App.js';\n\nhydrateRoot(document, <App />);\n```\n\nこれにより、サーバで生成された静的な HTML にイベントリスナが追加され、操作可能になります。\n\n<DeepDive>\n\n#### ビルド出力から CSS と JS のアセットパスを読み取る {/*reading-css-and-js-asset-paths-from-the-build-output*/}\n\nビルド後に、最終的なアセット URL（JavaScript や CSS ファイルなど）にはよくハッシュ化が行われます。例えば、`styles.css` が `styles.123456.css` になることがあります。静的なアセットのファイル名をハッシュ化することで、同じアセットがビルドごとに異なるファイル名になることが保証されます。これが有用なのは、ある特定の名前を持つファイルの内容が不変になり、静的なアセットの長期的なキャッシングを安全に行えるようになるためです。\n\nしかし、ビルド後までアセット URL が分からない場合、それらをソースコードに含めることができません。例えば、先ほどのように JSX に `\"/styles.css\"` をハードコーディングする方法は動作しません。ソースコードにそれらを含めないようにするため、ルートコンポーネントが、props 経由で渡されたマップから実際のファイル名を読み取るようにすることができます。\n\n```js {1,6}\nexport default function App({ assetMap }) {\n  return (\n    <html>\n      <head>\n        <title>My app</title>\n        <link rel=\"stylesheet\" href={assetMap['styles.css']}></link>\n      </head>\n      ...\n    </html>\n  );\n}\n```\n\nサーバ上では、`<App assetMap={assetMap} />` のようにレンダーし、アセット URL を含む `assetMap` を渡します。\n\n```js {1-5,8,9}\n// You'd need to get this JSON from your build tooling, e.g. read it from the build output.\nconst assetMap = {\n  'styles.css': '/styles.123456.css',\n  'main.js': '/main.123456.js'\n};\n\nasync function handler(request) {\n  const {prelude} = await prerender(<App assetMap={assetMap} />, {\n    bootstrapScripts: [assetMap['/main.js']]\n  });\n  return new Response(prelude, {\n    headers: { 'content-type': 'text/html' },\n  });\n}\n```\n\nサーバで `<App assetMap={assetMap} />` のようにレンダーしているので、クライアントでも `assetMap` を使ってレンダーしてハイドレーションエラーを避ける必要があります。このためには以下のように `assetMap` をシリアライズしてクライアントに渡します。\n\n```js {9-10}\n// You'd need to get this JSON from your build tooling.\nconst assetMap = {\n  'styles.css': '/styles.123456.css',\n  'main.js': '/main.123456.js'\n};\n\nasync function handler(request) {\n  const {prelude} = await prerender(<App assetMap={assetMap} />, {\n    // Careful: It's safe to stringify() this because this data isn't user-generated.\n    bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`,\n    bootstrapScripts: [assetMap['/main.js']],\n  });\n  return new Response(prelude, {\n    headers: { 'content-type': 'text/html' },\n  });\n}\n```\n\n上記の例では、`bootstrapScriptContent` オプションを使って`<script>` タグを追加して、クライアント上でグローバル `window.assetMap` 変数をセットしています。これにより、クライアントのコードが同じ `assetMap` を読み取れるようになります。\n\n```js {4}\nimport { hydrateRoot } from 'react-dom/client';\nimport App from './App.js';\n\nhydrateRoot(document, <App assetMap={window.assetMap} />);\n```\n\nクライアントとサーバの両方が props として同じ `assetMap` を使って `App` をレンダーするため、ハイドレーションエラーは発生しません。\n\n</DeepDive>\n\n---\n\n### React ツリーを静的な HTML 文字列にレンダーする {/*rendering-a-react-tree-to-a-string-of-static-html*/}\n\n`prerender` を呼び出して、アプリを静的な HTML 文字列にレンダーします。\n\n```js\nimport { prerender } from 'react-dom/static';\n\nasync function renderToString() {\n  const {prelude} = await prerender(<App />, {\n    bootstrapScripts: ['/main.js']\n  });\n\n  const reader = prelude.getReader();\n  let content = '';\n  while (true) {\n    const {done, value} = await reader.read();\n    if (done) {\n      return content;\n    }\n    content += Buffer.from(value).toString('utf8');\n  }\n}\n```\n\nこれにより、React コンポーネントの、操作できない初期 HTML が生成されます。クライアントでは、[`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を呼び出してこのサーバで生成された HTML に対する*ハイドレーション*を行い、操作可能にする必要があります。\n\n---\n\n### 全データの読み込みを待機 {/*waiting-for-all-data-to-load*/}\n\n`prerender` は、静的な HTML 生成を行って解決する前に、全データがロードされるのを待機します。例えば以下のようなプロフィールページがあり、カバー、フレンド・写真が含まれたサイドバー、投稿のリストを表示しているところを考えましょう。\n\n```js\nfunction ProfilePage() {\n  return (\n    <ProfileLayout>\n      <ProfileCover />\n      <Sidebar>\n        <Friends />\n        <Photos />\n      </Sidebar>\n      <Suspense fallback={<PostsGlimmer />}>\n        <Posts />\n      </Suspense>\n    </ProfileLayout>\n  );\n}\n```\n\nここで、`<Posts />` のデータを読み込むのに時間がかかるとしましょう。理想的には、投稿の読み込みを待機して、HTML に含めてしまいたいとします。これを実現するには、サスペンスを使ってそのデータをサスペンドします。`prerender` はそのサスペンドされているコンテンツの読み込みを待機してから、静的 HTML へと解決します。\n\n<Note>\n\n**サスペンスコンポーネントをアクティブ化できるのはサスペンス対応のデータソースだけです**。これには以下が含まれます：\n\n- [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) や [Next.js](https://nextjs.org/docs/getting-started/react-essentials) のようなサスペンス対応のフレームワークでのデータフェッチ\n- [`lazy`](/reference/react/lazy) を用いたコンポーネントコードの遅延ロード\n- [`use`](/reference/react/use) を用いたプロミス (Promise) からの値の読み取り\n\nサスペンスはエフェクトやイベントハンドラ内でデータフェッチが行われた場合にはそれを**検出しません**。\n\n上記の `Posts` コンポーネントで実際にデータをロードする方法は、使用するフレームワークによって異なります。サスペンス対応のフレームワークを使用している場合、詳細はデータフェッチに関するドキュメンテーション内に記載されているはずです。\n\n使い方の規約のある (opinionated) フレームワークを使用せずにサスペンスを使ったデータフェッチを行うことは、まだサポートされていません。サスペンス対応のデータソースを実装するための要件はまだ不安定であり、ドキュメント化されていません。データソースをサスペンスと統合するための公式な API は、React の将来のバージョンでリリースされる予定です。\n\n</Note>\n\n---\n\n### プリレンダーの中止 {/*aborting-prerendering*/}\n\nプリレンダー処理は、一定時間経過 (timeout) 後に強制的に「諦めさせる」ことが可能です。\n\n```js {2-5,11}\nasync function renderToString() {\n  const controller = new AbortController();\n  setTimeout(() => {\n    controller.abort()\n  }, 10000);\n\n  try {\n    // the prelude will contain all the HTML that was prerendered\n    // before the controller aborted.\n    const {prelude} = await prerender(<App />, {\n      signal: controller.signal,\n    });\n    //...\n```\n\nサスペンスバウンダリは、子のレンダーが未完了の場合にはフォールバックの状態で結果 (prelude) に含まれます。\n\nこれは [`resume`](/reference/react-dom/server/resume) または [`resumeAndPrerender`](/reference/react-dom/static/resumeAndPrerender) を用いた部分プリレンダリングで利用可能です。\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### アプリ全体がレンダーされるまでストリームが始まらない {/*my-stream-doesnt-start-until-the-entire-app-is-rendered*/}\n\n`prerender` の返り値は解決する前に、全サスペンスバウンダリが解決することも含む、アプリ全体のレンダーの終了を待機します。これは事前静的サイト生成 (SSG) のために設計されているものであり、コンテンツを読み込みながらのストリーミングをサポートしません。\n\nコンテンツを読み込みながらストリームしたい場合は、サーバレンダー API である [renderToReadableStream](/reference/react-dom/server/renderToReadableStream) などを使用してください。\n"
  },
  {
    "path": "src/content/reference/react-dom/static/prerenderToNodeStream.md",
    "content": "---\ntitle: prerenderToNodeStream\n---\n\n<Intro>\n\n`prerenderToNodeStream` は React ツリーを [Node.js ストリーム](https://nodejs.org/api/stream.html)を用いて静的な HTML 文字列にレンダーします。\n\n```js\nconst {prelude, postponed} = await prerenderToNodeStream(reactNode, options?)\n```\n\n</Intro>\n\n<InlineToc />\n\n<Note>\n\nこの API は Node.js 専用です。Deno やモダンエッジランタイムのような [Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) 環境では、代わりに [`prerender`](/reference/react-dom/static/prerender) を使用してください。\n\n</Note>\n\n---\n\n## リファレンス {/*reference*/}\n\n### `prerenderToNodeStream(reactNode, options?)` {/*prerender*/}\n\n`prerenderToNodeStream` を呼び出して、アプリを静的な HTML にレンダーします。\n\n```js\nimport { prerenderToNodeStream } from 'react-dom/static';\n\n// The route handler syntax depends on your backend framework\napp.use('/', async (request, response) => {\n  const { prelude } = await prerenderToNodeStream(<App />, {\n    bootstrapScripts: ['/main.js'],\n  });\n\n  response.setHeader('Content-Type', 'text/plain');\n  prelude.pipe(response);\n});\n```\n\nクライアント側では、このようにサーバ生成された HTML を操作可能にするために [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を用います。\n\n[さらに例を見る](#usage)\n\n#### 引数 {/*parameters*/}\n\n* `reactNode`: HTML へとレンダーしたい React ノード。例えば、`<App />` のような JSX 要素です。これはドキュメント全体を表すことが期待されているため、`App` コンポーネントは `<html>` タグをレンダーする必要があります。\n\n* **省略可能** `options`: 静的生成関連のオプションが含まれたオブジェクト。\n  * **省略可能** `bootstrapScriptContent`: 指定された場合、この文字列がインラインの `<script>` タグ内に配置されます。\n  * **省略可能** `bootstrapScripts`: ページ上に出力する `<script>` タグに対応する URL 文字列の配列。これを使用して、[`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を呼び出す `<script>` を含めます。クライアントで React をまったく実行したくない場合は省略します。\n  * **省略可能** `bootstrapModules`: `bootstrapScripts` と同様ですが、代わりに [`<script type=\"module\">`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) を出力します。\n  * **省略可能** `identifierPrefix`: React が [`useId`](/reference/react/useId) によって生成する ID に使用する文字列プレフィックス。同じページ上に複数のルートを使用する際に、競合を避けるために用います。[`hydrateRoot`](/reference/react-dom/client/hydrateRoot#parameters) にも同じプレフィックスを渡す必要があります。\n  * **省略可能** `namespaceURI`: このストリームのルート[ネームスペース URI](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#important_namespace_uris) 文字列。デフォルトでは通常の HTML です。SVG の場合は `'http://www.w3.org/2000/svg'`、MathML の場合は `'http://www.w3.org/1998/Math/MathML'` を渡します。\n  * **省略可能** `onError`: サーバエラーが発生するたびに発火するコールバック。[復帰可能なエラー](/reference/react-dom/server/renderToPipeableStream#recovering-from-errors-outside-the-shell)の場合も[そうでないエラー](/reference/react-dom/server/renderToPipeableStream#recovering-from-errors-inside-the-shell)の場合もあります。デフォルトでは `console.error` のみを呼び出します。これを上書きして[クラッシュレポートをログに記録する](/reference/react-dom/server/renderToPipeableStream#logging-crashes-on-the-server)場合でも `console.error` を呼び出すようにしてください。また、シェルが出力される前に[ステータスコードを調整する](/reference/react-dom/server/renderToPipeableStream#setting-the-status-code)ためにも使用できます。\n  * **省略可能** `progressiveChunkSize`: チャンクのバイト数。[デフォルトの推論方法についてはこちらを参照してください](https://github.com/facebook/react/blob/14c2be8dac2d5482fda8a0906a31d239df8551fc/packages/react-server/src/ReactFizzServer.js#L210-L225)。\n  * **省略可能** `signal`: [プリレンダーを中止](#aborting-prerendering)してクライアントで残りをレンダーするために使用できる [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)。\n\n#### 返り値 {/*returns*/}\n\n`prerenderToNodeStream` はプロミスを返します。\n- レンダーが成功した場合、プロミスは以下を含んだオブジェクトに解決 (resolve) されます。\n  - `prelude`: HTML の [Node.js ストリーム](https://nodejs.org/api/stream.html)。このストリームを使ってレスポンスを送信したり、ストリームを文字列に一括して読み出したりできます。\n  - `postponed`: `prerender` が終了しなかった場合には、[`resume`](/reference/react-dom/server/resume) に渡すために用いる、JSON シリアライズ可能な非公開のオブジェクト。そうでない場合は `null` で、これは `predule` に必要なすべてのコンテンツが入っており `resume` が必要ないことを表す。\n- レンダーが失敗した場合は、Promise は拒否 (reject) されます。[これを使用してフォールバックシェルを出力します](/reference/react-dom/server/renderToPipeableStream#recovering-from-errors-inside-the-shell)。\n\n#### 注意点 {/*caveats*/}\n\nプリレンダー中に `nonce` オプションは利用できません。nonce はリクエストごとに一意である必要があり、[CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP) でアプリケーションを保護するために nonce を使用する場合、プリレンダー自体に nonce 値を含めることは不適切かつ危険です。\n\n<Note>\n\n### `prerenderToNodeStream` をいつ使うのか {/*when-to-use-prerender*/}\n\n`prerenderToNodeStream` API は、静的なサーバサイド生成 (server-side generation; SSG) に使用するものです。`renderToString` とは異なり、`prerender` はすべてのデータの読み込みが完了するまで待機してから解決されます。このため、サスペンスを使用して取得するデータを含む、ページ全体の静的な HTML を生成するのに適しています。読み込み中のコンテンツをストリーミングする場合は、[renderToReadableStream](/reference/react-dom/server/renderToReadableStream) のようなストリーミング付きサーバサイドレンダリング (SSR) API を使用してください。\n\n部分プリレンダリング (partial pre-rendering) をサポートするため、`prerenderToNodeStream` は中断可能です。あとで `resumeToPipeableStream` でプリレンダーを継続することが可能です。\n\n</Note>\n\n---\n\n## 使用法 {/*usage*/}\n\n### React ツリーを静的な HTML としてストリームにレンダーする {/*rendering-a-react-tree-to-a-stream-of-static-html*/}\n\n`prerenderToNodeStream` を呼び出して、React ツリーを静的な HTML として [Node.js ストリーム](https://nodejs.org/api/stream.html)にレンダーします。\n\n```js [[1, 5, \"<App />\"], [2, 6, \"['/main.js']\"]]\nimport { prerenderToNodeStream } from 'react-dom/static';\n\n// The route handler syntax depends on your backend framework\napp.use('/', async (request, response) => {\n  const { prelude } = await prerenderToNodeStream(<App />, {\n    bootstrapScripts: ['/main.js'],\n  });\n\n  response.setHeader('Content-Type', 'text/plain');\n  prelude.pipe(response);\n});\n```\n\n<CodeStep step={1}>ルートコンポーネント</CodeStep> と <CodeStep step={2}>ブートストラップ `<script>` パス</CodeStep>のリストを指定する必要があります。ルートコンポーネントは、**ルートの `<html>` タグを含んだドキュメント全体**を返すようにします。\n\n例えば以下のような形になるでしょう。\n\n```js [[1, 1, \"App\"]]\nexport default function App() {\n  return (\n    <html>\n      <head>\n        <meta charSet=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        <link rel=\"stylesheet\" href=\"/styles.css\"></link>\n        <title>My app</title>\n      </head>\n      <body>\n        <Router />\n      </body>\n    </html>\n  );\n}\n```\n\nReact は [doctype](https://developer.mozilla.org/en-US/docs/Glossary/Doctype) とあなたが指定した<CodeStep step={2}>ブートストラップ `<script>` タグ</CodeStep>を結果の HTML ストリームに注入します。\n\n```html [[2, 5, \"/main.js\"]]\n<!DOCTYPE html>\n<html>\n  <!-- ... HTML from your components ... -->\n</html>\n<script src=\"/main.js\" async=\"\"></script>\n```\n\nクライアント側では、ブートストラップスクリプトは [`hydrateRoot` を呼び出して `document` 全体のハイドレーションを行う](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document)必要があります。\n\n```js [[1, 4, \"<App />\"]]\nimport { hydrateRoot } from 'react-dom/client';\nimport App from './App.js';\n\nhydrateRoot(document, <App />);\n```\n\nこれにより、サーバで生成された静的な HTML にイベントリスナが追加され、操作可能になります。\n\n<DeepDive>\n\n#### ビルド出力から CSS と JS のアセットパスを読み取る {/*reading-css-and-js-asset-paths-from-the-build-output*/}\n\nビルド後に、最終的なアセット URL（JavaScript や CSS ファイルなど）にはよくハッシュ化が行われます。例えば、`styles.css` が `styles.123456.css` になることがあります。静的なアセットのファイル名をハッシュ化することで、同じアセットがビルドごとに異なるファイル名になることが保証されます。これが有用なのは、ある特定の名前を持つファイルの内容が不変になり、静的なアセットの長期的なキャッシングを安全に行えるようになるためです。\n\nしかし、ビルド後までアセット URL が分からない場合、それらをソースコードに含めることができません。例えば、先ほどのように JSX に `\"/styles.css\"` をハードコーディングする方法は動作しません。ソースコードにそれらを含めないようにするため、ルートコンポーネントが、props 経由で渡されたマップから実際のファイル名を読み取るようにすることができます。\n\n```js {1,6}\nexport default function App({ assetMap }) {\n  return (\n    <html>\n      <head>\n        <title>My app</title>\n        <link rel=\"stylesheet\" href={assetMap['styles.css']}></link>\n      </head>\n      ...\n    </html>\n  );\n}\n```\n\nサーバ上では、`<App assetMap={assetMap} />` のようにレンダーし、アセット URL を含む `assetMap` を渡します。\n\n```js {1-5,8,9}\n// You'd need to get this JSON from your build tooling, e.g. read it from the build output.\nconst assetMap = {\n  'styles.css': '/styles.123456.css',\n  'main.js': '/main.123456.js'\n};\n\napp.use('/', async (request, response) => {\n  const { prelude } = await prerenderToNodeStream(<App />, {\n    bootstrapScripts: [assetMap['/main.js']]\n  });\n\n  response.setHeader('Content-Type', 'text/html');\n  prelude.pipe(response);\n});\n```\n\nサーバで `<App assetMap={assetMap} />` のようにレンダーしているので、クライアントでも `assetMap` を使ってレンダーしてハイドレーションエラーを避ける必要があります。このためには以下のように `assetMap` をシリアライズしてクライアントに渡します。\n\n```js {9-10}\n// You'd need to get this JSON from your build tooling.\nconst assetMap = {\n  'styles.css': '/styles.123456.css',\n  'main.js': '/main.123456.js'\n};\n\napp.use('/', async (request, response) => {\n  const { prelude } = await prerenderToNodeStream(<App />, {\n    // Careful: It's safe to stringify() this because this data isn't user-generated.\n    bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`,\n    bootstrapScripts: [assetMap['/main.js']],\n  });\n\n  response.setHeader('Content-Type', 'text/html');\n  prelude.pipe(response);\n});\n```\n\n上記の例では、`bootstrapScriptContent` オプションを使って`<script>` タグを追加して、クライアント上でグローバル `window.assetMap` 変数をセットしています。これにより、クライアントのコードが同じ `assetMap` を読み取れるようになります。\n\n```js {4}\nimport { hydrateRoot } from 'react-dom/client';\nimport App from './App.js';\n\nhydrateRoot(document, <App assetMap={window.assetMap} />);\n```\n\nクライアントとサーバの両方が props として同じ `assetMap` を使って `App` をレンダーするため、ハイドレーションエラーは発生しません。\n\n</DeepDive>\n\n---\n\n### React ツリーを静的な HTML 文字列にレンダーする {/*rendering-a-react-tree-to-a-string-of-static-html*/}\n\n`prerenderToNodeStream` を呼び出して、アプリを静的な HTML 文字列にレンダーします。\n\n```js\nimport { prerenderToNodeStream } from 'react-dom/static';\n\nasync function renderToString() {\n  const {prelude} = await prerenderToNodeStream(<App />, {\n    bootstrapScripts: ['/main.js']\n  });\n\n  return new Promise((resolve, reject) => {\n    let data = '';\n    prelude.on('data', chunk => {\n      data += chunk;\n    });\n    prelude.on('end', () => resolve(data));\n    prelude.on('error', reject);\n  });\n}\n```\n\nこれにより、React コンポーネントの、操作できない初期 HTML が生成されます。クライアントでは、[`hydrateRoot`](/reference/react-dom/client/hydrateRoot) を呼び出してこのサーバで生成された HTML に対する*ハイドレーション*を行い、操作可能にする必要があります。\n\n---\n\n### 全データの読み込みを待機 {/*waiting-for-all-data-to-load*/}\n\n`prerenderToNodeStream` は、静的な HTML 生成を行って解決する前に、全データがロードされるのを待機します。例えば以下のようなプロフィールページがあり、カバー、フレンド・写真が含まれたサイドバー、投稿のリストを表示しているところを考えましょう。\n\n```js\nfunction ProfilePage() {\n  return (\n    <ProfileLayout>\n      <ProfileCover />\n      <Sidebar>\n        <Friends />\n        <Photos />\n      </Sidebar>\n      <Suspense fallback={<PostsGlimmer />}>\n        <Posts />\n      </Suspense>\n    </ProfileLayout>\n  );\n}\n```\n\nここで、`<Posts />` のデータを読み込むのに時間がかかるとしましょう。理想的には、投稿の読み込みを待機して、HTML に含めてしまいたいとします。これを実現するには、サスペンスを使ってそのデータをサスペンドします。`prerender` はそのサスペンドされているコンテンツの読み込みを待機してから、静的 HTML へと解決します。\n\n<Note>\n\n**サスペンスコンポーネントをアクティブ化できるのはサスペンス対応のデータソースだけです**。これには以下が含まれます：\n\n- [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) や [Next.js](https://nextjs.org/docs/getting-started/react-essentials) のようなサスペンス対応のフレームワークでのデータフェッチ\n- [`lazy`](/reference/react/lazy) を用いたコンポーネントコードの遅延ロード\n- [`use`](/reference/react/use) を用いたプロミス (Promise) からの値の読み取り\n\nサスペンスはエフェクトやイベントハンドラ内でデータフェッチが行われた場合にはそれを**検出しません**。\n\n上記の `Posts` コンポーネントで実際にデータをロードする方法は、使用するフレームワークによって異なります。サスペンス対応のフレームワークを使用している場合、詳細はデータフェッチに関するドキュメンテーション内に記載されているはずです。\n\n使い方の規約のある (opinionated) フレームワークを使用せずにサスペンスを使ったデータフェッチを行うことは、まだサポートされていません。サスペンス対応のデータソースを実装するための要件はまだ不安定であり、ドキュメント化されていません。データソースをサスペンスと統合するための公式な API は、React の将来のバージョンでリリースされる予定です。\n\n</Note>\n\n---\n\n### プリレンダーの中止 {/*aborting-prerendering*/}\n\nプリレンダー処理は、一定時間経過 (timeout) 後に強制的に「諦めさせる」ことが可能です。\n\n```js {2-5,11}\nasync function renderToString() {\n  const controller = new AbortController();\n  setTimeout(() => {\n    controller.abort()\n  }, 10000);\n\n  try {\n    // the prelude will contain all the HTML that was prerendered\n    // before the controller aborted.\n    const {prelude} = await prerenderToNodeStream(<App />, {\n      signal: controller.signal,\n    });\n    //...\n```\n\nサスペンスバウンダリは、子のレンダーが未完了の場合にはフォールバックの状態で結果 (prelude) に含まれます。\n\nこれは  [`resumeToPipeableStream`](/reference/react-dom/server/resumeToPipeableStream) または [`resumeAndPrerenderToNodeStream`](/reference/react-dom/static/resumeAndPrerenderToNodeStream) を用いた部分プリレンダリングで利用可能です。\n\n## トラブルシューティング {/*troubleshooting*/}\n\n### アプリ全体がレンダーされるまでストリームが始まらない {/*my-stream-doesnt-start-until-the-entire-app-is-rendered*/}\n\n`prerenderToNodeStream` の返り値は解決する前に、全サスペンスバウンダリが解決することも含む、アプリ全体のレンダーの終了を待機します。これは事前静的サイト生成 (SSG) のために設計されているものであり、コンテンツを読み込みながらのストリーミングをサポートしません。\n\nコンテンツを読み込みながらストリームしたい場合は、サーバレンダー API である [renderToReadableStream](/reference/react-dom/server/renderToReadableStream) などを使用してください。\n"
  },
  {
    "path": "src/content/reference/react-dom/static/resumeAndPrerender.md",
    "content": "---\ntitle: resumeAndPrerender\n---\n\n<Intro>\n\n`resumeAndPrerender` continues a prerendered React tree to a static HTML string using a [Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API).\n\n```js\nconst { prelude,postpone } = await resumeAndPrerender(reactNode, postponedState, options?)\n```\n\n</Intro>\n\n<InlineToc />\n\n<Note>\n\nThis API depends on [Web Streams.](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) For Node.js, use [`resumeAndPrerenderToNodeStream`](/reference/react-dom/static/resumeAndPrerenderToNodeStream) instead.\n\n</Note>\n\n---\n\n## Reference {/*reference*/}\n\n### `resumeAndPrerender(reactNode, postponedState, options?)` {/*resumeandprerender*/}\n\nCall `resumeAndPrerender` to continue a prerendered React tree to a static HTML string.\n\n```js\nimport { resumeAndPrerender } from 'react-dom/static';\nimport { getPostponedState } from 'storage';\n\nasync function handler(request, response) {\n  const postponedState = getPostponedState(request);\n  const { prelude } = await resumeAndPrerender(<App />, postponedState, {\n    bootstrapScripts: ['/main.js']\n  });\n  return new Response(prelude, {\n    headers: { 'content-type': 'text/html' },\n  });\n}\n```\n\nOn the client, call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) to make the server-generated HTML interactive.\n\n[See more examples below.](#usage)\n\n#### Parameters {/*parameters*/}\n\n* `reactNode`: The React node you called `prerender` (or a previous `resumeAndPrerender`) with. For example, a JSX element like `<App />`. It is expected to represent the entire document, so the `App` component should render the `<html>` tag.\n* `postponedState`: The opaque `postpone` object returned from a [prerender API](/reference/react-dom/static/index), loaded from wherever you stored it (e.g. redis, a file, or S3).\n* **optional** `options`: An object with streaming options.\n  * **optional** `signal`: An [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that lets you [abort server rendering](#aborting-server-rendering) and render the rest on the client.\n  * **optional** `onError`: A callback that fires whenever there is a server error, whether [recoverable](#recovering-from-errors-outside-the-shell) or [not.](#recovering-from-errors-inside-the-shell) By default, this only calls `console.error`. If you override it to [log crash reports,](#logging-crashes-on-the-server) make sure that you still call `console.error`.\n\n#### Returns {/*returns*/}\n\n`prerender` returns a Promise:\n- If rendering the is successful, the Promise will resolve to an object containing:\n  - `prelude`: a [Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) of HTML. You can use this stream to send a response in chunks, or you can read the entire stream into a string.\n  - `postponed`: an JSON-serializeable, opaque object that can be passed to [`resume`](/reference/react-dom/server/resume) or [`resumeAndPrerender`](/reference/react-dom/static/resumeAndPrerender) if `prerender` is aborted.\n- If rendering fails, the Promise will be rejected. [Use this to output a fallback shell.](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-inside-the-shell)\n\n#### Caveats {/*caveats*/}\n\n`nonce` is not an available option when prerendering. Nonces must be unique per request and if you use nonces to secure your application with [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP) it would be inappropriate and insecure to include the nonce value in the prerender itself.\n\n<Note>\n\n### When should I use `resumeAndPrerender`? {/*when-to-use-prerender*/}\n\nThe static `resumeAndPrerender` API is used for static server-side generation (SSG). Unlike `renderToString`, `resumeAndPrerender` waits for all data to load before resolving. This makes it suitable for generating static HTML for a full page, including data that needs to be fetched using Suspense. To stream content as it loads, use a streaming server-side render (SSR) API like [renderToReadableStream](/reference/react-dom/server/renderToReadableStream).\n\n`resumeAndPrerender` can be aborted and later either continued with another `resumeAndPrerender` or resumed with `resume` to support partial pre-rendering.\n\n</Note>\n\n---\n\n## Usage {/*usage*/}\n\n### Further reading {/*further-reading*/}\n\n`resumeAndPrerender` behaves similarly to [`prerender`](/reference/react-dom/static/prerender) but can be used to continue a previously started prerendering process that was aborted.\nFor more information about resuming a prerendered tree, see the [resume documentation](/reference/react-dom/server/resume#resuming-a-prerender).\n"
  },
  {
    "path": "src/content/reference/react-dom/static/resumeAndPrerenderToNodeStream.md",
    "content": "---\ntitle: resumeAndPrerenderToNodeStream\n---\n\n<Intro>\n\n`resumeAndPrerenderToNodeStream` continues a prerendered React tree to a static HTML string using a a [Node.js Stream.](https://nodejs.org/api/stream.html).\n\n```js\nconst {prelude, postponed} = await resumeAndPrerenderToNodeStream(reactNode, postponedState, options?)\n```\n\n</Intro>\n\n<InlineToc />\n\n<Note>\n\nThis API is specific to Node.js. Environments with [Web Streams,](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) like Deno and modern edge runtimes, should use [`prerender`](/reference/react-dom/static/prerender) instead.\n\n</Note>\n\n---\n\n## Reference {/*reference*/}\n\n### `resumeAndPrerenderToNodeStream(reactNode, postponedState, options?)` {/*resumeandprerendertolnodestream*/}\n\nCall `resumeAndPrerenderToNodeStream` to continue a prerendered React tree to a static HTML string.\n\n```js\nimport { resumeAndPrerenderToNodeStream } from 'react-dom/static';\nimport { getPostponedState } from 'storage';\n\nasync function handler(request, writable) {\n  const postponedState = getPostponedState(request);\n  const { prelude } = await resumeAndPrerenderToNodeStream(<App />, JSON.parse(postponedState));\n  prelude.pipe(writable);\n}\n```\n\nOn the client, call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) to make the server-generated HTML interactive.\n\n[See more examples below.](#usage)\n\n#### Parameters {/*parameters*/}\n\n* `reactNode`: The React node you called `prerender` (or a previous `resumeAndPrerenderToNodeStream`) with. For example, a JSX element like `<App />`. It is expected to represent the entire document, so the `App` component should render the `<html>` tag.\n* `postponedState`: The opaque `postpone` object returned from a [prerender API](/reference/react-dom/static/index), loaded from wherever you stored it (e.g. redis, a file, or S3).\n* **optional** `options`: An object with streaming options.\n  * **optional** `signal`: An [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that lets you [abort server rendering](#aborting-server-rendering) and render the rest on the client.\n  * **optional** `onError`: A callback that fires whenever there is a server error, whether [recoverable](#recovering-from-errors-outside-the-shell) or [not.](#recovering-from-errors-inside-the-shell) By default, this only calls `console.error`. If you override it to [log crash reports,](#logging-crashes-on-the-server) make sure that you still call `console.error`.\n\n#### Returns {/*returns*/}\n\n`resumeAndPrerenderToNodeStream` returns a Promise:\n- If rendering the is successful, the Promise will resolve to an object containing:\n  - `prelude`: a [Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) of HTML. You can use this stream to send a response in chunks, or you can read the entire stream into a string.\n  - `postponed`: an JSON-serializeable, opaque object that can be passed to [`resumeToNodeStream`](/reference/react-dom/server/resume) or [`resumeAndPrerenderToNodeStream`](/reference/react-dom/static/resumeAndPrerenderToNodeStream) if `resumeAndPrerenderToNodeStream` is aborted.\n- If rendering fails, the Promise will be rejected. [Use this to output a fallback shell.](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-inside-the-shell)\n\n#### Caveats {/*caveats*/}\n\n`nonce` is not an available option when prerendering. Nonces must be unique per request and if you use nonces to secure your application with [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP) it would be inappropriate and insecure to include the nonce value in the prerender itself.\n\n<Note>\n\n### When should I use `resumeAndPrerenderToNodeStream`? {/*when-to-use-prerender*/}\n\nThe static `resumeAndPrerenderToNodeStream` API is used for static server-side generation (SSG). Unlike `renderToString`, `resumeAndPrerenderToNodeStream` waits for all data to load before resolving. This makes it suitable for generating static HTML for a full page, including data that needs to be fetched using Suspense. To stream content as it loads, use a streaming server-side render (SSR) API like [renderToReadableStream](/reference/react-dom/server/renderToReadableStream).\n\n`resumeAndPrerenderToNodeStream` can be aborted and later either continued with another `resumeAndPrerenderToNodeStream` or resumed with `resume` to support partial pre-rendering.\n\n</Note>\n\n---\n\n## Usage {/*usage*/}\n\n### Further reading {/*further-reading*/}\n\n`resumeAndPrerenderToNodeStream` behaves similarly to [`prerender`](/reference/react-dom/static/prerender) but can be used to continue a previously started prerendering process that was aborted.\nFor more information about resuming a prerendered tree, see the [resume documentation](/reference/react-dom/server/resume#resuming-a-prerender).\n\n"
  },
  {
    "path": "src/content/reference/rsc/directives.md",
    "content": "---\ntitle: \"ディレクティブ\"\n---\n\n<RSC>\n\nディレクティブは [React Server Components](/reference/rsc/server-components) 用の機能です。\n\n</RSC>\n\n<Intro>\n\nディレクティブによって、[React Server Components 互換バンドラ](/learn/creating-a-react-app#full-stack-frameworks)に指示を与えます。\n\n</Intro>\n\n---\n\n## ソースコードディレクティブ {/*source-code-directives*/}\n\n* [`'use client'`](/reference/rsc/use-client) によりどのコードがクライアント上で実行されるべきかマークします。\n* [`'use server'`](/reference/rsc/use-server) によりクライアント側のコードから呼び出すことができるサーバサイド関数をマークします。\n"
  },
  {
    "path": "src/content/reference/rsc/server-components.md",
    "content": "---\ntitle: サーバコンポーネント\n---\n\n<Intro>\n\nサーバコンポーネントは新しいタイプのコンポーネントです。クライアントアプリケーションや SSR サーバとは別の環境で、バンドル前に事前にレンダーされます。\n\n</Intro>\n\nReact Server Components の \"server\" とはこの別の環境を指しています。サーバコンポーネントは、CI サーバでビルド時に一度だけ実行することも、ウェブサーバを使用してリクエストごとに実行することもできます。\n\n<InlineToc />\n\n<Note>\n\n#### サーバコンポーネントのサポートを追加する方法 {/*how-do-i-build-support-for-server-components*/}\n\nReact 19 の React Server Components は安定しており、マイナーバージョン間での破壊的変更はありませんが、サーバコンポーネントのバンドラやフレームワークを実装するために使用される基盤となる API は semver に従いません。React 19.x のマイナーバージョン間で変更が生じる可能性があります。\n\nReact Server Components をバンドラやフレームワークでサポートする場合は、特定の React バージョンに固定するか、Canary リリースを使用することをお勧めします。React Server Components を実装するために使用される API を安定化させるため、今後もバンドラやフレームワークと連携を続けていきます。\n\n</Note>\n\n### サーバを使用しないサーバコンポーネント {/*server-components-without-a-server*/}\nサーバコンポーネントはビルド時に実行でき、ファイルシステムから読み取ったり静的なコンテンツをフェッチしたりすることが可能です。したがってウェブサーバは必須ではありません。たとえばコンテンツ管理システムから静的データを読み取ることもできるでしょう。\n\nサーバコンポーネントがない場合、静的データは一般的に、クライアントでエフェクトを使って以下のようにフェッチします。\n```js\n// bundle.js\nimport marked from 'marked'; // 35.9K (11.2K gzipped)\nimport sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)\n\nfunction Page({page}) {\n  const [content, setContent] = useState('');\n  // NOTE: loads *after* first page render.\n  useEffect(() => {\n    fetch(`/api/content/${page}`).then((data) => {\n      setContent(data.content);\n    });\n  }, [page]);\n\n  return <div>{sanitizeHtml(marked(content))}</div>;\n}\n```\n```js\n// api.js\napp.get(`/api/content/:page`, async (req, res) => {\n  const page = req.params.page;\n  const content = await file.readFile(`${page}.md`);\n  res.send({content});\n});\n```\n\nこのパターンを使用すると、ページの表示期間中に変化しない静的なコンテンツをただレンダーするためだけに、ユーザは 75K（gzip 後）のライブラリを追加でダウンロードしてパースし、さらにページのロード後に別のリクエストがデータをフェッチしてくるのを待つ必要があることになります。\n\nサーバコンポーネントを使用することで、これらのコンポーネントをビルド時に一度だけレンダーすることができます。\n\n```js\nimport marked from 'marked'; // Not included in bundle\nimport sanitizeHtml from 'sanitize-html'; // Not included in bundle\n\nasync function Page({page}) {\n  // NOTE: loads *during* render, when the app is built.\n  const content = await file.readFile(`${page}.md`);\n\n  return <div>{sanitizeHtml(marked(content))}</div>;\n}\n```\n\nこのレンダー出力は、サーバサイドレンダリング (SSR) で HTML に変換され、CDN にアップロードできます。アプリがロードされる際、クライアントには元の `Page` コンポーネントの存在や、Markdown をレンダーするための高コストなライブラリの存在は見えません。レンダーされた出力が見えるだけです。\n\n```js\n<div><!-- html for markdown --></div>\n```\n\nつまり、コンテンツは最初のページロード時にすぐ表示され、静的コンテンツをレンダーするためだけの高コストなライブラリをバンドルに含めなくともよくなるのです。\n\n<Note>\n\n上記のサーバコンポーネントが非同期関数であることに気付かれたかもしれません。\n\n```js\nasync function Page({page}) {\n  //...\n}\n```\n\n非同期コンポーネントは、レンダー中に `await` できるというサーバコンポーネントの新機能です。\n\n以下の[サーバコンポーネントでの非同期コンポーネント処理](#async-components-with-server-components)を参照してください。\n\n</Note>\n\n### サーバを使用するサーバコンポーネント {/*server-components-with-a-server*/}\nサーバコンポーネントは、ページのリクエスト時にウェブサーバ内で実行することも可能であり、これにより API を構築することなくデータレイヤにアクセスできます。アプリケーションがバンドルされる前にレンダーされ、データと JSX をクライアントコンポーネントに props として渡すことができます。\n\nサーバコンポーネントがない場合、一般的には以下のようにして、動的データをクライアントでエフェクトを使ってフェッチします。\n\n```js\n// bundle.js\nfunction Note({id}) {\n  const [note, setNote] = useState('');\n  // NOTE: loads *after* first render.\n  useEffect(() => {\n    fetch(`/api/notes/${id}`).then(data => {\n      setNote(data.note);\n    });\n  }, [id]);\n\n  return (\n    <div>\n      <Author id={note.authorId} />\n      <p>{note}</p>\n    </div>\n  );\n}\n\nfunction Author({id}) {\n  const [author, setAuthor] = useState('');\n  // NOTE: loads *after* Note renders.\n  // Causing an expensive client-server waterfall.\n  useEffect(() => {\n    fetch(`/api/authors/${id}`).then(data => {\n      setAuthor(data.author);\n    });\n  }, [id]);\n\n  return <span>By: {author.name}</span>;\n}\n```\n```js\n// api\nimport db from './database';\n\napp.get(`/api/notes/:id`, async (req, res) => {\n  const note = await db.notes.get(id);\n  res.send({note});\n});\n\napp.get(`/api/authors/:id`, async (req, res) => {\n  const author = await db.authors.get(id);\n  res.send({author});\n});\n```\n\nサーバコンポーネントを使用することで、コンポーネントの中で直接データを読み取ってレンダーできます。\n\n```js\nimport db from './database';\n\nasync function Note({id}) {\n  // NOTE: loads *during* render.\n  const note = await db.notes.get(id);\n  return (\n    <div>\n      <Author id={note.authorId} />\n      <p>{note}</p>\n    </div>\n  );\n}\n\nasync function Author({id}) {\n  // NOTE: loads *after* Note,\n  // but is fast if data is co-located.\n  const author = await db.authors.get(id);\n  return <span>By: {author.name}</span>;\n}\n```\n\nこの後バンドラは、データ、レンダー済みのサーバコンポーネント、および動的なクライアントコンポーネントをバンドルとして結合します。オプションとして、そのバンドルをサーバサイドレンダリング (SSR) して、ページの初期 HTML を作成できます。ページがロードされると、ブラウザには元の `Note` および `Author` コンポーネントの存在は見えません。クライアントにはレンダーされた出力のみが送信されます。\n\n```js\n<div>\n  <span>By: The React Team</span>\n  <p>React 19 is...</p>\n</div>\n```\n\nサーバコンポーネントをサーバから再フェッチして、サーバではデータにアクセスして再レンダーする、という形で、サーバコンポーネントを動的に扱うことができます。この新しいアプリケーションアーキテクチャは、サーバセントリックなマルチページアプリにおける単純な「リクエスト/レスポンス」式のメンタルモデルと、クライアントセントリックなシングルページアプリケーションにおけるシームレスな操作性を組み合わせ、両方の利点を提供できるものです。\n\n### サーバコンポーネントにインタラクティビティを追加する {/*adding-interactivity-to-server-components*/}\n\nサーバコンポーネントはブラウザに送信されないため、`useState` のようなインタラクティブな API を使用できません。サーバコンポーネントにインタラクティビティを追加するには、`\"use client\"` ディレクティブを使用してクライアントコンポーネントと組み合わせます。\n\n<Note>\n\n#### サーバコンポーネントのためのディレクティブはない {/*there-is-no-directive-for-server-components*/}\n\nよくある誤解として、サーバコンポーネントを \"use server\" を用いて定義するものだと考えるというものがあります。サーバコンポーネントにはディレクティブがありません。\"use server\" ディレクティブは、サーバ関数のためのものです。\n\n詳細については、[ディレクティブ](/reference/rsc/directives) のドキュメントをご覧ください。\n\n</Note>\n\n\n以下の例では、サーバコンポーネントである `Notes` がクライアントコンポーネントである `Expandable` をインポートしており、そこで state を使用して `expanded` をトグルしています。\n```js\n// Server Component\nimport Expandable from './Expandable';\n\nasync function Notes() {\n  const notes = await db.notes.getAll();\n  return (\n    <div>\n      {notes.map(note => (\n        <Expandable key={note.id}>\n          <p note={note} />\n        </Expandable>\n      ))}\n    </div>\n  )\n}\n```\n```js\n// Client Component\n\"use client\"\n\nexport default function Expandable({children}) {\n  const [expanded, setExpanded] = useState(false);\n  return (\n    <div>\n      <button\n        onClick={() => setExpanded(!expanded)}\n      >\n        Toggle\n      </button>\n      {expanded && children}\n    </div>\n  )\n}\n```\n\n動作としては、最初に `Notes` がサーバコンポーネントとしてレンダーされ、次にバンドラに `Expandable` というクライアントコンポーネントのバンドルを作成するよう指示しています。ブラウザ側では、クライアントコンポーネントには props 経由で渡されるサーバコンポーネントの出力だけが見えることになります。\n\n```js\n<head>\n  <!-- the bundle for Client Components -->\n  <script src=\"bundle.js\" />\n</head>\n<body>\n  <div>\n    <Expandable key={1}>\n      <p>this is the first note</p>\n    </Expandable>\n    <Expandable key={2}>\n      <p>this is the second note</p>\n    </Expandable>\n    <!--...-->\n  </div>\n</body>\n```\n\n### サーバコンポーネントでの非同期コンポーネント処理 {/*async-components-with-server-components*/}\n\nサーバコンポーネントにより、async/await を使用してコンポーネントを書くという新しい手法が導入されます。非同期コンポーネント内で `await` すると、React はサスペンド (suspend) し、プロミスが解決 (resolve) されるのを待ってからレンダーを再開します。サスペンスのストリーミングサポートのおかげで、これはサーバ／クライアントの境界をまたいで機能します。\n\nサーバでプロミスを作成し、それをクライアント側で待機することすら可能です。\n\n```js\n// Server Component\nimport db from './database';\n\nasync function Page({id}) {\n  // Will suspend the Server Component.\n  const note = await db.notes.get(id);\n\n  // NOTE: not awaited, will start here and await on the client.\n  const commentsPromise = db.comments.get(note.id);\n  return (\n    <div>\n      {note}\n      <Suspense fallback={<p>Loading Comments...</p>}>\n        <Comments commentsPromise={commentsPromise} />\n      </Suspense>\n    </div>\n  );\n}\n```\n\n```js\n// Client Component\n\"use client\";\nimport {use} from 'react';\n\nfunction Comments({commentsPromise}) {\n  // NOTE: this will resume the promise from the server.\n  // It will suspend until the data is available.\n  const comments = use(commentsPromise);\n  return comments.map(comment => <p>{comment}</p>);\n}\n```\n\n`note` の内容はページをレンダーするために重要なデータなので、サーバ側で `await` します。コメントは折りたたまれており優先度が低いため、サーバではプロミスを開始だけして、クライアントで `use` API を使用してそれを待機します。これによりクライアント側でサスペンドが起きますが、`note` の内容のレンダーをブロックしないで済みます。\n\n非同期コンポーネントはクライアントではサポートされていないため、プロミスの待機は `use` を使用して行います。\n"
  },
  {
    "path": "src/content/reference/rsc/server-functions.md",
    "content": "---\ntitle: サーバ関数\n---\n\n<RSC>\n\nサーバ関数は [React Server Components](/reference/rsc/server-components) で使用するための機能です。\n\n**補足**：2024 年 9 月までは、すべてのサーバ関数を「サーバアクション (Server Action)」と呼んでいました。サーバ関数が `action` プロパティに渡されるか `action` 内から呼び出されている場合は、それはサーバアクションとも呼べるでしょうが、すべてのサーバ関数がサーバアクションであるとは限りません。サーバ関数自体は様々な目的で使用できるものですので、それを反映するために本ドキュメントでは名前を変更しました。\n\n</RSC>\n\n<Intro>\n\nサーバ関数 (Server Function) を使用することで、サーバで実行される非同期関数をクライアントコンポーネントから呼び出すことができます。\n\n</Intro>\n\n<InlineToc />\n\n<Note>\n\n#### サーバ関数のサポートを追加する方法 {/*how-do-i-build-support-for-server-functions*/}\n\nReact 19 のサーバ関数は安定しており、マイナーバージョン間での破壊的変更はありませんが、サーバコンポーネントのバンドラやフレームワーク内でサーバアクションを実装するために使用される、基盤となる API は semver に従いません。React 19.x のマイナーバージョン間で変更が生じる可能性があります。\n\nサーバ関数をバンドラやフレームワークでサポートする場合は、特定の React バージョンに固定するか、Canary リリースを使用することをお勧めします。サーバ関数を実装するために使用される API を安定化させるため、今後もバンドラやフレームワークと連携を続けていきます。\n\n</Note>\n\nサーバ関数が `\"use server\"` ディレクティブを付けて定義されると、フレームワークは自動的にそのサーバ関数への参照を作成し、その参照をクライアントコンポーネントに渡します。クライアントでこの関数が呼び出されると、React はサーバにリクエストを送信して元の関数を実行し、その結果を返します。\n\nサーバアクションはサーバコンポーネント内で作成し、クライアントコンポーネントに props として渡すことができます。また、クライアントコンポーネントで直接インポートして使用することも可能です。\n\n## 使用法 {/*usage*/}\n\n### サーバコンポーネントでサーバ関数を作成する {/*creating-a-server-function-from-a-server-component*/}\n\nサーバコンポーネントは `\"use server\"` ディレクティブを使用してサーバ関数を定義できます。\n\n```js [[2, 7, \"'use server'\"], [1, 5, \"createNoteAction\"], [1, 12, \"createNoteAction\"]]\n// Server Component\nimport Button from './Button';\n\nfunction EmptyNote () {\n  async function createNoteAction() {\n    // Server Function\n    'use server';\n    \n    await db.notes.create();\n  }\n\n  return <Button onClick={createNoteAction}/>;\n}\n```\n\nReact はサーバコンポーネントである `EmptyNote` をレンダーする際に、`createNoteAction` 関数への参照を作成し、この参照をクライアントコンポーネントである `Button` に渡します。ボタンがクリックされると、React は渡された参照を使用してサーバにリクエストを送信し、`createNoteAction` 関数を実行します。\n\n```js {5}\n\"use client\";\n\nexport default function Button({onClick}) { \n  console.log(onClick); \n  // {$$typeof: Symbol.for(\"react.server.reference\"), $$id: 'createNoteAction'}\n  return <button onClick={() => onClick()}>Create Empty Note</button>\n}\n```\n\n詳細については、[`\"use server\"`](/reference/rsc/use-server) のドキュメントを参照してください。\n\n\n### クライアントコンポーネントからサーバ関数をインポートする {/*importing-server-functions-from-client-components*/}\n\nクライアントコンポーネントは `\"use server\"` ディレクティブを使用するファイルから、サーバ関数をインポートできます。\n\n```js [[1, 3, \"createNote\"]]\n\"use server\";\n\nexport async function createNote() {\n  await db.notes.create();\n}\n\n```\n\nバンドラがクライアントコンポーネントである `EmptyNote` をビルドする際に、バンドル内で `createNote` 関数への参照を作成します。`button` がクリックされると、React は渡された参照を使用してサーバにリクエストを送信し、`createNote` 関数を実行します。\n\n```js [[1, 2, \"createNote\"], [1, 5, \"createNote\"], [1, 7, \"createNote\"]]\n\"use client\";\nimport {createNote} from './actions';\n\nfunction EmptyNote() {\n  console.log(createNote);\n  // {$$typeof: Symbol.for(\"react.server.reference\"), $$id: 'createNote'}\n  <button onClick={() => createNote()} />\n}\n```\n\n詳細については、[`\"use server\"`](/reference/rsc/use-server) のドキュメントを参照してください。\n\n### サーバ関数をアクションと組み合わせる {/*server-functions-with-actions*/}\n\nクライアントでは、サーバ関数をアクション内で呼び出せます。\n\n```js [[1, 3, \"updateName\"]]\n\"use server\";\n\nexport async function updateName(name) {\n  if (!name) {\n    return {error: 'Name is required'};\n  }\n  await db.users.updateName(name);\n}\n```\n\n```js [[1, 3, \"updateName\"], [1, 13, \"updateName\"], [2, 11, \"submitAction\"],  [2, 23, \"submitAction\"]]\n\"use client\";\n\nimport {updateName} from './actions';\n\nfunction UpdateName() {\n  const [name, setName] = useState('');\n  const [error, setError] = useState(null);\n\n  const [isPending, startTransition] = useTransition();\n\n  const submitAction = async () => {\n    startTransition(async () => {\n      const {error} = await updateName(name);\n      if (error) {\n        setError(error);\n      } else {\n        setName('');\n      }\n    })\n  }\n  \n  return (\n    <form action={submitAction}>\n      <input type=\"text\" name=\"name\" disabled={isPending}/>\n      {error && <span>Failed: {error}</span>}\n    </form>\n  )\n}\n```\n\nこのようにクライアント側のアクションでラップすることで、サーバ関数由来の `isPending` state にアクセスできるようになります。\n\n詳細については、[`<form>` 外でサーバ関数を呼び出す](/reference/rsc/use-server#calling-a-server-function-outside-of-form)を参照してください。\n\n### サーバ関数とフォームアクション {/*using-server-functions-with-form-actions*/}\n\nサーバ関数は React 19 の新しいフォーム関連機能と連携して動作します。\n\nフォームにサーバ関数を渡すことで、自動的にフォームをサーバに送信できます。\n\n\n```js [[1, 3, \"updateName\"], [1, 7, \"updateName\"]]\n\"use client\";\n\nimport {updateName} from './actions';\n\nfunction UpdateName() {\n  return (\n    <form action={updateName}>\n      <input type=\"text\" name=\"name\" />\n    </form>\n  )\n}\n```\n\nフォームの送信が成功すると、React は自動的にフォームをリセットします。`useActionState` を追加して、進行中 (pending) state や最終的なレスポンスにアクセスしたり、プログレッシブエンハンスメント (progressive enhancement) をサポートしたりすることが可能です。\n\n詳細については、[フォーム内でのサーバ関数](/reference/rsc/use-server#server-functions-in-forms)を参照してください。\n\n### `useActionState` とサーバ関数 {/*server-functions-with-use-action-state*/}\n\n`useActionState` とサーバ関数を組み合わせることで、アクションの進行中 state と最後に返されたレスポンスにアクセスする、という一般的なユースケースに対応できます。\n\n```js [[1, 3, \"updateName\"], [1, 6, \"updateName\"], [2, 6, \"submitAction\"], [2, 9, \"submitAction\"]]\n\"use client\";\n\nimport {updateName} from './actions';\n\nfunction UpdateName() {\n  const [state, submitAction, isPending] = useActionState(updateName, {error: null});\n\n  return (\n    <form action={submitAction}>\n      <input type=\"text\" name=\"name\" disabled={isPending}/>\n      {state.error && <span>Failed: {state.error}</span>}\n    </form>\n  );\n}\n```\n\nサーバ関数と `useActionState` を使用する場合、React はハイドレーションの完了前に実行されたフォーム送信を自動的に再現します。これにより、ユーザはアプリのハイドレーションが起きる前からアプリを操作できるようになります。\n\n詳細については、[`useActionState`](/reference/react/useActionState) のドキュメントを参照してください。\n\n### `useActionState` を使用したプログレッシブエンハンスメント {/*progressive-enhancement-with-useactionstate*/}\n\nサーバ関数は `useActionState` の第 3 引数を使用してプログレッシブエンハンスメントもサポートします。\n\n```js [[1, 3, \"updateName\"], [1, 6, \"updateName\"], [2, 6, \"/name/update\"], [3, 6, \"submitAction\"], [3, 9, \"submitAction\"]]\n\"use client\";\n\nimport {updateName} from './actions';\n\nfunction UpdateName() {\n  const [, submitAction] = useActionState(updateName, null, `/name/update`);\n\n  return (\n    <form action={submitAction}>\n      ...\n    </form>\n  );\n}\n```\n\n<CodeStep step={2}>パーマリンク</CodeStep>が `useActionState` に渡された場合、JavaScript バンドルが読み込まれる前にフォームが送信されると、React はこの渡された URL にリダイレクトします。\n\n詳しくは、[`useActionState`](/reference/react/useActionState) のドキュメントを参照してください。\n"
  },
  {
    "path": "src/content/reference/rsc/use-client.md",
    "content": "---\ntitle: \"'use client'\"\ntitleForTitleTag: \"'use client' ディレクティブ\"\n---\n\n<RSC>\n\n`'use client'` は [React Server Components](/reference/rsc/server-components) 用の機能です。\n\n</RSC>\n\n\n<Intro>\n\n`'use client'` を使い、どのコードがクライアントで実行されるかをマークします。\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `'use client'` {/*use-client*/}\n\nファイルのトップに `'use client'` を加えることで、当該モジュールとそれが連動してインポートしている依存モジュールがクライアントコードであるとマークします。\n\n```js {1}\n'use client';\n\nimport { useState } from 'react';\nimport { formatDate } from './formatters';\nimport Button from './button';\n\nexport default function RichTextEditor({ timestamp, text }) {\n  const date = formatDate(timestamp);\n  // ...\n  const editButton = <Button />;\n  // ...\n}\n```\n\n`'use client'` でマークされているファイルがサーバコンポーネントからインポートされた場合、[互換性のあるバンドラ](/learn/creating-a-react-app#full-stack-frameworks)は当該モジュールのインポートを、サーバで実行されるコードとクライアントで実行されるコードの境界として扱います。\n\n上記では `formatDate` と `Button` は `RichTextEditor` が依存するモジュールですので、これらのモジュール自体に `'use client'` ディレクティブが含まれているかどうかに関わらず、これらもクライアントで評価されます。ある単一のモジュールが、サーバコードからインポートされた場合はサーバで、クライアントコードからインポートされた場合はクライアントで評価される場合があることに注意してください。\n\n#### 注意点 {/*caveats*/}\n\n* `'use client'` はファイルの冒頭、すなわちインポートや他のコードより先になければなりません（コメントは OK です）。シングルクォートまたはダブルクォートで書かれていなければならず、バックティックは無効です。\n* `'use client'` モジュールが別のクライアントレンダーされるモジュールからインポートされた場合、ディレクティブの効果はありません。\n* コンポーネントモジュールに `'use client'` ディレクティブが含まれている場合、そのコンポーネントは必ずクライアントコンポーネントであることが保証されます。しかしコンポーネントに直接 `'use client'` ディレクティブがなくとも、クライアントで評価されることがあります。\n\t* コンポーネントがクライアントコンポーネントと見なされるのは、それが `'use client'` ディレクティブを含むモジュールで定義されている場合、またはそれが `'use client'` ディレクティブを含むモジュールの間接的な依存モジュールである場合です。それ以外の場合、サーバコンポーネントとなります。\n* クライアントで評価されるようマークされるコードとはコンポーネントに限りません。クライアントモジュールのサブツリーに含まれるすべてのコードは、クライアントに送信され、クライアントで実行されます。\n* サーバで評価されるモジュールが `'use client'` のモジュールから値をインポートする場合、その値は React コンポーネントであるか、またはクライアントコンポーネントに渡せるよう[サポート済のシリアライズ可能な props の型](#passing-props-from-server-to-client-components)のいずれかでなければなりません。それ以外の方法で使用すると例外がスローされます。\n\n### `'use client'` がクライアントコードをマークする方法 {/*how-use-client-marks-client-code*/}\n\nReact アプリでは、コンポーネントはしばしば別々のファイル、すなわち[モジュール](/learn/importing-and-exporting-components#exporting-and-importing-a-component)に分割されます。\n\nReact Server Components を使用するアプリでは、デフォルトでアプリはサーバでレンダーされます。`'use client'` は[モジュール依存関係ツリー](/learn/understanding-your-ui-as-a-tree#the-module-dependency-tree)にサーバ・クライアント境界を導入、つまり実質的にはクライアントモジュールのサブツリーの作成を行います。\n\nこれをより具体的に示すために、以下の React Server Components アプリを考えてみましょう。\n\n<Sandpack>\n\n```js src/App.js\nimport FancyText from './FancyText';\nimport InspirationGenerator from './InspirationGenerator';\nimport Copyright from './Copyright';\n\nexport default function App() {\n  return (\n    <>\n      <FancyText title text=\"Get Inspired App\" />\n      <InspirationGenerator>\n        <Copyright year={2004} />\n      </InspirationGenerator>\n    </>\n  );\n}\n\n```\n\n```js src/FancyText.js\nexport default function FancyText({title, text}) {\n  return title\n    ? <h1 className='fancy title'>{text}</h1>\n    : <h3 className='fancy cursive'>{text}</h3>\n}\n```\n\n```js src/InspirationGenerator.js\n'use client';\n\nimport { useState } from 'react';\nimport inspirations from './inspirations';\nimport FancyText from './FancyText';\n\nexport default function InspirationGenerator({children}) {\n  const [index, setIndex] = useState(0);\n  const quote = inspirations[index];\n  const next = () => setIndex((index + 1) % inspirations.length);\n\n  return (\n    <>\n      <p>Your inspirational quote is:</p>\n      <FancyText text={quote} />\n      <button onClick={next}>Inspire me again</button>\n      {children}\n    </>\n  );\n}\n```\n\n```js src/Copyright.js\nexport default function Copyright({year}) {\n  return <p className='small'>©️ {year}</p>;\n}\n```\n\n```js src/inspirations.js\nexport default [\n  \"Don’t let yesterday take up too much of today.” — Will Rogers\",\n  \"Ambition is putting a ladder against the sky.\",\n  \"A joy that's shared is a joy made double.\",\n];\n```\n\n```css\n.fancy {\n  font-family: 'Georgia';\n}\n.title {\n  color: #007AA3;\n  text-decoration: underline;\n}\n.cursive {\n  font-style: italic;\n}\n.small {\n  font-size: 10px;\n}\n```\n\n</Sandpack>\n\nこのサンプルアプリのモジュール依存関係ツリーでは、`InspirationGenerator.js` に書かれた `'use client'` ディレクティブが、当該モジュールとそのすべての間接的な依存モジュールをクライアントモジュールとしてマークします。これで `InspirationGenerator.js` から始まるサブツリー全体がクライアントモジュールとなるのです。\n\n<Diagram name=\"use_client_module_dependency\" height={250} width={545} alt=\"トップノードがモジュール 'App.js' を表す木構造のグラフ。'App.js'には 'Copyright.js'、'FancyText.js'、'InspirationGenerator.js' の 3 つの子ノードがある。'InspirationGenerator.js'には 'FancyText.js'と'inspirations.js' の 2 つの子ノードがある。'InspirationGenerator.js'を含む下のノードには黄色い背景色が付けられており、'InspirationGenerator.js'の 'use client' ディレクティブによってこのサブグラフがクライアント側でレンダーされることを示している。\">\n`'use client'` は React Server Components アプリのモジュール依存関係ツリーを分割し、`InspirationGenerator.js` とそのすべての依存モジュールをクライアントレンダーされるものとしてマークする\n</Diagram>\n\nレンダー中、フレームワークはルートコンポーネントから始め、[レンダーツリー](/learn/understanding-your-ui-as-a-tree#the-render-tree)を順にレンダーしていきますが、その際にクライアントとマークされたものからインポートされたコードの評価を選択的に除外します。\n\nその後レンダーツリーのうちサーバでレンダーされた部分が、クライアントに送信されます。クライアントは、ダウンロードしたクライアントコードを用いて、ツリーの残りの部分のレンダーを完了します。\n\n<Diagram name=\"use_client_render_tree\" height={250} width={500} alt=\"各ノードがコンポーネントを表し、その子要素を子コンポーネントとして表すツリーグラフ。トップレベルのノードは 'App' とラベル付けされ、2つの子コンポーネント 'InspirationGenerator' と 'FancyText' を持っています。'InspirationGenerator' は2つの子コンポーネント、'FancyText' と 'Copyright' を持っています。'InspirationGenerator' とその子コンポーネント 'FancyText' はクライアントレンダリングされるとマークされています。\">\nReact Server Components アプリのレンダーツリー。`InspirationGenerator` とその子コンポーネント `FancyText` は、クライアントとマークされたコードからエクスポートされたコンポーネントであるため、クライアントコンポーネントと見なされる\n</Diagram>\n\nここで以下の定義を導入しましょう。\n\n* **クライアントコンポーネント**とは、レンダーツリーの中の、クライアントでレンダーされるコンポーネントです。\n* **サーバコンポーネント**とは、レンダーツリーの中の、サーバでレンダーされるコンポーネントです。\n\n上記のサンプルアプリでは、`App`、`FancyText`、`Copyright` はすべてサーバでレンダーされるのでサーバコンポーネントと見なされます。`InspirationGenerator.js` とその間接的な依存モジュールはクライアントコードとしてマークされているため、コンポーネント `InspirationGenerator` とその子コンポーネントである `FancyText` はクライアントコンポーネントとなります。\n\n<DeepDive>\n#### `FancyText` がサーバコンポーネントでありクライアントコンポーネントでもある理由 {/*how-is-fancytext-both-a-server-and-a-client-component*/}\n\n上記の定義によれば、コンポーネント `FancyText` は、サーバコンポーネントでもありクライアントコンポーネントでもあることになります。どういうことでしょうか？\n\nまずは \"コンポーネント\" という用語があまり厳密ではないことを明示的に意識しましょう。\"コンポーネント\" とは以下の 2 つの意味で使用されます。\n\n1. 「コンポーネント」は**コンポーネントの定義**を指すことがあります。ほとんどの場合、これは関数です。\n\n```js\n// This is a definition of a component\nfunction MyComponent() {\n  return <p>My Component</p>\n}\n```\n\n2. 「コンポーネント」はまた、その定義に従って**使用されている個々のコンポーネント**を指すこともあります。\n```js\nimport MyComponent from './MyComponent';\n\nfunction App() {\n  // This is a usage of a component\n  return <MyComponent />;\n}\n```\n\nここが厳密でなくとも大抵の場合は概念を説明する際に問題とはなりませんが、この場合はそうではありません。\n\nサーバコンポーネントやクライアントコンポーネントについて話すとき、コンポーネントの個々の使用法を指しています。\n\n* コンポーネントが `'use client'` ディレクティブを含んだモジュールで定義されている場合や、コンポーネントが他のクライアントコンポーネントからインポートされコールされる場合、そのコンポーネントの使用法はクライアントコンポーネントであるということになります。\n* それ以外の場合、そのコンポーネントの使用法はサーバコンポーネントだということになります。\n\n\n<Diagram name=\"use_client_render_tree\" height={150} width={450} alt=\"各ノードがコンポーネントを表し、その子供を子コンポーネントとして表すツリーグラフ。トップレベルのノードは 'App' とラベル付けされており、'InspirationGenerator' と 'FancyText' の 2 つの子コンポーネントを持っている。'InspirationGenerator' は 'FancyText' と 'Copyright' の 2 つの子コンポーネントを持っている。'InspirationGenerator' とその子コンポーネント 'FancyText' はクライアントでレンダリングされるとマークされている。\">コンポーネントの「使用法」を表すのがレンダーツリー</Diagram>\n\n`FancyText` の問題に戻りましょう。分かるのは、コンポーネントの定義として `'use client'` ディレクティブがないこと、そして使用法として 2 種類あることです。\n\n`FancyText` が `App` の子として使用されている場合、その使用法はサーバコンポーネントです。`FancyText` が `InspirationGenerator` の下でインポートされて呼び出されている場合、`InspirationGenerator` に `'use client'` ディレクティブが含まれているため、その `FancyText` の使用法はクライアントコンポーネントとなります。\n\nつまり、`FancyText` のコンポーネント定義がサーバ上で評価される一方で、クライアントコンポーネントとして使用されるためクライアントにダウンロードもされる、ということになります。\n\n</DeepDive>\n\n<DeepDive>\n\n#### `Copyright` がサーバコンポーネントである理由 {/*why-is-copyright-a-server-component*/}\n\n`Copyright` はクライアントコンポーネントである `InspirationGenerator` の子としてレンダーされているので、それがサーバコンポーネントになっていることに驚くかもしれません。\n\n`'use client'` がサーバとクライアントのコードの境界を、レンダーツリーではなく*モジュール依存関係ツリー*上で定義することを思い出してください。\n\n<Diagram name=\"use_client_module_dependency\" height={200} width={500} alt=\"トップノードがモジュール 'App.js' を表すツリーグラフ。'App.js' には 'Copyright.js'、'FancyText.js'、'InspirationGenerator.js' の 3 つの子ノードがある。'InspirationGenerator.js' には 'FancyText.js' と 'inspirations.js' の 2 つの子ノードがある。'InspirationGenerator.js' 自体とそれ以下のノードは背景が黄色になっており、'InspirationGenerator.js' に 'use client' ディレクティブがあるためこのサブグラフがクライアントでレンダーされることを示している。\">\n`'use client'` はモジュール依存ツリー上でサーバとクライアントのコードの境界を定義する\n</Diagram>\n\nモジュール依存関係ツリー上では、`App.js` が `Copyright.js` モジュールから `Copyright` をインポートして呼び出していることがわかります。`Copyright.js` には `'use client'` ディレクティブが含まれていないため、このコンポーネントはサーバレンダーで使用されています。`App` はルートコンポーネントでありサーバ上でレンダーされるからです。\n\nクライアントコンポーネントがサーバコンポーネントをレンダーできるのは、JSX を props として渡すことができるためです。上記の例の場合、`InspirationGenerator` は [children](/learn/passing-props-to-a-component#passing-jsx-as-children) として `Copyright` を受け取ります。しかし、`InspirationGenerator` モジュールは `Copyright` モジュールを直接インポートしたり、コンポーネントを呼び出したりしているわけではありません。それらはすべて `App` によって行われます。実際、`Copyright` コンポーネントは `InspirationGenerator` のレンダーすら始まらないうちに完全に実行されます。\n\nつまり、コンポーネント間に親子関係があるからといって、同じ環境でレンダーされることが保証されるわけではない、ということを覚えておきましょう。\n\n</DeepDive>\n\n### `'use client'` をいつ使うべきか {/*when-to-use-use-client*/}\n\n`'use client'` を使うことで、どのコンポーネントがクライアントコンポーネントであるかを決定できます。デフォルトはサーバコンポーネントですので、その利点と制限事項を簡単に概観することで、何をクライアントレンダーとしてマークする必要があるかを判断できるようにしましょう。\n\n話を簡単にするためサーバコンポーネントについて説明しますが、サーバで実行されるアプリのすべてのコードに同じ原則が適用されます。\n\n#### サーバコンポーネントの利点 {/*advantages*/}\n* サーバコンポーネントにより、クライアントに送信され実行されるコードの量を減らすことができます。バンドルされてクライアントで評価されるのはクライアントモジュールだけです。\n* サーバコンポーネントにはサーバ上で実行されることに伴う利点があります。ローカルファイルシステムにアクセスでき、データフェッチやネットワークリクエストのレイテンシが低い可能性があります。\n\n#### サーバコンポーネントの制限事項 {/*limitations*/}\n* サーバコンポーネントはユーザによるインタラクションをサポートできません。イベントハンドラはクライアントで登録されトリガされる必要があるためです。\n\t* 例えば、`onClick` のようなイベントハンドラはクライアントコンポーネントでのみ定義できます。\n* サーバコンポーネントではほとんどのフックを使用できません。\n\t* サーバコンポーネントがレンダーされるとき、その出力は本質的にクライアントでレンダーすべきコンポーネントのリストになります。サーバコンポーネントはレンダー後にメモリに残らないため、state を持つことはできません。\n\n### サーバコンポーネントから返せるシリアライズ可能な型 {/*serializable-types*/}\n\nReact アプリケーションでは一般的に、親コンポーネントから子コンポーネントにデータを渡します。これらが異なる環境でレンダーされるため、サーバコンポーネントからクライアントコンポーネントにデータを渡す際には特別な配慮が必要です。\n\nサーバコンポーネントからクライアントコンポーネントに渡される props の値は、シリアライズ可能 (serializable) である必要があります。\n\nシリアライズ可能な props には以下のものがあります：\n* プリミティブ\n\t* [文字列](https://developer.mozilla.org/en-US/docs/Glossary/String)\n\t* [数値](https://developer.mozilla.org/en-US/docs/Glossary/Number)\n\t* [bigint](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)\n\t* [ブーリアン](https://developer.mozilla.org/en-US/docs/Glossary/Boolean)\n\t* [undefined](https://developer.mozilla.org/en-US/docs/Glossary/Undefined)\n\t* [null](https://developer.mozilla.org/en-US/docs/Glossary/Null)\n\t* [シンボル](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol)、ただし [`Symbol.for`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for) を通じてグローバルシンボルレジストリに登録されたシンボルのみ\n* シリアライズ可能な値を含んだ Iterable\n\t* [文字列](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)\n\t* [配列](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)\n\t* [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)\n\t* [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set)\n\t* [TypedArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) と [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)\n* [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)\n* プレーンな[オブジェクト](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object): [オブジェクト初期化子](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer)で作成され、シリアライズ可能なプロパティを持つもの\n* [サーバ関数 (server function)](/reference/rsc/server-functions) としての関数\n* クライアントまたはサーバコンポーネントの要素（JSX）\n* [プロミス](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)\n\n以下のものはサポートされていません。\n* クライアントとマークされたモジュールからエクスポートされていない、または [`'use server'`](/reference/rsc/use-server) でマークされていない[関数](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function)\n* [クラス](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Classes_in_JavaScript)\n* 任意のクラスのインスタンス（上記の組み込みクラスを除く）や、[null プロトタイプのオブジェクト](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#null-prototype_objects)\n* グローバルに登録されていないシンボル、例：`Symbol('my new symbol')`\n\n\n## 使用法 {/*usage*/}\n\n### ユーザ操作や state を実装する {/*building-with-interactivity-and-state*/}\n\n<Sandpack>\n\n```js src/App.js\n'use client';\n\nimport { useState } from 'react';\n\nexport default function Counter({initialValue = 0}) {\n  const [countValue, setCountValue] = useState(initialValue);\n  const increment = () => setCountValue(countValue + 1);\n  const decrement = () => setCountValue(countValue - 1);\n  return (\n    <>\n      <h2>Count Value: {countValue}</h2>\n      <button onClick={increment}>+1</button>\n      <button onClick={decrement}>-1</button>\n    </>\n  );\n}\n```\n\n</Sandpack>\n\n`Counter` は値を増減させるために `useState` フックとイベントハンドラを必要とするので、このコンポーネントはクライアントコンポーネントでなければならず、ファイル冒頭に `'use client'` ディレクティブが必要です。\n\n対照的に、インタラクションなしで UI をレンダーするコンポーネントはクライアントコンポーネントである必要はありません。\n\n```js\nimport { readFile } from 'node:fs/promises';\nimport Counter from './Counter';\n\nexport default async function CounterContainer() {\n  const initialValue = await readFile('/path/to/counter_value');\n  return <Counter initialValue={initialValue} />\n}\n```\n\n例えば、`Counter` の親コンポーネントである `CounterContainer` は、インタラクティブではなく state を使用しないため、`'use client'` は必要ありません。むしろ `CounterContainer` はサーバコンポーネントでなければなりません。サーバ上のローカルファイルシステムから読み取っており、それが可能なのはサーバコンポーネントだけだからです。\n\nまた、サーバやクライアント固有の機能を使用しないため、レンダーされるべき場所に関して関知しないコンポーネントもあります。先ほどの例でいうと、`FancyText` がそのようなコンポーネントです。\n\n```js\nexport default function FancyText({title, text}) {\n  return title\n    ? <h1 className='fancy title'>{text}</h1>\n    : <h3 className='fancy cursive'>{text}</h3>\n}\n```\n\nこのような場合、`'use client'` ディレクティブを追加しません。その結果、サーバコンポーネントから参照されたときに、`FancyText` の（ソースコードではなく）*出力結果*が、ブラウザに送信されます。先ほどのアプリの例で示したように、`FancyText` はどこからインポートされ、どこで使用されるかによって、サーバコンポーネントになることもクライアントコンポーネントになることもあるのです。\n\nしかし、`FancyText` の HTML 出力が（依存モジュールも含んだ）ソースコードに比べて大きいような場合は、必ずクライアントコンポーネントとなるように強制する方が効率的かもしれません。一例として、長い SVG パス文字列を返すようなコンポーネントは、強制的にクライアントコンポーネントとしてレンダーする方が効率的である可能性があるでしょう。\n\n### クライアント API の使用 {/*using-client-apis*/}\n\nあなたの React アプリでは、Web ストレージ、オーディオやビデオの操作、デバイスハードウェア、[その他もろもろ](https://developer.mozilla.org/en-US/docs/Web/API)のブラウザ API といった、クライアント固有の API を使用することがあるでしょう。\n\n以下の例では、コンポーネントは [DOM API](https://developer.mozilla.org/en-US/docs/Glossary/DOM) を使用して [`canvas`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas) 要素を操作しています。これらの API はブラウザでのみ利用可能なので、クライアントコンポーネントとしてマークする必要があります。\n\n```js\n'use client';\n\nimport {useRef, useEffect} from 'react';\n\nexport default function Circle() {\n  const ref = useRef(null);\n  useLayoutEffect(() => {\n    const canvas = ref.current;\n    const context = canvas.getContext('2d');\n    context.reset();\n    context.beginPath();\n    context.arc(100, 75, 50, 0, 2 * Math.PI);\n    context.stroke();\n  });\n  return <canvas ref={ref} />;\n}\n```\n\n### サードパーティライブラリの使用 {/*using-third-party-libraries*/}\n\nReact アプリでは、一般的な UI パターンやロジックを処理するためにサードパーティのライブラリがよく用いられます。\n\nこれらのライブラリがコンポーネントのフックやクライアント API に依存することがあります。以下のような React API を使用するサードパーティのコンポーネントは、クライアント上で実行する必要があります。\n* [createContext](/reference/react/createContext)\n* [`react`](/reference/react/hooks) と [`react-dom`](/reference/react-dom/hooks) のフック、ただし [`use`](/reference/react/use) と [`useId`](/reference/react/useId) は除く\n* [forwardRef](/reference/react/forwardRef)\n* [memo](/reference/react/memo)\n* [startTransition](/reference/react/startTransition)\n* DOM の挿入やネイティブプラットフォームビューなどその他のクライアント API\n\nライブラリがサーバコンポーネントと互換性を有するように更新済みであれば、中に既に `'use client'` マーカが含まれていますので、サーバコンポーネントから直接使用することができます。ライブラリが更新されていない場合、あるいはコンポーネントが受け取る props にクライアントでのみ指定できるイベントハンドラのようなものが含まれている場合、サードパーティのクライアントコンポーネントとそれを使用したいサーバコンポーネントの間に、自分でクライアントコンポーネントファイルを追加する必要があるかもしれません。\n\n[TODO]: <> (Troubleshooting - need use-cases)\n"
  },
  {
    "path": "src/content/reference/rsc/use-server.md",
    "content": "---\ntitle: \"'use server'\"\ntitleForTitleTag: \"'use server' ディレクティブ\"\n---\n\n<RSC>\n\n`'use server'` は [React Server Components](/reference/rsc/server-components) 用の機能です。\n\n</RSC>\n\n\n<Intro>\n\n`'use server'` は、クライアントサイドのコードから呼び出せる、サーバサイドの関数をマークします。\n\n</Intro>\n\n<InlineToc />\n\n---\n\n## リファレンス {/*reference*/}\n\n### `'use server'` {/*use-server*/}\n\n非同期 (async) 関数の本体の冒頭に `'use server';` を追加することで、その関数がクライアントから実行可能であるとマークします。そのような関数のことを[サーバ関数 (Server Function)](/reference/rsc/server-functions) と呼びます。\n\n```js {2}\nasync function addToCart(data) {\n  'use server';\n  // ...\n}\n```\n\nクライアント上でサーバ関数を呼び出すと、渡された引数のシリアライズされたコピーを含んだネットワークリクエストがサーバに送信されます。サーバ関数が値を返す場合は、その値がシリアライズされてクライアントに返されます。\n\n個々の関数に `'use server'` をマークする代わりに、このディレクティブをファイルの先頭に追加することもできます。その場合はそのファイル内のすべてのエクスポートが、クライアントコードでインポートされる場合も含み、あらゆる場所で使用できるサーバアクションとしてマークされます。\n\n#### 注意点 {/*caveats*/}\n* `'use server'` は、関数やモジュールの冒頭、つまりインポートも含む他のコードよりも上にある必要があります（ディレクティブの上にコメントを書くことは OK）。シングルクォートまたはダブルクォートで書かれていなければならず、バックティックは無効です。\n* `'use server'` は、サーバサイドのファイルでのみ使用できます。結果として得られるサーバ関数は、props を通じてクライアントコンポーネントに渡せるようになります。サポートされている[シリアライズ可能な型](#serializable-parameters-and-return-values)を参照してください。\n* [クライアントコード](/reference/rsc/use-client)からサーバ関数をインポートする場合は、ディレクティブをモジュールレベルで使用する必要があります。\n* 内部で使用されるネットワーク呼び出しは常に非同期であるため、`'use server'` は非同期関数でのみ使用できます。\n* サーバ関数への引数は常に信頼できない入力として扱い、あらゆるデータ書き換えを検証してください。[セキュリティに関する考慮事項](#security)を参照してください。\n* サーバ関数は[トランジション](/reference/react/useTransition)の中で呼び出すようにしてください。サーバ関数が [`<form action>`](/reference/react-dom/components/form#props) または [`formAction`](/reference/react-dom/components/input#props) に渡される場合、自動的にトランジション内で呼び出されます。\n* サーバ関数は、サーバ側の状態を書き換える、更新目的のために設計されています。データの取得には推奨されません。したがって、サーバ関数を実装するフレームワークは通常、一度にひとつのアクションのみを処理し、返り値をキャッシュしないようにします。\n\n### セキュリティについての考慮事項 {/*security*/}\n\nサーバ関数への引数は、完全にクライアントで制御されるものです。セキュリティのため、入力は常に信頼できないものとして扱い、引数の検証やエスケープを適切に行ってください。\n\nあらゆるサーバ関数において、ログイン済みのユーザにその処理の実行が許可されているのかを確認するようにしてください。\n\n<Wip>\n\nサーバ関数から機密データが送信されるのを防ぐために、ユニークな値やオブジェクトがクライアントコードに渡されるのを防ぐ実験的な汚染 API (taint API) があります。\n\n[experimental_taintUniqueValue](/reference/react/experimental_taintUniqueValue) と [experimental_taintObjectReference](/reference/react/experimental_taintObjectReference) を参照してください。\n\n</Wip>\n\n### シリアライズ可能な引数と返り値 {/*serializable-parameters-and-return-values*/}\n\nクライアントコードのサーバ関数呼び出しはネットワーク経由で行われるため、関数に渡すあらゆる引数はシリアライズ可能である必要があります。\n\n以下は、サーバ関数の引数としてサポートされる型です。\n\n* プリミティブ\n\t* [文字列](https://developer.mozilla.org/en-US/docs/Glossary/String)\n\t* [数値](https://developer.mozilla.org/en-US/docs/Glossary/Number)\n\t* [bigint](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)\n\t* [ブーリアン](https://developer.mozilla.org/en-US/docs/Glossary/Boolean)\n\t* [undefined](https://developer.mozilla.org/en-US/docs/Glossary/Undefined)\n\t* [null](https://developer.mozilla.org/en-US/docs/Glossary/Null)\n\t* [シンボル](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol)、ただし [`Symbol.for`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for) を通じてグローバルシンボルレジストリに登録されたシンボルのみ\n* シリアライズ可能な値を含んだ Iterable\n\t* [文字列](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)\n\t* [配列](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)\n\t* [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)\n\t* [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set)\n\t* [TypedArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) と [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)\n* [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)\n* [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) のインスタンス\n* プレーンな[オブジェクト](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object): [オブジェクト初期化子](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer)で作成され、シリアライズ可能なプロパティを持つもの\n* それ自体がサーバ関数である関数\n* [プロミス](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)\n\n以下のものはサポートされていません。\n* React 要素すなわち [JSX](/learn/writing-markup-with-jsx)\n* 関数。関数コンポーネントや、それ自体がサーバ関数でない他のあらゆる関数を含む。\n* [クラス](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Classes_in_JavaScript)\n* 任意のクラスのインスタンス（上記の組み込みクラスを除く）や、[null プロトタイプのオブジェクト](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#null-prototype_objects)\n* グローバルに登録されていないシンボル、例：`Symbol('my new symbol')`\n* イベントハンドラが受け取るイベント\n\n\nサポートされるシリアライズ可能な返り値は、クライアントコンポーネントに渡せる[シリアライズ可能な props](/reference/rsc/use-client#serializable-types) の型と同じです。\n\n\n## 使用法 {/*usage*/}\n\n### フォームでサーバ関数を使用する {/*server-functions-in-forms*/}\n\nサーバ関数の最も一般的なユースケースは、データを更新するための関数呼び出しです。ブラウザ上においては [HTML フォーム要素](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)が、ユーザが更新処理を送信する際の伝統的な方法です。React Server Components により、[フォーム](/reference/react-dom/components/form)に書かれたサーバ関数に対する第 1 級サポートが導入されます。\n\n以下は、ユーザがユーザ名をリクエストできるフォームです。\n\n```js [[1, 3, \"formData\"]]\n// App.js\n\nasync function requestUsername(formData) {\n  'use server';\n  const username = formData.get('username');\n  // ...\n}\n\nexport default function App() {\n  return (\n    <form action={requestUsername}>\n      <input type=\"text\" name=\"username\" />\n      <button type=\"submit\">Request</button>\n    </form>\n  );\n}\n```\n\nこの例では、`requestUsername` は `<form>` に渡されるサーバ関数となります。ユーザがこのフォームを送信すると、サーバ関数 `requestUsername` へのネットワークリクエストが発生します。フォーム内でサーバ関数を呼び出すとき、React はフォームの <CodeStep step={1}>[FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData)</CodeStep> をサーバ関数の最初の引数として提供します。\n\nフォームの `action` にサーバ関数を渡すことで、React によるフォームの[プログレッシブエンハンスメント (progressive enhancement)](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement) が有効になります。つまり JavaScript バンドルがロードされる前にフォームを送信できるようになるということです。\n\n#### フォームでの返り値の取り扱い {/*handling-return-values*/}\n\n上記のユーザ名リクエストフォームでは、ユーザ名が利用できない可能性もあります。`requestUsername` は成功したか失敗したかを伝えられるべきです。\n\nプログレッシブエンハンスメントをサポートしつつサーバ関数の結果に基づいて UI を更新するには、[`useActionState`](/reference/react/useActionState) を使用します。\n\n```js\n// requestUsername.js\n'use server';\n\nexport default async function requestUsername(formData) {\n  const username = formData.get('username');\n  if (canRequest(username)) {\n    // ...\n    return 'successful';\n  }\n  return 'failed';\n}\n```\n\n```js {4,8}, [[2, 2, \"'use client'\"]]\n// UsernameForm.js\n'use client';\n\nimport { useActionState } from 'react';\nimport requestUsername from './requestUsername';\n\nfunction UsernameForm() {\n  const [state, action] = useActionState(requestUsername, null, 'n/a');\n\n  return (\n    <>\n      <form action={action}>\n        <input type=\"text\" name=\"username\" />\n        <button type=\"submit\">Request</button>\n      </form>\n      <p>Last submission request returned: {state}</p>\n    </>\n  );\n}\n```\n\nほとんどのフックと同様に、`useActionState` は<CodeStep step={1}>[クライアントコード](/reference/rsc/use-client)</CodeStep>内でしか呼び出せないことに注意してください。\n\n### `<form>` の外部でサーバ関数を呼び出す {/*calling-a-server-function-outside-of-form*/}\n\nサーバ関数とはサーバ側の公開エンドポイントとなるため、クライアントコードのどこからでも呼び出すことができます。\n\n[フォーム](/reference/react-dom/components/form)の外部でサーバ関数を使用する場合、[トランジション](/reference/react/useTransition)内でサーバ関数を呼び出すようにしてください。これによりローディングインジケータを表示したり、[楽観的に state 更新結果を表示](/reference/react/useOptimistic)したり、予期せぬエラーを処理したりすることができるようになります。フォームではサーバ関数は自動的にトランジション内にラップされます。\n\n```js {9-12}\nimport incrementLike from './actions';\nimport { useState, useTransition } from 'react';\n\nfunction LikeButton() {\n  const [isPending, startTransition] = useTransition();\n  const [likeCount, setLikeCount] = useState(0);\n\n  const onClick = () => {\n    startTransition(async () => {\n      const currentCount = await incrementLike();\n      setLikeCount(currentCount);\n    });\n  };\n\n  return (\n    <>\n      <p>Total Likes: {likeCount}</p>\n      <button onClick={onClick} disabled={isPending}>Like</button>;\n    </>\n  );\n}\n```\n\n```js\n// actions.js\n'use server';\n\nlet likeCount = 0;\nexport default async function incrementLike() {\n  likeCount++;\n  return likeCount;\n}\n```\n\nサーバ関数からの返り値を読み取るには、返されたプロミスを `await` する必要があります。"
  },
  {
    "path": "src/content/reference/rules/components-and-hooks-must-be-pure.md",
    "content": "---\ntitle: コンポーネントとフックを純粋に保つ\n---\n\n<Intro>\n純関数 (pure function) とは計算を行うだけで、それ以上のことはしない関数です。これによりコードの理解やデバッグが容易になり、React が自動的にコンポーネントとフックを最適化できるようになります。\n</Intro>\n\n<Note>\nこのリファレンスページで扱うのは高度なトピックです。あらかじめ[コンポーネントを純粋に保つ](/learn/keeping-components-pure)で説明されている概念に精通していることが必要です。\n</Note>\n\n<InlineToc />\n\n### 純粋性が重要である理由 {/*why-does-purity-matter*/}\n\nReact を React たらしめる重要な概念のひとつが*純粋性 (purity)* です。純粋なコンポーネントやフックとは、以下のような特徴を持つものです。\n\n* **冪等 (idempotent) であること** – 同じ入力で実行するたびに[常に同じ結果が得られる](/learn/keeping-components-pure#purity-components-as-formulas)こと。コンポーネントの入力とは props と state とコンテクスト。フックの入力とはその引数。\n* **レンダー時に副作用がない** – 副作用 (side effect) を伴うコードは[**レンダーとは別に実行**](#how-does-react-run-your-code)する必要がある。例えばユーザが UI を操作しそれによって UI が更新される場合は[イベントハンドラ](/learn/responding-to-events)として、またはレンダー直後に動作させる場合は[エフェクト](/reference/react/useEffect)として実行する。\n* **ローカルな値以外を変更しない**：コンポーネントとフックは、レンダー中に[ローカルに作成されたものではない値を決して変更してはならない](#mutation)。\n\nレンダーが純粋に保たれていれば、React はどの更新を優先してユーザに最初に提示すべきか理解することができます。これができるのはレンダーの純粋性のお陰です。コンポーネントが[レンダー時](#how-does-react-run-your-code)に副作用を持たないなら、React は更新がそれほど重要でないコンポーネントのレンダー処理を一時停止し、後で必要になったときに再開できます。\n\n具体的にはこれは、React がユーザに快適な体験を提供できるよう、レンダーのロジックが複数回実行されることがあるという意味です。しかしコンポーネントが[レンダー時](#how-does-react-run-your-code)に React が把握できない副作用、例えばグローバル変数の書き換えのようなことを行っている場合、React がレンダーコードを再実行した際にその副作用が望ましくない形でトリガされることになります。これはしばしば予期せぬバグを引き起こし、ユーザ体験を悪化させます。[「コンポーネントを純粋に保つ」のこちらの例](/learn/keeping-components-pure#side-effects-unintended-consequences)を参照してください。\n\n#### React はどのようにコードを実行するのか {/*how-does-react-run-your-code*/}\n\nReact は宣言型 (declarative) です。あなたは*何 (what)* をレンダーしたいのかだけを React に伝え、それを*どうやって (how)* ユーザにうまく表示するのかについては React が考えます。これを実現するため、React は複数のフェーズに分けてコードを実行します。React を使いこなすためにこれらのフェーズすべてを知っておく必要はありません。しかしどのコードが*レンダー*中に実行され、どのコードがそれ以外のタイミングで実行されるのかについては、概要を知っておくべきです。\n\n*レンダー*とは、UI の次のバージョンとして何が見えるべきかを計算する作業を指します。レンダーの後、React はこの新しい計算結果を受け取り、UI の以前のバージョンを作成する際に使われた計算結果と比較します。その後 React は、変更を適用するために必要な最小限の変更だけを [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model)（ユーザが実際に目にするもの）にコミットします。最後に、[エフェクト](/learn/synchronizing-with-effects) がフラッシュ（つまり未処理分がなくなるまで実行）されます。より詳しい情報は[レンダー](/learn/render-and-commit)および[コミットとフック](/reference/react/hooks#effect-hooks)のドキュメントを参照してください。\n\n<DeepDive>\n\n#### コードがレンダー中に走るかどうか判断する方法 {/*how-to-tell-if-code-runs-in-render*/}\n\nコードがレンダー中に走るかどうかを判断する簡単な方法は、そのコードがどこに書かれているかを見ることです。以下の例のようにトップレベルに書かれている場合、レンダー中に実行される可能性が高いでしょう。\n\n```js {2}\nfunction Dropdown() {\n  const selectedItems = new Set(); // created during render\n  // ...\n}\n```\n\nイベントハンドラやエフェクトはレンダー中には実行されません。\n\n```js {4}\nfunction Dropdown() {\n  const selectedItems = new Set();\n  const onSelect = (item) => {\n    // this code is in an event handler, so it's only run when the user triggers this\n    selectedItems.add(item);\n  }\n}\n```\n\n```js {4}\nfunction Dropdown() {\n  const selectedItems = new Set();\n  useEffect(() => {\n    // this code is inside of an Effect, so it only runs after rendering\n    logForAnalytics(selectedItems);\n  }, [selectedItems]);\n}\n```\n</DeepDive>\n\n---\n\n## コンポーネントとフックを冪等にする {/*components-and-hooks-must-be-idempotent*/}\n\nコンポーネントは、その入力である props、state、およびコンテクストに対して常に同じ出力を返さなければなりません。これを*冪等性 (idempotency)* と呼びます。[冪等性](https://en.wikipedia.org/wiki/Idempotence)は関数型プログラミングで広まった用語であり、同じ入力でそのコードを実行するたびに[常に同じ結果が得られる](learn/keeping-components-pure)という考え方を指します。\n\nこれは、[レンダー](#how-does-react-run-your-code)中に実行される*あらゆる*コードは冪等でなければならないという意味です。例えば以下のコードは冪等ではありません（したがって、コンポーネントも冪等ではありません）。\n\n```js {2}\nfunction Clock() {\n  const time = new Date(); // 🔴 Bad: always returns a different result!\n  return <span>{time.toLocaleString()}</span>\n}\n```\n\n`new Date()` は常に現在の日時を返し、呼び出すたびに結果が変わるため、冪等ではありません。上記のコンポーネントをレンダーすると、画面に表示される時間はコンポーネントがレンダーされた瞬間の時間に固定されます。同様に、`Math.random()` のような関数も冪等ではありません。なぜなら、入力が同じでも呼び出すたびに異なる結果を返すからです。\n\nこれは、`new Date()` のような冪等ではない関数を*絶対に*使用してはならないという意味ではありません。[レンダー](#how-does-react-run-your-code)中にだけは使用できないということです。上記の場合、最新の日付をこのコンポーネントと*同期*するために、[エフェクト](/reference/react/useEffect)が使用できます。\n\n<Sandpack>\n\n```js\nimport { useState, useEffect } from 'react';\n\nfunction useTime() {\n  // 1. Keep track of the current date's state. `useState` receives an initializer function as its\n  //    initial state. It only runs once when the hook is called, so only the current date at the\n  //    time the hook is called is set first.\n  const [time, setTime] = useState(() => new Date());\n\n  useEffect(() => {\n    // 2. Update the current date every second using `setInterval`.\n    const id = setInterval(() => {\n      setTime(new Date()); // ✅ Good: non-idempotent code no longer runs in render\n    }, 1000);\n    // 3. Return a cleanup function so we don't leak the `setInterval` timer.\n    return () => clearInterval(id);\n  }, []);\n\n  return time;\n}\n\nexport default function Clock() {\n  const time = useTime();\n  return <span>{time.toLocaleString()}</span>;\n}\n```\n\n</Sandpack>\n\n非冪等な `new Date()` の呼び出しをエフェクトでラップすることで、その計算を[レンダーの外側](#how-does-react-run-your-code)に移動させているのです。\n\nReact と外部状態を同期する必要がなく、ユーザの操作に応じて更新を行うだけの場合は、[イベントハンドラ](/learn/responding-to-events)の使用を考慮してください。\n\n---\n\n## 副作用はレンダーの外で実行する {/*side-effects-must-run-outside-of-render*/}\n\n[副作用](/learn/keeping-components-pure#side-effects-unintended-consequences)は[レンダー中](#how-does-react-run-your-code)に実行してはいけません。React が最適なユーザ体験のためにコンポーネントを複数回レンダーする可能性があるためです。\n\n<Note>\n副作用 (side effect) とはエフェクト (Effect) よりも広い概念を指す用語です。エフェクトとは `useEffect` でラップされるコードのみを指す用語であり、副作用とは呼び出し元に値を返すこと以外に観察可能な影響を及ぼすコード全般のことを指す、一般的な用語です。\n\n副作用は通常、[イベントハンドラ](/learn/responding-to-events)やエフェクトの中に書かれます。レンダーの中には決して書いてはいけません。\n</Note>\n\nレンダーは純粋に保つ必要がある一方で、アプリが何か面白いことをするためには、いずれかの時点で副作用が必要です。これには画面に何かを表示することも含まれます！ このルールの大事なところは、副作用は[レンダー時](#how-does-react-run-your-code)に起きてはならない、ということです。React がコンポーネントを複数回レンダーすることがあるからです。大抵の場合、副作用は[イベントハンドラ](learn/responding-to-events)を使用して処理します。イベントハンドラを使用することで、そのコードはレンダー中に実行しなくてよいと React に明示的に伝えていることになり、レンダーが純粋に保たれます。他の選択肢がない場合に最後の手段としてのみ、`useEffect` を使用して副作用を処理することもできます。\n\n### 書き換えを行ってもよいタイミング {/*mutation*/}\n\n#### ローカルミューテーション {/*local-mutation*/}\n副作用の一般的な例はミューテーション (mutation) です。JavaScript では、これは非[プリミティブ](https://developer.mozilla.org/en-US/docs/Glossary/Primitive)値の内容を書き換えることを指します。一般的に React では変数の書き換えを避けるべきですが、*ローカル*変数のミューテーション (local mutation) はまったく問題ありません。\n\n```js {2,7}\nfunction FriendList({ friends }) {\n  const items = []; // ✅ Good: locally created\n  for (let i = 0; i < friends.length; i++) {\n    const friend = friends[i];\n    items.push(\n      <Friend key={friend.id} friend={friend} />\n    ); // ✅ Good: local mutation is okay\n  }\n  return <section>{items}</section>;\n}\n```\n\nローカルミューテーションを避けるために無理にコードを変える必要はありません。[`Array.map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) を使用して短く書くこともできますが、[レンダー時](#how-does-react-run-your-code)にローカル配列を作成してアイテムを push していくのでも何ら問題ありません。\n\n`items` を書き換えているように見えますが、このコードは*ローカルでのみ*そうしているという点が重要です。コンポーネントの再レンダー時にこの書き換えは「記憶」されていません。言い換えると、`items` はコンポーネントの実行の最中にのみ存在します。`<FriendList />` がレンダーされるたびに `items` は*再作成*されるので、コンポーネントは常に同じ結果を返します。\n\n一方で、`items` がコンポーネントの外部で作成されている場合、以前の値を保持しつづけることで、変更の記憶が起きてしまいます。\n\n```js {1,7}\nconst items = []; // 🔴 Bad: created outside of the component\nfunction FriendList({ friends }) {\n  for (let i = 0; i < friends.length; i++) {\n    const friend = friends[i];\n    items.push(\n      <Friend key={friend.id} friend={friend} />\n    ); // 🔴 Bad: mutates a value created outside of render\n  }\n  return <section>{items}</section>;\n}\n```\n\n`<FriendList />` が再実行されると、このコンポーネントが実行されるたびに `friends` を `items` に追加し続け、結果の重複が生じます。この `<FriendList />` には[レンダー](#how-does-react-run-your-code)時に外部から観測可能な副作用があり、そのため**ルールに違反**しているというわけです。\n\n#### 遅延初期化 {/*lazy-initialization*/}\n\n厳密には「純粋」ではありませんが、遅延初期化 (lazy initialization) は問題ありません。\n\n```js {2}\nfunction ExpenseForm() {\n  SuperCalculator.initializeIfNotReady(); // ✅ Good: if it doesn't affect other components\n  // Continue rendering...\n}\n```\n\n#### DOM の書き換え {/*changing-the-dom*/}\n\nユーザに直接見えるような副作用は、React コンポーネントのレンダーロジックでは許可されていません。言い換えると、単にコンポーネント関数を呼び出すこと自体が、画面上の変化を生じさせてはいけません。\n\n```js {2}\nfunction ProductDetailPage({ product }) {\n  document.title = product.title; // 🔴 Bad: Changes the DOM\n}\n```\n\n`document.title` を更新するという望ましい結果をレンダーの外で達成する方法のひとつは、[`document` とコンポーネントを同期させる](/learn/synchronizing-with-effects)ことです。\n\nコンポーネントを複数回呼び出しても安全であり、他のコンポーネントのレンダーに影響を与えないのであれば、React はそれが厳密な関数型プログラミングの意味で 100% 純粋であるかどうかを気にしません。より重要なのは、[コンポーネントは冪等でなければならない](/reference/rules/components-and-hooks-must-be-pure)ということです。\n\n---\n\n## props と state はイミュータブル {/*props-and-state-are-immutable*/}\n\nコンポーネントの props と state はイミュータブルな[スナップショット](learn/state-as-a-snapshot)です。これらは決して直接書き換えてはいけません。代わりに新しい props を渡すか、`useState` のセッタ関数を使用してください。\n\nprops と state の値は、レンダーが終わってから更新されるスナップショットと考えることができます。したがって props や state 変数を直接書き換えることはありません。代わりに新しい props を渡すか、あるいはセッタ関数を使用して React に state をコンポーネントの次回レンダー時に更新する必要があると伝えます。\n\n### props を書き換えない {/*props*/}\nprops はイミュータブルです。props を変更すると、アプリケーションが一貫性のない出力を生成し、状況によって動作したりしなかったりするためデバッグが困難になるからです。\n\n```js {expectedErrors: {'react-compiler': [2]}} {2}\nfunction Post({ item }) {\n  item.url = new Url(item.url, base); // 🔴 Bad: never mutate props directly\n  return <Link url={item.url}>{item.title}</Link>;\n}\n```\n\n```js {2}\nfunction Post({ item }) {\n  const url = new Url(item.url, base); // ✅ Good: make a copy instead\n  return <Link url={url}>{item.title}</Link>;\n}\n```\n\n### state を書き換えない {/*state*/}\n`useState` は state 変数とその state を更新するためのセッタ関数を返します。\n\n```js\nconst [stateVariable, setter] = useState(0);\n```\n\nstate 変数はその場で書き換えるのではなく、`useState` によって返されるセッタ関数を使用して更新する必要があります。state 変数の中身を書き換えてもコンポーネントが更新されるわけではないため、ユーザに古くなった UI が表示されたままになります。セッタ関数を使用することで、state が変更され、UI を更新するため再レンダーをキューに入れる必要があるということを React に伝えます。\n\n```js {expectedErrors: {'react-compiler': [2, 5]}} {5}\nfunction Counter() {\n  const [count, setCount] = useState(0);\n\n  function handleClick() {\n    count = count + 1; // 🔴 Bad: never mutate state directly\n  }\n\n  return (\n    <button onClick={handleClick}>\n      You pressed me {count} times\n    </button>\n  );\n}\n```\n\n```js {5}\nfunction Counter() {\n  const [count, setCount] = useState(0);\n\n  function handleClick() {\n    setCount(count + 1); // ✅ Good: use the setter function returned by useState\n  }\n\n  return (\n    <button onClick={handleClick}>\n      You pressed me {count} times\n    </button>\n  );\n}\n```\n\n---\n\n## フックの引数と返り値はイミュータブル {/*return-values-and-arguments-to-hooks-are-immutable*/}\n\n一度値がフックに渡されたならそれを書き換えてはいけません。JSX の props と同様、フックに渡された時点でその値はイミュータブルです。\n\n```js {expectedErrors: {'react-compiler': [4]}} {4}\nfunction useIconStyle(icon) {\n  const theme = useContext(ThemeContext);\n  if (icon.enabled) {\n    icon.className = computeStyle(icon, theme); // 🔴 Bad: never mutate hook arguments directly\n  }\n  return icon;\n}\n```\n\n```js {3}\nfunction useIconStyle(icon) {\n  const theme = useContext(ThemeContext);\n  const newIcon = { ...icon }; // ✅ Good: make a copy instead\n  if (icon.enabled) {\n    newIcon.className = computeStyle(icon, theme);\n  }\n  return newIcon;\n}\n```\n\nReact における重要な原則のひとつは、*ローカル・リーズニング*、つまりコンポーネントやフックが何をしているのかそのコードだけを見て理解できることです。フックを呼び出す際には中身を「ブラックボックス」として扱うべきです。例えば、カスタムフックが引数を内部で値をメモ化するための依存値として使用していたらどうでしょう。\n\n```js {4}\nfunction useIconStyle(icon) {\n  const theme = useContext(ThemeContext);\n\n  return useMemo(() => {\n    const newIcon = { ...icon };\n    if (icon.enabled) {\n      newIcon.className = computeStyle(icon, theme);\n    }\n    return newIcon;\n  }, [icon, theme]);\n}\n```\n\nフックの引数を書き換えた場合、カスタムフック内のメモ化が正しく動作しなくなります。これを避けることが重要です。\n\n```js {4}\nstyle = useIconStyle(icon);         // `style` is memoized based on `icon`\nicon.enabled = false;               // Bad: 🔴 never mutate hook arguments directly\nstyle = useIconStyle(icon);         // previously memoized result is returned\n```\n\n```js {4}\nstyle = useIconStyle(icon);         // `style` is memoized based on `icon`\nicon = { ...icon, enabled: false }; // Good: ✅ make a copy instead\nstyle = useIconStyle(icon);         // new value of `style` is calculated\n```\n\n同様に、フックからの返り値はメモ化されている可能性があるため、それらを書き換えないことも重要です。\n\n---\n\n## JSX に渡された値はイミュータブル {/*values-are-immutable-after-being-passed-to-jsx*/}\n\nJSX で使用された後に値を書き換えてはいけません。ミューテーションは JSX が作成される前に行ってください。\n\n式として JSX を使用する際、React はコンポーネントのレンダーが完了する前に JSX を先行して評価してしまうかもしれません。つまり JSX に渡された後で値を変更した場合、React がコンポーネントの出力を更新する必要があることを認識しないため、古い UI が表示され続ける可能性があるということです。\n\n```js {expectedErrors: {'react-compiler': [4]}} {4}\nfunction Page({ colour }) {\n  const styles = { colour, size: \"large\" };\n  const header = <Header styles={styles} />;\n  styles.size = \"small\"; // 🔴 Bad: styles was already used in the JSX above\n  const footer = <Footer styles={styles} />;\n  return (\n    <>\n      {header}\n      <Content />\n      {footer}\n    </>\n  );\n}\n```\n\n```js {4}\nfunction Page({ colour }) {\n  const headerStyles = { colour, size: \"large\" };\n  const header = <Header styles={headerStyles} />;\n  const footerStyles = { colour, size: \"small\" }; // ✅ Good: we created a new value\n  const footer = <Footer styles={footerStyles} />;\n  return (\n    <>\n      {header}\n      <Content />\n      {footer}\n    </>\n  );\n}\n```\n"
  },
  {
    "path": "src/content/reference/rules/index.md",
    "content": "---\ntitle: React のルール\n---\n\n<Intro>\n様々な概念を表現する方法がプログラミング言語によってそれぞれ異なるように、React にも、理解しやすい方法でパターンを表現し高品質なアプリケーションを産み出すための慣用的な記法、ないしルールが存在します。\n</Intro>\n\n<InlineToc />\n\n---\n\n<Note>\nReact で UI を表現する方法についてさらに学びたい場合は、[React の流儀](/learn/thinking-in-react)を読むことをお勧めします。\n</Note>\n\nこのセクションでは、自然な React コードを書くために従うべきルールを説明します。自然な React コードを書くことで、安全で整理されており、組み合わせ可能なアプリケーションを作成することができます。以下に挙げる特性により、アプリは変更に対して頑健になり、他の開発者やライブラリやツールと連携しやすくなります。\n\n以下のルールは **React のルール**として知られています。これらを守っていないならアプリにバグがある可能性が高い、という意味で、これらは単なるガイドラインではなくルールです。またこれらを守らない場合、あなたのコードは不自然で、理解や推測が難しいものになるでしょう。\n\nReact のルールを守るため、React の [ESLint プラグイン](https://www.npmjs.com/package/eslint-plugin-react-hooks) と [Strict Mode](/reference/react/StrictMode) を併用して利用することを強くお勧めします。React のルールに従うことにより、バグを見つけて対処し、アプリケーションを保守可能に保つことができます。\n\n---\n\n## コンポーネントとフックを純粋に保つ {/*components-and-hooks-must-be-pure*/}\n\n[コンポーネントとフックを純粋に保つこと](/reference/rules/components-and-hooks-must-be-pure)は、アプリを予測可能にし、デバッグしやすくし、React がコードを自動的に最適化できるようにするための、React の重要なルールです。\n\n* [コンポーネントとフックを冪等にする](/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent) - React コンポーネントは、入力（props、state、およびコンテクスト）に対して常に同じ出力を返すことが前提となっています。\n* [副作用はレンダーの外で実行する](/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) - 副作用はレンダー内で実行するべきではありません。React は最適なユーザ体験を実現するために複数回コンポーネントをレンダーすることがあるからです。\n* [props と state はイミュータブル](/reference/rules/components-and-hooks-must-be-pure#props-and-state-are-immutable) - コンポーネントの props と state は、単一のレンダー内においては不変のスナップショットです。直接書き換えてはいけません。\n* [フックの引数と返り値はイミュータブル](/reference/rules/components-and-hooks-must-be-pure#return-values-and-arguments-to-hooks-are-immutable) - 一度フックに値を渡したならそれを書き換えてはいけません。JSX の props と同様、フックに渡された値はイミュータブルになります。\n* [JSX に渡された後の値はイミュータブル](/reference/rules/components-and-hooks-must-be-pure#values-are-immutable-after-being-passed-to-jsx) - JSX で使用した後に値を書き換えてはいけません。書き換えのロジックは JSX を作成する前に行います。\n\n---\n\n## コンポーネントやフックを呼び出すのは React {/*react-calls-components-and-hooks*/}\n\n[ユーザ体験を最適化するために必要に応じてコンポーネントやフックを呼び出すというのは React 自身の責務です](/reference/rules/react-calls-components-and-hooks)。React は宣言型 (declarative) です。あなたは何 (what) をレンダーしたいのかだけを React に伝え、それをどうやって (how) ユーザにうまく表示するのかについては React が考えます。\n\n* [コンポーネント関数を直接呼び出さない](/reference/rules/react-calls-components-and-hooks#never-call-component-functions-directly) – コンポーネントは JSX 内でのみ使用します。通常の関数として呼び出してはいけません。\n* [フックを通常の値として取り回さない](/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values) – フックはコンポーネント内で呼び出すだけにします。通常の値のようにコード内で取り回してはいけません。\n\n---\n\n## フックのルール {/*rules-of-hooks*/}\n\nフックは再利用可能な UI ロジックを表す JavaScript の関数として定義されており、呼び出せる場所に関する制約があります。フックを使用する際には、[フックのルール](/reference/rules/rules-of-hooks)に従う必要があります。\n\n* [フックはトップレベルでのみ呼び出す](/reference/rules/rules-of-hooks#only-call-hooks-at-the-top-level) – ループ、条件分岐、ネストされた関数の内部でフックを呼び出してはいけません。代わりに、フックは常に React 関数のトップレベルで、早期 return を行う前に行います。\n* [フックは React の関数からのみ呼び出す](/reference/rules/rules-of-hooks#only-call-hooks-from-react-functions) – 通常の JavaScript 関数からフックを呼び出してはいけません。\n\n"
  },
  {
    "path": "src/content/reference/rules/react-calls-components-and-hooks.md",
    "content": "---\ntitle: コンポーネントやフックを呼び出すのは React\n---\n\n<Intro>\nユーザ体験を最適化するために必要に応じてコンポーネントやフックを呼び出すというのは React 自身の責務です。React は宣言型 (declarative) です。あなたは*何 (what)* をレンダーしたいのかだけを React に伝え、それを*どうやって (how)* ユーザにうまく表示するのかについては React が考えます。\n</Intro>\n\n<InlineToc />\n\n---\n\n## コンポーネント関数を直接呼び出さない {/*never-call-component-functions-directly*/}\nコンポーネントは JSX 内でのみ使用すべきです。通常の関数として呼び出してはいけません。呼び出すのは React です。\n\n[レンダー中](/reference/rules/components-and-hooks-must-be-pure#how-does-react-run-your-code)にコンポーネント関数をいつ呼び出すかを決定する必要があるのは React です。React では、これを JSX を使用して行います。\n\n```js {2}\nfunction BlogPost() {\n  return <Layout><Article /></Layout>; // ✅ Good: Only use components in JSX\n}\n```\n\n```js {expectedErrors: {'react-compiler': [2]}} {2}\nfunction BlogPost() {\n  return <Layout>{Article()}</Layout>; // 🔴 Bad: Never call them directly\n}\n```\n\nコンポーネントにフックが含まれている場合、ループや条件内でコンポーネントを直接呼び出すと、[フックのルール](/reference/rules/rules-of-hooks)にいとも簡単に違反してしまいます。\n\nReact にレンダーの指揮権を与えることで、多くの利点が得られます。\n\n* **コンポーネントが単なる関数以上のものになる**。ツリー内のコンポーネントの同一性に基づいた処理を行うフックを通じて、React は*ローカル state* などの機能を追加できます。\n* **コンポーネントの型情報を差分検出処理時に利用できる**。React にコンポーネントの呼び出しを任せることで、ツリーの概念的構造について React はより多くの情報を得られます。たとえば `<Feed>` から `<Profile>` ページへとレンダーが移行するとき、React はそれらを再利用しようとせずに済みます。\n* **React がユーザ体験を向上させられる**。たとえば、大きなコンポーネントツリーの再レンダーがメインスレッドをブロックしないよう、複数のコンポーネントのレンダーの合間にブラウザに一部の作業を行わせることができます。\n* **より良いデバッグ体験**。ライブラリがコンポーネントのことを基本部品として認識していれば、開発中の調査のためのリッチな開発者ツールを構築できます。\n* **より効率的な差分検出処理**。React は、ツリー内のどのコンポーネントを再レンダーすべきか正確に把握し、残りをスキップします。これによりアプリの動作は高速でキビキビとしたものになります。\n\n---\n\n## フックを通常の値として取り回さない {/*never-pass-around-hooks-as-regular-values*/}\n\nフックはコンポーネントまたはフックの内部でのみ呼び出すべきです。通常の値のように取り回してはいけません。\n\nフックは、コンポーネントに React の機能を加えるために使用します。常に関数として呼び出すだけにし、通常の値のように取りまわしてはいけません。これにより*ローカル・リーズニング*が可能に、すなわち開発者がそのコンポーネントだけを見てコンポーネントにできることをすべて理解できるようになります。\n\nこのルールを破ると、React はコンポーネントを自動的に最適化することができなくなります。\n\n### フックを動的に変更しない {/*dont-dynamically-mutate-a-hook*/}\n\nフックは可能な限り「静的」であるべきです。つまり、動的に変更してはいけません。たとえば、高階 (higher-order) フックを書くべきではありません。\n\n```js {expectedErrors: {'react-compiler': [2, 3]}} {2}\nfunction ChatInput() {\n  const useDataWithLogging = withLogging(useData); // 🔴 Bad: don't write higher order Hooks\n  const data = useDataWithLogging();\n}\n```\n\nフックはイミュータブルであるべきで、動的に変更するべきではありません。フックを動的に変更する代わりに、望ましい機能を持つ静的なバージョンのフックを作成してください。\n\n```js {2,6}\nfunction ChatInput() {\n  const data = useDataWithLogging(); // ✅ Good: Create a new version of the Hook\n}\n\nfunction useDataWithLogging() {\n  // ... Create a new version of the Hook and inline the logic here\n}\n```\n\n### フックを動的に使用しない {/*dont-dynamically-use-hooks*/}\n\nフックを動的に使用してはいけません。例えば以下のように、フックそのものを値としてコンポーネントに渡して依存性注入を行わないようにしてください。\n\n```js {expectedErrors: {'react-compiler': [2]}} {2}\nfunction ChatInput() {\n  return <Button useData={useDataWithLogging} /> // 🔴 Bad: don't pass Hooks as props\n}\n```\n\n代わりにコンポーネント内でインラインでフックをコールし、そこでロジックを処理するべきです。\n\n```js {6}\nfunction ChatInput() {\n  return <Button />\n}\n\nfunction Button() {\n  const data = useDataWithLogging(); // ✅ Good: Use the Hook directly\n}\n\nfunction useDataWithLogging() {\n  // If there's any conditional logic to change the Hook's behavior, it should be inlined into\n  // the Hook\n}\n```\n\nこうすることで `<Button />` を理解しデバッグするのがずっと簡単になります。フックを動的に使用すると、アプリの複雑さが大幅に増し、ローカル・リーズニングを妨げ、長期的にはチームの生産性を低下させます。また、条件付きでフックを呼び出すべきではないという[フックのルール](/reference/rules/rules-of-hooks)を誤って破る可能性も高まります。テストのためにコンポーネントをモックする必要がある場合は、代わりにサーバをモックして固定データで応答する方が良いでしょう。可能であれば、end-to-end テストでアプリをテストする方が通常はより効果的です。\n\n"
  },
  {
    "path": "src/content/reference/rules/rules-of-hooks.md",
    "content": "---\ntitle: フックのルール\n---\n\n<Intro>\nフックは再利用可能な UI ロジックを表す JavaScript の関数として定義されており、呼び出せる場所に関する制約があります。\n</Intro>\n\n<InlineToc />\n\n---\n\n## フックはトップレベルでのみ呼び出す {/*only-call-hooks-at-the-top-level*/}\n\n`use` で始まる関数名を持つ関数は React では[*フック (hook)*](/reference/react) と呼ばれます。\n\n**ループ、条件分岐、ネストされた関数、`try`/`catch`/`finally` ブロックの内部でフックを呼び出してはいけません**。代わりに、フックは常に React 関数のトップレベルで、早期 return を行う前に呼び出します。フックは React が関数コンポーネントをレンダーしている間にのみ呼び出すことができます。\n\n* ✅ [関数コンポーネント](/learn/your-first-component)本体のトップレベルで呼び出す。\n* ✅ [カスタムフック](/learn/reusing-logic-with-custom-hooks)本体のトップレベルで呼び出す。\n\n```js{2-3,8-9}\nfunction Counter() {\n  // ✅ Good: top-level in a function component\n  const [count, setCount] = useState(0);\n  // ...\n}\n\nfunction useWindowWidth() {\n  // ✅ Good: top-level in a custom Hook\n  const [width, setWidth] = useState(window.innerWidth);\n  // ...\n}\n```\n\n以下のような場合にフック（`use` で始まる関数）を呼び出すことは**サポートされていません**。\n\n* 🔴 条件やループの内部でフックを呼び出してはいけない。\n* 🔴 条件付き `return` 文の後でフックを呼び出してはいけない。\n* 🔴 イベントハンドラ内でフックを呼び出してはいけない。\n* 🔴 クラスコンポーネント内でフックを呼び出してはいけない。\n* 🔴 `useMemo`、`useReducer`、`useEffect` に渡される関数内でフックを呼び出してはいけない。\n* 🔴 `try`/`catch`/`finally` ブロック内でフックを呼び出してはいけない。\n\nこれらのルールを破ると、以下のエラーが表示される可能性があります。\n\n```js{3-4,11-12,20-21}\nfunction Bad({ cond }) {\n  if (cond) {\n    // 🔴 Bad: inside a condition (to fix, move it outside!)\n    const theme = useContext(ThemeContext);\n  }\n  // ...\n}\n\nfunction Bad() {\n  for (let i = 0; i < 10; i++) {\n    // 🔴 Bad: inside a loop (to fix, move it outside!)\n    const theme = useContext(ThemeContext);\n  }\n  // ...\n}\n\nfunction Bad({ cond }) {\n  if (cond) {\n    return;\n  }\n  // 🔴 Bad: after a conditional return (to fix, move it before the return!)\n  const theme = useContext(ThemeContext);\n  // ...\n}\n\nfunction Bad() {\n  function handleClick() {\n    // 🔴 Bad: inside an event handler (to fix, move it outside!)\n    const theme = useContext(ThemeContext);\n  }\n  // ...\n}\n\nfunction Bad() {\n  const style = useMemo(() => {\n    // 🔴 Bad: inside useMemo (to fix, move it outside!)\n    const theme = useContext(ThemeContext);\n    return createStyle(theme);\n  });\n  // ...\n}\n\nclass Bad extends React.Component {\n  render() {\n    // 🔴 Bad: inside a class component (to fix, write a function component instead of a class!)\n    useEffect(() => {})\n    // ...\n  }\n}\n\nfunction Bad() {\n  try {\n    // 🔴 Bad: inside try/catch/finally block (to fix, move it outside!)\n    const [x, setX] = useState(0);\n  } catch {\n    const [x, setX] = useState(1);\n  }\n}\n```\n\nこれらの間違いを捕捉するために [`eslint-plugin-react-hooks` プラグイン](https://www.npmjs.com/package/eslint-plugin-react-hooks)が利用可能です。\n\n<Note>\n\n[カスタムフック](/learn/reusing-logic-with-custom-hooks)は他のフックを呼び出しても*構いません*（むしろそれが主目的です）。これは、カスタムフックも関数コンポーネントのレンダー中にのみ呼び出されることが前提だからです。\n\n</Note>\n\n---\n\n## React の関数からのみフックを呼び出す {/*only-call-hooks-from-react-functions*/}\n\n通常の JavaScript 関数からフックを呼び出さないでください。代わりに以下のようにします。\n\n✅ React の関数コンポーネントからフックを呼び出す。\n✅ [カスタムフック](/learn/reusing-logic-with-custom-hooks#extracting-your-own-custom-hook-from-a-component)からフックを呼び出す。\n\nこのルールに従うことで、コンポーネント内のすべてのステートフルなロジックがそのソースコードから明確に見えることが保証されます。\n\n```js {2,5}\nfunction FriendList() {\n  const [onlineStatus, setOnlineStatus] = useOnlineStatus(); // ✅\n}\n\nfunction setOnlineStatus() { // ❌ Not a component or custom Hook!\n  const [onlineStatus, setOnlineStatus] = useOnlineStatus();\n}\n```\n"
  },
  {
    "path": "src/content/versions.md",
    "content": "---\ntitle: React のバージョン\n---\n\n<Intro>\n\n[react.dev](https://react.dev)（日本語版：[ja.react.dev](https://ja.react.dev)）の React ドキュメントは、React の最新バージョン向けのドキュメントです。\n\n</Intro>\n\n私たちは、同一メジャーバージョン内ではドキュメントを最新の状態に保つようにしており、マイナーバージョンやパッチバージョン別にドキュメントを公開することはありません。新しいメジャーバージョンのリリース後、前バージョンのドキュメントは `x.react.dev` としてアーカイブされます。詳細は[バージョニングポリシー](/community/versioning-policy)を参照してください。\n\n過去のメジャーバージョンのアーカイブは以下から参照できます。\n\n## Latest version: 19.2 {/*latest-version*/}\n\n- [react.dev](https://react.dev) {/*docs-19*/}\n\n## Previous versions {/*previous-versions*/}\n\n- [18.react.dev](https://18.react.dev) {/*docs-18*/}\n- [17.react.dev](https://17.react.dev) {/*docs-17*/}\n- [16.react.dev](https://16.react.dev) {/*docs-16*/}\n- [15.react.dev](https://15.react.dev) {/*docs-15*/}\n\n<Note>\n\n#### レガシードキュメント {/*legacy-docs*/}\n\n2023 年、React 18 向けの新しいドキュメントを [react.dev](https://react.dev) として[公開しました](/blog/2023/03/16/introducing-react-dev)。従来の React 18 ドキュメントは [legacy.reactjs.org](https://legacy.reactjs.org) で利用できます。バージョン 17 以下はレガシーサイトでホストされています。\n\nReact 15 より前のバージョンについては [15.react.dev](https://15.react.dev) を参照してください。\n\n</Note>\n\n## Changelog {/*changelog*/}\n\n### React 19 {/*react-19*/}\n\n**Blog Posts**\n- [React v19](/blog/2024/12/05/react-19)\n- [React 19 Upgrade Guide](/blog/2024/04/25/react-19-upgrade-guide)\n- [React Compiler Beta Release](/blog/2024/10/21/react-compiler-beta-release)\n- [React Compiler v1.0](/blog/2025/10/07/react-compiler-1)\n- [React 19.2](/blog/2025/10/01/react-19-2)\n\n**Talks**\n- [React 19 Keynote](https://www.youtube.com/watch?v=lyEKhv8-3n0)\n- [A Roadmap to React 19](https://www.youtube.com/watch?v=R0B2HsSM78s)\n- [What's new in React 19](https://www.youtube.com/watch?v=AJOGzVygGcY)\n- [React for Two Computers](https://www.youtube.com/watch?v=ozI4V_29fj4)\n- [React Compiler Deep Dive](https://www.youtube.com/watch?v=uA_PVyZP7AI)\n- [React Compiler Case Studies](https://www.youtube.com/watch?v=lvhPq5chokM)\n- [React 19 Deep Dive: Coordinating HTML](https://www.youtube.com/watch?v=IBBN-s77YSI)\n\n**Releases**\n- [v19.2.1 (December, 2025)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1922-dec-11-2025)\n- [v19.2.1 (December, 2025)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1921-dec-3-2025)\n- [v19.2.0 (October, 2025)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1920-october-1st-2025)\n- [v19.1.3 (December, 2025)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1913-dec-11-2025)\n- [v19.1.2 (December, 2025)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1912-dec-3-2025)\n- [v19.1.1 (July, 2025)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1911-july-28-2025)\n- [v19.1.0 (March, 2025)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1910-march-28-2025)\n- [v19.0.2 (December, 2025)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1902-dec-11-2025)\n- [v19.0.1 (December, 2025)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1901-dec-3-2025)\n- [v19.0.0 (December, 2024)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1900-december-5-2024)\n\n### React 18 {/*react-18*/}\n\n**Blog Posts**\n- [React v18.0](/blog/2022/03/29/react-v18)\n- [How to Upgrade to React 18](/blog/2022/03/08/react-18-upgrade-guide)\n- [The Plan for React 18](/blog/2021/06/08/the-plan-for-react-18)\n\n**Talks**\n- [React 18 Keynote](https://www.youtube.com/watch?v=FZ0cG47msEk)\n- [React 18 for app developers](https://www.youtube.com/watch?v=ytudH8je5ko)\n- [Streaming Server Rendering with Suspense](https://www.youtube.com/watch?v=pj5N-Khihgc)\n- [React without memo](https://www.youtube.com/watch?v=lGEMwh32soc)\n- [React Docs Keynote](https://www.youtube.com/watch?v=mneDaMYOKP8)\n- [React Developer Tooling](https://www.youtube.com/watch?v=oxDfrke8rZg)\n- [The first React Working Group](https://www.youtube.com/watch?v=qn7gRClrC9U)\n- [React 18 for External Store Libraries](https://www.youtube.com/watch?v=oPfSC5bQPR8)\n\n**Releases**\n- [v18.3.1 (April, 2024)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1831-april-26-2024)\n- [v18.3.0 (April, 2024)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1830-april-25-2024)\n- [v18.2.0 (June, 2022)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1820-june-14-2022)\n- [v18.1.0 (April, 2022)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1810-april-26-2022)\n- [v18.0.0 (March 2022)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1800-march-29-2022)\n\n### React 17 {/*react-17*/}\n\n**Blog Posts**\n- [React v17.0](https://legacy.reactjs.org/blog/2020/10/20/react-v17.html)\n- [Introducing the New JSX Transform](https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html)\n- [React v17.0 Release Candidate: No New Features](https://legacy.reactjs.org/blog/2020/08/10/react-v17-rc.html)\n\n**Releases**\n- [v17.0.2 (March 2021)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1702-march-22-2021)\n- [v17.0.1 (October 2020)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1701-october-22-2020)\n- [v17.0.0 (October 2020)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1700-october-20-2020)\n\n### React 16 {/*react-16*/}\n\n**Blog Posts**\n- [React v16.0](https://legacy.reactjs.org/blog/2017/09/26/react-v16.0.html)\n- [DOM Attributes in React 16](https://legacy.reactjs.org/blog/2017/09/08/dom-attributes-in-react-16.html)\n- [Error Handling in React 16](https://legacy.reactjs.org/blog/2017/07/26/error-handling-in-react-16.html)\n- [React v16.2.0: Improved Support for Fragments](https://legacy.reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html)\n- [React v16.4.0: Pointer Events](https://legacy.reactjs.org/blog/2018/05/23/react-v-16-4.html)\n- [React v16.4.2: Server-side vulnerability fix](https://legacy.reactjs.org/blog/2018/08/01/react-v-16-4-2.html)\n- [React v16.6.0: lazy, memo and contextType](https://legacy.reactjs.org/blog/2018/10/23/react-v-16-6.html)\n- [React v16.7: No, This Is Not the One With Hooks](https://legacy.reactjs.org/blog/2018/12/19/react-v-16-7.html)\n- [React v16.8: The One With Hooks](https://legacy.reactjs.org/blog/2019/02/06/react-v16.8.0.html)\n- [React v16.9.0 and the Roadmap Update](https://legacy.reactjs.org/blog/2019/08/08/react-v16.9.0.html)\n- [React v16.13.0](https://legacy.reactjs.org/blog/2020/02/26/react-v16.13.0.html)\n\n**Releases**\n- [v16.14.0 (October 2020)](https://github.com/facebook/react/blob/main/CHANGELOG.md#16140-october-14-2020)\n- [v16.13.1 (March 2020)](https://github.com/facebook/react/blob/main/CHANGELOG.md#16131-march-19-2020)\n- [v16.13.0 (February 2020)](https://github.com/facebook/react/blob/main/CHANGELOG.md#16130-february-26-2020)\n- [v16.12.0 (November 2019)](https://github.com/facebook/react/blob/main/CHANGELOG.md#16120-november-14-2019)\n- [v16.11.0 (October 2019)](https://github.com/facebook/react/blob/main/CHANGELOG.md#16110-october-22-2019)\n- [v16.10.2 (October 2019)](https://github.com/facebook/react/blob/main/CHANGELOG.md#16102-october-3-2019)\n- [v16.10.1 (September 2019)](https://github.com/facebook/react/blob/main/CHANGELOG.md#16101-september-28-2019)\n- [v16.10.0 (September 2019)](https://github.com/facebook/react/blob/main/CHANGELOG.md#16100-september-27-2019)\n- [v16.9.0 (August 2019)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1690-august-8-2019)\n- [v16.8.6 (March 2019)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1686-march-27-2019)\n- [v16.8.5 (March 2019)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1685-march-22-2019)\n- [v16.8.4 (March 2019)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1684-march-5-2019)\n- [v16.8.3 (February 2019)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1683-february-21-2019)\n- [v16.8.2 (February 2019)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1682-february-14-2019)\n- [v16.8.1 (February 2019)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1681-february-6-2019)\n- [v16.8.0 (February 2019)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1680-february-6-2019)\n- [v16.7.0 (December 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1670-december-19-2018)\n- [v16.6.3 (November 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1663-november-12-2018)\n- [v16.6.2 (November 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1662-november-12-2018)\n- [v16.6.1 (November 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1661-november-6-2018)\n- [v16.6.0 (October 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1660-october-23-2018)\n- [v16.5.2 (September 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1652-september-18-2018)\n- [v16.5.1 (September 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1651-september-13-2018)\n- [v16.5.0 (September 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1650-september-5-2018)\n- [v16.4.2 (August 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1642-august-1-2018)\n- [v16.4.1 (June 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1641-june-13-2018)\n- [v16.4.0 (May 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1640-may-23-2018)\n- [v16.3.3 (August 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1633-august-1-2018)\n- [v16.3.2 (April 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1632-april-16-2018)\n- [v16.3.1 (April 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1631-april-3-2018)\n- [v16.3.0 (March 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1630-march-29-2018)\n- [v16.2.1 (August 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1621-august-1-2018)\n- [v16.2.0 (November 2017)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1620-november-28-2017)\n- [v16.1.2 (August 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1612-august-1-2018)\n- [v16.1.1 (November 2017)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1611-november-13-2017)\n- [v16.1.0 (November 2017)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1610-november-9-2017)\n- [v16.0.1 (August 2018)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1601-august-1-2018)\n- [v16.0 (September 2017)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1600-september-26-2017)\n\n### React 15 {/*react-15*/}\n\n**Blog Posts**\n- [React v15.0](https://legacy.reactjs.org/blog/2016/04/07/react-v15.html)\n- [React v15.0 Release Candidate 2](https://legacy.reactjs.org/blog/2016/03/16/react-v15-rc2.html)\n- [React v15.0 Release Candidate](https://legacy.reactjs.org/blog/2016/03/07/react-v15-rc1.html)\n- [New Versioning Scheme](https://legacy.reactjs.org/blog/2016/02/19/new-versioning-scheme.html)\n- [Discontinuing IE 8 Support in React DOM](https://legacy.reactjs.org/blog/2016/01/12/discontinuing-ie8-support.html)\n- [Introducing React's Error Code System](https://legacy.reactjs.org/blog/2016/07/11/introducing-reacts-error-code-system.html)\n- [React v15.0.1](https://legacy.reactjs.org/blog/2016/04/08/react-v15.0.1.html)\n- [React v15.4.0](https://legacy.reactjs.org/blog/2016/11/16/react-v15.4.0.html)\n- [React v15.5.0](https://legacy.reactjs.org/blog/2017/04/07/react-v15.5.0.html)\n- [React v15.6.0](https://legacy.reactjs.org/blog/2017/06/13/react-v15.6.0.html)\n- [React v15.6.2](https://legacy.reactjs.org/blog/2017/09/25/react-v15.6.2.html)\n\n**Releases**\n- [v15.7.0 (October 2017)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1570-october-14-2020)\n- [v15.6.2 (September 2017)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1562-september-25-2017)\n- [v15.6.1 (June 2017)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1561-june-14-2017)\n- [v15.6.0 (June 2017)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1560-june-13-2017)\n- [v15.5.4 (April 2017)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1554-april-11-2017)\n- [v15.5.3 (April 2017)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1553-april-7-2017)\n- [v15.5.2 (April 2017)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1552-april-7-2017)\n- [v15.5.1 (April 2017)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1551-april-7-2017)\n- [v15.5.0 (April 2017)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1550-april-7-2017)\n- [v15.4.2 (January 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1542-january-6-2017)\n- [v15.4.1 (November 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1541-november-22-2016)\n- [v15.4.0 (November 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1540-november-16-2016)\n- [v15.3.2 (September 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1532-september-19-2016)\n- [v15.3.1 (August 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1531-august-19-2016)\n- [v15.3.0 (July 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1530-july-29-2016)\n- [v15.2.1 (July 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1521-july-8-2016)\n- [v15.2.0 (July 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1520-july-1-2016)\n- [v15.1.0 (May 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1510-may-20-2016)\n- [v15.0.2 (April 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1502-april-29-2016)\n- [v15.0.1 (April 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1501-april-8-2016)\n- [v15.0.0 (April 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#1500-april-7-2016)\n\n### React 0.14 {/*react-14*/}\n\n**Blog Posts**\n- [React v0.14](https://legacy.reactjs.org/blog/2015/10/07/react-v0.14.html)\n- [React v0.14 Release Candidate](https://legacy.reactjs.org/blog/2015/09/10/react-v0.14-rc1.html)\n- [React v0.14 Beta 1](https://legacy.reactjs.org/blog/2015/07/03/react-v0.14-beta-1.html)\n- [New React Developer Tools](https://legacy.reactjs.org/blog/2015/09/02/new-react-developer-tools.html)\n- [New React Devtools Beta](https://legacy.reactjs.org/blog/2015/08/03/new-react-devtools-beta.html)\n- [React v0.14.1](https://legacy.reactjs.org/blog/2015/10/28/react-v0.14.1.html)\n- [React v0.14.2](https://legacy.reactjs.org/blog/2015/11/02/react-v0.14.2.html)\n- [React v0.14.3](https://legacy.reactjs.org/blog/2015/11/18/react-v0.14.3.html)\n- [React v0.14.4](https://legacy.reactjs.org/blog/2015/12/29/react-v0.14.4.html)\n- [React v0.14.8](https://legacy.reactjs.org/blog/2016/03/29/react-v0.14.8.html)\n\n**Releases**\n- [v0.14.10 (October 2020)](https://github.com/facebook/react/blob/main/CHANGELOG.md#01410-october-14-2020)\n- [v0.14.8 (March 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0148-march-29-2016)\n- [v0.14.7 (January 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0147-january-28-2016)\n- [v0.14.6 (January 2016)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0146-january-6-2016)\n- [v0.14.5 (December 2015)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0145-december-29-2015)\n- [v0.14.4 (December 2015)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0144-december-29-2015)\n- [v0.14.3 (November 2015)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0143-november-18-2015)\n- [v0.14.2 (November 2015)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0142-november-2-2015)\n- [v0.14.1 (October 2015)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0141-october-28-2015)\n- [v0.14.0 (October 2015)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0140-october-7-2015)\n\n### React 0.13 {/*react-13*/}\n\n**Blog Posts**\n- [React Native v0.4](https://legacy.reactjs.org/blog/2015/04/17/react-native-v0.4.html)\n- [React v0.13](https://legacy.reactjs.org/blog/2015/03/10/react-v0.13.html)\n- [React v0.13 RC2](https://legacy.reactjs.org/blog/2015/03/03/react-v0.13-rc2.html)\n- [React v0.13 RC](https://legacy.reactjs.org/blog/2015/02/24/react-v0.13-rc1.html)\n- [React v0.13.0 Beta 1](https://legacy.reactjs.org/blog/2015/01/27/react-v0.13.0-beta-1.html)\n- [Streamlining React Elements](https://legacy.reactjs.org/blog/2015/02/24/streamlining-react-elements.html)\n- [Introducing Relay and GraphQL](https://legacy.reactjs.org/blog/2015/02/20/introducing-relay-and-graphql.html)\n- [Introducing React Native](https://legacy.reactjs.org/blog/2015/03/26/introducing-react-native.html)\n- [React v0.13.1](https://legacy.reactjs.org/blog/2015/03/16/react-v0.13.1.html)\n- [React v0.13.2](https://legacy.reactjs.org/blog/2015/04/18/react-v0.13.2.html)\n- [React v0.13.3](https://legacy.reactjs.org/blog/2015/05/08/react-v0.13.3.html)\n\n**Releases**\n- [v0.13.3 (May 2015)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0133-may-8-2015)\n- [v0.13.2 (April 2015)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0132-april-18-2015)\n- [v0.13.1 (March 2015)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0131-march-16-2015)\n- [v0.13.0 (March 2015)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0130-march-10-2015)\n\n### React 0.12 {/*react-12*/}\n\n**Blog Posts**\n- [React v0.12](https://legacy.reactjs.org/blog/2014/10/28/react-v0.12.html)\n- [React v0.12 RC](https://legacy.reactjs.org/blog/2014/10/16/react-v0.12-rc1.html)\n- [Introducing React Elements](https://legacy.reactjs.org/blog/2014/10/14/introducing-react-elements.html)\n- [React v0.12.2](https://legacy.reactjs.org/blog/2014/12/18/react-v0.12.2.html)\n\n**Releases**\n- [v0.12.2 (December 2014)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0122-december-18-2014)\n- [v0.12.1 (November 2014)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0121-november-18-2014)\n- [v0.12.0 (October 2014)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0120-october-28-2014)\n\n### React 0.11 {/*react-11*/}\n\n**Blog Posts**\n- [React v0.11](https://legacy.reactjs.org/blog/2014/07/17/react-v0.11.html)\n- [React v0.11 RC](https://legacy.reactjs.org/blog/2014/07/13/react-v0.11-rc1.html)\n- [One Year of Open-Source React](https://legacy.reactjs.org/blog/2014/05/29/one-year-of-open-source-react.html)\n- [The Road to 1.0](https://legacy.reactjs.org/blog/2014/03/28/the-road-to-1.0.html)\n- [React v0.11.1](https://legacy.reactjs.org/blog/2014/07/25/react-v0.11.1.html)\n- [React v0.11.2](https://legacy.reactjs.org/blog/2014/09/16/react-v0.11.2.html)\n- [Introducing the JSX Specificaion](https://legacy.reactjs.org/blog/2014/09/03/introducing-the-jsx-specification.html)\n\n**Releases**\n- [v0.11.2 (September 2014)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0112-september-16-2014)\n- [v0.11.1 (July 2014)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0111-july-24-2014)\n- [v0.11.0 (July 2014)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0110-july-17-2014)\n\n### React 0.10 and below {/*react-10-and-below*/}\n\n**Blog Posts**\n- [React v0.10](https://legacy.reactjs.org/blog/2014/03/21/react-v0.10.html)\n- [React v0.10 RC](https://legacy.reactjs.org/blog/2014/03/19/react-v0.10-rc1.html)\n- [React v0.9](https://legacy.reactjs.org/blog/2014/02/20/react-v0.9.html)\n- [React v0.9 RC](https://legacy.reactjs.org/blog/2014/02/16/react-v0.9-rc1.html)\n- [React Chrome Developer Tools](https://legacy.reactjs.org/blog/2014/01/02/react-chrome-developer-tools.html)\n- [React v0.8](https://legacy.reactjs.org/blog/2013/12/19/react-v0.8.0.html)\n- [React v0.5.2, v0.4.2](https://legacy.reactjs.org/blog/2013/12/18/react-v0.5.2-v0.4.2.html)\n- [React v0.5.1](https://legacy.reactjs.org/blog/2013/10/29/react-v0-5-1.html)\n- [React v0.5](https://legacy.reactjs.org/blog/2013/10/16/react-v0.5.0.html)\n- [React v0.4.1](https://legacy.reactjs.org/blog/2013/07/26/react-v0-4-1.html)\n- [React v0.4.0](https://legacy.reactjs.org/blog/2013/07/17/react-v0-4-0.html)\n- [New in React v0.4: Prop Validation and Default Values](https://legacy.reactjs.org/blog/2013/07/11/react-v0-4-prop-validation-and-default-values.html)\n- [New in React v0.4: Autobind by Default](https://legacy.reactjs.org/blog/2013/07/02/react-v0-4-autobind-by-default.html)\n- [React v0.3.3](https://legacy.reactjs.org/blog/2013/07/02/react-v0-4-autobind-by-default.html)\n\n**Releases**\n- [v0.10.0 (March 2014)](https://github.com/facebook/react/blob/main/CHANGELOG.md#0100-march-21-2014)\n- [v0.9.0 (February 2014)](https://github.com/facebook/react/blob/main/CHANGELOG.md#090-february-20-2014)\n- [v0.8.0 (December 2013)](https://github.com/facebook/react/blob/main/CHANGELOG.md#080-december-19-2013)\n- [v0.5.2 (December 2013)](https://github.com/facebook/react/blob/main/CHANGELOG.md#052-042-december-18-2013)\n- [v0.5.1 (October 2013)](https://github.com/facebook/react/blob/main/CHANGELOG.md#051-october-29-2013)\n- [v0.5.0 (October 2013)](https://github.com/facebook/react/blob/main/CHANGELOG.md#050-october-16-2013)\n- [v0.4.1 (July 2013)](https://github.com/facebook/react/blob/main/CHANGELOG.md#041-july-26-2013)\n- [v0.4.0 (July 2013)](https://github.com/facebook/react/blob/main/CHANGELOG.md#040-july-17-2013)\n- [v0.3.3 (June 2013)](https://github.com/facebook/react/blob/main/CHANGELOG.md#033-june-20-2013)\n- [v0.3.2 (May 2013)](https://github.com/facebook/react/blob/main/CHANGELOG.md#032-may-31-2013)\n- [v0.3.1 (May 2013)](https://github.com/facebook/react/blob/main/CHANGELOG.md#031-may-30-2013)\n- [v0.3.0 (May 2013)](https://github.com/facebook/react/blob/main/CHANGELOG.md#031-may-30-2013)\n\n### 初回コミット {/*initial-commit*/}\n\nReact は 2013 年 5 月 29 日にオープンソース化されました。初回コミットはこちらです：[`75897c`: Initial public release](https://github.com/facebook/react/commit/75897c2dcd1dd3a6ca46284dd37e13d22b4b16b4)\n\n最初のブログ記事もご覧ください：[なぜ React を作ったのか？](https://legacy.reactjs.org/blog/2013/06/05/why-react.html)\n\nReact は 2013 年、シアトルの Facebook 社でオープンソース化されました。\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/XxVg_s8xAms?si=466vSJrnXTn05j9A\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe>\n"
  },
  {
    "path": "src/content/warnings/invalid-aria-prop.md",
    "content": "---\ntitle: Invalid ARIA Prop Warning\n---\n\nThis warning will fire if you attempt to render a DOM element with an `aria-*` prop that does not exist in the Web Accessibility Initiative (WAI) Accessible Rich Internet Application (ARIA) [specification](https://www.w3.org/TR/wai-aria-1.1/#states_and_properties).\n\n1. If you feel that you are using a valid prop, check the spelling carefully. `aria-labelledby` and `aria-activedescendant` are often misspelled.\n\n2. If you wrote `aria-role`, you may have meant `role`.\n\n3. Otherwise, if you're on the latest version of React DOM and verified that you're using a valid property name listed in the ARIA specification, please [report a bug](https://github.com/facebook/react/issues/new/choose).\n"
  },
  {
    "path": "src/content/warnings/invalid-hook-call-warning.md",
    "content": "---\ntitle: Rules of Hooks\n---\n\nYou are probably here because you got the following error message:\n\n<ConsoleBlock level=\"error\">\n\nHooks can only be called inside the body of a function component.\n\n</ConsoleBlock>\n\nThere are three common reasons you might be seeing it:\n\n1. You might be **breaking the Rules of Hooks**.\n2. You might have **mismatching versions** of React and React DOM.\n3. You might have **more than one copy of React** in the same app.\n\nLet's look at each of these cases.\n\n## Breaking Rules of Hooks {/*breaking-rules-of-hooks*/}\n\nFunctions whose names start with `use` are called [*Hooks*](/reference/react) in React.\n\n**Don’t call Hooks inside loops, conditions, or nested functions.** Instead, always use Hooks at the top level of your React function, before any early returns. You can only call Hooks while React is rendering a function component:\n\n* ✅ Call them at the top level in the body of a [function component](/learn/your-first-component).\n* ✅ Call them at the top level in the body of a [custom Hook](/learn/reusing-logic-with-custom-hooks).\n\n```js{2-3,8-9}\nfunction Counter() {\n  // ✅ Good: top-level in a function component\n  const [count, setCount] = useState(0);\n  // ...\n}\n\nfunction useWindowWidth() {\n  // ✅ Good: top-level in a custom Hook\n  const [width, setWidth] = useState(window.innerWidth);\n  // ...\n}\n```\n\nIt’s **not** supported to call Hooks (functions starting with `use`) in any other cases, for example:\n\n* 🔴 Do not call Hooks inside conditions or loops.\n* 🔴 Do not call Hooks after a conditional `return` statement.\n* 🔴 Do not call Hooks in event handlers.\n* 🔴 Do not call Hooks in class components.\n* 🔴 Do not call Hooks inside functions passed to `useMemo`, `useReducer`, or `useEffect`.\n\nIf you break these rules, you might see this error.\n\n```js{3-4,11-12,20-21}\nfunction Bad({ cond }) {\n  if (cond) {\n    // 🔴 Bad: inside a condition (to fix, move it outside!)\n    const theme = useContext(ThemeContext);\n  }\n  // ...\n}\n\nfunction Bad() {\n  for (let i = 0; i < 10; i++) {\n    // 🔴 Bad: inside a loop (to fix, move it outside!)\n    const theme = useContext(ThemeContext);\n  }\n  // ...\n}\n\nfunction Bad({ cond }) {\n  if (cond) {\n    return;\n  }\n  // 🔴 Bad: after a conditional return (to fix, move it before the return!)\n  const theme = useContext(ThemeContext);\n  // ...\n}\n\nfunction Bad() {\n  function handleClick() {\n    // 🔴 Bad: inside an event handler (to fix, move it outside!)\n    const theme = useContext(ThemeContext);\n  }\n  // ...\n}\n\nfunction Bad() {\n  const style = useMemo(() => {\n    // 🔴 Bad: inside useMemo (to fix, move it outside!)\n    const theme = useContext(ThemeContext);\n    return createStyle(theme);\n  });\n  // ...\n}\n\nclass Bad extends React.Component {\n  render() {\n    // 🔴 Bad: inside a class component (to fix, write a function component instead of a class!)\n    useEffect(() => {})\n    // ...\n  }\n}\n```\n\nYou can use the [`eslint-plugin-react-hooks` plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks) to catch these mistakes.\n\n<Note>\n\n[Custom Hooks](/learn/reusing-logic-with-custom-hooks) *may* call other Hooks (that's their whole purpose). This works because custom Hooks are also supposed to only be called while a function component is rendering.\n\n</Note>\n\n## Mismatching Versions of React and React DOM {/*mismatching-versions-of-react-and-react-dom*/}\n\nYou might be using a version of `react-dom` (< 16.8.0) or `react-native` (< 0.59) that doesn't yet support Hooks. You can run `npm ls react-dom` or `npm ls react-native` in your application folder to check which version you're using. If you find more than one of them, this might also create problems (more on that below).\n\n## Duplicate React {/*duplicate-react*/}\n\nIn order for Hooks to work, the `react` import from your application code needs to resolve to the same module as the `react` import from inside the `react-dom` package.\n\nIf these `react` imports resolve to two different exports objects, you will see this warning. This may happen if you **accidentally end up with two copies** of the `react` package.\n\nIf you use Node for package management, you can run this check in your project folder:\n\n<TerminalBlock>\n\nnpm ls react\n\n</TerminalBlock>\n\nIf you see more than one React, you'll need to figure out why this happens and fix your dependency tree. For example, maybe a library you're using incorrectly specifies `react` as a dependency (rather than a peer dependency). Until that library is fixed, [Yarn resolutions](https://yarnpkg.com/lang/en/docs/selective-version-resolutions/) is one possible workaround.\n\nYou can also try to debug this problem by adding some logs and restarting your development server:\n\n```js\n// Add this in node_modules/react-dom/index.js\nwindow.React1 = require('react');\n\n// Add this in your component file\nrequire('react-dom');\nwindow.React2 = require('react');\nconsole.log(window.React1 === window.React2);\n```\n\nIf it prints `false` then you might have two Reacts and need to figure out why that happened. [This issue](https://github.com/facebook/react/issues/13991) includes some common reasons encountered by the community.\n\nThis problem can also come up when you use `npm link` or an equivalent. In that case, your bundler might \"see\" two Reacts — one in application folder and one in your library folder. Assuming `myapp` and `mylib` are sibling folders, one possible fix is to run `npm link ../myapp/node_modules/react` from `mylib`. This should make the library use the application's React copy.\n\n<Note>\n\nIn general, React supports using multiple independent copies on one page (for example, if an app and a third-party widget both use it). It only breaks if `require('react')` resolves differently between the component and the `react-dom` copy it was rendered with.\n\n</Note>\n\n## Other Causes {/*other-causes*/}\n\nIf none of this worked, please comment in [this issue](https://github.com/facebook/react/issues/13991) and we'll try to help. Try to create a small reproducing example — you might discover the problem as you're doing it.\n"
  },
  {
    "path": "src/content/warnings/react-dom-test-utils.md",
    "content": "---\ntitle: react-dom/test-utils Deprecation Warnings\n---\n\n## ReactDOMTestUtils.act() warning {/*reactdomtestutilsact-warning*/}\n\n`act` from `react-dom/test-utils` has been deprecated in favor of `act` from `react`.\n\nBefore:\n\n```js\nimport {act} from 'react-dom/test-utils';\n```\n\nAfter:\n\n```js\nimport {act} from 'react';\n```\n\n## Rest of ReactDOMTestUtils APIS {/*rest-of-reactdomtestutils-apis*/}\n\nAll APIs except `act` have been removed.\n\nThe React Team recommends migrating your tests to [@testing-library/react](https://testing-library.com/docs/react-testing-library/intro/) for a modern and well supported testing experience.\n\n### ReactDOMTestUtils.renderIntoDocument {/*reactdomtestutilsrenderintodocument*/}\n\n`renderIntoDocument` can be replaced with `render` from `@testing-library/react`.\n\nBefore:\n\n```js\nimport {renderIntoDocument} from 'react-dom/test-utils';\n\nrenderIntoDocument(<Component />);\n```\n\nAfter:\n\n```js\nimport {render} from '@testing-library/react';\n\nrender(<Component />);\n```\n\n### ReactDOMTestUtils.Simulate {/*reactdomtestutilssimulate*/}\n\n`Simulate` can be replaced with `fireEvent` from `@testing-library/react`.\n\nBefore:\n\n```js\nimport {Simulate} from 'react-dom/test-utils';\n\nconst element = document.querySelector('button');\nSimulate.click(element);\n```\n\nAfter:\n\n```js\nimport {fireEvent} from '@testing-library/react';\n\nconst element = document.querySelector('button');\nfireEvent.click(element);\n```\n\nBe aware that `fireEvent` dispatches an actual event on the element and doesn't just synthetically call the event handler.\n\n### List of all removed APIs {/*list-of-all-removed-apis-list-of-all-removed-apis*/}\n\n- `mockComponent()`\n- `isElement()`\n- `isElementOfType()`\n- `isDOMComponent()`\n- `isCompositeComponent()`\n- `isCompositeComponentWithType()`\n- `findAllInRenderedTree()`\n- `scryRenderedDOMComponentsWithClass()`\n- `findRenderedDOMComponentWithClass()`\n- `scryRenderedDOMComponentsWithTag()`\n- `findRenderedDOMComponentWithTag()`\n- `scryRenderedComponentsWithType()`\n- `findRenderedComponentWithType()`\n- `renderIntoDocument`\n- `Simulate`\n"
  },
  {
    "path": "src/content/warnings/react-test-renderer.md",
    "content": "---\ntitle: react-test-renderer Deprecation Warnings\n---\n\n## ReactTestRenderer.create() warning {/*reacttestrenderercreate-warning*/}\n\nreact-test-renderer is deprecated. A warning will fire whenever calling ReactTestRenderer.create() or ReactShallowRender.render(). The react-test-renderer package will remain available on NPM but will not be maintained and may break with new React features or changes to React's internals.\n\nThe React Team recommends migrating your tests to [@testing-library/react](https://testing-library.com/docs/react-testing-library/intro/) or [@testing-library/react-native](https://callstack.github.io/react-native-testing-library/docs/start/intro) for a modern and well supported testing experience.\n\n\n## new ShallowRenderer() warning {/*new-shallowrenderer-warning*/}\n\nThe react-test-renderer package no longer exports a shallow renderer at `react-test-renderer/shallow`. This was simply a repackaging of a previously extracted separate package: `react-shallow-renderer`. Therefore you can continue using the shallow renderer in the same way by installing it directly. See [Github](https://github.com/enzymejs/react-shallow-renderer) / [NPM](https://www.npmjs.com/package/react-shallow-renderer).\n"
  },
  {
    "path": "src/content/warnings/special-props.md",
    "content": "---\ntitle: Special Props Warning\n---\n\nMost props on a JSX element are passed on to the component, however, there are two special props (`ref` and `key`) which are used by React, and are thus not forwarded to the component.\n\nFor instance, you can't read `props.key` from a component. If you need to access the same value within the child component, you should pass it as a different prop (ex: `<ListItemWrapper key={result.id} id={result.id} />` and read `props.id`). While this may seem redundant, it's important to separate app logic from hints to React.\n"
  },
  {
    "path": "src/content/warnings/unknown-prop.md",
    "content": "---\ntitle: Unknown Prop Warning\n---\n\nThe unknown-prop warning will fire if you attempt to render a DOM element with a prop that is not recognized by React as a legal DOM attribute/property. You should ensure that your DOM elements do not have spurious props floating around.\n\nThere are a couple of likely reasons this warning could be appearing:\n\n1. Are you using `{...props}` or `cloneElement(element, props)`? When copying props to a child component, you should ensure that you are not accidentally forwarding props that were intended only for the parent component. See common fixes for this problem below.\n\n2. You are using a non-standard DOM attribute on a native DOM node, perhaps to represent custom data. If you are trying to attach custom data to a standard DOM element, consider using a custom data attribute as described [on MDN](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_data_attributes).\n\n3. React does not yet recognize the attribute you specified. This will likely be fixed in a future version of React. React will allow you to pass it without a warning if you write the attribute name lowercase.\n\n4. You are using a React component without an upper case, for example `<myButton />`. React interprets it as a DOM tag because React JSX transform uses the upper vs. lower case convention to distinguish between user-defined components and DOM tags. For your own React components, use PascalCase. For example, write `<MyButton />` instead of `<myButton />`.\n\n---\n\nIf you get this warning because you pass props like `{...props}`, your parent component needs to \"consume\" any prop that is intended for the parent component and not intended for the child component. Example:\n\n**Bad:** Unexpected `layout` prop is forwarded to the `div` tag.\n\n```js\nfunction MyDiv(props) {\n  if (props.layout === 'horizontal') {\n    // BAD! Because you know for sure \"layout\" is not a prop that <div> understands.\n    return <div {...props} style={getHorizontalStyle()} />\n  } else {\n    // BAD! Because you know for sure \"layout\" is not a prop that <div> understands.\n    return <div {...props} style={getVerticalStyle()} />\n  }\n}\n```\n\n**Good:** The spread syntax can be used to pull variables off props, and put the remaining props into a variable.\n\n```js\nfunction MyDiv(props) {\n  const { layout, ...rest } = props\n  if (layout === 'horizontal') {\n    return <div {...rest} style={getHorizontalStyle()} />\n  } else {\n    return <div {...rest} style={getVerticalStyle()} />\n  }\n}\n```\n\n**Good:** You can also assign the props to a new object and delete the keys that you're using from the new object. Be sure not to delete the props from the original `this.props` object, since that object should be considered immutable.\n\n```js\nfunction MyDiv(props) {\n  const divProps = Object.assign({}, props);\n  delete divProps.layout;\n\n  if (props.layout === 'horizontal') {\n    return <div {...divProps} style={getHorizontalStyle()} />\n  } else {\n    return <div {...divProps} style={getVerticalStyle()} />\n  }\n}\n```\n"
  },
  {
    "path": "src/css/ja-fix.css",
    "content": ".em-ja {\n  font-weight: bolder;\n  font-style: normal;\n}\n"
  },
  {
    "path": "src/hooks/usePendingRoute.ts",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {useRouter} from 'next/router';\nimport {useState, useRef, useEffect} from 'react';\n\nconst usePendingRoute = () => {\n  const {events} = useRouter();\n  const [pendingRoute, setPendingRoute] = useState<string | null>(null);\n  const currentRoute = useRef<string | null>(null);\n  useEffect(() => {\n    let routeTransitionTimer: any = null;\n\n    const handleRouteChangeStart = (url: string) => {\n      clearTimeout(routeTransitionTimer);\n      routeTransitionTimer = setTimeout(() => {\n        if (currentRoute.current !== url) {\n          currentRoute.current = url;\n          setPendingRoute(url);\n        }\n      }, 100);\n    };\n    const handleRouteChangeComplete = () => {\n      setPendingRoute(null);\n      clearTimeout(routeTransitionTimer);\n    };\n    events.on('routeChangeStart', handleRouteChangeStart);\n    events.on('routeChangeComplete', handleRouteChangeComplete);\n\n    return () => {\n      events.off('routeChangeStart', handleRouteChangeStart);\n      events.off('routeChangeComplete', handleRouteChangeComplete);\n      clearTimeout(routeTransitionTimer);\n    };\n  }, [events]);\n\n  return pendingRoute;\n};\n\nexport default usePendingRoute;\n"
  },
  {
    "path": "src/pages/404.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {Page} from 'components/Layout/Page';\nimport {MDXComponents} from 'components/MDX/MDXComponents';\nimport sidebarLearn from '../sidebarLearn.json';\n\nconst {Intro, MaxWidth, p: P, a: A} = MDXComponents;\n\nexport default function NotFound() {\n  return (\n    <Page toc={[]} meta={{title: 'Not Found'}} routeTree={sidebarLearn}>\n      <MaxWidth>\n        <Intro>\n          <P>This page doesn’t exist.</P>\n          <P>\n            If this is a mistake{', '}\n            <A href=\"https://github.com/reactjs/react.dev/issues/new\">\n              let us know\n            </A>\n            {', '}\n            and we will try to fix it!\n          </P>\n        </Intro>\n      </MaxWidth>\n    </Page>\n  );\n}\n"
  },
  {
    "path": "src/pages/500.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {Page} from 'components/Layout/Page';\nimport {MDXComponents} from 'components/MDX/MDXComponents';\nimport sidebarLearn from '../sidebarLearn.json';\n\nconst {Intro, MaxWidth, p: P, a: A} = MDXComponents;\n\nexport default function NotFound() {\n  return (\n    <Page\n      toc={[]}\n      routeTree={sidebarLearn}\n      meta={{title: 'Something Went Wrong'}}>\n      <MaxWidth>\n        <Intro>\n          <P>Something went very wrong.</P>\n          <P>Sorry about that.</P>\n          <P>\n            If you’d like, please{' '}\n            <A href=\"https://github.com/reactjs/react.dev/issues/new\">\n              report a bug.\n            </A>\n          </P>\n        </Intro>\n      </MaxWidth>\n    </Page>\n  );\n}\n"
  },
  {
    "path": "src/pages/[[...markdownPath]].js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {Fragment, useMemo} from 'react';\nimport {useRouter} from 'next/router';\nimport {Page} from 'components/Layout/Page';\nimport sidebarHome from '../sidebarHome.json';\nimport sidebarLearn from '../sidebarLearn.json';\nimport sidebarReference from '../sidebarReference.json';\nimport sidebarCommunity from '../sidebarCommunity.json';\nimport sidebarBlog from '../sidebarBlog.json';\nimport {MDXComponents} from 'components/MDX/MDXComponents';\nimport compileMDX from 'utils/compileMDX';\nimport {generateRssFeed} from '../utils/rss';\n\nexport default function Layout({content, toc, meta, languages}) {\n  const parsedContent = useMemo(\n    () => JSON.parse(content, reviveNodeOnClient),\n    [content]\n  );\n  const parsedToc = useMemo(() => JSON.parse(toc, reviveNodeOnClient), [toc]);\n  const section = useActiveSection();\n  let routeTree;\n  switch (section) {\n    case 'home':\n    case 'unknown':\n      routeTree = sidebarHome;\n      break;\n    case 'learn':\n      routeTree = sidebarLearn;\n      break;\n    case 'reference':\n      routeTree = sidebarReference;\n      break;\n    case 'community':\n      routeTree = sidebarCommunity;\n      break;\n    case 'blog':\n      routeTree = sidebarBlog;\n      break;\n  }\n  return (\n    <Page\n      toc={parsedToc}\n      routeTree={routeTree}\n      meta={meta}\n      section={section}\n      languages={languages}>\n      {parsedContent}\n    </Page>\n  );\n}\n\nfunction useActiveSection() {\n  const {asPath} = useRouter();\n  const cleanedPath = asPath.split(/[\\?\\#]/)[0];\n  if (cleanedPath === '/') {\n    return 'home';\n  } else if (cleanedPath.startsWith('/reference')) {\n    return 'reference';\n  } else if (asPath.startsWith('/learn')) {\n    return 'learn';\n  } else if (asPath.startsWith('/community')) {\n    return 'community';\n  } else if (asPath.startsWith('/blog')) {\n    return 'blog';\n  } else {\n    return 'unknown';\n  }\n}\n\n// Deserialize a client React tree from JSON.\nfunction reviveNodeOnClient(parentPropertyName, val) {\n  if (Array.isArray(val) && val[0] == '$r') {\n    // Assume it's a React element.\n    let Type = val[1];\n    let key = val[2];\n    if (key == null) {\n      key = parentPropertyName; // Index within a parent.\n    }\n    let props = val[3];\n    if (Type === 'wrapper') {\n      Type = Fragment;\n      props = {children: props.children};\n    }\n    if (Type in MDXComponents) {\n      Type = MDXComponents[Type];\n    }\n    if (!Type) {\n      console.error('Unknown type: ' + Type);\n      Type = Fragment;\n    }\n    return <Type key={key} {...props} />;\n  } else {\n    return val;\n  }\n}\n\n// Put MDX output into JSON for client.\nexport async function getStaticProps(context) {\n  generateRssFeed();\n  const fs = require('fs');\n  const rootDir = process.cwd() + '/src/content/';\n\n  // Read MDX from the file.\n  let path = (context.params.markdownPath || []).join('/') || 'index';\n  let mdx;\n  try {\n    mdx = fs.readFileSync(rootDir + path + '.md', 'utf8');\n  } catch {\n    mdx = fs.readFileSync(rootDir + path + '/index.md', 'utf8');\n  }\n\n  const {toc, content, meta, languages} = await compileMDX(mdx, path, {});\n  return {\n    props: {\n      toc,\n      content,\n      meta,\n      languages,\n    },\n  };\n}\n\n// Collect all MDX files for static generation.\nexport async function getStaticPaths() {\n  const {promisify} = require('util');\n  const {resolve} = require('path');\n  const fs = require('fs');\n  const readdir = promisify(fs.readdir);\n  const stat = promisify(fs.stat);\n  const rootDir = process.cwd() + '/src/content';\n\n  // Find all MD files recursively.\n  async function getFiles(dir) {\n    const subdirs = await readdir(dir);\n    const files = await Promise.all(\n      subdirs.map(async (subdir) => {\n        const res = resolve(dir, subdir);\n        return (await stat(res)).isDirectory()\n          ? getFiles(res)\n          : res.slice(rootDir.length + 1);\n      })\n    );\n    return (\n      files\n        .flat()\n        // ignores `errors/*.md`, they will be handled by `pages/errors/[errorCode].tsx`\n        .filter((file) => file.endsWith('.md') && !file.startsWith('errors/'))\n    );\n  }\n\n  // 'foo/bar/baz.md' -> ['foo', 'bar', 'baz']\n  // 'foo/bar/qux/index.md' -> ['foo', 'bar', 'qux']\n  function getSegments(file) {\n    let segments = file.slice(0, -3).replace(/\\\\/g, '/').split('/');\n    if (segments[segments.length - 1] === 'index') {\n      segments.pop();\n    }\n    return segments;\n  }\n\n  const files = await getFiles(rootDir);\n\n  const paths = files.map((file) => ({\n    params: {\n      markdownPath: getSegments(file),\n      // ^^^ CAREFUL HERE.\n      // If you rename markdownPath, update patches/next-remote-watch.patch too.\n      // Otherwise you'll break Fast Refresh for all MD files.\n    },\n  }));\n\n  return {\n    paths: paths,\n    fallback: false,\n  };\n}\n"
  },
  {
    "path": "src/pages/_app.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {useEffect} from 'react';\nimport {AppProps} from 'next/app';\nimport {useRouter} from 'next/router';\n\nimport '@docsearch/css';\nimport '../styles/algolia.css';\nimport '../styles/index.css';\nimport '../styles/sandpack.css';\n\nif (typeof window !== 'undefined') {\n  const terminationEvent = 'onpagehide' in window ? 'pagehide' : 'unload';\n  window.addEventListener(terminationEvent, function () {\n    // @ts-ignore\n    gtag('event', 'timing', {\n      event_label: 'JS Dependencies',\n      event: 'unload',\n    });\n  });\n}\n\nexport default function MyApp({Component, pageProps}: AppProps) {\n  const router = useRouter();\n\n  useEffect(() => {\n    // Taken from StackOverflow. Trying to detect both Safari desktop and mobile.\n    const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);\n    if (isSafari) {\n      // This is kind of a lie.\n      // We still rely on the manual Next.js scrollRestoration logic.\n      // However, we *also* don't want Safari grey screen during the back swipe gesture.\n      // Seems like it doesn't hurt to enable auto restore *and* Next.js logic at the same time.\n      history.scrollRestoration = 'auto';\n    } else {\n      // For other browsers, let Next.js set scrollRestoration to 'manual'.\n      // It seems to work better for Chrome and Firefox which don't animate the back swipe.\n    }\n  }, []);\n\n  useEffect(() => {\n    const handleRouteChange = (url: string) => {\n      const cleanedUrl = url.split(/[\\?\\#]/)[0];\n      // @ts-ignore\n      gtag('event', 'pageview', {\n        event_label: cleanedUrl,\n      });\n    };\n    router.events.on('routeChangeComplete', handleRouteChange);\n    return () => {\n      router.events.off('routeChangeComplete', handleRouteChange);\n    };\n  }, [router.events]);\n\n  return <Component {...pageProps} />;\n}\n"
  },
  {
    "path": "src/pages/_document.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {Html, Head, Main, NextScript} from 'next/document';\nimport {siteConfig} from '../siteConfig';\n\nconst MyDocument = () => {\n  return (\n    <Html lang={siteConfig.languageCode} dir={siteConfig.isRTL ? 'rtl' : 'ltr'}>\n      <Head />\n      <link\n        rel=\"apple-touch-icon\"\n        sizes=\"180x180\"\n        href=\"/apple-touch-icon.png\"\n      />\n      <link\n        rel=\"icon\"\n        type=\"image/png\"\n        sizes=\"32x32\"\n        href=\"/favicon-32x32.png\"\n      />\n      <link\n        rel=\"icon\"\n        type=\"image/png\"\n        sizes=\"16x16\"\n        href=\"/favicon-16x16.png\"\n      />\n      <link rel=\"manifest\" href=\"/site.webmanifest\" />\n      <link rel=\"mask-icon\" href=\"/safari-pinned-tab.svg\" color=\"#404756\" />\n      <meta name=\"msapplication-TileColor\" content=\"#2b5797\" />\n      <meta name=\"theme-color\" content=\"#23272f\" />\n      <script\n        async\n        src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GA_TRACKING_ID}`}\n      />\n      <script\n        dangerouslySetInnerHTML={{\n          __html: `window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', '${process.env.NEXT_PUBLIC_GA_TRACKING_ID}');`,\n        }}\n      />\n      <body className=\"font-text font-medium antialiased text-lg bg-wash dark:bg-wash-dark text-secondary dark:text-secondary-dark leading-base\">\n        <script\n          dangerouslySetInnerHTML={{\n            __html: `\n              (function () {\n                try {\n                  let logShown = false;\n                  function setUwu(isUwu) {\n                    try {\n                      if (isUwu) {\n                        localStorage.setItem('uwu', true);\n                        document.documentElement.classList.add('uwu');\n                        if (!logShown) {\n                          console.log('uwu mode! turn off with ?uwu=0');\n                          console.log('logo credit to @sawaratsuki1004 via https://github.com/SAWARATSUKI/ServiceLogos');\n                          logShown = true;\n                        }\n                      } else {\n                        localStorage.removeItem('uwu');\n                        document.documentElement.classList.remove('uwu');\n                        console.log('uwu mode off. turn on with ?uwu');\n                      }\n                    } catch (err) { }\n                  }\n                  window.__setUwu = setUwu;\n                  function checkQueryParam() {\n                    const params = new URLSearchParams(window.location.search);\n                    const value = params.get('uwu');\n                    switch(value) {\n                      case '':\n                      case 'true':\n                      case '1':\n                        return true;\n                      case 'false':\n                      case '0':\n                        return false;\n                      default:\n                        return null;\n                    }\n                  }\n                  function checkLocalStorage() {\n                    try {\n                      return localStorage.getItem('uwu') === 'true';\n                    } catch (err) {\n                      return false;\n                    }\n                  }\n                  const uwuQueryParam = checkQueryParam();\n                  if (uwuQueryParam != null) {\n                    setUwu(uwuQueryParam);\n                  } else if (checkLocalStorage()) {\n                    document.documentElement.classList.add('uwu');\n                  }\n                } catch (err) { }\n              })();\n            `,\n          }}\n        />\n        <script\n          dangerouslySetInnerHTML={{\n            __html: `\n                (function () {\n                  function setTheme(newTheme) {\n                    window.__theme = newTheme;\n                    if (newTheme === 'dark') {\n                      document.documentElement.classList.add('dark');\n                    } else if (newTheme === 'light') {\n                      document.documentElement.classList.remove('dark');\n                    }\n                  }\n\n                  var preferredTheme;\n                  try {\n                    preferredTheme = localStorage.getItem('theme');\n                  } catch (err) { }\n\n                  window.__setPreferredTheme = function(newTheme) {\n                    preferredTheme = newTheme;\n                    setTheme(newTheme);\n                    try {\n                      localStorage.setItem('theme', newTheme);\n                    } catch (err) { }\n                  };\n\n                  var initialTheme = preferredTheme;\n                  var darkQuery = window.matchMedia('(prefers-color-scheme: dark)');\n\n                  if (!initialTheme) {\n                    initialTheme = darkQuery.matches ? 'dark' : 'light';\n                  }\n                  setTheme(initialTheme);\n\n                  darkQuery.addEventListener('change', function (e) {\n                    if (!preferredTheme) {\n                      setTheme(e.matches ? 'dark' : 'light');\n                    }\n                  });\n\n                  // Detect whether the browser is Mac to display platform specific content\n                  // An example of such content can be the keyboard shortcut displayed in the search bar\n                  document.documentElement.classList.add(\n                      window.navigator.platform.includes('Mac')\n                      ? \"platform-mac\" \n                      : \"platform-win\"\n                  );\n                })();\n              `,\n          }}\n        />\n        <Main />\n        <NextScript />\n      </body>\n    </Html>\n  );\n};\n\nexport default MyDocument;\n"
  },
  {
    "path": "src/pages/api/md/[...path].ts",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport type {NextApiRequest, NextApiResponse} from 'next';\nimport fs from 'fs';\nimport path from 'path';\n\nconst FOOTER = `\n---\n\n## Sitemap\n\n[Overview of all docs pages](/llms.txt)\n`;\n\nexport default function handler(req: NextApiRequest, res: NextApiResponse) {\n  const pathSegments = req.query.path;\n  if (!pathSegments) {\n    return res.status(404).send('Not found');\n  }\n\n  const filePath = Array.isArray(pathSegments)\n    ? pathSegments.join('/')\n    : pathSegments;\n\n  // Block /index.md URLs - use /foo.md instead of /foo/index.md\n  if (filePath.endsWith('/index') || filePath === 'index') {\n    return res.status(404).send('Not found');\n  }\n\n  // Try exact path first, then with /index\n  const candidates = [\n    path.join(process.cwd(), 'src/content', filePath + '.md'),\n    path.join(process.cwd(), 'src/content', filePath, 'index.md'),\n  ];\n\n  for (const fullPath of candidates) {\n    try {\n      const content = fs.readFileSync(fullPath, 'utf8');\n      res.setHeader('Content-Type', 'text/plain; charset=utf-8');\n      res.setHeader('Cache-Control', 'public, max-age=3600');\n      return res.status(200).send(content + FOOTER);\n    } catch {\n      // Try next candidate\n    }\n  }\n\n  res.status(404).send('Not found');\n}\n"
  },
  {
    "path": "src/pages/errors/[errorCode].tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport {Fragment, useMemo} from 'react';\nimport {Page} from 'components/Layout/Page';\nimport {MDXComponents} from 'components/MDX/MDXComponents';\nimport sidebarLearn from 'sidebarLearn.json';\nimport type {RouteItem} from 'components/Layout/getRouteMeta';\nimport {GetStaticPaths, GetStaticProps, InferGetStaticPropsType} from 'next';\nimport {ErrorDecoderContext} from 'components/ErrorDecoderContext';\nimport compileMDX from 'utils/compileMDX';\n\ninterface ErrorDecoderProps {\n  errorCode: string | null;\n  errorMessage: string | null;\n  content: string;\n  toc: string;\n  meta: any;\n}\n\nexport default function ErrorDecoderPage({\n  errorMessage,\n  errorCode,\n  content,\n}: InferGetStaticPropsType<typeof getStaticProps>) {\n  const parsedContent = useMemo<React.ReactNode>(\n    () => JSON.parse(content, reviveNodeOnClient),\n    [content]\n  );\n\n  return (\n    <ErrorDecoderContext value={{errorMessage, errorCode}}>\n      <Page\n        toc={[]}\n        meta={{\n          title: errorCode\n            ? `Minified React error #${errorCode}`\n            : 'Minified Error Decoder',\n        }}\n        routeTree={sidebarLearn as RouteItem}\n        section=\"unknown\">\n        <div>{parsedContent}</div>\n        {/* <MaxWidth>\n          <P>\n            We highly recommend using the development build locally when debugging\n            your app since it tracks additional debug info and provides helpful\n            warnings about potential problems in your apps, but if you encounter\n            an exception while using the production build, this page will\n            reassemble the original error message.\n          </P>\n          <ErrorDecoder />\n        </MaxWidth> */}\n      </Page>\n    </ErrorDecoderContext>\n  );\n}\n\n// Deserialize a client React tree from JSON.\nfunction reviveNodeOnClient(parentPropertyName: unknown, val: any) {\n  if (Array.isArray(val) && val[0] == '$r') {\n    // Assume it's a React element.\n    let Type = val[1];\n    let key = val[2];\n    if (key == null) {\n      key = parentPropertyName; // Index within a parent.\n    }\n    let props = val[3];\n    if (Type === 'wrapper') {\n      Type = Fragment;\n      props = {children: props.children};\n    }\n    if (Type in MDXComponents) {\n      Type = MDXComponents[Type as keyof typeof MDXComponents];\n    }\n    if (!Type) {\n      console.error('Unknown type: ' + Type);\n      Type = Fragment;\n    }\n    return <Type key={key} {...props} />;\n  } else {\n    return val;\n  }\n}\n\n/**\n * Next.js Page Router doesn't have a way to cache specific data fetching request.\n * But since Next.js uses limited number of workers, keep \"cachedErrorCodes\" as a\n * module level memory cache can reduce the number of requests down to once per worker.\n *\n * TODO: use `next/unstable_cache` when migrating to Next.js App Router\n */\nlet cachedErrorCodes: Record<string, string> | null = null;\n\nexport const getStaticProps: GetStaticProps<ErrorDecoderProps> = async ({\n  params,\n}) => {\n  const errorCodes: {[key: string]: string} = (cachedErrorCodes ||= await (\n    await fetch(\n      'https://raw.githubusercontent.com/facebook/react/main/scripts/error-codes/codes.json'\n    )\n  ).json());\n\n  const code = typeof params?.errorCode === 'string' ? params?.errorCode : null;\n  if (code && !errorCodes[code]) {\n    return {\n      notFound: true,\n    };\n  }\n\n  const fs = require('fs');\n  const rootDir = process.cwd() + '/src/content/errors';\n\n  // Read MDX from the file.\n  let path = params?.errorCode || 'index';\n  let mdx;\n  try {\n    mdx = fs.readFileSync(rootDir + '/' + path + '.md', 'utf8');\n  } catch {\n    // if [errorCode].md is not found, fallback to generic.md\n    mdx = fs.readFileSync(rootDir + '/generic.md', 'utf8');\n  }\n\n  const {content, toc, meta} = await compileMDX(mdx, path, {code, errorCodes});\n\n  return {\n    props: {\n      content,\n      toc,\n      meta,\n      errorCode: code,\n      errorMessage: code ? errorCodes[code] : null,\n    },\n  };\n};\n\nexport const getStaticPaths: GetStaticPaths = async () => {\n  /**\n   * Fetch error codes from GitHub\n   */\n  const errorCodes = (cachedErrorCodes ||= await (\n    await fetch(\n      'https://raw.githubusercontent.com/facebook/react/main/scripts/error-codes/codes.json'\n    )\n  ).json());\n\n  const paths = Object.keys(errorCodes).map((code) => ({\n    params: {\n      errorCode: code,\n    },\n  }));\n\n  return {\n    paths,\n    fallback: 'blocking',\n  };\n};\n"
  },
  {
    "path": "src/pages/errors/index.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport ErrorDecoderPage from './[errorCode]';\nexport default ErrorDecoderPage;\nexport {getStaticProps} from './[errorCode]';\n"
  },
  {
    "path": "src/pages/llms.txt.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport type {GetServerSideProps} from 'next';\nimport {siteConfig} from '../siteConfig';\nimport sidebarLearn from '../sidebarLearn.json';\nimport sidebarReference from '../sidebarReference.json';\n\ninterface RouteItem {\n  title?: string;\n  path?: string;\n  routes?: RouteItem[];\n  hasSectionHeader?: boolean;\n  sectionHeader?: string;\n}\n\ninterface Sidebar {\n  title: string;\n  routes: RouteItem[];\n}\n\ninterface Page {\n  title: string;\n  url: string;\n}\n\ninterface SubGroup {\n  heading: string;\n  pages: Page[];\n}\n\ninterface Section {\n  heading: string | null;\n  pages: Page[];\n  subGroups: SubGroup[];\n}\n\n// Clean up section header names (remove version placeholders)\nfunction cleanSectionHeader(header: string): string {\n  return header\n    .replace(/@\\{\\{version\\}\\}/g, '')\n    .replace(/-/g, ' ')\n    .split(' ')\n    .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n    .join(' ')\n    .trim();\n}\n\n// Extract routes for sidebars that use hasSectionHeader to define major sections\n// (like the API Reference sidebar)\nfunction extractSectionedRoutes(\n  routes: RouteItem[],\n  baseUrl: string\n): Section[] {\n  const sections: Section[] = [];\n  let currentSection: Section | null = null;\n\n  for (const route of routes) {\n    // Skip external links\n    if (route.path?.startsWith('http')) {\n      continue;\n    }\n\n    // Start a new section when we hit a section header\n    if (route.hasSectionHeader && route.sectionHeader) {\n      if (currentSection) {\n        sections.push(currentSection);\n      }\n      currentSection = {\n        heading: cleanSectionHeader(route.sectionHeader),\n        pages: [],\n        subGroups: [],\n      };\n      continue;\n    }\n\n    // If no section started yet, skip\n    if (!currentSection) {\n      continue;\n    }\n\n    // Route with children - create a sub-group\n    if (route.title && route.routes && route.routes.length > 0) {\n      const subGroup: SubGroup = {\n        heading: route.title,\n        pages: [],\n      };\n\n      // Include parent page if it has a path\n      if (route.path) {\n        subGroup.pages.push({\n          title: route.title,\n          url: `${baseUrl}${route.path}.md`,\n        });\n      }\n\n      // Add child pages\n      for (const child of route.routes) {\n        if (child.title && child.path && !child.path.startsWith('http')) {\n          subGroup.pages.push({\n            title: child.title,\n            url: `${baseUrl}${child.path}.md`,\n          });\n        }\n      }\n\n      if (subGroup.pages.length > 0) {\n        currentSection.subGroups.push(subGroup);\n      }\n    }\n    // Single page without children\n    else if (route.title && route.path) {\n      currentSection.pages.push({\n        title: route.title,\n        url: `${baseUrl}${route.path}.md`,\n      });\n    }\n  }\n\n  // Don't forget the last section\n  if (currentSection) {\n    sections.push(currentSection);\n  }\n\n  return sections;\n}\n\n// Extract routes for sidebars that use routes with children as the primary grouping\n// (like the Learn sidebar)\nfunction extractGroupedRoutes(\n  routes: RouteItem[],\n  baseUrl: string\n): SubGroup[] {\n  const groups: SubGroup[] = [];\n\n  for (const route of routes) {\n    // Skip section headers\n    if (route.hasSectionHeader) {\n      continue;\n    }\n\n    // Skip external links\n    if (route.path?.startsWith('http')) {\n      continue;\n    }\n\n    // Route with children - create a group\n    if (route.title && route.routes && route.routes.length > 0) {\n      const pages: Page[] = [];\n\n      // Include parent page if it has a path\n      if (route.path) {\n        pages.push({\n          title: route.title,\n          url: `${baseUrl}${route.path}.md`,\n        });\n      }\n\n      // Add child pages\n      for (const child of route.routes) {\n        if (child.title && child.path && !child.path.startsWith('http')) {\n          pages.push({\n            title: child.title,\n            url: `${baseUrl}${child.path}.md`,\n          });\n        }\n      }\n\n      if (pages.length > 0) {\n        groups.push({\n          heading: route.title,\n          pages,\n        });\n      }\n    }\n    // Single page without children - group under its own heading\n    else if (route.title && route.path) {\n      groups.push({\n        heading: route.title,\n        pages: [\n          {\n            title: route.title,\n            url: `${baseUrl}${route.path}.md`,\n          },\n        ],\n      });\n    }\n  }\n\n  return groups;\n}\n\n// Check if sidebar uses section headers as primary grouping\nfunction usesSectionHeaders(routes: RouteItem[]): boolean {\n  return routes.some((r) => r.hasSectionHeader && r.sectionHeader);\n}\n\nexport const getServerSideProps: GetServerSideProps = async ({res}) => {\n  const subdomain =\n    siteConfig.languageCode === 'en' ? '' : siteConfig.languageCode + '.';\n  const baseUrl = 'https://' + subdomain + 'react.dev';\n\n  const lines = [\n    '# React Documentation',\n    '',\n    '> The library for web and native user interfaces.',\n  ];\n\n  const sidebars: Sidebar[] = [\n    sidebarLearn as Sidebar,\n    sidebarReference as Sidebar,\n  ];\n\n  for (const sidebar of sidebars) {\n    lines.push('');\n    lines.push(`## ${sidebar.title}`);\n\n    if (usesSectionHeaders(sidebar.routes)) {\n      // API Reference style: section headers define major groups\n      const sections = extractSectionedRoutes(sidebar.routes, baseUrl);\n      for (const section of sections) {\n        if (section.heading) {\n          lines.push('');\n          lines.push(`### ${section.heading}`);\n        }\n\n        // Output pages directly under section\n        for (const page of section.pages) {\n          lines.push(`- [${page.title}](${page.url})`);\n        }\n\n        // Output sub-groups with #### headings\n        for (const subGroup of section.subGroups) {\n          lines.push('');\n          lines.push(`#### ${subGroup.heading}`);\n          for (const page of subGroup.pages) {\n            lines.push(`- [${page.title}](${page.url})`);\n          }\n        }\n      }\n    } else {\n      // Learn style: routes with children define groups\n      const groups = extractGroupedRoutes(sidebar.routes, baseUrl);\n      for (const group of groups) {\n        lines.push('');\n        lines.push(`### ${group.heading}`);\n        for (const page of group.pages) {\n          lines.push(`- [${page.title}](${page.url})`);\n        }\n      }\n    }\n  }\n\n  const content = lines.join('\\n');\n\n  res.setHeader('Content-Type', 'text/plain; charset=utf-8');\n  res.write(content);\n  res.end();\n\n  return {props: {}};\n};\n\nexport default function LlmsTxt() {\n  return null;\n}\n"
  },
  {
    "path": "src/sidebarBlog.json",
    "content": "{\n  \"title\": \"Blog\",\n  \"path\": \"/blog\",\n  \"routes\": [\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"STAY INFORMED\"\n    },\n    {\n      \"title\": \"Blog\",\n      \"path\": \"/blog\",\n      \"skipBreadcrumb\": true,\n      \"routes\": [\n        {\n          \"title\": \"React Server Components におけるサービス拒否攻撃とソースコード露出\",\n          \"titleForHomepage\": \"RSC における追加の脆弱性\",\n          \"icon\": \"blog\",\n          \"date\": \"December 11, 2025\",\n          \"path\": \"/blog/2025/12/11/denial-of-service-and-source-code-exposure-in-react-server-components\"\n        },\n        {\n          \"title\": \"React Server Components における重大なセキュリティ脆弱性\",\n          \"titleForHomepage\": \"React Server Components の脆弱性\",\n          \"icon\": \"blog\",\n          \"date\": \"December 3, 2025\",\n          \"path\": \"/blog/2025/12/03/critical-security-vulnerability-in-react-server-components\"\n        },\n        {\n          \"title\": \"React Conf 2025 振り返り\",\n          \"titleForHomepage\": \"React Conf 2025 振り返り\",\n          \"icon\": \"blog\",\n          \"date\": \"October 16, 2025\",\n          \"path\": \"/blog/2025/10/16/react-conf-2025-recap\"\n        },\n        {\n          \"title\": \"React Compiler v1.0\",\n          \"titleForHomepage\": \"React Compiler v1.0\",\n          \"icon\": \"blog\",\n          \"date\": \"October 7, 2025\",\n          \"path\": \"/blog/2025/10/07/react-compiler-1\"\n        },\n        {\n          \"title\": \"React Foundation 設立\",\n          \"titleForHomepage\": \"React Foundation 設立\",\n          \"icon\": \"blog\",\n          \"date\": \"October 7, 2025\",\n          \"path\": \"/blog/2025/10/07/introducing-the-react-foundation\"\n        },\n        {\n          \"title\": \"React 19.2\",\n          \"titleForHomepage\": \"React 19.2\",\n          \"icon\": \"blog\",\n          \"date\": \"October 1, 2025\",\n          \"path\": \"/blog/2025/10/01/react-19-2\"\n        },\n        {\n          \"title\": \"React Labs: ビュー遷移、Activity、その他もろもろ\",\n          \"titleForHomepage\": \"ビュー遷移と Activity\",\n          \"icon\": \"blog\",\n          \"date\": \"April 23, 2025\",\n          \"path\": \"/blog/2025/04/23/react-labs-view-transitions-activity-and-more\"\n        },\n        {\n          \"title\": \"Sunsetting Create React App\",\n          \"titleForHomepage\": \"Sunsetting Create React App\",\n          \"icon\": \"blog\",\n          \"date\": \"February 14, 2025\",\n          \"path\": \"/blog/2025/02/14/sunsetting-create-react-app\"\n        },\n        {\n          \"title\": \"React 19\",\n          \"titleForHomepage\": \"React 19\",\n          \"icon\": \"blog\",\n          \"date\": \"December 05, 2024\",\n          \"path\": \"/blog/2024/12/05/react-19\"\n        },\n        {\n          \"title\": \"React Compiler Beta リリースとロードマップ\",\n          \"titleForHomepage\": \"React Compiler Beta リリースとロードマップ\",\n          \"icon\": \"blog\",\n          \"date\": \"October 21, 2024\",\n          \"path\": \"/blog/2024/10/21/react-compiler-beta-release\"\n        },\n        {\n          \"title\": \"React Conf 2024 振り返り\",\n          \"titleForHomepage\": \"React Conf 2024 振り返り\",\n          \"icon\": \"blog\",\n          \"date\": \"May 22, 2024\",\n          \"path\": \"/blog/2024/05/22/react-conf-2024-recap\"\n        },\n        {\n          \"title\": \"React 19 RC\",\n          \"titleForHomepage\": \"React 19 RC\",\n          \"icon\": \"blog\",\n          \"date\": \"April 25, 2024\",\n          \"path\": \"/blog/2024/04/25/react-19\"\n        },\n        {\n          \"title\": \"React 19 RC アップグレードガイド\",\n          \"titleForHomepage\": \"React 19 Beta アップグレードガイド\",\n          \"icon\": \"blog\",\n          \"date\": \"April 25, 2024\",\n          \"path\": \"/blog/2024/04/25/react-19-upgrade-guide\"\n        },\n        {\n          \"title\": \"React Labs: What We've Been Working On – February 2024\",\n          \"titleForHomepage\": \"React Labs: February 2024\",\n          \"icon\": \"labs\",\n          \"date\": \"February 15, 2024\",\n          \"path\": \"/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024\"\n        },\n        {\n          \"title\": \"React Canary: Meta 外での段階的な新機能導入\",\n          \"titleForHomepage\": \"React Canaries: 段階的な新機能導入\",\n          \"icon\": \"blog\",\n          \"date\": \"May 3, 2023\",\n          \"path\": \"/blog/2023/05/03/react-canaries\"\n        },\n        {\n          \"title\": \"React Labs: 私達のこれまでの取り組み - 2023年3月版\",\n          \"titleForHomepage\": \"React Labs: 2023年3月\",\n          \"icon\": \"labs\",\n          \"date\": \"March 22, 2023\",\n          \"path\": \"/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023\"\n        },\n        {\n          \"title\": \"react.dev のご紹介\",\n          \"titleForHomepage\": \"react.dev のご紹介\",\n          \"icon\": \"blog\",\n          \"date\": \"March 16, 2023\",\n          \"path\": \"/blog/2023/03/16/introducing-react-dev\"\n        },\n        {\n          \"title\": \"React Labs: 私達のこれまでの取り組み - 2022年6月版\",\n          \"titleForHomepage\": \"React Labs: 2022年6月\",\n          \"icon\": \"labs\",\n          \"date\": \"June 15, 2022\",\n          \"path\": \"/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022\"\n        },\n        {\n          \"title\": \"React v18.0\",\n          \"titleForHomepage\": \"React v18.0\",\n          \"icon\": \"blog\",\n          \"date\": \"March 29, 2022\",\n          \"path\": \"/blog/2022/03/29/react-v18\"\n        },\n        {\n          \"title\": \"React 18 アップグレードガイド\",\n          \"titleForHomepage\": \"React 18 アップグレードガイド\",\n          \"icon\": \"blog\",\n          \"date\": \"March 8, 2022\",\n          \"path\": \"/blog/2022/03/08/react-18-upgrade-guide\"\n        },\n        {\n          \"title\": \"React Conf 2021 振り返り\",\n          \"titleForHomepage\": \"React Conf 2021 振り返り\",\n          \"icon\": \"blog\",\n          \"date\": \"December 17, 2021\",\n          \"path\": \"/blog/2021/12/17/react-conf-2021-recap\"\n        },\n        {\n          \"title\": \"React 18に向けてのプラン\",\n          \"titleForHomepage\": \"React 18に向けてのプラン\",\n          \"icon\": \"blog\",\n          \"date\": \"June 8, 2021\",\n          \"path\": \"/blog/2021/06/08/the-plan-for-react-18\"\n        },\n        {\n          \"title\": \"React Server Components の紹介\",\n          \"titleForHomepage\": \"React Server Components の紹介\",\n          \"icon\": \"labs\",\n          \"date\": \"December 21, 2020\",\n          \"path\": \"/blog/2020/12/21/data-fetching-with-react-server-components\"\n        },\n        {\n          \"title\": \"過去の投稿\",\n          \"path\": \"https://ja.reactjs.org/blog/all.html\"\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "src/sidebarCommunity.json",
    "content": "{\n  \"title\": \"コミュニティ\",\n  \"path\": \"/community\",\n  \"routes\": [\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"参加する\"\n    },\n    {\n      \"title\": \"コミュニティ\",\n      \"path\": \"/community\",\n      \"skipBreadcrumb\": true,\n      \"routes\": [\n        {\n          \"title\": \"React カンファレンス\",\n          \"path\": \"/community/conferences\"\n        },\n        {\n          \"title\": \"React ミーティング\",\n          \"path\": \"/community/meetups\"\n        },\n        {\n          \"title\": \"React 関連動画\",\n          \"path\": \"/community/videos\"\n        },\n        {\n          \"title\": \"チーム紹介\",\n          \"path\": \"/community/team\"\n        },\n        {\n          \"title\": \"ドキュメント貢献者\",\n          \"path\": \"/community/docs-contributors\"\n        },\n        {\n          \"title\": \"翻訳\",\n          \"path\": \"/community/translations\"\n        },\n        {\n          \"title\": \"謝辞\",\n          \"path\": \"/community/acknowledgements\"\n        },\n        {\n          \"title\": \"バージョニングポリシー\",\n          \"path\": \"/community/versioning-policy\"\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "src/sidebarHome.json",
    "content": "{\n  \"title\": \"React Docs\",\n  \"path\": \"/\",\n  \"routes\": [\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"スタートガイド\"\n    },\n    {\n      \"title\": \"クイックスタート\",\n      \"path\": \"/learn\"\n    },\n    {\n      \"title\": \"インストール\",\n      \"path\": \"/learn/installation\"\n    },\n    {\n      \"title\": \"Setup\",\n      \"path\": \"/learn/setup\"\n    },\n    {\n      \"title\": \"React Compiler\",\n      \"path\": \"/learn/react-compiler\"\n    },\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"REACT を学ぶ\"\n    },\n    {\n      \"title\": \"UI の記述\",\n      \"path\": \"/learn/describing-the-ui\"\n    },\n    {\n      \"title\": \"インタラクティビティの追加\",\n      \"path\": \"/learn/adding-interactivity\"\n    },\n    {\n      \"title\": \"state の管理\",\n      \"path\": \"/learn/managing-state\"\n    },\n    {\n      \"title\": \"避難ハッチ\",\n      \"path\": \"/learn/escape-hatches\"\n    },\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"REACT API\"\n    },\n    {\n      \"title\": \"フック\",\n      \"path\": \"/reference/react\"\n    },\n    {\n      \"title\": \"コンポーネント\",\n      \"path\": \"/reference/react/components\"\n    },\n    {\n      \"title\": \"API\",\n      \"path\": \"/reference/react/apis\"\n    },\n    {\n      \"title\": \"レガシー API\",\n      \"path\": \"/reference/react/legacy\"\n    },\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"REACT DOM API\"\n    },\n    {\n      \"title\": \"コンポーネント\",\n      \"path\": \"/reference/react-dom/components\"\n    },\n    {\n      \"title\": \"API\",\n      \"path\": \"/reference/react-dom\"\n    },\n    {\n      \"title\": \"クライアント API\",\n      \"path\": \"/reference/react-dom/client\"\n    },\n    {\n      \"title\": \"サーバ API\",\n      \"path\": \"/reference/react-dom/server\"\n    },\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"REACT COMPILER API\"\n    },\n    {\n      \"title\": \"設定\",\n      \"path\": \"/reference/react-compiler/configuration\"\n    },\n    {\n      \"title\": \"Directives\",\n      \"path\": \"/reference/react-compiler/directives\"\n    },\n    {\n      \"title\": \"Compiling Libraries\",\n      \"path\": \"/reference/react-compiler/compiling-libraries\"\n    },\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"コミュニティに参加する\"\n    },\n    {\n      \"title\": \"React コミュニティ\",\n      \"path\": \"/community\"\n    },\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"最新の情報\"\n    },\n    {\n      \"title\": \"React ブログ\",\n      \"path\": \"/blog\"\n    }\n  ]\n}\n"
  },
  {
    "path": "src/sidebarLearn.json",
    "content": "{\n  \"title\": \"Learn React\",\n  \"path\": \"/learn\",\n  \"routes\": [\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"スタートガイド\"\n    },\n    {\n      \"title\": \"クイックスタート\",\n      \"path\": \"/learn\",\n      \"routes\": [\n        {\n          \"title\": \"チュートリアル：三目並べ\",\n          \"path\": \"/learn/tutorial-tic-tac-toe\"\n        },\n        {\n          \"title\": \"React の流儀\",\n          \"path\": \"/learn/thinking-in-react\"\n        }\n      ]\n    },\n    {\n      \"title\": \"インストール\",\n      \"path\": \"/learn/installation\",\n      \"routes\": [\n        {\n          \"title\": \"React アプリの作成\",\n          \"path\": \"/learn/creating-a-react-app\"\n        },\n        {\n          \"title\": \"ゼロからの React アプリ構築\",\n          \"path\": \"/learn/build-a-react-app-from-scratch\"\n        },\n        {\n          \"title\": \"既存プロジェクトに React を追加する\",\n          \"path\": \"/learn/add-react-to-an-existing-project\"\n        }\n      ]\n    },\n    {\n      \"title\": \"セットアップ\",\n      \"path\": \"/learn/setup\",\n      \"routes\": [\n        {\n          \"title\": \"エディタのセットアップ\",\n          \"path\": \"/learn/editor-setup\"\n        },\n        {\n          \"title\": \"TypeScript の使用\",\n          \"path\": \"/learn/typescript\"\n        },\n        {\n          \"title\": \"React Developer Tools\",\n          \"path\": \"/learn/react-developer-tools\"\n        }\n      ]\n    },\n    {\n      \"title\": \"React Compiler\",\n      \"path\": \"/learn/react-compiler\",\n      \"canary\": true,\n      \"routes\": [\n        {\n          \"title\": \"はじめに\",\n          \"path\": \"/learn/react-compiler/introduction\"\n        },\n        {\n          \"title\": \"インストール\",\n          \"path\": \"/learn/react-compiler/installation\"\n        },\n        {\n          \"title\": \"段階的な導入\",\n          \"path\": \"/learn/react-compiler/incremental-adoption\"\n        },\n        {\n          \"title\": \"デバッグとトラブルシューティング\",\n          \"path\": \"/learn/react-compiler/debugging\"\n        }\n      ]\n    },\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"REACT を学ぶ\"\n    },\n    {\n      \"title\": \"UI の記述\",\n      \"tags\": [],\n      \"path\": \"/learn/describing-the-ui\",\n      \"routes\": [\n        {\n          \"title\": \"初めてのコンポーネント\",\n          \"path\": \"/learn/your-first-component\"\n        },\n        {\n          \"title\": \"コンポーネントのインポートとエクスポート\",\n          \"path\": \"/learn/importing-and-exporting-components\"\n        },\n        {\n          \"title\": \"JSX でマークアップを記述する\",\n          \"path\": \"/learn/writing-markup-with-jsx\"\n        },\n        {\n          \"title\": \"JSX に波括弧で JavaScript を含める\",\n          \"path\": \"/learn/javascript-in-jsx-with-curly-braces\"\n        },\n        {\n          \"title\": \"コンポーネントに props を渡す\",\n          \"path\": \"/learn/passing-props-to-a-component\"\n        },\n        {\n          \"title\": \"条件付きレンダー\",\n          \"path\": \"/learn/conditional-rendering\"\n        },\n        {\n          \"title\": \"リストのレンダー\",\n          \"path\": \"/learn/rendering-lists\"\n        },\n        {\n          \"title\": \"コンポーネントを純粋に保つ\",\n          \"path\": \"/learn/keeping-components-pure\"\n        },\n        {\n          \"title\": \"UI をツリーとして理解する\",\n          \"path\": \"/learn/understanding-your-ui-as-a-tree\"\n        }\n      ]\n    },\n    {\n      \"title\": \"インタラクティビティの追加\",\n      \"path\": \"/learn/adding-interactivity\",\n      \"tags\": [],\n      \"routes\": [\n        {\n          \"title\": \"イベントへの応答\",\n          \"path\": \"/learn/responding-to-events\"\n        },\n        {\n          \"title\": \"state：コンポーネントのメモリ\",\n          \"path\": \"/learn/state-a-components-memory\"\n        },\n        {\n          \"title\": \"レンダーとコミット\",\n          \"path\": \"/learn/render-and-commit\"\n        },\n        {\n          \"title\": \"state はスナップショットである\",\n          \"path\": \"/learn/state-as-a-snapshot\"\n        },\n        {\n          \"title\": \"一連の state の更新をキューに入れる\",\n          \"path\": \"/learn/queueing-a-series-of-state-updates\"\n        },\n        {\n          \"title\": \"state 内のオブジェクトの更新\",\n          \"path\": \"/learn/updating-objects-in-state\"\n        },\n        {\n          \"title\": \"state 内の配列の更新\",\n          \"path\": \"/learn/updating-arrays-in-state\"\n        }\n      ]\n    },\n    {\n      \"title\": \"state の管理\",\n      \"path\": \"/learn/managing-state\",\n      \"tags\": [\"intermediate\"],\n      \"routes\": [\n        {\n          \"title\": \"state を使って入力に反応する\",\n          \"path\": \"/learn/reacting-to-input-with-state\"\n        },\n        {\n          \"title\": \"state 構造の選択\",\n          \"path\": \"/learn/choosing-the-state-structure\"\n        },\n        {\n          \"title\": \"コンポーネント間で state を共有する\",\n          \"path\": \"/learn/sharing-state-between-components\"\n        },\n        {\n          \"title\": \"state の保持とリセット\",\n          \"path\": \"/learn/preserving-and-resetting-state\"\n        },\n        {\n          \"title\": \"state ロジックをリデューサに抽出する\",\n          \"path\": \"/learn/extracting-state-logic-into-a-reducer\"\n        },\n        {\n          \"title\": \"コンテクストで深くデータを受け渡す\",\n          \"path\": \"/learn/passing-data-deeply-with-context\"\n        },\n        {\n          \"title\": \"リデューサとコンテクストでスケールアップ\",\n          \"path\": \"/learn/scaling-up-with-reducer-and-context\"\n        }\n      ]\n    },\n    {\n      \"title\": \"避難ハッチ\",\n      \"path\": \"/learn/escape-hatches\",\n      \"tags\": [\"advanced\"],\n      \"routes\": [\n        {\n          \"title\": \"ref で値を参照する\",\n          \"path\": \"/learn/referencing-values-with-refs\"\n        },\n        {\n          \"title\": \"ref で DOM を操作する\",\n          \"path\": \"/learn/manipulating-the-dom-with-refs\"\n        },\n        {\n          \"title\": \"エフェクトを使って同期を行う\",\n          \"path\": \"/learn/synchronizing-with-effects\"\n        },\n        {\n          \"title\": \"そのエフェクトは不要かも\",\n          \"path\": \"/learn/you-might-not-need-an-effect\"\n        },\n        {\n          \"title\": \"リアクティブなエフェクトのライフサイクル\",\n          \"path\": \"/learn/lifecycle-of-reactive-effects\"\n        },\n        {\n          \"title\": \"エフェクトからイベントを分離する\",\n          \"path\": \"/learn/separating-events-from-effects\"\n        },\n        {\n          \"title\": \"エフェクトから依存値を取り除く\",\n          \"path\": \"/learn/removing-effect-dependencies\"\n        },\n        {\n          \"title\": \"カスタムフックでロジックを再利用する\",\n          \"path\": \"/learn/reusing-logic-with-custom-hooks\"\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "src/sidebarReference.json",
    "content": "{\n  \"title\": \"API Reference\",\n  \"path\": \"/reference/react\",\n  \"routes\": [\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"react@{{version}}\"\n    },\n    {\n      \"title\": \"概要\",\n      \"path\": \"/reference/react\"\n    },\n    {\n      \"title\": \"フック\",\n      \"path\": \"/reference/react/hooks\",\n      \"routes\": [\n        {\n          \"title\": \"useActionState\",\n          \"path\": \"/reference/react/useActionState\"\n        },\n        {\n          \"title\": \"useCallback\",\n          \"path\": \"/reference/react/useCallback\"\n        },\n        {\n          \"title\": \"useContext\",\n          \"path\": \"/reference/react/useContext\"\n        },\n        {\n          \"title\": \"useDebugValue\",\n          \"path\": \"/reference/react/useDebugValue\"\n        },\n        {\n          \"title\": \"useDeferredValue\",\n          \"path\": \"/reference/react/useDeferredValue\"\n        },\n        {\n          \"title\": \"useEffect\",\n          \"path\": \"/reference/react/useEffect\"\n        },\n        {\n          \"title\": \"useEffectEvent\",\n          \"path\": \"/reference/react/useEffectEvent\"\n        },\n        {\n          \"title\": \"useId\",\n          \"path\": \"/reference/react/useId\"\n        },\n        {\n          \"title\": \"useImperativeHandle\",\n          \"path\": \"/reference/react/useImperativeHandle\"\n        },\n        {\n          \"title\": \"useInsertionEffect\",\n          \"path\": \"/reference/react/useInsertionEffect\"\n        },\n        {\n          \"title\": \"useLayoutEffect\",\n          \"path\": \"/reference/react/useLayoutEffect\"\n        },\n        {\n          \"title\": \"useMemo\",\n          \"path\": \"/reference/react/useMemo\"\n        },\n        {\n          \"title\": \"useOptimistic\",\n          \"path\": \"/reference/react/useOptimistic\"\n        },\n        {\n          \"title\": \"useReducer\",\n          \"path\": \"/reference/react/useReducer\"\n        },\n        {\n          \"title\": \"useRef\",\n          \"path\": \"/reference/react/useRef\"\n        },\n        {\n          \"title\": \"useState\",\n          \"path\": \"/reference/react/useState\"\n        },\n        {\n          \"title\": \"useSyncExternalStore\",\n          \"path\": \"/reference/react/useSyncExternalStore\"\n        },\n        {\n          \"title\": \"useTransition\",\n          \"path\": \"/reference/react/useTransition\"\n        }\n      ]\n    },\n    {\n      \"title\": \"コンポーネント\",\n      \"path\": \"/reference/react/components\",\n      \"routes\": [\n        {\n          \"title\": \"<Fragment> (<>)\",\n          \"path\": \"/reference/react/Fragment\"\n        },\n        {\n          \"title\": \"<Profiler>\",\n          \"path\": \"/reference/react/Profiler\"\n        },\n        {\n          \"title\": \"<StrictMode>\",\n          \"path\": \"/reference/react/StrictMode\"\n        },\n        {\n          \"title\": \"<Suspense>\",\n          \"path\": \"/reference/react/Suspense\"\n        },\n        {\n          \"title\": \"<Activity>\",\n          \"path\": \"/reference/react/Activity\"\n        },\n        {\n          \"title\": \"<ViewTransition>\",\n          \"path\": \"/reference/react/ViewTransition\",\n          \"version\": \"canary\"\n        }\n      ]\n    },\n    {\n      \"title\": \"API\",\n      \"path\": \"/reference/react/apis\",\n      \"routes\": [\n        {\n          \"title\": \"act\",\n          \"path\": \"/reference/react/act\"\n        },\n        {\n          \"title\": \"addTransitionType\",\n          \"path\": \"/reference/react/addTransitionType\",\n          \"version\": \"canary\"\n        },\n        {\n          \"title\": \"cache\",\n          \"path\": \"/reference/react/cache\"\n        },\n        {\n          \"title\": \"cacheSignal\",\n          \"path\": \"/reference/react/cacheSignal\"\n        },\n        {\n          \"title\": \"captureOwnerStack\",\n          \"path\": \"/reference/react/captureOwnerStack\"\n        },\n        {\n          \"title\": \"createContext\",\n          \"path\": \"/reference/react/createContext\"\n        },\n        {\n          \"title\": \"lazy\",\n          \"path\": \"/reference/react/lazy\"\n        },\n        {\n          \"title\": \"memo\",\n          \"path\": \"/reference/react/memo\"\n        },\n        {\n          \"title\": \"startTransition\",\n          \"path\": \"/reference/react/startTransition\"\n        },\n        {\n          \"title\": \"use\",\n          \"path\": \"/reference/react/use\"\n        },\n        {\n          \"title\": \"experimental_taintObjectReference\",\n          \"path\": \"/reference/react/experimental_taintObjectReference\",\n          \"version\": \"experimental\"\n        },\n        {\n          \"title\": \"experimental_taintUniqueValue\",\n          \"path\": \"/reference/react/experimental_taintUniqueValue\",\n          \"version\": \"experimental\"\n        }\n      ]\n    },\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"react-dom@{{version}}\"\n    },\n    {\n      \"title\": \"フック\",\n      \"path\": \"/reference/react-dom/hooks\",\n      \"routes\": [\n        {\n          \"title\": \"useFormStatus\",\n          \"path\": \"/reference/react-dom/hooks/useFormStatus\"\n        }\n      ]\n    },\n    {\n      \"title\": \"コンポーネント\",\n      \"path\": \"/reference/react-dom/components\",\n      \"routes\": [\n        {\n          \"title\": \"<div> などの一般的なコンポーネント\",\n          \"path\": \"/reference/react-dom/components/common\"\n        },\n        {\n          \"title\": \"<form>\",\n          \"path\": \"/reference/react-dom/components/form\"\n        },\n        {\n          \"title\": \"<input>\",\n          \"path\": \"/reference/react-dom/components/input\"\n        },\n        {\n          \"title\": \"<option>\",\n          \"path\": \"/reference/react-dom/components/option\"\n        },\n        {\n          \"title\": \"<progress>\",\n          \"path\": \"/reference/react-dom/components/progress\"\n        },\n        {\n          \"title\": \"<select>\",\n          \"path\": \"/reference/react-dom/components/select\"\n        },\n        {\n          \"title\": \"<textarea>\",\n          \"path\": \"/reference/react-dom/components/textarea\"\n        },\n        {\n          \"title\": \"<link>\",\n          \"path\": \"/reference/react-dom/components/link\"\n        },\n        {\n          \"title\": \"<meta>\",\n          \"path\": \"/reference/react-dom/components/meta\"\n        },\n        {\n          \"title\": \"<script>\",\n          \"path\": \"/reference/react-dom/components/script\"\n        },\n        {\n          \"title\": \"<style>\",\n          \"path\": \"/reference/react-dom/components/style\"\n        },\n        {\n          \"title\": \"<title>\",\n          \"path\": \"/reference/react-dom/components/title\"\n        }\n      ]\n    },\n    {\n      \"title\": \"API\",\n      \"path\": \"/reference/react-dom\",\n      \"routes\": [\n        {\n          \"title\": \"createPortal\",\n          \"path\": \"/reference/react-dom/createPortal\"\n        },\n        {\n          \"title\": \"flushSync\",\n          \"path\": \"/reference/react-dom/flushSync\"\n        },\n        {\n          \"title\": \"preconnect\",\n          \"path\": \"/reference/react-dom/preconnect\"\n        },\n        {\n          \"title\": \"prefetchDNS\",\n          \"path\": \"/reference/react-dom/prefetchDNS\"\n        },\n        {\n          \"title\": \"preinit\",\n          \"path\": \"/reference/react-dom/preinit\"\n        },\n        {\n          \"title\": \"preinitModule\",\n          \"path\": \"/reference/react-dom/preinitModule\"\n        },\n        {\n          \"title\": \"preload\",\n          \"path\": \"/reference/react-dom/preload\"\n        },\n        {\n          \"title\": \"preloadModule\",\n          \"path\": \"/reference/react-dom/preloadModule\"\n        }\n      ]\n    },\n    {\n      \"title\": \"クライアント API\",\n      \"path\": \"/reference/react-dom/client\",\n      \"routes\": [\n        {\n          \"title\": \"createRoot\",\n          \"path\": \"/reference/react-dom/client/createRoot\"\n        },\n        {\n          \"title\": \"hydrateRoot\",\n          \"path\": \"/reference/react-dom/client/hydrateRoot\"\n        }\n      ]\n    },\n    {\n      \"title\": \"サーバ API\",\n      \"path\": \"/reference/react-dom/server\",\n      \"routes\": [\n        {\n          \"title\": \"renderToPipeableStream\",\n          \"path\": \"/reference/react-dom/server/renderToPipeableStream\"\n        },\n        {\n          \"title\": \"renderToReadableStream\",\n          \"path\": \"/reference/react-dom/server/renderToReadableStream\"\n        },\n        {\n          \"title\": \"renderToStaticMarkup\",\n          \"path\": \"/reference/react-dom/server/renderToStaticMarkup\"\n        },\n        {\n          \"title\": \"renderToString\",\n          \"path\": \"/reference/react-dom/server/renderToString\"\n        },\n        {\n          \"title\": \"resume\",\n          \"path\": \"/reference/react-dom/server/resume\"\n        },\n        {\n          \"title\": \"resumeToPipeableStream\",\n          \"path\": \"/reference/react-dom/server/resumeToPipeableStream\"\n        }\n      ]\n    },\n    {\n      \"title\": \"静的サイト用 API\",\n      \"path\": \"/reference/react-dom/static\",\n      \"routes\": [\n        {\n          \"title\": \"prerender\",\n          \"path\": \"/reference/react-dom/static/prerender\"\n        },\n        {\n          \"title\": \"prerenderToNodeStream\",\n          \"path\": \"/reference/react-dom/static/prerenderToNodeStream\"\n        },\n        {\n          \"title\": \"resumeAndPrerender\",\n          \"path\": \"/reference/react-dom/static/resumeAndPrerender\"\n        },\n        {\n          \"title\": \"resumeAndPrerenderToNodeStream\",\n          \"path\": \"/reference/react-dom/static/resumeAndPrerenderToNodeStream\"\n        }\n      ]\n    },\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"React Compiler\"\n    },\n    {\n      \"title\": \"設定\",\n      \"path\": \"/reference/react-compiler/configuration\",\n      \"routes\": [\n        {\n          \"title\": \"compilationMode\",\n          \"path\": \"/reference/react-compiler/compilationMode\"\n        },\n        {\n          \"title\": \"gating\",\n          \"path\": \"/reference/react-compiler/gating\"\n        },\n        {\n          \"title\": \"logger\",\n          \"path\": \"/reference/react-compiler/logger\"\n        },\n        {\n          \"title\": \"panicThreshold\",\n          \"path\": \"/reference/react-compiler/panicThreshold\"\n        },\n        {\n          \"title\": \"target\",\n          \"path\": \"/reference/react-compiler/target\"\n        }\n      ]\n    },\n    {\n      \"title\": \"Directives\",\n      \"path\": \"/reference/react-compiler/directives\",\n      \"routes\": [\n        {\n          \"title\": \"\\\"use memo\\\"\",\n          \"path\": \"/reference/react-compiler/directives/use-memo\"\n        },\n        {\n          \"title\": \"\\\"use no memo\\\"\",\n          \"path\": \"/reference/react-compiler/directives/use-no-memo\"\n        }\n      ]\n    },\n    {\n      \"title\": \"Compiling Libraries\",\n      \"path\": \"/reference/react-compiler/compiling-libraries\"\n    },\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"React DevTools\"\n    },\n    {\n      \"title\": \"React Performance tracks\",\n      \"path\": \"/reference/dev-tools/react-performance-tracks\"\n    },\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"eslint-plugin-react-hooks\"\n    },\n    {\n      \"title\": \"Lints\",\n      \"path\": \"/reference/eslint-plugin-react-hooks\",\n      \"routes\": [\n        {\n          \"title\": \"exhaustive-deps\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/exhaustive-deps\"\n        },\n        {\n          \"title\": \"rules-of-hooks\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/rules-of-hooks\"\n        },\n        {\n          \"title\": \"component-hook-factories\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/component-hook-factories\"\n        },\n        {\n          \"title\": \"config\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/config\"\n        },\n        {\n          \"title\": \"error-boundaries\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/error-boundaries\"\n        },\n        {\n          \"title\": \"gating\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/gating\"\n        },\n        {\n          \"title\": \"globals\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/globals\"\n        },\n        {\n          \"title\": \"immutability\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/immutability\"\n        },\n        {\n          \"title\": \"incompatible-library\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/incompatible-library\"\n        },\n\n        {\n          \"title\": \"preserve-manual-memoization\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/preserve-manual-memoization\"\n        },\n        {\n          \"title\": \"purity\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/purity\"\n        },\n        {\n          \"title\": \"refs\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/refs\"\n        },\n\n        {\n          \"title\": \"set-state-in-effect\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/set-state-in-effect\"\n        },\n        {\n          \"title\": \"set-state-in-render\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/set-state-in-render\"\n        },\n        {\n          \"title\": \"static-components\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/static-components\"\n        },\n        {\n          \"title\": \"unsupported-syntax\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/unsupported-syntax\"\n        },\n        {\n          \"title\": \"use-memo\",\n          \"path\": \"/reference/eslint-plugin-react-hooks/lints/use-memo\"\n        }\n      ]\n    },\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"React のルール\"\n    },\n    {\n      \"title\": \"概要\",\n      \"path\": \"/reference/rules\",\n      \"routes\": [\n        {\n          \"title\": \"コンポーネントとフックを純粋に保つ\",\n          \"path\": \"/reference/rules/components-and-hooks-must-be-pure\"\n        },\n        {\n          \"title\": \"コンポーネントやフックを呼び出すのは React\",\n          \"path\": \"/reference/rules/react-calls-components-and-hooks\"\n        },\n        {\n          \"title\": \"フックのルール\",\n          \"path\": \"/reference/rules/rules-of-hooks\"\n        }\n      ]\n    },\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"React Server Components\"\n    },\n    {\n      \"title\": \"サーバコンポーネント\",\n      \"path\": \"/reference/rsc/server-components\"\n    },\n    {\n      \"title\": \"サーバ関数\",\n      \"path\": \"/reference/rsc/server-functions\"\n    },\n    {\n      \"title\": \"ディレクティブ\",\n      \"path\": \"/reference/rsc/directives\",\n      \"routes\": [\n        {\n          \"title\": \"'use client'\",\n          \"path\": \"/reference/rsc/use-client\"\n        },\n        {\n          \"title\": \"'use server'\",\n          \"path\": \"/reference/rsc/use-server\"\n        }\n      ]\n    },\n    {\n      \"hasSectionHeader\": true,\n      \"sectionHeader\": \"レガシー API\"\n    },\n    {\n      \"title\": \"レガシー React API\",\n      \"path\": \"/reference/react/legacy\",\n      \"routes\": [\n        {\n          \"title\": \"Children\",\n          \"path\": \"/reference/react/Children\"\n        },\n        {\n          \"title\": \"cloneElement\",\n          \"path\": \"/reference/react/cloneElement\"\n        },\n        {\n          \"title\": \"Component\",\n          \"path\": \"/reference/react/Component\"\n        },\n        {\n          \"title\": \"createElement\",\n          \"path\": \"/reference/react/createElement\"\n        },\n        {\n          \"title\": \"createRef\",\n          \"path\": \"/reference/react/createRef\"\n        },\n        {\n          \"title\": \"forwardRef\",\n          \"path\": \"/reference/react/forwardRef\"\n        },\n        {\n          \"title\": \"isValidElement\",\n          \"path\": \"/reference/react/isValidElement\"\n        },\n        {\n          \"title\": \"PureComponent\",\n          \"path\": \"/reference/react/PureComponent\"\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "src/siteConfig.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\nexports.siteConfig = {\n  version: '19.2',\n  // --------------------------------------\n  // Translations should replace these lines:\n  languageCode: 'ja',\n  hasLegacySite: true,\n  isRTL: false,\n  // --------------------------------------\n  copyright: `Copyright © ${new Date().getFullYear()} Facebook Inc. All Rights Reserved.`,\n  repoUrl: 'https://github.com/facebook/react',\n  twitterUrl: 'https://twitter.com/reactjs',\n  algolia: {\n    appId: '1FCF9AYYAT',\n    apiKey: '1b7ad4e1c89e645e351e59d40544eda1',\n    indexName: 'beta-react',\n  },\n};\n"
  },
  {
    "path": "src/styles/algolia.css",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\n/* Algolia v3 overrides */\n:root {\n  --docsearch-modal-background: #fff;\n  --docsearch-highlight-color: #087ea4;\n  --docsearch-primary-color: #0074a6;\n  --docsearch-container-background: rgba(52, 58, 70, 0.8);\n  --docsearch-modal-shadow: none;\n  --docsearch-searchbox-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);\n  --ifm-z-index-fixed: 1000;\n  --docsearch-hit-height: 48px;\n  --docsearch-searchbox-height: 72px;\n  --docsearch-footer-height: 108px;\n  --docsearch-icon-stroke-width: 1.4;\n  --hover-overlay: rgba(0, 0, 0, 0.05);\n  --fds-animation-fade-in: cubic-bezier(0, 0, 1, 1);\n  --fds-animation-fade-out: cubic-bezier(0, 0, 1, 1);\n}\nhtml.dark {\n  --docsearch-modal-background: #23272f;\n  --docsearch-hit-background: #23272f;\n  --docsearch-highlight-color: #149eca;\n}\n.DocSearch--active #__next {\n  -webkit-filter: blur(0px);\n  filter: blur(0px);\n}\n.DocSearch-SearchBar {\n  @apply py-3;\n  @apply px-5;\n}\n.DocSearch-Form {\n  @apply rounded-full;\n  @apply shadow-none;\n  @apply text-base;\n  @apply text-tertiary;\n  @apply bg-gray-10;\n  @apply focus:outline-link;\n  @apply h-10;\n  @apply focus-within:outline-none;\n}\nhtml.dark .DocSearch-Form {\n  @apply bg-gray-80;\n}\n.DocSearch-Dropdown {\n  @apply px-0;\n  @apply h-full;\n  @apply max-h-full;\n}\n.DocSearch-Commands {\n  @apply w-full;\n  @apply justify-between;\n  @apply border-t;\n  @apply border-border;\n  @apply pt-4;\n}\nhtml.dark .DocSearch-Commands {\n  @apply border-border-dark;\n}\n.DocSearch-Commands-Key {\n  @apply shadow-none me-[.4rem] ms-0;\n  @apply bg-gray-10;\n  @apply text-primary;\n}\n.DocSearch-Logo {\n  @apply pt-4;\n  @apply pb-2;\n}\n.DocSearch-Logo svg {\n  @apply ms-2 me-0;\n}\n.DocSearch-Label {\n  @apply text-xs;\n}\n.DocSearch-Footer {\n  @apply bg-transparent;\n  @apply flex-col-reverse;\n  @apply items-start;\n  @apply h-auto;\n  @apply pb-2;\n  @apply px-5;\n  @apply shadow-none;\n}\nhtml.dark .DocSearch-Footer {\n  @apply bg-wash-dark;\n}\n.DocSearch-Input {\n  @apply py-3 ps-2 pe-0;\n  @apply text-base;\n  @apply leading-tight;\n  @apply text-primary;\n  @apply appearance-none !important;\n  @apply focus:outline-link !important;\n}\nhtml.dark .DocSearch-Input {\n  @apply text-primary-dark;\n}\n.DocSearch-Hit a {\n  @apply rounded-e-lg;\n  @apply rounded-s-none;\n  @apply shadow-none;\n  @apply ps-5;\n}\n.DocSearch-Hit-source {\n  @apply uppercase;\n  @apply tracking-wide;\n  @apply text-sm;\n  @apply text-secondary;\n  @apply font-bold;\n  @apply pt-0;\n  @apply ps-5;\n  @apply m-0;\n}\nhtml.dark .DocSearch-Hit-source {\n  @apply text-secondary-dark;\n}\n.DocSearch-Dropdown ul {\n  @apply me-5;\n}\n.DocSearch-Hit-title {\n  @apply text-base;\n  @apply text-primary;\n  @apply font-normal;\n  @apply text-ellipsis;\n  @apply whitespace-nowrap;\n  @apply overflow-hidden;\n}\nhtml.dark .DocSearch-Hit-title {\n  @apply text-primary-dark;\n}\n.DocSearch-Hit-path {\n  @apply font-normal;\n}\n.DocSearch-LoadingIndicator svg,\n.DocSearch-MagnifierLabel svg {\n  width: 15px;\n  height: 15px;\n  @apply text-gray-30;\n  @apply mx-1;\n}\n.DocSearch-Container {\n  @apply flex;\n  @apply justify-center;\n}\n.DocSearch-Modal {\n  margin: 0;\n  @apply flex;\n  @apply justify-center;\n  @apply max-w-3xl;\n  @apply w-full;\n  @apply rounded-2xl;\n  @apply overflow-hidden;\n  @apply my-4;\n  @apply shadow-nav;\n}\nhtml.dark .DocSearch-Modal {\n  @apply shadow-nav-dark;\n}\n.DocSearch-Cancel {\n  @apply ps-5;\n  @apply ms-0;\n  @apply text-base;\n  @apply text-link;\n  @apply font-normal;\n}\n.DocSearch-Screen-Icon {\n  @apply flex;\n  @apply justify-center;\n}\n.DocSearch-Help {\n  @apply text-center;\n  @apply mt-4;\n}\n@media (max-width: 1024px) {\n  .DocSearch-Modal {\n    @apply max-w-full;\n  }\n  .DocSearch-Cancel {\n    @apply inline-block;\n  }\n  .DocSearch-Commands {\n    @apply hidden;\n  }\n  .DocSearch-Modal {\n    @apply rounded-none;\n    @apply my-0;\n  }\n}\n.DocSearch-Search-Icon {\n  height: 20px;\n  width: 20px;\n  stroke-width: 1.6;\n  @apply text-gray-60;\n}\n"
  },
  {
    "path": "src/styles/index.css",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n  @font-face {\n    font-family: 'Source Code Pro';\n    font-style: normal;\n    font-weight: 400;\n    font-display: swap;\n    src: url('https://react.dev/fonts/Source-Code-Pro-Regular.woff2')\n      format('woff2');\n  }\n\n  @font-face {\n    font-family: 'Source Code Pro';\n    font-style: normal;\n    font-weight: 700;\n    font-display: swap;\n    src: url('https://react.dev/fonts/Source-Code-Pro-Bold.woff2')\n      format('woff2');\n  }\n\n  /* Latin */\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_W_Md.woff2')\n      format('woff2');\n    font-weight: 500;\n    font-style: normal;\n    font-display: swap;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_W_MdIt.woff2')\n      format('woff2');\n    font-weight: 500;\n    font-style: italic;\n    font-display: swap;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_W_SBd.woff2')\n      format('woff2');\n    font-weight: 600;\n    font-style: normal;\n    font-display: swap;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_W_SBdIt.woff2')\n      format('woff2');\n    font-weight: 600;\n    font-style: italic;\n    font-display: swap;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_W_Bd.woff2')\n      format('woff2');\n    font-weight: 700;\n    font-style: normal;\n    font-display: swap;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_W_BdIt.woff2')\n      format('woff2');\n    font-weight: 700;\n    font-style: italic;\n    font-display: swap;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_W_Rg.woff2')\n      format('woff2');\n    font-weight: 400;\n    font-style: normal;\n    font-display: swap;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_W_It.woff2')\n      format('woff2');\n    font-weight: 400;\n    font-style: italic;\n    font-display: swap;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_W_Md.woff2')\n      format('woff2');\n    font-weight: 500;\n    font-style: normal;\n    font-display: swap;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_W_MdIt.woff2')\n      format('woff2');\n    font-weight: 500;\n    font-style: italic;\n    font-display: swap;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_W_Bd.woff2')\n      format('woff2');\n    font-weight: 700;\n    font-style: normal;\n    font-display: swap;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_W_BdIt.woff2')\n      format('woff2');\n    font-weight: 700;\n    font-style: italic;\n    font-display: swap;\n  }\n\n  /* Arabic */\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_Arbc_W_Md.woff2')\n      format('woff2');\n    font-weight: 500;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0600-06FF;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_Arbc_W_SBd.woff2')\n      format('woff2');\n    font-weight: 600;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0600-06FF;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_Arbc_W_Bd.woff2')\n      format('woff2');\n    font-weight: 700;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0600-06FF;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_Arbc_W_Rg.woff2')\n      format('woff2');\n    font-weight: 400;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0600-06FF;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_Arbc_W_Md.woff2')\n      format('woff2');\n    font-weight: 500;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0600-06FF;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_Arbc_W_Bd.woff2')\n      format('woff2');\n    font-weight: 700;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0600-06FF;\n  }\n\n  /* Cyrillic */\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_Cyrl_W_Md.woff2')\n      format('woff2');\n    font-weight: 500;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0400-045F, U+2116;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_Cyrl_W_SBd.woff2')\n      format('woff2');\n    font-weight: 600;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0400-045F, U+2116;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_Cyrl_W_Bd.woff2')\n      format('woff2');\n    font-weight: 700;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0400-045F, U+2116;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_Cyrl_W_Rg.woff2')\n      format('woff2');\n    font-weight: 400;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0400-045F, U+2116;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_Cyrl_W_Md.woff2')\n      format('woff2');\n    font-weight: 500;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0400-045F, U+2116;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_Cyrl_W_Bd.woff2')\n      format('woff2');\n    font-weight: 700;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0400-045F, U+2116;\n  }\n\n  /* Devanagari */\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_Deva_W_Md.woff2')\n      format('woff2');\n    font-weight: 500;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8,\n      U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_Deva_W_SBd.woff2')\n      format('woff2');\n    font-weight: 600;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8,\n      U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_Deva_W_Bd.woff2')\n      format('woff2');\n    font-weight: 700;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8,\n      U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_Deva_W_Rg.woff2')\n      format('woff2');\n    font-weight: 400;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8,\n      U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_Deva_W_Md.woff2')\n      format('woff2');\n    font-weight: 500;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8,\n      U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_Deva_W_Bd.woff2')\n      format('woff2');\n    font-weight: 700;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8,\n      U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;\n  }\n\n  /* Vietnamese */\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_Viet_W_Md.woff2')\n      format('woff2');\n    font-weight: 500;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_Viet_W_SBd.woff2')\n      format('woff2');\n    font-weight: 600;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Display';\n    src: url('https://react.dev/fonts/Optimistic_Display_Viet_W_Bd.woff2')\n      format('woff2');\n    font-weight: 700;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_Viet_W_Rg.woff2')\n      format('woff2');\n    font-weight: 400;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_Viet_W_Md.woff2')\n      format('woff2');\n    font-weight: 500;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;\n  }\n\n  @font-face {\n    font-family: 'Optimistic Text';\n    src: url('https://react.dev/fonts/Optimistic_Text_Viet_W_Bd.woff2')\n      format('woff2');\n    font-weight: 700;\n    font-style: normal;\n    font-display: swap;\n    unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;\n  }\n\n  /* Write your own custom base styles here */\n  html {\n    color-scheme: light;\n\n    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n    -webkit-tap-highlight-color: transparent;\n  }\n\n  html.dark {\n    color-scheme: dark;\n  }\n\n  html .dark-image {\n    display: none;\n  }\n\n  html .light-image {\n    display: block;\n  }\n\n  html.dark .dark-image {\n    display: block;\n  }\n\n  html.dark .light-image {\n    display: none;\n  }\n\n  /* Hide all content that's relevant only to a specific platform */\n  html.platform-mac [data-platform='win'] {\n    display: none;\n  }\n\n  html.platform-win [data-platform='mac'] {\n    display: none;\n  }\n\n  html,\n  body {\n    padding: 0;\n    margin: 0;\n  }\n\n  @media screen and (max-width: 1023px) {\n    body {\n      overflow-x: hidden;\n    }\n  }\n\n  /* Start purging... */\n  /* Force GPU Accelerated scrolling, credit: Twitter Lite */\n  .scrolling-gpu {\n    transform: translateZ(0);\n  }\n\n  @layer utilities {\n    .text-7xl {\n      font-size: 5rem;\n    }\n\n    .text-8xl {\n      font-size: 6rem;\n    }\n  }\n\n  a > code {\n    color: #087ea4 !important; /* blue-50 */\n    text-decoration: none !important;\n  }\n\n  html.dark a > code {\n    color: #58c4dc !important; /* blue-40 */\n  }\n\n  .text-code {\n    font-size: calc(1em - 10%) !important;\n  }\n\n  .text-gradient {\n    background-clip: text;\n    -webkit-background-clip: text;\n    -webkit-text-fill-color: transparent;\n    box-decoration-break: clone;\n    background-repeat: no-repeat;\n    color: transparent;\n  }\n\n  .text-gradient-electric-blue {\n    background-image: linear-gradient(45deg, #61dafb, #0072ff);\n  }\n  /* Stop purging. */\n  /* Your own custom utilities */\n\n  details {\n    margin-bottom: 1rem;\n  }\n\n  table {\n    width: 100%;\n    margin-bottom: 1rem;\n    display: block;\n    overflow-x: auto;\n  }\n\n  table td,\n  table th {\n    padding: 0.75rem;\n    vertical-align: top;\n    border: 1px solid #dee2e6;\n    overflow: auto;\n  }\n\n  summary::-webkit-details-marker {\n    display: none;\n  }\n\n  /*\n   * Hopefully when scrollbar-color lands everywhere,\n   * (and not just in FF), we'll be able to keep just this.\n   */\n  html .no-bg-scrollbar {\n    scrollbar-color: rgba(0, 0, 0, 0.2) transparent;\n  }\n  html.dark .no-bg-scrollbar {\n    scrollbar-color: rgba(255, 255, 255, 0.2) transparent;\n  }\n  /*\n   * Until then, we have ... this.\n   * If you're changing this, make sure you've tested:\n   * - Different browsers (Chrome, Safari, FF)\n   * - Dark and light modes\n   * - System scrollbar settings (\"always on\" vs \"when scrolling\")\n   * - Switching between modes should never jump width\n   * - When you interact with a sidebar, it should always be visible\n   * - For each combination, test overflowing and non-overflowing sidebar\n   * I've spent hours picking these so I expect no less diligence from you.\n   */\n  html .no-bg-scrollbar::-webkit-scrollbar,\n  html .no-bg-scrollbar::-webkit-scrollbar-track {\n    background-color: transparent;\n  }\n  html .no-bg-scrollbar:hover::-webkit-scrollbar-thumb,\n  html .no-bg-scrollbar:focus::-webkit-scrollbar-thumb,\n  html .no-bg-scrollbar:focus-within::-webkit-scrollbar-thumb,\n  html .no-bg-scrollbar:active::-webkit-scrollbar-thumb {\n    background-color: rgba(0, 0, 0, 0.2);\n    border: 4px solid transparent;\n    background-clip: content-box;\n    border-radius: 10px;\n  }\n  html .no-bg-scrollbar::-webkit-scrollbar-thumb:hover,\n  html .no-bg-scrollbar::-webkit-scrollbar-thumb:active {\n    background-color: rgba(0, 0, 0, 0.35) !important;\n  }\n  html.dark .no-bg-scrollbar:hover::-webkit-scrollbar-thumb,\n  html.dark .no-bg-scrollbar:focus::-webkit-scrollbar-thumb,\n  html.dark .no-bg-scrollbar:focus-within::-webkit-scrollbar-thumb,\n  html.dark .no-bg-scrollbar:active::-webkit-scrollbar-thumb {\n    background-color: rgba(255, 255, 255, 0.2);\n  }\n  html.dark .no-bg-scrollbar::-webkit-scrollbar-thumb:hover,\n  html.dark .no-bg-scrollbar::-webkit-scrollbar-thumb:active {\n    background-color: rgba(255, 255, 255, 0.35) !important;\n  }\n}\n\n@layer utilities {\n  [class*='space-x-'] {\n    @apply rtl:space-x-reverse;\n  }\n}\n\n.code-step * {\n  color: inherit !important;\n}\n\n.code-step code {\n  background: none !important;\n  padding: 2px !important;\n}\n\n.dark .console-block code {\n  background: rgba(235 236 240 / 0.05) !important;\n  color: rgba(208, 125, 119) !important;\n}\n\n.console-block code {\n  background: rgba(235 236 240 / 0.95) !important;\n  color: rgb(166, 66, 58) !important;\n}\n\nhtml.dark .code-step * {\n  color: inherit !important;\n}\n\n.mdx-heading {\n  scroll-margin-top: calc(4rem + 20px);\n  /* Space for the anchor */\n  padding-inline-end: 1em;\n}\n\n.mdx-heading:before {\n  height: 6rem;\n  margin-top: -6rem;\n  visibility: hidden;\n  content: '';\n}\n.mdx-heading .mdx-header-anchor {\n  /* Prevent the anchor from\n     overflowing to its own line */\n  height: 0px;\n  width: 0px;\n}\n.mdx-heading .mdx-header-anchor svg {\n  display: inline;\n}\n.mdx-heading .mdx-header-anchor svg {\n  visibility: hidden;\n}\n.mdx-heading:hover .mdx-header-anchor svg {\n  visibility: visible;\n}\n.mdx-heading .mdx-header-anchor:focus svg {\n  visibility: visible;\n}\n\n.mdx-blockquote > span > p:first-of-type {\n  margin-bottom: 0;\n}\n.mdx-blockquote > span > p:last-of-type {\n  margin-bottom: 1rem;\n}\n.mdx-illustration-block {\n  display: flex;\n  flex-direction: row;\n  flex-wrap: nowrap;\n  justify-content: center;\n  align-content: stretch;\n  align-items: stretch;\n  gap: 42px;\n}\nol.mdx-illustration-block {\n  gap: 60px;\n}\n.mdx-illustration-block li {\n  display: flex;\n  align-items: flex-start;\n  align-content: stretch;\n  justify-content: space-around;\n  position: relative;\n  padding: 1rem;\n}\n.mdx-illustration-block figure {\n  display: flex;\n  flex-direction: column;\n  align-content: center;\n  align-items: center;\n\n  justify-content: space-between;\n  position: relative;\n  height: 100%;\n}\n.mdx-illustration-block li:after {\n  content: ' ';\n  display: block;\n  position: absolute;\n  top: 50%;\n  inset-inline-end: 100%;\n  transform: translateY(-50%);\n  width: 60px;\n  height: 49px;\n  background: center / contain no-repeat url('/images/g_arrow.png');\n}\n.mdx-illustration-block li:first-child:after {\n  content: ' ';\n  display: none;\n}\n.mdx-illustration-block img {\n  max-height: 250px;\n  width: 100%;\n}\n@media (max-width: 680px) {\n  .mdx-illustration-block {\n    flex-direction: column;\n  }\n  .mdx-illustration-block img {\n    max-height: 200px;\n    width: auto;\n  }\n  .mdx-illustration-block li:after {\n    top: 0;\n    inset-inline-start: 50%;\n    inset-inline-end: auto;\n    transform: translateX(-50%) translateY(-100%) rotate(90deg);\n  }\n}\n\n@keyframes fadein {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n.animation-pulse-button {\n  animation: pulse-button 2s infinite;\n}\n\n.animation-pulse-shadow {\n  animation: pulse-shadow 2s infinite;\n}\n\n@keyframes pulse-button {\n  0% {\n    transform: scale(0.9);\n  }\n  70% {\n    transform: scale(1);\n  }\n  100% {\n    transform: scale(0.9);\n  }\n}\n\n@keyframes pulse-shadow {\n  0% {\n    transform: scale(0.65);\n    opacity: 1;\n  }\n\n  70% {\n    transform: scale(1);\n    opacity: 0;\n  }\n\n  100% {\n    transform: scale(0.65);\n    opacity: 0;\n  }\n}\n\n@keyframes progressbar {\n  from {\n    width: 0;\n  }\n  to {\n    width: 100%;\n  }\n}\n\n.uwu-visible {\n  display: none;\n}\n.uwu-hidden {\n  display: flex;\n}\n\n.uwu .uwu-visible {\n  display: flex;\n}\n\n.uwu .uwu-hidden {\n  display: none;\n}\n"
  },
  {
    "path": "src/styles/sandpack.css",
    "content": "/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\n.sandpack {\n  color-scheme: inherit;\n  -webkit-font-smoothing: antialiased;\n\n  --sp-space-1: 4px;\n  --sp-space-2: 8px;\n  --sp-space-3: 12px;\n  --sp-space-4: 16px;\n  --sp-space-5: 20px;\n  --sp-space-6: 24px;\n  --sp-space-7: 28px;\n  --sp-space-8: 32px;\n  --sp-space-9: 36px;\n  --sp-space-10: 40px;\n  --sp-space-11: 44px;\n  --sp-border-radius: 4px;\n  --sp-layout-height: 300px;\n  --sp-layout-headerHeight: 40px;\n  --sp-transitions-default: 150ms ease;\n  --sp-zIndices-base: 1;\n  --sp-zIndices-overlay: 2;\n  --sp-zIndices-top: 3;\n\n  --sp-font-body: Optimistic Display, -apple-system, ui-sans-serif, system-ui,\n    -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial,\n    Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol,\n    Noto Color Emoji;\n  --sp-font-mono: Source Code Pro, ui-monospace, SFMono-Regular, Menlo, Monaco,\n    Consolas, Liberation Mono, Courier New, monospace;\n  --sp-font-size: calc(1em - 20%);\n  --sp-font-lineHeight: 24px;\n}\n\n/* Default theme */\nhtml .sandpack {\n  --sp-colors-accent: #087ea4;\n  --sp-colors-clickable: #959da5;\n  --sp-colors-disabled: #24292e;\n  --sp-colors-error: #811e18;\n  --sp-colors-error-surface: #ffcdca;\n  --sp-colors-surface1: #fff;\n  --sp-colors-surface2: #e4e7eb;\n\n  --sp-syntax-color-plain: #24292e;\n  --sp-syntax-color-comment: #6a737d;\n  --sp-syntax-color-keyword: #d73a49;\n  --sp-syntax-color-tag: #22863a;\n  --sp-syntax-color-punctuation: #24292e;\n  --sp-syntax-color-definition: #6f42c1;\n  --sp-syntax-color-property: #005cc5;\n  --sp-syntax-color-static: #032f62;\n  --sp-syntax-color-string: #032f62;\n}\n\n/* Dark theme */\nhtml.dark .sp-wrapper {\n  --sp-colors-accent: #58c4dc;\n  --sp-colors-clickable: #999;\n  --sp-colors-disabled: #fff;\n  --sp-colors-error: #811e18;\n  --sp-colors-error-surface: #ffcdca;\n  --sp-colors-surface1: #16181d;\n  --sp-colors-surface2: #343a46;\n\n  --sp-syntax-color-plain: #ffffff;\n  --sp-syntax-color-comment: #757575;\n  --sp-syntax-color-keyword: #77b7d7;\n  --sp-syntax-color-tag: #dfab5c;\n  --sp-syntax-color-punctuation: #ffffff;\n  --sp-syntax-color-definition: #86d9ca;\n  --sp-syntax-color-property: #77b7d7;\n  --sp-syntax-color-static: #c64640;\n  --sp-syntax-color-string: #977cdc;\n}\n\n/**\n * Reset\n */\n.sandpack .sp-wrapper {\n  width: 100%;\n\n  font-size: var(--sp-font-size);\n  font-family: var(--sp-font-body);\n  line-height: var(--sp-font-lineHeight);\n}\n\n/**\n * Layout\n */\n.sandpack .sp-layout {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: stretch;\n  background-color: var(--sp-colors-surface2);\n\n  -webkit-mask-image: -webkit-radial-gradient(\n    var(--sp-colors-surface1),\n    var(--sp-colors-surface1)\n  ); /* safest way to make all corner rounded */\n\n  border-bottom-left-radius: 0.5rem;\n  border-bottom-right-radius: 0.5rem;\n  overflow: initial;\n\n  gap: 1px;\n}\n\n.sandpack .sp-stack {\n  display: flex;\n  flex-direction: column;\n  width: 100%;\n  position: relative;\n}\n\n@media screen and (max-width: 768px) {\n  .sandpack .sp-layout > .sp-stack {\n    height: auto;\n    min-width: 100% !important;\n  }\n}\n\n.sandpack .sp-layout > .sp-stack {\n  flex: 1 1 0px;\n  height: var(--sp-layout-height);\n}\n\n/**\n * Focus ring\n */\n.sandpack--playground .sp-tab-button {\n  transition: none;\n}\n\n.sandpack--playground .sp-tab-button:focus {\n  outline: revert;\n}\n\n.sandpack--playground .sp-tab-button:focus-visible {\n  box-shadow: none;\n}\n\n.sandpack .sp-cm:focus-visible {\n  box-shadow: inset 0 0 0 4px rgba(20, 158, 202, 0.4);\n  outline: none;\n  height: 100%;\n}\n\n/**\n * Navigation\n */\n.sandpack .sp-tabs-scrollable-container {\n  overflow: auto;\n  display: flex;\n  flex-wrap: nowrap;\n  align-items: stretch;\n  min-height: 40px;\n  margin-bottom: -1px;\n}\n\n.sp-tabs .sp-tab-button {\n  padding: 0 6px;\n  border-bottom: 2px solid transparent;\n}\n\n@media (min-width: 768px) {\n  .sp-tabs .sp-tab-button {\n    margin: 0 12px 0 0;\n  }\n}\n\n.sp-tabs .sp-tab-button,\n.sp-tabs .sp-tab-button:hover:not(:disabled, [data-active='true']),\n.sp-tabs .sp-tab-button[data-active='true'] {\n  color: var(--sp-colors-accent);\n}\n\n.sp-tabs .sp-tab-button[data-active='true'] {\n  border-bottom: 2px solid var(--sp-colors-accent);\n}\n\n/**\n * Code block\n */\n.cm-line {\n  padding-left: var(--sp-space-5);\n}\n\n/**\n * Editor\n */\n.sandpack .sp-code-editor {\n  flex: 1 1;\n  position: relative;\n  overflow: auto;\n  background: var(--sp-colors-surface1);\n}\n\n.sandpack .sp-code-editor .cm-editor {\n  background-color: transparent;\n}\n\n.sandpack .sp-code-editor .cm-content,\n.sandpack .sp-code-editor .cm-gutters,\n.sandpack .sp-code-editor .cm-gutterElement {\n  padding: 0;\n  -webkit-font-smoothing: auto; /* Improve the legibility */\n}\n\n.sandpack .sp-code-editor .cm-content {\n  padding-bottom: 18px;\n}\n\n.sandpack--playground .sp-code-editor .cm-line {\n  padding: 0 var(--sp-space-3);\n  width: max-content;\n}\n\n.sandpack--playground .sp-code-editor .cm-lineNumbers {\n  padding-left: var(--sp-space-3);\n  padding-right: var(--sp-space-1);\n  font-size: 13.6px;\n}\n\n.sandpack--playground .sp-code-editor .cm-line.cm-errorLine {\n  @apply bg-red-400;\n  --tw-bg-opacity: 0.1; /* Background tweak: base color + opacity */\n  position: relative;\n  padding-right: 2em;\n  display: inline-block;\n  min-width: 100%;\n}\n\n.sp-code-editor .cm-errorLine:after {\n  @apply text-red-500;\n  position: absolute;\n  right: 8px;\n  top: 0;\n  content: '\\26A0';\n  font-size: 22px;\n  line-height: 20px;\n}\n\n.sp-code-editor .cm-tooltip {\n  border: 0;\n  max-width: 200px;\n}\n\n.sp-code-editor .cm-diagnostic-error {\n  @apply border-red-40;\n}\n\n.sandpack .sp-cm {\n  margin: 0px;\n  outline: none;\n  height: 100%;\n}\n\n.sp-code-editor .sp-cm .cm-scroller {\n  padding-top: 18px;\n}\n\n/**\n * Syntax highlight (code editor + code block)\n */\n.sandpack .sp-syntax-string {\n  color: var(--sp-syntax-color-string);\n}\n\n.sandpack .sp-syntax-plain {\n  color: var(--sp-syntax-color-plain);\n}\n\n.sandpack .sp-syntax-comment {\n  color: var(--sp-syntax-color-comment);\n}\n\n.sandpack .sp-syntax-keyword {\n  color: var(--sp-syntax-color-keyword);\n}\n\n.sandpack .sp-syntax-definition {\n  color: var(--sp-syntax-color-definition);\n}\n\n.sandpack .sp-syntax-punctuation {\n  color: var(--sp-syntax-color-punctuation);\n}\n\n.sandpack .sp-syntax-property {\n  color: var(--sp-syntax-color-property);\n}\n\n.sandpack .sp-syntax-tag {\n  color: var(--sp-syntax-color-tag);\n}\n\n.sandpack .sp-syntax-static {\n  color: var(--sp-syntax-color-static);\n}\n\n/**\n * Loading & error overlay component\n */\n.sandpack .sp-cube-wrapper {\n  background-color: var(--sp-colors-surface1);\n  position: absolute;\n  right: var(--sp-space-2);\n  bottom: var(--sp-space-2);\n  z-index: var(--sp-zIndices-top);\n  width: 32px;\n  height: 32px;\n  border-radius: var(--sp-border-radius);\n}\n\n.sandpack .sp-button {\n  display: flex;\n  align-items: center;\n  margin: auto;\n  width: 100%;\n  height: 100%;\n}\n\n.sandpack .sp-button svg {\n  min-width: var(--sp-space-5);\n  width: var(--sp-space-5);\n  height: var(--sp-space-5);\n  margin: auto;\n}\n\n.sandpack .sp-cube-wrapper .sp-cube {\n  display: flex;\n}\n\n.sandpack .sp-cube-wrapper .sp-button {\n  display: none;\n}\n\n.sandpack .sp-cube-wrapper:hover .sp-button {\n  display: block;\n}\n\n.sandpack .sp-cube-wrapper:hover .sp-cube {\n  display: none;\n}\n\n.sandpack .sp-cube {\n  transform: translate(-4px, 9px) scale(0.13, 0.13);\n}\n\n.sandpack .sp-cube * {\n  position: absolute;\n  width: 96px;\n  height: 96px;\n}\n\n@keyframes cubeRotate {\n  0% {\n    transform: rotateX(-25.5deg) rotateY(45deg);\n  }\n\n  100% {\n    transform: rotateX(-25.5deg) rotateY(405deg);\n  }\n}\n\n.sandpack .sp-sides {\n  animation: cubeRotate 1s linear infinite;\n  animation-fill-mode: forwards;\n  transform-style: preserve-3d;\n  transform: rotateX(-25.5deg) rotateY(45deg);\n}\n\n.sandpack .sp-sides * {\n  border: 10px solid var(--sp-colors-clickable);\n  border-radius: 8px;\n  background: var(--sp-colors-surface1);\n}\n\n.sandpack .sp-sides .top {\n  transform: rotateX(90deg) translateZ(44px);\n  transform-origin: 50% 50%;\n}\n.sandpack .sp-sides .bottom {\n  transform: rotateX(-90deg) translateZ(44px);\n  transform-origin: 50% 50%;\n}\n.sandpack .sp-sides .front {\n  transform: rotateY(0deg) translateZ(44px);\n  transform-origin: 50% 50%;\n}\n.sandpack .sp-sides .back {\n  transform: rotateY(-180deg) translateZ(44px);\n  transform-origin: 50% 50%;\n}\n.sandpack .sp-sides .left {\n  transform: rotateY(-90deg) translateZ(44px);\n  transform-origin: 50% 50%;\n}\n.sandpack .sp-sides .right {\n  transform: rotateY(90deg) translateZ(44px);\n  transform-origin: 50% 50%;\n}\n\n.sandpack .sp-overlay {\n  @apply bg-card;\n  position: absolute;\n  inset: 0;\n  z-index: var(--sp-zIndices-top);\n}\n\n.sandpack .sp-error {\n  padding: var(--sp-space-4);\n  white-space: pre-wrap;\n  font-family: var(--sp-font-mono);\n  background-color: var(--sp-colors-error-surface);\n}\n\n@keyframes fadeIn {\n  0% {\n    opacity: 0;\n    transform: translateY(4px);\n  }\n\n  100% {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n\n.sandpack .sp-error-message {\n  animation: fadeIn 150ms ease;\n  color: var(--sp-colors-error);\n}\n\nhtml.dark .sandpack--playground .sp-overlay {\n  @apply bg-wash-dark;\n}\n\n/**\n * Placeholder\n */\n.sandpack .sp-code-editor .sp-pre-placeholder {\n  @apply font-mono;\n  font-size: 13.6px;\n  line-height: 24px;\n  padding: 18px 0;\n  -webkit-font-smoothing: auto;\n}\n\n.sandpack--playground .sp-code-editor .sp-pre-placeholder {\n  padding-left: 48px !important;\n  margin-left: 0px !important;\n}\n\n.text-xl .sp-pre-placeholder {\n  font-size: 16px !important;\n  line-height: 24px !important;\n}\n\n/**\n * Expand button\n */\n.sandpack .sp-layout {\n  min-height: 216px;\n}\n\n.sandpack .sp-layout > .sp-stack:nth-child(1) {\n  /* Force vertical if there isn't enough space. */\n  min-width: 431px;\n  /* No min height on mobile because we know code in advance. */\n  /* Max height is needed to avoid too long files. */\n  max-height: 40vh;\n}\n\n.sandpack .sp-layout > .sp-stack:nth-child(2) {\n  /* Force vertical if there isn't enough space. */\n  min-width: 431px;\n  /* Keep preview a fixed size on mobile to avoid jumps. */\n  /* This is because we don't know its content in advance. */\n  min-height: 40vh;\n  max-height: 40vh;\n}\n.sandpack .sp-layout.sp-layout-expanded > .sp-stack:nth-child(1) {\n  /* Clicking \"show more\" lets mobile editor go full height. */\n  max-height: unset;\n  height: auto;\n}\n\n.sandpack .sp-layout.sp-layout-expanded > .sp-stack:nth-child(2) {\n  /* Clicking \"show more\" lets mobile preview go full height. */\n  max-height: unset;\n  height: auto;\n}\n\n@media (min-width: 1280px) {\n  .sandpack .sp-layout > .sp-stack:nth-child(1) {\n    /* On desktop, clamp height by pixels instead. */\n    height: auto;\n    min-height: unset;\n    max-height: 406px;\n  }\n  .sandpack .sp-layout > .sp-stack:nth-child(2) {\n    /* On desktop, clamp height by pixels instead. */\n    height: auto;\n    min-height: unset;\n    max-height: 406px;\n  }\n  .sandpack .sp-layout.sp-layout-expanded > .sp-stack:nth-child(1) {\n    max-height: unset;\n  }\n  .sandpack .sp-layout.sp-layout-expanded > .sp-stack:nth-child(2) {\n    max-height: unset;\n  }\n}\n\n.sandpack .sp-layout .sandpack-expand {\n  border-left: none;\n  margin-left: 0;\n}\n\n.expandable-callout .sp-stack:nth-child(2) {\n  min-width: 431px;\n  min-height: 40vh;\n  max-height: 40vh;\n}\n\n/**\n * Integrations: console\n */\n.sandpack .console .sp-cm,\n.sandpack .console .sp-cm .cm-scroller,\n.sandpack .console .sp-cm .cm-line {\n  padding: 0px !important;\n}\n\n/**\n * Integrations: eslint\n */\n.sandpack .sp-code-editor .cm-diagnostic {\n  @apply text-secondary;\n}\n\n/**\n * Overwrite inline sty\n */\n.sandpack .sp-devtools > div {\n  --color-background: var(--sp-colors-surface1) !important;\n  --color-background-inactive: var(--sp-colors-surface2) !important;\n  --color-background-selected: var(--sp-colors-accent) !important;\n  --color-background-hover: transparent !important;\n  --color-modal-background: #ffffffd2 !important;\n\n  --color-tab-selected-border: #087ea4 !important;\n\n  --color-component-name: var(--sp-syntax-color-definition) !important;\n  --color-attribute-name: var(--sp-syntax-color-property) !important;\n  --color-attribute-value: var(--sp-syntax-color-string) !important;\n  --color-attribute-editable-value: var(--sp-syntax-color-property) !important;\n  --color-attribute-name-not-editable: var(--sp-colors-clickable) !important;\n  --color-button-background-focus: var(--sp-colors-surface2) !important;\n\n  --color-button-active: var(--sp-colors-accent) !important;\n  --color-button-background: transparent !important;\n  --color-button: var(--sp-colors-clickable) !important;\n  --color-button-hover: var(--sp-colors-disabled) !important;\n\n  --color-border: var(--sp-colors-surface2) !important;\n  --color-text: rgb(35, 39, 47) !important;\n}\n\nhtml.dark .sp-devtools > div {\n  --color-text: var(--sp-colors-clickable) !important;\n  --color-modal-background: #16181de0 !important;\n}\n\n.sandpack .sp-devtools table td {\n  border: 1px solid var(--sp-colors-surface2);\n}\n\n/**\n * Hard fixes\n */\n\n/**\n  * The text-size-adjust CSS property controls the text inflation\n  * algorithm used on some smartphones and tablets\n  */\n.sandpack .sp-cm {\n  -webkit-text-size-adjust: none;\n}\n\n/**\n * For iOS: prevent browser zoom when clicking on sandbox.\n * Does NOT apply to code blocks.\n */\n@media screen and (max-width: 768px) {\n  @supports (-webkit-overflow-scrolling: touch) {\n    .sandpack--playground .cm-content,\n    .sandpack--playground .sp-code-editor .sp-pre-placeholder {\n      font-size: initial;\n    }\n    .DocSearch-Input {\n      font-size: initial;\n    }\n  }\n}\n\n.sp-loading .sp-icon-standalone span {\n  display: none;\n}\n"
  },
  {
    "path": "src/types/docsearch-react-modal.d.ts",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\n// This module must be declared and because the dynamic import in\n// \"src/components/Search.tsx\" is not able to resolve the types from\n// the package.\ndeclare module '@docsearch/react/modal' {\n  // re-exports the types from @docsearch/react/dist/esm/index.d.ts\n  export * from '@docsearch/react/dist/esm/index';\n}\n"
  },
  {
    "path": "src/utils/analytics.ts",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nlet buffer: Array<any> = [];\nlet galite: null | Function = null;\nlet galitePromise: null | Promise<any> = null;\n\nexport function ga(...args: any[]): void {\n  if (typeof galite === 'function') {\n    galite.apply(null, args);\n    return;\n  }\n  buffer.push(args);\n  if (!galitePromise) {\n    // @ts-ignore\n    galitePromise = import('ga-lite').then((mod) => {\n      galite = mod.default;\n      galitePromise = null;\n      buffer.forEach((args) => {\n        mod.default.apply(null, args);\n      });\n      buffer = [];\n    });\n  }\n}\n"
  },
  {
    "path": "src/utils/compileMDX.ts",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport {LanguageItem} from 'components/MDX/LanguagesContext';\nimport {MDXComponents} from 'components/MDX/MDXComponents';\n\n// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n// ~~~~ IMPORTANT: BUMP THIS IF YOU CHANGE ANY CODE BELOW ~~~\nconst DISK_CACHE_BREAKER = 11;\n// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nexport default async function compileMDX(\n  mdx: string,\n  path: string | string[],\n  params: {[key: string]: any}\n): Promise<{content: string; toc: string; meta: any}> {\n  const fs = require('fs');\n  const {\n    prepareMDX,\n    PREPARE_MDX_CACHE_BREAKER,\n  } = require('../utils/prepareMDX');\n  const mdxComponentNames = Object.keys(MDXComponents);\n\n  // See if we have a cached output first.\n  const {FileStore, stableHash} = require('metro-cache');\n  const store = new FileStore({\n    root: process.cwd() + '/node_modules/.cache/react-docs-mdx/',\n  });\n  const hash = Buffer.from(\n    stableHash({\n      // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n      // ~~~~ IMPORTANT: Everything that the code below may rely on.\n      // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n      mdx,\n      ...params,\n      mdxComponentNames,\n      DISK_CACHE_BREAKER,\n      PREPARE_MDX_CACHE_BREAKER,\n      lockfile: fs.readFileSync(process.cwd() + '/yarn.lock', 'utf8'),\n    })\n  );\n  const cached = await store.get(hash);\n  if (cached) {\n    console.log(\n      'Reading compiled MDX for /' + path + ' from ./node_modules/.cache/'\n    );\n    return cached;\n  }\n  if (process.env.NODE_ENV === 'production') {\n    console.log(\n      'Cache miss for MDX for /' + path + ' from ./node_modules/.cache/'\n    );\n  }\n\n  // If we don't add these fake imports, the MDX compiler\n  // will insert a bunch of opaque components we can't introspect.\n  // This will break the prepareMDX() call below.\n  let mdxWithFakeImports =\n    mdx +\n    '\\n\\n' +\n    mdxComponentNames\n      .map((key) => 'import ' + key + ' from \"' + key + '\";\\n')\n      .join('\\n');\n\n  // Turn the MDX we just read into some JS we can execute.\n  const {remarkPlugins} = require('../../plugins/markdownToHtml');\n  const {compile: compileMdx} = await import('@mdx-js/mdx');\n  const visit = (await import('unist-util-visit')).default;\n  const jsxCode = await compileMdx(mdxWithFakeImports, {\n    remarkPlugins: [\n      ...remarkPlugins,\n      (await import('remark-gfm')).default,\n      (await import('remark-frontmatter')).default,\n    ],\n    rehypePlugins: [\n      // Support stuff like ```js App.js {1-5} active by passing it through.\n      function rehypeMetaAsAttributes() {\n        return (tree) => {\n          visit(tree, 'element', (node) => {\n            if (\n              // @ts-expect-error -- tagName is a valid property\n              node.tagName === 'code' &&\n              node.data &&\n              node.data.meta\n            ) {\n              // @ts-expect-error -- properties is a valid property\n              node.properties.meta = node.data.meta;\n            }\n          });\n        };\n      },\n    ],\n  });\n  const {transform} = require('@babel/core');\n  const jsCode = await transform(jsxCode, {\n    plugins: ['@babel/plugin-transform-modules-commonjs'],\n    presets: ['@babel/preset-react'],\n  }).code;\n\n  // Prepare environment for MDX.\n  let fakeExports = {};\n  const fakeRequire = (name: string) => {\n    if (name === 'react/jsx-runtime') {\n      return require('react/jsx-runtime');\n    } else {\n      // For each fake MDX import, give back the string component name.\n      // It will get serialized later.\n      return name;\n    }\n  };\n  const evalJSCode = new Function('require', 'exports', jsCode);\n  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n  // THIS IS A BUILD-TIME EVAL. NEVER DO THIS WITH UNTRUSTED MDX (LIKE FROM CMS)!!!\n  // In this case it's okay because anyone who can edit our MDX can also edit this file.\n  evalJSCode(fakeRequire, fakeExports);\n  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n  // @ts-expect-error -- default exports is existed after eval\n  const reactTree = fakeExports.default({});\n\n  // Pre-process MDX output and serialize it.\n  let {toc, children} = prepareMDX(reactTree.props.children);\n  if (path === 'index') {\n    toc = [];\n  }\n\n  // Parse Frontmatter headers from MDX.\n  const fm = require('gray-matter');\n  const meta = fm(mdx).data;\n\n  // Load the list of translated languages conditionally.\n  let languages: Array<LanguageItem> | null = null;\n  if (typeof path === 'string' && path.endsWith('/translations')) {\n    languages = await (\n      await fetch(\n        'https://raw.githubusercontent.com/reactjs/translations.react.dev/main/langs/langs.json'\n      )\n    ).json(); // { code: string; name: string; enName: string}[]\n  }\n\n  const output = {\n    content: JSON.stringify(children, stringifyNodeOnServer),\n    toc: JSON.stringify(toc, stringifyNodeOnServer),\n    meta,\n    languages,\n  };\n\n  // Serialize a server React tree node to JSON.\n  function stringifyNodeOnServer(key: unknown, val: any) {\n    if (\n      val != null &&\n      val.$$typeof === Symbol.for('react.transitional.element')\n    ) {\n      // Remove fake MDX props.\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n      const {mdxType, originalType, parentName, ...cleanProps} = val.props;\n      return [\n        '$r',\n        typeof val.type === 'string' ? val.type : mdxType,\n        val.key,\n        cleanProps,\n      ];\n    } else {\n      return val;\n    }\n  }\n\n  // Cache it on the disk.\n  await store.set(hash, output);\n  return output;\n}\n"
  },
  {
    "path": "src/utils/finishedTranslations.ts",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n// This is a list of languages with enough translated content.\n// Add more languages here when they have enough content.\n// Please DO NOT edit this list without a discussion in the reactjs/react.dev repo.\n// It must be the same between all translations.\n// This will also affect the 'Translations' article.\n\n// prettier-ignore\nexport const finishedTranslations = [\n  'en',\n  'zh-hans',\n  'es',\n  'fr',\n  'ja',\n  'tr',\n  'ko'\n];\n"
  },
  {
    "path": "src/utils/forwardRefWithAs.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\n/**\n * Copied from Reach UI utils...\n *\n * It fixes TypeScript type inferencing to work with <Comp as={AnotherComp} />\n */\n\nimport * as React from 'react';\nimport {ValidationMap} from 'prop-types';\n\n/**\n * React.Ref uses the readonly type `React.RefObject` instead of\n * `React.MutableRefObject`, We pretty much always assume ref objects are\n * mutable (at least when we create them), so this type is a workaround so some\n * of the weird mechanics of using refs with TS.\n */\nexport type AssignableRef<ValueType> =\n  | {\n      bivarianceHack(instance: ValueType | null): void;\n    }['bivarianceHack']\n  | React.MutableRefObject<ValueType | null>\n  | null;\n\n////////////////////////////////////////////////////////////////////////////////\n// The following types help us deal with the `as` prop.\n// I kind of hacked around until I got this to work using some other projects,\n// as a rough guide, but it does seem to work so, err, that's cool? Yay TS! 🙃\n// P = additional props\n// T = type of component to render\n\nexport type As<BaseProps = any> = React.ElementType<BaseProps>;\n\nexport type PropsWithAs<\n  ComponentType extends As,\n  ComponentProps\n> = ComponentProps &\n  Omit<\n    React.ComponentPropsWithRef<ComponentType>,\n    'as' | keyof ComponentProps\n  > & {\n    as?: ComponentType;\n  };\n\nexport type PropsFromAs<\n  ComponentType extends As,\n  ComponentProps\n> = (PropsWithAs<ComponentType, ComponentProps> & {as: ComponentType}) &\n  PropsWithAs<ComponentType, ComponentProps>;\n\nexport type ComponentWithForwardedRef<\n  ElementType extends React.ElementType,\n  ComponentProps\n> = React.ForwardRefExoticComponent<\n  ComponentProps &\n    React.HTMLProps<React.ElementType<ElementType>> &\n    React.ComponentPropsWithRef<ElementType>\n>;\n\nexport interface ComponentWithAs<ComponentType extends As, ComponentProps> {\n  // These types are a bit of a hack, but cover us in cases where the `as` prop\n  // is not a JSX string type. Makes the compiler happy so 🤷‍♂️\n  <TT extends As>(\n    props: PropsWithAs<TT, ComponentProps>\n  ): React.ReactElement | null;\n  (\n    props: PropsWithAs<ComponentType, ComponentProps>\n  ): React.ReactElement | null;\n\n  displayName?: string;\n  propTypes?: ValidationMap<React.ReactNode>;\n  contextTypes?: ValidationMap<React.ReactNode>;\n  defaultProps?: Partial<PropsWithAs<ComponentType, ComponentProps>>;\n}\n\n/**\n * This is a hack for sure. The thing is, getting a component to intelligently\n * infer props based on a component or JSX string passed into an `as` prop is\n * kind of a huge pain. Getting it to work and satisfy the constraints of\n * `forwardRef` seems dang near impossible. To avoid needing to do this awkward\n * type song-and-dance every time we want to forward a ref into a component\n * that accepts an `as` prop, we abstract all of that mess to this function for\n * the time time being.\n *\n * TODO: Eventually we should probably just try to get the type defs above\n * working across the board, but ain't nobody got time for that mess!\n *\n * @param Comp\n */\nexport function forwardRefWithAs<Props, ComponentType extends As>(\n  comp: (\n    props: PropsFromAs<ComponentType, Props>,\n    ref: React.RefObject<any>\n  ) => React.ReactElement | null\n) {\n  return React.forwardRef(comp as any) as unknown as ComponentWithAs<\n    ComponentType,\n    Props\n  >;\n}\n\n/*\nTest components to make sure our dynamic As prop components work as intended \ntype PopupProps = {\n  lol: string;\n  children?: React.ReactNode | ((value?: number) => JSX.Element);\n};\nexport const Popup = forwardRefWithAs<PopupProps, 'input'>(\n  ({ as: Comp = 'input', lol, className, children, ...props }, ref) => {\n    return (\n      <Comp ref={ref} {...props}>\n        {typeof children === 'function' ? children(56) : children}\n      </Comp>\n    );\n  }\n);\nexport const TryMe1: React.FC = () => {\n  return <Popup as=\"input\" lol=\"lol\" name=\"me\" />;\n};\nexport const TryMe2: React.FC = () => {\n  let ref = React.useRef(null);\n  return <Popup ref={ref} as=\"div\" lol=\"lol\" />;\n};\n\nexport const TryMe4: React.FC = () => {\n  return <Popup as={Whoa} lol=\"lol\" test=\"123\" name=\"boop\" />;\n};\nexport const Whoa: React.FC<{\n  help?: boolean;\n  lol: string;\n  name: string;\n  test: string;\n}> = props => {\n  return <input {...props} />;\n};\n*/\n// export const TryMe3: React.FC = () => {\n//   return <Popup as={Cool} lol=\"lol\" name=\"me\" test=\"123\" />;\n// };\n// let Cool = styled(Whoa)`\n//   padding: 10px;\n// `\n"
  },
  {
    "path": "src/utils/prepareMDX.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport {Children} from 'react';\n\n// TODO: This logic could be in MDX plugins instead.\n\n// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nexport const PREPARE_MDX_CACHE_BREAKER = 3;\n// !!! IMPORTANT !!! Bump this if you change any logic.\n// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nexport function prepareMDX(rawChildren) {\n  const toc = getTableOfContents(rawChildren, /* depth */ 10);\n  const children = wrapChildrenInMaxWidthContainers(rawChildren);\n  return {toc, children};\n}\n\nfunction wrapChildrenInMaxWidthContainers(children) {\n  // Auto-wrap everything except a few types into\n  // <MaxWidth> wrappers. Keep reusing the same\n  // wrapper as long as we can until we meet\n  // a full-width section which interrupts it.\n  let fullWidthTypes = [\n    'Sandpack',\n    'FullWidth',\n    'Illustration',\n    'IllustrationBlock',\n    'Challenges',\n    'Recipes',\n  ];\n  let wrapQueue = [];\n  let finalChildren = [];\n  function flushWrapper(key) {\n    if (wrapQueue.length > 0) {\n      const Wrapper = 'MaxWidth';\n      finalChildren.push(<Wrapper key={key}>{wrapQueue}</Wrapper>);\n      wrapQueue = [];\n    }\n  }\n  function handleChild(child, key) {\n    if (child == null) {\n      return;\n    }\n    if (typeof child !== 'object') {\n      wrapQueue.push(child);\n      return;\n    }\n    if (fullWidthTypes.includes(child.type)) {\n      flushWrapper(key);\n      finalChildren.push(child);\n    } else {\n      wrapQueue.push(child);\n    }\n  }\n  Children.forEach(children, handleChild);\n  flushWrapper('last');\n  return finalChildren;\n}\n\nfunction getTableOfContents(children, depth) {\n  const anchors = [];\n  extractHeaders(children, depth, anchors);\n  if (anchors.length > 0) {\n    anchors.unshift({\n      url: '#',\n      text: '概要',\n      depth: 2,\n    });\n  }\n  return anchors;\n}\n\nconst headerTypes = new Set([\n  'h1',\n  'h2',\n  'h3',\n  'Challenges',\n  'Recap',\n  'TeamMember',\n]);\nfunction extractHeaders(children, depth, out) {\n  for (const child of Children.toArray(children)) {\n    if (child.type && headerTypes.has(child.type)) {\n      let header;\n      if (child.type === 'Challenges') {\n        header = {\n          url: '#challenges',\n          depth: 2,\n          text: 'チャレンジ問題',\n        };\n      } else if (child.type === 'Recap') {\n        header = {\n          url: '#recap',\n          depth: 2,\n          text: 'まとめ',\n        };\n      } else if (child.type === 'TeamMember') {\n        header = {\n          url: '#' + child.props.permalink,\n          depth: 3,\n          text: child.props.name,\n        };\n      } else {\n        header = {\n          url: '#' + child.props.id,\n          depth: (child.type && parseInt(child.type.replace('h', ''), 0)) ?? 0,\n          text: child.props.children,\n        };\n      }\n      out.push(header);\n    } else if (child.children && depth > 0) {\n      extractHeaders(child.children, depth - 1, out);\n    }\n  }\n}\n"
  },
  {
    "path": "src/utils/processShim.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\n// Used in next.config.js to remove the process transitive dependency.\nmodule.exports = {\n  env: {},\n  cwd() {},\n};\n"
  },
  {
    "path": "src/utils/rafShim.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\n// Used in next.config.js to remove the raf transitive dependency.\nexport default window.requestAnimationFrame;\n"
  },
  {
    "path": "src/utils/rss.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\nconst Feed = require('rss');\nconst fs = require('fs');\nconst path = require('path');\nconst matter = require('gray-matter');\n\nconst getAllFiles = function (dirPath, arrayOfFiles) {\n  const files = fs.readdirSync(dirPath);\n\n  arrayOfFiles = arrayOfFiles || [];\n\n  files.forEach(function (file) {\n    if (fs.statSync(dirPath + '/' + file).isDirectory()) {\n      arrayOfFiles = getAllFiles(dirPath + '/' + file, arrayOfFiles);\n    } else {\n      arrayOfFiles.push(path.join(dirPath, '/', file));\n    }\n  });\n\n  return arrayOfFiles;\n};\n\nexports.generateRssFeed = function () {\n  const feed = new Feed({\n    title: 'React Blog (JA)',\n    description:\n      'React チームからの公式な更新のお知らせが掲載されるブログです。リリースノートや非推奨化のお知らせなどの重要なことはすべて、まずこちらに掲載されます。',\n    feed_url: 'https://ja.react.dev/rss.xml',\n    site_url: 'https://ja.react.dev/',\n    language: 'ja',\n    favicon: 'https://ja.react.dev/favicon.ico',\n    pubDate: new Date(),\n    generator: 'react.dev rss module',\n  });\n\n  const dirPath = path.join(process.cwd(), 'src/content/blog');\n  const filesByOldest = getAllFiles(dirPath);\n  const files = filesByOldest.reverse();\n\n  for (const filePath of files) {\n    const id = path.basename(filePath);\n    if (id !== 'index.md') {\n      const content = fs.readFileSync(filePath, 'utf-8');\n      const {data} = matter(content);\n      const slug = filePath.split('/').slice(-4).join('/').replace('.md', '');\n\n      if (data.title == null || data.title.trim() === '') {\n        throw new Error(\n          `${id}: Blog posts must include a title in the metadata, for RSS feeds`\n        );\n      }\n      if (data.author == null || data.author.trim() === '') {\n        throw new Error(\n          `${id}: Blog posts must include an author in the metadata, for RSS feeds`\n        );\n      }\n      if (data.date == null || data.date.trim() === '') {\n        throw new Error(\n          `${id}: Blog posts must include a date in the metadata, for RSS feeds`\n        );\n      }\n      if (data.description == null || data.description.trim() === '') {\n        throw new Error(\n          `${id}: Blog posts must include a description in the metadata, for RSS feeds`\n        );\n      }\n\n      feed.item({\n        id,\n        title: data.title,\n        author: data.author || '',\n        date: new Date(data.date),\n        url: `https://ja.react.dev/blog/${slug}`,\n        description: data.description,\n      });\n    }\n  }\n\n  fs.writeFileSync('./public/rss.xml', feed.xml({indent: true}));\n};\n"
  },
  {
    "path": "src/utils/toCommaSeparatedList.tsx",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nimport * as React from 'react';\n\nconst addString = (list: React.ReactNode[], string: string) =>\n  list.push(<span key={`${list.length}-${string}`}>{string}</span>);\n\nfunction toCommaSeparatedList<Item>(\n  array: Item[],\n  renderCallback: (item: Item, index: number) => React.ReactNode\n): React.ReactNode[] {\n  if (array.length <= 1) {\n    return array.map(renderCallback);\n  }\n\n  const list: React.ReactNode[] = [];\n\n  array.forEach((item, index) => {\n    if (index === array.length - 1) {\n      addString(list, array.length === 2 ? ' and ' : ', and ');\n      list.push(renderCallback(item, index));\n    } else if (index > 0) {\n      addString(list, ', ');\n      list.push(renderCallback(item, index));\n    } else {\n      list.push(renderCallback(item, index));\n    }\n  });\n\n  return list;\n}\n\nexport default toCommaSeparatedList;\n"
  },
  {
    "path": "tailwind.config.js",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/*\n * Copyright (c) Facebook, Inc. and its affiliates.\n */\n\nconst defaultTheme = require('tailwindcss/defaultTheme');\nconst colors = require('./colors');\n\nmodule.exports = {\n  content: [\n    './src/components/**/*.{js,ts,jsx,tsx}',\n    './src/pages/**/*.{js,ts,jsx,tsx}',\n    './src/styles/**/*.{js,ts,jsx,tsx}',\n  ],\n  darkMode: 'class',\n  theme: {\n    // Override base screen sizes\n    screens: {\n      ...defaultTheme.screens,\n      betterhover: {raw: '(hover: hover)'},\n      xs: '374px',\n      '3xl': '1919px',\n    },\n    boxShadow: {\n      sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',\n      DEFAULT:\n        '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)',\n      md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',\n      lg: '0px 0.8px 2px rgba(0, 0, 0, 0.032), 0px 2.7px 6.7px rgba(0, 0, 0, 0.048), 0px 12px 30px rgba(0, 0, 0, 0.08)',\n      'lg-dark':\n        '0 0 0 1px rgba(255,255,255,.15), 0px 0.8px 2px rgba(0, 0, 0, 0.032), 0px 2.7px 6.7px rgba(0, 0, 0, 0.048), 0px 12px 30px rgba(0, 0, 0, 0.08)',\n      xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',\n      '2xl': '0 25px 50px -12px rgba(0, 0, 0, 0.25)',\n      '3xl': '0 35px 60px -15px rgba(0, 0, 0, 0.3)',\n      nav: '0 16px 32px -16px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0,0,0,.10)',\n      'nav-dark':\n        '0 16px 32px -16px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255,255,255,.05)',\n      inner: 'inset 0 1px 4px 0 rgba(0, 0, 0, 0.05)',\n      'inner-border': 'inset 0 0 0 1px rgba(0, 0, 0, 0.08)',\n      'inner-border-dark': 'inset 0 0 0 1px rgba(255, 255, 255, 0.08)',\n      'outer-border': '0 0 0 1px rgba(0, 0, 0, 0.1)',\n      'outer-border-dark': '0 0 0 1px rgba(255, 255, 255, 0.1)',\n      'secondary-button-stroke': 'inset 0 0 0 1px #D9DBE3',\n      'secondary-button-stroke-dark': 'inset 0 0 0 1px #404756',\n      none: 'none',\n    },\n    extend: {\n      backgroundImage: {\n        'gradient-left-dark':\n          'conic-gradient(from 90deg at -10% 100%, #2B303B 0deg, #2B303B 90deg, #16181D 360deg)',\n        'gradient-right-dark':\n          'conic-gradient(from -90deg at 110% 100%, #2B303B 0deg, #16181D 90deg, #16181D 360deg)',\n        'gradient-left':\n          'conic-gradient(from 90deg at -10% 100%, #BCC1CD 0deg, #BCC1CD 90deg, #FFFFFF 360deg)',\n        'gradient-right':\n          'conic-gradient(from -90deg at 110% 100%, #FFFFFF 0deg, #EBECF0 90deg, #EBECF0 360deg)',\n        'meta-gradient': \"url('/images/meta-gradient.png')\",\n        'meta-gradient-dark': \"url('/images/meta-gradient-dark.png')\",\n      },\n      maxWidth: {\n        ...defaultTheme.maxWidth,\n        'custom-xs': '21rem',\n      },\n      outline: {\n        blue: ['1px auto ' + colors.link, '3px'],\n      },\n      opacity: {\n        8: '0.08',\n      },\n      fontFamily: {\n        display: [\n          'Optimistic Display',\n          '-apple-system',\n          // In Windows, font which is suitable for long text should be selected\n          // for Japanese characters instead of Yu Gothic UI (system-ui)\n          '\"BIZ UDGothic\"',\n          'Meiryo',\n          ...defaultTheme.fontFamily.sans,\n        ],\n        text: [\n          'Optimistic Text',\n          '-apple-system',\n          '\"BIZ UDGothic\"',\n          'Meiryo',\n          ...defaultTheme.fontFamily.sans,\n        ],\n        mono: ['\"Source Code Pro\"', ...defaultTheme.fontFamily.mono],\n      },\n      lineHeight: {\n        base: '30px',\n        large: '38px',\n        xl: '1.15',\n      },\n      fontSize: {\n        '6xl': '52px',\n        '5xl': '40px',\n        '4xl': '32px',\n        '3xl': '28px',\n        '2xl': '24px',\n        xl: '20px',\n        lg: '17px',\n        base: '15px',\n        sm: '13px',\n        xs: '11px',\n        code: 'calc(1em - 20%)',\n      },\n      animation: {\n        marquee: 'marquee 40s linear infinite',\n        marquee2: 'marquee2 40s linear infinite',\n        'large-marquee': 'large-marquee 80s linear infinite',\n        'large-marquee2': 'large-marquee2 80s linear infinite',\n        'fade-up': 'fade-up 1s 100ms both',\n      },\n      keyframes: {\n        shimmer: {\n          '100%': {\n            transform: 'translateX(100%)',\n          },\n        },\n        rotate: {\n          from: {transform: 'rotate(0deg)'},\n          to: {transform: 'rotate(180deg)'},\n        },\n        scale: {\n          from: {transform: 'scale(0.8)'},\n          '90%': {transform: 'scale(1.05)'},\n          to: {transform: 'scale(1)'},\n        },\n        circle: {\n          from: {transform: 'scale(0)', strokeWidth: '16px'},\n          '50%': {transform: 'scale(0.5)', strokeWidth: '16px'},\n          to: {transform: 'scale(1)', strokeWidth: '0px'},\n        },\n        marquee: {\n          '0%': {transform: 'translateX(0%)'},\n          '100%': {transform: 'translateX(-400%)'},\n        },\n        marquee2: {\n          '0%': {transform: 'translateX(400%)'},\n          '100%': {transform: 'translateX(0%)'},\n        },\n        'large-marquee': {\n          '0%': {transform: 'translateX(0%)'},\n          '100%': {transform: 'translateX(-200%)'},\n        },\n        'large-marquee2': {\n          '0%': {transform: 'translateX(200%)'},\n          '100%': {transform: 'translateX(0%)'},\n        },\n        'fade-up': {\n          '0%': {\n            opacity: '0',\n            transform: 'translateY(2rem)',\n          },\n          '100%': {\n            opacity: '1',\n            transform: 'translateY(0)',\n          },\n        },\n      },\n      colors,\n      gridTemplateColumns: {\n        'only-content': 'auto',\n        'sidebar-content': '20rem auto',\n        'sidebar-content-toc': '20rem auto 20rem',\n      },\n    },\n  },\n  plugins: [],\n};\n"
  },
  {
    "path": "textlint/.gitignore",
    "content": "node_modules"
  },
  {
    "path": "textlint/.textlintrc.json",
    "content": "{\n  \"plugins\": {},\n  \"filters\": {},\n  \"rules\": {\n    \"preset-jtf-style\": {\n      \"1.1.3.箇条書き\": false,\n      \"1.2.1.句点(。)と読点(、)\": false,\n      \"1.2.2.ピリオド(.)とカンマ(,)\": false,\n      \"3.1.1.全角文字と半角文字の間\": false,\n      \"4.1.1.句点(。)\": false,\n      \"4.2.1.感嘆符(！)\": false,\n      \"4.2.2.疑問符(？)\": false,\n      \"4.3.7.山かっこ<>\": false\n    },\n    \"prh\": {\n      \"rulePaths\": [\"./prh.yml\"],\n      \"checkBlockQuote\": true\n    }\n  }\n}\n"
  },
  {
    "path": "textlint/jp-reactjs-org-lint.js",
    "content": "const toString = require('mdast-util-to-string');\n\nmodule.exports = context => {\n  return {\n    // [context.Syntax.Header]: node => {},\n\n    [context.Syntax.Paragraph]: node => {\n      // Checks against paragraph-based rules.\n\n      // Convert a paragraph to plain text, stripping any markups\n      const text = toString(node);\n\n      enforceSpaceAfterQuestionOrExclamation(node, text, context);\n      noHanPunctSpace(node, text, context);\n      enforceZenHanSpace(node, text, context);\n      noLineEndSpace(node, text, context);\n      noConflictMarker(node, text, context);\n    },\n\n    [context.Syntax.Str]: node => {\n      const text = toString(node);\n      const index = text.indexOf('　');\n      if (index >= 0) {\n        context.report(\n          node,\n          new context.RuleError(\n            'いかなる場合も全角スペースは使用しないでください。',\n            {index},\n          ),\n        );\n      }\n    },\n  };\n};\n\nconst enforceSpaceAfterQuestionOrExclamation = (node, text, context) => {\n  const markReg = /[！？](\\u3000|[A-Za-z0-9]|[\\u3400-\\u4DBF\\u4E00-\\u9FFF\\uF900-\\uFAFF]|[\\uD840-\\uD87F][\\uDC00-\\uDFFF]|[ぁ-んァ-ヶ])/;\n  if (markReg.exec(text)) {\n    context.report(\n      node,\n      new context.RuleError(\n        '全角の「！」「？」と次の文の間には半角スペースを挿入してください。',\n      ),\n    );\n  }\n};\n\nconst noHanPunctSpace = (node, text, context) => {\n  const match = /(.{0,3})[、。]( |\\u3000)/.exec(text);\n  if (match) {\n    context.report(\n      node,\n      new context.RuleError(\n        `全角の句読点の直後にスペースを入れてはいけません(\"${match[0]}\")。`,\n      ),\n    );\n  }\n};\n\nconst enforceZenHanSpace = (node, text, context) => {\n  const hanZen = /[A-Za-z0-9](?:[\\u3400-\\u4DBF\\u4E00-\\u9FFF\\uF900-\\uFAFF]|[\\uD840-\\uD87F][\\uDC00-\\uDFFF]|[ぁ-んァ-ヶ])/;\n  const zenHan = /(?:[\\u3400-\\u4DBF\\u4E00-\\u9FFF\\uF900-\\uFAFF]|[\\uD840-\\uD87F][\\uDC00-\\uDFFF]|[ぁ-んァ-ヶ])[A-Za-z0-9]/;\n\n  const hanZenMatch = hanZen.exec(text);\n  const zenHanMatch = zenHan.exec(text);\n\n  if (hanZenMatch || zenHanMatch) {\n    const matched = hanZenMatch ? hanZenMatch[0] : zenHanMatch[0];\n    context.report(\n      node,\n      new context.RuleError(\n        `全角文字と半角英数字とが隣接しています(\"${matched}\")。` +\n          `半角スペースを挿入してください。`,\n      ),\n    );\n  }\n};\n\nconst noLineEndSpace = (node, text, context) => {\n  // This detects a line-end space *only when* the line contains Japanese characters.\n  const reg = /(?:[\\u3400-\\u4DBF\\u4E00-\\u9FFF\\uF900-\\uFAFF]|[\\uD840-\\uD87F][\\uDC00-\\uDFFF]|[ぁ-んァ-ヶ]).?(\\s|\\u3000)$/;\n\n  if (reg.exec(text)) {\n    context.report(\n      node,\n      new context.RuleError('行末にスペース文字を入れないでください。'),\n    );\n  }\n};\n\nconst noConflictMarker = (node, text, context) => {\n  const reg = /<<<<<<< HEAD/;\n\n  if (reg.exec(text)) {\n    context.report(\n      node,\n      new context.RuleError(\n        'コンフリクトマーカーが残っています。コンフリクトを解消してください。',\n      ),\n    );\n  }\n};\n"
  },
  {
    "path": "textlint/package.json",
    "content": "{\n  \"name\": \"ja-react-dev-textlint\",\n  \"version\": \"0.1.0\",\n  \"description\": \"Checks Markdown on ja.react.dev\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n    \"textlint\": \"textlint --rulesdir=. ../src/content\",\n    \"textlint-staged\": \"textlint --rulesdir=. --\"\n  },\n  \"author\": \"Soichiro Miki\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"textlint\": \"^13.3.2\",\n    \"textlint-rule-preset-jtf-style\": \"^2.3.13\",\n    \"textlint-rule-prh\": \"^5.3.0\"\n  }\n}\n"
  },
  {
    "path": "textlint/prh.yml",
    "content": "# prh version\nversion: 1\nrules:\n\n  - expected: $1くださ\n    pattern: /(て|[いん]で)下さ/\n    prh: 補助動詞はかな書きにします\n    specs:\n      - from: 確認して下さい\n        to:   確認してください\n\n  - expected: $1み$3\n    pattern: /((つい)?て|[いん]で)見(て|た|よ|る|ま|な|れ|ろ|ず|つつ|ながら)/\n    regexpMustEmpty: $2\n    prh: 補助動詞はかな書きにします\n    specs:\n      - from: やって見てください\n        to:   やってみてください\n      - from: 見て見ませんか\n        to:   見てみませんか\n      - from: 本件について見ていると\n        to:   本件について見ていると\n      - from: テレビで見た\n        to:   テレビで見た\n\n  - expected: $1き$2\n    pattern: /(て|[いん]で)来(て|た|なさ|なよ|つつ|ながら|ま)/\n    prh: 補助動詞はかな書きにします\n    specs:\n      - from: やって来ました\n        to:   やってきました\n      - from: チャリで来た\n        to:   チャリで来た\n  - expected: $1こ$2\n    pattern: /(て|[いん]で)来(い|な|よ)/\n    prh: 補助動詞はかな書きにします\n    specs:\n      - from: メッセージが出て来ない場合\n        to:   メッセージが出てこない場合\n  - expected: $1く$2\n    pattern: /(て|[いん]で)来(る|れ)/\n    prh: 補助動詞はかな書きにします\n    specs:\n      - from: 見て来る\n        to:   見てくる\n      - from: 飛んで来れば\n        to:   飛んでくれば\n\n  - expected: $1しま$2\n    pattern: /(て|[いん]で)仕舞(わ|い|う|え|お|っ)/\n    prh: 補助動詞はかな書きにします\n\n  - expected: $1い$3\n    # 「行っ(て)」は「おこなっ」とも読めるため除外\n    pattern: /((つい)?て|[いん]で)行(か|き|く|け|こ)/\n    regexpMustEmpty: $2\n    prh: 補助動詞はかな書きにします\n    specs:\n      - from: 泳いで行こう\n        to:   泳いでいこう\n      - from: 本件について行くべき場所\n        to:   本件について行くべき場所\n      - from: 修正を最優先で行った\n        to:   修正を最優先で行った\n\n  - expected: $1お$2\n    pattern: /(て|[いん]で)置(か|き|く|け|こ|い)/\n    prh: 補助動詞はかな書きにします\n\n  - expected: ついに\n    pattern: /遂に/\n    prh: 語彙化した副詞はなるべくかな書きにします\n\n  - expected: まれに\n    pattern: /(希|稀)に/\n    prh: 語彙化した副詞はなるべくかな書きにします\n\n  - expected: ようやく\n    pattern: /漸く/\n    prh: 語彙化した副詞はかな書きにします\n\n  - expected: しばらく\n    pattern: /暫く/\n    prh: 語彙化した副詞はかな書きにします\n\n  - expected: おそらく\n    pattern: 恐らく\n    prh: 語彙化した副詞はかな書きにします\n\n  - expected: すべて\n    pattern: 全て\n    prh: 語彙化した副詞はかな書きにします\n\n  - expected: でき$1\n    pattern: /出来(る|た[^て]|ます|まし|ませ|ない|ず)/\n    prh: 「出来る」は「できる」にします\n    specs:\n      - from: 出来ますが\n        to: できますが\n      - from: 出来ない\n        to: できない\n      - from: 出来たての\n        to: 出来たての\n      - from: よい出来の\n        to: よい出来の\n\n  - expected: $1とき$2\n    pattern:  /([うくぐすつぬぶむる]|[なの]|[いん]だ|た)時(は|が|で|を|すら|な|の|に|も|から|まで|、|。|$)/\n    prh: 形式名詞としての「とき」はかな書きにします\n    specs:\n      - from: どんな時も\n        to:   どんなときも\n      - from: その時はそう思った\n        to:   そのときはそう思った\n      - from: エラーが出た時は\n        to:   エラーが出たときは\n      - from: 終了までの時間\n        to:   終了までの時間\n      - from: 針が歪んだ時計\n        to:   針が歪んだ時計\n      - from: 処理が終わった時。\n        to:   処理が終わったとき。\n\n  - expected: $1\n    pattern: /(ユーザ|リスナ|エディタ|ハンドラ|トリガ|バンドラ|テスタ|リンタ|フォーマッタ|レンダラ|カウンタ|リデューサ|ブラウザ|アクセシビリティ|パラメータ|ギャラリ|サーバ)ー/\n    prh: 3音以上の技術用語の最後の長音府は原則として省略します\n\n  - expected: $1レンダー\n    pattern: /(初回|再|条件付き)レンダリング/\n    prh: レンダリングではなくレンダーとします\n    specs:\n      - from: 初回レンダリング\n        to: 初回レンダー\n      - from: 再レンダリング\n        to: 再レンダー\n\n  - expected: レンダー$1\n    pattern: /レンダリング(する|し|時|後|前)/\n    prh: レンダリングではなくレンダーとします\n\n  - expected: コンテクスト\n    pattern: コンテキスト\n\n  - expected: エクスポート\n    pattern: エキスポート\n\n  - expected: 伝播\n    pattern: 伝搬\n    prh: イベントの propagation は伝搬ではなく伝播とします\n\n  - expected: 純関数\n    pattern: 純粋関数\n\n  - expected: プレフィックス\n    pattern: (プレフィクス|プリフィックス|プリフィクス)\n\n  - expected: サフィックス\n    pattern: サフィクス\n\n  - expected: リファレンス\n    pattern: リファランス\n\n  - expected: 返り値\n    pattern: 戻り値\n    prh: 関数からの return value は返り値とします\n\n  - expected: プレーヤ\n    patterns:\n      - プレーヤー\n      - プレイヤー\n      - プレイヤ"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\n      \"dom\",\n      \"dom.iterable\",\n      \"esnext\"\n    ],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"noImplicitAny\": true,\n    \"noImplicitReturns\": true,\n    \"noImplicitThis\": true,\n    \"strictNullChecks\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"baseUrl\": \"src\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ]\n  },\n  \"include\": [\n    \"next-env.d.ts\",\n    \"src/**/*.ts\",\n    \"src/**/*.tsx\",\n    \".next/types/**/*.ts\"\n  ],\n  \"exclude\": [\n    \"node_modules\"\n  ]\n}\n"
  },
  {
    "path": "vercel.json",
    "content": "{\n  \"github\": {\n    \"silent\": true\n  },\n  \"trailingSlash\": false,\n  \"redirects\": [\n    {\n      \"source\": \"/reference\",\n      \"destination\": \"/reference/react\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/reference/react-dom/hooks/useFormState\",\n      \"destination\": \"/reference/react/useActionState\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/learn/meet-the-team\",\n      \"destination\": \"/community/team\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/link/warning-keys\",\n      \"destination\": \"/learn/rendering-lists#keeping-list-items-in-order-with-key\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/lists-and-keys\",\n      \"destination\": \"/learn/rendering-lists#keeping-list-items-in-order-with-key\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/invalid-hook-call\",\n      \"destination\": \"/warnings/invalid-hook-call-warning\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/hooks-data-fetching\",\n      \"destination\": \"/reference/react/useEffect#fetching-data-with-effects\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/special-props\",\n      \"destination\": \"/warnings/special-props\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/dangerously-set-inner-html\",\n      \"destination\": \"/reference/react-dom/components/common#dangerously-setting-the-inner-html\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/controlled-components\",\n      \"destination\": \"/reference/react-dom/components/input#controlling-an-input-with-a-state-variable\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/react-devtools\",\n      \"destination\": \"/learn/react-developer-tools\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/invalid-aria-props\",\n      \"destination\": \"/warnings/invalid-aria-prop\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/hydration-mismatch\",\n      \"destination\": \"/reference/react-dom/client/hydrateRoot#hydrating-server-rendered-html\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/switch-to-createroot\",\n      \"destination\": \"/blog/2022/03/08/react-18-upgrade-guide#updates-to-client-rendering-apis\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/error-boundaries\",\n      \"destination\": \"/reference/react/Component#catching-rendering-errors-with-an-error-boundary\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/strict-mode-find-node\",\n      \"destination\": \"https://18.react.dev/reference/react-dom/findDOMNode#alternatives\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/rules-of-hooks\",\n      \"destination\": \"/warnings/invalid-hook-call-warning\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/event-pooling\",\n      \"destination\": \"https://legacy.reactjs.org/docs/legacy-event-pooling.html\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/legacy-context\",\n      \"destination\": \"https://react.dev/blog/2024/04/25/react-19-upgrade-guide#removed-removing-legacy-context\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/crossorigin-error\",\n      \"destination\": \"https://legacy.reactjs.org/docs/cross-origin-errors.html\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/react-polyfills\",\n      \"destination\": \"https://legacy.reactjs.org/docs/javascript-environment-requirements.html\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/wrap-tests-with-act\",\n      \"destination\": \"https://legacy.reactjs.org/docs/test-utils.html#act\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/refs-must-have-owner\",\n      \"destination\": \"https://legacy.reactjs.org/warnings/refs-must-have-owner.html\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/derived-state\",\n      \"destination\": \"https://legacy.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/strict-mode-string-ref\",\n      \"destination\": \"https://legacy.reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/perf-use-production-build\",\n      \"destination\": \"https://legacy.reactjs.org/docs/optimizing-performance.html#use-the-production-build\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/unsafe-component-lifecycles\",\n      \"destination\": \"https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/test-utils-mock-component\",\n      \"destination\": \"https://gist.github.com/bvaughn/fbf41b3f895bf2d297935faa5525eee9\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/attribute-behavior\",\n      \"destination\": \"https://legacy.reactjs.org/blog/2017/09/08/dom-attributes-in-react-16.html#changes-in-detail\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/react-devtools-faq\",\n      \"destination\": \"https://github.com/facebook/react/tree/main/packages/react-devtools#faq\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/setstate-in-render\",\n      \"destination\": \"https://github.com/facebook/react/issues/18178#issuecomment-595846312\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/new-jsx-transform\",\n      \"destination\": \"https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/link/cra\",\n      \"destination\": \"/blog/2025/02/14/sunsetting-create-react-app\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/warnings/version-mismatch\",\n      \"destination\": \"/warnings/invalid-hook-call-warning#mismatching-versions-of-react-and-react-dom\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/reference/react/directives\",\n      \"destination\": \"/reference/rsc/directives\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/reference/react/use-client\",\n      \"destination\": \"/reference/rsc/use-client\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/reference/react/use-server\",\n      \"destination\": \"/reference/rsc/use-server\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/reference/rsc/server-actions\",\n      \"destination\": \"/reference/rsc/server-functions\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/reference/react-dom/findDOMNode\",\n      \"destination\": \"https://18.react.dev/reference/react-dom/findDOMNode\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/reference/react/createFactory\",\n      \"destination\": \"https://18.react.dev/reference/react/createFactory\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/reference/react-dom/render\",\n      \"destination\": \"https://18.react.dev/reference/react-dom/render\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/reference/react-dom/hydrate\",\n      \"destination\": \"https://18.react.dev/reference/react-dom/hydrate\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/reference/react-dom/unmountComponentAtNode\",\n      \"destination\": \"https://18.react.dev/reference/react-dom/unmountComponentAtNode\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/reference/react-dom/server/renderToStaticNodeStream\",\n      \"destination\": \"https://18.react.dev/reference/react-dom/server/renderToStaticNodeStream\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/reference/react-dom/server/renderToNodeStream\",\n      \"destination\": \"https://18.react.dev/reference/react-dom/server/renderToNodeStream\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/learn/start-a-new-react-project\",\n      \"destination\": \"/learn/creating-a-react-app\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/learn/building-a-react-framework\",\n      \"destination\": \"/learn/build-a-react-app-from-scratch\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/blog/2024/04/25/react-19\",\n      \"destination\": \"/blog/2024/12/05/react-19\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/feed.xml\",\n      \"destination\": \"/rss.xml\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/reference/react/experimental_useEffectEvent\",\n      \"destination\": \"/reference/react/useEffectEvent\",\n      \"permanent\": true\n    },\n    {\n      \"source\": \"/blog/2025/04/21/react-compiler-rc\",\n      \"destination\": \"/blog/2025/10/07/react-compiler-1\",\n      \"permanent\": true\n    }\n  ],\n  \"headers\": [\n    {\n      \"source\": \"/fonts/(.*).woff2\",\n      \"headers\": [\n        {\n          \"key\": \"Cache-Control\",\n          \"value\": \"public, max-age=31536000, immutable\"\n        }\n      ]\n    }\n  ]\n}\n"
  }
]