🤖 作者简介:水煮白菜王 (juejin/csdn同名),一位资深前端劝退师 👻
👀 文章专栏: 前端专栏 ,记录一下平时在博客写作中,总结出的一些开发技巧✍。
感谢支持💕💕💕
目录
- [React v19,他来了!🚀](#React v19,他来了!🚀)
- [React v19 新特性一览及改进](#React v19 新特性一览及改进)
-
- Actions:
- 异步数据更新操作Actions,会自动管理提交数据
- [New hook: useActionState](#New hook: useActionState)
- [New hook: useOptimistic](#New hook: useOptimistic)
- [新API: use](#新API: use)
- [Server Components支持 ( RSC)](#Server Components支持 ( RSC))
- [React 19 中的改进](#React 19 中的改进)
-
- [ref作为 prop](#ref作为 prop)
- [支持 `<Context> `](#支持
<Context>
) - [ref 的回调清理](#ref 的回调清理)
- [支持Document Metadata](#支持Document Metadata)
- 支持stylesheets样式表
- 支持异步async脚本
- 支持预加载资源
- [更好的 Error报告及hydration错误改进](#更好的 Error报告及hydration错误改进)
- 支持自定义元素
React 19 的第一个版本是在2024年4月发布的,具体来说是4月25日发布了版本号为19.0.0-Beta的测试版。这个版本主要是面向各大库以确保它们与 React 19 的兼容性。因此,对于开发者而言,建议先升级至最新的稳定版... 所以
React v19,他来了!🚀
React 团队于 2024 年 12 月 5 日正式发布了 React v19 稳定版,现已在 npm 上可用。开发者们可以通过 React 19 升级指南获取详细的升级步骤和注意事项。本文将介绍在React v19 版本中给大家都带来了哪些新特性和改进。
React v19 新特性一览及改进
功能名称 | 描述 |
---|---|
Actions | Actions 现在支持异步数据更新操作,允许在 Transition 中执行副作用(如 API 请求)。每个 Transition 可以包含一个或多个 Actions,这些 Actions 在后台更新状态,最后通过一次提交来刷新 UI。此外,Actions 支持错误处理和乐观更新useOptimistic,确保用户界面在等待后台任务完成时保持响应。 |
useActioState | 这是一个新的 Hook,用于在 Transition 中调度 Actions进行排序。它提供了访问 Action 的状态和 pending 状态的能力。useAction 接受一个 reducer 函数,用于调用 Actions,以及一个初始状态对象。还可以接受一个可选字符串参数,当操作传递给 form prop 时,该字符串可用于表单中的渐进式增强。 |
useOptimistic | 此 Hook 用于在过渡期间立即更新状态,提供一种乐观更新的方式。它返回当前状态及一个 set 函数,可以在过渡中调用来即时应用预期的最终状态值。一旦过渡完成,状态会自动更新为实际的新值。 |
use | 这是 React 19 引入的一个新 API,允许在渲染过程中读取资源。它可以接受 Promise 或 Context 对象。如果提供的是 Promise,则 React 将暂停渲染,直到 Promise 解析完成。此功能只能在 render 方法中使用,但可以根据条件调用。 |
ref 作为 prop | Refs 现在可以直接作为 props 传递给子组件,无需显式使用 forwardRef 。这简化了 ref 的传递方式,使得代码更加简洁。 |
Suspense 同级预热 | 当组件挂起(例如等待数据加载)时,React 不再等待整个同级树渲染完毕,而是立即提交最近的 Suspense 边界的回退内容。之后,React 会继续为其他暂停的兄弟组件安排渲染,提前启动惰性请求,从而加快整体渲染速度。 这项改进显著提升了用户体验,特别是在加载多个异步组件时。通过尽早展示部分可用的内容,用户可以更快地开始与页面互动,而不需要等待所有内容都准备好。 |
简洁的 <Context> 写法 |
在 React 19 中,引入了更简洁的 Context 写法,可以直接使用 <Context> 代替 <Context.Provider> ,简化了上下文提供者的使用。 |
ref 的回调清理 | React 19 支持从回调返回清理函数,允许在组件卸载时执行回调清理逻辑,确保资源释放和避免内存泄漏。 |
Document Metadata 支持 | 现在 React v19 原生支持 <title> 、<meta> 和 <link> 等文档元数据标签。这些标签可直接在组件中声明,React 会自动将它们提升至 <head> 或 document 部分,简化 SEO 和元数据管理逻辑,确保与服务端渲染、客户端渲染、流式 SSR 和服务器组件兼容。 |
增强的样式表管理 | React 19 增强了样式表的加载管理,通过引入 precedence 属性,React 可以动态调整样式表的插入顺序,确保正确的样式覆盖。此外,React 解决了由于样式优先规则导致的复杂性,允许在组件内实现可组合性的样式表功能。React 现在能够管理 DOM 中样式表(无论是外部 <link rel="stylesheet" href="..."> 还是内联 <style>...</style> )的插入位置,并提供与客户端上的并发渲染和服务器上的流式渲染的更深入集成。如果你告诉 React 你的样式表,它将自动处理样式表的加载顺序,确保在显示依赖于这些样式规则的内容之前加载必要的样式表(特别是外部样式表)。 |
异步脚本支持 | 在 React 19 中,增强了对异步脚本的支持,允许在组件树的任何位置渲染脚本,并确保它们在所有渲染环境中被去重,从而只加载和执行一次,即使由多个组件渲染。这简化了脚本管理,避免了重复实例和重新定位的问题。 |
预加载资源支持 | React 19 引入了多项新 API,用于高效加载和预加载浏览器资源,从而简化构建高性能应用的过程。这些 API 确保资源加载不会成为用户体验的瓶颈,提供流畅、无阻碍的出色体验。 |
改进的错误处理 | React 19 改进了错误处理机制,消除了重复错误记录,并提供了处理捕获和未捕获错误的选项。现在,当渲染过程中出现错误并被 Error Boundary 捕获时,React 只会记录一次错误信息,并包含所有相关细节,避免了之前版本中多次抛出错误(如原始错误和自动恢复失败)的情况。此外,React 提供了关于错误发生位置的详细信息,帮助开发者更轻松地诊断和解决问题。 |
自定义元素支持 | React 19 增加了对自定义元素的全面支持,并通过了 Custom Elements Everywhere 的所有测试。这一改进解决了过去版本中使用自定义元素的困难,确保了 React 组件与 Web Components 标准无缝集成。使开发者能够更轻松地创建和使用自定义 HTML 元素,提升应用程序的灵活性和可维护性。 |
更多更新内容 👉 | react-blog 、GitHub发行版本v19.0.0 |
Actions:
React 应用程序中的一个常见用例是执行数据更改,然后更新状态作为响应。例如,当用户提交表单以更改其名称时,您将发出 API 请求,然后处理响应。过去,您需要手动处理待处理状态、错误、乐观更新和顺序请求。
在 React 19 中,增加了对在 transition 中使用异步函数的支持,以自动处理待处理Pending状态、错误、表单和乐观更新(useOptimistic)。
异步数据更新操作Actions,会自动管理提交数据
- Pending 待处理状态:操作提供待处理状态,该状态从请求开始时开始,并在提交最终状态更新时自动重置。
- Optimistic 更新:Actions 支持新的 useOptimistic 钩子,因此您可以在提交请求时向用户显示即时反馈。
- 错误处理:操作提供错误处理,因此您可以在请求失败时显示 Error Boundaries,并自动将乐观更新恢复为其原始值。
- Forms: 元素现在支持将函数传递给 和 props。将函数传递给 props 默认使用 Actions,并在提交后自动重置表单。
此功能增强了 Transitions 的灵活性,使其能够更好地处理复杂的 UI 更新逻辑。
typescript
// 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 (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
{error && <p>{error}</p>}
</div>
);
}
New hook: useActionState
React 19 构建在 Actions 之上,引入了 useOptimistic 来管理乐观更新,并引入了一个新的钩子 React.useActionState 来处理 Action 的常见情况。在添加 Actions 来自动管理表单,并使用 useFormStatus 来支持表单中 Actions 的常见情况。
通过这个 Hook,开发者可以在不直接管理复杂状态的情况下轻松地触发 Actions,并跟踪它们的状态变化。这对于构建交互性强的应用程序特别有用。
在 React 19 中,上面的例子可以简化为:
typescript
// Using <form> 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 (
<form action={submitAction}>
<input type="text" name="name" />
<button type="submit" disabled={isPending}>Update</button>
{error && <p>{error}</p>}
</form>
);
}
为了简化 Actions 的常见情况,新增了一个名为 :useActionState
typescript
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接受一个函数("Action"),并返回一个包装好的 Action 来调用。这之所以有效,是因为 Actions 组合。调用包装的 Action 时,将返回 Action 的最后一个结果为 ,并将 Action 的待处理状态返回为 。useActionState data pending
注意:
React.useActionState之前在 Canary 版本中调用,但已重命名并弃用 。ReactDOM.useFormStateuseFormState 。 useActionState文档
New hook: useOptimistic
这种方法提高了用户体验,因为用户不必等待服务器响应即可看到更改的结果
执行数据更改时,另一种常见的 UI 模式是在异步请求进行时乐观地显示最终状态。在 React 19 中,新增了一个名为 new hook 来简化此操作:useOptimistic
typescript
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>
);
}
当请求正在进行时,钩子将立即呈现。当更新完成或出错时,React 会自动切换回该值。useOptimistic optimisticName updateName currentName. useOptimistic文档
新API: use
此 API 的引入简化了从服务器获取数据的过程,同时保证了良好的用户体验。通过让 React 处理加载状态,开发者可以专注于构建应用程序的核心逻辑。
例如,你可以用 来读取一个 promise,React 会 Suspend 直到 promise 解析:use
typescript
import {use} from 'react';
function Comments({commentsPromise}) {
// `use` will suspend until the promise resolves.
const comments = use(commentsPromise);
return comments.map(comment => <p key={comment.id}>{comment}</p>);
}
function Page({commentsPromise}) {
// When `use` suspends in Comments,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Loading...</div>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
)
}
注意:
use不支持在 render 中创建的 Promise。如果你尝试将 render 中创建的 promise 传递给 ,React 会发出警告:
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.
要修复,需要从支持 Promise 缓存的 suspense 驱动的库或框架中传递一个 Promise。React官方后面会计划发布功能,以便更轻松地在 render 中缓存 promise。
Server Components支持 ( RSC)
React 19 的服务器组件提供了一种全新的渲染模式,在服务器上提前渲染组件,减少了客户端的渲染负担,并且无需引入额外工具链即可与现有项目无缝集成,支持在构建时或请求时生成组件。
React 19 中的改进
ref作为 prop
这一改变减少了不必要的样板代码,使 ref 的使用更加直观和方便,对于需要直接操作 DOM 元素的情况尤其有帮助。
从 React 19 开始,你现在可以作为函数组件的 prop 进行访问:ref
typescript
function MyInput({placeholder, ref}) {
return <input placeholder={placeholder} ref={ref} />
}
//...
<MyInput ref={ref} />
支持 <Context>
在 React v19 中,引入了简洁的 Context
写法,你可以渲染为 provider 而不是 :<Context><Context.Provider>
,可以直接使用 <Context>
代替 <Context.Provider>
typescript
const ThemeContext = createContext('');
function App({children}) {
return (
<ThemeContext value="dark">
{children}
</ThemeContext>
);
}
新的 Context providers 可以使用,React官方后面将发布一个 codemod 来转换现有的 providers。React官方在后面的版本中,会弃用
<Context><Context.Provider>
ref 的回调清理
React 19 支持从回调返回清理函数,允许在组件卸载时执行回调清理逻辑
typescript
<input
ref={(ref) => {
// ref created
// NEW: return a cleanup function to reset
// the ref when element is removed from DOM.
return () => {
// ref cleanup
};
}}
/>
支持Document Metadata
在 HTML 中,文档元数据标记保留用于放置在文档的部分中。在 React 中,决定哪些元数据适合应用程序的组件可能离你渲染的地方很远,或者 React 根本不渲染。过去,这些元素需要手动插入到 effect 中,或者通过 react-helmet 等库插入,并且在服务器渲染 React 应用程序时需要小心处理。
现在React v19 原生支持 <title>
、<meta>
和 <link>
等文档元数据标签,这些标签可直接在组件中声明,React 会自动将资源提升至 <head>
或 document 部分,简化 SEO 和元数据管理逻辑,确保与服务端渲染、客户端渲染、流式 SSR 和服务器组件兼容。
typescript
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>
);
}
注意:
在开发上有可能仍然需要一个 Metadata library库,对于简单的用例,将文档元数据呈现为标记可能比较合适,但库可以提供更强大的功能,例如根据当前路由使用特定元数据覆盖通用元数据。这些功能使 react-helmet 等框架和库更容易支持元数据标签,而不是直接替换。
支持stylesheets样式表
在 React 19 中,增强了样式表的加载管理,通过引入 precedence 属性,React 可以动态调整样式表的插入顺序,确保正确的样式覆盖。此外,React 解决了由于样式优先规则导致的复杂性,允许在组件内实现可组合性的样式表功能。
React 现在能够管理 DOM 中样式表(无论是外部 <link rel="stylesheet" href="..."> 还是内联 <style>...</style>
)的插入位置,并提供与客户端上的并发渲染和服务器上的流式渲染的更深入集成。如果你告诉 React 你的样式表,它将自动处理样式表的加载顺序,确保在显示依赖于这些样式规则的内容之前加载必要的样式表(特别是外部样式表)。
typescript
function ComponentOne() {
return (
<Suspense fallback="loading...">
<link rel="stylesheet" href="foo.css" precedence="default" />
<link rel="stylesheet" href="bar.css" precedence="high" />
<article class="foo-class bar-class">
{...}
</article>
</Suspense>
)
}
function ComponentTwo() {
return (
<div>
<p>{...}</p>
<link rel="stylesheet" href="baz.css" precedence="default" /> <-- will be inserted between foo & bar -->
</div>
)
}
支持异步async脚本
在 React 19 中,增强了对异步脚本的支持,允许在组件树的任何位置渲染脚本,并确保它们在所有渲染环境中被去重,从而只加载和执行一次,即使由多个组件渲染。这简化了脚本管理,避免了重复实例和重新定位的问题。
typescript
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>
}
支持预加载资源
React 19 引入了多项新 API,用于高效加载和预加载浏览器资源,从而简化构建高性能应用的过程。这些 API 确保资源加载不会成为用户体验的瓶颈,提供流畅、无阻碍的出色体验。
typescript
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
}
typescript
<!-- 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>
更好的 Error报告及hydration错误改进
React 19 改进了错误处理机制,消除了重复错误记录,并提供了处理捕获和未捕获错误的选项。现在,当渲染过程中出现错误并被 Error Boundary 捕获时,React 只会记录一次错误信息,并包含所有相关细节,避免了之前版本中多次抛出错误(如原始错误和自动恢复失败)的情况。此外,React 提供了关于错误发生位置的详细信息,提供更详细的差异化日志,帮助开发者更轻松地诊断和解决问题。
一个错误信息,其中包含所有错误信息,减少冗余错误日志
支持自定义元素
React 19 新增了对自定义元素的全面支持,完全兼容 Custom Elements Everywhere
的所有测试。这一改进解决了过去版本中使用自定义元素的困难,确保了 React 组件与 Web Components 标准无缝集成。使开发者能够更轻松地创建和使用自定义 HTML 元素,提升应用程序的灵活性和可维护性。
在 React 19 中,新增了对在客户端和 SSR 期间工作的属性的支持,策略如下:
- 服务器端渲染:如果其类型是 string、number 或等原始值或值为 true ,则传递给自定义元素的 props 将呈现为属性。具有非原始类型(如 object、symbol、 function或 value)的 props 将被省略。
- 客户端渲染:与 Custom Element 实例上的属性匹配的 props 将被分配为 properties,否则它们将被分配为 attributes。
React 19 引入了许多新特性,旨在提升开发体验、性能和灵活性。本文介绍了部分新增功能和对现有功能的优化改进,帮助开发者更好地理解和利用这些更新。
更多的更新内容请阅读 官方博客文档、GitHub发行版本v19.0.0
如果你觉得这篇文章对你有帮助,请点赞 👍、收藏 👏 并关注我!👀