diff --git a/src/components/Icon/IconRocket.tsx b/src/components/Icon/IconRocket.tsx new file mode 100644 index 0000000000..457736c7c5 --- /dev/null +++ b/src/components/Icon/IconRocket.tsx @@ -0,0 +1,32 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + */ + +import {memo} from 'react'; + +export const IconRocket = memo< + JSX.IntrinsicElements['svg'] & {title?: string; size?: 's' | 'md'} +>(function IconRocket({className, size = 'md'}) { + return ( + + ); +}); diff --git a/src/components/Layout/Sidebar/SidebarLink.tsx b/src/components/Layout/Sidebar/SidebarLink.tsx index 8a71d9e6e3..4429989d2f 100644 --- a/src/components/Layout/Sidebar/SidebarLink.tsx +++ b/src/components/Layout/Sidebar/SidebarLink.tsx @@ -16,7 +16,7 @@ interface SidebarLinkProps { selected?: boolean; title: string; level: number; - canary?: boolean; + version?: 'canary' | 'major'; icon?: React.ReactNode; isExpanded?: boolean; hideArrow?: boolean; @@ -27,7 +27,7 @@ export function SidebarLink({ href, selected = false, title, - canary, + version, level, isExpanded, hideArrow, @@ -75,10 +75,17 @@ export function SidebarLink({ {/* This here needs to be refactored ofc */}
{title}{' '} - {canary && ( + {version === 'major' && ( + + React 19 + + )} + {version === 'canary' && ( )}
diff --git a/src/components/Layout/Sidebar/SidebarRouteTree.tsx b/src/components/Layout/Sidebar/SidebarRouteTree.tsx index 3f058073cb..54f02b9252 100644 --- a/src/components/Layout/Sidebar/SidebarRouteTree.tsx +++ b/src/components/Layout/Sidebar/SidebarRouteTree.tsx @@ -87,7 +87,7 @@ export function SidebarRouteTree({ path, title, routes, - canary, + version, heading, hasSectionHeader, sectionHeader, @@ -121,7 +121,7 @@ export function SidebarRouteTree({ selected={selected} level={level} title={title} - canary={canary} + version={version} isExpanded={isExpanded} hideArrow={isForceExpanded} /> @@ -145,7 +145,7 @@ export function SidebarRouteTree({ selected={selected} level={level} title={title} - canary={canary} + version={version} /> ); diff --git a/src/components/Layout/getRouteMeta.tsx b/src/components/Layout/getRouteMeta.tsx index 3564dd7385..b3d14725d4 100644 --- a/src/components/Layout/getRouteMeta.tsx +++ b/src/components/Layout/getRouteMeta.tsx @@ -19,8 +19,8 @@ export type RouteTag = export interface RouteItem { /** Page title (for the sidebar) */ title: string; - /** Optional canary flag for heading */ - canary?: boolean; + /** Optional version flag for heading */ + version?: 'canary' | 'major'; /** Optional page description for heading */ description?: string; /* Additional meta info for page tagging */ diff --git a/src/components/MDX/ExpandableCallout.tsx b/src/components/MDX/ExpandableCallout.tsx index 8354160a7b..f51684fde3 100644 --- a/src/components/MDX/ExpandableCallout.tsx +++ b/src/components/MDX/ExpandableCallout.tsx @@ -8,8 +8,16 @@ import {IconNote} from '../Icon/IconNote'; import {IconWarning} from '../Icon/IconWarning'; import {IconPitfall} from '../Icon/IconPitfall'; import {IconCanary} from '../Icon/IconCanary'; +import {IconRocket} from '../Icon/IconRocket'; -type CalloutVariants = 'deprecated' | 'pitfall' | 'note' | 'wip' | 'canary'; +type CalloutVariants = + | 'deprecated' + | 'pitfall' + | 'note' + | 'wip' + | 'canary' + | 'major' + | 'rsc'; interface ExpandableCalloutProps { children: React.ReactNode; @@ -59,6 +67,22 @@ const variantMap = { overlayGradient: 'linear-gradient(rgba(249, 247, 243, 0), rgba(249, 247, 243, 1)', }, + major: { + title: 'React 19', + Icon: IconRocket, + containerClasses: 'bg-blue-10 dark:bg-blue-60 dark:bg-opacity-20', + textColor: 'text-blue-50 dark:text-blue-40', + overlayGradient: + 'linear-gradient(rgba(249, 247, 243, 0), rgba(249, 247, 243, 1)', + }, + rsc: { + title: 'React Server Components', + Icon: null, + containerClasses: 'bg-blue-10 dark:bg-blue-60 dark:bg-opacity-20', + textColor: 'text-blue-50 dark:text-blue-40', + overlayGradient: + 'linear-gradient(rgba(249, 247, 243, 0), rgba(249, 247, 243, 1)', + }, }; function ExpandableCallout({children, type = 'note'}: ExpandableCalloutProps) { @@ -72,9 +96,11 @@ function ExpandableCallout({children, type = 'note'}: ExpandableCalloutProps) { variant.containerClasses )}>

- + {variant.Icon && ( + + )} {variant.title}

diff --git a/src/components/MDX/MDXComponents.tsx b/src/components/MDX/MDXComponents.tsx index 38a0100399..d2e7d3b34c 100644 --- a/src/components/MDX/MDXComponents.tsx +++ b/src/components/MDX/MDXComponents.tsx @@ -97,6 +97,14 @@ const Canary = ({children}: {children: React.ReactNode}) => ( {children} ); +const NextMajor = ({children}: {children: React.ReactNode}) => ( + {children} +); + +const RSC = ({children}: {children: React.ReactNode}) => ( + {children} +); + const CanaryBadge = ({title}: {title: string}) => ( ( ); +const NextMajorBadge = ({title}: {title: string}) => ( + + React 19 + +); + +const RSCBadge = ({title}: {title: string}) => ( + + RSC + +); + const Blockquote = ({ children, ...props @@ -483,6 +511,10 @@ export const MDXComponents = { Note, Canary, CanaryBadge, + NextMajor, + NextMajorBadge, + RSC, + RSCBadge, PackageImport, ReadBlogPost, Recap, diff --git a/src/components/MDX/Sandpack/template.ts b/src/components/MDX/Sandpack/template.ts index 9ead18a14e..42f02f6a68 100644 --- a/src/components/MDX/Sandpack/template.ts +++ b/src/components/MDX/Sandpack/template.ts @@ -28,8 +28,8 @@ root.render( eject: 'react-scripts eject', }, dependencies: { - react: '^18.0.0', - 'react-dom': '^18.0.0', + react: '19.0.0-rc-3edc000d-20240926', + 'react-dom': '19.0.0-rc-3edc000d-20240926', 'react-scripts': '^5.0.0', }, }, diff --git a/src/content/blog/2024/04/25/react-19-upgrade-guide.md b/src/content/blog/2024/04/25/react-19-upgrade-guide.md index 68ef9420d9..fd160e943b 100644 --- a/src/content/blog/2024/04/25/react-19-upgrade-guide.md +++ b/src/content/blog/2024/04/25/react-19-upgrade-guide.md @@ -1,5 +1,5 @@ --- -title: "React 19 RC Upgrade Guide" +title: "React 19 Upgrade Guide" author: Ricky Hanlon date: 2024/04/25 description: The improvements added to React 19 require some breaking changes, but we've worked to make the upgrade as smooth as possible and we don't expect the changes to impact most apps. In this post, we will guide you through the steps for upgrading apps and libraries to React 19. @@ -12,7 +12,7 @@ April 25, 2024 by [Ricky Hanlon](https://twitter.com/rickhanlonii) -The improvements added to React 19 RC require some breaking changes, but we've worked to make the upgrade as smooth as possible, and we don't expect the changes to impact most apps. +The improvements added to React 19 require some breaking changes, but we've worked to make the upgrade as smooth as possible, and we don't expect the changes to impact most apps. @@ -38,7 +38,7 @@ In this post, we will guide you through the steps for upgrading to React 19: - [TypeScript changes](#typescript-changes) - [Changelog](#changelog) -If you'd like to help us test React 19, follow the steps in this upgrade guide and [report any issues](https://github.com/facebook/react/issues/new?assignees=&labels=React+19&projects=&template=19.md&title=%5BReact+19%5D) you encounter. For a list of new features added to React 19, see the [React 19 release post](/blog/2024/04/25/react-19). +If you'd like to help us test React 19, follow the steps in this upgrade guide and [report any issues](https://github.com/facebook/react/issues/new?assignees=&labels=React+19&projects=&template=19.md&title=%5BReact+19%5D) you encounter. For a list of new features added to React 19, see the [React 19 release post](/blog/2024/12/05/react-19). --- ## Installing {/*installing*/} @@ -70,28 +70,23 @@ We expect most apps will not be affected since the transform is enabled in most To install the latest version of React and React DOM: ```bash -npm install --save-exact react@rc react-dom@rc +npm install --save-exact react@^19.0.0 react-dom@^19.0.0 ``` Or, if you're using Yarn: ```bash -yarn add --exact react@rc react-dom@rc +yarn add --exact react@^19.0.0 react-dom@^19.0.0 ``` -If you're using TypeScript, you also need to update the types. Once React 19 is released as stable, you can install the types as usual from `@types/react` and `@types/react-dom`. Until the stable release, the types are available in different packages which need to be enforced in your `package.json`: +If you're using TypeScript, you also need to update the types. +```bash +npm install --save-exact @types/react@^19.0.0 @types/react-dom@^19.0.0 +``` -```json -{ - "dependencies": { - "@types/react": "npm:types-react@rc", - "@types/react-dom": "npm:types-react-dom@rc" - }, - "overrides": { - "@types/react": "npm:types-react@rc", - "@types/react-dom": "npm:types-react-dom@rc" - } -} +Or, if you're using Yarn: +```bash +yarn add --exact @types/react@^19.0.0 @types/react-dom@^19.0.0 ``` We're also including a codemod for the most common replacements. See [TypeScript changes](#typescript-changes) below. @@ -752,7 +747,7 @@ const reducer = (state: State, action: Action) => state; - **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) -We'll publish the full changelog with the stable release of React 19. +For a full list of changes, please see the [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md#1900-december-5-2024). --- diff --git a/src/content/blog/2024/05/22/react-conf-2024-recap.md b/src/content/blog/2024/05/22/react-conf-2024-recap.md index a77f0c830f..19019a42b4 100644 --- a/src/content/blog/2024/05/22/react-conf-2024-recap.md +++ b/src/content/blog/2024/05/22/react-conf-2024-recap.md @@ -17,7 +17,7 @@ description: 上周我们在内华达州亨德森举办了为期两天的 React --- -在 React Conf 2024 上,我们宣布了 [React 19 RC](/blog/2024/04/25/react-19)、[React Native 新架构 Beta 版](https://github.com/reactwg/react-native-new-architecture/discussions/189),以及 [React Compiler](/learn/react-compiler) 的实验版本。社区同时登台宣布了 [React Router v7](https://remix.run/blog/merging-remix-and-react-router)、Expo Router 中的 [通用服务器组件](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=20765s)、[RedwoodJS](https://redwoodjs.com/blog/rsc-now-in-redwoodjs) 中的 React 服务器组件等等。 +在 React Conf 2024 上,我们宣布了 [React 19 RC](/blog/2024/12/05/react-19)、[React Native 新架构 Beta 版](https://github.com/reactwg/react-native-new-architecture/discussions/189),以及 [React Compiler](/learn/react-compiler) 的实验版本。社区同时登台宣布了 [React Router v7](https://remix.run/blog/merging-remix-and-react-router)、Expo Router 中的 [通用服务器组件](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=20765s)、[RedwoodJS](https://redwoodjs.com/blog/rsc-now-in-redwoodjs) 中的 React 服务器组件等等。 完整的 [第一天](https://www.youtube.com/watch?v=T8TZQ6k4SLE) 和 [第二天](https://www.youtube.com/watch?v=0ckOUBiuxVY) 的直播已经可以在线观看了。在这篇文章中,我们将总结活动中的演讲和公告。 @@ -36,7 +36,7 @@ _[点击这里观看第一天完整直播。](https://www.youtube.com/watch?v=T8 - [RedwoodJS 现已支持 React 服务器组件](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=26815s),来自 [Amy Dutton](https://twitter.com/selfteachme) - [介绍 Expo Router 中的通用 React 服务器组件](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=20765s),来自 [Evan Bacon](https://twitter.com/Baconbrix) -接下来的主题演讲中,[Josh Story](https://twitter.com/joshcstory) 和 [Andrew Clark](https://twitter.com/acdlite) 分享了 React 19 即将推出的新功能,并宣布了 React 19 RC,该版本已准备好用于生产环境测试。你可以在 [React 19 RC](/blog/2024/04/25/react-19) 这篇文章中查看 React 19 的全部功能,也可以查看以下关于新功能深入分析的演讲: +接下来的主题演讲中,[Josh Story](https://twitter.com/joshcstory) 和 [Andrew Clark](https://twitter.com/acdlite) 分享了 React 19 即将推出的新功能,并宣布了 React 19 RC,该版本已准备好用于生产环境测试。你可以在 [React 19 RC](/blog/2024/12/05/react-19) 这篇文章中查看 React 19 的全部功能,也可以查看以下关于新功能深入分析的演讲: - [React 19 有哪些更新](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=8880s),来自 [Lydia Hallie](https://twitter.com/lydiahallie) - [React 解读:React 19 路线图](https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=10112s),来自 [Sam Selikoff](https://twitter.com/samselikoff) diff --git a/src/content/blog/2024/04/25/react-19.md b/src/content/blog/2024/12/05/react-19.md similarity index 92% rename from src/content/blog/2024/04/25/react-19.md rename to src/content/blog/2024/12/05/react-19.md index 2aff514778..6494c49426 100644 --- a/src/content/blog/2024/04/25/react-19.md +++ b/src/content/blog/2024/12/05/react-19.md @@ -1,21 +1,33 @@ --- -title: "React 19 RC" +title: "React v19" author: The React Team -date: 2024/04/25 -description: React 19 RC 版现在可以在 npm 上使用了! 在这篇文章中,我们将概述 React 19 的新特性,以及如何使用它们。 +date: 2024/12/05 +description: React 19 版现在可以在 npm 上使用了! 在这篇文章中,我们将概述 React 19 的新特性,以及如何使用它们。 --- -2024 年 4 月 25 日 [The React Team](/community/team) +2024 年 12 月 25 日 [The React Team](/community/team) --- + + +### React 19 现已稳定! {/*react-19-is-now-stable*/} + +从 4 月份发布这篇介绍 React 19 RC 的博客以来有以下新增内容: + +- **预热 suspend 树**:阅读 [改善 Suspense](/blog/2024/04/25/react-19-upgrade-guide#improvements-to-suspense) 来了解更多。 +- **React DOM 静态 API**:阅读 [新的 React DOM 静态 API](#new-react-dom-static-apis) 来了解更多。 + +__本文的日期已更新,以反映稳定版的发布日期。__ + + -React 19 RC 版本现在可以在 npm 上使用了! +React 19 版本现在可以在 npm 上使用了! -在我们的 [React 19 RC 升级指南](/blog/2024/04/25/react-19-upgrade-guide) 中, 我们分享了将应用程序升级到 React 19 的分步说明。在这篇文章中,我们将概述 React 19 的新特性,以及如何使用它们。 +在我们的 [React 19 升级指南](/blog/2024/04/25/react-19-upgrade-guide) 中, 我们分享了将应用程序升级到 React 19 的分步说明。在这篇文章中,我们将概述 React 19 的新特性,以及如何使用它们。 - [React 19 中的新功能](#whats-new-in-react-19) - [React 19 中的改进](#improvements-in-react-19) @@ -312,6 +324,30 @@ function Heading({children}) { 有关更多信息,请参阅 [`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 服务器组件 {/*react-server-components*/} @@ -771,5 +807,4 @@ React 19 添加了对自定义元素的全面支持,并通过了 [Custom Eleme 请查看 [React 19 升级指南](/blog/2024/04/25/react-19-upgrade-guide) 以获取逐步指导和完整的破坏性和显著变化列表。 - - +__注意:这篇文章最初发布于 2024 年 4 月 25 日,并已将内容更新至 2024 年 12 月 5 日发布的稳定版本。__ \ No newline at end of file diff --git a/src/content/blog/index.md b/src/content/blog/index.md index fe678cb057..3f5d5a0ddd 100644 --- a/src/content/blog/index.md +++ b/src/content/blog/index.md @@ -10,25 +10,25 @@ title: React Blog
- + -我们在 React Conf 2024 上宣布了 React Compiler 的实验性版本。从那时起我们已经取得了很多进展,在这篇文章中,我们将分享 React Compiler 下一步要做的事情…… +在我们的 React 19 升级指南中, 我们分享了将应用程序升级到 React 19 的分步说明。在这篇文章中,我们将概述 React 19 的新特性,以及如何使用它们…… - + -上周我们在内华达州亨德森举办了为期两天的 React Conf 2024 大会,有 700 多名与会者亲临现场,讨论 UI 工程领域的最新进展。这是我们自 2019 年以来首次举办线下会议,我们很高兴能够再次将社区团结在一起…… +我们在 React Conf 2024 上宣布了 React Compiler 的实验性版本。从那时起我们已经取得了很多进展,在这篇文章中,我们将分享 React Compiler 下一步要做的事情…… - + -在我们的 React 19 RC 升级指南中, 我们分享了将应用程序升级到 React 19 的分步说明。在这篇文章中,我们将概述 React 19 的新特性,以及如何使用它们…… +上周我们在内华达州亨德森举办了为期两天的 React Conf 2024 大会,有 700 多名与会者亲临现场,讨论 UI 工程领域的最新进展。这是我们自 2019 年以来首次举办线下会议,我们很高兴能够再次将社区团结在一起…… - + React 19 中新增的改进需要一些破坏性变更,但我们已经尽力让升级过程尽可能平滑,我们预计这些变更不会影响大多数应用。在这篇文章中,我们将指导你完成将库升级到 React 19 的步骤…… diff --git a/src/content/learn/manipulating-the-dom-with-refs.md b/src/content/learn/manipulating-the-dom-with-refs.md index f255a69940..fe0668c5aa 100644 --- a/src/content/learn/manipulating-the-dom-with-refs.md +++ b/src/content/learn/manipulating-the-dom-with-refs.md @@ -258,11 +258,11 @@ export default function CatFriends() { key={cat} ref={(node) => { const map = getMap(); - if (node) { - map.set(cat, node); - } else { + map.set(cat, node); + + return () => { map.delete(cat); - } + }; }} > @@ -311,16 +311,6 @@ li { } ``` -```json package.json hidden -{ - "dependencies": { - "react": "canary", - "react-dom": "canary", - "react-scripts": "^5.0.0" - } -} -``` - 在这个例子中,`itemsRef` 保存的不是单个 DOM 节点,而是保存了包含列表项 ID 和 DOM 节点的 [Map](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map)。([Ref 可以保存任何值!](/learn/referencing-values-with-refs)) 每个列表项上的 [`ref` 回调](/reference/react-dom/components/common#ref-callback)负责更新 Map: @@ -330,40 +320,26 @@ li { key={cat.id} ref={node => { const map = getMap(); - if (node) { - // Add to the Map - map.set(cat, node); - } else { - // Remove from the Map + // 添加到 Map 中 + map.set(cat, node); + + return () => { + // 从 Map 中移除 map.delete(cat); - } + }; }} > ``` 这使你可以之后从 Map 读取单个 DOM 节点。 - + -这个例子展示了另一种使用 `ref` 回调清理函数来管理 Map 的方法。 +启用严格模式后,ref 回调将在开发中运行两次。 -```js -
  • { - const map = getMap(); - // Add to the Map - map.set(cat, node); - - return () => { - // Remove from the Map - map.delete(cat); - }; - }} -> -``` +阅读更多这将 [如何帮助你在 ref 回调中找到 bug](/reference/react/StrictMode#fixing-bugs-found-by-re-running-ref-callbacks-in-development) 的细节。 - + diff --git a/src/content/reference/react-dom/client/createRoot.md b/src/content/reference/react-dom/client/createRoot.md index 67751950e5..dc69de27c7 100644 --- a/src/content/reference/react-dom/client/createRoot.md +++ b/src/content/reference/react-dom/client/createRoot.md @@ -45,8 +45,8 @@ root.render(); * **可选** `options`:用于配置这个 React 根节点的对象。 - * **optional** `onCaughtError`: Callback called when React catches an error in an Error Boundary. Called with the `error` caught by the Error Boundary, and an `errorInfo` object containing the `componentStack`. - * **optional** `onUncaughtError`: Callback called when an error is thrown and not caught by an Error Boundary. Called with the `error` that was thrown, and an `errorInfo` object containing the `componentStack`. + * **optional** `onCaughtError`: Callback called when React catches an error in an Error Boundary. Called with the `error` caught by the Error Boundary, and an `errorInfo` object containing the `componentStack`. + * **optional** `onUncaughtError`: Callback called when an error is thrown and not caught by an Error Boundary. Called with the `error` that was thrown, and an `errorInfo` object containing the `componentStack`. * **optional** `onRecoverableError`: Callback called when React automatically recovers from errors. Called with an `error` React throws, and an `errorInfo` object containing the `componentStack`. Some recoverable errors may include the original error cause as `error.cause`. * **optional** `identifierPrefix`: A string prefix React uses for IDs generated by [`useId`.](/reference/react/useId) Useful to avoid conflicts when using multiple roots on the same page. @@ -346,12 +346,6 @@ export default function App({counter}) { ### Show a dialog for uncaught errors {/*show-a-dialog-for-uncaught-errors*/} - - -`onUncaughtError` is only available in the latest React Canary release. - - - By default, React will log all uncaught errors to the console. To implement your own error reporting, you can provide the optional `onUncaughtError` root option: ```js [[1, 6, "onUncaughtError"], [2, 6, "error", 1], [3, 6, "errorInfo"], [4, 10, "componentStack"]] @@ -578,28 +572,11 @@ export default function App() { } ``` -```json package.json hidden -{ - "dependencies": { - "react": "canary", - "react-dom": "canary", - "react-scripts": "^5.0.0" - }, - "main": "/index.js" -} -``` - ### Displaying Error Boundary errors {/*displaying-error-boundary-errors*/} - - -`onCaughtError` is only available in the latest React Canary release. - - - By default, React will log all errors caught by an Error Boundary to `console.error`. To override this behavior, you can provide the optional `onCaughtError` root option to handle errors caught by an [Error Boundary](/reference/react/Component#catching-rendering-errors-with-an-error-boundary): ```js [[1, 6, "onCaughtError"], [2, 6, "error", 1], [3, 6, "errorInfo"], [4, 10, "componentStack"]] @@ -865,8 +842,8 @@ function Throw({error}) { ```json package.json hidden { "dependencies": { - "react": "canary", - "react-dom": "canary", + "react": "19.0.0-rc-3edc000d-20240926", + "react-dom": "19.0.0-rc-3edc000d-20240926", "react-scripts": "^5.0.0", "react-error-boundary": "4.0.3" }, @@ -1123,8 +1100,8 @@ function Throw({error}) { ```json package.json hidden { "dependencies": { - "react": "canary", - "react-dom": "canary", + "react": "19.0.0-rc-3edc000d-20240926", + "react-dom": "19.0.0-rc-3edc000d-20240926", "react-scripts": "^5.0.0", "react-error-boundary": "4.0.3" }, diff --git a/src/content/reference/react-dom/client/hydrateRoot.md b/src/content/reference/react-dom/client/hydrateRoot.md index db2a9072bb..729ca5ca3c 100644 --- a/src/content/reference/react-dom/client/hydrateRoot.md +++ b/src/content/reference/react-dom/client/hydrateRoot.md @@ -43,8 +43,8 @@ React 将会连接到内部有 `domNode` 的 HTML 上,然后接管其中的 `d * **可选** `options`:一个包含此 React 根元素选项的对象。 - * **optional** `onCaughtError`: Callback called when React catches an error in an Error Boundary. Called with the `error` caught by the Error Boundary, and an `errorInfo` object containing the `componentStack`. - * **optional** `onUncaughtError`: Callback called when an error is thrown and not caught by an Error Boundary. Called with the `error` that was thrown and an `errorInfo` object containing the `componentStack`. + * **optional** `onCaughtError`: Callback called when React catches an error in an Error Boundary. Called with the `error` caught by the Error Boundary, and an `errorInfo` object containing the `componentStack`. + * **optional** `onUncaughtError`: Callback called when an error is thrown and not caught by an Error Boundary. Called with the `error` that was thrown and an `errorInfo` object containing the `componentStack`. * **optional** `onRecoverableError`: Callback called when React automatically recovers from errors. Called with the `error` React throws, and an `errorInfo` object containing the `componentStack`. Some recoverable errors may include the original error cause as `error.cause`. * **optional** `identifierPrefix`: A string prefix React uses for IDs generated by [`useId`.](/reference/react/useId) Useful to avoid conflicts when using multiple roots on the same page. Must be the same prefix as used on the server. @@ -378,12 +378,6 @@ It is uncommon to call [`root.render`](#root-render) on a hydrated root. Usually ### Show a dialog for uncaught errors {/*show-a-dialog-for-uncaught-errors*/} - - -`onUncaughtError` is only available in the latest React Canary release. - - - By default, React will log all uncaught errors to the console. To implement your own error reporting, you can provide the optional `onUncaughtError` root option: ```js [[1, 7, "onUncaughtError"], [2, 7, "error", 1], [3, 7, "errorInfo"], [4, 11, "componentStack"]] @@ -614,28 +608,11 @@ export default function App() { } ``` -```json package.json hidden -{ - "dependencies": { - "react": "canary", - "react-dom": "canary", - "react-scripts": "^5.0.0" - }, - "main": "/index.js" -} -``` - ### Displaying Error Boundary errors {/*displaying-error-boundary-errors*/} - - -`onCaughtError` is only available in the latest React Canary release. - - - By default, React will log all errors caught by an Error Boundary to `console.error`. To override this behavior, you can provide the optional `onCaughtError` root option for errors caught by an [Error Boundary](/reference/react/Component#catching-rendering-errors-with-an-error-boundary): ```js [[1, 7, "onCaughtError"], [2, 7, "error", 1], [3, 7, "errorInfo"], [4, 11, "componentStack"]] @@ -904,8 +881,8 @@ function Throw({error}) { ```json package.json hidden { "dependencies": { - "react": "canary", - "react-dom": "canary", + "react": "19.0.0-rc-3edc000d-20240926", + "react-dom": "19.0.0-rc-3edc000d-20240926", "react-scripts": "^5.0.0", "react-error-boundary": "4.0.3" }, @@ -1166,8 +1143,8 @@ function Throw({error}) { ```json package.json hidden { "dependencies": { - "react": "canary", - "react-dom": "canary", + "react": "19.0.0-rc-3edc000d-20240926", + "react-dom": "19.0.0-rc-3edc000d-20240926", "react-scripts": "^5.0.0", "react-error-boundary": "4.0.3" }, diff --git a/src/content/reference/react-dom/components/common.md b/src/content/reference/react-dom/components/common.md index f7e6208872..8a6ad6a3af 100644 --- a/src/content/reference/react-dom/components/common.md +++ b/src/content/reference/react-dom/components/common.md @@ -246,43 +246,41 @@ title: "普通组件(例如
    )" 与 [`useRef`](/reference/react/useRef#manipulating-the-dom-with-a-ref) 返回的 ref 对象不同,可以将函数传递给 `ref` 属性。 ```js -
    console.log(node)} /> +
    { + console.log('Attached', node); + + return () => { + console.log('Clean up', node) + } +}}> ``` -[查看使用 `ref` 回调函数的示例](/learn/manipulating-the-dom-with-refs#how-to-manage-a-list-of-refs-using-a-ref-callback)。 +[查看使用 `ref` 回调函数的示例](/learn/manipulating-the-dom-with-refs#how-to-manage-a-list-of-refs-using-a-ref-callback) -当 `
    ` DOM 节点被添加到屏幕上时,React 将使用该节点作为参数调用 `ref` 回调函数。 +当 `
    ` DOM 节点被添加到屏幕上时,React 将使用该节点作为参数调用 `ref` 回调函数。当这个 `
    ` DOM 节点被移除的时候, React 将调用回调返回的清理函数。 当传递不同的` ref `回调时,React 也会调用 `ref` 回调。在上面的示例中,`(node) => { ... }` 在每次渲染时都是一个不同的函数。当组件重新渲染时,先前的函数将被调用并传递 `null` 作为参数,并且下一个函数将被调用并传递对应 DOM 节点作为参数。 #### 参数 {/*ref-callback-parameters*/} -* `node`:DOM 节点或 `null`。当回调函数被附加在 `ref` 属性后,触发回调时,该参数为对应的 DOM 节点。当 ref 被分离时值为 `null`。除非在每次渲染时都传递相同的函数引用作为 `ref` 回调,否则该回调将在组件的每次重新渲染期间被暂时分离和重新连接。 +* `node`: DOM 节点。当 ref 被附加时,React 会向你传递 DOM 节点作为参数。除非你在每次渲染时为 `ref` 的回调函数传递相同引用,否则回调将在组件的每次重新渲染期间临时清理并重新创建。 - + -#### 返回值 {/*returns*/} +#### React 19 添加了 `ref` 回调的清理函数。{/*react-19-added-cleanup-functions-for-ref-callbacks*/} -* **optional** `cleanup function`: When the `ref` is detached, React will call the cleanup function. If a function is not returned by the `ref` callback, React will call the callback again with `null` as the argument when the `ref` gets detached. +为了向后兼容,如果 `ref` 回调未返回清理函数,当 `ref` 被分离时 `node` 会被作为 `null` 传递。此行为将在未来版本中删除。 -```js + -
    { - console.log(node); - - return () => { - console.log('Clean up', node) - } -}}> +#### 返回值 {/*returns*/} -``` +* **可选的** `清理函数`:当 `ref` 被分离时,React 会调用清理函数。如果 `ref` 回调未返回清理函数,React 会再次使用 `null` 作为参数调用回调函数。此行为将在未来版本中删除。 #### Caveats {/*caveats*/} * When Strict Mode is on, React will **run one extra development-only setup+cleanup cycle** before the first real setup. This is a stress-test that ensures that your cleanup logic "mirrors" your setup logic and that it stops or undoes whatever the setup is doing. If this causes a problem, implement the cleanup function. -* When you pass a *different* `ref` callback, React will call the *previous* callback's cleanup function if provided. If not cleanup function is defined, the `ref` callback will be called with `null` as the argument. The *next* function will be called with the DOM node. - - +* When you pass a *different* `ref` callback, React will call the *previous* callback's cleanup function if provided. If no cleanup function is defined, the `ref` callback will be called with `null` as the argument. The *next* function will be called with the DOM node. --- diff --git a/src/content/reference/react-dom/components/form.md b/src/content/reference/react-dom/components/form.md index 8f6ab00e00..047b65aa7e 100644 --- a/src/content/reference/react-dom/components/form.md +++ b/src/content/reference/react-dom/components/form.md @@ -1,15 +1,7 @@ --- title: "
    " -canary: true --- - - -React's extensions to `` are currently only available in React's canary and experimental channels. In stable releases of React, `` works only as a [built-in browser HTML component](https://react.dev/reference/react-dom/components#all-html-components). Learn more about [React's release channels here](/community/versioning-policy#all-release-channels). - - - - The [built-in browser `` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) lets you create interactive controls for submitting information. @@ -77,27 +69,15 @@ export default function Search() { } ``` -```json package.json hidden -{ - "dependencies": { - "react": "18.3.0-canary-6db7f4209-20231021", - "react-dom": "18.3.0-canary-6db7f4209-20231021", - "react-scripts": "^5.0.0" - }, - "main": "/index.js", - "devDependencies": {} -} -``` - -### Handle form submission with a Server Action {/*handle-form-submission-with-a-server-action*/} +### Handle form submission with a Server Function {/*handle-form-submission-with-a-server-function*/} -Render a `` with an input and submit button. Pass a Server Action (a function marked with [`'use server'`](/reference/rsc/use-server)) to the `action` prop of form to run the function when the form is submitted. +Render a `` with an input and submit button. Pass a Server Function (a function marked with [`'use server'`](/reference/rsc/use-server)) to the `action` prop of form to run the function when the form is submitted. -Passing a Server Action to `` allow users to submit forms without JavaScript enabled or before the code has loaded. This is beneficial to users who have a slow connection, device, or have JavaScript disabled and is similar to the way forms work when a URL is passed to the `action` prop. +Passing a Server Function to `` allow users to submit forms without JavaScript enabled or before the code has loaded. This is beneficial to users who have a slow connection, device, or have JavaScript disabled and is similar to the way forms work when a URL is passed to the `action` prop. -You can use hidden form fields to provide data to the ``'s action. The Server Action will be called with the hidden form field data as an instance of [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData). +You can use hidden form fields to provide data to the ``'s action. The Server Function will be called with the hidden form field data as an instance of [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData). ```jsx import { updateCart } from './lib.js'; @@ -137,7 +117,7 @@ function AddToCart({productId}) { } ``` -When `` is rendered by a [Server Component](/reference/rsc/use-client), and a [Server Action](/reference/rsc/use-server) is passed to the ``'s `action` prop, the form is [progressively enhanced](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement). +When `` is rendered by a [Server Component](/reference/rsc/use-client), and a [Server Function](/reference/rsc/server-function) is passed to the ``'s `action` prop, the form is [progressively enhanced](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement). ### Display a pending state during form submission {/*display-a-pending-state-during-form-submission*/} To display a pending state when a form is being submitted, you can call the `useFormStatus` Hook in a component rendered in a `` and read the `pending` property returned. @@ -178,17 +158,6 @@ export async function submitForm(query) { } ``` -```json package.json hidden -{ - "dependencies": { - "react": "canary", - "react-dom": "canary", - "react-scripts": "^5.0.0" - }, - "main": "/index.js", - "devDependencies": {} -} -``` To learn more about the `useFormStatus` Hook see the [reference documentation](/reference/react-dom/hooks/useFormStatus). @@ -258,19 +227,6 @@ export async function deliverMessage(message) { } ``` - -```json package.json hidden -{ - "dependencies": { - "react": "18.3.0-canary-6db7f4209-20231021", - "react-dom": "18.3.0-canary-6db7f4209-20231021", - "react-scripts": "^5.0.0" - }, - "main": "/index.js", - "devDependencies": {} -} -``` - [//]: # 'Uncomment the next line, and delete this line after the `useOptimistic` reference documentatino page is published' @@ -306,8 +262,8 @@ export default function Search() { ```json package.json hidden { "dependencies": { - "react": "18.3.0-canary-6db7f4209-20231021", - "react-dom": "18.3.0-canary-6db7f4209-20231021", + "react": "19.0.0-rc-3edc000d-20240926", + "react-dom": "19.0.0-rc-3edc000d-20240926", "react-scripts": "^5.0.0", "react-error-boundary": "4.0.3" }, @@ -323,10 +279,10 @@ export default function Search() { Displaying a form submission error message before the JavaScript bundle loads for progressive enhancement requires that: 1. `` be rendered by a [Server Component](/reference/rsc/use-client) -1. the function passed to the ``'s `action` prop be a [Server Action](/reference/rsc/use-server) +1. the function passed to the ``'s `action` prop be a [Server Function](/reference/rsc/server-functions) 1. the `useActionState` Hook be used to display the error message -`useActionState` takes two parameters: a [Server Action](/reference/rsc/use-server) and an initial state. `useActionState` returns two values, a state variable and an action. The action returned by `useActionState` should be passed to the `action` prop of the form. The state variable returned by `useActionState` can be used to displayed an error message. The value returned by the [Server Action](/reference/rsc/use-server) passed to `useActionState` will be used to update the state variable. +`useActionState` takes two parameters: a [Server Function](/reference/rsc/server-functions) and an initial state. `useActionState` returns two values, a state variable and an action. The action returned by `useActionState` should be passed to the `action` prop of the form. The state variable returned by `useActionState` can be used to display an error message. The value returned by the Server Function passed to `useActionState` will be used to update the state variable. @@ -372,18 +328,6 @@ export async function signUpNewUser(newEmail) { } ``` -```json package.json hidden -{ - "dependencies": { - "react": "canary", - "react-dom": "canary", - "react-scripts": "^5.0.0" - }, - "main": "/index.js", - "devDependencies": {} -} -``` - Learn more about updating state from a form action with the [`useActionState`](/reference/react/useActionState) docs @@ -420,16 +364,4 @@ export default function Search() { } ``` -```json package.json hidden -{ - "dependencies": { - "react": "18.3.0-canary-6db7f4209-20231021", - "react-dom": "18.3.0-canary-6db7f4209-20231021", - "react-scripts": "^5.0.0" - }, - "main": "/index.js", - "devDependencies": {} -} -``` - diff --git a/src/content/reference/react-dom/components/input.md b/src/content/reference/react-dom/components/input.md index 2fa50a4b3a..6868186035 100644 --- a/src/content/reference/react-dom/components/input.md +++ b/src/content/reference/react-dom/components/input.md @@ -32,13 +32,7 @@ title: "" `` 支持所有 [常见的元素属性](/reference/react-dom/components/common#props)。 - - -React's extensions to the `formAction` prop are currently only available in React's Canary and experimental channels. In stable releases of React, `formAction` works only as a [built-in browser HTML component](/reference/react-dom/components#all-html-components). Learn more about [React's release channels here](/community/versioning-policy#all-release-channels). - - - -[`formAction`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formaction): A string or function. Overrides the parent `` for `type="submit"` and `type="image"`. When a URL is passed to `action` the form will behave like a standard HTML form. When a function is passed to `formAction` the function will handle the form submission. See [``](/reference/react-dom/components/form#props). +- [`formAction`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formaction): A string or function. Overrides the parent `` for `type="submit"` and `type="image"`. When a URL is passed to `action` the form will behave like a standard HTML form. When a function is passed to `formAction` the function will handle the form submission. See [``](/reference/react-dom/components/form#props). You can [make an input controlled](#controlling-an-input-with-a-state-variable) by passing one of these props: diff --git a/src/content/reference/react-dom/components/link.md b/src/content/reference/react-dom/components/link.md index ade2c178f1..961e3d7de6 100644 --- a/src/content/reference/react-dom/components/link.md +++ b/src/content/reference/react-dom/components/link.md @@ -1,15 +1,7 @@ --- link: "" -canary: true --- - - -React 对 `` 的扩展当前仅在 React Canary 与 experimental 渠道中可用。在 React 的稳定版本中,`` 仅作为 [浏览器内置 HTML 组件](https://react.dev/reference/react-dom/components#all-html-components) 使用。 -请在 [此处了解更多关于 React 发布渠道的信息](/community/versioning-policy#all-release-channels)。 - - - 浏览器内置的 `` 组件允许使用外部资源,例如样式表,或使用链接元数据注释文档。 diff --git a/src/content/reference/react-dom/components/meta.md b/src/content/reference/react-dom/components/meta.md index 285e5ad6bf..381faaf3e8 100644 --- a/src/content/reference/react-dom/components/meta.md +++ b/src/content/reference/react-dom/components/meta.md @@ -1,15 +1,7 @@ --- meta: "" -canary: true --- - - -React 对 `` 的扩展当前仅在 React Canary 与 experimental 渠道中可用。在 React 的稳定版本中,`` 仅作为 [浏览器内置 HTML 组件](https://react.dev/reference/react-dom/components#all-html-components) 使用。请在 [此处了解更多关于 React 发布渠道的信息](/community/versioning-policy#all-release-channels)。 - - - - [浏览器内置的 `` 组件](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta) 允许向文档添加元数据。 diff --git a/src/content/reference/react-dom/components/script.md b/src/content/reference/react-dom/components/script.md index 5cd3af6787..7f449dccb1 100644 --- a/src/content/reference/react-dom/components/script.md +++ b/src/content/reference/react-dom/components/script.md @@ -1,14 +1,7 @@ --- script: " +``` + +On the client, your bootstrap script should [hydrate the entire `document` with a call to `hydrateRoot`:](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document) + +```js [[1, 4, ""]] +import { hydrateRoot } from 'react-dom/client'; +import App from './App.js'; + +hydrateRoot(document, ); +``` + +This will attach event listeners to the static server-generated HTML and make it interactive. + + + +#### Reading CSS and JS asset paths from the build output {/*reading-css-and-js-asset-paths-from-the-build-output*/} + +The final asset URLs (like JavaScript and CSS files) are often hashed after the build. For example, instead of `styles.css` you might end up with `styles.123456.css`. Hashing static asset filenames guarantees that every distinct build of the same asset will have a different filename. This is useful because it lets you safely enable long-term caching for static assets: a file with a certain name would never change content. + +However, if you don't know the asset URLs until after the build, there's no way for you to put them in the source code. For example, hardcoding `"/styles.css"` into JSX like earlier wouldn't work. To keep them out of your source code, your root component can read the real filenames from a map passed as a prop: + +```js {1,6} +export default function App({ assetMap }) { + return ( + + + My app + + + ... + + ); +} +``` + +On the server, render `` and pass your `assetMap` with the asset URLs: + +```js {1-5,8,9} +// You'd need to get this JSON from your build tooling, e.g. read it from the build output. +const assetMap = { + 'styles.css': '/styles.123456.css', + 'main.js': '/main.123456.js' +}; + +async function handler(request) { + const {prelude} = await prerender(, { + bootstrapScripts: [assetMap['/main.js']] + }); + return new Response(prelude, { + headers: { 'content-type': 'text/html' }, + }); +} +``` + +Since your server is now rendering ``, you need to render it with `assetMap` on the client too to avoid hydration errors. You can serialize and pass `assetMap` to the client like this: + +```js {9-10} +// You'd need to get this JSON from your build tooling. +const assetMap = { + 'styles.css': '/styles.123456.css', + 'main.js': '/main.123456.js' +}; + +async function handler(request) { + const {prelude} = await prerender(, { + // Careful: It's safe to stringify() this because this data isn't user-generated. + bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`, + bootstrapScripts: [assetMap['/main.js']], + }); + return new Response(prelude, { + headers: { 'content-type': 'text/html' }, + }); +} +``` + +In the example above, the `bootstrapScriptContent` option adds an extra inline ` +``` + +On the client, your bootstrap script should [hydrate the entire `document` with a call to `hydrateRoot`:](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document) + +```js [[1, 4, ""]] +import { hydrateRoot } from 'react-dom/client'; +import App from './App.js'; + +hydrateRoot(document, ); +``` + +This will attach event listeners to the static server-generated HTML and make it interactive. + + + +#### Reading CSS and JS asset paths from the build output {/*reading-css-and-js-asset-paths-from-the-build-output*/} + +The final asset URLs (like JavaScript and CSS files) are often hashed after the build. For example, instead of `styles.css` you might end up with `styles.123456.css`. Hashing static asset filenames guarantees that every distinct build of the same asset will have a different filename. This is useful because it lets you safely enable long-term caching for static assets: a file with a certain name would never change content. + +However, if you don't know the asset URLs until after the build, there's no way for you to put them in the source code. For example, hardcoding `"/styles.css"` into JSX like earlier wouldn't work. To keep them out of your source code, your root component can read the real filenames from a map passed as a prop: + +```js {1,6} +export default function App({ assetMap }) { + return ( + + + My app + + + ... + + ); +} +``` + +On the server, render `` and pass your `assetMap` with the asset URLs: + +```js {1-5,8,9} +// You'd need to get this JSON from your build tooling, e.g. read it from the build output. +const assetMap = { + 'styles.css': '/styles.123456.css', + 'main.js': '/main.123456.js' +}; + +app.use('/', async (request, response) => { + const { prelude } = await prerenderToNodeStream(, { + bootstrapScripts: [assetMap['/main.js']] + }); + + response.setHeader('Content-Type', 'text/html'); + prelude.pipe(response); +}); +``` + +Since your server is now rendering ``, you need to render it with `assetMap` on the client too to avoid hydration errors. You can serialize and pass `assetMap` to the client like this: + +```js {9-10} +// You'd need to get this JSON from your build tooling. +const assetMap = { + 'styles.css': '/styles.123456.css', + 'main.js': '/main.123456.js' +}; + +app.use('/', async (request, response) => { + const { prelude } = await prerenderToNodeStream(, { + // Careful: It's safe to stringify() this because this data isn't user-generated. + bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`, + bootstrapScripts: [assetMap['/main.js']], + }); + + response.setHeader('Content-Type', 'text/html'); + prelude.pipe(response); +}); +``` + +In the example above, the `bootstrapScriptContent` option adds an extra inline `