React 19 核心更新全面解析
2024年12月 正式稳定版发布 | 📚 学习指南
📑 目录
- Actions - 革命性的数据提交方式
- 新 Hooks 详解
- use API - 渲染中读取资源
- React Server Components
- ref 作为 prop - 告别 forwardRef
- Context 作为 Provider
- ref 清理函数
- 文档元数据原生支持
- 样式表原生支持
- 资源预加载 API
- 其他重要改进
1. Actions - 革命性的数据提交方式
什么是 Actions?
Actions 是 React 19 引入的约定,用于在表单提交和数据变更时自动处理:
- ✅ Pending 状态 - 自动管理加载状态
- ✅ 乐观更新 - 即时反馈用户体验
- ✅ 错误处理 - 自动显示错误边界
- ✅ 表单重置 - 提交成功后自动重置
对比:Before vs After
以前的写法:
// 手动管理 pending、error 状态
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");
};
}
React 19 的写法:
// 使用 Actions + useActionState
function ChangeName({}) {
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 本质上是在
useTransition中执行的异步函数,React 会自动管理 pending 状态!
2. 新 Hooks 详解
useActionState - 处理 Actions 的常见场景
const [data, action, isPending] = useActionState(actionFn, initialState);
// 返回值:
// - data: Action 的返回结果
// - action: 包装后的 Action 函数
// - isPending: 是否正在处理
简化了 Actions 的状态管理,是之前 useFormState 的升级版。
useOptimistic - 乐观更新
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>
<input name="name" disabled={currentName !== optimisticName} />
</form>
);
}
在请求完成前就显示更新后的 UI,请求失败时自动回滚。
useFormStatus - 获取表单状态
import {useFormStatus} from 'react-dom';
function DesignButton() {
const {pending} = useFormStatus();
return <button type="submit" disabled={pending} />;
}
在子组件中获取父表单的 pending 状态,无需 props 传递!
3. use API - 渲染中读取资源
use() 可以在渲染中读取 Promise 和 Context
读取 Promise(自动 Suspense)
import {use} from 'react';
function Comments({commentsPromise}) {
const comments = use(commentsPromise); // 会 Suspense
return comments.map(c => <p key={c.id}>{c}</p>);
}
function Page({commentsPromise}) {
return (
<Suspense fallback={<div>Loading...</div>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
);
}
读取 Context(支持条件调用)
import {use} from 'react';
import ThemeContext from './ThemeContext';
function Heading({children}) {
if (children == null) return null; // 早期返回
const theme = use(ThemeContext); // ✅ 这里可以用 use()
return <h1 style={{color: theme.color}}>{children}</h1>;
}
⚠️ 注意:
use()不支持在渲染中创建的 Promise!需要从外部传入已缓存的 Promise。
4. React Server Components
什么是 Server Components?
在服务端运行的组件,优势包括:
- 🔒 安全 - 敏感代码(如数据库查询)不会暴露给客户端
- ⚡ 性能 - 减少客户端 JavaScript 体积
- 📦 直接访问后端资源 - 可以直接调用数据库、文件系统
Server Actions
// 定义 Server Action
async function addTodo(formData) {
'use server'; // 标记为服务端函数
const title = formData.get('title');
await db.todos.create({ title });
}
// 在客户端使用
<form action={addTodo}>
<input name="title" />
<button type="submit">Add</button>
</form>
💡 注意:
'use server'是用于 Server Actions,Server Components 没有特殊的 directive!
5. ref 作为 prop - 告别 forwardRef
直接接收 ref 作为 prop
// React 19 - 不需要 forwardRef
function MyInput({placeholder, ref}) {
return <input placeholder={placeholder} ref={ref} />
}
// 使用
<MyInput ref={inputRef} />
React 19 会自动处理 ref,不需要 forwardRef 包装。未来版本将废弃 forwardRef。
6. Context 作为 Provider
直接使用 Context 作为 Provider
// 以前
<ThemeContext.Provider value="dark">
{children}
</ThemeContext.Provider>
// React 19 - 直接使用
<ThemeContext value="dark">
{children}
</ThemeContext>
7. ref 清理函数
ref 回调可以返回清理函数
<input
ref={(ref) => {
// 创建时执行
console.log('ref created:', ref);
// 返回清理函数 - 组件卸载时执行
return () => {
console.log('ref cleanup');
};
}}
/>
⚠️ 重要变化: 如果 ref 回调返回了清理函数,React 不会再用
null调用它。未来将废弃用null清理 ref 的方式。
8. 文档元数据原生支持
可以在组件中直接使用 meta 标签
function BlogPost({post}) {
return (
<article>
<h1>{post.title}</h1>
<title>{post.title}</title> {/* 页面标题 */}
<meta name="author" content="Josh" /> {/* Meta 信息 */}
<link rel="author" href="..." /> {/* 链接资源 */}
<meta name="keywords" content={post.keywords} />
<p>{post.content}</p>
</article>
);
}
React 会自动把这些标签提升到 <head> 区域,无需手动管理!
9. 样式表原生支持
组件内联样式表,自动管理加载顺序
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>
);
}
- 📌 precedence 属性控制样式优先级
- 🔒 SSR 时确保样式加载完成后再显示内容
- 📦 自动去重,不会重复加载
10. 资源预加载 API
提前告诉浏览器加载资源
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';
function MyComponent() {
// 预连接第三方域
preconnect('https://cdn.example.com');
// 预加载字体
preload('/fonts/font.woff', { as: 'font' });
// 预加载样式
preload('/styles.css', { as: 'style' });
// 立即加载并执行脚本
preinit('https://analytics.com/script.js', { as: 'script' });
}
preconnect- 建立网络连接preload- 预加载资源(但不会执行)preinit- 立即加载并执行(适合关键脚本)prefetchDNS- 预解析域名
11. 其他重要改进
改进的 Hydration 错误提示
现在会显示具体的差异 diff,而不是只显示匹配失败。
第三方脚本/扩展兼容
Hydration 时会跳过第三方脚本和浏览器扩展插入的内容,避免无谓的错误。
自定义元素完整支持
React 19 完整支持 Web Custom Elements,解决了之前属性/属性不匹配的问题。
useDeferredValue initialValue
// 首次渲染使用 initialValue,然后后台更新
const value = useDeferredValue(deferredValue, '');
SSR 静态 API
import { prerender } from 'react-dom/static';
const {prelude} = await prerender(<App />, {
bootstrapScripts: ['/main.js']
});
用于静态站点生成,等待所有数据加载后再输出 HTML。
总结
🎉 React 19 核心主题
- 🎯 Actions - 简化数据提交和表单处理
- 🔮 use() - 渲染中直接读取异步资源
- 🖥️ RSC - 服务端组件生态完善
- ⚡ 性能 - 资源预加载、样式表管理
- 📝 DX - ref 作为 prop、Context provider 简化
🚀 升级建议
新项目可以直接使用 React 19。旧项目建议阅读官方 升级指南,注意 breaking changes。
📚 来源:React 19 官方博客 | 🦎 整理:小龙