参考 React 19 官网
Actions
支持异步函数
待定状态: 提供一个待定状态,该状态在请求开始时启动,并在最终状态更新提交时自动重置
乐观更新: 支持新的 useOptimistic Hook,可以在请求提交时向用户显示即时反馈
错误处理: 当请求失败时,可以显示错误边界,并自动将乐观更新恢复到其原始值
表单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 function ChangeName ({ name, setName } ) { const [error, submitAction, isPending] = useActionState ( async (previousState, formData) => { const error = await updateName (formData.get ("name" )); if (error) { return error; } return null ; }, null ); return ( <form action ={submitAction} > <input type ="text" name ="name" /> <button type ="submit" disabled ={isPending} > Update </button > {error && <p > {error}</p > } </form > ); }
useActionState
接受一个函数 (Action),并返回一个被包装的用于调用的 Action。这是因为 Actions 是可以组合的。当调用被包装的 Action 时,useActionState 将返回 Action 的最后结果作为 data,以及 Action 的待定状态作为 pending
新 <form>
功能集成在 react-dom 中。我们已经添加了对将函数作为 <form>
、<input>
和 <button>
元素的 action 和 formAction 属性的支持,以便使用 Actions 自动提交表单 当 <form>
Action 成功时,React 将自动为非受控组件重置表单。如果需要手动重置 <form>
,你可以调用新的 requestFormReset React DOM API
1 <form action={actionFunction}>
在设计系统中,常常需要编写设计一类能够访问其所在的 <form>
的信息而无需将属性传递到组件内的组件。这可以通过 Context 来实现,但为了使这类常见情况更简单,我们添加了一个新的 Hook useFormStatus
1 2 3 4 5 6 import { useFormStatus } from "react-dom" ;function DesignButton ( ) { const { pending } = useFormStatus (); return <button type ="submit" disabled ={pending} /> ; }
useOptimistic
执行数据变更时的另一个常见 UI 模式是在异步请求进行时乐观地显示最终状态 useOptimistic 会在 updateName 请求进行时立即渲染 optimisticName。当更新完成或出错时,React 将自动切换回 currentName 值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function ChangeName ({ currentName, onUpdateName } ) { const [optimisticName, setOptimisticName] = useOptimistic (currentName); const submitAction = async (formData ) => { const newName = formData.get ("name" ); setOptimisticName (newName); const updatedName = await updateName (newName); onUpdateName (updatedName); }; return ( <form action ={submitAction} > <p > Your name is: {optimisticName}</p > <p > <label > Change Name:</label > <input type ="text" name ="name" disabled ={currentName !== optimisticName} /> </p > </form > ); }
use
在渲染中读取资源。例如,可以使用 use 读取一个 promise,React 将挂起,直到 promise 解析完成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { use } from "react" ;function Comments ({ commentsPromise } ) { const comments = use (commentsPromise); return comments.map ((comment ) => <p key ={comment.id} > {comment}</p > ); } function Page ({ commentsPromise } ) { return ( <Suspense fallback ={ <div > Loading...</div > }> <Comments commentsPromise ={commentsPromise} /> </Suspense > ); }
可以在循环和条件语句(如 if)中调用 use。但需要注意的是,调用 use 的函数仍然必须是一个组件或 Hook
1 2 3 4 5 6 7 8 9 10 11 12 13 import { use } from "react" ;import ThemeContext from "./ThemeContext" ;function Heading ({ children } ) { if (children == null ) { return null ; } const theme = use (ThemeContext ); return <h1 style ={{ color: theme.color }}> {children}</h1 > ; }
读取 context
当 context 被传递给 use 时,它的工作方式类似于 useContext。而 useContext 必须在组件的顶层调用,use 可以在条件语句如 if 和循环如 for 内调用。相比之下,use 比 useContext 更加灵活
1 2 3 4 5 import { use } from 'react' ;function Button ( ) { const theme = use (ThemeContext );
数据传递
数据可以通过将 Promise 作为 prop 从 服务器组件 传递到 客户端组件,以从服务器流式传输到客户端
将来自服务器组件的 Promise 传递至客户端组件时,其解析值必须可序列化以在服务器和客户端之间传递。像函数这样的数据类型不可序列化,不能成为这种 Promise 的解析值
1 2 3 4 5 6 7 8 9 10 11 import { fetchMessage } from "./lib.js" ;import { Message } from "./message.js" ;export default function App ( ) { const messagePromise = fetchMessage (); return ( <Suspense fallback ={ <p > waiting for message...</p > }> <Message messagePromise ={messagePromise} /> </Suspense > ); }
客户端组件将 从 prop 中接收到的 Promise 传递给 use API。这允许客户端组件从最初由服务器组件创建的 Promise 中读取值
1 2 3 4 5 6 7 8 9 "use client" ;import { use } from "react" ;export function Message ({ messagePromise } ) { const messageContent = use (messagePromise); return <p > Here is the message: {messageContent}</p > ; }
rejected Promise
传递给 use 的 Promise 可能会被拒绝(rejected)。可以通过以下方式处理 rejected Promise
ErrorBoundary 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 "use client" ;import { use, Suspense } from "react" ;import { ErrorBoundary } from "react-error-boundary" ;export function MessageContainer ({ messagePromise } ) { return ( <ErrorBoundary fallback ={ <p > ⚠️Something went wrong</p > }> <Suspense fallback ={ <p > ⌛Downloading message...</p > }> <Message messagePromise ={messagePromise} /> </Suspense > </ErrorBoundary > ); } function Message ({ messagePromise } ) { const content = use (messagePromise); return <p > Here is the message: {content}</p > ; }
Promise.catch 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { Message } from "./message.js" ;export default function App ( ) { const messagePromise = new Promise ((resolve, reject ) => { reject (); }).catch (() => { return "no new message found." ; }); return ( <Suspense fallback ={ <p > waiting for message...</p > }> <Message messagePromise ={messagePromise} /> </Suspense > ); }
React DOM Static APIs prerender
使用 Web 流将 React 树渲染为静态 HTML 字符串
1 const {prelude} = await prerender (reactNode, options?)
prerenderToNodeStream
使用 Node.js 流将 React 树渲染为静态 HTML 字符串
1 const {prelude} = await prerenderToNodeStream (reactNode, options?)
Server Components
一种全新的组件渲染模式,允许在打包前提前渲染组件,与客户端应用程序或 SSR 服务器在不同的环境中。这个独立的环境就是 React 服务器组件中的 “服务器”。服务器组件可以在你的 CI 服务器上在构建时运行一次,或者可以在每次请求时使用 web 服务器运行
支持在构建时或请求时生成组件
无需引入额外的工具链,即可与现有 React 项目集成
ref 属性
现在可以在函数组件中将 ref 作为 prop 进行访问,新的函数组件将不再需要 forwardRef。在未来的版本中,我们将弃用并移除 forwardRef 在类组件中,ref 不作为 props 传递,因为它们引用的是组件实例。这意味着,如果你在类组件中需要访问 ref,你需要使用 React.forwardRef 或者 React.createRef
函数清理
使得在 ref 改变时执行清理操作变得更加容易,当组件卸载时,React 将调用从 ref 回调返回的清理函数。例如,你可以在 ref 改变时取消订阅事件
1 2 3 4 5 6 7 8 9 10 11 <input ref={(ref ) => { return () => { }; }} />
支持 <title>
、<meta>
和 <link>
等文档元数据标签。这些标签可直接在组件中声明,React 会自动将它们提升至 <head>
,并确保与服务端渲染和客户端渲染兼容,简化 SEO 和元数据管理逻辑
1 2 3 4 5 6 7 8 9 10 11 12 function BlogPost ({ post } ) { return ( <article > <h1 > {post.title}</h1 > <title > {post.title}</title > <meta name ="author" content ="Josh" /> <link rel ="author" href ="https://twitter.com/joshcstory/" /> <meta name ="keywords" content ={post.keywords} /> <p > Eee equals em-see-squared...</p > </article > ); }
样式表
增强了样式表的加载管理,通过指定 precedence 属性,React 可以动态调整样式表的插入顺序,确保正确的样式覆盖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 > { /* will be inserted between foo & bar */} <link rel ="stylesheet" href ="baz" precedence ="default" /> </div > ) }
Async 脚本
在 HTML 中,普通脚本 (<script src="...">)
和延迟脚本 (<script defer="" src="...">)
按照文档顺序加载,这使得在组件树深处渲染这些类型的脚本变得具有挑战性。然而,异步脚本 (<script async="" src="...">)
将去重并以任意顺序加载
1 2 3 4 5 6 7 8 function MyComponent ( ) { return ( <div > <script async ={true} src ="..." /> Hello World </div > ); }
预加载
preload 和 preinit 指定浏览器提前加载的资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { prefetchDNS, preconnect, preload, preinit } from "react-dom" ;function MyComponent ( ) { preinit ("https://.../path/to/some/script.js" , { as : "script" }); preload ("https://.../path/to/font.woff" , { as : "font" }); preload ("https://.../path/to/stylesheet.css" , { as : "style" }); prefetchDNS ("https://..." ); preconnect ("https://..." ); }
错误报告
改进了错误日志系统,减少了重复日志,并添加了更详细的调试信息。例如,对于 SSR 和客户端渲染不匹配的问题,提供了差异化日志
onCaughtError
: 当 React 在错误边界中捕获错误时调用
onUncaughtError
: 当抛出错误并且未被错误边界捕获时调用
onRecoverableError
: 当抛出错误并自动恢复时调用
Context
可以将 <Context>
渲染为提供者,无需再使用 <Context.Provider>
1 2 3 4 5 const ThemeContext = createContext ("" );function App ({ children } ) { return <ThemeContext value ="dark" > {children}</ThemeContext > ; }