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 680fd323b..644bbcee3 100644 --- a/src/content/community/meetups.md +++ b/src/content/community/meetups.md @@ -48,6 +48,7 @@ Do you have a local React.js meetup? Add it here! (Please keep the list alphabet * [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 bc9f4ffb3..44ba4d1c7 100644 --- a/src/content/learn/describing-the-ui.md +++ b/src/content/learn/describing-the-ui.md @@ -10,14 +10,15 @@ React on JavaScript kirjasto käyttöliittymien (UI) renderöintiin. Käyttölii -- [Miten kirjoitat ensimmäisen komponenttisi](/learn/your-first-component) -- [Miten ja milloin luot monikomponenttisia tiedostoja](/learn/importing-and-exporting-components) -- [Miten lisäät merkintäkoodia JavaScriptiin JSX hyödyntäen](/learn/writing-markup-with-jsx) -- [Miten käyttää aaltosulkeita JSX:n kanssa JavaScript toiminnallisuuksien hyödyntämiseksi komponenteissa](/learn/javascript-in-jsx-with-curly-braces) -- [Miten mukauttaa komponentteja propeilla](/learn/passing-props-to-a-component) -- [Miten renderöidä ehdollisesti komponentteja](/learn/conditional-rendering) -- [Miten renderöidä useita komponentteja samanaikaisesti](/learn/rendering-lists) -- [Miten välttää bugeja pitämällä komponentit puhtaina](/learn/keeping-components-pure) +* [Miten kirjoitat ensimmäisen komponenttisi](/learn/your-first-component) +* [Miten ja milloin luot monikomponenttisia tiedostoja](/learn/importing-and-exporting-components) +* [Miten lisäät merkintäkoodia JavaScriptiin JSX hyödyntäen](/learn/writing-markup-with-jsx) +* [Miten käyttää aaltosulkeita JSX:n kanssa JavaScript toiminnallisuuksien hyödyntämiseksi komponenteissa](/learn/javascript-in-jsx-with-curly-braces) +* [Miten mukauttaa komponentteja propeilla](/learn/passing-props-to-a-component) +* [Miten renderöidä ehdollisesti komponentteja](/learn/conditional-rendering) +* [Miten renderöidä useita komponentteja samanaikaisesti](/learn/rendering-lists) +* [Miten välttää bugeja pitämällä komponentit puhtaina](/learn/keeping-components-pure) +* [Miten UI:n ajatteleminen puuna on hyödyllistä](/learn/understanding-your-ui-as-a-tree) @@ -519,6 +520,29 @@ Lue **[Komponenttien pitäminen puhtaana](/learn/keeping-components-pure)** oppi +## UI puuna {/*your-ui-as-a-tree*/} + +React käyttää puita mallintaakseen relaatioita komponenttien ja moduulien välillä. + +React-renderöintipuu on esitys ylä- ja alatason suhteesta komponenttien välillä. + +Esimerkki React-renderöintipuusta. + +Komponentit lähellä puun juurta, lähellä yläosaa, ajatellaan olevan ylätason komponentteja. Komponentteja, joilla ei ole alakomponentteja ovat lehti-komponentteja. Tämä kategorisointi komponenteista on hyvä ymmärtää datavirtauksen ja renderöintitehon kannalta. + +JavaScript-moduulien välisen suhteen mallintaminen on toinen hyödyllinen tapa ymmärtää sovellustasi. Viittaamme siihen moduuliriippuvuuspuuna. + +Esimerkki moduuliriippuvuuspuu. + +Riippuvuuspuuta käytetään usein rakennustyökaluissa, jotta kaikki relevantti JavaScript-koodi voidaan pakata selaimen ladattavaksi ja renderöitavaksi. Suuri pakkauskoko heikentää käyttäjäkokemusta React-sovelluksissa. Moduuliriippuvuuspuun ymmärtäminen on hyödyllistä tällaisia ongelmia debugatessa. + + + +Lue **[Your UI as a Tree](/learn/understanding-your-ui-as-a-tree)** oppiaksesi, miten luoda renderöinti- ja moduuliriippuvuuspuut React-sovellukselle sekä miten ne ovat hyödyllisiä ajatusmalleja käyttäjäkokemuksen ja suorituskyvyn parantamiseksi. + + + + ## Mitä seuraavaksi? {/*whats-next*/} Siirry seuraavaksi [Ensimmäinen komponenttisi](/learn/your-first-component) lukeaksesi tämän luvun sivu kerrallaan! diff --git a/src/content/learn/preserving-and-resetting-state.md b/src/content/learn/preserving-and-resetting-state.md index e92cb096d..fbb31fbb1 100644 --- a/src/content/learn/preserving-and-resetting-state.md +++ b/src/content/learn/preserving-and-resetting-state.md @@ -17,24 +17,10 @@ Tila on eristetty komponenttien välillä. React pitää kirjaa siitä, mikä ti -## Käyttöliittymäpuu {/*the-ui-tree*/} - -Selaimet käyttävät monia puumalleja käyttöliittymän mallintamiseen. [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) edustaa HTML-elementtejä, [CSSOM](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model) tekee saman CSS:lle. On olemassa jopa [saavutettavuuspuu](https://developer.mozilla.org/docs/Glossary/Accessibility_tree)! - -React käyttää myös puurakenteita käyttöliittymäsi hallintaan ja mallintamiseen. React rakentaa **UI puita** JSX koodistasi. Sitten React DOM päivittää selaimen DOM elementit vastaamaan tuota UI puuta. (React Native kääntää nämä puut näkymiksi, jotka voidaan näyttää puhelinalustoilla.) - - - - - -Komponenteista React luo käyttöliittymäpuun, jota React DOM käyttää renderöidäkseen DOM:n - - - - - ## Tila on sidottu sijaintiin puussa {/*state-is-tied-to-a-position-in-the-tree*/} +React rakentaa [renderöintipuun](learn/understanding-your-ui-as-a-tree#the-render-tree) UI:n komponenttirakenteesta. + Kun annat komponentille tilan, saatat ajatella, että tila "asuu" komponentin sisällä. Mutta tila oikeasti pidetään Reactin sisällä. React yhdistää jokaisen hallussa olevan tilatiedon oikeaan komponenttiin sen mukaan, missä kohtaa käyttöliittymäpuuta kyseinen komponentti sijaitsee. Tässä esimerkissä on vain yksi `` JSX tagi, mutta se on renderöity kahdessa eri kohdassa: @@ -188,7 +174,7 @@ Tilan päivittäminen -React pitää tilan muistissa niin kauan kuin renderlit samaa komponenttia samassa sijainnissa. Tämän nähdäksesi, korota molempia laskureita ja sitten poista toinen komponentti poistamalla valinta "Render the second counter" valintaruudusta, ja sitten lisää se takaisin valitsemalla se uudelleen: +React pitää tilan muistissa niin kauan kuin renderöit samaa komponenttia samassa sijainnissa. Tämän nähdäksesi, korota molempia laskureita ja sitten poista toinen komponentti poistamalla valinta "Render the second counter" valintaruudusta, ja sitten lisää se takaisin valitsemalla se uudelleen: diff --git a/src/content/learn/tutorial-tic-tac-toe.md b/src/content/learn/tutorial-tic-tac-toe.md index dc33c3ce3..860a79985 100644 --- a/src/content/learn/tutorial-tic-tac-toe.md +++ b/src/content/learn/tutorial-tic-tac-toe.md @@ -2075,7 +2075,7 @@ export default function Game() { } ``` -Voit nähdä miltä koodisi tulisi näyttää alla. Huomaa, että sinun tulisi nähdä virhe kehittäjätyökalujen konsolissa, jossa lukee: +Voit nähdä miltä koodisi tulisi näyttää alla. Huomaa, että sinun tulisi nähdä virhe kehittäjätyökalujen konsolissa, jossa lukee: Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `Game`. 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..2a5a24b85 --- /dev/null +++ b/src/content/learn/understanding-your-ui-as-a-tree.md @@ -0,0 +1,300 @@ +--- +title: Understanding Your UI as a Tree +--- + + + +Your React app is taking shape with many components being nested within each other. How does React keep track of your app's component structure? + +React, and many other UI libraries, model UI as a tree. Thinking of your app as a tree is useful for understanding the relationship between components. This understanding will help you debug future concepts like performance and state management. + + + + + +* How React "sees" component structures +* What a render tree is and what it is useful for +* What a module dependency tree is and what it is useful for + + + +## Your UI as a tree {/*your-ui-as-a-tree*/} + +Trees are a relationship model between items and UI is often represented using tree structures. For example, browsers use tree structures to model HTML ([DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction)) and CSS ([CSSOM](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model)). Mobile platforms also use trees to represent their view hierarchy. + + + +React creates a UI tree from your components. In this example, the UI tree is then used to render to the DOM. + + +Like browsers and mobile platforms, React also uses tree structures to manage and model the relationship between components in a React app. These trees are useful tools to understand how data flows through a React app and how to optimize rendering and app size. + +## The Render Tree {/*the-render-tree*/} + +A major feature of components is the ability to compose components of other components. As we [nest components](/learn/your-first-component#nesting-and-organizing-components), we have the concept of parent and child components, where each parent component may itself be a child of another component. + +When we render a React app, we can model this relationship in a tree, known as the render tree. + +Here is a React app that renders inspirational quotes. + + + +```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 creates a *render tree*, a UI tree, composed of the rendered components. + + + + +From the example app, we can construct the above render tree. + +The tree is composed of nodes, each of which represents a component. `App`, `FancyText`, `Copyright`, to name a few, are all nodes in our tree. + +The root node in a React render tree is the [root component](/learn/importing-and-exporting-components#the-root-component-file) of the app. In this case, the root component is `App` and it is the first component React renders. Each arrow in the tree points from a parent component to a child component. + + + +#### Where are the HTML tags in the render tree? {/*where-are-the-html-elements-in-the-render-tree*/} + +You'll notice in the above render tree, there is no mention of the HTML tags that each component renders. This is because the render tree is only composed of React [components](learn/your-first-component#components-ui-building-blocks). + +React, as a UI framework, is platform agnostic. On react.dev, we showcase examples that render to the web, which uses HTML markup as its UI primitives. But a React app could just as likely render to a mobile or desktop platform, which may use different UI primitives like [UIView](https://developer.apple.com/documentation/uikit/uiview) or [FrameworkElement](https://learn.microsoft.com/en-us/dotnet/api/system.windows.frameworkelement?view=windowsdesktop-7.0). + +These platform UI primitives are not a part of React. React render trees can provide insight to our React app regardless of what platform your app renders to. + + + +A render tree represents a single render pass of a React application. With [conditional rendering](/learn/conditional-rendering), a parent component may render different children depending on the data passed. + +We can update the app to conditionally render either an inspirational quote or color. + + + +```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; +} +``` + + + + +With conditional rendering, across different renders, the render tree may render different components. + + + +In this example, depending on what `inspiration.type` is, we may render `` or ``. The render tree may be different for each render pass. + +Although render trees may differ across render pases, these trees are generally helpful for identifying what the top-level and leaf components are in a React app. Top-level components are the components nearest to the root component and affect the rendering performance of all the components beneath them and often contain the most complexity. Leaf components are near the bottom of the tree and have no child components and are often frequently re-rendered. + +Identifying these categories of components are useful for understanding data flow and performance of your app. + +## The Module Dependency Tree {/*the-module-dependency-tree*/} + +Another relationship in a React app that can be modeled with a tree are an app's module dependencies. As we [break up our components](/learn/importing-and-exporting-components#exporting-and-importing-a-component) and logic into separate files, we create [JS modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) where we may export components, functions, or constants. + +Each node in a module dependency tree is a module and each branch represents an `import` statement in that module. + +If we take the previous Inspirations app, we can build a module dependency tree, or dependency tree for short. + + + +The module dependency tree for the Inspirations app. + + + +The root node of the tree is the root module, also known as the entrypoint file. It often is the module that contains the root component. + +Comparing to the render tree of the same app, there are similar structures but some notable differences: + +* The nodes that make-up the tree represent modules, not components. +* Non-component modules, like `inspirations.js`, are also represented in this tree. The render tree only encapsulates components. +* `Copyright.js` appears under `App.js` but in the render tree, `Copyright`, the component, appears as a child of `InspirationGenerator`. This is because `InspirationGenerator` accepts JSX as [children props](/learn/passing-props-to-a-component#passing-jsx-as-children), so it renders `Copyright` as a child component but does not import the module. + +Dependency trees are useful to determine what modules are necessary to run your React app. When building a React app for production, there is typically a build step that will bundle all the necessary JavaScript to ship to the client. The tool responsible for this is called a [bundler](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview#the_modern_tooling_ecosystem), and bundlers will use the dependency tree to determine what modules should be included. + +As your app grows, often the bundle size does too. Large bundle sizes are expensive for a client to download and run. Large bundle sizes can delay the time for your UI to get drawn. Getting a sense of your app's dependency tree may help with debugging these issues. + +[comment]: <> (perhaps we should also deep dive on conditional imports) + + + +* Trees are a common way to represent the relationship between entities. They are often used to model UI. +* Render trees represent the nested relationship between React components across a single render. +* With conditional rendering, the render tree may change across different renders. With different prop values, components may render different children components. +* Render trees help identify what the top-level and leaf components are. Top-level components affect the rendering performance of all components beneath them and leaf components are often re-rendered frequently. Identifying them is useful for understanding and debugging rendering performance. +* Dependency trees represent the module dependencies in a React app. +* Dependency trees are used by build tools to bundle the necessary code to ship an app. +* Dependency trees are useful for debugging large bundle sizes that slow time to paint and expose opportunities for optimizing what code is bundled. + + + +[TODO]: <> (Add challenges) diff --git a/src/content/reference/react-dom/components/common.md b/src/content/reference/react-dom/components/common.md index 52b5f1912..09f1901fc 100644 --- a/src/content/reference/react-dom/components/common.md +++ b/src/content/reference/react-dom/components/common.md @@ -694,7 +694,7 @@ Tapahtumakäsittelijätyyppi `onWheel` tapahtumalle. ```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/content/reference/react/cache.md b/src/content/reference/react/cache.md index 7f9afdb99..65d95ab76 100644 --- a/src/content/reference/react/cache.md +++ b/src/content/reference/react/cache.md @@ -252,7 +252,7 @@ function Page({id}) { When rendering `Page`, the component calls `getUser` but note that it doesn't use the returned data. This early `getUser` call kicks off the asynchronous database query that occurs while `Page` is doing other computational work and rendering children. -When rendering `Profile`, we call `getUser` again. If the initial `getUser` call has already returned and cached the user data, when when `Profile` asks and waits for this data, it can simply read from the cache without requiring another remote procedure call. If the initial data request hasn't been completed, preloading data in this pattern reduces delay in data-fetching. +When rendering `Profile`, we call `getUser` again. If the initial `getUser` call has already returned and cached the user data, when `Profile` asks and waits for this data, it can simply read from the cache without requiring another remote procedure call. If the initial data request hasn't been completed, preloading data in this pattern reduces delay in data-fetching. 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 ( +