diff --git a/public/images/docs/diagrams/conditional_render_tree.dark.png b/public/images/docs/diagrams/conditional_render_tree.dark.png new file mode 100644 index 000000000..5189a44c8 Binary files /dev/null and b/public/images/docs/diagrams/conditional_render_tree.dark.png differ diff --git a/public/images/docs/diagrams/conditional_render_tree.png b/public/images/docs/diagrams/conditional_render_tree.png new file mode 100644 index 000000000..c76e8cb63 Binary files /dev/null and b/public/images/docs/diagrams/conditional_render_tree.png differ diff --git a/public/images/docs/diagrams/generic_dependency_tree.dark.png b/public/images/docs/diagrams/generic_dependency_tree.dark.png new file mode 100644 index 000000000..64694f585 Binary files /dev/null and b/public/images/docs/diagrams/generic_dependency_tree.dark.png differ diff --git a/public/images/docs/diagrams/generic_dependency_tree.png b/public/images/docs/diagrams/generic_dependency_tree.png new file mode 100644 index 000000000..8ab6f1a34 Binary files /dev/null and b/public/images/docs/diagrams/generic_dependency_tree.png differ diff --git a/public/images/docs/diagrams/generic_render_tree.dark.png b/public/images/docs/diagrams/generic_render_tree.dark.png new file mode 100644 index 000000000..859fdaa96 Binary files /dev/null and b/public/images/docs/diagrams/generic_render_tree.dark.png differ diff --git a/public/images/docs/diagrams/generic_render_tree.png b/public/images/docs/diagrams/generic_render_tree.png new file mode 100644 index 000000000..952cf5faa Binary files /dev/null and b/public/images/docs/diagrams/generic_render_tree.png differ diff --git a/public/images/docs/diagrams/module_dependency_tree.dark.png b/public/images/docs/diagrams/module_dependency_tree.dark.png new file mode 100644 index 000000000..e8a85f7c0 Binary files /dev/null and b/public/images/docs/diagrams/module_dependency_tree.dark.png differ diff --git a/public/images/docs/diagrams/module_dependency_tree.png b/public/images/docs/diagrams/module_dependency_tree.png new file mode 100644 index 000000000..0dcaaa7aa Binary files /dev/null and b/public/images/docs/diagrams/module_dependency_tree.png differ diff --git a/public/images/docs/diagrams/render_tree.dark.png b/public/images/docs/diagrams/render_tree.dark.png new file mode 100644 index 000000000..117d7ff3e Binary files /dev/null and b/public/images/docs/diagrams/render_tree.dark.png differ diff --git a/public/images/docs/diagrams/render_tree.png b/public/images/docs/diagrams/render_tree.png new file mode 100644 index 000000000..1ea750bb0 Binary files /dev/null and b/public/images/docs/diagrams/render_tree.png differ diff --git a/src/content/community/meetups.md b/src/content/community/meetups.md index 8e9204414..8af572910 100644 --- a/src/content/community/meetups.md +++ b/src/content/community/meetups.md @@ -48,6 +48,7 @@ title: React ミーティング * [Montreal, QC - React Native](https://www.meetup.com/fr-FR/React-Native-MTL/) * [Vancouver, BC](https://www.meetup.com/ReactJS-Vancouver-Meetup/) * [Ottawa, ON](https://www.meetup.com/Ottawa-ReactJS-Meetup/) +* [Saskatoon, SK](https://www.meetup.com/saskatoon-react-meetup/) * [Toronto, ON](https://www.meetup.com/Toronto-React-Native/events/) ## Chile {/*chile*/} diff --git a/src/content/learn/describing-the-ui.md b/src/content/learn/describing-the-ui.md index b8d5b1b58..7e4165b35 100644 --- a/src/content/learn/describing-the-ui.md +++ b/src/content/learn/describing-the-ui.md @@ -18,6 +18,7 @@ React は、ユーザインターフェース(UI)を表示するための Ja * [コンポーネントを条件付きでレンダーする方法](/learn/conditional-rendering) * [複数のコンポーネントを同時にレンダーする方法](/learn/rendering-lists) * [コンポーネントを純粋に保つことで混乱を避ける方法](/learn/keeping-components-pure) +* [UI をツリーとして理解することが有用である理由](/learn/understanding-your-ui-as-a-tree) @@ -523,6 +524,29 @@ export default function TeaSet() { +## UI をツリーとして理解する {/*your-ui-as-a-tree*/} + +React はコンポーネント間あるいはモジュール間の関係性をモデル化するために、ツリー構造を使用します。 + +React レンダーツリーとはコンポーネントの親子関係を表現したものです。 + +React のレンダーツリーの例 + +ツリーの上側、つまりルートに近いコンポーネントはトップレベルコンポーネントです。子を持たないコンポーネントはリーフ(葉)コンポーネントです。このようなコンポーネントの分類は、データの流れやレンダーパフォーマンスを理解する際に有用です。 + +アプリを理解する上では、JavaScript のモジュール間の関係性をモデルすることも重要です。このようなものをモジュール依存関係ツリーと呼びます。 + +モジュール依存関係ツリーの例 + +依存関係ツリーは、関連する JavaScript コードをすべてバンドルしてクライアントがダウンロード・レンダーできるようにするために、ビルドツールでよく使用されます。バンドルサイズが大きいと、React アプリのユーザ体験は悪化します。モジュール依存関係ツリーを理解することは、そのような問題をデバッグするのに役立ちます。 + + + +[**UI をツリーとして理解する**](/learn/understanding-your-ui-as-a-tree)を読んで、レンダーツリーやモジュール依存関係ツリーの作り方、そしてそのような考え方がユーザ体験やパフォーマンスを改善する際にどのように役立つのかについて学びましょう。 + + + + ## 次のステップ {/*whats-next*/} [初めてのコンポーネント](/learn/your-first-component)に進んで、この章をページごとに読み進めましょう! diff --git a/src/content/learn/preserving-and-resetting-state.md b/src/content/learn/preserving-and-resetting-state.md index 76ce861ab..548f5cfe7 100644 --- a/src/content/learn/preserving-and-resetting-state.md +++ b/src/content/learn/preserving-and-resetting-state.md @@ -10,33 +10,17 @@ state は複数のコンポーネント間で独立しています。React は U -* React にはコンポーネント構造がどのように「見える」のか * React が state の保持とリセットを行うタイミング * React にコンポーネントの state のリセットを強制する方法 * key とタイプが state の保持にどのように影響するか -## UI ツリー {/*the-ui-tree*/} +## state はレンダーツリー内の位置に結びついている {/*state-is-tied-to-a-position-in-the-tree*/} -ブラウザは、UI をモデル化するために多くのツリー構造を使用します。[DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) は HTML 要素を、[CSSOM](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model) は CSS を表現します。[アクセシビリティツリー](https://developer.mozilla.org/docs/Glossary/Accessibility_tree)というものもあります! - -React もまた、ユーザが作成した UI を管理しモデリングするためにツリー構造を使用します。React は JSX から **UI ツリー**を作成します。次に React DOM が、その UI ツリーに合わせてブラウザの DOM 要素を更新します。(React Native の場合は UI ツリーをモバイルプラットフォーム固有の要素に変換します。) - - - - - -React はコンポーネントから UI ツリーを作成し、React DOM はそれを DOM に変換する - - - - - -## state はツリー内の位置に結びついている {/*state-is-tied-to-a-position-in-the-tree*/} - -コンポーネントに state を与えると、その state はそのコンポーネントの内部で「生存」しているように思えるかもしれません。しかし、実際には state は React の中に保持されています。React は、「UI ツリー内でそのコンポーネントがどの位置にあるか」に基づいて、保持している各 state を正しいコンポーネントに関連付けます。 +React はあなたの UI のコンポーネント構造を[レンダーツリー](learn/understanding-your-ui-as-a-tree#the-render-tree)としてビルドします。 +コンポーネントに state を与えると、その state はそのコンポーネントの内部で「生存」しているように思えるかもしれません。しかし、実際には state は React の中に保持されています。React は、「レンダーツリー内でそのコンポーネントがどの位置にあるか」に基づいて、保持している各 state を正しいコンポーネントに関連付けます。 以下のコードには `` JSX タグは 1 つしかありませんが、それが 2 つの異なる位置にレンダーされています。 @@ -190,7 +174,7 @@ state の更新 -React は、同じコンポーネントを同じ位置でレンダーしている限り、その state を保持し続けます。これを確認するため、両方のカウンタを増加させてから、"Render the second counter" のチェックボックスのチェックを外して 2 つ目のコンポーネントを削除し、再びチェックを入れて元に戻してみてください。 +React は、同じコンポーネントをツリー内の同じ位置でレンダーしている限り、その state を保持し続けます。これを確認するため、両方のカウンタを増加させてから、"Render the second counter" のチェックボックスのチェックを外して 2 つ目のコンポーネントを削除し、再びチェックを入れて元に戻してみてください。 diff --git a/src/content/learn/understanding-your-ui-as-a-tree.md b/src/content/learn/understanding-your-ui-as-a-tree.md new file mode 100644 index 000000000..146b49802 --- /dev/null +++ b/src/content/learn/understanding-your-ui-as-a-tree.md @@ -0,0 +1,300 @@ +--- +title: UI をツリーとして理解する +--- + + + +React アプリは、多数のコンポーネントが互いにネストされることで形成されます。React はどのようにアプリのコンポーネント構造を管理しているのでしょうか? + +React をはじめとする多くの UI ライブラリは、UI をツリーとしてモデル化します。アプリをツリーとして捉えることにより、コンポーネント間の関係を理解するのに役立ちます。これを理解することで、これから学んでいくパフォーマンスや state 管理に関連した問題をデバッグするのに役立つでしょう。 + + + + + +* React にはコンポーネント構造がどのように「見える」のか +* レンダーツリーとは何で、何の役に立つのか +* モジュール依存ツリーとは何で、何の役に立つのか + + + +## UI をツリーとして理解する {/*your-ui-as-a-tree*/} + +ツリーとはアイテム間の関係を表すモデルの一種です。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)) をモデル化するためにツリー構造を使用します。モバイルプラットフォームもビューの階層構造を表現するためにツリーを使用します。 + + + +React はコンポーネントから UI ツリーを作成する。この例では、UI ツリーは DOM へのレンダーに使用されている。 + + +ブラウザやモバイルプラットフォームと同様に、React もツリー構造を使用して React アプリ内のコンポーネント間の関係を管理し、モデル化します。そのようなツリーは、React アプリ内をデータがどのように流れるか理解し、レンダーやアプリサイズを最適化する際の有用なツールとなります。 + +## レンダーツリー {/*the-render-tree*/} + +コンポーネントの主要な特徴のひとつは、コンポーネント同士を組み合わせられることです。[コンポーネントをネストする](/learn/your-first-component#nesting-and-organizing-components)ことで、親コンポーネント・子コンポーネントという概念が発生します。その親コンポーネントもまた、別のコンポーネントの子かもしれません。 + +React アプリをレンダーする際、この関係性をツリーとしてモデル化することができます。これをレンダーツリーと呼びます。 + +以下は、ひらめきを与えてくれる格言をレンダーするための React アプリです。 + + + +```js App.js +import FancyText from './FancyText'; +import InspirationGenerator from './InspirationGenerator'; +import Copyright from './Copyright'; + +export default function App() { + return ( + <> + + + + + + ); +} + +``` + +```js FancyText.js +export default function FancyText({title, text}) { + return title + ?

{text}

+ :

{text}

+} +``` + +```js InspirationGenerator.js +import * as React from 'react'; +import quotes from './quotes'; +import FancyText from './FancyText'; + +export default function InspirationGenerator({children}) { + const [index, setIndex] = React.useState(0); + const quote = quotes[index]; + const next = () => setIndex((index + 1) % quotes.length); + + return ( + <> +

Your inspirational quote is:

+ + + {children} + + ); +} +``` + +```js Copyright.js +export default function Copyright({year}) { + return

©️ {year}

; +} +``` + +```js quotes.js +export default [ + "Don’t let yesterday take up too much of today.” — Will Rogers", + "Ambition is putting a ladder against the sky.", + "A joy that's shared is a joy made double.", + ]; +``` + +```css +.fancy { + font-family: 'Georgia'; +} +.title { + color: #007AA3; + text-decoration: underline; +} +.cursive { + font-style: italic; +} +.small { + font-size: 10px; +} +``` + +
+ + + +React は、レンダーされたコンポーネントから構成される UI ツリーである*レンダーツリー*を作成する + + + + +このアプリから、上のようなレンダーツリーを構築することができます。 + +ツリー構造はノードで構成されており、各ノードがコンポーネントを表します。`App`、`FancyText`、`Copyright` などはすべてこのツリーのノードです。 + +React レンダーツリーのルートノードは、アプリの[ルートコンポーネント](/learn/importing-and-exporting-components#the-root-component-file)となります。この場合、ルートコンポーネントは `App` であり、React が最初にレンダーするコンポーネントです。ツリーの各矢印は、親コンポーネントから子コンポーネントに伸びています。 + + + +#### レンダーツリー内の HTML タグはどこに? {/*where-are-the-html-elements-in-the-render-tree*/} + +上記のレンダーツリーの図には、各コンポーネントがレンダーする HTML タグについては載っていません。これは、レンダーツリーとは React の[コンポーネント](learn/your-first-component#components-ui-building-blocks)だけで構成されるものだからです。 + +UI フレームワークとしての 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 プリミティブが使用されるでしょう。 + +これらのプラットフォームの UI プリミティブは React の一部ではありません。React のレンダーツリーを考えることにより、アプリがどのプラットフォームにレンダーされるのかとは独立して、React アプリを理解できるようになります。 + + + +レンダーツリーは、React アプリケーションにおける 1 回のレンダーを表します。[条件付きレンダー](/learn/conditional-rendering)を使用することで、親コンポーネントは渡されたデータに応じて異なる子をレンダーすることができます。 + +アプリを更新して、格言とカラーのいずれかが条件付きでレンダーされるようにしてみましょう。 + + + +```js App.js +import FancyText from './FancyText'; +import InspirationGenerator from './InspirationGenerator'; +import Copyright from './Copyright'; + +export default function App() { + return ( + <> + + + + + + ); +} + +``` + +```js FancyText.js +export default function FancyText({title, text}) { + return title + ?

{text}

+ :

{text}

+} +``` + +```js Color.js +export default function Color({value}) { + return
+} +``` + +```js InspirationGenerator.js +import * as React from 'react'; +import inspirations from './inspirations'; +import FancyText from './FancyText'; +import Color from './Color'; + +export default function InspirationGenerator({children}) { + const [index, setIndex] = React.useState(0); + const inspiration = inspirations[index]; + const next = () => setIndex((index + 1) % inspirations.length); + + return ( + <> +

Your inspirational {inspiration.type} is:

+ {inspiration.type === 'quote' + ? + : } + + + {children} + + ); +} +``` + +```js Copyright.js +export default function Copyright({year}) { + return

©️ {year}

; +} +``` + +```js inspirations.js +export default [ + {type: 'quote', value: "Don’t let yesterday take up too much of today.” — Will Rogers"}, + {type: 'color', value: "#B73636"}, + {type: 'quote', value: "Ambition is putting a ladder against the sky."}, + {type: 'color', value: "#256266"}, + {type: 'quote', value: "A joy that's shared is a joy made double."}, + {type: 'color', value: "#F9F2B4"}, +]; +``` + +```css +.fancy { + font-family: 'Georgia'; +} +.title { + color: #007AA3; + text-decoration: underline; +} +.cursive { + font-style: italic; +} +.small { + font-size: 10px; +} +.colorbox { + height: 100px; + width: 100px; + margin: 8px; +} +``` + + + + +条件付きレンダーにより、違うレンダーではレンダーツリーが異なるコンポーネントをレンダーする。 + + + +この例では、`inspiration.type` の値によって、`` または `` のいずれかがレンダーされます。一連のレンダーが起きるたびに、レンダーツリーは異なったものになる可能性があるのです。 + +毎回のレンダーごとにレンダーツリーが異なることがあるにせよ、このようなツリーは一般的に、React アプリケーションにおいてトップレベルコンポーネントとリーフ(葉, 末端)コンポーネントがどれなのかを理解するのに役立ちます。トップレベルコンポーネントとはルートコンポーネントに最も近いコンポーネントです。下にあるすべてのコンポーネントのレンダーパフォーマンスに影響を与え、しばしばとても複雑な内容を含んでいます。リーフコンポーネントはツリーの下側にあり、子コンポーネントを持たず、通常は頻繁に再レンダーされます。 + +これらのカテゴリのコンポーネントを特定することにより、アプリケーションのデータの流れとパフォーマンスを理解するのに役立ちます。 + +## モジュール依存関係ツリー {/*the-module-dependency-tree*/} + +React アプリにおいて、ツリー構造で関係性をモデル化できるものがもうひとつあります。アプリのモジュールの依存関係です。コンポーネントやロジックを別々のファイルに[分割する](/learn/importing-and-exporting-components#exporting-and-importing-a-component)ことで、[JS モジュール](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)を作成し、コンポーネントや関数や定数をエクスポートします。 + +モジュール依存関係ツリーにおいては、各ノードはモジュールとなり、それぞれの枝はそのモジュール内の `import` 文を表します。 + +先ほどのひらめきアプリの例では、以下のようなモジュール依存関係ツリー(あるいは単に依存関係ツリー)を作成することができます。 + + + +ひらめきアプリのモジュール依存関係ツリー + + + +このツリーのルートノードはルートモジュールで、エントリーポイントファイルとも呼ばれます。これがルートコンポーネントを含んだモジュールであることも多いでしょう。 + +同じアプリのレンダーツリーと比べると、似た部分もありますが、いくつか注目すべき違いがあります。 + +* ツリーを構成するノードが表しているのはコンポーネントではなくモジュールです。 +* コンポーネントの書かれていない `inspirations.js` のようなモジュールもこのツリーには含まれています。レンダーツリーはコンポーネントのみを含みます。 +* `Copyright.js` は `App.js` の下に表示されていますが、レンダーツリーの方では、`Copyright` コンポーネントは `InspirationGenerator` の子でした。これは、`InspirationGenerator` が props である [children](/learn/passing-props-to-a-component#passing-jsx-as-children) 経由で JSX を受け付けるためです。`Copyright` を子コンポーネントとしてレンダーしてはいますが、それに対応するモジュールをインポートしているわけではありません。 + +依存関係ツリーは、React アプリを実行するためにどのモジュールが必要かを判断するのに役立ちます。通常、React アプリを本番環境用にビルドする際には、クライアントに送信するために必要な JavaScript をすべてバンドルにまとめるというビルドステップが存在します。これを担当するツールは[バンドラ](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview#the_modern_tooling_ecosystem)と呼ばれ、バンドラは依存関係ツリーを使用することで、どのモジュールを含めるべきかを決定します。 + +アプリが成長するにつれて、バンドルサイズも大きくなります。バンドルサイズが大きいと、クライアントがダウンロードして実行するのにコストがかかります。バンドルサイズが大きいと、UI が描画されるまでの時間も遅くなります。アプリの依存関係ツリーを把握することで、これらの問題のデバッグに役立つでしょう。 + +[comment]: <> (perhaps we should also deep dive on conditional imports) + + + +* ツリー構造とは、何らかの物どうしの関係性を表現する際の一般的な方法である。UI をモデル化するために多用される。 +* レンダーツリーは、1 回のレンダーにおける React コンポーネント間のネスト関係を表現するものである。 +* 条件付きレンダーにより、毎回のレンダー間でレンダーツリーは変化する可能性がある。例えば props の値が変わることでコンポーネントは異なる子コンポーネントをレンダーする可能性がある。 +* レンダーツリーの概念は、トップレベルとリーフコンポーネントを特定するのに役立つ。トップレベルのコンポーネントはそれらの下の全コンポーネントのレンダーパフォーマンスに影響を与え、リーフコンポーネントは頻繁に再レンダーされる。これらを把握することでレンダーパフォーマンスの理解とデバッグに役立つ。 +* 依存関係ツリーは、React アプリ内のモジュール依存関係を表現する。 +* 依存関係ツリーは、アプリを届けるために必要なコードをバンドルするビルドツールによって使用される。 +* 依存関係ツリーは、ペイントまでの時間を遅らせるバンドルサイズの問題をデバッグしたり、どのコードをバンドル対象とするか最適化するきっかけとなることに役立つ。 + + + +[TODO]: <> (Add challenges) diff --git a/src/content/reference/react-dom/components/common.md b/src/content/reference/react-dom/components/common.md index 5fd7e1211..8ef558402 100644 --- a/src/content/reference/react-dom/components/common.md +++ b/src/content/reference/react-dom/components/common.md @@ -694,7 +694,7 @@ CSS トランジションイベントのためのイベントハンドラタイ ```js
console.log('onScroll')} + onWheel={e => console.log('onWheel')} /> ``` diff --git a/src/content/reference/react-dom/hooks/index.md b/src/content/reference/react-dom/hooks/index.md new file mode 100644 index 000000000..6490dc111 --- /dev/null +++ b/src/content/reference/react-dom/hooks/index.md @@ -0,0 +1,48 @@ +--- +title: "React DOM Hooks" +--- + + + +The `react-dom` package contains Hooks that are only supported for web applications (which run in the browser DOM environment). These Hooks are not supported in non-browser environments like iOS, Android, or Windows applications. If you are looking for Hooks that are supported in web browsers *and other environments* see [the React Hooks page](/reference/react). This page lists all the Hooks in the `react-dom` package. + + + +--- + +## Form Hooks {/*form-hooks*/} + + + +Form Hooks are currently only available in React's canary and experimental channels. Learn more about [React's release channels here](/community/versioning-policy#all-release-channels). + + + +*Forms* let you create interactive controls for submitting information. To manage forms in your components, use one of these Hooks: + +* [`useFormStatus`](/reference/react-dom/hooks/useFormStatus) allows you to make updates to the UI based on the status of the a form. +* `useFormState` allows you to manage state inside a form. + +```js +function Form({ action }) { + async function increment(n) { + return n + 1; + } + const [count, incrementFormAction] = useFormState(increment, 0); + return ( +
+ +
+ ); +} +``` diff --git a/src/content/reference/react-dom/hooks/useFormStatus.md b/src/content/reference/react-dom/hooks/useFormStatus.md new file mode 100644 index 000000000..abaa9b6f2 --- /dev/null +++ b/src/content/reference/react-dom/hooks/useFormStatus.md @@ -0,0 +1,261 @@ +--- +title: useFormStatus +canary: true +--- + + + +The `useFormStatus` Hook is currently only available in React's canary and experimental channels. Learn more about [React's release channels here](/community/versioning-policy#all-release-channels). + + + + + +`useFormStatus` is a Hook that gives you status information of the last form submission. + +```js +const { pending, data, method, action } = useFormStatus(); +``` + + + + + +--- + +## Reference {/*reference*/} + +### `useFormStatus()` {/*use-form-status*/} + +The `useFormStatus` Hook provides status information of the last form submission. + +```js {5},[[1, 6, "status.pending"]] +import { useFormStatus } from "react-dom"; +import action from './actions'; + +function Submit() { + const status = useFormStatus(); + return +} + +export default App() { + return ( +
+ + + ); +} +``` + +To get status information, the `Submit` component must be rendered within a `
`. The Hook returns information like the `pending` property which tells you if the form is actively submitting. + +In the above example, `Submit` uses this information to disable ` + ); +} + +function Form({ action }) { + return ( + + + + ); +} + +export default function App() { + return
; +} +``` + +```js actions.js hidden +export async function submitForm(query) { + await new Promise((res) => setTimeout(res, 1000)); +} +``` + +```json package.json hidden +{ + "dependencies": { + "react": "canary", + "react-dom": "canary", + "react-scripts": "^5.0.0" + }, + "main": "/index.js", + "devDependencies": {} +} +``` + + + + +##### `useFormStatus` will not return status information for a `` rendered in the same component. {/*useformstatus-will-not-return-status-information-for-a-form-rendered-in-the-same-component*/} + +The `useFormStatus` Hook only returns status information for a parent `` and not for any `` rendered in the same component calling the Hook, or child components. + +```js +function Form() { + // 🚩 `pending` will never be true + // useFormStatus does not track the form rendered in this component + const { pending } = useFormStatus(); + return ; +} +``` + +Instead call `useFormStatus` from inside a component that is located inside `
`. + +```js +function Submit() { + // ✅ `pending` will be derived from the form that wraps the Submit component + const { pending } = useFormStatus(); + return ; +} + +function Form() { + // This is the `useFormStatus` tracks + return ( + + + + ); +} +``` + +
+ +### Read the form data being submitted {/*read-form-data-being-submitted*/} + +You can use the `data` property of the status information returned from `useFormStatus` to display what data is being submitted by the user. + +Here, we have a form where users can request a username. We can use `useFormStatus` to display a temporary status message confirming what username they have requested. + + + +```js UsernameForm.js active +import {useState, useMemo, useRef} from 'react'; +import {useFormStatus} from 'react-dom'; + +export default function UsernameForm() { + const {pending, data} = useFormStatus(); + + const [showSubmitted, setShowSubmitted] = useState(false); + const submittedUsername = useRef(null); + const timeoutId = useRef(null); + + useMemo(() => { + if (pending) { + submittedUsername.current = data?.get('username'); + if (timeoutId.current != null) { + clearTimeout(timeoutId.current); + } + + timeoutId.current = setTimeout(() => { + timeoutId.current = null; + setShowSubmitted(false); + }, 2000); + setShowSubmitted(true); + } + }, [pending, data]); + + return ( + <> +
+ + + {showSubmitted ? ( +

Submitted request for username: {submittedUsername.current}

+ ) : null} + + ); +} +``` + +```js App.js +import UsernameForm from './UsernameForm'; +import { submitForm } from "./actions.js"; + +export default function App() { + return ( +
+ + + ); +} +``` + +```js actions.js hidden +export async function submitForm(query) { + await new Promise((res) => setTimeout(res, 1000)); +} +``` + +```json package.json hidden +{ + "dependencies": { + "react": "canary", + "react-dom": "canary", + "react-scripts": "^5.0.0" + }, + "main": "/index.js", + "devDependencies": {} +} +``` +
+ +--- + +## Troubleshooting {/*troubleshooting*/} + +### `status.pending` is never `true` {/*pending-is-never-true*/} + +`useFormStatus` will only return status information for a parent `
`. + +If the component that calls `useFormStatus` is not nested in a ``, `status.pending` will always return `false`. Verify `useFormStatus` is called in a component that is a child of a `` element. + +`useFormStatus` will not track the status of a `` rendered in the same component. See [Pitfall](#useformstatus-will-not-return-status-information-for-a-form-rendered-in-the-same-component) for more details. diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index 392fdf88a..d8fb0c621 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -9,6 +9,15 @@ const MyDocument = () => { return ( +