diff --git a/.eslintrc b/.eslintrc index f617dea2..5731d244 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,8 +5,17 @@ "plugins": ["@typescript-eslint"], "rules": { "no-unused-vars": "off", +<<<<<<< HEAD "@typescript-eslint/no-unused-vars": ["error", { "varsIgnorePattern": "^_" }], "react-hooks/exhaustive-deps": "error" +======= + "@typescript-eslint/no-unused-vars": ["error", {"varsIgnorePattern": "^_"}], + "react-hooks/exhaustive-deps": "error", + "react/no-unknown-property": ["error", {"ignore": ["meta"]}], + "react-compiler/react-compiler": "error", + "local-rules/lint-markdown-code-blocks": "error", + "no-trailing-spaces": "error" +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a }, "env": { "node": true, diff --git a/public/images/docs/scientists/1bX5QH6.jpg b/public/images/docs/scientists/1bX5QH6.jpg new file mode 100644 index 00000000..630b91bd Binary files /dev/null and b/public/images/docs/scientists/1bX5QH6.jpg differ diff --git a/public/images/docs/scientists/1bX5QH6b.jpg b/public/images/docs/scientists/1bX5QH6b.jpg new file mode 100644 index 00000000..7bd07436 Binary files /dev/null and b/public/images/docs/scientists/1bX5QH6b.jpg differ diff --git a/public/images/docs/scientists/1bX5QH6s.jpg b/public/images/docs/scientists/1bX5QH6s.jpg new file mode 100644 index 00000000..0bc3a6f4 Binary files /dev/null and b/public/images/docs/scientists/1bX5QH6s.jpg differ diff --git a/public/images/docs/scientists/2heNQDcm.jpg b/public/images/docs/scientists/2heNQDcm.jpg new file mode 100644 index 00000000..ecc8ab39 Binary files /dev/null and b/public/images/docs/scientists/2heNQDcm.jpg differ diff --git a/public/images/docs/scientists/3aIiwfm.jpg b/public/images/docs/scientists/3aIiwfm.jpg new file mode 100644 index 00000000..e93d8c80 Binary files /dev/null and b/public/images/docs/scientists/3aIiwfm.jpg differ diff --git a/public/images/docs/scientists/5qwVYb1.jpeg b/public/images/docs/scientists/5qwVYb1.jpeg new file mode 100644 index 00000000..cd8b4e55 Binary files /dev/null and b/public/images/docs/scientists/5qwVYb1.jpeg differ diff --git a/public/images/docs/scientists/6o5Vuyu.jpg b/public/images/docs/scientists/6o5Vuyu.jpg new file mode 100644 index 00000000..941563f8 Binary files /dev/null and b/public/images/docs/scientists/6o5Vuyu.jpg differ diff --git a/public/images/docs/scientists/7vQD0fPb.jpg b/public/images/docs/scientists/7vQD0fPb.jpg new file mode 100644 index 00000000..71baab95 Binary files /dev/null and b/public/images/docs/scientists/7vQD0fPb.jpg differ diff --git a/public/images/docs/scientists/7vQD0fPs.jpg b/public/images/docs/scientists/7vQD0fPs.jpg new file mode 100644 index 00000000..5da6b45f Binary files /dev/null and b/public/images/docs/scientists/7vQD0fPs.jpg differ diff --git a/public/images/docs/scientists/9EAYZrtl.jpg b/public/images/docs/scientists/9EAYZrtl.jpg new file mode 100644 index 00000000..7313ffdb Binary files /dev/null and b/public/images/docs/scientists/9EAYZrtl.jpg differ diff --git a/public/images/docs/scientists/AlHTAdDm.jpg b/public/images/docs/scientists/AlHTAdDm.jpg new file mode 100644 index 00000000..735c29cd Binary files /dev/null and b/public/images/docs/scientists/AlHTAdDm.jpg differ diff --git a/public/images/docs/scientists/DgXHVwul.jpg b/public/images/docs/scientists/DgXHVwul.jpg new file mode 100644 index 00000000..a9dba869 Binary files /dev/null and b/public/images/docs/scientists/DgXHVwul.jpg differ diff --git a/public/images/docs/scientists/FJeJR8M.jpg b/public/images/docs/scientists/FJeJR8M.jpg new file mode 100644 index 00000000..433fc350 Binary files /dev/null and b/public/images/docs/scientists/FJeJR8M.jpg differ diff --git a/public/images/docs/scientists/HMFmH6m.jpg b/public/images/docs/scientists/HMFmH6m.jpg new file mode 100644 index 00000000..ac0a5f6c Binary files /dev/null and b/public/images/docs/scientists/HMFmH6m.jpg differ diff --git a/public/images/docs/scientists/IOjWm71s.jpg b/public/images/docs/scientists/IOjWm71s.jpg new file mode 100644 index 00000000..af912e34 Binary files /dev/null and b/public/images/docs/scientists/IOjWm71s.jpg differ diff --git a/public/images/docs/scientists/JBbMpWY.jpg b/public/images/docs/scientists/JBbMpWY.jpg new file mode 100644 index 00000000..a59002bc Binary files /dev/null and b/public/images/docs/scientists/JBbMpWY.jpg differ diff --git a/public/images/docs/scientists/K9HVAGHl.jpg b/public/images/docs/scientists/K9HVAGHl.jpg new file mode 100644 index 00000000..03894f39 Binary files /dev/null and b/public/images/docs/scientists/K9HVAGHl.jpg differ diff --git a/public/images/docs/scientists/MK3eW3Am.jpg b/public/images/docs/scientists/MK3eW3Am.jpg new file mode 100644 index 00000000..53287dd0 Binary files /dev/null and b/public/images/docs/scientists/MK3eW3Am.jpg differ diff --git a/public/images/docs/scientists/MK3eW3As.jpg b/public/images/docs/scientists/MK3eW3As.jpg new file mode 100644 index 00000000..43244d0c Binary files /dev/null and b/public/images/docs/scientists/MK3eW3As.jpg differ diff --git a/public/images/docs/scientists/Mx7dA2Y.jpg b/public/images/docs/scientists/Mx7dA2Y.jpg new file mode 100644 index 00000000..ee41fbba Binary files /dev/null and b/public/images/docs/scientists/Mx7dA2Y.jpg differ diff --git a/public/images/docs/scientists/OKS67lhb.jpg b/public/images/docs/scientists/OKS67lhb.jpg new file mode 100644 index 00000000..71d2917a Binary files /dev/null and b/public/images/docs/scientists/OKS67lhb.jpg differ diff --git a/public/images/docs/scientists/OKS67lhm.jpg b/public/images/docs/scientists/OKS67lhm.jpg new file mode 100644 index 00000000..9fe8f630 Binary files /dev/null and b/public/images/docs/scientists/OKS67lhm.jpg differ diff --git a/public/images/docs/scientists/OKS67lhs.jpg b/public/images/docs/scientists/OKS67lhs.jpg new file mode 100644 index 00000000..fb3cf212 Binary files /dev/null and b/public/images/docs/scientists/OKS67lhs.jpg differ diff --git a/public/images/docs/scientists/QIrZWGIs.jpg b/public/images/docs/scientists/QIrZWGIs.jpg new file mode 100644 index 00000000..2bfa8ab8 Binary files /dev/null and b/public/images/docs/scientists/QIrZWGIs.jpg differ diff --git a/public/images/docs/scientists/QwUKKmF.jpg b/public/images/docs/scientists/QwUKKmF.jpg new file mode 100644 index 00000000..05aa061e Binary files /dev/null and b/public/images/docs/scientists/QwUKKmF.jpg differ diff --git a/public/images/docs/scientists/RCwLEoQm.jpg b/public/images/docs/scientists/RCwLEoQm.jpg new file mode 100644 index 00000000..4d7d0b6d Binary files /dev/null and b/public/images/docs/scientists/RCwLEoQm.jpg differ diff --git a/public/images/docs/scientists/Sd1AgUOm.jpg b/public/images/docs/scientists/Sd1AgUOm.jpg new file mode 100644 index 00000000..b81b83d2 Binary files /dev/null and b/public/images/docs/scientists/Sd1AgUOm.jpg differ diff --git a/public/images/docs/scientists/Y3utgTi.jpg b/public/images/docs/scientists/Y3utgTi.jpg new file mode 100644 index 00000000..8d44e4fe Binary files /dev/null and b/public/images/docs/scientists/Y3utgTi.jpg differ diff --git a/public/images/docs/scientists/YfeOqp2b.jpg b/public/images/docs/scientists/YfeOqp2b.jpg new file mode 100644 index 00000000..44e0c65c Binary files /dev/null and b/public/images/docs/scientists/YfeOqp2b.jpg differ diff --git a/public/images/docs/scientists/YfeOqp2s.jpg b/public/images/docs/scientists/YfeOqp2s.jpg new file mode 100644 index 00000000..19ef1570 Binary files /dev/null and b/public/images/docs/scientists/YfeOqp2s.jpg differ diff --git a/public/images/docs/scientists/ZF6s192.jpg b/public/images/docs/scientists/ZF6s192.jpg new file mode 100644 index 00000000..f50c7e34 Binary files /dev/null and b/public/images/docs/scientists/ZF6s192.jpg differ diff --git a/public/images/docs/scientists/ZF6s192m.jpg b/public/images/docs/scientists/ZF6s192m.jpg new file mode 100644 index 00000000..056f8d52 Binary files /dev/null and b/public/images/docs/scientists/ZF6s192m.jpg differ diff --git a/public/images/docs/scientists/ZfQOOzfl.jpg b/public/images/docs/scientists/ZfQOOzfl.jpg new file mode 100644 index 00000000..5c9e249b Binary files /dev/null and b/public/images/docs/scientists/ZfQOOzfl.jpg differ diff --git a/public/images/docs/scientists/aTtVpES.jpg b/public/images/docs/scientists/aTtVpES.jpg new file mode 100644 index 00000000..00e09d09 Binary files /dev/null and b/public/images/docs/scientists/aTtVpES.jpg differ diff --git a/public/images/docs/scientists/aeO3rpIl.jpg b/public/images/docs/scientists/aeO3rpIl.jpg new file mode 100644 index 00000000..3b535b07 Binary files /dev/null and b/public/images/docs/scientists/aeO3rpIl.jpg differ diff --git a/public/images/docs/scientists/bE7W1jis.jpg b/public/images/docs/scientists/bE7W1jis.jpg new file mode 100644 index 00000000..a15a897e Binary files /dev/null and b/public/images/docs/scientists/bE7W1jis.jpg differ diff --git a/public/images/docs/scientists/dB2LRbj.jpg b/public/images/docs/scientists/dB2LRbj.jpg new file mode 100644 index 00000000..f2ac0482 Binary files /dev/null and b/public/images/docs/scientists/dB2LRbj.jpg differ diff --git a/public/images/docs/scientists/jA8hHMpm.jpg b/public/images/docs/scientists/jA8hHMpm.jpg new file mode 100644 index 00000000..ba2168f8 Binary files /dev/null and b/public/images/docs/scientists/jA8hHMpm.jpg differ diff --git a/public/images/docs/scientists/kxsph5Cl.jpg b/public/images/docs/scientists/kxsph5Cl.jpg new file mode 100644 index 00000000..f3336072 Binary files /dev/null and b/public/images/docs/scientists/kxsph5Cl.jpg differ diff --git a/public/images/docs/scientists/lICfvbD.jpg b/public/images/docs/scientists/lICfvbD.jpg new file mode 100644 index 00000000..67393f31 Binary files /dev/null and b/public/images/docs/scientists/lICfvbD.jpg differ diff --git a/public/images/docs/scientists/lrWQx8ls.jpg b/public/images/docs/scientists/lrWQx8ls.jpg new file mode 100644 index 00000000..bc0708bd Binary files /dev/null and b/public/images/docs/scientists/lrWQx8ls.jpg differ diff --git a/public/images/docs/scientists/mynHUSas.jpg b/public/images/docs/scientists/mynHUSas.jpg new file mode 100644 index 00000000..e369df8c Binary files /dev/null and b/public/images/docs/scientists/mynHUSas.jpg differ diff --git a/public/images/docs/scientists/okTpbHhm.jpg b/public/images/docs/scientists/okTpbHhm.jpg new file mode 100644 index 00000000..a96c5c03 Binary files /dev/null and b/public/images/docs/scientists/okTpbHhm.jpg differ diff --git a/public/images/docs/scientists/rN7hY6om.jpg b/public/images/docs/scientists/rN7hY6om.jpg new file mode 100644 index 00000000..3c7afe1f Binary files /dev/null and b/public/images/docs/scientists/rN7hY6om.jpg differ diff --git a/public/images/docs/scientists/rTqKo46l.jpg b/public/images/docs/scientists/rTqKo46l.jpg new file mode 100644 index 00000000..4a0b3dc3 Binary files /dev/null and b/public/images/docs/scientists/rTqKo46l.jpg differ diff --git a/public/images/docs/scientists/szV5sdGb.jpg b/public/images/docs/scientists/szV5sdGb.jpg new file mode 100644 index 00000000..8d657940 Binary files /dev/null and b/public/images/docs/scientists/szV5sdGb.jpg differ diff --git a/public/images/docs/scientists/szV5sdGs.jpg b/public/images/docs/scientists/szV5sdGs.jpg new file mode 100644 index 00000000..fc3c3426 Binary files /dev/null and b/public/images/docs/scientists/szV5sdGs.jpg differ diff --git a/public/images/docs/scientists/wIdGuZwm.png b/public/images/docs/scientists/wIdGuZwm.png new file mode 100644 index 00000000..5f482def Binary files /dev/null and b/public/images/docs/scientists/wIdGuZwm.png differ diff --git a/public/images/docs/scientists/yXOvdOSs.jpg b/public/images/docs/scientists/yXOvdOSs.jpg new file mode 100644 index 00000000..0a326951 Binary files /dev/null and b/public/images/docs/scientists/yXOvdOSs.jpg differ diff --git a/public/images/docs/scientists/z08o2TS.jpg b/public/images/docs/scientists/z08o2TS.jpg new file mode 100644 index 00000000..42a0a00e Binary files /dev/null and b/public/images/docs/scientists/z08o2TS.jpg differ diff --git a/src/content/blog/2023/03/16/introducing-react-dev.md b/src/content/blog/2023/03/16/introducing-react-dev.md index 4ce209d7..7328d011 100644 --- a/src/content/blog/2023/03/16/introducing-react-dev.md +++ b/src/content/blog/2023/03/16/introducing-react-dev.md @@ -276,17 +276,17 @@ export default function PackingList() {

Sally Ride's Packing List

@@ -314,17 +314,17 @@ export default function PackingList() {

Sally Ride's Packing List

@@ -362,17 +362,17 @@ export default function PackingList() {

Sally Ride's Packing List

@@ -406,17 +406,17 @@ export default function PackingList() {

Sally Ride's Packing List

diff --git a/src/content/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024.md b/src/content/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024.md new file mode 100644 index 00000000..002dae2a --- /dev/null +++ b/src/content/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024.md @@ -0,0 +1,112 @@ +--- +title: "React Labs: What We've Been Working On – February 2024" +author: Joseph Savona, Ricky Hanlon, Andrew Clark, Matt Carroll, and Dan Abramov +date: 2024/02/15 +description: In React Labs posts, we write about projects in active research and development. We’ve made significant progress since our last update, and we’d like to share our progress. +--- + +February 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). + +--- + + + +In React Labs posts, we write about projects in active research and development. We’ve made significant progress since our [last update](/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023), and we’d like to share our progress. + + + +--- + +## React Compiler {/*react-compiler*/} + +React Compiler is no longer a research project: the compiler now powers instagram.com in production, and we are working to ship the compiler across additional surfaces at Meta and to prepare the first open source release. + +As discussed in our [previous post](/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#react-optimizing-compiler), React can *sometimes* re-render too much when state changes. Since the early days of React our solution for such cases has been manual memoization. In our current APIs, this means applying the [`useMemo`](/reference/react/useMemo), [`useCallback`](/reference/react/useCallback), and [`memo`](/reference/react/memo) APIs to manually tune how much React re-renders on state changes. But manual memoization is a compromise. It clutters up our code, is easy to get wrong, and requires extra work to keep up to date. + +Manual memoization is a reasonable compromise, but we weren’t satisfied. Our vision is for React to *automatically* re-render just the right parts of the UI when state changes, *without compromising on React’s core mental model*. We believe that React’s approach — UI as a simple function of state, with standard JavaScript values and idioms — is a key part of why React has been approachable for so many developers. That’s why we’ve invested in building an optimizing compiler for React. + +JavaScript is a notoriously challenging language to optimize, thanks to its loose rules and dynamic nature. React Compiler is able to compile code safely by modeling both the rules of JavaScript *and* the “rules of React”. For example, React components must be idempotent — returning the same value given the same inputs — and can’t mutate props or state values. These rules limit what developers can do and help to carve out a safe space for the compiler to optimize. + +Of course, we understand that developers sometimes bend the rules a bit, and our goal is to make React Compiler work out of the box on as much code as possible. The compiler attempts to detect when code doesn’t strictly follow React’s rules and will either compile the code where safe or skip compilation if it isn’t safe. We’re testing against Meta’s large and varied codebase in order to help validate this approach. + +For developers who are curious about making sure their code follows React’s rules, we recommend [enabling Strict Mode](/reference/react/StrictMode) and [configuring React’s ESLint plugin](/learn/editor-setup#linting). These tools can help to catch subtle bugs in your React code, improving the quality of your applications today, and future-proofs your applications for upcoming features such as React Compiler. We are also working on consolidated documentation of the rules of React and updates to our ESLint plugin to help teams understand and apply these rules to create more robust apps. + +To see the compiler in action, you can check out our [talk from last fall](https://www.youtube.com/watch?v=qOQClO3g8-Y). At the time of the talk, we had early experimental data from trying React Compiler on one page of instagram.com. Since then, we shipped the compiler to production across instagram.com. We’ve also expanded our team to accelerate the rollout to additional surfaces at Meta and to open source. We’re excited about the path ahead and will have more to share in the coming months. + +## Actions {/*actions*/} + + +We [previously shared](/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#react-server-components) that we were exploring solutions for sending data from the client to the server with Server Actions, so that you can execute database mutations and implement forms. During development of Server Actions, we extended these APIs to support data handling in client-only applications as well. + +We refer to this broader collection of features as simply "Actions". Actions allow you to pass a function to DOM elements such as [`
`](/reference/react-dom/components/form): + +```js + + + +
+``` + +The `action` function can operate synchronously or asynchronously. You can define them on the client side using standard JavaScript or on the server with the [`'use server'`](/reference/rsc/use-server) directive. When using an action, React will manage the life cycle of the data submission for you, providing hooks like [`useFormStatus`](/reference/react-dom/hooks/useFormStatus), and [`useActionState`](/reference/react/useActionState) to access the current state and response of the form action. + +By default, Actions are submitted within a [transition](/reference/react/useTransition), keeping the current page interactive while the action is processing. Since Actions support async functions, we've also added the ability to use `async/await` in transitions. This allows you to show pending UI with the `isPending` state of a transition when an async request like `fetch` starts, and show the pending UI all the way through the update being applied. + +Alongside Actions, we're introducing a feature named [`useOptimistic`](/reference/react/useOptimistic) for managing optimistic state updates. With this hook, you can apply temporary updates that are automatically reverted once the final state commits. For Actions, this allows you to optimistically set the final state of the data on the client, assuming the submission is successful, and revert to the value for data received from the server. It works using regular `async`/`await`, so it works the same whether you're using `fetch` on the client, or a Server Action from the server. + +Library authors can implement custom `action={fn}` props in their own components with `useTransition`. Our intent is for libraries to adopt the Actions pattern when designing their component APIs, to provide a consistent experience for React developers. For example, if your library provides a `` component, consider also exposing a `` API, too. + +While we initially focused on Server Actions for client-server data transfer, our philosophy for React is to provide the same programming model across all platforms and environments. When possible, if we introduce a feature on the client, we aim to make it also work on the server, and vice versa. This philosophy allows us to create a single set of APIs that work no matter where your app runs, making it easier to upgrade to different environments later. + +Actions are now available in the Canary channel and will ship in the next release of React. + +## New Features in React Canary {/*new-features-in-react-canary*/} + +We introduced [React Canaries](/blog/2023/05/03/react-canaries) as an option to adopt individual new stable features as soon as their design is close to final, before they’re released in a stable semver version. + +Canaries are a change to the way we develop React. Previously, features would be researched and built privately inside of Meta, so users would only see the final polished product when released to Stable. With Canaries, we’re building in public with the help of the community to finalize features we share in the React Labs blog series. This means you hear about new features sooner, as they’re being finalized instead of after they’re complete. + +React Server Components, Asset Loading, Document Metadata, and Actions have all landed in the React Canary, and we've added docs for these features on react.dev: + +- **Directives**: [`"use client"`](/reference/rsc/use-client) and [`"use server"`](/reference/rsc/use-server) are bundler features designed for full-stack React frameworks. They mark the "split points" between the two environments: `"use client"` instructs the bundler to generate a ` +``` + +### Libraries depending on React internals may block upgrades {/*libraries-depending-on-react-internals-may-block-upgrades*/} + +This release includes changes to React internals that may impact libraries that ignore our pleas to not use internals like `SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED`. These changes are necessary to land improvements in React 19, and will not break libraries that follow our guidelines. + +Based on our [Versioning Policy](https://react.dev/community/versioning-policy#what-counts-as-a-breaking-change), these updates are not listed as breaking changes, and we are not including docs for how to upgrade them. The recommendation is to remove any code that depends on internals. + +To reflect the impact of using internals, we have renamed the `SECRET_INTERNALS` suffix to: + +`_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE` + +In the future we will more aggressively block accessing internals from React to discourage usage and ensure users are not blocked from upgrading. + +## TypeScript changes {/*typescript-changes*/} + +### Removed deprecated TypeScript types {/*removed-deprecated-typescript-types*/} + +We've cleaned up the TypeScript types based on the removed APIs in React 19. Some of the removed have types been moved to more relevant packages, and others are no longer needed to describe React's behavior. + + +We've published [`types-react-codemod`](https://github.com/eps1lon/types-react-codemod/) to migrate most type related breaking changes: + +```bash +npx types-react-codemod@latest preset-19 ./path-to-app +``` + +If you have a lot of unsound access to `element.props`, you can run this additional codemod: + +```bash +npx types-react-codemod@latest react-element-default-any-props ./path-to-your-react-ts-files +``` + + + +Check out [`types-react-codemod`](https://github.com/eps1lon/types-react-codemod/) for a list of supported replacements. If you feel a codemod is missing, it can be tracked in the [list of missing React 19 codemods](https://github.com/eps1lon/types-react-codemod/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22React+19%22+label%3Aenhancement). + + +### `ref` cleanups required {/*ref-cleanup-required*/} + +_This change is included in the `react-19` codemod preset as [`no-implicit-ref-callback-return +`](https://github.com/eps1lon/types-react-codemod/#no-implicit-ref-callback-return)._ + +Due to the introduction of ref cleanup functions, returning anything else from a ref callback will now be rejected by TypeScript. The fix is usually to stop using implicit returns: + +```diff [[1, 1, "("], [1, 1, ")"], [2, 2, "{", 15], [2, 2, "}", 1]] +-
(instance = current)} /> ++
{instance = current}} /> +``` + +The original code returned the instance of the `HTMLDivElement` and TypeScript wouldn't know if this was supposed to be a cleanup function or not. + +### `useRef` requires an argument {/*useref-requires-argument*/} + +_This change is included in the `react-19` codemod preset as [`refobject-defaults`](https://github.com/eps1lon/types-react-codemod/#refobject-defaults)._ + +A long-time complaint of how TypeScript and React work has been `useRef`. We've changed the types so that `useRef` now requires an argument. This significantly simplifies its type signature. It'll now behave more like `createContext`. + +```ts +// @ts-expect-error: Expected 1 argument but saw none +useRef(); +// Passes +useRef(undefined); +// @ts-expect-error: Expected 1 argument but saw none +createContext(); +// Passes +createContext(undefined); +``` + +This now also means that all refs are mutable. You'll no longer hit the issue where you can't mutate a ref because you initialised it with `null`: + +```ts +const ref = useRef(null); + +// Cannot assign to 'current' because it is a read-only property +ref.current = 1; +``` + +`MutableRef` is now deprecated in favor of a single `RefObject` type which `useRef` will always return: + +```ts +interface RefObject { + current: T +} + +declare function useRef: RefObject +``` + +`useRef` still has a convenience overload for `useRef(null)` that automatically returns `RefObject`. To ease migration due to the required argument for `useRef`, a convenience overload for `useRef(undefined)` was added that automatically returns `RefObject`. + +Check out [[RFC] Make all refs mutable](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/64772) for prior discussions about this change. + +### Changes to the `ReactElement` TypeScript type {/*changes-to-the-reactelement-typescript-type*/} + +_This change is included in the [`react-element-default-any-props`](https://github.com/eps1lon/types-react-codemod#react-element-default-any-props) codemod._ + +The `props` of React elements now default to `unknown` instead of `any` if the element is typed as `ReactElement`. This does not affect you if you pass a type argument to `ReactElement`: + +```ts +type Example2 = ReactElement<{ id: string }>["props"]; +// ^? { id: string } +``` + +But if you relied on the default, you now have to handle `unknown`: + +```ts +type Example = ReactElement["props"]; +// ^? Before, was 'any', now 'unknown' +``` + +You should only need it if you have a lot of legacy code relying on unsound access of element props. Element introspection only exists as an escape hatch, and you should make it explicit that your props access is unsound via an explicit `any`. + +### The JSX namespace in TypeScript {/*the-jsx-namespace-in-typescript*/} +This change is included in the `react-19` codemod preset as [`scoped-jsx`](https://github.com/eps1lon/types-react-codemod#scoped-jsx) + +A long-time request is to remove the global `JSX` namespace from our types in favor of `React.JSX`. This helps prevent pollution of global types which prevents conflicts between different UI libraries that leverage JSX. + +You'll now need to wrap module augmentation of the JSX namespace in `declare module "....": + +```diff +// global.d.ts ++ declare module "react" { + namespace JSX { + interface IntrinsicElements { + "my-element": { + myElementProps: string; + }; + } + } ++ } +``` + +The exact module specifier depends on the JSX runtime you specified in the `compilerOptions` of your `tsconfig.json`: + +- For `"jsx": "react-jsx"` it would be `react/jsx-runtime`. +- For `"jsx": "react-jsxdev"` it would be `react/jsx-dev-runtime`. +- For `"jsx": "react"` and `"jsx": "preserve"` it would be `react`. + +### Better `useReducer` typings {/*better-usereducer-typings*/} + +`useReducer` now has improved type inference thanks to [@mfp22](https://github.com/mfp22). + +However, this required a breaking change where `useReducer` doesn't accept the full reducer type as a type parameter but instead either needs none (and rely on contextual typing) or needs both the state and action type. + +The new best practice is _not_ to pass type arguments to `useReducer`. +```diff +- useReducer>(reducer) ++ useReducer(reducer) +``` +This may not work in edge cases where you can explicitly type the state and action, by passing in the `Action` in a tuple: +```diff +- useReducer>(reducer) ++ useReducer(reducer) +``` +If you define the reducer inline, we encourage to annotate the function parameters instead: +```diff +- useReducer>((state, action) => state) ++ useReducer((state: State, action: Action) => state) +``` +This is also what you'd also have to do if you move the reducer outside of the `useReducer` call: + +```ts +const reducer = (state: State, action: Action) => state; +``` + +## Changelog {/*changelog*/} + +### Other breaking changes {/*other-breaking-changes*/} + +- **react-dom**: Error for javascript URLs in `src` and `href` [#26507](https://github.com/facebook/react/pull/26507) +- **react-dom**: Remove `errorInfo.digest` from `onRecoverableError` [#28222](https://github.com/facebook/react/pull/28222) +- **react-dom**: Remove `unstable_flushControlled` [#26397](https://github.com/facebook/react/pull/26397) +- **react-dom**: Remove `unstable_createEventHandle` [#28271](https://github.com/facebook/react/pull/28271) +- **react-dom**: Remove `unstable_renderSubtreeIntoContainer` [#28271](https://github.com/facebook/react/pull/28271) +- **react-dom**: Remove `unstable_runWithPriority` [#28271](https://github.com/facebook/react/pull/28271) +- **react-is**: Remove deprecated methods from `react-is` [28224](https://github.com/facebook/react/pull/28224) + +### Other notable changes {/*other-notable-changes*/} + +- **react**: Batch sync, default and continuous lanes [#25700](https://github.com/facebook/react/pull/25700) +- **react**: Don't prerender siblings of suspended component [#26380](https://github.com/facebook/react/pull/26380) +- **react**: Detect infinite update loops caused by render phase updates [#26625](https://github.com/facebook/react/pull/26625) +- **react-dom**: Transitions in popstate are now synchronous [#26025](https://github.com/facebook/react/pull/26025) +- **react-dom**: Remove layout effect warning during SSR [#26395](https://github.com/facebook/react/pull/26395) +- **react-dom**: Warn and don’t set empty string for src/href (except anchor tags) [#28124](https://github.com/facebook/react/pull/28124) + +For a full list of changes, please see the [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md#1900-december-5-2024). + +--- + +Thanks to [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), and [Sebastian Silbermann](https://twitter.com/sebsilbermann) for reviewing and editing this post. diff --git a/src/content/blog/2024/12/05/react-19.md b/src/content/blog/2024/12/05/react-19.md new file mode 100644 index 00000000..74964645 --- /dev/null +++ b/src/content/blog/2024/12/05/react-19.md @@ -0,0 +1,810 @@ +--- +title: "React v19" +author: The React Team +date: 2024/12/05 +description: React 19 is now available on npm! In this post, we'll give an overview of the new features in React 19, and how you can adopt them. +--- + +December 05, 2024 by [The React Team](/community/team) + +--- + + +### React 19 is now stable! {/*react-19-is-now-stable*/} + +Additions since this post was originally shared with the React 19 RC in April: + +- **Pre-warming for suspended trees**: see [Improvements to Suspense](/blog/2024/04/25/react-19-upgrade-guide#improvements-to-suspense). +- **React DOM static APIs**: see [New React DOM Static APIs](#new-react-dom-static-apis). + +_The date for this post has been updated to reflect the stable release date._ + + + + + +React v19 is now available on npm! + + + +In our [React 19 Upgrade Guide](/blog/2024/04/25/react-19-upgrade-guide), we shared step-by-step instructions for upgrading your app to React 19. In this post, we'll give an overview of the new features in React 19, and how you can adopt them. + +- [What's new in React 19](#whats-new-in-react-19) +- [Improvements in React 19](#improvements-in-react-19) +- [How to upgrade](#how-to-upgrade) + +For a list of breaking changes, see the [Upgrade Guide](/blog/2024/04/25/react-19-upgrade-guide). + +--- + +## What's new in React 19 {/*whats-new-in-react-19*/} + +### Actions {/*actions*/} + +A common use case in React apps is to perform a data mutation and then update state in response. For example, when a user submits a form to change their name, you will make an API request, and then handle the response. In the past, you would need to handle pending states, errors, optimistic updates, and sequential requests manually. + +For example, you could handle the pending and error state in `useState`: + +```js +// Before Actions +function UpdateName({}) { + const [name, setName] = useState(""); + const [error, setError] = useState(null); + const [isPending, setIsPending] = useState(false); + + const handleSubmit = async () => { + setIsPending(true); + const error = await updateName(name); + setIsPending(false); + if (error) { + setError(error); + return; + } + redirect("/path"); + }; + + return ( +
+ setName(event.target.value)} /> + + {error &&

{error}

} +
+ ); +} +``` + +In React 19, we're adding support for using async functions in transitions to handle pending states, errors, forms, and optimistic updates automatically. + +For example, you can use `useTransition` to handle the pending state for you: + +```js +// Using pending state from Actions +function UpdateName({}) { + const [name, setName] = useState(""); + const [error, setError] = useState(null); + const [isPending, startTransition] = useTransition(); + + const handleSubmit = () => { + startTransition(async () => { + const error = await updateName(name); + if (error) { + setError(error); + return; + } + redirect("/path"); + }) + }; + + return ( +
+ setName(event.target.value)} /> + + {error &&

{error}

} +
+ ); +} +``` + +The async transition will immediately set the `isPending` state to true, make the async request(s), and switch `isPending` to false after any transitions. This allows you to keep the current UI responsive and interactive while the data is changing. + + + +#### By convention, functions that use async transitions are called "Actions". {/*by-convention-functions-that-use-async-transitions-are-called-actions*/} + +Actions automatically manage submitting data for you: + +- **Pending state**: Actions provide a pending state that starts at the beginning of a request and automatically resets when the final state update is committed. +- **Optimistic updates**: Actions support the new [`useOptimistic`](#new-hook-optimistic-updates) hook so you can show users instant feedback while the requests are submitting. +- **Error handling**: Actions provide error handling so you can display Error Boundaries when a request fails, and revert optimistic updates to their original value automatically. +- **Forms**: `
` elements now support passing functions to the `action` and `formAction` props. Passing functions to the `action` props use Actions by default and reset the form automatically after submission. + + + +Building on top of Actions, React 19 introduces [`useOptimistic`](#new-hook-optimistic-updates) to manage optimistic updates, and a new hook [`React.useActionState`](#new-hook-useactionstate) to handle common cases for Actions. In `react-dom` we're adding [`` Actions](#form-actions) to manage forms automatically and [`useFormStatus`](#new-hook-useformstatus) to support the common cases for Actions in forms. + +In React 19, the above example can be simplified to: + +```js +// Using Actions and useActionState +function ChangeName({ name, setName }) { + const [error, submitAction, isPending] = useActionState( + async (previousState, formData) => { + const error = await updateName(formData.get("name")); + if (error) { + return error; + } + redirect("/path"); + return null; + }, + null, + ); + + return ( + + + + {error &&

{error}

} +
+ ); +} +``` + +In the next section, we'll break down each of the new Action features in React 19. + +### New hook: `useActionState` {/*new-hook-useactionstate*/} + +To make the common cases easier for Actions, we've added a new hook called `useActionState`: + +```js +const [error, submitAction, isPending] = useActionState( + async (previousState, newName) => { + const error = await updateName(newName); + if (error) { + // You can return any result of the action. + // Here, we return only the error. + return error; + } + + // handle success + return null; + }, + null, +); +``` + +`useActionState` accepts a function (the "Action"), and returns a wrapped Action to call. This works because Actions compose. When the wrapped Action is called, `useActionState` will return the last result of the Action as `data`, and the pending state of the Action as `pending`. + + + +`React.useActionState` was previously called `ReactDOM.useFormState` in the Canary releases, but we've renamed it and deprecated `useFormState`. + +See [#28491](https://github.com/facebook/react/pull/28491) for more info. + + + +For more information, see the docs for [`useActionState`](/reference/react/useActionState). + +### React DOM: `
` Actions {/*form-actions*/} + +Actions are also integrated with React 19's new `` features for `react-dom`. We've added support for passing functions as the `action` and `formAction` props of ``, ``, and `
}> + + + ) +} +``` + + + +#### `use` does not support promises created in render. {/*use-does-not-support-promises-created-in-render*/} + +If you try to pass a promise created in render to `use`, React will warn: + + + + + +A 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. + + + + + +To fix, you need to pass a promise from a Suspense powered library or framework that supports caching for promises. In the future we plan to ship features to make it easier to cache promises in render. + + + +You can also read context with `use`, allowing you to read Context conditionally such as after early returns: + +```js {1,11} +import {use} from 'react'; +import ThemeContext from './ThemeContext' + +function Heading({children}) { + if (children == null) { + return null; + } + + // This would not work with useContext + // because of the early return. + const theme = use(ThemeContext); + return ( +

+ {children} +

+ ); +} +``` + +The `use` API can only be called in render, similar to hooks. Unlike hooks, `use` can be called conditionally. In the future we plan to support more ways to consume resources in render with `use`. + +For more information, see the docs for [`use`](/reference/react/use). + +## New React DOM Static APIs {/*new-react-dom-static-apis*/} + +We've added two new APIs to `react-dom/static` for static site generation: +- [`prerender`](/reference/react-dom/static/prerender) +- [`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream) + +These new APIs improve on `renderToString` by waiting for data to load for static HTML generation. They are designed to work with streaming environments like Node.js Streams and Web Streams. For example, in a Web Stream environment, you can prerender a React tree to static HTML with `prerender`: + +```js +import { prerender } from 'react-dom/static'; + +async function handler(request) { + const {prelude} = await prerender(, { + bootstrapScripts: ['/main.js'] + }); + return new Response(prelude, { + headers: { 'content-type': 'text/html' }, + }); +} +``` + +Prerender APIs will wait for all data to load before returning the static HTML stream. Streams can be converted to strings, or sent with a streaming response. They do not support streaming content as it loads, which is supported by the existing [React DOM server rendering APIs](/reference/react-dom/server). + +For more information, see [React DOM Static APIs](/reference/react-dom/static). + +## React Server Components {/*react-server-components*/} + +### Server Components {/*server-components*/} + +Server Components are a new option that allows rendering components ahead of time, before bundling, in an environment separate from your client application or SSR server. This separate environment is the "server" in React Server Components. Server Components can run once at build time on your CI server, or they can be run for each request using a web server. + +React 19 includes all of the React Server Components features included from the Canary channel. This means libraries that ship with Server Components can now target React 19 as a peer dependency with a `react-server` [export condition](https://github.com/reactjs/rfcs/blob/main/text/0227-server-module-conventions.md#react-server-conditional-exports) for use in frameworks that support the [Full-stack React Architecture](/learn/creating-a-react-app#which-features-make-up-the-react-teams-full-stack-architecture-vision). + + + + +#### How do I build support for Server Components? {/*how-do-i-build-support-for-server-components*/} + +While React Server Components in React 19 are stable and will not break between minor versions, the underlying APIs used to implement a React Server Components bundler or framework do not follow semver and may break between minors in React 19.x. + +To support React Server Components as a bundler or framework, we recommend pinning to a specific React version, or using the Canary release. We will continue working with bundlers and frameworks to stabilize the APIs used to implement React Server Components in the future. + + + + +For more, see the docs for [React Server Components](/reference/rsc/server-components). + +### Server Actions {/*server-actions*/} + +Server Actions allow Client Components to call async functions executed on the server. + +When a Server Action is defined with the `"use server"` directive, your framework will automatically create a reference to the server function, and pass that reference to the Client Component. When that function is called on the client, React will send a request to the server to execute the function, and return the result. + + + +#### There is no directive for Server Components. {/*there-is-no-directive-for-server-components*/} + +A common misunderstanding is that Server Components are denoted by `"use server"`, but there is no directive for Server Components. The `"use server"` directive is used for Server Actions. + +For more info, see the docs for [Directives](/reference/rsc/directives). + + + +Server Actions can be created in Server Components and passed as props to Client Components, or they can be imported and used in Client Components. + +For more, see the docs for [React Server Actions](/reference/rsc/server-actions). + +## Improvements in React 19 {/*improvements-in-react-19*/} + +### `ref` as a prop {/*ref-as-a-prop*/} + +Starting in React 19, you can now access `ref` as a prop for function components: + +```js [[1, 1, "ref"], [1, 2, "ref", 45], [1, 6, "ref", 14]] +function MyInput({placeholder, ref}) { + return +} + +//... + +``` + +New function components will no longer need `forwardRef`, and we will be publishing a codemod to automatically update your components to use the new `ref` prop. In future versions we will deprecate and remove `forwardRef`. + + + +`ref`s passed to classes are not passed as props since they reference the component instance. + + + +### Diffs for hydration errors {/*diffs-for-hydration-errors*/} + +We also improved error reporting for hydration errors in `react-dom`. For example, instead of logging multiple errors in DEV without any information about the mismatch: + + + + + +Warning: Text content did not match. Server: "Server" Client: "Client" +{' '}at span +{' '}at App + + + + + +Warning: An error occurred during hydration. The server HTML was replaced with client content in \. + + + + + +Warning: Text content did not match. Server: "Server" Client: "Client" +{' '}at span +{' '}at App + + + + + +Warning: An error occurred during hydration. The server HTML was replaced with client content in \. + + + + + +Uncaught Error: Text content does not match server-rendered HTML. +{' '}at checkForUnmatchedText +{' '}... + + + + + +We now log a single message with a diff of the mismatch: + + + + + + +Uncaught 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'} +\- A server/client branch `if (typeof window !== 'undefined')`. +\- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called. +\- Date formatting in a user's locale which doesn't match the server. +\- External changing data without sending a snapshot of it along with the HTML. +\- Invalid HTML tag nesting.{'\n'} +It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.{'\n'} +https://react.dev/link/hydration-mismatch {'\n'} +{' '}\ +{' '}\ +{'+ '}Client +{'- '}Server{'\n'} +{' '}at throwOnHydrationMismatch +{' '}... + + + + + +### `` as a provider {/*context-as-a-provider*/} + +In React 19, you can render `` as a provider instead of ``: + + +```js {5,7} +const ThemeContext = createContext(''); + +function App({children}) { + return ( + + {children} + + ); +} +``` + +New Context providers can use `` and we will be publishing a codemod to convert existing providers. In future versions we will deprecate ``. + +### Cleanup functions for refs {/*cleanup-functions-for-refs*/} + +We now support returning a cleanup function from `ref` callbacks: + +```js {7-9} + { + // ref created + + // NEW: return a cleanup function to reset + // the ref when element is removed from DOM. + return () => { + // ref cleanup + }; + }} +/> +``` + +When the component unmounts, React will call the cleanup function returned from the `ref` callback. This works for DOM refs, refs to class components, and `useImperativeHandle`. + + + +Previously, React would call `ref` functions with `null` when unmounting the component. If your `ref` returns a cleanup function, React will now skip this step. + +In future versions, we will deprecate calling refs with `null` when unmounting components. + + + +Due to the introduction of ref cleanup functions, returning anything else from a `ref` callback will now be rejected by TypeScript. The fix is usually to stop using implicit returns, for example: + +```diff [[1, 1, "("], [1, 1, ")"], [2, 2, "{", 15], [2, 2, "}", 1]] +-
(instance = current)} /> ++
{instance = current}} /> +``` + +The original code returned the instance of the `HTMLDivElement` and TypeScript wouldn't know if this was _supposed_ to be a cleanup function or if you didn't want to return a cleanup function. + +You can codemod this pattern with [`no-implicit-ref-callback-return`](https://github.com/eps1lon/types-react-codemod/#no-implicit-ref-callback-return). + +### `useDeferredValue` initial value {/*use-deferred-value-initial-value*/} + +We've added an `initialValue` option to `useDeferredValue`: + +```js [[1, 1, "deferredValue"], [1, 4, "deferredValue"], [2, 4, "''"]] +function Search({deferredValue}) { + // On initial render the value is ''. + // Then a re-render is scheduled with the deferredValue. + const value = useDeferredValue(deferredValue, ''); + + return ( + + ); +} +```` + +When initialValue is provided, `useDeferredValue` will return it as `value` for the initial render of the component, and schedules a re-render in the background with the deferredValue returned. + +For more, see [`useDeferredValue`](/reference/react/useDeferredValue). + +### Support for Document Metadata {/*support-for-metadata-tags*/} + +In HTML, document metadata tags like ``, `<link>`, and `<meta>` are reserved for placement in the `<head>` section of the document. In React, the component that decides what metadata is appropriate for the app may be very far from the place where you render the `<head>` or React does not render the `<head>` at all. In the past, these elements would need to be inserted manually in an effect, or by libraries like [`react-helmet`](https://github.com/nfl/react-helmet), and required careful handling when server rendering a React application. + +In React 19, we're adding support for rendering document metadata tags in components natively: + +```js {5-8} +function BlogPost({post}) { + return ( + <article> + <h1>{post.title}</h1> + <title>{post.title} + + + +

+ Eee equals em-see-squared... +

+ + ); +} +``` + +When React renders this component, it will see the `` `<link>` and `<meta>` tags, and automatically hoist them to the `<head>` section of document. By supporting these metadata tags natively, we're able to ensure they work with client-only apps, streaming SSR, and Server Components. + +<Note> + +#### You may still want a Metadata library {/*you-may-still-want-a-metadata-library*/} + +For simple use cases, rendering Document Metadata as tags may be suitable, but libraries can offer more powerful features like overriding generic metadata with specific metadata based on the current route. These features make it easier for frameworks and libraries like [`react-helmet`](https://github.com/nfl/react-helmet) to support metadata tags, rather than replace them. + +</Note> + +For more info, see the docs for [`<title>`](/reference/react-dom/components/title), [`<link>`](/reference/react-dom/components/link), and [`<meta>`](/reference/react-dom/components/meta). + +### Support for stylesheets {/*support-for-stylesheets*/} + +Stylesheets, both externally linked (`<link rel="stylesheet" href="...">`) and inline (`<style>...</style>`), require careful positioning in the DOM due to style precedence rules. Building a stylesheet capability that allows for composability within components is hard, so users often end up either loading all of their styles far from the components that may depend on them, or they use a style library which encapsulates this complexity. + +In React 19, we're addressing this complexity and providing even deeper integration into Concurrent Rendering on the Client and Streaming Rendering on the Server with built in support for stylesheets. If you tell React the `precedence` of your stylesheet it will manage the insertion order of the stylesheet in the DOM and ensure that the stylesheet (if external) is loaded before revealing content that depends on those style rules. + +```js {4,5,17} +function ComponentOne() { + return ( + <Suspense fallback="loading..."> + <link rel="stylesheet" href="foo" precedence="default" /> + <link rel="stylesheet" href="bar" precedence="high" /> + <article class="foo-class bar-class"> + {...} + </article> + </Suspense> + ) +} + +function ComponentTwo() { + return ( + <div> + <p>{...}</p> + <link rel="stylesheet" href="baz" precedence="default" /> <-- will be inserted between foo & bar + </div> + ) +} +``` + +During Server Side Rendering React will include the stylesheet in the `<head>`, which ensures that the browser will not paint until it has loaded. If the stylesheet is discovered late after we've already started streaming, React will ensure that the stylesheet is inserted into the `<head>` on the client before revealing the content of a Suspense boundary that depends on that stylesheet. + +During Client Side Rendering React will wait for newly rendered stylesheets to load before committing the render. If you render this component from multiple places within your application React will only include the stylesheet once in the document: + +```js {5} +function App() { + return <> + <ComponentOne /> + ... + <ComponentOne /> // won't lead to a duplicate stylesheet link in the DOM + </> +} +``` + +For users accustomed to loading stylesheets manually this is an opportunity to locate those stylesheets alongside the components that depend on them allowing for better local reasoning and an easier time ensuring you only load the stylesheets that you actually depend on. + +Style libraries and style integrations with bundlers can also adopt this new capability so even if you don't directly render your own stylesheets, you can still benefit as your tools are upgraded to use this feature. + +For more details, read the docs for [`<link>`](/reference/react-dom/components/link) and [`<style>`](/reference/react-dom/components/style). + +### Support for async scripts {/*support-for-async-scripts*/} + +In HTML normal scripts (`<script src="...">`) and deferred scripts (`<script defer="" src="...">`) load in document order which makes rendering these kinds of scripts deep within your component tree challenging. Async scripts (`<script async="" src="...">`) however will load in arbitrary order. + +In React 19 we've included better support for async scripts by allowing you to render them anywhere in your component tree, inside the components that actually depend on the script, without having to manage relocating and deduplicating script instances. + +```js {4,15} +function MyComponent() { + return ( + <div> + <script async={true} src="..." /> + Hello World + </div> + ) +} + +function App() { + <html> + <body> + <MyComponent> + ... + <MyComponent> // won't lead to duplicate script in the DOM + </body> + </html> +} +``` + +In all rendering environments, async scripts will be deduplicated so that React will only load and execute the script once even if it is rendered by multiple different components. + +In Server Side Rendering, async scripts will be included in the `<head>` and prioritized behind more critical resources that block paint such as stylesheets, fonts, and image preloads. + +For more details, read the docs for [`<script>`](/reference/react-dom/components/script). + +### Support for preloading resources {/*support-for-preloading-resources*/} + +During initial document load and on client side updates, telling the Browser about resources that it will likely need to load as early as possible can have a dramatic effect on page performance. + +React 19 includes a number of new APIs for loading and preloading Browser resources to make it as easy as possible to build great experiences that aren't held back by inefficient resource loading. + +```js +import { prefetchDNS, preconnect, preload, preinit } from 'react-dom' +function MyComponent() { + preinit('https://.../path/to/some/script.js', {as: 'script' }) // loads and executes this script eagerly + preload('https://.../path/to/font.woff', { as: 'font' }) // preloads this font + preload('https://.../path/to/stylesheet.css', { as: 'style' }) // preloads this stylesheet + prefetchDNS('https://...') // when you may not actually request anything from this host + preconnect('https://...') // when you will request something but aren't sure what +} +``` +```html +<!-- the above would result in the following DOM/HTML --> +<html> + <head> + <!-- links/scripts are prioritized by their utility to early loading, not call order --> + <link rel="prefetch-dns" href="https://..."> + <link rel="preconnect" href="https://..."> + <link rel="preload" as="font" href="https://.../path/to/font.woff"> + <link rel="preload" as="style" href="https://.../path/to/stylesheet.css"> + <script async="" src="https://.../path/to/some/script.js"></script> + </head> + <body> + ... + </body> +</html> +``` + +These APIs can be used to optimize initial page loads by moving discovery of additional resources like fonts out of stylesheet loading. They can also make client updates faster by prefetching a list of resources used by an anticipated navigation and then eagerly preloading those resources on click or even on hover. + +For more details see [Resource Preloading APIs](/reference/react-dom#resource-preloading-apis). + +### Compatibility with third-party scripts and extensions {/*compatibility-with-third-party-scripts-and-extensions*/} + +We've improved hydration to account for third-party scripts and browser extensions. + +When hydrating, if an element that renders on the client doesn't match the element found in the HTML from the server, React will force a client re-render to fix up the content. Previously, if an element was inserted by third-party scripts or browser extensions, it would trigger a mismatch error and client render. + +In React 19, unexpected tags in the `<head>` and `<body>` will be skipped over, avoiding the mismatch errors. If React needs to re-render the entire document due to an unrelated hydration mismatch, it will leave in place stylesheets inserted by third-party scripts and browser extensions. + +### Better error reporting {/*error-handling*/} + +We improved error handling in React 19 to remove duplication and provide options for handling caught and uncaught errors. For example, when there's an error in render caught by an Error Boundary, previously React would throw the error twice (once for the original error, then again after failing to automatically recover), and then call `console.error` with info about where the error occurred. + +This resulted in three errors for every caught error: + +<ConsoleBlockMulti> + +<ConsoleLogLine level="error"> + +Uncaught Error: hit +{' '}at Throws +{' '}at renderWithHooks +{' '}... + +</ConsoleLogLine> + +<ConsoleLogLine level="error"> + +Uncaught Error: hit<span className="ms-2 text-gray-30">{' <--'} Duplicate</span> +{' '}at Throws +{' '}at renderWithHooks +{' '}... + +</ConsoleLogLine> + +<ConsoleLogLine level="error"> + +The above error occurred in the Throws component: +{' '}at Throws +{' '}at ErrorBoundary +{' '}at App{'\n'} +React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary. + +</ConsoleLogLine> + +</ConsoleBlockMulti> + +In React 19, we log a single error with all the error information included: + +<ConsoleBlockMulti> + +<ConsoleLogLine level="error"> + +Error: hit +{' '}at Throws +{' '}at renderWithHooks +{' '}...{'\n'} +The above error occurred in the Throws component: +{' '}at Throws +{' '}at ErrorBoundary +{' '}at App{'\n'} +React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary. +{' '}at ErrorBoundary +{' '}at App + +</ConsoleLogLine> + +</ConsoleBlockMulti> + +Additionally, we've added two new root options to complement `onRecoverableError`: + +- `onCaughtError`: called when React catches an error in an Error Boundary. +- `onUncaughtError`: called when an error is thrown and not caught by an Error Boundary. +- `onRecoverableError`: called when an error is thrown and automatically recovered. + +For more info and examples, see the docs for [`createRoot`](/reference/react-dom/client/createRoot) and [`hydrateRoot`](/reference/react-dom/client/hydrateRoot). + +### Support for Custom Elements {/*support-for-custom-elements*/} + +React 19 adds full support for custom elements and passes all tests on [Custom Elements Everywhere](https://custom-elements-everywhere.com/). + +In past versions, using Custom Elements in React has been difficult because React treated unrecognized props as attributes rather than properties. In React 19, we've added support for properties that works on the client and during SSR with the following strategy: + +- **Server Side Rendering**: props passed to a custom element will render as attributes if their type is a primitive value like `string`, `number`, or the value is `true`. Props with non-primitive types like `object`, `symbol`, `function`, or value `false` will be omitted. +- **Client Side Rendering**: props that match a property on the Custom Element instance will be assigned as properties, otherwise they will be assigned as attributes. + +Thanks to [Joey Arhar](https://github.com/josepharhar) for driving the design and implementation of Custom Element support in React. + + +#### How to upgrade {/*how-to-upgrade*/} +See the [React 19 Upgrade Guide](/blog/2024/04/25/react-19-upgrade-guide) for step-by-step instructions and a full list of breaking and notable changes. + +_Note: this post was originally published 04/25/2024 and has been updated to 12/05/2024 with the stable release._ diff --git a/src/content/blog/2025/02/14/sunsetting-create-react-app.md b/src/content/blog/2025/02/14/sunsetting-create-react-app.md new file mode 100644 index 00000000..45c0090d --- /dev/null +++ b/src/content/blog/2025/02/14/sunsetting-create-react-app.md @@ -0,0 +1,320 @@ +--- +title: "Sunsetting Create React App" +author: Matt Carroll and Ricky Hanlon +date: 2025/02/14 +description: Today, we’re deprecating Create React App for new apps, and encouraging existing apps to migrate to a framework, or to migrate to a build tool like Vite, Parcel, or RSBuild. We’re also providing docs for when a framework isn’t a good fit for your project, you want to build your own framework, or you just want to learn how React works by building a React app from scratch. +--- + +February 14, 2025 by [Matt Carroll](https://twitter.com/mattcarrollcode) and [Ricky Hanlon](https://bsky.app/profile/ricky.fm) + +--- + +<Intro> + +Today, we’re deprecating [Create React App](https://create-react-app.dev/) for new apps, and encouraging existing apps to migrate to a [framework](#how-to-migrate-to-a-framework), or to [migrate to a build tool](#how-to-migrate-to-a-build-tool) like Vite, Parcel, or RSBuild. + +We’re also providing docs for when a framework isn’t a good fit for your project, you want to build your own framework, or you just want to learn how React works by [building a React app from scratch](/learn/build-a-react-app-from-scratch). + +</Intro> + +----- + +When we released Create React App in 2016, there was no clear way to build a new React app. + +To create a React app, you had to install a bunch of tools and wire them up together yourself to support basic features like JSX, linting, and hot reloading. This was very tricky to do correctly, so the [community](https://github.com/react-boilerplate/react-boilerplate) [created](https://github.com/kriasoft/react-starter-kit) [boilerplates](https://github.com/petehunt/react-boilerplate) for [common](https://github.com/gaearon/react-hot-boilerplate) [setups](https://github.com/erikras/react-redux-universal-hot-example). However, boilerplates were difficult to update and fragmentation made it difficult for React to release new features. + +Create React App solved these problems by combining several tools into a single recommended configuration. This allowed apps a simple way to upgrade to new tooling features, and allowed the React team to deploy non-trivial tooling changes (Fast Refresh support, React Hooks lint rules) to the broadest possible audience. + +This model became so popular that there's an entire category of tools working this way today. + +## Deprecating Create React App {/*deprecating-create-react-app*/} + +Although Create React App makes it easy to get started, [there are several limitations](#limitations-of-build-tools) that make it difficult to build high performant production apps. In principle, we could solve these problems by essentially evolving it into a [framework](#why-we-recommend-frameworks). + +However, since Create React App currently has no active maintainers, and there are many existing frameworks that solve these problems already, we’ve decided to deprecate Create React App. + +Starting today, if you install a new app, you will see a deprecation warning: + +<ConsoleBlockMulti> +<ConsoleLogLine level="error"> + +create-react-app is deprecated. +{'\n\n'} +You can find a list of up-to-date React frameworks on react.dev +For more info see: react.dev/link/cra +{'\n\n'} +This error message will only be shown once per install. + +</ConsoleLogLine> +</ConsoleBlockMulti> + +We've also added a deprecation notice to the Create React App [website](https://create-react-app.dev/) and GitHub [repo](https://github.com/facebook/create-react-app). Create React App will continue working in maintenance mode, and we've published a new version of Create React App to work with React 19. + +## How to Migrate to a Framework {/*how-to-migrate-to-a-framework*/} +We recommend [creating new React apps](/learn/creating-a-react-app) with a framework. All the frameworks we recommend support client-side rendering ([CSR](https://developer.mozilla.org/en-US/docs/Glossary/CSR)) and single-page apps ([SPA](https://developer.mozilla.org/en-US/docs/Glossary/SPA)), and can be deployed to a CDN or static hosting service without a server. + +For existing apps, these guides will help you migrate to a client-only SPA: + +* [Next.js’ Create React App migration guide](https://nextjs.org/docs/app/building-your-application/upgrading/from-create-react-app) +* [React Router’s framework adoption guide](https://reactrouter.com/upgrading/component-routes). +* [Expo webpack to Expo Router migration guide](https://docs.expo.dev/router/migrate/from-expo-webpack/) + +## How to Migrate to a Build Tool {/*how-to-migrate-to-a-build-tool*/} + +If your app has unusual constraints, or you prefer to solve these problems by building your own framework, or you just want to learn how react works from scratch, you can roll your own custom setup with React using Vite, Parcel or Rsbuild. + +For existing apps, these guides will help you migrate to a build tool: + +* [Vite Create React App migration guide](https://www.robinwieruch.de/vite-create-react-app/) +* [Parcel Create React App migration guide](https://parceljs.org/migration/cra/) +* [Rsbuild Create React App migration guide](https://rsbuild.dev/guide/migration/cra) + +To help get started with Vite, Parcel or Rsbuild, we've added new docs for [Building a React App from Scratch](/learn/build-a-react-app-from-scratch). + +<DeepDive> + +#### Do I need a framework? {/*do-i-need-a-framework*/} + +Most apps would benefit from a framework, but there are valid cases to build a React app from scratch. A good rule of thumb is if your app needs routing, you would probably benefit from a framework. + +Just like Svelte has Sveltekit, Vue has Nuxt, and Solid has SolidStart, [React recommends using a framework](#why-we-recommend-frameworks) that fully integrates routing into features like data-fetching and code-splitting out of the box. This avoids the pain of needing to write your own complex configurations and essentially build a framework yourself. + +However, you can always [build a React app from scratch](/learn/build-a-react-app-from-scratch) using a build tool like Vite, Parcel, or Rsbuild. + +</DeepDive> + +Continue reading to learn more about the [limitations of build tools](#limitations-of-build-tools) and [why we recommend frameworks](#why-we-recommend-frameworks). + +## Limitations of Build Tools {/*limitations-of-build-tools*/} + +Create React App and build tools like it make it easy to get started building a React app. After running `npx create-react-app my-app`, you get a fully configured React app with a development server, linting, and a production build. + +For example, if you're building an internal admin tool, you can start with a landing page: + +```js +export default function App() { + return ( + <div> + <h1>Welcome to the Admin Tool!</h1> + </div> + ) +} +``` + +This allows you to immediately start coding in React with features like JSX, default linting rules, and a bundler to run in both development and production. However, this setup is missing the tools you need to build a real production app. + +Most production apps need solutions to problems like routing, data fetching, and code splitting. + +### Routing {/*routing*/} + +Create React App does not include a specific routing solution. If you're just getting started, one option is to use `useState` to switch between routes. But doing this means that you can't share links to your app - every link would go to the same page - and structuring your app becomes difficult over time: + +```js +import {useState} from 'react'; + +import Home from './Home'; +import Dashboard from './Dashboard'; + +export default function App() { + // ❌ Routing in state does not create URLs + const [route, setRoute] = useState('home'); + return ( + <div> + {route === 'home' && <Home />} + {route === 'dashboard' && <Dashboard />} + </div> + ) +} +``` + +This is why most apps that use Create React App solve add routing with a routing library like [React Router](https://reactrouter.com/) or [Tanstack Router](https://tanstack.com/router/latest). With a routing library, you can add additional routes to the app, which provides opinions on the structure of your app, and allows you to start sharing links to routes. For example, with React Router you can define routes: + +```js +import {RouterProvider, createBrowserRouter} from 'react-router'; + +import Home from './Home'; +import Dashboard from './Dashboard'; + +// ✅ Each route has it's own URL +const router = createBrowserRouter([ + {path: '/', element: <Home />}, + {path: '/dashboard', element: <Dashboard />} +]); + +export default function App() { + return ( + <RouterProvider value={router} /> + ) +} +``` + +With this change, you can share a link to `/dashboard` and the app will navigate to the dashboard page . Once you have a routing library, you can add additional features like nested routes, route guards, and route transitions, which are difficult to implement without a routing library. + +There's a tradeoff being made here: the routing library adds complexity to the app, but it also adds features that are difficult to implement without it. + +### Data Fetching {/*data-fetching*/} + +Another common problem in Create React App is data fetching. Create React App does not include a specific data fetching solution. If you're just getting started, a common option is to use `fetch` in an effect to load data. + +But doing this means that the data is fetched after the component renders, which can cause network waterfalls. Network waterfalls are caused by fetching data when your app renders instead of in parallel while the code is downloading: + +```js +export default function Dashboard() { + const [data, setData] = useState(null); + + // ❌ Fetching data in a component causes network waterfalls + useEffect(() => { + fetch('/api/data') + .then(response => response.json()) + .then(data => setData(data)); + }, []); + + return ( + <div> + {data.map(item => <div key={item.id}>{item.name}</div>)} + </div> + ) +} +``` + +Fetching in an effect means the user has to wait longer to see the content, even though the data could have been fetched earlier. To solve this, you can use a data fetching library like [TanStack Query](https://tanstack.com/query/), [SWR](https://swr.vercel.app/), [Apollo](https://www.apollographql.com/docs/react), or [Relay](https://relay.dev/) which provide options to prefetch data so the request is started before the component renders. + +These libraries work best when integrated with your routing "loader" pattern to specify data dependencies at the route level, which allows the router to optimize your data fetches: + +```js +export async function loader() { + const response = await fetch(`/api/data`); + const data = await response.json(); + return data; +} + +// ✅ Fetching data in parallel while the code is downloading +export default function Dashboard({loaderData}) { + return ( + <div> + {loaderData.map(item => <div key={item.id}>{item.name}</div>)} + </div> + ) +} +``` + +On initial load, the router can fetch the data immediately before the route is rendered. As the user navigates around the app, the router is able to fetch both the data and the route at the same time, parallelizing the fetches. This reduces the time it takes to see the content on the screen, and can improve the user experience. + +However, this requires correctly configuring the loaders in your app and trades off complexity for performance. + +### Code Splitting {/*code-splitting*/} + +Another common problem in Create React App is [code splitting](https://www.patterns.dev/vanilla/bundle-splitting). Create React App does not include a specific code splitting solution. If you're just getting started, you might not consider code splitting at all. + +This means your app is shipped as a single bundle: + +```txt +- bundle.js 75kb +``` + +But for ideal performance, you should "split" your code into separate bundles so the user only needs to download what they need. This decreases the time the user needs to wait to load your app, by only downloading the code they need to see the page they are on. + +```txt +- core.js 25kb +- home.js 25kb +- dashboard.js 25kb +``` + +One way to do code-splitting is with `React.lazy`. However, this means that the code is not fetched until the component renders, which can cause network waterfalls. A more optimal solution is to use a router feature that fetches the code in parallel while the code is downloading. For example, React Router provides a `lazy` option to specify that a route should be code split and optimize when it is loaded: + +```js +import Home from './Home'; +import Dashboard from './Dashboard'; + +// ✅ Routes are downloaded before rendering +const router = createBrowserRouter([ + {path: '/', lazy: () => import('./Home')}, + {path: '/dashboard', lazy: () => import('Dashboard')} +]); +``` + +Optimized code-splitting is tricky to get right, and it's easy to make mistakes that can cause the user to download more code than they need. It works best when integrated with your router and data loading solutions to maximize caching, parallelize fetches, and support ["import on interaction"](https://www.patterns.dev/vanilla/import-on-interaction) patterns. + +### And more... {/*and-more*/} + +These are just a few examples of the limitations of Create React App. + +Once you've integrated routing, data-fetching, and code splitting, you now also need to consider pending states, navigation interruptions, error messages to the user, and revalidation of the data. There are entire categories of problems that users need to solve like: + +<div style={{display: 'flex', width: '100%', justifyContent: 'space-around'}}> + <ul> + <li>Accessibility</li> + <li>Asset loading</li> + <li>Authentication</li> + <li>Caching</li> + </ul> + <ul> + <li>Error handling</li> + <li>Mutating data</li> + <li>Navigations</li> + <li>Optimistic updates</li> + </ul> + <ul> + <li>Progressive enhancement</li> + <li>Server-side rendering</li> + <li>Static site generation</li> + <li>Streaming</li> + </ul> +</div> + +All of these work together to create the most optimal [loading sequence](https://www.patterns.dev/vanilla/loading-sequence). + +Solving each of these problems individually in Create React App can be difficult as each problem is interconnected with the others and can require deep expertise in problem areas users may not be familiar with. In order to solve these problems, users end up building their own bespoke solutions on top of Create React App, which was the problem Create React App originally tried to solve. + +## Why we Recommend Frameworks {/*why-we-recommend-frameworks*/} + +Although you could solve all these pieces yourself in a build tool like Create React App, Vite, or Parcel, it is hard to do well. Just like when Create React App itself integrated several build tools together, you need a tool to integrate all of these features together to provide the best experience to users. + +This category of tools that integrates build tools, rendering, routing, data fetching, and code splitting are known as "frameworks" -- or if you prefer to call React itself a framework, you might call them "metaframeworks". + +Frameworks impose some opinions about structuring your app in order to provide a much better user experience, in the same way build tools impose some opinions to make tooling easier. This is why we started recommending frameworks like [Next.js](https://nextjs.org/), [React Router](https://reactrouter.com/), and [Expo](https://expo.dev/) for new projects. + +Frameworks provide the same getting started experience as Create React App, but also provide solutions to problems users need to solve anyway in real production apps. + +<DeepDive> + +#### Server rendering is optional {/*server-rendering-is-optional*/} + +The frameworks we recommend all provide the option to create a [client-side rendered (CSR)](https://developer.mozilla.org/en-US/docs/Glossary/CSR) app. + +In some cases, CSR is the right choice for a page, but many times it's not. Even if most of your app is client-side, there are often individual pages that could benefit from server rendering features like [static-site generation (SSG)](https://developer.mozilla.org/en-US/docs/Glossary/SSG) or [server-side rendering (SSR)](https://developer.mozilla.org/en-US/docs/Glossary/SSR), for example a Terms of Service page, or documentation. + +Server rendering generally sends less JavaScript to the client, and a full HTML document which produces a faster [First Contentful Paint (FCP)](https://web.dev/articles/fcp) by reducing [Total Blocking Time (TBD)](https://web.dev/articles/tbt), which can also lower [Interaction to Next Paint (INP)](https://web.dev/articles/inp). This is why the [Chrome team has encouraged](https://web.dev/articles/rendering-on-the-web) developers to consider static or server-side render over a full client-side approach to achieve the best possible performance. + +There are tradeoffs to using a server, and it is not always the best option for every page. Generating pages on the server incurs additional cost and takes time to generate which can increase [Time to First Byte (TTFB)](https://web.dev/articles/ttfb). The best performing apps are able to pick the right rendering strategy on a per-page basis, based on the tradeoffs of each strategy. + +Frameworks provide the option to use a server on any page if you want to, but do not force you to use a server. This allows you to pick the right rendering strategy for each page in your app. + +#### What About Server Components {/*server-components*/} + +The frameworks we recommend also include support for React Server Components. + +Server Components help solve these problems by moving routing and data fetching to the server, and allowing code splitting to be done for client components based on the data you render, instead of just the route rendered, and reducing the amount of JavaScript shipped for the best possible [loading sequence](https://www.patterns.dev/vanilla/loading-sequence). + +Server Components do not require a server. They can be run at build time on your CI server to create a static-site generated app (SSG) app, at runtime on a web server for a server-side rendered (SSR) app. + +See [Introducing zero-bundle size React Server Components](/blog/2020/12/21/data-fetching-with-react-server-components) and [the docs](/reference/rsc/server-components) for more info. + +</DeepDive> + +<Note> + +#### Server Rendering is not just for SEO {/*server-rendering-is-not-just-for-seo*/} + +A common misunderstanding is that server rendering is only for [SEO](https://developer.mozilla.org/en-US/docs/Glossary/SEO). + +While server rendering can improve SEO, it also improves performance by reducing the amount of JavaScript the user needs to download and parse before they can see the content on the screen. + +This is why the Chrome team [has encouraged](https://web.dev/articles/rendering-on-the-web) developers to consider static or server-side render over a full client-side approach to achieve the best possible performance. + +</Note> + +--- + +_Thank you to [Dan Abramov](https://bsky.app/profile/danabra.mov) for creating Create React App, and [Joe Haddad](https://github.com/Timer), [Ian Schmitz](https://github.com/ianschmitz), [Brody McKee](https://github.com/mrmckeb), and [many others](https://github.com/facebook/create-react-app/graphs/contributors) for maintaining Create React App over the years. Thank you to [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), and [Theo Browne](https://x.com/theo) for reviewing and providing feedback on this post._ + diff --git a/src/content/blog/2025/12/11/denial-of-service-and-source-code-exposure-in-react-server-components.md b/src/content/blog/2025/12/11/denial-of-service-and-source-code-exposure-in-react-server-components.md new file mode 100644 index 00000000..70e5c2e6 --- /dev/null +++ b/src/content/blog/2025/12/11/denial-of-service-and-source-code-exposure-in-react-server-components.md @@ -0,0 +1,202 @@ +--- +title: "Denial of Service and Source Code Exposure in React Server Components" +author: The React Team +date: 2025/12/11 +description: Security researchers have found and disclosed two additional vulnerabilities in React Server Components while attempting to exploit the patches in last week’s critical vulnerability. High vulnerability Denial of Service (CVE-2025-55184), and medium vulnerability Source Code Exposure (CVE-2025-55183) + + +--- + +December 11, 2025 by [The React Team](/community/team) + +_Updated January 26, 2026._ + +--- + +<Intro> + +Security researchers have found and disclosed two additional vulnerabilities in React Server Components while attempting to exploit the patches in last week’s critical vulnerability. + +**These new vulnerabilities do not allow for Remote Code Execution.** The patch for React2Shell remains effective at mitigating the Remote Code Execution exploit. + +</Intro> + +--- + +The new vulnerabilities are disclosed as: + +- **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) +- **Source Code Exposure - Medium Severity**: [CVE-2025-55183](https://www.cve.org/CVERecord?id=CVE-2025-55183) (CVSS 5.3) + +We recommend upgrading immediately due to the severity of the newly disclosed vulnerabilities. + +<Note> + +#### The patches published earlier are vulnerable. {/*the-patches-published-earlier-are-vulnerable*/} + +If you already updated for the previous vulnerabilities, you will need to update again. + +If you updated to 19.0.3, 19.1.4, and 19.2.3, [these are incomplete](#additional-fix-published), and you will need to update again. + +Please see [the instructions in the previous post](/blog/2025/12/03/critical-security-vulnerability-in-react-server-components#update-instructions) for upgrade steps. + +----- + +_Updated January 26, 2026._ + +</Note> + +Further details of these vulnerabilities will be provided after the rollout of the fixes are complete. + +## Immediate Action Required {/*immediate-action-required*/} + +These vulnerabilities are present in the same packages and versions as [CVE-2025-55182](/blog/2025/12/03/critical-security-vulnerability-in-react-server-components). + +This includes 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, and 19.2.3 of: + +* [react-server-dom-webpack](https://www.npmjs.com/package/react-server-dom-webpack) +* [react-server-dom-parcel](https://www.npmjs.com/package/react-server-dom-parcel) +* [react-server-dom-turbopack](https://www.npmjs.com/package/react-server-dom-turbopack?activeTab=readme) + +Fixes were backported to versions 19.0.4, 19.1.5, and 19.2.4. If you are using any of the above packages please upgrade to any of the fixed versions immediately. + +As before, if your app’s React code does not use a server, your app is not affected by these vulnerabilities. If your app does not use a framework, bundler, or bundler plugin that supports React Server Components, your app is not affected by these vulnerabilities. + +<Note> + +#### It’s common for critical CVEs to uncover follow‑up vulnerabilities. {/*its-common-for-critical-cves-to-uncover-followup-vulnerabilities*/} + +When a critical vulnerability is disclosed, researchers scrutinize adjacent code paths looking for variant exploit techniques to test whether the initial mitigation can be bypassed. + +This pattern shows up across the industry, not just in JavaScript. For example, after [Log4Shell](https://nvd.nist.gov/vuln/detail/cve-2021-44228), additional CVEs ([1](https://nvd.nist.gov/vuln/detail/cve-2021-45046), [2](https://nvd.nist.gov/vuln/detail/cve-2021-45105)) were reported as the community probed the original fix. + +Additional disclosures can be frustrating, but they are generally a sign of a healthy response cycle. + +</Note> + +### Affected frameworks and bundlers {/*affected-frameworks-and-bundlers*/} + +Some React frameworks and bundlers depended on, had peer dependencies for, or included the vulnerable React packages. The following React frameworks & bundlers are affected: [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), and [rwsdk](https://www.npmjs.com/package/rwsdk). + +Please see [the instructions in the previous post](/blog/2025/12/03/critical-security-vulnerability-in-react-server-components#update-instructions) for upgrade steps. + +### Hosting Provider Mitigations {/*hosting-provider-mitigations*/} + +As before, we have worked with a number of hosting providers to apply temporary mitigations. + +You should not depend on these to secure your app, and still update immediately. + +### React Native {/*react-native*/} + +For React Native users not using a monorepo or `react-dom`, your `react` version should be pinned in your `package.json`, and there are no additional steps needed. + +If you are using React Native in a monorepo, you should update _only_ the impacted packages if they are installed: + +- `react-server-dom-webpack` +- `react-server-dom-parcel` +- `react-server-dom-turbopack` + +This is required to mitigate the security advisories, but you do not need to update `react` and `react-dom` so this will not cause the version mismatch error in React Native. + +See [this issue](https://github.com/facebook/react-native/issues/54772#issuecomment-3617929832) for more information. + +--- + +## High Severity: Multiple Denial of Service {/*high-severity-multiple-denial-of-service*/} + +**CVEs:** [CVE-2026-23864](https://www.cve.org/CVERecord?id=CVE-2026-23864) +**Base Score:** 7.5 (High) +**Date**: January 26, 2026 + +Security researchers discovered additional DoS vulnerabilities still exist in React Server Components. + +The vulnerabilities are triggered by sending specially crafted HTTP requests to Server Function endpoints, and could lead to server crashes, out-of-memory exceptions or excessive CPU usage; depending on the vulnerable code path being exercised, the application configuration and application code. + +The patches published January 26th mitigate these DoS vulnerabilities. + +<Note> + +#### Additional fixes published {/*additional-fix-published*/} + +The original fix addressing the DoS in [CVE-2025-55184](https://www.cve.org/CVERecord?id=CVE-2025-55184) was incomplete. + +This left previous versions vulnerable. Versions 19.0.4, 19.1.5, 19.2.4 are safe. + +----- + +_Updated January 26, 2026._ + +</Note> + +--- + +## High Severity: Denial of Service {/*high-severity-denial-of-service*/} + +**CVEs:** [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) +**Base Score:** 7.5 (High) + +Security researchers have discovered that a malicious HTTP request can be crafted and sent to any Server Functions endpoint that, when deserialized by React, can cause an infinite loop that hangs the server process and consumes CPU. Even if your app does not implement any React Server Function endpoints it may still be vulnerable if your app supports React Server Components. + +This creates a vulnerability vector where an attacker may be able to deny users from accessing the product, and potentially have a performance impact on the server environment. + +The patches published today mitigate by preventing the infinite loop. + +## Medium Severity: Source Code Exposure {/*low-severity-source-code-exposure*/} + +**CVE:** [CVE-2025-55183](https://www.cve.org/CVERecord?id=CVE-2025-55183) +**Base Score**: 5.3 (Medium) + +A security researcher has discovered that a malicious HTTP request sent to a vulnerable Server Function may unsafely return the source code of any Server Function. Exploitation requires the existence of a Server Function which explicitly or implicitly exposes a stringified argument: + +```javascript +'use server'; + +export async function serverFunction(name) { + const conn = db.createConnection('SECRET KEY'); + const user = await conn.createUser(name); // implicitly stringified, leaked in db + + return { + id: user.id, + message: `Hello, ${name}!` // explicitly stringified, leaked in reply + }} +``` + +An attacker may be able to leak the following: + +```txt +0:{"a":"$@1","f":"","b":"Wy43RxUKdxmr5iuBzJ1pN"} +1:{"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}!`}}!"} +``` + +The patches published today prevent stringifying the Server Function source code. + +<Note> + +#### Only secrets in source code may be exposed. {/*only-secrets-in-source-code-may-be-exposed*/} + +Secrets hardcoded in source code may be exposed, but runtime secrets such as `process.env.SECRET` are not affected. + +The scope of the exposed code is limited to the code inside the Server Function, which may include other functions depending on the amount of inlining your bundler provides. + +Always verify against production bundles. + +</Note> + +--- + +## Timeline {/*timeline*/} +* **December 3rd**: Leak reported to Vercel and [Meta Bug Bounty](https://bugbounty.meta.com/) by [Andrew MacPherson](https://github.com/AndrewMohawk). +* **December 4th**: Initial DoS reported to [Meta Bug Bounty](https://bugbounty.meta.com/) by [RyotaK](https://ryotak.net). +* **December 6th**: Both issues confirmed by the React team, and the team began investigating. +* **December 7th**: Initial fixes created and the React team began verifying and planning new patch. +* **December 8th**: Affected hosting providers and open source projects notified. +* **December 10th**: Hosting provider mitigations in place and patches verified. +* **December 11th**: Additional DoS reported to [Meta Bug Bounty](https://bugbounty.meta.com/) by Shinsaku Nomura. +* **December 11th**: Patches published and publicly disclosed as [CVE-2025-55183](https://www.cve.org/CVERecord?id=CVE-2025-55183) and [CVE-2025-55184](https://www.cve.org/CVERecord?id=CVE-2025-55184). +* **December 11th**: Missing DoS case found internally, patched and publicly disclosed as [CVE-2025-67779](https://www.cve.org/CVERecord?id=CVE-2025-67779). +* **January 26th**: Additional DoS cases found, patched, and publicly disclosed as [CVE-2026-23864](https://www.cve.org/CVERecord?id=CVE-2026-23864). +--- + +## Attribution {/*attribution*/} + +Thank you to [Andrew MacPherson (AndrewMohawk)](https://github.com/AndrewMohawk) for reporting the Source Code Exposure, [RyotaK](https://ryotak.net) from GMO Flatt Security Inc and Shinsaku Nomura of Bitforest Co., Ltd. for reporting the Denial of Service vulnerabilities. Thank you to [Mufeed VH](https://x.com/mufeedvh) from [Winfunc Research](https://winfunc.com), [Joachim Viide](https://jviide.iki.fi), [RyotaK](https://ryotak.net) from [GMO Flatt Security Inc](https://flatt.tech/en/) and Xiangwei Zhang of Tencent Security YUNDING LAB for reporting the additional DoS vulnerabilities. diff --git a/src/content/community/conferences.md b/src/content/community/conferences.md index f706c692..cbdd70f7 100644 --- a/src/content/community/conferences.md +++ b/src/content/community/conferences.md @@ -39,6 +39,19 @@ May 15 - 16, 2024. In-person in Henderson, NV, USA + remote [Website](https://conf.react.dev) - [Twitter](https://twitter.com/reactjs) +<<<<<<< HEAD +======= +### React Native Connection 2024 {/*react-native-connection-2024*/} +April 23, 2024. In-person in Paris, France + +[Website](https://reactnativeconnection.io/) - [Twitter](https://twitter.com/ReactNativeConn) + +### React Miami 2024 {/*react-miami-2024*/} +April 19 - 20, 2024. In-person in Miami, FL, USA + +[Website](https://reactmiami.com/) - [Twitter](https://twitter.com/ReactMiamiConf) + +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ### Epic Web Conf 2024 {/*epic-web-2024*/} April 10 - 11, 2024. In-person in Park City, UT, USA @@ -275,7 +288,7 @@ October 20 & 23, 2023. In-person in London, UK + remote first interactivity (hyb ### React Brussels 2023 {/*react-brussels-2023*/} October 13th 2023. In-person in Brussels, Belgium + Remote (hybrid) -[Website](https://www.react.brussels/) - [Twitter](https://twitter.com/BrusselsReact) - [Videos](https://www.youtube.com/playlist?list=PL53Z0yyYnpWh85KeMomUoVz8_brrmh_aC) +[Website](https://www.react.brussels/) - [Twitter](https://twitter.com/BrusselsReact) - [Videos](https://www.youtube.com/playlist?list=PL53Z0yyYnpWh85KeMomUoVz8_brrmh_aC) ### React India 2023 {/*react-india-2023*/} October 5 - 7, 2023. In-person in Goa, India (hybrid event) + Oct 3 2023 - remote day @@ -299,7 +312,7 @@ September 28 - 30, 2023. Alicante, Spain ### RedwoodJS Conference 2023 {/*redwoodjs-conference-2023*/} -September 26 - 29, 2023. Grants Pass, Oregon + remote (hybrid event) +September 26 - 29, 2023. Grants Pass, Oregon + remote (hybrid event) [Website](https://www.redwoodjsconf.com/) - [Twitter](https://twitter.com/redwoodjs) diff --git a/src/content/community/translations.md b/src/content/community/translations.md new file mode 100644 index 00000000..fd49bd3e --- /dev/null +++ b/src/content/community/translations.md @@ -0,0 +1,35 @@ +--- +title: Translations +--- + +<Intro> + +React docs are translated by the global community into many languages all over the world. + +</Intro> + +## Source site {/*main-site*/} + +All translations are provided from the canonical source docs: + +- [English](https://react.dev/) — [Contribute](https://github.com/reactjs/react.dev/) + +## Full translations {/*full-translations*/} + +{/* If you are a language maintainer and want to add your language here, finish the "Core" translations and edit `deployedTranslations` under `src/utils`. */} + +<LanguageList progress="complete" /> + +## In-progress translations {/*in-progress-translations*/} + +For the progress of each translation, see: [Is React Translated Yet?](https://translations.react.dev/) + +<LanguageList progress="in-progress" /> + +## How to contribute {/*how-to-contribute*/} + +You can contribute to the translation efforts! + +The community conducts the translation work for the React docs on each language-specific fork of react.dev. Typical translation work involves directly translating a Markdown file and creating a pull request. Click the "contribute" link above to the GitHub repository for your language, and follow the instructions there to help with the translation effort. + +If you want to start a new translation for your language, visit: [translations.react.dev](https://github.com/reactjs/translations.react.dev) \ No newline at end of file diff --git a/src/content/learn/adding-interactivity.md b/src/content/learn/adding-interactivity.md index 17de698a..3882c142 100644 --- a/src/content/learn/adding-interactivity.md +++ b/src/content/learn/adding-interactivity.md @@ -139,73 +139,73 @@ export const sculptureList = [{ name: 'Homenaje a la Neurocirugía', artist: 'Marta Colvin Andrade', 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.', - url: 'https://i.imgur.com/Mx7dA2Y.jpg', + url: 'https://react.dev/images/docs/scientists/Mx7dA2Y.jpg', alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' }, { name: 'Floralis Genérica', artist: 'Eduardo Catalano', 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.', - url: 'https://i.imgur.com/ZF6s192m.jpg', + url: 'https://react.dev/images/docs/scientists/ZF6s192m.jpg', alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.' }, { name: 'Eternal Presence', artist: 'John Woodrow Wilson', 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."', - url: 'https://i.imgur.com/aTtVpES.jpg', + url: 'https://react.dev/images/docs/scientists/aTtVpES.jpg', alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.' }, { name: 'Moai', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/RCwLEoQm.jpg', + url: 'https://react.dev/images/docs/scientists/RCwLEoQm.jpg', alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.' }, { name: 'Blue Nana', artist: 'Niki de Saint Phalle', 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.', - url: 'https://i.imgur.com/Sd1AgUOm.jpg', + url: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.' }, { name: 'Ultimate Form', artist: 'Barbara Hepworth', 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.', - url: 'https://i.imgur.com/2heNQDcm.jpg', + url: 'https://react.dev/images/docs/scientists/2heNQDcm.jpg', alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.' }, { name: 'Cavaliere', artist: 'Lamidi Olonade Fakeye', description: "Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.", - url: 'https://i.imgur.com/wIdGuZwm.png', + url: 'https://react.dev/images/docs/scientists/wIdGuZwm.png', alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.' }, { name: 'Big Bellies', artist: 'Alina Szapocznikow', 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.", - url: 'https://i.imgur.com/AlHTAdDm.jpg', + url: 'https://react.dev/images/docs/scientists/AlHTAdDm.jpg', alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.' }, { name: 'Terracotta Army', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/HMFmH6m.jpg', + url: 'https://react.dev/images/docs/scientists/HMFmH6m.jpg', alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.' }, { name: 'Lunar Landscape', artist: 'Louise Nevelson', 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.', - url: 'https://i.imgur.com/rN7hY6om.jpg', + url: 'https://react.dev/images/docs/scientists/rN7hY6om.jpg', alt: 'A black matte sculpture where the individual elements are initially indistinguishable.' }, { name: 'Aureole', artist: 'Ranjani Shettar', 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."', - url: 'https://i.imgur.com/okTpbHhm.jpg', + url: 'https://react.dev/images/docs/scientists/okTpbHhm.jpg', alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.' }, { name: 'Hippos', artist: 'Taipei Zoo', description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.', - url: 'https://i.imgur.com/6o5Vuyu.jpg', + url: 'https://react.dev/images/docs/scientists/6o5Vuyu.jpg', alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.' }]; ``` @@ -424,7 +424,7 @@ export default function Form() { artwork: { title: 'Blue Nana', city: 'Hamburg', - image: 'https://i.imgur.com/Sd1AgUOm.jpg', + image: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', } }); @@ -532,7 +532,7 @@ export default function Form() { artwork: { title: 'Blue Nana', city: 'Hamburg', - image: 'https://i.imgur.com/Sd1AgUOm.jpg', + image: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', } }); diff --git a/src/content/learn/build-a-react-app-from-scratch.md b/src/content/learn/build-a-react-app-from-scratch.md new file mode 100644 index 00000000..182ad54b --- /dev/null +++ b/src/content/learn/build-a-react-app-from-scratch.md @@ -0,0 +1,143 @@ +--- +title: Build a React app from Scratch +--- + +<Intro> + +If your app has constraints not well-served by existing frameworks, you prefer to build your own framework, or you just want to learn the basics of a React app, you can build a React app from scratch. + +</Intro> + +<DeepDive> + +#### Consider using a framework {/*consider-using-a-framework*/} + +Starting from scratch is an easy way to get started using React, but a major tradeoff to be aware of is that going this route is often the same as building your own adhoc framework. As your requirements evolve, you may need to solve more framework-like problems that our recommended frameworks already have well developed and supported solutions for. + +For example, if in the future your app needs support for server-side rendering (SSR), static site generation (SSG), and/or React Server Components (RSC), you will have to implement those on your own. Similarly, future React features that require integrating at the framework level will have to be implemented on your own if you want to use them. + +Our recommended frameworks also help you build better performing apps. For example, reducing or eliminating waterfalls from network requests makes for a better user experience. This might not be a high priority when you are building a toy project, but if your app gains users you may want to improve its performance. + +Going this route also makes it more difficult to get support, since the way you develop routing, data-fetching, and other features will be unique to your situation. You should only choose this option if you are comfortable tackling these problems on your own, or if you’re confident that you will never need these features. + +For a list of recommended frameworks, check out [Creating a React App](/learn/creating-a-react-app). + +</DeepDive> + + +## Step 1: Install a build tool {/*step-1-install-a-build-tool*/} + +The first step is to install a build tool like `vite`, `parcel`, or `rsbuild`. These build tools provide features to package and run source code, provide a development server for local development and a build command to deploy your app to a production server. + +### Vite {/*vite*/} + +[Vite](https://vite.dev/) is a build tool that aims to provide a faster and leaner development experience for modern web projects. + +<TerminalBlock> +npm create vite@latest my-app -- --template react-ts +</TerminalBlock> + +Vite is opinionated and comes with sensible defaults out of the box. Vite has a rich ecosystem of plugins to support fast refresh, JSX, Babel/SWC, and other common features. See Vite's [React plugin](https://vite.dev/plugins/#vitejs-plugin-react) or [React SWC plugin](https://vite.dev/plugins/#vitejs-plugin-react-swc) and [React SSR example project](https://vite.dev/guide/ssr.html#example-projects) to get started. + +Vite is already being used as a build tool in one of our [recommended frameworks](/learn/creating-a-react-app): [React Router](https://reactrouter.com/start/framework/installation). + +### Parcel {/*parcel*/} + +[Parcel](https://parceljs.org/) combines a great out-of-the-box development experience with a scalable architecture that can take your project from just getting started to massive production applications. + +<TerminalBlock> +npm install --save-dev parcel +</TerminalBlock> + +Parcel supports fast refresh, JSX, TypeScript, Flow, and styling out of the box. See [Parcel's React recipe](https://parceljs.org/recipes/react/#getting-started) to get started. + +### Rsbuild {/*rsbuild*/} + +[Rsbuild](https://rsbuild.dev/) is an Rspack-powered build tool that provides a seamless development experience for React applications. It comes with carefully tuned defaults and performance optimizations ready to use. + +<TerminalBlock> +npx create-rsbuild --template react +</TerminalBlock> + +Rsbuild includes built-in support for React features like fast refresh, JSX, TypeScript, and styling. See [Rsbuild's React guide](https://rsbuild.dev/guide/framework/react) to get started. + +<Note> + +#### Metro for React Native {/*react-native*/} + +If you're starting from scratch with React Native you'll need to use [Metro](https://metrobundler.dev/), the JavaScript bundler for React Native. Metro supports bundling for platforms like iOS and Android, but lacks many features when compared to the tools here. We recommend starting with Vite, Parcel, or Rsbuild unless your project requires React Native support. + +</Note> + +## Step 2: Build Common Application Patterns {/*step-2-build-common-application-patterns*/} + +The build tools listed above start off with a client-only, single-page app (SPA), but don't include any further solutions for common functionality like routing, data fetching, or styling. + +The React ecosystem includes many tools for these problems. We've listed a few that are widely used as a starting point, but feel free to choose other tools if those work better for you. + +### Routing {/*routing*/} + +Routing determines what content or pages to display when a user visits a particular URL. You need to set up a router to map URLs to different parts of your app. You'll also need to handle nested routes, route parameters, and query parameters. Routers can be configured within your code, or defined based on your component folder and file structures. + +Routers are a core part of modern applications, and are usually integrated with data fetching (including prefetching data for a whole page for faster loading), code splitting (to minimize client bundle sizes), and page rendering approaches (to decide how each page gets generated). + +We suggest using: + +- [React Router](https://reactrouter.com/start/data/custom) +- [Tanstack Router](https://tanstack.com/router/latest) + + +### Data Fetching {/*data-fetching*/} + +Fetching data from a server or other data source is a key part of most applications. Doing this properly requires handling loading states, error states, and caching the fetched data, which can be complex. + +Purpose-built data fetching libraries do the hard work of fetching and caching the data for you, letting you focus on what data your app needs and how to display it. These libraries are typically used directly in your components, but can also be integrated into routing loaders for faster pre-fetching and better performance, and in server rendering as well. + +Note that fetching data directly in components can lead to slower loading times due to network request waterfalls, so we recommend prefetching data in router loaders or on the server as much as possible! This allows a page's data to be fetched all at once as the page is being displayed. + +If you're fetching data from most backends or REST-style APIs, we suggest using: + +- [TanStack Query](https://tanstack.com/query/) +- [SWR](https://swr.vercel.app/) +- [RTK Query](https://redux-toolkit.js.org/rtk-query/overview) + +If you're fetching data from a GraphQL API, we suggest using: + +- [Apollo](https://www.apollographql.com/docs/react) +- [Relay](https://relay.dev/) + + +### Code-splitting {/*code-splitting*/} + +Code-splitting is the process of breaking your app into smaller bundles that can be loaded on demand. An app's code size increases with every new feature and additional dependency. Apps can become slow to load because all of the code for the entire app needs to be sent before it can be used. Caching, reducing features/dependencies, and moving some code to run on the server can help mitigate slow loading but are incomplete solutions that can sacrifice functionality if overused. + +Similarly, if you rely on the apps using your framework to split the code, you might encounter situations where loading becomes slower than if no code splitting were happening at all. For example, [lazily loading](/reference/react/lazy) a chart delays sending the code needed to render the chart, splitting the chart code from the rest of the app. [Parcel supports code splitting with React.lazy](https://parceljs.org/recipes/react/#code-splitting). However, if the chart loads its data *after* it has been initially rendered you are now waiting twice. This is a waterfall: rather than fetching the data for the chart and sending the code to render it simultaneously, you must wait for each step to complete one after the other. + +Splitting code by route, when integrated with bundling and data fetching, can reduce the initial load time of your app and the time it takes for the largest visible content of the app to render ([Largest Contentful Paint](https://web.dev/articles/lcp)). + +For code-splitting instructions, see your build tool docs: +- [Vite build optimizations](https://vite.dev/guide/features.html#build-optimizations) +- [Parcel code splitting](https://parceljs.org/features/code-splitting/) +- [Rsbuild code splitting](https://rsbuild.dev/guide/optimization/code-splitting) + +### Improving Application Performance {/*improving-application-performance*/} + +Since the build tool you select only supports single page apps (SPAs), you'll need to implement other [rendering patterns](https://www.patterns.dev/vanilla/rendering-patterns) like server-side rendering (SSR), static site generation (SSG), and/or React Server Components (RSC). Even if you don't need these features at first, in the future there may be some routes that would benefit SSR, SSG or RSC. + +* **Single-page apps (SPA)** load a single HTML page and dynamically updates the page as the user interacts with the app. SPAs are easier to get started with, but they can have slower initial load times. SPAs are the default architecture for most build tools. + +* **Streaming Server-side rendering (SSR)** renders a page on the server and sends the fully rendered page to the client. SSR can improve performance, but it can be more complex to set up and maintain than a single-page app. With the addition of streaming, SSR can be very complex to set up and maintain. See [Vite's SSR guide]( https://vite.dev/guide/ssr). + +* **Static site generation (SSG)** generates static HTML files for your app at build time. SSG can improve performance, but it can be more complex to set up and maintain than server-side rendering. See [Vite's SSG guide](https://vite.dev/guide/ssr.html#pre-rendering-ssg). + +* **React Server Components (RSC)** lets you mix build-time, server-only, and interactive components in a single React tree. RSC can improve performance, but it currently requires deep expertise to set up and maintain. See [Parcel's RSC examples](https://github.com/parcel-bundler/rsc-examples). + +Your rendering strategies need to integrate with your router so apps built with your framework can choose the rendering strategy on a per-route level. This will enable different rendering strategies without having to rewrite your whole app. For example, the landing page for your app might benefit from being statically generated (SSG), while a page with a content feed might perform best with server-side rendering. + +Using the right rendering strategy for the right routes can decrease the time it takes for the first byte of content to be loaded ([Time to First Byte](https://web.dev/articles/ttfb)), the first piece of content to render ([First Contentful Paint](https://web.dev/articles/fcp)), and the largest visible content of the app to render ([Largest Contentful Paint](https://web.dev/articles/lcp)). + +### And more... {/*and-more*/} + +These are just a few examples of the features a new app will need to consider when building from scratch. Many limitations you'll hit can be difficult to solve as each problem is interconnected with the others and can require deep expertise in problem areas you may not be familiar with. + +If you don't want to solve these problems on your own, you can [get started with a framework](/learn/creating-a-react-app) that provides these features out of the box. diff --git a/src/content/learn/choosing-the-state-structure.md b/src/content/learn/choosing-the-state-structure.md index c3218589..93f9e87f 100644 --- a/src/content/learn/choosing-the-state-structure.md +++ b/src/content/learn/choosing-the-state-structure.md @@ -458,7 +458,7 @@ export default function Menu() { return ( <> - <h2>What's your travel snack?</h2> + <h2>What's your travel snack?</h2> <ul> {items.map((item, index) => ( <li key={item.id}> @@ -806,7 +806,7 @@ export const initialTravelPlan = { }, { id: 48, title: 'Green Hill', - childPlaces: [] + childPlaces: [] }] }] }; @@ -884,7 +884,7 @@ export const initialTravelPlan = { id: 2, title: 'Africa', childIds: [3, 4, 5, 6 , 7, 8, 9] - }, + }, 3: { id: 3, title: 'Botswana', @@ -904,7 +904,7 @@ export const initialTravelPlan = { id: 6, title: 'Madagascar', childIds: [] - }, + }, 7: { id: 7, title: 'Morocco', @@ -923,7 +923,7 @@ export const initialTravelPlan = { 10: { id: 10, title: 'Americas', - childIds: [11, 12, 13, 14, 15, 16, 17, 18], + childIds: [11, 12, 13, 14, 15, 16, 17, 18], }, 11: { id: 11, @@ -939,7 +939,7 @@ export const initialTravelPlan = { id: 13, title: 'Barbados', childIds: [] - }, + }, 14: { id: 14, title: 'Canada', @@ -968,7 +968,7 @@ export const initialTravelPlan = { 19: { id: 19, title: 'Asia', - childIds: [20, 21, 22, 23, 24, 25], + childIds: [20, 21, 22, 23, 24, 25], }, 20: { id: 20, @@ -1003,7 +1003,7 @@ export const initialTravelPlan = { 26: { id: 26, title: 'Europe', - childIds: [27, 28, 29, 30, 31, 32, 33], + childIds: [27, 28, 29, 30, 31, 32, 33], }, 27: { id: 27, @@ -1043,7 +1043,7 @@ export const initialTravelPlan = { 34: { id: 34, title: 'Oceania', - childIds: [35, 36, 37, 38, 39, 40, 41], + childIds: [35, 36, 37, 38, 39, 40, 41], }, 35: { id: 35, @@ -1220,7 +1220,7 @@ export const initialTravelPlan = { id: 2, title: 'Africa', childIds: [3, 4, 5, 6 , 7, 8, 9] - }, + }, 3: { id: 3, title: 'Botswana', @@ -1240,7 +1240,7 @@ export const initialTravelPlan = { id: 6, title: 'Madagascar', childIds: [] - }, + }, 7: { id: 7, title: 'Morocco', @@ -1259,7 +1259,7 @@ export const initialTravelPlan = { 10: { id: 10, title: 'Americas', - childIds: [11, 12, 13, 14, 15, 16, 17, 18], + childIds: [11, 12, 13, 14, 15, 16, 17, 18], }, 11: { id: 11, @@ -1275,7 +1275,7 @@ export const initialTravelPlan = { id: 13, title: 'Barbados', childIds: [] - }, + }, 14: { id: 14, title: 'Canada', @@ -1304,7 +1304,7 @@ export const initialTravelPlan = { 19: { id: 19, title: 'Asia', - childIds: [20, 21, 22, 23, 24, 25], + childIds: [20, 21, 22, 23, 24, 25], }, 20: { id: 20, @@ -1339,7 +1339,7 @@ export const initialTravelPlan = { 26: { id: 26, title: 'Europe', - childIds: [27, 28, 29, 30, 31, 32, 33], + childIds: [27, 28, 29, 30, 31, 32, 33], }, 27: { id: 27, @@ -1379,7 +1379,7 @@ export const initialTravelPlan = { 34: { id: 34, title: 'Oceania', - childIds: [35, 36, 37, 38, 39, 40, 41], + childIds: [35, 36, 37, 38, 39, 40, 41], }, 35: { id: 35, @@ -1559,7 +1559,7 @@ export const initialTravelPlan = { id: 2, title: 'Africa', childIds: [3, 4, 5, 6 , 7, 8, 9] - }, + }, 3: { id: 3, title: 'Botswana', @@ -1579,7 +1579,7 @@ export const initialTravelPlan = { id: 6, title: 'Madagascar', childIds: [] - }, + }, 7: { id: 7, title: 'Morocco', @@ -1598,7 +1598,7 @@ export const initialTravelPlan = { 10: { id: 10, title: 'Americas', - childIds: [11, 12, 13, 14, 15, 16, 17, 18], + childIds: [11, 12, 13, 14, 15, 16, 17, 18], }, 11: { id: 11, @@ -1614,7 +1614,7 @@ export const initialTravelPlan = { id: 13, title: 'Barbados', childIds: [] - }, + }, 14: { id: 14, title: 'Canada', @@ -1643,7 +1643,7 @@ export const initialTravelPlan = { 19: { id: 19, title: 'Asia', - childIds: [20, 21, 22, 23, 24, 25,], + childIds: [20, 21, 22, 23, 24, 25,], }, 20: { id: 20, @@ -1678,7 +1678,7 @@ export const initialTravelPlan = { 26: { id: 26, title: 'Europe', - childIds: [27, 28, 29, 30, 31, 32, 33], + childIds: [27, 28, 29, 30, 31, 32, 33], }, 27: { id: 27, @@ -1718,7 +1718,11 @@ export const initialTravelPlan = { 34: { id: 34, title: 'Oceania', +<<<<<<< HEAD childIds: [35, 36, 37, 38, 39, 40,, 41], +======= + childIds: [35, 36, 37, 38, 39, 40, 41], +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a }, 35: { id: 35, @@ -1823,7 +1827,7 @@ Sometimes, you can also reduce state nesting by moving some of the nested state <Recap> -* If two state variables always update together, consider merging them into one. +* If two state variables always update together, consider merging them into one. * Choose your state variables carefully to avoid creating "impossible" states. * Structure your state in a way that reduces the chances that you'll make a mistake updating it. * Avoid redundant and duplicate state so that you don't need to keep it in sync. @@ -2062,7 +2066,7 @@ export default function TravelPlan() { } return ( - <> + <> <AddItem onAddItem={handleAddItem} /> @@ -2197,7 +2201,7 @@ export default function TravelPlan() { } return ( - <> + <> <AddItem onAddItem={handleAddItem} /> @@ -2350,7 +2354,7 @@ export default function Letter({ isHighlighted ? 'highlighted' : '' } onFocus={() => { - onHover(letter); + onHover(letter); }} onPointerMove={() => { onHover(letter); @@ -2459,7 +2463,7 @@ export default function Letter({ isHighlighted ? 'highlighted' : '' } onFocus={() => { - onHover(letter.id); + onHover(letter.id); }} onPointerMove={() => { onHover(letter.id); diff --git a/src/content/learn/conditional-rendering.md b/src/content/learn/conditional-rendering.md index 1e38801f..f311607c 100644 --- a/src/content/learn/conditional-rendering.md +++ b/src/content/learn/conditional-rendering.md @@ -32,17 +32,17 @@ export default function PackingList() { <section> <h1>Sally Ride's Packing List</h1> <ul> - <Item - isPacked={true} - name="Space suit" + <Item + isPacked={true} + name="Space suit" /> - <Item - isPacked={true} - name="Helmet with a golden leaf" + <Item + isPacked={true} + name="Helmet with a golden leaf" /> - <Item - isPacked={false} - name="Photo of Tam" + <Item + isPacked={false} + name="Photo of Tam" /> </ul> </section> @@ -80,17 +80,17 @@ export default function PackingList() { <section> <h1>Sally Ride's Packing List</h1> <ul> - <Item - isPacked={true} - name="Space suit" + <Item + isPacked={true} + name="Space suit" /> - <Item - isPacked={true} - name="Helmet with a golden leaf" + <Item + isPacked={true} + name="Helmet with a golden leaf" /> - <Item - isPacked={false} - name="Photo of Tam" + <Item + isPacked={false} + name="Photo of Tam" /> </ul> </section> @@ -132,17 +132,17 @@ export default function PackingList() { <section> <h1>Sally Ride's Packing List</h1> <ul> - <Item - isPacked={true} - name="Space suit" + <Item + isPacked={true} + name="Space suit" /> - <Item - isPacked={true} - name="Helmet with a golden leaf" + <Item + isPacked={true} + name="Helmet with a golden leaf" /> - <Item - isPacked={false} - name="Photo of Tam" + <Item + isPacked={false} + name="Photo of Tam" /> </ul> </section> @@ -236,17 +236,17 @@ export default function PackingList() { <section> <h1>Sally Ride's Packing List</h1> <ul> - <Item - isPacked={true} - name="Space suit" + <Item + isPacked={true} + name="Space suit" /> - <Item - isPacked={true} - name="Helmet with a golden leaf" + <Item + isPacked={true} + name="Helmet with a golden leaf" /> - <Item - isPacked={false} - name="Photo of Tam" + <Item + isPacked={false} + name="Photo of Tam" /> </ul> </section> @@ -290,17 +290,17 @@ export default function PackingList() { <section> <h1>Sally Ride's Packing List</h1> <ul> - <Item - isPacked={true} - name="Space suit" + <Item + isPacked={true} + name="Space suit" /> - <Item - isPacked={true} - name="Helmet with a golden leaf" + <Item + isPacked={true} + name="Helmet with a golden leaf" /> - <Item - isPacked={false} - name="Photo of Tam" + <Item + isPacked={false} + name="Photo of Tam" /> </ul> </section> @@ -371,17 +371,17 @@ export default function PackingList() { <section> <h1>Sally Ride's Packing List</h1> <ul> - <Item - isPacked={true} - name="Space suit" + <Item + isPacked={true} + name="Space suit" /> - <Item - isPacked={true} - name="Helmet with a golden leaf" + <Item + isPacked={true} + name="Helmet with a golden leaf" /> - <Item - isPacked={false} - name="Photo of Tam" + <Item + isPacked={false} + name="Photo of Tam" /> </ul> </section> @@ -417,17 +417,17 @@ export default function PackingList() { <section> <h1>Sally Ride's Packing List</h1> <ul> - <Item - isPacked={true} - name="Space suit" + <Item + isPacked={true} + name="Space suit" /> - <Item - isPacked={true} - name="Helmet with a golden leaf" + <Item + isPacked={true} + name="Helmet with a golden leaf" /> - <Item - isPacked={false} - name="Photo of Tam" + <Item + isPacked={false} + name="Photo of Tam" /> </ul> </section> @@ -474,17 +474,17 @@ export default function PackingList() { <section> <h1>Sally Ride's Packing List</h1> <ul> - <Item - isPacked={true} - name="Space suit" + <Item + isPacked={true} + name="Space suit" /> - <Item - isPacked={true} - name="Helmet with a golden leaf" + <Item + isPacked={true} + name="Helmet with a golden leaf" /> - <Item - isPacked={false} - name="Photo of Tam" + <Item + isPacked={false} + name="Photo of Tam" /> </ul> </section> @@ -512,17 +512,17 @@ export default function PackingList() { <section> <h1>Sally Ride's Packing List</h1> <ul> - <Item - isPacked={true} - name="Space suit" + <Item + isPacked={true} + name="Space suit" /> - <Item - isPacked={true} - name="Helmet with a golden leaf" + <Item + isPacked={true} + name="Helmet with a golden leaf" /> - <Item - isPacked={false} - name="Photo of Tam" + <Item + isPacked={false} + name="Photo of Tam" /> </ul> </section> @@ -560,17 +560,17 @@ export default function PackingList() { <section> <h1>Sally Ride's Packing List</h1> <ul> - <Item - importance={9} - name="Space suit" + <Item + importance={9} + name="Space suit" /> - <Item - importance={0} - name="Helmet with a golden leaf" + <Item + importance={0} + name="Helmet with a golden leaf" /> - <Item - importance={6} - name="Photo of Tam" + <Item + importance={6} + name="Photo of Tam" /> </ul> </section> @@ -604,17 +604,17 @@ export default function PackingList() { <section> <h1>Sally Ride's Packing List</h1> <ul> - <Item - importance={9} - name="Space suit" + <Item + importance={9} + name="Space suit" /> - <Item - importance={0} - name="Helmet with a golden leaf" + <Item + importance={0} + name="Helmet with a golden leaf" /> - <Item - importance={6} - name="Photo of Tam" + <Item + importance={6} + name="Photo of Tam" /> </ul> </section> diff --git a/src/content/learn/creating-a-react-app.md b/src/content/learn/creating-a-react-app.md new file mode 100644 index 00000000..fef66838 --- /dev/null +++ b/src/content/learn/creating-a-react-app.md @@ -0,0 +1,113 @@ +--- +title: Creating a React App +--- + +<Intro> + +If you want to build a new app or website with React, we recommend starting with a framework. + +</Intro> + +If your app has constraints not well-served by existing frameworks, you prefer to build your own framework, or you just want to learn the basics of a React app, you can [build a React app from scratch](/learn/build-a-react-app-from-scratch). + +## Full-stack frameworks {/*full-stack-frameworks*/} + +These recommended frameworks support all the features you need to deploy and scale your app in production. They have integrated the latest React features and take advantage of React’s architecture. + +<Note> + +#### Full-stack frameworks do not require a server. {/*react-frameworks-do-not-require-a-server*/} + +All the frameworks on this page support client-side rendering ([CSR](https://developer.mozilla.org/en-US/docs/Glossary/CSR)), single-page apps ([SPA](https://developer.mozilla.org/en-US/docs/Glossary/SPA)), and static-site generation ([SSG](https://developer.mozilla.org/en-US/docs/Glossary/SSG)). These apps can be deployed to a [CDN](https://developer.mozilla.org/en-US/docs/Glossary/CDN) or static hosting service without a server. Additionally, these frameworks allow you to add server-side rendering on a per-route basis, when it makes sense for your use case. + +This allows you to start with a client-only app, and if your needs change later, you can opt-in to using server features on individual routes without rewriting your app. See your framework's documentation for configuring the rendering strategy. + +</Note> + +### Next.js (App Router) {/*nextjs-app-router*/} + +**[Next.js's App Router](https://nextjs.org/docs) is a React framework that takes full advantage of React's architecture to enable full-stack React apps.** + +<TerminalBlock> +npx create-next-app@latest +</TerminalBlock> + +Next.js is maintained by [Vercel](https://vercel.com/). You can [deploy a Next.js app](https://nextjs.org/docs/app/building-your-application/deploying) to any hosting provider that supports Node.js or Docker containers, or to your own server. Next.js also supports [static export](https://nextjs.org/docs/app/building-your-application/deploying/static-exports) which doesn't require a server. + +### React Router (v7) {/*react-router-v7*/} + +**[React Router](https://reactrouter.com/start/framework/installation) is the most popular routing library for React and can be paired with Vite to create a full-stack React framework**. It emphasizes standard Web APIs and has several [ready to deploy templates](https://github.com/remix-run/react-router-templates) for various JavaScript runtimes and platforms. + +To create a new React Router framework project, run: + +<TerminalBlock> +npx create-react-router@latest +</TerminalBlock> + +React Router is maintained by [Shopify](https://www.shopify.com). + +### Expo (for native apps) {/*expo*/} + +**[Expo](https://expo.dev/) is a React framework that lets you create universal Android, iOS, and web apps with truly native UIs.** It provides an SDK for [React Native](https://reactnative.dev/) that makes the native parts easier to use. To create a new Expo project, run: + +<TerminalBlock> +npx create-expo-app@latest +</TerminalBlock> + +If you're new to Expo, check out the [Expo tutorial](https://docs.expo.dev/tutorial/introduction/). + +Expo is maintained by [Expo (the company)](https://expo.dev/about). Building apps with Expo is free, and you can submit them to the Google and Apple app stores without restrictions. Expo additionally provides opt-in paid cloud services. + + +## Other frameworks {/*other-frameworks*/} + +There are other up-and-coming frameworks that are working towards our full stack React vision: + +- [TanStack Start (Beta)](https://tanstack.com/start/): TanStack Start is a full-stack React framework powered by TanStack Router. It provides a full-document SSR, streaming, server functions, bundling, and more using tools like Nitro and Vite. +- [RedwoodSDK](https://rwsdk.com/): Redwood is a full stack React framework with lots of pre-installed packages and configuration that makes it easy to build full-stack web applications. + +<DeepDive> + +#### Which features make up the React team’s full-stack architecture vision? {/*which-features-make-up-the-react-teams-full-stack-architecture-vision*/} + +Next.js's App Router bundler fully implements the official [React Server Components specification](https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md). This lets you mix build-time, server-only, and interactive components in a single React tree. + +For example, you can write a server-only React component as an `async` function that reads from a database or from a file. Then you can pass data down from it to your interactive components: + +```js +// This component runs *only* on the server (or during the build). +async function Talks({ confId }) { + // 1. You're on the server, so you can talk to your data layer. API endpoint not required. + const talks = await db.Talks.findAll({ confId }); + + // 2. Add any amount of rendering logic. It won't make your JavaScript bundle larger. + const videos = talks.map(talk => talk.video); + + // 3. Pass the data down to the components that will run in the browser. + return <SearchableVideoList videos={videos} />; +} +``` + +Next.js's App Router also integrates [data fetching with Suspense](/blog/2022/03/29/react-v18#suspense-in-data-frameworks). This lets you specify a loading state (like a skeleton placeholder) for different parts of your user interface directly in your React tree: + +```js +<Suspense fallback={<TalksLoading />}> + <Talks confId={conf.id} /> +</Suspense> +``` + +Server Components and Suspense are React features rather than Next.js features. However, adopting them at the framework level requires buy-in and non-trivial implementation work. At the moment, the Next.js App Router is the most complete implementation. The React team is working with bundler developers to make these features easier to implement in the next generation of frameworks. + +</DeepDive> + +## Start From Scratch {/*start-from-scratch*/} + +If your app has constraints not well-served by existing frameworks, you prefer to build your own framework, or you just want to learn the basics of a React app, there are other options available for starting a React project from scratch. + +Starting from scratch gives you more flexibility, but does require that you make choices on which tools to use for routing, data fetching, and other common usage patterns. It's a lot like building your own framework, instead of using a framework that already exists. The [frameworks we recommend](#full-stack-frameworks) have built-in solutions for these problems. + +If you want to build your own solutions, see our guide to [build a React app from Scratch](/learn/build-a-react-app-from-scratch) for instructions on how to set up a new React project starting with a build tool like [Vite](https://vite.dev/), [Parcel](https://parceljs.org/), or [RSbuild](https://rsbuild.dev/). + +----- + +_If you’re a framework author interested in being included on this page, [please let us know](https://github.com/reactjs/react.dev/issues/new?assignees=&labels=type%3A+framework&projects=&template=3-framework.yml&title=%5BFramework%5D%3A+)._ diff --git a/src/content/learn/describing-the-ui.md b/src/content/learn/describing-the-ui.md index be7a0e42..52af0e16 100644 --- a/src/content/learn/describing-the-ui.md +++ b/src/content/learn/describing-the-ui.md @@ -33,7 +33,7 @@ React ແມ່ນ JavaScript library ສຳລັບສະແດງຜົນໜ function Profile() { return ( <img - src="https://i.imgur.com/MK3eW3As.jpg" + src="https://react.dev/images/docs/scientists/MK3eW3As.jpg" alt="Katherine Johnson" /> ); @@ -99,7 +99,7 @@ export default function Gallery() { export default function Profile() { return ( <img - src="https://i.imgur.com/QIrZWGIs.jpg" + src="https://react.dev/images/docs/scientists/QIrZWGIs.jpg" alt="Alan L. Hart" /> ); @@ -132,7 +132,7 @@ export default function TodoList() { // ນີ້ມັນບໍ່ສາມາດເຮັດວຽກໄດ້ເລີຍ! <h1>Todos ຂອງ Hedy Lamarr</h1> <img - src="https://i.imgur.com/yXOvdOSs.jpg" + src="https://react.dev/images/docs/scientists/yXOvdOSs.jpg" alt="Hedy Lamarr" class="photo" > @@ -161,7 +161,7 @@ export default function TodoList() { <> <h1>Todos ຂອງ Hedy Lamarr</h1> <img - src="https://i.imgur.com/yXOvdOSs.jpg" + src="https://react.dev/images/docs/scientists/yXOvdOSs.jpg" alt="Hedy Lamarr" className="photo" /> @@ -208,7 +208,7 @@ export default function TodoList() { <h1>Todos ຂອງ {person.name} </h1> <img className="avatar" - src="https://i.imgur.com/7vQD0fPs.jpg" + src="https://react.dev/images/docs/scientists/7vQD0fPs.jpg" alt="Gregorio Y. Zara" /> <ul> @@ -283,7 +283,7 @@ function Card({ children }) { ```js src/utils.js export function getImageUrl(person, size = 's') { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + size + '.jpg' @@ -436,7 +436,7 @@ export const people = [{ ```js src/utils.js export function getImageUrl(person) { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + 's.jpg' ); @@ -527,9 +527,15 @@ export default function TeaSet() { ## UI ຂອງທ່ານຄືກັບຕົ້ນໄມ້ {/*your-ui-as-a-tree*/} +<<<<<<< HEAD React ໃຊ້ tree ເພື່ອສ້າງ model ຄວາມສຳພັນລະຫວ່າງ component ແລະ module. tree ການ render React ເປັນຕົວແທນຂອງຄວາມສຳພັນລະຫວ່າງ parent ແລະ child ລະຫວ່າງ component ຕ່າງໆ. +======= +React uses trees to model the relationships between components and modules. + +A React render tree is a representation of the parent and child relationship between components. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a <Diagram name="generic_render_tree" height={250} width={500} alt="A tree graph with five nodes, with each node representing a component. The root node is located at the top the tree graph and is labelled 'Root Component'. It has two arrows extending down to two nodes labelled 'Component A' and 'Component C'. Each of the arrows is labelled with 'renders'. 'Component A' has a single 'renders' arrow to a node labelled 'Component B'. 'Component C' has a single 'renders' arrow to a node labelled 'Component D'."> @@ -539,7 +545,11 @@ tree ການ render React ເປັນຕົວແທນຂອງຄວາມ Component ທີ່ຢູ່ດ້ານເທິງສຸດຂອງ tree, ໃກ້ກັບ root component, ຖືເປັນສ່ວນປະກອບຂອງ top-level component. Component ທີ່ບໍ່ມີ child component ເປັນ leaf component. ການຈັດໝວດໝູ່ component ນີ້ເປັນປະໂຫຍດຫຼາຍສຳລັບການທຳຄວາມເຂົ້າໃຈການໄຫຼຂໍ້ມູນ ແລະ ປະສິດທິພາບການ render. +<<<<<<< HEAD ການສ້າງ Model ຄວາມສຳພັນລະຫວ່າງ JavaScript module ເປັນອີກວິທີທີ່ມີປະໂຫຍດໃນການທຳຄວາມເຂົ້າໃຈແອັບຂອງທ່ານ. ພວກເຮົາເອີ້ນມັນວ່າເປັນ module dependency tree. +======= +Modelling the relationship between JavaScript modules is another useful way to understand your app. We refer to it as a module dependency tree. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a <Diagram name="generic_dependency_tree" height={250} width={500} alt="A tree graph with five nodes. Each node represents a JavaScript module. The top-most node is labelled 'RootModule.js'. It has three arrows extending to the nodes: 'ModuleA.js', 'ModuleB.js', and 'ModuleC.js'. Each arrow is labelled as 'imports'. 'ModuleC.js' node has a single 'imports' arrow that points to a node labelled 'ModuleD.js'."> @@ -547,7 +557,11 @@ Component ທີ່ຢູ່ດ້ານເທິງສຸດຂອງ tree, ໃ </Diagram> +<<<<<<< HEAD Build tools ມັກໃຊ້ dependency tree ເພື່ອລວມ JavaScript Code ທີ່ກ່ຽວຂ້ອງທັງໝົດເພື່ອໃຫ້ client ດາວໂຫຼດ ແລະ render. Bundle ຂະໜາດໃຫຍ່ເຮັດໃຫ້ປະສົບການໃຊ້ງານຂອງຜູ້ໃຊ້ງານບໍ່ຄ່ອຍດີສຳລັບແອັບ React. ການທຳຄວາມເຂົ້າໃຈ module dependency tree ເປັນປະໂຫຍດຫຼາຍໃນການ debug ຫາບັນຫາດັ່ງກ່າວ. +======= +A dependency tree is often used by build tools to bundle all the relevant JavaScript code for the client to download and render. A large bundle size regresses user experience for React apps. Understanding the module dependency tree is helpful to debug such issues. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a <LearnMore path="/learn/understanding-your-ui-as-a-tree"> diff --git a/src/content/learn/editor-setup.md b/src/content/learn/editor-setup.md index b01b4401..414445fe 100644 --- a/src/content/learn/editor-setup.md +++ b/src/content/learn/editor-setup.md @@ -32,7 +32,11 @@ Some editors come with these features built in, but others might require adding ### Linting {/*linting*/} +<<<<<<< HEAD Code linters ຄົ້ນຫາບັນຫາໃນ code ຂອງທ່ານໃນຂະນະທີ່ທ່ານກຳລັງຂຽນ, ຊ່ວຍທ່ານແກ້ໄຂໄດ້ແຕ່ເນີ້ນໆ. [ESLint](https://eslint.org/) ແມ່ນໄດ້ຮັບຄວາມນິຍົມ, ເປັນ open source linter ສຳລັບ JavaScript. +======= +Code linters find problems in your code as you write, helping you fix them early. [ESLint](https://eslint.org/) is a popular, open source linter for JavaScript. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a * [ຕິດຕັ້ງ ESLintr ພ້ອມກັບຄຳແນະນຳການຕັ້ງຄ່າສຳລັບ React](https://www.npmjs.com/package/eslint-config-react-app) (ໃຫ້ແນ່ໃຈວ່າທ່ານໄດ້ [ຕິດຕັ້ງ Node ແລ້ວ!](https://nodejs.org/en/download/current/)) * [Integrate ESLint ໃນ VSCode ກັບ extension ທາງການ](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) diff --git a/src/content/learn/escape-hatches.md b/src/content/learn/escape-hatches.md index 23f11f54..1dbdc724 100644 --- a/src/content/learn/escape-hatches.md +++ b/src/content/learn/escape-hatches.md @@ -227,7 +227,7 @@ function Form() { } ``` -However, you *do* need Effects to synchronize with external systems. +However, you *do* need Effects to synchronize with external systems. <LearnMore path="/learn/you-might-not-need-an-effect"> @@ -388,7 +388,7 @@ export default function App() { <hr /> <ChatRoom roomId={roomId} - theme={isDark ? 'dark' : 'light'} + theme={isDark ? 'dark' : 'light'} /> </> ); @@ -521,7 +521,7 @@ export default function App() { <hr /> <ChatRoom roomId={roomId} - theme={isDark ? 'dark' : 'light'} + theme={isDark ? 'dark' : 'light'} /> </> ); diff --git a/src/content/learn/importing-and-exporting-components.md b/src/content/learn/importing-and-exporting-components.md index 4d99a6da..5f847bfd 100644 --- a/src/content/learn/importing-and-exporting-components.md +++ b/src/content/learn/importing-and-exporting-components.md @@ -28,7 +28,7 @@ title: ການ Import ແລະ Export Components function Profile() { return ( <img - src="https://i.imgur.com/MK3eW3As.jpg" + src="https://react.dev/images/docs/scientists/MK3eW3As.jpg" alt="Katherine Johnson" /> ); @@ -85,7 +85,7 @@ export default function App() { function Profile() { return ( <img - src="https://i.imgur.com/QIrZWGIs.jpg" + src="https://react.dev/images/docs/scientists/QIrZWGIs.jpg" alt="Alan L. Hart" /> ); @@ -123,7 +123,7 @@ img { margin: 0 10px 10px 0; height: 90px; } ທ່ານອາດຈະພົບຟາຍທີ່ບໍ່ມີນາມສະກຸນຟາຍ `.js` ດັ່ງນີ້: -```js +```js import Gallery from './Gallery'; ``` @@ -203,7 +203,7 @@ export default function App() { export function Profile() { return ( <img - src="https://i.imgur.com/QIrZWGIs.jpg" + src="https://react.dev/images/docs/scientists/QIrZWGIs.jpg" alt="Alan L. Hart" /> ); @@ -292,7 +292,7 @@ export default function App() { export function Profile() { return ( <img - src="https://i.imgur.com/QIrZWGIs.jpg" + src="https://react.dev/images/docs/scientists/QIrZWGIs.jpg" alt="Alan L. Hart" /> ); @@ -360,7 +360,7 @@ export default function Gallery() { export function Profile() { return ( <img - src="https://i.imgur.com/QIrZWGIs.jpg" + src="https://react.dev/images/docs/scientists/QIrZWGIs.jpg" alt="Alan L. Hart" /> ); @@ -410,7 +410,7 @@ export default function Gallery() { export default function Profile() { return ( <img - src="https://i.imgur.com/QIrZWGIs.jpg" + src="https://react.dev/images/docs/scientists/QIrZWGIs.jpg" alt="Alan L. Hart" /> ); diff --git a/src/content/learn/index.md b/src/content/learn/index.md index a22df241..f60b5bc9 100644 --- a/src/content/learn/index.md +++ b/src/content/learn/index.md @@ -143,7 +143,7 @@ return ( ```js const user = { name: 'Hedy Lamarr', - imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg', + imageUrl: 'https://react.dev/images/docs/scientists/yXOvdOSs.jpg', imageSize: 90, }; diff --git a/src/content/learn/javascript-in-jsx-with-curly-braces.md b/src/content/learn/javascript-in-jsx-with-curly-braces.md index 01c88d85..4252b051 100644 --- a/src/content/learn/javascript-in-jsx-with-curly-braces.md +++ b/src/content/learn/javascript-in-jsx-with-curly-braces.md @@ -28,7 +28,7 @@ export default function Avatar() { return ( <img className="avatar" - src="https://i.imgur.com/7vQD0fPs.jpg" + src="https://react.dev/images/docs/scientists/7vQD0fPs.jpg" alt="Gregorio Y. Zara" /> ); @@ -41,7 +41,11 @@ export default function Avatar() { </Sandpack> +<<<<<<< HEAD ນີ້, `"https://i.imgur.com/7vQD0fPs.jpg"` ແລະ `"Gregorio Y. Zara"` ແມ່ນຖືກສົ່ງຜ່ານເປັນ string. +======= +Here, `"https://react.dev/images/docs/scientists/7vQD0fPs.jpg"` and `"Gregorio Y. Zara"` are being passed as strings. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ແຕ່ຖ້າທ່ານຕ້ອງການລະບຸຂໍ້ຄວາມ `src` ຫຼື `alt` ແບບ dynamic ເດ? ທ່ານສາມາດ **ໃຊ້ຄ່າຈາກ JavaScript ໂດຍແທນທີ່ `"` ແລະ `"` ດ້ວຍ `{` ແລະ `}`** @@ -49,7 +53,7 @@ export default function Avatar() { ```js export default function Avatar() { - const avatar = 'https://i.imgur.com/7vQD0fPs.jpg'; + const avatar = 'https://react.dev/images/docs/scientists/7vQD0fPs.jpg'; const description = 'Gregorio Y. Zara'; return ( <img @@ -190,7 +194,7 @@ export default function TodoList() { <h1>Todos ຂອງ {person.name}</h1> <img className="avatar" - src="https://i.imgur.com/7vQD0fPs.jpg" + src="https://react.dev/images/docs/scientists/7vQD0fPs.jpg" alt="Gregorio Y. Zara" /> <ul> @@ -266,7 +270,7 @@ export default function TodoList() { <h1>Todos ຂອງ {person}</h1> <img className="avatar" - src="https://i.imgur.com/7vQD0fPs.jpg" + src="https://react.dev/images/docs/scientists/7vQD0fPs.jpg" alt="Gregorio Y. Zara" /> <ul> @@ -314,7 +318,7 @@ export default function TodoList() { <h1>Todos ຂອງ {person.name}</h1> <img className="avatar" - src="https://i.imgur.com/7vQD0fPs.jpg" + src="https://react.dev/images/docs/scientists/7vQD0fPs.jpg" alt="Gregorio Y. Zara" /> <ul> @@ -358,7 +362,7 @@ export default function TodoList() { <h1>Todos ຂອງ {person.name}</h1> <img className="avatar" - src="https://i.imgur.com/7vQD0fPs.jpg" + src="https://react.dev/images/docs/scientists/7vQD0fPs.jpg" alt="Gregorio Y. Zara" /> <ul> @@ -388,7 +392,7 @@ body > div > div { padding: 20px; } ```js const person = { name: 'Gregorio Y. Zara', - imageUrl: "https://i.imgur.com/7vQD0fPs.jpg", + imageUrl: "https://react.dev/images/docs/scientists/7vQD0fPs.jpg", theme: { backgroundColor: 'black', color: 'pink' @@ -428,7 +432,11 @@ body > div > div { padding: 20px; } ໃນ object ດ້ານລຸ່ມ, Image URL ເຕັມແມ່ນແຍກເປັນສີ່ສ່ວນ: base URL, `imageId`, `imageSize` ແລະ ຟາຍ extension. +<<<<<<< HEAD ພວກເຮົາຕ້ອງການ image URL ເພື່ອປະສົມກັບ attribute ເຫຼົ່ານີ້ເຂົ້ານຳກັນ: base URL (ເປັນ `'https://i.imgur.com/'` ສະເໝີ), `imageId` (`'7vQD0fP'`), `imageSize` (`'s'`), ແລະ ຟາຍ extension (ເປັນ `'.jpg'` ສະເໝີ). ເຖິງຢ່າງໃດກໍ່ຕາມ, ມີບາງຢ່າງຜິດປົກະຕິກັບວິທີທີ່ແທັກ `<img>` ກຳນົດ `src`. +======= +We want the image URL to combine these attributes together: base URL (always `'https://react.dev/images/docs/scientists/'`), `imageId` (`'7vQD0fP'`), `imageSize` (`'s'`), and file extension (always `'.jpg'`). However, something is wrong with how the `<img>` tag specifies its `src`. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ທ່ານແປງໄດ້ບໍ່? @@ -436,7 +444,7 @@ body > div > div { padding: 20px; } ```js -const baseUrl = 'https://i.imgur.com/'; +const baseUrl = 'https://react.dev/images/docs/scientists/'; const person = { name: 'Gregorio Y. Zara', imageId: '7vQD0fP', @@ -487,7 +495,7 @@ body > div > div { padding: 20px; } <Sandpack> ```js -const baseUrl = 'https://i.imgur.com/'; +const baseUrl = 'https://react.dev/images/docs/scientists/'; const person = { name: 'Gregorio Y. Zara', imageId: '7vQD0fP', @@ -564,7 +572,7 @@ export default function TodoList() { ```js src/utils.js export function getImageUrl(person) { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + person.imageSize + '.jpg' diff --git a/src/content/learn/keeping-components-pure.md b/src/content/learn/keeping-components-pure.md index 0b13bdda..7a065a41 100644 --- a/src/content/learn/keeping-components-pure.md +++ b/src/content/learn/keeping-components-pure.md @@ -27,6 +27,7 @@ title: ເຮັດໃຫ້ Components Pure ພິຈາລະນາສູດຄະນິດສາດນີ້: <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math>. +<<<<<<< HEAD ຖ້າ <Math><MathI>x</MathI> = 2</Math> ແລ້ວ <Math><MathI>y</MathI> = 4</Math>. ສະເໝີ. ຖ້າ <Math><MathI>x</MathI> = 3</Math> ແລ້ວ <Math><MathI>y</MathI> = 6</Math>. ສະເໝີ. @@ -34,6 +35,15 @@ title: ເຮັດໃຫ້ Components Pure ຖ້າ <Math><MathI>x</MathI> = 3</Math>, <MathI>y</MathI> ບາງເທື່ອຈະບໍ່ເປັນ <Math>9</Math> ຫຼື <Math>–1</Math> ຫຼື <Math>2.5</Math> ຂຶ້ນກັບຊ່ວງເວລາຂອງວັນ ຫຼື ສະຖານະການຂອງຕະຫຼາດຫຸ້ນ. ຖ້າ <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math> ແລະ <Math><MathI>x</MathI> = 3</Math>, <MathI>y</MathI> ຈະເປັນ <Math>6</Math> _ຢູ່ສະເໝີ_. +======= +If <Math><MathI>x</MathI> = 2</Math> then <Math><MathI>y</MathI> = 4</Math>. Always. + +If <Math><MathI>x</MathI> = 3</Math> then <Math><MathI>y</MathI> = 6</Math>. Always. + +If <Math><MathI>x</MathI> = 3</Math>, <MathI>y</MathI> won't sometimes be <Math>9</Math> or <Math>–1</Math> or <Math>2.5</Math> depending on the time of day or the state of the stock market. + +If <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math> and <Math><MathI>x</MathI> = 3</Math>, <MathI>y</MathI> will _always_ be <Math>6</Math>. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ຖ້າເຮົາເອົາເຂົ້າເປັນຟັງຊັ່ນ JavScript, ໜ້າຕາຈະປະມານນີ້: @@ -52,7 +62,7 @@ React ໄດ້ຮັບການອອກແບບຕາມແນວຄິດ ```js src/App.js function Recipe({ drinkers }) { return ( - <ol> + <ol> <li>Boil {drinkers} cups of water.</li> <li>Add {drinkers} spoons of tea and {0.5 * drinkers} spoons of spice.</li> <li>Add {0.5 * drinkers} cups of milk to boil and sugar to taste.</li> @@ -75,11 +85,19 @@ export default function App() { </Sandpack> +<<<<<<< HEAD ເມື່ອທ່ານສົ່ງ `drinker={2}` ຫາ `Recipe`. ມັນຈະ return JSX ທີ່ປະກອບມີ `2 cups of water`. ສະເໝີ. +======= +When you pass `drinkers={2}` to `Recipe`, it will return JSX containing `2 cups of water`. Always. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ຖ້າທ່ານສົ່ງ `drinkers={4}`, ມັນຈະ return JSX ທີ່ປະກອບມີ `4 cups of water`. ສະເໝີ. +<<<<<<< HEAD ຄືກັບສູດຄະນິດສາດ. +======= +Just like a math formula. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ທ່ານສາມາດຄິດວ່າ component ຂອງທ່ານເປັນສູດອາຫານ: ຖ້າທ່ານເຮັດຕາມ ແລະ ບໍ່ເພີ່ມສ່ວນປະສົມໃໝ່ລະຫວ່າງຂັ້ນຕອນການເຮັດອາຫານ, ທ່ານຈະໄດ້ອາຫານຈານເກົ່າທຸກເທື່ອ. "ຈານ" ເປັນ JSX ທີ່ component ເຮັດໜ້າທີ່ຕອບສະໜອງຕໍ່ React ເພື່ອ [ສະແດງຜົນ.](/learn/render-and-commit) @@ -215,6 +233,7 @@ export default function TeaGathering() { <Recap> +<<<<<<< HEAD * Component ຕ້ອງ pure, ໝາຍຄວາມວ່າ: * **ມັນຄິດເຖິງເລື່ອງຂອງໂຕເອງ.** ບໍ່ຄວນປ່ຽນແປງ object ຫຼື ຕົວແປໃດໆທີ່ມີຢູ່ກ່ອນການ render. * **input ດຽວກັນ, output ດຽວກັນ.** ເມື່ອພິຈາລະນາ input ດຽວກັນ, component ຄວນ return JSX ດຽວກັນສະເໝີ. @@ -222,11 +241,20 @@ export default function TeaGathering() { * ທ່ານບໍ່ຄວນ mutate input ໃດໆທີ່ component ຂອງທ່ານໃຊ້ສຳລັບການ render. ຊື່ງລວມ prop, state ແລະ context. ເພື່ອອັບເດດໜ້າຈໍ, ["set" state](/learn/state-a-components-memory) ແທນການ mutate object ທີ່ມີຢູ່ກ່ອນ. * ພະຍາຍາມສະແດງ logic ຂອງ component ທ່ານໃນ JSX ທີ່ທ່ານ return. ເມື່ອທ່ານຕ້ອງການ "ປ່ຽນແປງສິ່ງ", ທ່ານມັກຈະຕ້ອງເຮັດ event handler. ເປັນທາງເລືອກສຸດທ້າຍ, ທ່ານສາມາດໃຊ້ `useEffect` * ການຂຽນຟັງຊັ່ນ pure ຕ້ອງອາໄສການເຝິກຊ້ອມໜ້ອຍໜຶ່ງ, ແຕ່ຈະປົດລ໋ອກພະລັງຂອງ React paradigm. +======= +* A component must be pure, meaning: + * **It minds its own business.** It should not change any objects or variables that existed before rendering. + * **Same inputs, same output.** Given the same inputs, a component should always return the same JSX. +* Rendering can happen at any time, so components should not depend on each others' rendering sequence. +* You should not mutate any of the inputs that your components use for rendering. That includes props, state, and context. To update the screen, ["set" state](/learn/state-a-components-memory) instead of mutating preexisting objects. +* Strive to express your component's logic in the JSX you return. When you need to "change things", you'll usually want to do it in an event handler. As a last resort, you can `useEffect`. +* Writing pure functions takes a bit of practice, but it unlocks the power of React's paradigm. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a </Recap> - + <Challenges> #### ແປງໂມງເພ {/*fix-a-broken-clock*/} @@ -451,7 +479,7 @@ export default function App() { ```js src/utils.js hidden export function getImageUrl(person, size = 's') { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + size + '.jpg' @@ -549,7 +577,7 @@ export default function App() { ```js src/utils.js hidden export function getImageUrl(person, size = 's') { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + size + '.jpg' diff --git a/src/content/learn/lifecycle-of-reactive-effects.md b/src/content/learn/lifecycle-of-reactive-effects.md index 3dc9a75f..b6da549b 100644 --- a/src/content/learn/lifecycle-of-reactive-effects.md +++ b/src/content/learn/lifecycle-of-reactive-effects.md @@ -293,7 +293,7 @@ You might be wondering how React knew that your Effect needed to re-synchronize ```js {1,3,8} function ChatRoom({ roomId }) { // The roomId prop may change over time useEffect(() => { - const connection = createConnection(serverUrl, roomId); // This Effect reads roomId + const connection = createConnection(serverUrl, roomId); // This Effect reads roomId connection.connect(); return () => { connection.disconnect(); @@ -969,7 +969,7 @@ export default function App() { <label> <input type="checkbox" checked={canMove} - onChange={e => setCanMove(e.target.checked)} + onChange={e => setCanMove(e.target.checked)} /> The dot is allowed to move </label> @@ -1027,7 +1027,7 @@ export default function App() { <label> <input type="checkbox" checked={canMove} - onChange={e => setCanMove(e.target.checked)} + onChange={e => setCanMove(e.target.checked)} /> The dot is allowed to move </label> @@ -1083,7 +1083,7 @@ export default function App() { <label> <input type="checkbox" checked={canMove} - onChange={e => setCanMove(e.target.checked)} + onChange={e => setCanMove(e.target.checked)} /> The dot is allowed to move </label> @@ -1155,7 +1155,7 @@ export default function App() { <label> <input type="checkbox" checked={canMove} - onChange={e => setCanMove(e.target.checked)} + onChange={e => setCanMove(e.target.checked)} /> The dot is allowed to move </label> @@ -1220,7 +1220,7 @@ export default function App() { <label> <input type="checkbox" checked={canMove} - onChange={e => setCanMove(e.target.checked)} + onChange={e => setCanMove(e.target.checked)} /> The dot is allowed to move </label> @@ -1279,7 +1279,7 @@ export default function App() { <label> <input type="checkbox" checked={canMove} - onChange={e => setCanMove(e.target.checked)} + onChange={e => setCanMove(e.target.checked)} /> The dot is allowed to move </label> @@ -1711,7 +1711,7 @@ async function fetchPlanets() { name: 'Venus' }, { id: 'mars', - name: 'Mars' + name: 'Mars' }]); }, 1000); }); @@ -1735,7 +1735,7 @@ async function fetchPlaces(planetId) { name: 'Spain' }, { id: 'vietnam', - name: 'Vietnam' + name: 'Vietnam' }]); } else if (planetId === 'venus') { resolve([{ @@ -1746,7 +1746,7 @@ async function fetchPlaces(planetId) { name: 'Diana Chasma' }, { id: 'kumsong-vallis', - name: 'Kŭmsŏng Vallis' + name: 'Kŭmsŏng Vallis' }]); } else if (planetId === 'mars') { resolve([{ @@ -1879,7 +1879,7 @@ async function fetchPlanets() { name: 'Venus' }, { id: 'mars', - name: 'Mars' + name: 'Mars' }]); }, 1000); }); @@ -1903,7 +1903,7 @@ async function fetchPlaces(planetId) { name: 'Spain' }, { id: 'vietnam', - name: 'Vietnam' + name: 'Vietnam' }]); } else if (planetId === 'venus') { resolve([{ @@ -1914,7 +1914,7 @@ async function fetchPlaces(planetId) { name: 'Diana Chasma' }, { id: 'kumsong-vallis', - name: 'Kŭmsŏng Vallis' + name: 'Kŭmsŏng Vallis' }]); } else if (planetId === 'mars') { resolve([{ @@ -2042,7 +2042,7 @@ async function fetchPlanets() { name: 'Venus' }, { id: 'mars', - name: 'Mars' + name: 'Mars' }]); }, 1000); }); @@ -2066,7 +2066,7 @@ async function fetchPlaces(planetId) { name: 'Spain' }, { id: 'vietnam', - name: 'Vietnam' + name: 'Vietnam' }]); } else if (planetId === 'venus') { resolve([{ @@ -2077,7 +2077,7 @@ async function fetchPlaces(planetId) { name: 'Diana Chasma' }, { id: 'kumsong-vallis', - name: 'Kŭmsŏng Vallis' + name: 'Kŭmsŏng Vallis' }]); } else if (planetId === 'mars') { resolve([{ diff --git a/src/content/learn/passing-data-deeply-with-context.md b/src/content/learn/passing-data-deeply-with-context.md index 1aea87b3..2ea44c42 100644 --- a/src/content/learn/passing-data-deeply-with-context.md +++ b/src/content/learn/passing-data-deeply-with-context.md @@ -857,7 +857,7 @@ If neither of these approaches works well for you, consider context. * **Current account:** Many components might need to know the currently logged in user. Putting it in context makes it convenient to read it anywhere in the tree. Some apps also let you operate multiple accounts at the same time (e.g. to leave a comment as a different user). In those cases, it can be convenient to wrap a part of the UI into a nested provider with a different current account value. * **Routing:** Most routing solutions use context internally to hold the current route. This is how every link "knows" whether it's active or not. If you build your own router, you might want to do it too. * **Managing state:** As your app grows, you might end up with a lot of state closer to the top of your app. Many distant components below may want to change it. It is common to [use a reducer together with context](/learn/scaling-up-with-reducer-and-context) to manage complex state and pass it down to distant components without too much hassle. - + Context is not limited to static values. If you pass a different value on the next render, React will update all the components reading it below! This is why context is often used in combination with state. In general, if some information is needed by distant components in different parts of the tree, it's a good indication that context will help you. @@ -963,27 +963,27 @@ export const places = [{ description: 'The tradition of choosing bright colors for houses began in the late 20th century.', imageId: 'K9HVAGH' }, { - id: 1, + id: 1, name: 'Rainbow Village in Taichung, Taiwan', description: 'To save the houses from demolition, Huang Yung-Fu, a local resident, painted all 1,200 of them in 1924.', imageId: '9EAYZrt' }, { - id: 2, + id: 2, name: 'Macromural de Pachuca, Mexico', description: 'One of the largest murals in the world covering homes in a hillside neighborhood.', imageId: 'DgXHVwu' }, { - id: 3, + id: 3, name: 'Selarón Staircase in Rio de Janeiro, Brazil', description: 'This landmark was created by Jorge Selarón, a Chilean-born artist, as a "tribute to the Brazilian people."', imageId: 'aeO3rpI' }, { - id: 4, + id: 4, name: 'Burano, Italy', description: 'The houses are painted following a specific color system dating back to 16th century.', imageId: 'kxsph5C' }, { - id: 5, + id: 5, name: 'Chefchaouen, Marocco', 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.', imageId: 'rTqKo46' @@ -998,7 +998,7 @@ export const places = [{ ```js src/utils.js export function getImageUrl(place) { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + place.imageId + 'l.jpg' ); @@ -1007,9 +1007,9 @@ export function getImageUrl(place) { ```css ul { list-style-type: none; padding: 0px 10px; } -li { - margin-bottom: 10px; - display: grid; +li { + margin-bottom: 10px; + display: grid; grid-template-columns: auto 1fr; gap: 20px; align-items: center; @@ -1102,27 +1102,27 @@ export const places = [{ description: 'The tradition of choosing bright colors for houses began in the late 20th century.', imageId: 'K9HVAGH' }, { - id: 1, + id: 1, name: 'Rainbow Village in Taichung, Taiwan', description: 'To save the houses from demolition, Huang Yung-Fu, a local resident, painted all 1,200 of them in 1924.', imageId: '9EAYZrt' }, { - id: 2, + id: 2, name: 'Macromural de Pachuca, Mexico', description: 'One of the largest murals in the world covering homes in a hillside neighborhood.', imageId: 'DgXHVwu' }, { - id: 3, + id: 3, name: 'Selarón Staircase in Rio de Janeiro, Brazil', description: 'This landmark was created by Jorge Selarón, a Chilean-born artist, as a "tribute to the Brazilian people".', imageId: 'aeO3rpI' }, { - id: 4, + id: 4, name: 'Burano, Italy', description: 'The houses are painted following a specific color system dating back to 16th century.', imageId: 'kxsph5C' }, { - id: 5, + id: 5, name: 'Chefchaouen, Marocco', 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.', imageId: 'rTqKo46' @@ -1137,7 +1137,7 @@ export const places = [{ ```js src/utils.js export function getImageUrl(place) { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + place.imageId + 'l.jpg' ); @@ -1146,9 +1146,9 @@ export function getImageUrl(place) { ```css ul { list-style-type: none; padding: 0px 10px; } -li { - margin-bottom: 10px; - display: grid; +li { + margin-bottom: 10px; + display: grid; grid-template-columns: auto 1fr; gap: 20px; align-items: center; diff --git a/src/content/learn/passing-props-to-a-component.md b/src/content/learn/passing-props-to-a-component.md index 82f19009..01fee5c7 100644 --- a/src/content/learn/passing-props-to-a-component.md +++ b/src/content/learn/passing-props-to-a-component.md @@ -29,7 +29,7 @@ function Avatar() { return ( <img className="avatar" - src="https://i.imgur.com/1bX5QH6.jpg" + src="https://react.dev/images/docs/scientists/1bX5QH6.jpg" alt="Lin Lanying" width={100} height={100} @@ -126,21 +126,21 @@ export default function Profile() { <div> <Avatar size={100} - person={{ - name: 'Katsuko Saruhashi', + person={{ + name: 'Katsuko Saruhashi', imageId: 'YfeOqp2' }} /> <Avatar size={80} person={{ - name: 'Aklilu Lemma', + name: 'Aklilu Lemma', imageId: 'OKS67lh' }} /> <Avatar size={50} - person={{ + person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }} @@ -153,7 +153,7 @@ export default function Profile() { ```js src/utils.js export function getImageUrl(person, size = 's') { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + size + '.jpg' @@ -291,7 +291,7 @@ export default function Profile() { <Card> <Avatar size={100} - person={{ + person={{ name: 'Katsuko Saruhashi', imageId: 'YfeOqp2' }} @@ -320,7 +320,7 @@ export default function Avatar({ person, size }) { ```js src/utils.js export function getImageUrl(person, size = 's') { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + size + '.jpg' @@ -454,11 +454,11 @@ export default function Gallery() { /> <ul> <li> - <b>Profession: </b> + <b>Profession: </b> physicist and chemist </li> <li> - <b>Awards: 4 </b> + <b>Awards: 4 </b> (Nobel Prize in Physics, Nobel Prize in Chemistry, Davy Medal, Matteucci Medal) </li> <li> @@ -478,11 +478,11 @@ export default function Gallery() { /> <ul> <li> - <b>Profession: </b> + <b>Profession: </b> geochemist </li> <li> - <b>Awards: 2 </b> + <b>Awards: 2 </b> (Miyake Prize for geochemistry, Tanaka Prize) </li> <li> @@ -499,7 +499,7 @@ export default function Gallery() { ```js src/utils.js export function getImageUrl(imageId, size = 's') { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + imageId + size + '.jpg' @@ -607,7 +607,7 @@ export default function Gallery() { ```js src/utils.js export function getImageUrl(imageId, size = 's') { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + imageId + size + '.jpg' @@ -704,7 +704,7 @@ export default function Gallery() { ```js src/utils.js export function getImageUrl(person, size = 's') { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + size + '.jpg' @@ -759,8 +759,8 @@ export default function Profile() { return ( <Avatar size={40} - person={{ - name: 'Gregorio Y. Zara', + person={{ + name: 'Gregorio Y. Zara', imageId: '7vQD0fP' }} /> @@ -771,7 +771,7 @@ export default function Profile() { ```js src/utils.js export function getImageUrl(person, size) { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + size + '.jpg' @@ -815,15 +815,15 @@ export default function Profile() { <> <Avatar size={40} - person={{ - name: 'Gregorio Y. Zara', + person={{ + name: 'Gregorio Y. Zara', imageId: '7vQD0fP' }} /> <Avatar size={120} - person={{ - name: 'Gregorio Y. Zara', + person={{ + name: 'Gregorio Y. Zara', imageId: '7vQD0fP' }} /> @@ -835,7 +835,7 @@ export default function Profile() { ```js src/utils.js export function getImageUrl(person, size) { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + size + '.jpg' @@ -879,22 +879,22 @@ export default function Profile() { <> <Avatar size={40} - person={{ - name: 'Gregorio Y. Zara', + person={{ + name: 'Gregorio Y. Zara', imageId: '7vQD0fP' }} /> <Avatar size={70} - person={{ - name: 'Gregorio Y. Zara', + person={{ + name: 'Gregorio Y. Zara', imageId: '7vQD0fP' }} /> <Avatar size={120} - person={{ - name: 'Gregorio Y. Zara', + person={{ + name: 'Gregorio Y. Zara', imageId: '7vQD0fP' }} /> @@ -906,7 +906,7 @@ export default function Profile() { ```js src/utils.js export function getImageUrl(person, size) { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + size + '.jpg' @@ -939,7 +939,7 @@ export default function Profile() { <h1>Photo</h1> <img className="avatar" - src="https://i.imgur.com/OKS67lhm.jpg" + src="https://react.dev/images/docs/scientists/OKS67lhm.jpg" alt="Aklilu Lemma" width={70} height={70} @@ -1012,7 +1012,7 @@ export default function Profile() { <h1>Photo</h1> <img className="avatar" - src="https://i.imgur.com/OKS67lhm.jpg" + src="https://react.dev/images/docs/scientists/OKS67lhm.jpg" alt="Aklilu Lemma" width={100} height={100} @@ -1074,7 +1074,7 @@ export default function Profile() { <Card title="Photo"> <img className="avatar" - src="https://i.imgur.com/OKS67lhm.jpg" + src="https://react.dev/images/docs/scientists/OKS67lhm.jpg" alt="Aklilu Lemma" width={100} height={100} diff --git a/src/content/learn/preserving-and-resetting-state.md b/src/content/learn/preserving-and-resetting-state.md index d3507184..31a9541c 100644 --- a/src/content/learn/preserving-and-resetting-state.md +++ b/src/content/learn/preserving-and-resetting-state.md @@ -86,7 +86,7 @@ label { </Sandpack> -Here's how these look as a tree: +Here's how these look as a tree: <DiagramGroup> @@ -186,7 +186,7 @@ export default function App() { return ( <div> <Counter /> - {showB && <Counter />} + {showB && <Counter />} <label> <input type="checkbox" @@ -288,9 +288,9 @@ export default function App() { return ( <div> {isFancy ? ( - <Counter isFancy={true} /> + <Counter isFancy={true} /> ) : ( - <Counter isFancy={false} /> + <Counter isFancy={false} /> )} <label> <input @@ -496,9 +496,9 @@ export default function App() { return ( <div> {isPaused ? ( - <p>See you later!</p> + <p>See you later!</p> ) : ( - <Counter /> + <Counter /> )} <label> <input @@ -596,7 +596,7 @@ export default function App() { <div> {isFancy ? ( <div> - <Counter isFancy={true} /> + <Counter isFancy={true} /> </div> ) : ( <section> @@ -1431,7 +1431,7 @@ export default function App() { if (reverse) { return ( <> - <Field label="Last name" /> + <Field label="Last name" /> <Field label="First name" /> {checkbox} </> @@ -1439,11 +1439,11 @@ export default function App() { } else { return ( <> - <Field label="First name" /> + <Field label="First name" /> <Field label="Last name" /> {checkbox} </> - ); + ); } } @@ -1493,7 +1493,7 @@ export default function App() { if (reverse) { return ( <> - <Field key="lastName" label="Last name" /> + <Field key="lastName" label="Last name" /> <Field key="firstName" label="First name" /> {checkbox} </> @@ -1501,11 +1501,11 @@ export default function App() { } else { return ( <> - <Field key="firstName" label="First name" /> + <Field key="firstName" label="First name" /> <Field key="lastName" label="Last name" /> {checkbox} </> - ); + ); } } @@ -1888,25 +1888,25 @@ export default function Gallery() { let images = [{ place: 'Penang, Malaysia', - src: 'https://i.imgur.com/FJeJR8M.jpg' + src: 'https://react.dev/images/docs/scientists/FJeJR8M.jpg' }, { place: 'Lisbon, Portugal', - src: 'https://i.imgur.com/dB2LRbj.jpg' + src: 'https://react.dev/images/docs/scientists/dB2LRbj.jpg' }, { place: 'Bilbao, Spain', - src: 'https://i.imgur.com/z08o2TS.jpg' + src: 'https://react.dev/images/docs/scientists/z08o2TS.jpg' }, { place: 'Valparaíso, Chile', - src: 'https://i.imgur.com/Y3utgTi.jpg' + src: 'https://react.dev/images/docs/scientists/Y3utgTi.jpg' }, { place: 'Schwyz, Switzerland', - src: 'https://i.imgur.com/JBbMpWY.jpg' + src: 'https://react.dev/images/docs/scientists/JBbMpWY.jpg' }, { place: 'Prague, Czechia', - src: 'https://i.imgur.com/QwUKKmF.jpg' + src: 'https://react.dev/images/docs/scientists/QwUKKmF.jpg' }, { place: 'Ljubljana, Slovenia', - src: 'https://i.imgur.com/3aIiwfm.jpg' + src: 'https://react.dev/images/docs/scientists/3aIiwfm.jpg' }]; ``` @@ -1956,25 +1956,25 @@ export default function Gallery() { let images = [{ place: 'Penang, Malaysia', - src: 'https://i.imgur.com/FJeJR8M.jpg' + src: 'https://react.dev/images/docs/scientists/FJeJR8M.jpg' }, { place: 'Lisbon, Portugal', - src: 'https://i.imgur.com/dB2LRbj.jpg' + src: 'https://react.dev/images/docs/scientists/dB2LRbj.jpg' }, { place: 'Bilbao, Spain', - src: 'https://i.imgur.com/z08o2TS.jpg' + src: 'https://react.dev/images/docs/scientists/z08o2TS.jpg' }, { place: 'Valparaíso, Chile', - src: 'https://i.imgur.com/Y3utgTi.jpg' + src: 'https://react.dev/images/docs/scientists/Y3utgTi.jpg' }, { place: 'Schwyz, Switzerland', - src: 'https://i.imgur.com/JBbMpWY.jpg' + src: 'https://react.dev/images/docs/scientists/JBbMpWY.jpg' }, { place: 'Prague, Czechia', - src: 'https://i.imgur.com/QwUKKmF.jpg' + src: 'https://react.dev/images/docs/scientists/QwUKKmF.jpg' }, { place: 'Ljubljana, Slovenia', - src: 'https://i.imgur.com/3aIiwfm.jpg' + src: 'https://react.dev/images/docs/scientists/3aIiwfm.jpg' }]; ``` diff --git a/src/content/learn/queueing-a-series-of-state-updates.md b/src/content/learn/queueing-a-series-of-state-updates.md index df710542..772fce77 100644 --- a/src/content/learn/queueing-a-series-of-state-updates.md +++ b/src/content/learn/queueing-a-series-of-state-updates.md @@ -177,7 +177,11 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } | "replace with `5`" | `0` (unused) | `5` | | `n => n + 1` | `5` | `5 + 1 = 6` | +<<<<<<< HEAD React ເກັບ `6` ເປັນຜົນລັບສຸດທ້າຍ ແລະ return ຈາກ `useState`. +======= +React stores `6` as the final result and returns it from `useState`. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a <Note> @@ -305,7 +309,7 @@ export default function RequestTracker() { Completed: {completed} </h3> <button onClick={handleClick}> - Buy + Buy </button> </> ); @@ -349,7 +353,7 @@ export default function RequestTracker() { Completed: {completed} </h3> <button onClick={handleClick}> - Buy + Buy </button> </> ); diff --git a/src/content/learn/reacting-to-input-with-state.md b/src/content/learn/reacting-to-input-with-state.md index 878ab0fc..507c4721 100644 --- a/src/content/learn/reacting-to-input-with-state.md +++ b/src/content/learn/reacting-to-input-with-state.md @@ -518,7 +518,7 @@ export default function Picture() { <img className="picture" alt="Rainbow houses in Kampung Pelangi, Indonesia" - src="https://i.imgur.com/5qwVYb1.jpeg" + src="https://react.dev/images/docs/scientists/5qwVYb1.jpeg" /> </div> ); @@ -593,7 +593,7 @@ export default function Picture() { }} className={pictureClassName} alt="Rainbow houses in Kampung Pelangi, Indonesia" - src="https://i.imgur.com/5qwVYb1.jpeg" + src="https://react.dev/images/docs/scientists/5qwVYb1.jpeg" /> </div> ); @@ -648,7 +648,7 @@ export default function Picture() { <img className="picture picture--active" alt="Rainbow houses in Kampung Pelangi, Indonesia" - src="https://i.imgur.com/5qwVYb1.jpeg" + src="https://react.dev/images/docs/scientists/5qwVYb1.jpeg" onClick={e => e.stopPropagation()} /> </div> @@ -659,7 +659,7 @@ export default function Picture() { <img className="picture" alt="Rainbow houses in Kampung Pelangi, Indonesia" - src="https://i.imgur.com/5qwVYb1.jpeg" + src="https://react.dev/images/docs/scientists/5qwVYb1.jpeg" onClick={() => setIsActive(true)} /> </div> diff --git a/src/content/learn/referencing-values-with-refs.md b/src/content/learn/referencing-values-with-refs.md index 4faf1878..60bba5e6 100644 --- a/src/content/learn/referencing-values-with-refs.md +++ b/src/content/learn/referencing-values-with-refs.md @@ -34,7 +34,7 @@ const ref = useRef(0); `useRef` returns an object like this: ```js -{ +{ current: 0 // The value you passed to useRef } ``` diff --git a/src/content/learn/removing-effect-dependencies.md b/src/content/learn/removing-effect-dependencies.md index 9a871c6c..dbd35388 100644 --- a/src/content/learn/removing-effect-dependencies.md +++ b/src/content/learn/removing-effect-dependencies.md @@ -411,7 +411,7 @@ function Form() { function handleSubmit() { setSubmitted(true); - } + } // ... } @@ -429,7 +429,7 @@ function Form() { // ✅ Good: Event-specific logic is called from event handlers post('/api/register'); showNotification('Successfully registered!', theme); - } + } // ... } @@ -884,7 +884,7 @@ const options2 = { serverUrl: 'https://localhost:1234', roomId: 'music' }; console.log(Object.is(options1, options2)); // false ``` -**Object and function dependencies can make your Effect re-synchronize more often than you need.** +**Object and function dependencies can make your Effect re-synchronize more often than you need.** This is why, whenever possible, you should try to avoid objects and functions as your Effect's dependencies. Instead, try moving them outside the component, inside the Effect, or extracting primitive values out of them. diff --git a/src/content/learn/render-and-commit.md b/src/content/learn/render-and-commit.md index 1e9e3165..1a24cdbe 100644 --- a/src/content/learn/render-and-commit.md +++ b/src/content/learn/render-and-commit.md @@ -56,7 +56,7 @@ root.render(<Image />); export default function Image() { return ( <img - src="https://i.imgur.com/ZF6s192.jpg" + src="https://react.dev/images/docs/scientists/ZF6s192.jpg" alt="'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals" /> ); @@ -105,7 +105,7 @@ export default function Gallery() { function Image() { return ( <img - src="https://i.imgur.com/ZF6s192.jpg" + src="https://react.dev/images/docs/scientists/ZF6s192.jpg" alt="'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals" /> ); @@ -126,8 +126,13 @@ img { margin: 0 10px 10px 0; } </Sandpack> +<<<<<<< HEAD * **ລະຫວ່າງການ render ເລີ່ມຕົ້ນ,** React ຈະ [ສ້າງ DOM nodes](https://developer.mozilla.org/docs/Web/API/Document/createElement) ສຳລັບ `<section>`, `<h1>`, ແລະ ສາມແທັກ `<img>`. * **ລະຫວ່າງການ render ໃໝ່,** React ຈະຄຳນວນວ່າ property ໃດຂອງພວກມັນ (ຖ້າມີ) ມີການປ່ຽນແປງຕັ້ງແຕ່ການ render ຄັ້ງກ່ອນ. ມັນຈະບໍ່ເຮັດຫຍັງກັບຂໍ້ມູນນັ້ນຈົນກວ່າຈະເຖິງຂັ້ນຕອນຕໍ່ໄປ, ຂັ້ນຕອນການ commit. +======= +* **During the initial render,** React will [create the DOM nodes](https://developer.mozilla.org/docs/Web/API/Document/createElement) for `<section>`, `<h1>`, and three `<img>` tags. +* **During a re-render,** React will calculate which of their properties, if any, have changed since the previous render. It won't do anything with that information until the next step, the commit phase. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a <Pitfall> diff --git a/src/content/learn/rendering-lists.md b/src/content/learn/rendering-lists.md index a7116701..b4ce75ba 100644 --- a/src/content/learn/rendering-lists.md +++ b/src/content/learn/rendering-lists.md @@ -114,7 +114,7 @@ const people = [{ profession: 'physicist', }, { name: 'Percy Lavon Julian', - profession: 'chemist', + profession: 'chemist', }, { name: 'Subrahmanyan Chandrasekhar', profession: 'astrophysicist', @@ -221,7 +221,7 @@ export const people = [{ ```js src/utils.js export function getImageUrl(person) { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + 's.jpg' ); @@ -230,9 +230,9 @@ export function getImageUrl(person) { ```css ul { list-style-type: none; padding: 0px 10px; } -li { - margin-bottom: 10px; - display: grid; +li { + margin-bottom: 10px; + display: grid; grid-template-columns: auto 1fr; gap: 20px; align-items: center; @@ -351,7 +351,7 @@ export const people = [{ ```js src/utils.js export function getImageUrl(person) { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + 's.jpg' ); @@ -360,9 +360,9 @@ export function getImageUrl(person) { ```css ul { list-style-type: none; padding: 0px 10px; } -li { - margin-bottom: 10px; - display: grid; +li { + margin-bottom: 10px; + display: grid; grid-template-columns: auto 1fr; gap: 20px; align-items: center; @@ -512,7 +512,7 @@ export const people = [{ ```js src/utils.js export function getImageUrl(person) { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + 's.jpg' ); @@ -627,7 +627,7 @@ export const people = [{ ```js src/utils.js export function getImageUrl(person) { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + 's.jpg' ); @@ -741,7 +741,7 @@ export const people = [{ ```js src/utils.js export function getImageUrl(person) { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + 's.jpg' ); @@ -859,7 +859,7 @@ export const people = [{ ```js src/utils.js export function getImageUrl(person) { return ( - 'https://i.imgur.com/' + + 'https://react.dev/images/docs/scientists/' + person.imageId + 's.jpg' ); diff --git a/src/content/learn/responding-to-events.md b/src/content/learn/responding-to-events.md index 15a495a1..3abafaed 100644 --- a/src/content/learn/responding-to-events.md +++ b/src/content/learn/responding-to-events.md @@ -169,7 +169,11 @@ button { margin-right: 10px; } ### ການສົ່ງ event handlers ເປັນ props {/*passing-event-handlers-as-props*/} +<<<<<<< HEAD ຫຼາຍເທື່ອທີ່ທ່ານຕ້ອງການໃຫ້ parent component ລະບຸ child event handler. ຂຶ້ນຢູ່ກັບປຸ່ມ: ຂຶ້ນຢູ່ກັບວ່າທ່ານໃຊ້ component `Button` ບ່ອນໃດ, ທ່ານອາດຈ້ອງ execute ຟັງຊັ່ນອື່ນ ບາງເທື່ອຟັງຊັ່ນໜຶ່ງຫຼິ້ນວີດີໂອ ແລະ ອີກຟັງຊັ່ນໜໜຶ່ງອັບໂຫຼດຮູບພາບ. +======= +Often you'll want the parent component to specify a child's event handler. Consider buttons: depending on where you're using a `Button` component, you might want to execute a different function—perhaps one plays a movie and another uploads an image. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ເພື່ອເຮັດສິ່ງນີ້, ໃຫ້ສົ່ງ prop ທີ່ component ໄດ້ຮັບຈາກ parent ເປັນ event handler ດັ່ງນີ້: @@ -312,12 +316,21 @@ button { margin-right: 10px; } </Sandpack> +<<<<<<< HEAD ສັງເກດວ່າ component `App` ບໍ່ຈຳເປັນຕ້ອງຮູ້ວ່າ *ສິ່ງທີ່* `Toolbar` ຈະເຮັດຫຍັງກັບ `onPlayMovie` ຫຼື `onUploadImage`. ນັ້ນແມ່ນລາຍລະອຽດການ implementation ຂອງ `Toolbar`. ໃນນີ້, `Toolbar` ສົ່ງຜ່ານ `onClick` handler ໄປຍັງ `Button` ຂອງມັນ, ແຕ່ພາຍຫຼັງຍັງສາມາດ trigger ເທິງທາງລັດຄີບອດ. ການຕັ້ງຊື່ prop ຕາມການ interaction ສະເພາະແອັບເຊັ່ນ `onPlayMovie` ຊ່ວຍໃຫ້ທ່ານປ່ຽນວິທີໃຊ້ງານໃນພາຍຫຼັງໄດ້ຢ່າງຍືດຫຍຸ່ນ. <Note> ກວດສອບວ່າທ່ານໃຊ້ແທັກ HTML ທີ່ເໝາະສົມສຳລັບ event handler. ຕົວຢ່າງ, ເພື່ອຈັດການຄິກ, ໃຊ້ [`<button onClick={handleClick}>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) ແທນ `<div onClick={handleClick}>`. ການໃຊ້ບາວເຊີແທ້ `<button>` ເປີດໃຊ້ງານລັກສະນະການເຮັດວຽກຂອງ built-in browser ເຊັ່ນການນຳທາງດ້ວຍຄີບອດ. ຖ້າທ່ານບໍ່ມັກ style ບາວເຊີເລີ່ມຕົ້ນຂອງປຸ່ມ ແລະ ຕ້ອງການເຮັດໃຫ້ມັນຄືລີ້ງ ຫຼື UI element, ທ່ານສາມາດເຮັດໄດ້ດ້ວຍ CSS. [ຮຽນຮູ້ເພີມເຕີມກ່ຽວກັບການຂຽນ accessible markup.](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) +======= +Notice how the `App` component does not need to know *what* `Toolbar` will do with `onPlayMovie` or `onUploadImage`. That's an implementation detail of the `Toolbar`. Here, `Toolbar` passes them down as `onClick` handlers to its `Button`s, but it could later also trigger them on a keyboard shortcut. Naming props after app-specific interactions like `onPlayMovie` gives you the flexibility to change how they're used later. + +<Note> + +Make sure that you use the appropriate HTML tags for your event handlers. For example, to handle clicks, use [`<button onClick={handleClick}>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) instead of `<div onClick={handleClick}>`. Using a real browser `<button>` enables built-in browser behaviors like keyboard navigation. If you don't like the default browser styling of a button and want to make it look more like a link or a different UI element, you can achieve it with CSS. [Learn more about writing accessible markup.](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) + +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a </Note> ## ການເຜີຍແຜ່ Event {/*event-propagation*/} @@ -411,12 +424,21 @@ button { margin: 5px; } ເມື່ອທ່ານຄິກທີ່ປຸ່ມ: +<<<<<<< HEAD 1. React ເອີ້ນໃຊ້ `onClick` handler ທີ່ສົ່ງຜ່ານໄປຍັງ `<button>`. 2. Handler, ຖືກກຳນົດໃນ `Button`, ເຮັດສິ່ງຕໍ່ໄປນີ້: * ເອີ້ນ `e.stopPropagation()`, ເພື່ອປ້ອງກັນບໍ່ໃຫ້ event ເດືອດອີກຕໍ່ໄປ. * ເອີ້ນໃຊ້ຟັງຊັ່ນ `onClick`, ເຊິ່ງເປັນ prop ທີ່ສົ່ງຜ່ານຈາກ component `Toolbar` 3. ຟັງຊັ່ນນັ້ນເຊິ່ງກຳນົດໄວ້ໃນ component `Toolbar`, ສະແດງການແຈ້ງເຕືອນຂອງປຸ່ມເອງ. 4. ເນື່ອງຈາກການເຜີດແຜ່ຢຸດລົງ, `onClick` handler ຂອງ parent `<div>` ຈຶ່ງ *ບໍ່* ເຮັດວຽກ. +======= +1. React calls the `onClick` handler passed to `<button>`. +2. That handler, defined in `Button`, does the following: + * Calls `e.stopPropagation()`, preventing the event from bubbling further. + * Calls the `onClick` function, which is a prop passed from the `Toolbar` component. +3. That function, defined in the `Toolbar` component, displays the button's own alert. +4. Since the propagation was stopped, the parent `<div>`'s `onClick` handler does *not* run. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ຈາກຜົນຂອງ `e.stopPropagation()`, ຕອນນີ້ການຄິກທີ່ປຸ່ມຈະສະແດງພຽງການແຈ້ງເຕືອນດຽວ (ຈາກ `<button>`) ແທນທີ່ຈະເປັນສອງປຸ່ມ (ຈາກ `<botton>` ແລະ parent toobar `<div>`). ການຄິກປຸ່ມບໍ່ຄືກັບການຄິກ toolbar ຮອບໆ, ສະນັ້ນການຢຸດເຜີຍແຜ່ຈິງເໝາະສົມສຳລັບ UI ນີ້. @@ -433,11 +455,19 @@ button { margin: 5px; } </div> ``` +<<<<<<< HEAD ແຕ່ລະ event ເຜີຍແຜ່ໃນສາມຂັ້ນຕອນ: 1. ມັນເຄື່ອນລົງມາ, ໂດຍການເອີ້ນ `onClickCapture` handler. 2. ມັນແລ່ນ `onClick` handler ຂອງ element ການຄິກ. 3. ມັນເຄື່ອນທີ່ຂຶ້ນທາງເທິງ, ເອີ້ນ `onClick` handler ທັງໝົດ. +======= +Each event propagates in three phases: + +1. It travels down, calling all `onClickCapture` handlers. +2. It runs the clicked element's `onClick` handler. +3. It travels upwards, calling all `onClick` handlers. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a Event Capture ມີປະໂຫຍດສຳລັບ code ເຊັ່ນ router ຫຼື ການວິເຄາະ, ແຕ່ທ່ານອາດບໍ່ແມ່ນ event ເຫຼົ່ານັ້ນໃນ code ຂອງແອັບ. diff --git a/src/content/learn/scaling-up-with-reducer-and-context.md b/src/content/learn/scaling-up-with-reducer-and-context.md index c3da0c63..cd159de0 100644 --- a/src/content/learn/scaling-up-with-reducer-and-context.md +++ b/src/content/learn/scaling-up-with-reducer-and-context.md @@ -801,7 +801,7 @@ export default function AddTask() { type: 'added', id: nextId++, text: text, - }); + }); }}>Add</button> </> ); @@ -1025,7 +1025,7 @@ export default function AddTask() { type: 'added', id: nextId++, text: text, - }); + }); }}>Add</button> </> ); @@ -1244,7 +1244,7 @@ export default function AddTask() { type: 'added', id: nextId++, text: text, - }); + }); }}>Add</button> </> ); diff --git a/src/content/learn/sharing-state-between-components.md b/src/content/learn/sharing-state-between-components.md index 52eaf28f..2fbef891 100644 --- a/src/content/learn/sharing-state-between-components.md +++ b/src/content/learn/sharing-state-between-components.md @@ -323,7 +323,7 @@ To see what this feels like in practice with a few more components, read [Thinki #### Synced inputs {/*synced-inputs*/} -These two inputs are independent. Make them stay in sync: editing one input should update the other input with the same text, and vice versa. +These two inputs are independent. Make them stay in sync: editing one input should update the other input with the same text, and vice versa. <Hint> @@ -571,7 +571,7 @@ function SearchBar({ query, onChange }) { function List({ items }) { return ( <table> - <tbody> + <tbody> {items.map(food => ( <tr key={food.id}> <td>{food.name}</td> diff --git a/src/content/learn/state-a-components-memory.md b/src/content/learn/state-a-components-memory.md index fdaa8b42..f015743e 100644 --- a/src/content/learn/state-a-components-memory.md +++ b/src/content/learn/state-a-components-memory.md @@ -40,14 +40,14 @@ export default function Gallery() { Next </button> <h2> - <i>{sculpture.name} </i> + <i>{sculpture.name} </i> by {sculpture.artist} </h2> - <h3> + <h3> ({index + 1} of {sculptureList.length}) </h3> - <img - src={sculpture.url} + <img + src={sculpture.url} alt={sculpture.alt} /> <p> @@ -63,73 +63,73 @@ export const sculptureList = [{ name: 'Homenaje a la Neurocirugía', artist: 'Marta Colvin Andrade', 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.', - url: 'https://i.imgur.com/Mx7dA2Y.jpg', - alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' + url: 'https://react.dev/images/docs/scientists/Mx7dA2Y.jpg', + alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' }, { name: 'Floralis Genérica', artist: 'Eduardo Catalano', 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.', - url: 'https://i.imgur.com/ZF6s192m.jpg', + url: 'https://react.dev/images/docs/scientists/ZF6s192m.jpg', alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.' }, { name: 'Eternal Presence', artist: 'John Woodrow Wilson', 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."', - url: 'https://i.imgur.com/aTtVpES.jpg', + url: 'https://react.dev/images/docs/scientists/aTtVpES.jpg', alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.' }, { name: 'Moai', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/RCwLEoQm.jpg', + url: 'https://react.dev/images/docs/scientists/RCwLEoQm.jpg', alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.' }, { name: 'Blue Nana', artist: 'Niki de Saint Phalle', 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.', - url: 'https://i.imgur.com/Sd1AgUOm.jpg', + url: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.' }, { name: 'Ultimate Form', artist: 'Barbara Hepworth', 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.', - url: 'https://i.imgur.com/2heNQDcm.jpg', + url: 'https://react.dev/images/docs/scientists/2heNQDcm.jpg', alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.' }, { name: 'Cavaliere', artist: 'Lamidi Olonade Fakeye', description: "Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.", - url: 'https://i.imgur.com/wIdGuZwm.png', + url: 'https://react.dev/images/docs/scientists/wIdGuZwm.png', alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.' }, { name: 'Big Bellies', artist: 'Alina Szapocznikow', 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.", - url: 'https://i.imgur.com/AlHTAdDm.jpg', + url: 'https://react.dev/images/docs/scientists/AlHTAdDm.jpg', alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.' }, { name: 'Terracotta Army', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/HMFmH6m.jpg', + url: 'https://react.dev/images/docs/scientists/HMFmH6m.jpg', alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.' }, { name: 'Lunar Landscape', artist: 'Louise Nevelson', 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.', - url: 'https://i.imgur.com/rN7hY6om.jpg', + url: 'https://react.dev/images/docs/scientists/rN7hY6om.jpg', alt: 'A black matte sculpture where the individual elements are initially indistinguishable.' }, { name: 'Aureole', artist: 'Ranjani Shettar', 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."', - url: 'https://i.imgur.com/okTpbHhm.jpg', + url: 'https://react.dev/images/docs/scientists/okTpbHhm.jpg', alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.' }, { name: 'Hippos', artist: 'Taipei Zoo', description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.', - url: 'https://i.imgur.com/6o5Vuyu.jpg', + url: 'https://react.dev/images/docs/scientists/6o5Vuyu.jpg', alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.' }]; ``` @@ -220,14 +220,14 @@ export default function Gallery() { Next </button> <h2> - <i>{sculpture.name} </i> + <i>{sculpture.name} </i> by {sculpture.artist} </h2> - <h3> + <h3> ({index + 1} of {sculptureList.length}) </h3> - <img - src={sculpture.url} + <img + src={sculpture.url} alt={sculpture.alt} /> <p> @@ -243,73 +243,73 @@ export const sculptureList = [{ name: 'Homenaje a la Neurocirugía', artist: 'Marta Colvin Andrade', 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.', - url: 'https://i.imgur.com/Mx7dA2Y.jpg', - alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' + url: 'https://react.dev/images/docs/scientists/Mx7dA2Y.jpg', + alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' }, { name: 'Floralis Genérica', artist: 'Eduardo Catalano', 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.', - url: 'https://i.imgur.com/ZF6s192m.jpg', + url: 'https://react.dev/images/docs/scientists/ZF6s192m.jpg', alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.' }, { name: 'Eternal Presence', artist: 'John Woodrow Wilson', 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."', - url: 'https://i.imgur.com/aTtVpES.jpg', + url: 'https://react.dev/images/docs/scientists/aTtVpES.jpg', alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.' }, { name: 'Moai', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/RCwLEoQm.jpg', + url: 'https://react.dev/images/docs/scientists/RCwLEoQm.jpg', alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.' }, { name: 'Blue Nana', artist: 'Niki de Saint Phalle', 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.', - url: 'https://i.imgur.com/Sd1AgUOm.jpg', + url: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.' }, { name: 'Ultimate Form', artist: 'Barbara Hepworth', 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.', - url: 'https://i.imgur.com/2heNQDcm.jpg', + url: 'https://react.dev/images/docs/scientists/2heNQDcm.jpg', alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.' }, { name: 'Cavaliere', artist: 'Lamidi Olonade Fakeye', description: "Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.", - url: 'https://i.imgur.com/wIdGuZwm.png', + url: 'https://react.dev/images/docs/scientists/wIdGuZwm.png', alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.' }, { name: 'Big Bellies', artist: 'Alina Szapocznikow', 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.", - url: 'https://i.imgur.com/AlHTAdDm.jpg', + url: 'https://react.dev/images/docs/scientists/AlHTAdDm.jpg', alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.' }, { name: 'Terracotta Army', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/HMFmH6m.jpg', + url: 'https://react.dev/images/docs/scientists/HMFmH6m.jpg', alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.' }, { name: 'Lunar Landscape', artist: 'Louise Nevelson', 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.', - url: 'https://i.imgur.com/rN7hY6om.jpg', + url: 'https://react.dev/images/docs/scientists/rN7hY6om.jpg', alt: 'A black matte sculpture where the individual elements are initially indistinguishable.' }, { name: 'Aureole', artist: 'Ranjani Shettar', 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."', - url: 'https://i.imgur.com/okTpbHhm.jpg', + url: 'https://react.dev/images/docs/scientists/okTpbHhm.jpg', alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.' }, { name: 'Hippos', artist: 'Taipei Zoo', description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.', - url: 'https://i.imgur.com/6o5Vuyu.jpg', + url: 'https://react.dev/images/docs/scientists/6o5Vuyu.jpg', alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.' }]; ``` @@ -361,7 +361,11 @@ Convention ແມ່ນການຕັ້ງຊື່ຄູ່ນີ້ວ່າ </Note> +<<<<<<< HEAD Argument ດຽວສຳລັບ `useState` ແມ່ນ **ຄ່າເລີ່ມຕົ້ນ** ຂອງຕົວແປ state ຂອງທ່ານ. ໃນຕົວຢ່າງນີ້, ຄ່າເລີ່ມຕົ້ນຂອງ `index` ຖືກກຳນົດເປັນ `0` ດ້ວຍ `useState(0)`. +======= +The only argument to `useState` is the **initial value** of your state variable. In this example, the `index`'s initial value is set to `0` with `useState(0)`. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ທຸກຄັ້ງທີ່ component ຂອງທ່ານ render, `useState` ຈະໃຫ້ array ທີມີສອງຄ່າ: @@ -408,18 +412,18 @@ export default function Gallery() { Next </button> <h2> - <i>{sculpture.name} </i> + <i>{sculpture.name} </i> by {sculpture.artist} </h2> - <h3> + <h3> ({index + 1} of {sculptureList.length}) </h3> <button onClick={handleMoreClick}> {showMore ? 'Hide' : 'Show'} details </button> {showMore && <p>{sculpture.description}</p>} - <img - src={sculpture.url} + <img + src={sculpture.url} alt={sculpture.alt} /> </> @@ -432,73 +436,73 @@ export const sculptureList = [{ name: 'Homenaje a la Neurocirugía', artist: 'Marta Colvin Andrade', 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.', - url: 'https://i.imgur.com/Mx7dA2Y.jpg', - alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' + url: 'https://react.dev/images/docs/scientists/Mx7dA2Y.jpg', + alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' }, { name: 'Floralis Genérica', artist: 'Eduardo Catalano', 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.', - url: 'https://i.imgur.com/ZF6s192m.jpg', + url: 'https://react.dev/images/docs/scientists/ZF6s192m.jpg', alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.' }, { name: 'Eternal Presence', artist: 'John Woodrow Wilson', 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."', - url: 'https://i.imgur.com/aTtVpES.jpg', + url: 'https://react.dev/images/docs/scientists/aTtVpES.jpg', alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.' }, { name: 'Moai', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/RCwLEoQm.jpg', + url: 'https://react.dev/images/docs/scientists/RCwLEoQm.jpg', alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.' }, { name: 'Blue Nana', artist: 'Niki de Saint Phalle', 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.', - url: 'https://i.imgur.com/Sd1AgUOm.jpg', + url: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.' }, { name: 'Ultimate Form', artist: 'Barbara Hepworth', 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.', - url: 'https://i.imgur.com/2heNQDcm.jpg', + url: 'https://react.dev/images/docs/scientists/2heNQDcm.jpg', alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.' }, { name: 'Cavaliere', artist: 'Lamidi Olonade Fakeye', description: "Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.", - url: 'https://i.imgur.com/wIdGuZwm.png', + url: 'https://react.dev/images/docs/scientists/wIdGuZwm.png', alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.' }, { name: 'Big Bellies', artist: 'Alina Szapocznikow', 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.", - url: 'https://i.imgur.com/AlHTAdDm.jpg', + url: 'https://react.dev/images/docs/scientists/AlHTAdDm.jpg', alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.' }, { name: 'Terracotta Army', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/HMFmH6m.jpg', + url: 'https://react.dev/images/docs/scientists/HMFmH6m.jpg', alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.' }, { name: 'Lunar Landscape', artist: 'Louise Nevelson', 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.', - url: 'https://i.imgur.com/rN7hY6om.jpg', + url: 'https://react.dev/images/docs/scientists/rN7hY6om.jpg', alt: 'A black matte sculpture where the individual elements are initially indistinguishable.' }, { name: 'Aureole', artist: 'Ranjani Shettar', 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."', - url: 'https://i.imgur.com/okTpbHhm.jpg', + url: 'https://react.dev/images/docs/scientists/okTpbHhm.jpg', alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.' }, { name: 'Hippos', artist: 'Taipei Zoo', description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.', - url: 'https://i.imgur.com/6o5Vuyu.jpg', + url: 'https://react.dev/images/docs/scientists/6o5Vuyu.jpg', alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.' }]; ``` @@ -628,73 +632,73 @@ let sculptureList = [{ name: 'Homenaje a la Neurocirugía', artist: 'Marta Colvin Andrade', 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.', - url: 'https://i.imgur.com/Mx7dA2Y.jpg', - alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' + url: 'https://react.dev/images/docs/scientists/Mx7dA2Y.jpg', + alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' }, { name: 'Floralis Genérica', artist: 'Eduardo Catalano', 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.', - url: 'https://i.imgur.com/ZF6s192m.jpg', + url: 'https://react.dev/images/docs/scientists/ZF6s192m.jpg', alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.' }, { name: 'Eternal Presence', artist: 'John Woodrow Wilson', 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."', - url: 'https://i.imgur.com/aTtVpES.jpg', + url: 'https://react.dev/images/docs/scientists/aTtVpES.jpg', alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.' }, { name: 'Moai', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/RCwLEoQm.jpg', + url: 'https://react.dev/images/docs/scientists/RCwLEoQm.jpg', alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.' }, { name: 'Blue Nana', artist: 'Niki de Saint Phalle', 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.', - url: 'https://i.imgur.com/Sd1AgUOm.jpg', + url: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.' }, { name: 'Ultimate Form', artist: 'Barbara Hepworth', 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.', - url: 'https://i.imgur.com/2heNQDcm.jpg', + url: 'https://react.dev/images/docs/scientists/2heNQDcm.jpg', alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.' }, { name: 'Cavaliere', artist: 'Lamidi Olonade Fakeye', description: "Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.", - url: 'https://i.imgur.com/wIdGuZwm.png', + url: 'https://react.dev/images/docs/scientists/wIdGuZwm.png', alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.' }, { name: 'Big Bellies', artist: 'Alina Szapocznikow', 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.", - url: 'https://i.imgur.com/AlHTAdDm.jpg', + url: 'https://react.dev/images/docs/scientists/AlHTAdDm.jpg', alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.' }, { name: 'Terracotta Army', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/HMFmH6m.jpg', + url: 'https://react.dev/images/docs/scientists/HMFmH6m.jpg', alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.' }, { name: 'Lunar Landscape', artist: 'Louise Nevelson', 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.', - url: 'https://i.imgur.com/rN7hY6om.jpg', + url: 'https://react.dev/images/docs/scientists/rN7hY6om.jpg', alt: 'A black matte sculpture where the individual elements are initially indistinguishable.' }, { name: 'Aureole', artist: 'Ranjani Shettar', 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."', - url: 'https://i.imgur.com/okTpbHhm.jpg', + url: 'https://react.dev/images/docs/scientists/okTpbHhm.jpg', alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.' }, { name: 'Hippos', artist: 'Taipei Zoo', description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.', - url: 'https://i.imgur.com/6o5Vuyu.jpg', + url: 'https://react.dev/images/docs/scientists/6o5Vuyu.jpg', alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.' }]; @@ -773,18 +777,18 @@ export default function Gallery() { Next </button> <h2> - <i>{sculpture.name} </i> + <i>{sculpture.name} </i> by {sculpture.artist} </h2> - <h3> + <h3> ({index + 1} of {sculptureList.length}) </h3> <button onClick={handleMoreClick}> {showMore ? 'Hide' : 'Show'} details </button> {showMore && <p>{sculpture.description}</p>} - <img - src={sculpture.url} + <img + src={sculpture.url} alt={sculpture.alt} /> </section> @@ -797,73 +801,73 @@ export const sculptureList = [{ name: 'Homenaje a la Neurocirugía', artist: 'Marta Colvin Andrade', 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.', - url: 'https://i.imgur.com/Mx7dA2Y.jpg', - alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' + url: 'https://react.dev/images/docs/scientists/Mx7dA2Y.jpg', + alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' }, { name: 'Floralis Genérica', artist: 'Eduardo Catalano', 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.', - url: 'https://i.imgur.com/ZF6s192m.jpg', + url: 'https://react.dev/images/docs/scientists/ZF6s192m.jpg', alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.' }, { name: 'Eternal Presence', artist: 'John Woodrow Wilson', 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."', - url: 'https://i.imgur.com/aTtVpES.jpg', + url: 'https://react.dev/images/docs/scientists/aTtVpES.jpg', alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.' }, { name: 'Moai', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/RCwLEoQm.jpg', + url: 'https://react.dev/images/docs/scientists/RCwLEoQm.jpg', alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.' }, { name: 'Blue Nana', artist: 'Niki de Saint Phalle', 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.', - url: 'https://i.imgur.com/Sd1AgUOm.jpg', + url: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.' }, { name: 'Ultimate Form', artist: 'Barbara Hepworth', 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.', - url: 'https://i.imgur.com/2heNQDcm.jpg', + url: 'https://react.dev/images/docs/scientists/2heNQDcm.jpg', alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.' }, { name: 'Cavaliere', artist: 'Lamidi Olonade Fakeye', description: "Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.", - url: 'https://i.imgur.com/wIdGuZwm.png', + url: 'https://react.dev/images/docs/scientists/wIdGuZwm.png', alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.' }, { name: 'Big Bellies', artist: 'Alina Szapocznikow', 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.", - url: 'https://i.imgur.com/AlHTAdDm.jpg', + url: 'https://react.dev/images/docs/scientists/AlHTAdDm.jpg', alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.' }, { name: 'Terracotta Army', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/HMFmH6m.jpg', + url: 'https://react.dev/images/docs/scientists/HMFmH6m.jpg', alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.' }, { name: 'Lunar Landscape', artist: 'Louise Nevelson', 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.', - url: 'https://i.imgur.com/rN7hY6om.jpg', + url: 'https://react.dev/images/docs/scientists/rN7hY6om.jpg', alt: 'A black matte sculpture where the individual elements are initially indistinguishable.' }, { name: 'Aureole', artist: 'Ranjani Shettar', 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."', - url: 'https://i.imgur.com/okTpbHhm.jpg', + url: 'https://react.dev/images/docs/scientists/okTpbHhm.jpg', alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.' }, { name: 'Hippos', artist: 'Taipei Zoo', description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.', - url: 'https://i.imgur.com/6o5Vuyu.jpg', + url: 'https://react.dev/images/docs/scientists/6o5Vuyu.jpg', alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.' }]; ``` @@ -944,18 +948,18 @@ export default function Gallery() { Next </button> <h2> - <i>{sculpture.name} </i> + <i>{sculpture.name} </i> by {sculpture.artist} </h2> - <h3> + <h3> ({index + 1} of {sculptureList.length}) </h3> <button onClick={handleMoreClick}> {showMore ? 'Hide' : 'Show'} details </button> {showMore && <p>{sculpture.description}</p>} - <img - src={sculpture.url} + <img + src={sculpture.url} alt={sculpture.alt} /> </> @@ -968,73 +972,73 @@ export const sculptureList = [{ name: 'Homenaje a la Neurocirugía', artist: 'Marta Colvin Andrade', 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.', - url: 'https://i.imgur.com/Mx7dA2Y.jpg', - alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' + url: 'https://react.dev/images/docs/scientists/Mx7dA2Y.jpg', + alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' }, { name: 'Floralis Genérica', artist: 'Eduardo Catalano', 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.', - url: 'https://i.imgur.com/ZF6s192m.jpg', + url: 'https://react.dev/images/docs/scientists/ZF6s192m.jpg', alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.' }, { name: 'Eternal Presence', artist: 'John Woodrow Wilson', 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."', - url: 'https://i.imgur.com/aTtVpES.jpg', + url: 'https://react.dev/images/docs/scientists/aTtVpES.jpg', alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.' }, { name: 'Moai', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/RCwLEoQm.jpg', + url: 'https://react.dev/images/docs/scientists/RCwLEoQm.jpg', alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.' }, { name: 'Blue Nana', artist: 'Niki de Saint Phalle', 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.', - url: 'https://i.imgur.com/Sd1AgUOm.jpg', + url: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.' }, { name: 'Ultimate Form', artist: 'Barbara Hepworth', 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.', - url: 'https://i.imgur.com/2heNQDcm.jpg', + url: 'https://react.dev/images/docs/scientists/2heNQDcm.jpg', alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.' }, { name: 'Cavaliere', artist: 'Lamidi Olonade Fakeye', description: "Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.", - url: 'https://i.imgur.com/wIdGuZwm.png', + url: 'https://react.dev/images/docs/scientists/wIdGuZwm.png', alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.' }, { name: 'Big Bellies', artist: 'Alina Szapocznikow', 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.", - url: 'https://i.imgur.com/AlHTAdDm.jpg', + url: 'https://react.dev/images/docs/scientists/AlHTAdDm.jpg', alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.' }, { name: 'Terracotta Army', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/HMFmH6m.jpg', + url: 'https://react.dev/images/docs/scientists/HMFmH6m.jpg', alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.' }, { name: 'Lunar Landscape', artist: 'Louise Nevelson', 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.', - url: 'https://i.imgur.com/rN7hY6om.jpg', + url: 'https://react.dev/images/docs/scientists/rN7hY6om.jpg', alt: 'A black matte sculpture where the individual elements are initially indistinguishable.' }, { name: 'Aureole', artist: 'Ranjani Shettar', 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."', - url: 'https://i.imgur.com/okTpbHhm.jpg', + url: 'https://react.dev/images/docs/scientists/okTpbHhm.jpg', alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.' }, { name: 'Hippos', artist: 'Taipei Zoo', description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.', - url: 'https://i.imgur.com/6o5Vuyu.jpg', + url: 'https://react.dev/images/docs/scientists/6o5Vuyu.jpg', alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.' }]; ``` @@ -1106,18 +1110,18 @@ export default function Gallery() { Next </button> <h2> - <i>{sculpture.name} </i> + <i>{sculpture.name} </i> by {sculpture.artist} </h2> - <h3> + <h3> ({index + 1} of {sculptureList.length}) </h3> <button onClick={handleMoreClick}> {showMore ? 'Hide' : 'Show'} details </button> {showMore && <p>{sculpture.description}</p>} - <img - src={sculpture.url} + <img + src={sculpture.url} alt={sculpture.alt} /> </> @@ -1130,73 +1134,73 @@ export const sculptureList = [{ name: 'Homenaje a la Neurocirugía', artist: 'Marta Colvin Andrade', 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.', - url: 'https://i.imgur.com/Mx7dA2Y.jpg', - alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' + url: 'https://react.dev/images/docs/scientists/Mx7dA2Y.jpg', + alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' }, { name: 'Floralis Genérica', artist: 'Eduardo Catalano', 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.', - url: 'https://i.imgur.com/ZF6s192m.jpg', + url: 'https://react.dev/images/docs/scientists/ZF6s192m.jpg', alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.' }, { name: 'Eternal Presence', artist: 'John Woodrow Wilson', 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."', - url: 'https://i.imgur.com/aTtVpES.jpg', + url: 'https://react.dev/images/docs/scientists/aTtVpES.jpg', alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.' }, { name: 'Moai', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/RCwLEoQm.jpg', + url: 'https://react.dev/images/docs/scientists/RCwLEoQm.jpg', alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.' }, { name: 'Blue Nana', artist: 'Niki de Saint Phalle', 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.', - url: 'https://i.imgur.com/Sd1AgUOm.jpg', + url: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.' }, { name: 'Ultimate Form', artist: 'Barbara Hepworth', 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.', - url: 'https://i.imgur.com/2heNQDcm.jpg', + url: 'https://react.dev/images/docs/scientists/2heNQDcm.jpg', alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.' }, { name: 'Cavaliere', artist: 'Lamidi Olonade Fakeye', description: "Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.", - url: 'https://i.imgur.com/wIdGuZwm.png', + url: 'https://react.dev/images/docs/scientists/wIdGuZwm.png', alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.' }, { name: 'Big Bellies', artist: 'Alina Szapocznikow', 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.", - url: 'https://i.imgur.com/AlHTAdDm.jpg', + url: 'https://react.dev/images/docs/scientists/AlHTAdDm.jpg', alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.' }, { name: 'Terracotta Army', artist: 'Unknown Artist', 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.', - url: 'https://i.imgur.com/HMFmH6m.jpg', + url: 'https://react.dev/images/docs/scientists/HMFmH6m.jpg', alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.' }, { name: 'Lunar Landscape', artist: 'Louise Nevelson', 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.', - url: 'https://i.imgur.com/rN7hY6om.jpg', + url: 'https://react.dev/images/docs/scientists/rN7hY6om.jpg', alt: 'A black matte sculpture where the individual elements are initially indistinguishable.' }, { name: 'Aureole', artist: 'Ranjani Shettar', 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."', - url: 'https://i.imgur.com/okTpbHhm.jpg', + url: 'https://react.dev/images/docs/scientists/okTpbHhm.jpg', alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.' }, { name: 'Hippos', artist: 'Taipei Zoo', description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.', - url: 'https://i.imgur.com/6o5Vuyu.jpg', + url: 'https://react.dev/images/docs/scientists/6o5Vuyu.jpg', alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.' }]; ``` @@ -1266,7 +1270,7 @@ export default function Form() { } ``` -```css +```css h1 { margin-top: 10px; } ``` @@ -1317,7 +1321,7 @@ export default function Form() { } ``` -```css +```css h1 { margin-top: 10px; } ``` @@ -1446,7 +1450,11 @@ export default function FeedbackForm() { ລອງຍ້າຍການເອີ້ນໃຊ້ `useState` ຄັ້ງທີ່ສອງຫຼັງຈາກເງື່ອນໄຊ `if` ແລະ ສັງເກດວ່າສີ່ງນີ້ຈະຢຸດການເຮັດວຽກອີກຄັ້ງແນວໃດ. +<<<<<<< HEAD ຫາກ linter ຂອງທ່ານ [ມີກຳນົດຄ່າສຳລັບ React ແລ້ວ](/learn/editor-setup#linting), ທ່ານຄວນເຫັນຂໍ້ຜິດພາດ lint ເມື່ອທ່ານເຮັດຜິດພາດແບບນີ້. ຖ້າທ່ານບໍ່ພົບຂໍ້ຜິດພາດເມື່ອທ່ານລອງຂຽນ code ທີ່ຜິດພາດໃນເຄື່ອງ, ທ່ານຕ້ອງຕັ້ງຄ່າ linting ສຳລັບ project ຂອງທ່ານ. +======= +If your linter is [configured for React](/learn/editor-setup#linting), you should see a lint error when you make a mistake like this. If you don't see an error when you try the faulty code locally, you need to set up linting for your project. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a </Solution> diff --git a/src/content/learn/thinking-in-react.md b/src/content/learn/thinking-in-react.md index e864977d..d59824fe 100644 --- a/src/content/learn/thinking-in-react.md +++ b/src/content/learn/thinking-in-react.md @@ -37,9 +37,15 @@ JSON API ສົ່ງບາງຂໍ້ມູນທີ່ມີລັກສະ ຂຶ້ນນຳພື້ນຖານຂອງທ່ານ, ທ່ານສາມາດຄິດກ່ຽວກັບການແຍກ design ໄປເປັນ component ໄດ້ໃນຫຼາຍແບບ: +<<<<<<< HEAD * **Programming**--ໃຊ້ເທັກນິກດຽວກັບສຳລັບການຕັດສິນໃຈວ່າທ່ານຄວນຈະສ້າງຟັງຊັ່ນໃໝ່ ຫຼື ອອບເຈັກ. ໜຶ່ງເທັກນິກແມ່ນ [ຫຼັກການໜຶ່ງຄວາມຮັບຜິດຊອບ](https://en.wikipedia.org/wiki/Single_responsibility_principle), ນັ້ນແຫຼະ, component ຄວນເຮັດແຕ່ສິ່ງດຽວເທົ່ານັ້ນ. ຖ້າມັນໃຫຍ່ຂຶ້ນ, ມັນຄວນຈະຖືກແຕກຍ່ອຍໄປເປັນສ່ວນນ້ອຍໆເປັນ subcomponent. * **CSS**--ຂຶ້ນກັບວ່າທ່ານຕ້ອງການສ້າງ class selector ສະເພາະ. (ເຖິງຢ່າງໃດກໍ່ຕາມ, component ມີລາຍລະອຽດນ້ອຍກວ່າໜ້ອຍໜຶ່ງ) * **Design**--ຂຶ້ນກັບວ່າທ່ານຕ້ອງການຈັດການຊັ້ນການອອກແບບແນວໃດ. +======= +* **Programming**--use the same techniques for deciding if you should create a new function or object. One such technique is the [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns), that is, a component should ideally only be concerned with one thing. If it ends up growing, it should be decomposed into smaller subcomponents. +* **CSS**--consider what you would make class selectors for. (However, components are a bit less granular.) +* **Design**--consider how you would organize the design's layers. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ຖ້າ JSON ຂອງທ່ານມີໂຄ່ງສ້າງທີ່ດີ, ທ່ານຈະພົບວ່າມັນຈະ map ກັບ ໂຄ່ງສ້າງ component ຂອງ UI ທ່ານໄດ້ຢ່າງເປັນທຳມະຊາດ. ນັ້ນເປັນເພາະວ່າ UI ແລະ data model ມີສະຖາປັດຕະຍະກຳຂໍ້ມູນອັນດຽວກັນ, ໂຄງດຽວກັນ. ແຍກ UI ຂອງທ່ານໄປເປັນ component, ໂດຍທີ່ແຕ່ລະ component ຈະໄປກົງກັບຊີ້ນສ່ວນ data model ຂອງທ່ານ. @@ -228,10 +234,17 @@ td { ມາເບິ່ງກັນໃນແຕ່ລະຂໍ້ອີກຄັ້ງ: +<<<<<<< HEAD 1. ລາຍການດັ່ງເດີ່ມຂອງ products ແມ່ນ **ຖືກສົ່ງເຂົ້າມາຜ່ານ props, ສະນັ້ນ ມັນຈິງບໍ່ແມ່ນ state.** 2. ຂໍ້ຄວາມຄົ້ນຫາເບິ່ງຄືຈະເປັນ state ໄດ້ເນື່ອງຈາກມັນມີການປ່ຽນແປງຕະຫຼອດເວລາ ແລະ ບໍ່ສາມາດຄຳນວນໄດ້ຈາກສິ່ງໃດ. 3. ຄ່າຂອງ checkbox ເບິ່ງຄືຈະເປັນ state ໄດ້ເນື່ອງຈາກມັນມີການປ່ຽນແປງຕະຫຼອດເວລາ ແລະ ບໍ່ສາມາດຄຳນວນໄດ້ຈາກສິ່ງໃດ. 4. ລາຍການຂອງ products ທີ filter ແລ້ວ **ບໍ່ແມ່ນ state ເພາະວ່າມັນສາມາດຄຳນວນໄດ້** ໂດຍນຳລາຍການ products ດັ່ງເດີມມາ filter ຕາມຂໍ້ຄວາມຄົ້ນຫາ ແລະ ຄ່າຂອງ checkbox. +======= +1. The original list of products is **passed in as props, so it's not state.** +2. The search text seems to be state since it changes over time and can't be computed from anything. +3. The value of the checkbox seems to be state since it changes over time and can't be computed from anything. +4. The filtered list of products **isn't state because it can be computed** by taking the original list of products and filtering it according to the search text and value of the checkbox. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ນີ້ໝາຍຄວາມວ່າ ຂໍ້ຄວາມຄົ້ນຫາ ແລະ ຄ່າຂອງ checkbox ແມ່ນ state! ເກັ່ງຫຼາຍ! @@ -265,6 +278,7 @@ Props ແລະ state ແມ່ນແຕກຕ່າງກັນ, ແຕ່ພ ບາດນີ້ມາປັບໃສ່ວິທີການຂອງພວກເຮົາ: +<<<<<<< HEAD 1. **ກຳນົດ component ທີ່ໃຊ້ state:** * `ProductTable` ຕ້ອງການ filter ລາຍການ product ອີງຕາມ state (ຂໍ້ຄວາມຄົ້ນຫາ ແລະ ຄ່າຂອງ checkbox). * `SearchBar` ຕ້ອງການສະແດງ state ນັ້ນ (ຂໍ້ຄວາມຄົ້ນຫາ ແລະ ຄ່າຂອງ checkbox). @@ -272,23 +286,32 @@ Props ແລະ state ແມ່ນແຕກຕ່າງກັນ, ແຕ່ພ 2. **ຕັດສິນໃຈວ່າ state ຈະຢູ່ໃສ**: ພວກເຮົາຈະຮັກສາ filter text ແລະ checked ເປັນ state ໃນ `FilterableProductTable`. ສະນັ້ນຄ່າຂອງ state ຈະຢູ່ໃນ `FilterableProductTable`. +======= +1. **Identify components that use state:** + * `ProductTable` needs to filter the product list based on that state (search text and checkbox value). + * `SearchBar` needs to display that state (search text and checkbox value). +2. **Find their common parent:** The first parent component both components share is `FilterableProductTable`. +3. **Decide where the state lives**: We'll keep the filter text and checked state values in `FilterableProductTable`. + +So the state values will live in `FilterableProductTable`. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ເພີ່ມ state ໃສ່ໃນ component ດ້ວຍ [`useState()` Hook.](/reference/react/useState) Hooks ແມ່ນ function ພິເສດທີ່ທ່ານສາມາດ "ຂໍເຂົ້າໄປໃນ" React. ເພີ່ມຕົວແປ state 2 ອັນ ໃນດ້ານເທິງຂອງ `FilterableProductTable` ແລະ ລະບຸ state ເລີ່ມຕົ້ນ: ```js function FilterableProductTable({ products }) { const [filterText, setFilterText] = useState(''); - const [inStockOnly, setInStockOnly] = useState(false); + const [inStockOnly, setInStockOnly] = useState(false); ``` ຈາກນັ້ນ, ສົ່ງ `filterText` ແລະ `inStockOnly` ໄປຍັງ `ProductTable` ແລະ `SearchBar` ເປັນ props: ```js <div> - <SearchBar - filterText={filterText} + <SearchBar + filterText={filterText} inStockOnly={inStockOnly} /> - <ProductTable + <ProductTable products={products} filterText={filterText} inStockOnly={inStockOnly} /> @@ -308,10 +331,10 @@ function FilterableProductTable({ products }) { return ( <div> - <SearchBar - filterText={filterText} + <SearchBar + filterText={filterText} inStockOnly={inStockOnly} /> - <ProductTable + <ProductTable products={products} filterText={filterText} inStockOnly={inStockOnly} /> @@ -389,13 +412,13 @@ function ProductTable({ products, filterText, inStockOnly }) { function SearchBar({ filterText, inStockOnly }) { return ( <form> - <input - type="text" - value={filterText} + <input + type="text" + value={filterText} placeholder="Search..."/> <label> - <input - type="checkbox" + <input + type="checkbox" checked={inStockOnly} /> {' '} Only show products in stock @@ -451,9 +474,9 @@ You provided a \`value\` prop to a form field without an \`onChange\` handler. T function SearchBar({ filterText, inStockOnly }) { return ( <form> - <input - type="text" - value={filterText} + <input + type="text" + value={filterText} placeholder="Search..."/> ``` @@ -462,7 +485,11 @@ function SearchBar({ filterText, inStockOnly }) { ## Step 5: ເພີ່ມການໄຫຂໍ້ມູນແບບ inverse {/*step-5-add-inverse-data-flow*/} +<<<<<<< HEAD ຕອນນີ້ແອັບຂອງທ່ານສະແດງຜົນຢ່າງຖືກຕ້ອງດ້ວຍ props ແລະ state ໄຫຼລົງມາຕາມລຳດັບຊັ້ນ. ແຕ່ຫາກຕ້ອງການປ່ຽນ state ໃຫ້ອີງຕາມ user input, ທ່ານຈະຕ້ອງສະໜັບສະໜູນການໄຫຼຂໍ້ມູນໄປວິທີອື່ນ: form component ທີ່ຢູ່ເລິກລົງໄປໃນລຳດັບຊັ້ນຈຳເປັນຕ້ອງໄດ້ອັບເດດ state ໃນ `FilterableProductTable`. +======= +Currently your app renders correctly with props and state flowing down the hierarchy. But to change the state according to user input, you will need to support data flowing the other way: the form components deep in the hierarchy need to update the state in `FilterableProductTable`. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a React ເຮັດໃຫ້ການໄຫຼຂໍ້ມູນນີ້ຊັດເຈນ, ແຕ່ຕ້ອງອາໄສການພິມທີ່ຫຼາຍກວ່າ two-way data binding ນ້ອຍໜຶ່ງ. ຖ້າທ່ານພະຍາຍາມພິມ ຫຼື ໝາຍຕິກໃສ່ກ່ອງໃນຕົວຢ່າງດ້ານເທິງ, ທ່ານຈະເຫັນວ່າ React ບໍ່ໄດ້ສົນໃຈຂໍ້ຄວາມທີ່ທ່ານປ້ອນ. ນີ້ແມ່ນຄວາມຕັ້ງໃຈ, ການຂຽນ `<input value={filterText} />`, ທ່ານໄດ້ຕັ້ງ prop `value` ຂອງ `input` ທີໃຫ້ເທົ່າກັບຄ່າ `filterText` ທີ່ສົ່ງ state ຜ່ານ `FilterableProductTable`ຕະຫຼອດ. ນັບແຕ່ `filterText` staet ຍັງບໍ່ຖືກຕັ້ງ, ຄ່າ input ແມ່ນບໍ່ມີການປ່ຽນແປງ. @@ -475,8 +502,8 @@ function FilterableProductTable({ products }) { return ( <div> - <SearchBar - filterText={filterText} + <SearchBar + filterText={filterText} inStockOnly={inStockOnly} onFilterTextChange={setFilterText} onInStockOnlyChange={setInStockOnly} /> @@ -519,13 +546,13 @@ function FilterableProductTable({ products }) { return ( <div> - <SearchBar - filterText={filterText} - inStockOnly={inStockOnly} - onFilterTextChange={setFilterText} + <SearchBar + filterText={filterText} + inStockOnly={inStockOnly} + onFilterTextChange={setFilterText} onInStockOnlyChange={setInStockOnly} /> - <ProductTable - products={products} + <ProductTable + products={products} filterText={filterText} inStockOnly={inStockOnly} /> </div> @@ -607,14 +634,14 @@ function SearchBar({ }) { return ( <form> - <input - type="text" - value={filterText} placeholder="Search..." + <input + type="text" + value={filterText} placeholder="Search..." onChange={(e) => onFilterTextChange(e.target.value)} /> <label> - <input - type="checkbox" - checked={inStockOnly} + <input + type="checkbox" + checked={inStockOnly} onChange={(e) => onInStockOnlyChange(e.target.checked)} /> {' '} Only show products in stock diff --git a/src/content/learn/tutorial-tic-tac-toe.md b/src/content/learn/tutorial-tic-tac-toe.md index d8b89447..5c5c98cc 100644 --- a/src/content/learn/tutorial-tic-tac-toe.md +++ b/src/content/learn/tutorial-tic-tac-toe.md @@ -345,8 +345,12 @@ import './styles.css'; import App from './App'; ``` +<<<<<<< HEAD ແຖວທີ 1-5 ແມ່ນລວມສ່ວນທີ່ຈຳເປັນມາລວມກັນ: +======= +Lines 1-5 bring all the necessary pieces together: +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a * React * Library React ເພື່ອສື່ສານກັບເວັບບາວເຊີ (React DOM) @@ -565,7 +569,11 @@ export default function Board() { } ``` +<<<<<<< HEAD ສັງເກດເຫັນຄວາມແຕກຕ່າງຂອງບາວເຊີ `div` component `Board` ແລະ `Square` ຕ້ອງເລີ່ມຕົ້ນດ້ວຍໂຕພິມໃຫຍ່. +======= +Note how unlike the browser `div`s, your own components `Board` and `Square` must start with a capital letter. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ມາເບິ່ງ: @@ -1108,7 +1116,11 @@ function Square({ value, onSquareClick }) { } ``` +<<<<<<< HEAD ຕອນນີ້ທ່ານຈະເຊື່ອມຕໍ່ prop `onSquareClick` ໃສ່ຟັງຊັ່ນໃນ component `Board` ທີ່ທ່ານໃສ່ຊື່ວ່າ `handleClick`. ໃນການເຊື່ອມຕໍ່ `onSquareClick` ກັບ `handleClick` ທ່ານຈະຕ້ອງສົ່ງຜ່ານຟັງຊັນໄປຫາ prop `onSquareClick` ຂອງ component `Square` ທຳອິດ: +======= +Now you'll connect the `onSquareClick` prop to a function in the `Board` component that you'll name `handleClick`. To connect `onSquareClick` to `handleClick` you'll pass a function to the `onSquareClick` prop of the first `Square` component: +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ```js {7} export default function Board() { @@ -2090,7 +2102,17 @@ export default function Game() { } ``` +<<<<<<< HEAD ທ່ານສາມາດເບິ່ງໄດ້ວ່າ code ຂອງທ່ານຄວນເປັນແນວໃດໃນດ້ານລຸ່ມນີ້. ສັງເກດວ່າທ່ານຄວນເຫັນຂໍ້ຜິດພາດໃນ console developer tools ທີ່ບອກວ່າ: ``Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `Game`.`` ທ່ານຈະແປງຂໍ້ຜິດພາດນີ້ໃນຫົວຂໍ້ຕໍ່ໄປ. +======= +You can see what your code should look like below. Note that you should see an error in the developer tools console that says: + +<ConsoleBlock level="warning"> +Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `Game`. +</ConsoleBlock> + +You'll fix this error in the next section. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a <Sandpack> diff --git a/src/content/learn/updating-objects-in-state.md b/src/content/learn/updating-objects-in-state.md index 90e434d5..36472a7e 100644 --- a/src/content/learn/updating-objects-in-state.md +++ b/src/content/learn/updating-objects-in-state.md @@ -198,7 +198,7 @@ setPosition({ ການ Mutate ຈະເປັນບັນຫາເມື່ອທ່ານປ່ຽນແປງ object *ທີ່ມີຢູ່* ໃນ state ແລ້ວ. ການ mutate object ທີ່ທ່ານຫາກໍສ້າງຂຶ້ນນັ້ນບໍ່ເປັນຫຍັງເພາະວ່າ *ຍັງບໍ່ມີ code ອື່ນອ້າງອີງເຖິງມັນເທື່ອ.* ການປ່ຽນແປງຈະບໍ່ສົ່ງຜົນກະທົບຕໍ່ບາງສິ່ງທີ່ຂຶ້ນຢູ່ກັບ object ໂດຍບໍ່ໄດ້ຕັ້ງໃຈ. ສິ່ງນີ້ເອີ້ນວ່າ "ການ mutate local". ທ່ານສາມາດ mutate local [ໃນຂະນະ render.](/learn/keeping-components-pure#local-mutation-your-components-little-secret) ສະດວກ ແລະ ດີຫຼາຍ! -</DeepDive> +</DeepDive> ## ການ copy object ດ້ວຍ spread syntax {/*copying-objects-with-the-spread-syntax*/} @@ -295,7 +295,11 @@ setPerson({ }); ``` +<<<<<<< HEAD ຕອນນີ້ form ເຮັດວຽກໄດ້! +======= +Now the form works! +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ສັງເກດວ່າທ່ານບໍ່ໄດ້ປະກາດຕົວແປ state ແຍກຕ່າງຫາກສຳລັບແຕ່ລະ field input. ສຳລັບ form ໃຫຍ່, ການເກັບກຸ່ມຂໍ້ມູນທັງໝົດໄວ້ໃນ object ຈະສະດວກກວ່າຫຼາຍ--ຖ້າວ່າທ່ານອັບເດດຂໍ້ມູນຢ່າງຖືກຕ້ອງ! @@ -372,7 +376,11 @@ input { margin-left: 5px; margin-bottom: 5px; } </Sandpack> +<<<<<<< HEAD ຈື່ໄວ້ວ່າ syntax spread `...` ແມ່ນ "ຕື້ນ"--ເຊິ່ງຈະ copy ສະເພາະສິ່ງທີ່ຢູ່ເລິກລົງໄປລະດັບໜຶ່ງເທົ່ານັ້ນ. ສິ່ງນີ້ເຮັດໃຫ້ໄວ, ແຕ່ກໍໝາຍຄວາມວ່າຖ້າທ່ານຕ້ອງການອັບເດດ property ທີ່ຊ້ອນກັນ, ທ່ານຈະຕ້ອງໄດ້ໃຊ້ຫຼາຍກວ່າໜຶ່ງຄັ້ງ. +======= +Note that the `...` spread syntax is "shallow"--it only copies things one level deep. This makes it fast, but it also means that if you want to update a nested property, you'll have to use it more than once. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a <DeepDive> @@ -456,7 +464,7 @@ const [person, setPerson] = useState({ artwork: { title: 'Blue Nana', city: 'Hamburg', - image: 'https://i.imgur.com/Sd1AgUOm.jpg', + image: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', } }); ``` @@ -500,7 +508,7 @@ export default function Form() { artwork: { title: 'Blue Nana', city: 'Hamburg', - image: 'https://i.imgur.com/Sd1AgUOm.jpg', + image: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', } }); @@ -578,8 +586,8 @@ export default function Form() { <br /> (located in {person.artwork.city}) </p> - <img - src={person.artwork.image} + <img + src={person.artwork.image} alt={person.artwork.title} /> </> @@ -607,7 +615,7 @@ let obj = { artwork: { title: 'Blue Nana', city: 'Hamburg', - image: 'https://i.imgur.com/Sd1AgUOm.jpg', + image: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', } }; ``` @@ -618,7 +626,7 @@ let obj = { let obj1 = { title: 'Blue Nana', city: 'Hamburg', - image: 'https://i.imgur.com/Sd1AgUOm.jpg', + image: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', }; let obj2 = { @@ -632,7 +640,7 @@ Object `obj1` ບໍ່ໄດ້ຢູ່ໃນ `obj2`. ຕົວຢ່າງ, ` let obj1 = { title: 'Blue Nana', city: 'Hamburg', - image: 'https://i.imgur.com/Sd1AgUOm.jpg', + image: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', }; let obj2 = { @@ -648,7 +656,7 @@ let obj3 = { ຖ້າທ່ານຕ້ອງ mutate `obj3.artwork.city`, ຈະມີຜົນກັບ `obj2.artwork.city` ແລະ `obj1.city`. ເນື່ອງຈາກ `obj3.artwork`, `obj3.artwork` ແລະ `obj1` ແມ່ນເປັນ object ດຽວກັນ. ສິ່ງນີ້ຍາກທີ່ຈະເບິ່ງເຫັນເມື່ອທ່ານຄິດວ່າ object ນັ້ນ "ຊ້ອນກັນ". ແຕ່ເປັນ object ທີ່ແຍກກັນມີການ "point" ເຊິ່ງກັນ ແລະ ກັນດ້ວຍ property. -</DeepDive> +</DeepDive> ### ການຂຽນ logic ການອັບເດດແບບຫຍໍ້ດ້ວຍ Immer {/*write-concise-update-logic-with-immer*/} @@ -688,7 +696,7 @@ export default function Form() { artwork: { title: 'Blue Nana', city: 'Hamburg', - image: 'https://i.imgur.com/Sd1AgUOm.jpg', + image: 'https://react.dev/images/docs/scientists/Sd1AgUOm.jpg', } }); @@ -753,8 +761,8 @@ export default function Form() { <br /> (located in {person.artwork.city}) </p> - <img - src={person.artwork.image} + <img + src={person.artwork.image} alt={person.artwork.title} /> </> diff --git a/src/content/learn/writing-markup-with-jsx.md b/src/content/learn/writing-markup-with-jsx.md index 53f69db1..964b7b9d 100644 --- a/src/content/learn/writing-markup-with-jsx.md +++ b/src/content/learn/writing-markup-with-jsx.md @@ -69,10 +69,17 @@ JSX ແລະ React ເປັນສອງຢ່າງທີ່ແຍກກັນ ສົມມຸດວ່າທ່ານມີ HTML ບາງສ່ວນ (ຖືກຕ້ອງຄົບຖ້ວນ): ```html +<<<<<<< HEAD <h1>Todos ຂອງ Hedy Lamarr</h1> <img src="https://i.imgur.com/yXOvdOSs.jpg" alt="Hedy Lamarr" +======= +<h1>Hedy Lamarr's Todos</h1> +<img + src="https://react.dev/images/docs/scientists/yXOvdOSs.jpg" + alt="Hedy Lamarr" +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a class="photo" > <ul> @@ -101,10 +108,17 @@ export default function TodoList() { export default function TodoList() { return ( // This doesn't quite work! +<<<<<<< HEAD <h1>Todos ຂອງ Hedy Lamarr</h1> <img src="https://i.imgur.com/yXOvdOSs.jpg" alt="Hedy Lamarr" +======= + <h1>Hedy Lamarr's Todos</h1> + <img + src="https://react.dev/images/docs/scientists/yXOvdOSs.jpg" + alt="Hedy Lamarr" +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a class="photo" > <ul> @@ -140,10 +154,17 @@ img { height: 90px } ```js {1,11} <div> +<<<<<<< HEAD <h1>Todos ຂອງ Hedy Lamarr</h1> <img src="https://i.imgur.com/yXOvdOSs.jpg" alt="Hedy Lamarr" +======= + <h1>Hedy Lamarr's Todos</h1> + <img + src="https://react.dev/images/docs/scientists/yXOvdOSs.jpg" + alt="Hedy Lamarr" +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a class="photo" > <ul> @@ -157,10 +178,17 @@ img { height: 90px } ```js {1,11} <> +<<<<<<< HEAD <h1>Todos ຂອງ Hedy Lamarr</h1> <img src="https://i.imgur.com/yXOvdOSs.jpg" alt="Hedy Lamarr" +======= + <h1>Hedy Lamarr's Todos</h1> + <img + src="https://react.dev/images/docs/scientists/yXOvdOSs.jpg" + alt="Hedy Lamarr" +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a class="photo" > <ul> @@ -187,9 +215,9 @@ JSX ກຳນົດໃຫ້ tag ຕ້ອງປິດເທົ່ານັ້ ```js {2-6,8-10} <> - <img - src="https://i.imgur.com/yXOvdOSs.jpg" - alt="Hedy Lamarr" + <img + src="https://react.dev/images/docs/scientists/yXOvdOSs.jpg" + alt="Hedy Lamarr" class="photo" /> <ul> @@ -207,9 +235,9 @@ JSX ປ່ຽນເປັນ JavaScript ແລະ attribute ທີ່ຂຽນ ນີ້ແມ່ນເຫດຜົນວ່າເປັນຫຍັງ, ໃນ React, ຫຼາຍ attribute HTML ແລະ SVG ຖືກຂຽນເປັນແບບ camelCase. ຕົວຢ່າງ, ແທນທີ່ `stroke-width` ທ່ານໃຊ້ `strokeWidth`. ນັບແຕ່ `class` ຖືກສະຫງວນ, ໃນ React ທ່ານຂຽນ `className` ແທນ, ຊື່ຫຼັງຈາກ [DOM property ທີ່ກ່ຽວຂ້ອງ](https://developer.mozilla.org/en-US/docs/Web/API/Element/className): ```js {4} -<img - src="https://i.imgur.com/yXOvdOSs.jpg" - alt="Hedy Lamarr" +<img + src="https://react.dev/images/docs/scientists/yXOvdOSs.jpg" + alt="Hedy Lamarr" className="photo" /> ``` @@ -234,11 +262,19 @@ JSX ປ່ຽນເປັນ JavaScript ແລະ attribute ທີ່ຂຽນ export default function TodoList() { return ( <> +<<<<<<< HEAD <h1>Todos ຂອງ Hedy Lamarr</h1> <img src="https://i.imgur.com/yXOvdOSs.jpg" alt="Hedy Lamarr" className="photo" +======= + <h1>Hedy Lamarr's Todos</h1> + <img + src="https://react.dev/images/docs/scientists/yXOvdOSs.jpg" + alt="Hedy Lamarr" + className="photo" +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a /> <ul> <li>ປະດິດສັນຍານໄຟຈາລະຈອນໃໝ່</li> diff --git a/src/content/learn/your-first-component.md b/src/content/learn/your-first-component.md index 11551283..3aa08469 100644 --- a/src/content/learn/your-first-component.md +++ b/src/content/learn/your-first-component.md @@ -63,7 +63,7 @@ React ໃຫ້ທ່ານລວມ markup, CSS, ແລະ JavaScript ເຂົ export default function Profile() { return ( <img - src="https://i.imgur.com/MK3eW3Am.jpg" + src="https://react.dev/images/docs/scientists/MK3eW3Am.jpg" alt="Katherine Johnson" /> ) @@ -99,7 +99,7 @@ Component return ແທັກ `<img />` ດ້ວຍ attribute `src` ແລະ ` ສາມາດຂຽນ return statement ທັງໝົດໄດ້ໃນໜຶ່ງແຖວ, ເຊັ່ນດຽວກັບ component ນີ້: ```js -return <img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" />; +return <img src="https://react.dev/images/docs/scientists/MK3eW3As.jpg" alt="Katherine Johnson" />; ``` ແຕ່ຖ້າ markup ຂອງທ່ານບໍ່ໄດ້ຢູ່ໃນແຖວດຽວກັບ keyword `return`, ທ່ານສາມາດລວມມັນດ້ວຍຄູ່ຂອງວົງເລັບ: @@ -107,7 +107,7 @@ return <img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" />; ```js return ( <div> - <img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" /> + <img src="https://react.dev/images/docs/scientists/MK3eW3As.jpg" alt="Katherine Johnson" /> </div> ); ``` @@ -128,7 +128,7 @@ return ( function Profile() { return ( <img - src="https://i.imgur.com/MK3eW3As.jpg" + src="https://react.dev/images/docs/scientists/MK3eW3As.jpg" alt="Katherine Johnson" /> ); @@ -163,10 +163,17 @@ img { margin: 0 10px 10px 0; height: 90px; } ```html <section> +<<<<<<< HEAD <h1>ນັກວິທະຍາສາດທີ່ນ່າປະຫຼາດໃຈ</h1> <img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" /> <img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" /> <img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" /> +======= + <h1>Amazing scientists</h1> + <img src="https://react.dev/images/docs/scientists/MK3eW3As.jpg" alt="Katherine Johnson" /> + <img src="https://react.dev/images/docs/scientists/MK3eW3As.jpg" alt="Katherine Johnson" /> + <img src="https://react.dev/images/docs/scientists/MK3eW3As.jpg" alt="Katherine Johnson" /> +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a </section> ``` @@ -252,7 +259,7 @@ Sanbox ນີ້ບໍ່ເຮັດວຽກຍ້ອນວ່າ root compone function Profile() { return ( <img - src="https://i.imgur.com/lICfvbD.jpg" + src="https://react.dev/images/docs/scientists/lICfvbD.jpg" alt="Aklilu Lemma" /> ); @@ -277,7 +284,7 @@ img { height: 181px; } export default function Profile() { return ( <img - src="https://i.imgur.com/lICfvbD.jpg" + src="https://react.dev/images/docs/scientists/lICfvbD.jpg" alt="Aklilu Lemma" /> ); @@ -310,7 +317,7 @@ You may get an "Unexpected token" error while trying to fix this. In that case, ```js export default function Profile() { return - <img src="https://i.imgur.com/jA8hHMpm.jpg" alt="Katsuko Saruhashi" />; + <img src="https://react.dev/images/docs/scientists/jA8hHMpm.jpg" alt="Katsuko Saruhashi" />; } ``` @@ -328,7 +335,7 @@ img { height: 180px; } ```js export default function Profile() { - return <img src="https://i.imgur.com/jA8hHMpm.jpg" alt="Katsuko Saruhashi" />; + return <img src="https://react.dev/images/docs/scientists/jA8hHMpm.jpg" alt="Katsuko Saruhashi" />; } ``` @@ -345,9 +352,9 @@ img { height: 180px; } ```js export default function Profile() { return ( - <img - src="https://i.imgur.com/jA8hHMpm.jpg" - alt="Katsuko Saruhashi" + <img + src="https://react.dev/images/docs/scientists/jA8hHMpm.jpg" + alt="Katsuko Saruhashi" /> ); } @@ -371,7 +378,7 @@ img { height: 180px; } function profile() { return ( <img - src="https://i.imgur.com/QIrZWGIs.jpg" + src="https://react.dev/images/docs/scientists/QIrZWGIs.jpg" alt="Alan L. Hart" /> ); @@ -408,7 +415,7 @@ React component names must start with a capital letter. function Profile() { return ( <img - src="https://i.imgur.com/QIrZWGIs.jpg" + src="https://react.dev/images/docs/scientists/QIrZWGIs.jpg" alt="Alan L. Hart" /> ); diff --git a/src/content/reference/dev-tools/react-performance-tracks.md b/src/content/reference/dev-tools/react-performance-tracks.md new file mode 100644 index 00000000..d4757b9e --- /dev/null +++ b/src/content/reference/dev-tools/react-performance-tracks.md @@ -0,0 +1,159 @@ +--- +title: React Performance tracks +--- + +<Intro> + +React Performance tracks are specialized custom entries that appear on the Performance panel's timeline in your browser developer tools. + +</Intro> + +These 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. + +<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}> + <img className="w-full light-image" src="/images/docs/performance-tracks/overview.png" alt="React Performance Tracks" /> + <img className="w-full dark-image" src="/images/docs/performance-tracks/overview.dark.png" alt="React Performance Tracks" /> +</div> + +<InlineToc /> + +--- + +## Usage {/*usage*/} + +React Performance tracks are only available in development and profiling builds of React: + +- **Development**: enabled by default. +- **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. + +If 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). + +<Pitfall> + +The profiling instrumentation that powers React Performance tracks adds some additional overhead, so it is disabled in production builds by default. +Server Components and Server Requests tracks are only available in development builds. + +</Pitfall> + +### Using profiling builds {/*using-profiling-builds*/} + +In addition to production and development builds, React also includes a special profiling build. +To use profiling builds, you have to use `react-dom/profiling` instead of `react-dom/client`. +We 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. +Your framework might have built-in support for enabling React's profiling build. + +--- + +## Tracks {/*tracks*/} + +### Scheduler {/*scheduler*/} + +The 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: + +- **Blocking** - The synchronous updates, which could've been initiated by user interactions. +- **Transition** - Non-blocking work that happens in the background, usually initiated via [`startTransition`](/reference/react/startTransition). +- **Suspense** - Work related to Suspense boundaries, such as displaying fallbacks or revealing content. +- **Idle** - The lowest priority work that is done when there are no other tasks with higher priority. + +<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}> + <img className="w-full light-image" src="/images/docs/performance-tracks/scheduler.png" alt="Scheduler track" /> + <img className="w-full dark-image" src="/images/docs/performance-tracks/scheduler.dark.png" alt="Scheduler track" /> +</div> + +#### Renders {/*renders*/} + +Every render pass consists of multiple phases that you can see on a timeline: + +- **Update** - this is what caused a new render pass. +- **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. +- **Commit** - After rendering components, React will submit the changes to the DOM and run layout effects, like [`useLayoutEffect`](/reference/react/useLayoutEffect). +- **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. + +<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}> + <img className="w-full light-image" src="/images/docs/performance-tracks/scheduler-update.png" alt="Scheduler track: updates" /> + <img className="w-full dark-image" src="/images/docs/performance-tracks/scheduler-update.dark.png" alt="Scheduler track: updates" /> +</div> + +[Learn more about renders and commits](/learn/render-and-commit). + +#### Cascading updates {/*cascading-updates*/} + +Cascading 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. + +In 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. + +<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}> + <img className="w-full light-image" src="/images/docs/performance-tracks/scheduler-cascading-update.png" alt="Scheduler track: cascading updates" /> + <img className="w-full dark-image" src="/images/docs/performance-tracks/scheduler-cascading-update.dark.png" alt="Scheduler track: cascading updates" /> +</div> + +[Learn more about Effects](/learn/you-might-not-need-an-effect). + +### Components {/*components*/} + +The 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. + +<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}> + <img className="w-full light-image" src="/images/docs/performance-tracks/components-render.png" alt="Components track: render durations" /> + <img className="w-full dark-image" src="/images/docs/performance-tracks/components-render.dark.png" alt="Components track: render durations" /> +</div> + +Similar 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. + +<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}> + <img className="w-full light-image" src="/images/docs/performance-tracks/components-effects.png" alt="Components track: effects durations" /> + <img className="w-full dark-image" src="/images/docs/performance-tracks/components-effects.dark.png" alt="Components track: effects durations" /> +</div> + +<Note> + +Unlike renders, not all effects are shown on the Components track by default. + +To 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. + +</Note> + +Additional events may be displayed during the render and effects phases: + +- <span style={{padding: '0.125rem 0.25rem', backgroundColor: '#facc15', color: '#1f1f1fff'}}>Mount</span> - A corresponding subtree of component renders or effects was mounted. +- <span style={{padding: '0.125rem 0.25rem', backgroundColor: '#facc15', color: '#1f1f1fff'}}>Unmount</span> - A corresponding subtree of component renders or effects was unmounted. +- <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. +- <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. + +#### Changed props {/*changed-props*/} + +In 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. + +<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}> + <img className="w-full light-image" src="/images/docs/performance-tracks/changed-props.png" alt="Components track: changed props" /> + <img className="w-full dark-image" src="/images/docs/performance-tracks/changed-props.dark.png" alt="Components track: changed props" /> +</div> + +### Server {/*server*/} + +<div style={{display: 'flex', justifyContent: 'center', marginBottom: '1rem'}}> + <img className="w-full light-image" src="/images/docs/performance-tracks/server-overview.png" alt="React Server Performance Tracks" /> + <img className="w-full dark-image" src="/images/docs/performance-tracks/server-overview.dark.png" alt="React Server Performance Tracks" /> +</div> + +#### Server Requests {/*server-requests*/} + +The 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. + +React 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. +For 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. + +Clicking 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. + +Rejected Promises are displayed as red with their rejected value. + +#### Server Components {/*server-components*/} + +The 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. + +If you await a Promise, React will display duration of that Promise. To see all I/O operations, use the Server Requests track. + +Different colors are used to indicate the duration of the component render. The darker the color, the longer the duration. + +The 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. +If more than 8 Server Components are rendered concurrently, React will associate them with the last "Parallel" track instead of adding more tracks. diff --git a/src/content/reference/react-dom/client/createRoot.md b/src/content/reference/react-dom/client/createRoot.md index afddb417..e9a9b1aa 100644 --- a/src/content/reference/react-dom/client/createRoot.md +++ b/src/content/reference/react-dom/client/createRoot.md @@ -88,6 +88,18 @@ React will display `<App />` in the `root`, and take over managing the DOM insid * If you call `render` on the same root more than once, React will update the DOM as necessary to reflect the latest JSX you passed. React will decide which parts of the DOM can be reused and which need to be recreated by ["matching it up"](/learn/preserving-and-resetting-state) with the previously rendered tree. Calling `render` on the same root again is similar to calling the [`set` function](/reference/react/useState#setstate) on the root component: React avoids unnecessary DOM updates. +<<<<<<< HEAD +======= +* Although rendering is synchronous once it starts, `root.render(...)` is not. This means code after `root.render()` may run before any effects (`useLayoutEffect`, `useEffect`) of that specific render are fired. This is usually fine and rarely needs adjustment. In rare cases where effect timing matters, you can wrap `root.render(...)` in [`flushSync`](https://react.dev/reference/react-dom/flushSync) to ensure the initial render runs fully synchronously. + + ```js + const root = createRoot(document.getElementById('root')); + root.render(<App />); + // 🚩 The HTML will not include the rendered <App /> yet: + console.log(document.body.innerHTML); + ``` + +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a --- ### `root.unmount()` {/*root-unmount*/} @@ -102,7 +114,7 @@ An app fully built with React will usually not have any calls to `root.unmount`. This is mostly useful if your React root's DOM node (or any of its ancestors) may get removed from the DOM by some other code. For example, imagine a jQuery tab panel that removes inactive tabs from the DOM. If a tab gets removed, everything inside it (including the React roots inside) would get removed from the DOM as well. In that case, you need to tell React to "stop" managing the removed root's content by calling `root.unmount`. Otherwise, the components inside the removed root won't know to clean up and free up global resources like subscriptions. -Calling `root.unmount` will unmount all the components in the root and "detach" React from the root DOM node, including removing any event handlers or state in the tree. +Calling `root.unmount` will unmount all the components in the root and "detach" React from the root DOM node, including removing any event handlers or state in the tree. #### Parameters {/*root-unmount-parameters*/} @@ -186,7 +198,7 @@ function Counter() { </Sandpack> -**If your app is fully built with React, you shouldn't need to create any more roots, or to call [`root.render`](#root-render) again.** +**If your app is fully built with React, you shouldn't need to create any more roots, or to call [`root.render`](#root-render) again.** From this point on, React will manage the DOM of your entire app. To add more components, [nest them inside the `App` component.](/learn/importing-and-exporting-components) When you need to update the UI, each of your components can do this by [using state.](/reference/react/useState) When you need to display extra content like a modal or a tooltip outside the DOM node, [render it with a portal.](/reference/react-dom/createPortal) @@ -238,11 +250,11 @@ import { createRoot } from 'react-dom/client'; import { Comments, Navigation } from './Components.js'; const navDomNode = document.getElementById('navigation'); -const navRoot = createRoot(navDomNode); +const navRoot = createRoot(navDomNode); navRoot.render(<Navigation />); const commentDomNode = document.getElementById('comments'); -const commentRoot = createRoot(commentDomNode); +const commentRoot = createRoot(commentDomNode); commentRoot.render(<Comments />); ``` @@ -292,7 +304,7 @@ You could also create a new DOM node with [`document.createElement()`](https://d ```js const domNode = document.createElement('div'); -const root = createRoot(domNode); +const root = createRoot(domNode); root.render(<Comment />); document.body.appendChild(domNode); // You can add it anywhere in the document ``` @@ -361,6 +373,31 @@ Until you do that, nothing is displayed. --- +<<<<<<< HEAD +======= +### I'm getting an error: "You passed a second argument to root.render" {/*im-getting-an-error-you-passed-a-second-argument-to-root-render*/} + +A common mistake is to pass the options for `createRoot` to `root.render(...)`: + +<ConsoleBlock level="error"> + +Warning: You passed a second argument to root.render(...) but it only accepts one argument. + +</ConsoleBlock> + +To fix, pass the root options to `createRoot(...)`, not `root.render(...)`: +```js {2,5} +// 🚩 Wrong: root.render only takes one argument. +root.render(App, {onUncaughtError}); + +// ✅ Correct: pass options to createRoot. +const root = createRoot(container, {onUncaughtError}); +root.render(<App />); +``` + +--- + +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a ### I'm getting an error: "Target container is not a DOM element" {/*im-getting-an-error-target-container-is-not-a-dom-element*/} This error means that whatever you're passing to `createRoot` is not a DOM node. diff --git a/src/content/reference/react-dom/components/common.md b/src/content/reference/react-dom/components/common.md index 61074273..601370da 100644 --- a/src/content/reference/react-dom/components/common.md +++ b/src/content/reference/react-dom/components/common.md @@ -84,11 +84,11 @@ These standard DOM props are also supported for all built-in components: * `onCutCapture`: A version of `onCut` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) * `onDoubleClick`: A [`MouseEvent` handler](#mouseevent-handler) function. Fires when the user clicks twice. Corresponds to the browser [`dblclick` event.](https://developer.mozilla.org/en-US/docs/Web/API/Element/dblclick_event) * `onDoubleClickCapture`: A version of `onDoubleClick` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onDrag`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drag_event): A [`DragEvent` handler](#dragevent-handler) function. Fires while the user is dragging something. +* [`onDrag`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drag_event): A [`DragEvent` handler](#dragevent-handler) function. Fires while the user is dragging something. * `onDragCapture`: A version of `onDrag` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onDragEnd`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragend_event): A [`DragEvent` handler](#dragevent-handler) function. Fires when the user stops dragging something. +* [`onDragEnd`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragend_event): A [`DragEvent` handler](#dragevent-handler) function. Fires when the user stops dragging something. * `onDragEndCapture`: A version of `onDragEnd` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onDragEnter`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragenter_event): A [`DragEvent` handler](#dragevent-handler) function. Fires when the dragged content enters a valid drop target. +* [`onDragEnter`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragenter_event): A [`DragEvent` handler](#dragevent-handler) function. Fires when the dragged content enters a valid drop target. * `onDragEnterCapture`: A version of `onDragEnter` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) * [`onDragOver`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragover_event): A [`DragEvent` handler](#dragevent-handler) function. Fires on a valid drop target while the dragged content is dragged over it. You must call `e.preventDefault()` here to allow dropping. * `onDragOverCapture`: A version of `onDragOver` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) @@ -283,7 +283,7 @@ Some React events do not map directly to the browser's native events. For exampl React event objects implement some of the standard [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) properties: -* [`bubbles`](https://developer.mozilla.org/en-US/docs/Web/API/Event/bubbles): A boolean. Returns whether the event bubbles through the DOM. +* [`bubbles`](https://developer.mozilla.org/en-US/docs/Web/API/Event/bubbles): A boolean. Returns whether the event bubbles through the DOM. * [`cancelable`](https://developer.mozilla.org/en-US/docs/Web/API/Event/cancelable): A boolean. Returns whether the event can be canceled. * [`currentTarget`](https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget): A DOM node. Returns the node to which the current handler is attached in the React tree. * [`defaultPrevented`](https://developer.mozilla.org/en-US/docs/Web/API/Event/defaultPrevented): A boolean. Returns whether `preventDefault` was called. @@ -643,7 +643,7 @@ An event handler type for [touch events.](https://developer.mozilla.org/en-US/do * [`shiftKey`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/shiftKey) * [`touches`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/touches) * [`targetTouches`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/targetTouches) - + It also includes the inherited [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) properties: * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail) @@ -776,7 +776,7 @@ import Avatar from './Avatar.js'; const user = { name: 'Hedy Lamarr', - imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg', + imageUrl: 'https://react.dev/images/docs/scientists/yXOvdOSs.jpg', imageSize: 90, }; diff --git a/src/content/reference/react-dom/components/index.md b/src/content/reference/react-dom/components/index.md index c9b355c8..46981d17 100644 --- a/src/content/reference/react-dom/components/index.md +++ b/src/content/reference/react-dom/components/index.md @@ -169,6 +169,126 @@ If you render a tag with a dash, like `<my-element>`, React will assume you want If you render a built-in browser HTML element with an [`is`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/is) attribute, it will also be treated as a custom element. +<<<<<<< HEAD +======= +#### Setting values on custom elements {/*attributes-vs-properties*/} + +Custom elements have two methods of passing data into them: + +1) Attributes: Which are displayed in markup and can only be set to string values +2) Properties: Which are not displayed in markup and can be set to arbitrary JavaScript values + +By default, React will pass values bound in JSX as attributes: + +```jsx +<my-element value="Hello, world!"></my-element> +``` + +Non-string JavaScript values passed to custom elements will be serialized by default: + +```jsx +// Will be passed as `"1,2,3"` as the output of `[1,2,3].toString()` +<my-element value={[1,2,3]}></my-element> +``` + +React will, however, recognize an custom element's property as one that it may pass arbitrary values to if the property name shows up on the class during construction: + +<Sandpack> + +```js src/index.js hidden +import {MyElement} from './MyElement.js'; +import { createRoot } from 'react-dom/client'; +import {App} from "./App.js"; + +customElements.define('my-element', MyElement); + +const root = createRoot(document.getElementById('root')) +root.render(<App />); +``` + +```js src/MyElement.js active +export class MyElement extends HTMLElement { + constructor() { + super(); + // The value here will be overwritten by React + // when initialized as an element + this.value = undefined; + } + + connectedCallback() { + this.innerHTML = this.value.join(", "); + } +} +``` + +```js src/App.js +export function App() { + return <my-element value={[1,2,3]}></my-element> +} +``` + +</Sandpack> + +#### Listening for events on custom elements {/*custom-element-events*/} + +A common pattern when using custom elements is that they may dispatch [`CustomEvent`s](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) rather than accept a function to call when an event occur. You can listen for these events using an `on` prefix when binding to the event via JSX. + +<Sandpack> + +```js src/index.js hidden +import {MyElement} from './MyElement.js'; +import { createRoot } from 'react-dom/client'; +import {App} from "./App.js"; + +customElements.define('my-element', MyElement); + +const root = createRoot(document.getElementById('root')) +root.render(<App />); +``` + +```javascript src/MyElement.js +export class MyElement extends HTMLElement { + constructor() { + super(); + this.test = undefined; + this.emitEvent = this._emitEvent.bind(this); + } + + _emitEvent() { + const event = new CustomEvent('speak', { + detail: { + message: 'Hello, world!', + }, + }); + this.dispatchEvent(event); + } + + connectedCallback() { + this.el = document.createElement('button'); + this.el.innerText = 'Say hi'; + this.el.addEventListener('click', this.emitEvent); + this.appendChild(this.el); + } + + disconnectedCallback() { + this.el.removeEventListener('click', this.emitEvent); + } +} +``` + +```jsx src/App.js active +export function App() { + return ( + <my-element + onspeak={e => console.log(e.detail.message)} + ></my-element> + ) +} +``` + +</Sandpack> + +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a <Note> [A future version of React will include more comprehensive support for custom elements.](https://github.com/facebook/react/issues/11347#issuecomment-1122275286) diff --git a/src/content/reference/react-dom/components/input.md b/src/content/reference/react-dom/components/input.md index 87546611..5025476a 100644 --- a/src/content/reference/react-dom/components/input.md +++ b/src/content/reference/react-dom/components/input.md @@ -219,7 +219,7 @@ export default function MyForm() { type="radio" name="myRadio" value="option2" - defaultChecked={true} + defaultChecked={true} /> Option 2 </label> diff --git a/src/content/reference/react-dom/components/link.md b/src/content/reference/react-dom/components/link.md index 890cf090..d0c964ef 100644 --- a/src/content/reference/react-dom/components/link.md +++ b/src/content/reference/react-dom/components/link.md @@ -158,9 +158,13 @@ export default function SiteMapPage() { ### Controlling stylesheet precedence {/*controlling-stylesheet-precedence*/} +<<<<<<< HEAD Stylesheets can conflict with each other, and when they do, the browser goes with the one that comes later in the document. React lets you control the order of stylesheets with the `precedence` prop. In this example, two components render stylesheets, and the one with the higher precedence goes later in the document even though the component that renders it comes earlier. {/*FIXME: this doesn't appear to actually work -- I guess precedence isn't implemented yet?*/} +======= +Stylesheets can conflict with each other, and when they do, the browser goes with the one that comes later in the document. React lets you control the order of stylesheets with the `precedence` prop. In this example, three components render stylesheets, and the ones with the same precedence are grouped together in the `<head>`. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a <SandpackWithHTMLOutput> diff --git a/src/content/reference/react-dom/components/meta.md b/src/content/reference/react-dom/components/meta.md index 8489d4a4..72c12143 100644 --- a/src/content/reference/react-dom/components/meta.md +++ b/src/content/reference/react-dom/components/meta.md @@ -42,7 +42,7 @@ To add document metadata, render the [built-in browser `<meta>` component](https It should have *exactly one* of the following props: `name`, `httpEquiv`, `charset`, `itemProp`. The `<meta>` component does something different depending on which of these props is specified. -* `name`: a string. Specifies the [kind of metadata](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name) to be attached to the document. +* `name`: a string. Specifies the [kind of metadata](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name) to be attached to the document. * `charset`: a string. Specifies the character set used by the document. The only valid value is `"utf-8"`. * `httpEquiv`: a string. Specifies a directive for processing the document. * `itemProp`: a string. Specifies metadata about a particular item within the document rather than the document as a whole. @@ -50,9 +50,9 @@ It should have *exactly one* of the following props: `name`, `httpEquiv`, `chars #### Special rendering behavior {/*special-rendering-behavior*/} -React will always place the DOM element corresponding to the `<meta>` component within the document’s `<head>`, regardless of where in the React tree it is rendered. The `<head>` is the only valid place for `<meta>` to exist within the DOM, yet it’s convenient and keeps things composable if a component representing a specific page can render `<meta>` components itself. +React will always place the DOM element corresponding to the `<meta>` component within the document’s `<head>`, regardless of where in the React tree it is rendered. The `<head>` is the only valid place for `<meta>` to exist within the DOM, yet it’s convenient and keeps things composable if a component representing a specific page can render `<meta>` components itself. -There is one exception to this: if `<meta>` has an [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop) prop, there is no special behavior, because in this case it doesn’t represent metadata about the document but rather metadata about a specific part of the page. +There is one exception to this: if `<meta>` has an [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop) prop, there is no special behavior, because in this case it doesn’t represent metadata about the document but rather metadata about a specific part of the page. --- @@ -60,7 +60,7 @@ There is one exception to this: if `<meta>` has an [`itemProp`](https://develope ### Annotating the document with metadata {/*annotating-the-document-with-metadata*/} -You can annotate the document with metadata such as keywords, a summary, or the author’s name. React will place this metadata within the document `<head>` regardless of where in the React tree it is rendered. +You can annotate the document with metadata such as keywords, a summary, or the author’s name. React will place this metadata within the document `<head>` regardless of where in the React tree it is rendered. ```html <meta name="author" content="John Smith" /> @@ -91,7 +91,7 @@ export default function SiteMapPage() { ### Annotating specific items within the document with metadata {/*annotating-specific-items-within-the-document-with-metadata*/} -You can use the `<meta>` component with the `itemProp` prop to annotate specific items within the document with metadata. In this case, React will *not* place these annotations within the document `<head>` but will place them like any other React component. +You can use the `<meta>` component with the `itemProp` prop to annotate specific items within the document with metadata. In this case, React will *not* place these annotations within the document `<head>` but will place them like any other React component. ```js <section itemScope> diff --git a/src/content/reference/react-dom/components/option.md b/src/content/reference/react-dom/components/option.md index 84725854..7e0d9f00 100644 --- a/src/content/reference/react-dom/components/option.md +++ b/src/content/reference/react-dom/components/option.md @@ -79,5 +79,5 @@ export default function FruitPicker() { select { margin: 5px; } ``` -</Sandpack> +</Sandpack> diff --git a/src/content/reference/react-dom/components/script.md b/src/content/reference/react-dom/components/script.md index 50c0af80..b7f8afe7 100644 --- a/src/content/reference/react-dom/components/script.md +++ b/src/content/reference/react-dom/components/script.md @@ -53,7 +53,7 @@ Other supported props: * `integrity`: a string. A cryptographic hash of the script, to [verify its authenticity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). * `noModule`: a boolean. Disables the script in browsers that support ES modules — allowing for a fallback script for browsers that do not. * `nonce`: a string. A cryptographic [nonce to allow the resource](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. -* `referrer`: a string. Says [what Referer header to send](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#referrerpolicy) when fetching the script and any resources that the script fetches in turn. +* `referrer`: a string. Says [what Referer header to send](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#referrerpolicy) when fetching the script and any resources that the script fetches in turn. * `type`: a string. Says whether the script is a [classic script, ES module, or import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type). Props that disable React's [special treatment of scripts](#special-rendering-behavior): diff --git a/src/content/reference/react-dom/components/select.md b/src/content/reference/react-dom/components/select.md index 0dbdc153..977ba404 100644 --- a/src/content/reference/react-dom/components/select.md +++ b/src/content/reference/react-dom/components/select.md @@ -103,7 +103,7 @@ export default function FruitPicker() { select { margin: 5px; } ``` -</Sandpack> +</Sandpack> --- @@ -178,7 +178,7 @@ export default function FruitPicker() { select { margin: 5px; } ``` -</Sandpack> +</Sandpack> <Pitfall> diff --git a/src/content/reference/react-dom/components/title.md b/src/content/reference/react-dom/components/title.md index 9d6ed18c..cefe91ce 100644 --- a/src/content/reference/react-dom/components/title.md +++ b/src/content/reference/react-dom/components/title.md @@ -44,11 +44,11 @@ To specify the title of the document, render the [built-in browser `<title>` com #### Special rendering behavior {/*special-rendering-behavior*/} -React will always place the DOM element corresponding to the `<title>` component within the document’s `<head>`, regardless of where in the React tree it is rendered. The `<head>` is the only valid place for `<title>` to exist within the DOM, yet it’s convenient and keeps things composable if a component representing a specific page can render its `<title>` itself. +React will always place the DOM element corresponding to the `<title>` component within the document’s `<head>`, regardless of where in the React tree it is rendered. The `<head>` is the only valid place for `<title>` to exist within the DOM, yet it’s convenient and keeps things composable if a component representing a specific page can render its `<title>` itself. There are two exception to this: * If `<title>` is within an `<svg>` component, then there is no special behavior, because in this context it doesn’t represent the document’s title but rather is an [accessibility annotation for that SVG graphic](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title). -* If the `<title>` has an [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop) prop, there is no special behavior, because in this case it doesn’t represent the document’s title but rather metadata about a specific part of the page. +* If the `<title>` has an [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop) prop, there is no special behavior, because in this case it doesn’t represent the document’s title but rather metadata about a specific part of the page. <Pitfall> diff --git a/src/content/reference/react-dom/hooks/useFormStatus.md b/src/content/reference/react-dom/hooks/useFormStatus.md index 8733ec0c..a4cacabe 100644 --- a/src/content/reference/react-dom/hooks/useFormStatus.md +++ b/src/content/reference/react-dom/hooks/useFormStatus.md @@ -47,7 +47,7 @@ export default function App() { } ``` -To get status information, the `Submit` component must be rendered within a `<form>`. The Hook returns information like the <CodeStep step={1}>`pending`</CodeStep> property which tells you if the form is actively submitting. +To get status information, the `Submit` component must be rendered within a `<form>`. The Hook returns information like the <CodeStep step={1}>`pending`</CodeStep> property which tells you if the form is actively submitting. In the above example, `Submit` uses this information to disable `<button>` presses while the form is submitting. @@ -72,7 +72,7 @@ A `status` object with the following properties: #### Caveats {/*caveats*/} -* The `useFormStatus` Hook must be called from a component that is rendered inside a `<form>`. +* The `useFormStatus` Hook must be called from a component that is rendered inside a `<form>`. * `useFormStatus` will only return status information for a parent `<form>`. It will not return status information for any `<form>` rendered in that same component or children components. --- @@ -82,7 +82,7 @@ A `status` object with the following properties: ### Display a pending state during form submission {/*display-a-pending-state-during-form-submission*/} To display a pending state while a form is submitting, you can call the `useFormStatus` Hook in a component rendered in a `<form>` and read the `pending` property returned. -Here, we use the `pending` property to indicate the form is submitting. +Here, we use the `pending` property to indicate the form is submitting. <Sandpack> @@ -117,6 +117,7 @@ export async function submitForm(query) { await new Promise((res) => setTimeout(res, 1000)); } ``` +<<<<<<< HEAD ```json package.json hidden { @@ -130,6 +131,9 @@ export async function submitForm(query) { } ``` </Sandpack> +======= +</Sandpack> +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a <Pitfall> @@ -151,7 +155,7 @@ Instead call `useFormStatus` from inside a component that is located inside `<fo ```js function Submit() { // ✅ `pending` will be derived from the form that wraps the Submit component - const { pending } = useFormStatus(); + const { pending } = useFormStatus(); return <button disabled={pending}>...</button>; } @@ -246,7 +250,12 @@ export async function submitForm(query) { "devDependencies": {} } ``` +<<<<<<< HEAD </Sandpack> +======= + +</Sandpack> +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a --- @@ -254,7 +263,7 @@ export async function submitForm(query) { ### `status.pending` is never `true` {/*pending-is-never-true*/} -`useFormStatus` will only return status information for a parent `<form>`. +`useFormStatus` will only return status information for a parent `<form>`. If the component that calls `useFormStatus` is not nested in a `<form>`, `status.pending` will always return `false`. Verify `useFormStatus` is called in a component that is a child of a `<form>` element. diff --git a/src/content/reference/react-dom/preconnect.md b/src/content/reference/react-dom/preconnect.md index cb14a5e8..a244893e 100644 --- a/src/content/reference/react-dom/preconnect.md +++ b/src/content/reference/react-dom/preconnect.md @@ -41,7 +41,7 @@ function AppRoot() { [See more examples below.](#usage) -The `preconnect` function provides the browser with a hint that it should open a connection to the given server. If the browser chooses to do so, this can speed up the loading of resources from that server. +The `preconnect` function provides the browser with a hint that it should open a connection to the given server. If the browser chooses to do so, this can speed up the loading of resources from that server. #### Parameters {/*parameters*/} diff --git a/src/content/reference/react-dom/prefetchDNS.md b/src/content/reference/react-dom/prefetchDNS.md index f9889836..bbcca53d 100644 --- a/src/content/reference/react-dom/prefetchDNS.md +++ b/src/content/reference/react-dom/prefetchDNS.md @@ -41,7 +41,7 @@ function AppRoot() { [See more examples below.](#usage) -The prefetchDNS function provides the browser with a hint that it should look up the IP address of a given server. If the browser chooses to do so, this can speed up the loading of resources from that server. +The prefetchDNS function provides the browser with a hint that it should look up the IP address of a given server. If the browser chooses to do so, this can speed up the loading of resources from that server. #### Parameters {/*parameters*/} diff --git a/src/content/reference/react-dom/preinit.md b/src/content/reference/react-dom/preinit.md index 5a04528b..ae98d2bc 100644 --- a/src/content/reference/react-dom/preinit.md +++ b/src/content/reference/react-dom/preinit.md @@ -54,10 +54,15 @@ The `preinit` function provides the browser with a hint that it should start dow * `href`: a string. The URL of the resource you want to download and execute. * `options`: an object. It contains the following properties: * `as`: a required string. The type of resource. Its possible values are `script` and `style`. +<<<<<<< HEAD * `precedence`: a string. Required with stylesheets. Says where to insert the stylesheet relative to others. Stylesheets with higher precedence can override those with lower precedence. The possible values are `reset`, `low`, `medium`, `high`. * `crossOrigin`: a string. The [CORS policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) to use. Its possible values are `anonymous` and `use-credentials`. It is required when `as` is set to `"fetch"`. +======= + * `precedence`: a string. Required with stylesheets. Says where to insert the stylesheet relative to others. Stylesheets with higher precedence can override those with lower precedence. The possible values are `reset`, `low`, `medium`, `high`. + * `crossOrigin`: a string. The [CORS policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) to use. Its possible values are `anonymous` and `use-credentials`. +>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a * `integrity`: a string. A cryptographic hash of the resource, to [verify its authenticity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). - * `nonce`: a string. A cryptographic [nonce to allow the resource](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. + * `nonce`: a string. A cryptographic [nonce to allow the resource](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. * `fetchPriority`: a string. Suggests a relative priority for fetching the resource. The possible values are `auto` (the default), `high`, and `low`. #### Returns {/*returns*/} diff --git a/src/content/reference/react-dom/preinitModule.md b/src/content/reference/react-dom/preinitModule.md index ba6316a7..1efa5be7 100644 --- a/src/content/reference/react-dom/preinitModule.md +++ b/src/content/reference/react-dom/preinitModule.md @@ -56,7 +56,7 @@ The `preinitModule` function provides the browser with a hint that it should sta * `as`: a required string. It must be `'script'`. * `crossOrigin`: a string. The [CORS policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) to use. Its possible values are `anonymous` and `use-credentials`. * `integrity`: a string. A cryptographic hash of the module, to [verify its authenticity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). - * `nonce`: a string. A cryptographic [nonce to allow the module](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. + * `nonce`: a string. A cryptographic [nonce to allow the module](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. #### Returns {/*returns*/} diff --git a/src/content/reference/react-dom/preload.md b/src/content/reference/react-dom/preload.md index ffec1177..806782ce 100644 --- a/src/content/reference/react-dom/preload.md +++ b/src/content/reference/react-dom/preload.md @@ -58,7 +58,7 @@ The `preload` function provides the browser with a hint that it should start dow * `referrerPolicy`: a string. The [Referrer header](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#referrerpolicy) to send when fetching. Its possible values are `no-referrer-when-downgrade` (the default), `no-referrer`, `origin`, `origin-when-cross-origin`, and `unsafe-url`. * `integrity`: a string. A cryptographic hash of the resource, to [verify its authenticity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). * `type`: a string. The MIME type of the resource. - * `nonce`: a string. A cryptographic [nonce to allow the resource](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. + * `nonce`: a string. A cryptographic [nonce to allow the resource](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. * `fetchPriority`: a string. Suggests a relative priority for fetching the resource. The possible values are `auto` (the default), `high`, and `low`. * `imageSrcSet`: a string. For use only with `as: "image"`. Specifies the [source set of the image](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images). * `imageSizes`: a string. For use only with `as: "image"`. Specifies the [sizes of the image](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images). diff --git a/src/content/reference/react-dom/preloadModule.md b/src/content/reference/react-dom/preloadModule.md index 05333f1c..da6d6990 100644 --- a/src/content/reference/react-dom/preloadModule.md +++ b/src/content/reference/react-dom/preloadModule.md @@ -56,7 +56,7 @@ The `preloadModule` function provides the browser with a hint that it should sta * `as`: a required string. It must be `'script'`. * `crossOrigin`: a string. The [CORS policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) to use. Its possible values are `anonymous` and `use-credentials`. * `integrity`: a string. A cryptographic hash of the module, to [verify its authenticity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). - * `nonce`: a string. A cryptographic [nonce to allow the module](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. + * `nonce`: a string. A cryptographic [nonce to allow the module](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. #### Returns {/*returns*/} diff --git a/src/content/reference/react-dom/server/renderToPipeableStream.md b/src/content/reference/react-dom/server/renderToPipeableStream.md index 20a5960e..b027d1b3 100644 --- a/src/content/reference/react-dom/server/renderToPipeableStream.md +++ b/src/content/reference/react-dom/server/renderToPipeableStream.md @@ -294,7 +294,7 @@ Suspense **does not** detect when data is fetched inside an Effect or event hand The exact way you would load data in the `Posts` component above depends on your framework. If you use a Suspense-enabled framework, you'll find the details in its data fetching documentation. -Suspense-enabled data fetching without the use of an opinionated framework is not yet supported. The requirements for implementing a Suspense-enabled data source are unstable and undocumented. An official API for integrating data sources with Suspense will be released in a future version of React. +Suspense-enabled data fetching without the use of an opinionated framework is not yet supported. The requirements for implementing a Suspense-enabled data source are unstable and undocumented. An official API for integrating data sources with Suspense will be released in a future version of React. </Note> @@ -401,7 +401,7 @@ const { pipe } = renderToPipeableStream(<App />, { onShellError(error) { response.statusCode = 500; response.setHeader('content-type', 'text/html'); - response.send('<h1>Something went wrong</h1>'); + response.send('<h1>Something went wrong</h1>'); }, onError(error) { console.error(error); @@ -460,7 +460,7 @@ const { pipe } = renderToPipeableStream(<App />, { onShellError(error) { response.statusCode = 500; response.setHeader('content-type', 'text/html'); - response.send('<h1>Something went wrong</h1>'); + response.send('<h1>Something went wrong</h1>'); }, onError(error) { console.error(error); @@ -486,7 +486,7 @@ const { pipe } = renderToPipeableStream(<App />, { onShellError(error) { response.statusCode = 500; response.setHeader('content-type', 'text/html'); - response.send('<h1>Something went wrong</h1>'); + response.send('<h1>Something went wrong</h1>'); }, onError(error) { didError = true; @@ -530,7 +530,7 @@ const { pipe } = renderToPipeableStream(<App />, { onShellError(error) { response.statusCode = getStatusCode(); response.setHeader('content-type', 'text/html'); - response.send('<h1>Something went wrong</h1>'); + response.send('<h1>Something went wrong</h1>'); }, onError(error) { didError = true; @@ -570,13 +570,13 @@ const { pipe } = renderToPipeableStream(<App />, { onShellError(error) { response.statusCode = 500; response.setHeader('content-type', 'text/html'); - response.send('<h1>Something went wrong</h1>'); + response.send('<h1>Something went wrong</h1>'); }, onAllReady() { if (isCrawler) { response.statusCode = didError ? 500 : 200; response.setHeader('content-type', 'text/html'); - pipe(response); + pipe(response); } }, onError(error) { diff --git a/src/content/reference/react-dom/server/renderToReadableStream.md b/src/content/reference/react-dom/server/renderToReadableStream.md index f407f224..f3e86212 100644 --- a/src/content/reference/react-dom/server/renderToReadableStream.md +++ b/src/content/reference/react-dom/server/renderToReadableStream.md @@ -293,7 +293,7 @@ Suspense **does not** detect when data is fetched inside an Effect or event hand The exact way you would load data in the `Posts` component above depends on your framework. If you use a Suspense-enabled framework, you'll find the details in its data fetching documentation. -Suspense-enabled data fetching without the use of an opinionated framework is not yet supported. The requirements for implementing a Suspense-enabled data source are unstable and undocumented. An official API for integrating data sources with Suspense will be released in a future version of React. +Suspense-enabled data fetching without the use of an opinionated framework is not yet supported. The requirements for implementing a Suspense-enabled data source are unstable and undocumented. An official API for integrating data sources with Suspense will be released in a future version of React. </Note> diff --git a/src/content/reference/react-dom/server/resume.md b/src/content/reference/react-dom/server/resume.md new file mode 100644 index 00000000..ad61f6da --- /dev/null +++ b/src/content/reference/react-dom/server/resume.md @@ -0,0 +1,236 @@ +--- +title: resume +--- + +<Intro> + +`resume` streams a pre-rendered React tree to a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) + +```js +const stream = await resume(reactNode, postponedState, options?) +``` + +</Intro> + +<InlineToc /> + +<Note> + +This 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. + +</Note> + +--- + +## Reference {/*reference*/} + +### `resume(node, postponedState, options?)` {/*resume*/} + +Call `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) + +```js +import { resume } from 'react-dom/server'; +import {getPostponedState} from './storage'; + +async function handler(request, writable) { + const postponed = await getPostponedState(request); + const resumeStream = await resume(<App />, postponed); + return resumeStream.pipeTo(writable) +} +``` + +[See more examples below.](#usage) + +#### Parameters {/*parameters*/} + +* `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. +* `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). +* **optional** `options`: An object with streaming options. + * **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). + * **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. + * **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`. + + +#### Returns {/*returns*/} + +`resume` returns a Promise: + +- 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). +- If an error happens in the shell, the Promise will reject with that error. + +The returned stream has an additional property: + +* `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. + +#### Caveats {/*caveats*/} + +- `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. +- `resume` does not accept `identifierPrefix` since the prefix needs to be the same in both `prerender` and `resume`. +- Since `nonce` cannot be provided to prerender, you should only provide `nonce` to `resume` if you're not providing scripts to prerender. +- `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. + +## Usage {/*usage*/} + +### Resuming a prerender {/*resuming-a-prerender*/} + +<Sandpack> + +```js src/App.js hidden +``` + +```html public/index.html +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Document + + + + + +``` + +```js src/index.js +import { + flushReadableStreamToFrame, + getUser, + Postponed, + sleep, +} from "./demo-helpers"; +import { StrictMode, Suspense, use, useEffect } from "react"; +import { prerender } from "react-dom/static"; +import { resume } from "react-dom/server"; +import { hydrateRoot } from "react-dom/client"; + +function Header() { + return
Me and my descendants can be prerendered
; +} + +const { promise: cookies, resolve: resolveCookies } = Promise.withResolvers(); + +function Main() { + const { sessionID } = use(cookies); + const user = getUser(sessionID); + + useEffect(() => { + console.log("reached interactivity!"); + }, []); + + return ( +
+ Hello, {user.name}! + +
+ ); +} + +function Shell({ children }) { + // In a real app, this is where you would put your html and body. + // We're just using tags here we can include in an existing body for demonstration purposes + return ( + + {children} + + ); +} + +function App() { + return ( + + +
+ + +
+ + + ); +} + +async function main(frame) { + // Layer 1 + const controller = new AbortController(); + const prerenderedApp = prerender(, { + signal: controller.signal, + onError(error) { + if (error instanceof Postponed) { + } else { + console.error(error); + } + }, + }); + // We're immediately aborting in a macrotask. + // Any data fetching that's not available synchronously, or in a microtask, will not have finished. + setTimeout(() => { + controller.abort(new Postponed()); + }); + + const { prelude, postponed } = await prerenderedApp; + await flushReadableStreamToFrame(prelude, frame); + + // Layer 2 + // Just waiting here for demonstration purposes. + // In a real app, the prelude and postponed state would've been serialized in Layer 1 and Layer would deserialize them. + // The prelude content could be flushed immediated as plain HTML while + // React is continuing to render from where the prerender left off. + await sleep(2000); + + // You would get the cookies from the incoming HTTP request + resolveCookies({ sessionID: "abc" }); + + const stream = await resume(, postponed); + + await flushReadableStreamToFrame(stream, frame); + + // Layer 3 + // Just waiting here for demonstration purposes. + await sleep(2000); + + hydrateRoot(frame.contentWindow.document, ); +} + +main(document.getElementById("container")); + +``` + +```js src/demo-helpers.js +export async function flushReadableStreamToFrame(readable, frame) { + const document = frame.contentWindow.document; + const decoder = new TextDecoder(); + for await (const chunk of readable) { + const partialHTML = decoder.decode(chunk); + document.write(partialHTML); + } +} + +// This doesn't need to be an error. +// You can use any other means to check if an error during prerender was +// from an intentional abort or a real error. +export class Postponed extends Error {} + +// We're just hardcoding a session here. +export function getUser(sessionID) { + return { + name: "Alice", + }; +} + +export function sleep(timeoutMS) { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, timeoutMS); + }); +} +``` + + + +### Further reading {/*further-reading*/} + +Resuming behaves like `renderToReadableStream`. For more examples, check out the [usage section of `renderToReadableStream`](/reference/react-dom/server/renderToReadableStream#usage). +The [usage section of `prerender`](/reference/react-dom/static/prerender#usage) includes examples of how to use `prerender` specifically. \ No newline at end of file diff --git a/src/content/reference/react/Activity.md b/src/content/reference/react/Activity.md new file mode 100644 index 00000000..127a4b8d --- /dev/null +++ b/src/content/reference/react/Activity.md @@ -0,0 +1,1252 @@ +--- +title: +--- + + + +`` lets you hide and restore the UI and internal state of its children. + +```js + + + +``` + + + + + +--- + +## Reference {/*reference*/} + +### `` {/*activity*/} + +You can use Activity to hide part of your application: + +```js [[1, 1, "\\"hidden\\""], [2, 2, ""], [3, 1, "\\"visible\\""]] + + + +``` + +When an Activity boundary is hidden, React will visually hide its children using the `display: "none"` CSS property. It will also destroy their Effects, cleaning up any active subscriptions. + +While hidden, children still re-render in response to new props, albeit at a lower priority than the rest of the content. + +When the boundary becomes visible again, React will reveal the children with their previous state restored, and re-create their Effects. + +In this way, Activity can be thought of as a mechanism for rendering "background activity". Rather than completely discarding content that's likely to become visible again, you can use Activity to maintain and restore that content's UI and internal state, while ensuring that your hidden content has no unwanted side effects. + +[See more examples below.](#usage) + +#### Props {/*props*/} + +* `children`: The UI you intend to show and hide. +* `mode`: A string value of either `'visible'` or `'hidden'`. If omitted, defaults to `'visible'`. + +#### Caveats {/*caveats*/} + +- If an Activity is rendered inside of a [ViewTransition](/reference/react/ViewTransition), and it becomes visible as a result of an update caused by [startTransition](/reference/react/startTransition), it will activate the ViewTransition's `enter` animation. If it becomes hidden, it will activate its `exit` animation. +- A *hidden* Activity that just renders text will not render anything rather than rendering hidden text, because there’s no corresponding DOM element to apply visibility changes to. For example, `` will not produce any output in the DOM for `const ComponentThatJustReturnsText = () => "Hello, World!"`. `` will render visible text. + +--- + +## Usage {/*usage*/} + +### Restoring the state of hidden components {/*restoring-the-state-of-hidden-components*/} + +In React, when you want to conditionally show or hide a component, you typically mount or unmount it based on that condition: + +```jsx +{isShowingSidebar && ( + +)} +``` + +But unmounting a component destroys its internal state, which is not always what you want. + +When you hide a component using an Activity boundary instead, React will "save" its state for later: + +```jsx + + + +``` + +This makes it possible to hide and then later restore components in the state they were previously in. + +The following example has a sidebar with an expandable section. You can press "Overview" to reveal the three subitems below it. The main app area also has a button that hides and shows the sidebar. + +Try expanding the Overview section, and then toggling the sidebar closed then open: + + + +```js src/App.js active +import { useState } from 'react'; +import Sidebar from './Sidebar.js'; + +export default function App() { + const [isShowingSidebar, setIsShowingSidebar] = useState(true); + + return ( + <> + {isShowingSidebar && ( + + )} + +
+ +

Main content

+
+ + ); +} +``` + +```js src/Sidebar.js +import { useState } from 'react'; + +export default function Sidebar() { + const [isExpanded, setIsExpanded] = useState(false) + + return ( + + ); +} +``` + +```css +body { height: 275px; margin: 0; } +#root { + display: flex; + gap: 10px; + height: 100%; +} +nav { + padding: 10px; + background: #eee; + font-size: 14px; + height: 100%; +} +main { + padding: 10px; +} +p { + margin: 0; +} +h1 { + margin-top: 10px; +} +.indicator { + margin-left: 4px; + display: inline-block; + rotate: 90deg; +} +.indicator.down { + rotate: 180deg; +} +``` + +
+ +The Overview section always starts out collapsed. Because we unmount the sidebar when `isShowingSidebar` flips to `false`, all its internal state is lost. + +This is a perfect use case for Activity. We can preserve the internal state of our sidebar, even when visually hiding it. + +Let's replace the conditional rendering of our sidebar with an Activity boundary: + +```jsx {7,9} +// Before +{isShowingSidebar && ( + +)} + +// After + + + +``` + +and check out the new behavior: + + + +```js src/App.js active +import { Activity, useState } from 'react'; + +import Sidebar from './Sidebar.js'; + +export default function App() { + const [isShowingSidebar, setIsShowingSidebar] = useState(true); + + return ( + <> + + + + +
+ +

Main content

+
+ + ); +} +``` + +```js src/Sidebar.js +import { useState } from 'react'; + +export default function Sidebar() { + const [isExpanded, setIsExpanded] = useState(false) + + return ( + + ); +} +``` + +```css +body { height: 275px; margin: 0; } +#root { + display: flex; + gap: 10px; + height: 100%; +} +nav { + padding: 10px; + background: #eee; + font-size: 14px; + height: 100%; +} +main { + padding: 10px; +} +p { + margin: 0; +} +h1 { + margin-top: 10px; +} +.indicator { + margin-left: 4px; + display: inline-block; + rotate: 90deg; +} +.indicator.down { + rotate: 180deg; +} +``` + +
+ +Our sidebar's internal state is now restored, without any changes to its implementation. + +--- + +### Restoring the DOM of hidden components {/*restoring-the-dom-of-hidden-components*/} + +Since Activity boundaries hide their children using `display: none`, their children's DOM is also preserved when hidden. This makes them great for maintaining ephemeral state in parts of the UI that the user is likely to interact with again. + +In this example, the Contact tab has a `