React 19 是 React 自 Hooks 以来最重要的版本更新。它不仅让 Server Components 脱离实验阶段,还引入了 Actions、use() Hook 和备受期待的 React Compiler(前身为 React Forget),将从根本上改变 React 的性能优化范式——开发者不再需要手动写 useMemo、useCallback 和 React.memo。
一、React 19 核心变化速览
| 特性 | 状态 | 影响 | 本文章节 |
|---|---|---|---|
| Actions | 稳定 | 异步状态管理原生化 | 二 |
use() Hook | 稳定 | Promise、Context、Suspense 统一消费 | 三 |
| React Compiler | Beta | 自动记忆化,消除手动优化 | 四 |
| Server Components | 稳定 | 服务端渲染原生支持 | 五 |
| Document Metadata | 稳定 | <title>、<meta> 内置支持 | 六 |
| Asset Loading | 稳定 | 样式/字体预加载集成 | 六 |
| Form Actions | 稳定 | HTML Form + React 深度整合 | 二 |
| 新 Ref API | 稳定 | ref 作为 Prop 传递 | 七 |
| Context 作为 Provider | 稳定 | <Context> 直接当 Provider | 七 |
二、Actions:原生的异步状态管理
2.1 为什么需要 Actions?
在 React 19 之前,异步操作(提交表单、调用 API)的状态管理需要自己实现:
// ❌ React 18 的样板代码
function Form() {
const [isPending, setIsPending] = useState(false);
const [error, setError] = useState(null);
const handleSubmit = async (formData) => {
setIsPending(true);
setError(null);
try {
await submitForm(formData);
} catch (e) {
setError(e);
} finally {
setIsPending(false);
}
};
}
Actions 将这个模式内置到 React 中。
2.2 useTransition + Action 基础模式
import { useTransition } from 'react';
function UpdateName() {
const [isPending, startTransition] = useTransition();
const handleSubmit = async (formData) => {
startTransition(async () => {
await updateName(formData.get('name'));
});
};
return (
<form action={handleSubmit}>
<input name="name" />
<button type="submit" disabled={isPending}>
{isPending ? 'Updating...' : 'Update'}
</button>
</form>
);
}
核心变化:
action属性接收 async 函数isPending自动跟踪异步状态- Transition 包裹保证 UI 不被阻塞
2.3 useActionState(原 useFormState)
import { useActionState } from 'react';
async function submitAction(prevState, formData) {
const name = formData.get('name');
try {
await updateName(name);
return { success: true, message: 'Name updated!' };
} catch (error) {
return { success: false, message: error.message };
}
}
function Form() {
const [state, formAction, isPending] = useActionState(submitAction, {
success: false,
message: '',
});
return (
<form action={formAction}>
<input name="name" required />
<button type="submit" disabled={isPending}>
{isPending ? 'Updating...' : 'Update'}
</button>
{state.message && (
<p className={state.success ? 'success' : 'error'}>
{state.message}
</p>
)}
</form>
);
}
useActionState 三返回:
state:Action 返回的最新状态formAction:绑定到 form 的 action handlerisPending:Action 是否执行中
2.4 useOptimistic:乐观更新
import { useOptimistic } from 'react';
function Messages({ messages }) {
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state, newMessage) => [...state, { ...newMessage, sending: true }]
);
const sendMessage = async (formData) => {
const message = formData.get('message');
// 立即显示乐观消息
addOptimisticMessage({ text: message });
// 实际发送
await api.sendMessage(message);
};
return (
<div>
{optimisticMessages.map((msg, i) => (
<div key={i} style={{ opacity: msg.sending ? 0.5 : 1 }}>
{msg.text}
</div>
))}
<form action={sendMessage}>
<input name="message" />
<button>Send</button>
</form>
</div>
);
}
乐观更新流程:
- 用户提交 → UI 立即更新(乐观状态)
- 实际请求发送
- 请求成功 → 乐观状态与实际状态合并
- 请求失败 → 乐观状态回滚,显示错误
2.5 Actions vs TanStack Query / RTK Query
| 维度 | Actions | TanStack Query |
|---|---|---|
| 定位 | React 原生异步模式 | 独立数据获取库 |
| 缓存 | 无内置缓存 | ✅ 智能缓存 |
| 重取 | 手动 | ✅ 自动重取、窗口聚焦重取 |
| 乐观更新 | ✅ useOptimistic | ✅ 内置支持 |
| 请求去重 | ❌ | ✅ |
| 服务端状态分离 | 弱 | 强 |
| 适用 | 表单提交、简单异步 | 复杂数据获取、缓存策略 |
结论:Actions 适合表单提交和简单异步操作。复杂的服务端状态管理仍推荐 TanStack Query。
三、use() Hook:统一资源消费
3.1 use() 的设计目标
React 19 之前,Context 和 Promise 的消费方式不一致:
- Context:
useContext(MyContext) - Promise:需要
use+ Suspense 包装(实验性)
use() 统一了所有可挂起资源(Suspense-enabled resources)的消费。
3.2 use() 消费 Context
import { use } from 'react';
import { ThemeContext } from './ThemeContext';
function Button() {
// 替代 useContext,可以在条件/循环中调用
const theme = use(ThemeContext);
return <button className={theme}>{/* ... */}</button>;
}
use() 与 useContext 的区别:
use()可以在 if/for 中调用(不像 hooks 规则严格)use()可以与 Suspense 配合挂起useContext仍可用,但use()是推荐的新 API
3.3 use() 消费 Promise
import { use, Suspense } from 'react';
function Comments({ commentsPromise }) {
// use() 可以挂起组件,直到 Promise resolve
const comments = use(commentsPromise);
return (
<ul>
{comments.map((comment) => (
<li key={comment.id}>{comment.text}</li>
))}
</ul>
);
}
function Post({ post }) {
return (
<div>
<h1>{post.title}</h1>
<Suspense fallback={<CommentsSkeleton />}>
<Comments commentsPromise={fetchComments(post.id)} />
</Suspense>
</div>
);
}
关键行为:
use(promise)首次调用时挂起组件- Promise resolve 后,React 重新渲染组件并返回结果
- Promise reject 时,由最近的 Error Boundary 捕获
3.4 与 React Router Loader 的配合
import { use, Suspense } from 'react';
import { Await, useLoaderData } from 'react-router-dom';
function Dashboard() {
const { user, stats, notifications } = useLoaderData();
return (
<div>
<h1>Welcome, {user.name}</h1>
{/* 旧方式:Await */}
<Suspense fallback={<StatsSkeleton />}>
<Await resolve={stats}>
{(data) => <Stats data={data} />}
</Await>
</Suspense>
{/* React 19 新方式:use() — 更简洁 */}
<Suspense fallback={<NotificationsSkeleton />}>
<Notifications promise={notifications} />
</Suspense>
</div>
);
}
function Notifications({ promise }) {
const notifications = use(promise); // 直接解包 Promise
return <NotificationList data={notifications} />;
}
四、React Compiler(React Forget):自动记忆化
4.1 手动优化的痛苦
React 18 之前的性能优化需要大量手动工作:
// ❌ React 18 的手动优化样板
function ExpensiveComponent({ data, onUpdate }) {
const processed = useMemo(() =>
data.map(item => heavyComputation(item)),
[data]
);
const handleClick = useCallback((id) => {
onUpdate(id);
}, [onUpdate]);
return <List items={processed} onClick={handleClick} />;
}
4.2 React Compiler 的原理
React Compiler(以前叫 React Forget)是一个 Babel 编译器插件,自动分析组件依赖并在编译时注入 memoization。
// ✅ React 19 + Compiler:开发者只写业务逻辑
function ExpensiveComponent({ data, onUpdate }) {
const processed = data.map(item => heavyComputation(item));
// Compiler 自动包裹 useMemo
const handleClick = (id) => {
onUpdate(id);
};
// Compiler 自动包裹 useCallback
return <List items={processed} onClick={handleClick} />;
}
编译后(伪代码):
function ExpensiveComponent({ data, onUpdate }) {
const processed = useMemo(
() => data.map(item => heavyComputation(item)),
[data] // Compiler 自动推导依赖
);
const handleClick = useCallback(
(id) => { onUpdate(id); },
[onUpdate]
);
return <List items={processed} onClick={handleClick} />;
}
4.3 配置 React Compiler
npm install -D babel-plugin-react-compiler
// babel.config.js
module.exports = {
plugins: [
['babel-plugin-react-compiler', {
runtimeModule: 'react/compiler-runtime',
}],
],
};
目前状态:React Compiler 在 Meta 内部大规模使用,已通过 npm 发布但标注为 Beta。不建议在关键业务上完全依赖,但可以在新项目尝试。
4.4 Compiler 的规则与限制
Compiler 遵循闭包和不可变数据的假设:
- 依赖必须是不可变的(推荐 Immer 或直接创建新对象)
- 状态更新必须通过 React 官方 API(
setState、useReducer) - 对
ref.current的修改不会被追踪(ref 是 escape hatch)
// ✅ Compiler 可以安全优化
const [count, setCount] = useState(0);
const doubled = count * 2; // 自动 memoized
// ❌ Compiler 无法追踪(会跳过优化警告)
const ref = useRef({ count: 0 });
ref.current.count += 1; // 直接修改 ref
五、Server Components 稳定版
5.1 Server Components 是什么(再确认)
Server Components(RSC)在 React 19 中脱离实验标记,成为推荐架构的一部分:
- 服务端组件在服务端执行,不进入客户端 bundle
- 可以直接访问服务端资源(数据库、文件系统)
- 通过流式 RSC Payload 发送到客户端
5.2 “use server” 指令
// actions.ts —— Server Actions
'use server';
export async function updateName(name: string) {
// 在服务端执行,无需 API 路由
await db.user.update({
where: { id: getCurrentUserId() },
data: { name },
});
revalidatePath('/profile'); // 重新验证缓存
}
// 客户端组件直接调用
'use client';
function ProfileForm() {
return (
<form action={updateName}>
<input name="name" />
<button>Update</button>
</form>
);
}
5.3 Server/Client 组件边界
Server Component
├── Server Component(可以)
├── Client Component(通过 props 传递序列化数据)
│ └── Server Component(❌ 不能嵌套在 Client 中)
└── Server Action(通过 props 传递函数引用)
关键规则:
- Server Component 可以 import Client Component
- Client Component 不能 import Server Component(但可以通过
childrenprop 接收) - Server Action 可以在 Client Component 中直接调用
六、Document Metadata 与 Asset Loading
6.1 原生 Metadata 支持
React 19 允许在组件树任何地方声明 <title>、<meta>、<link>,React 自动提升到 <head>:
function BlogPost({ post }) {
return (
<>
<title>{post.title}</title>
<meta name="description" content={post.excerpt} />
<meta property="og:title" content={post.title} />
<link rel="canonical" href={`https://site.com/posts/${post.id}`} />
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
</>
);
}
优势:
- SEO 相关标签与组件业务逻辑在一起
- 不再依赖
react-helmet等第三方库 - 服务端渲染时正确输出到
<head>
6.2 样式表加载 Prefetching
function Component() {
return (
<>
<link rel="stylesheet" href="styles.css" precedence="default" />
<div>Content</div>
</>
);
}
precedence 属性控制样式优先级,React 自动处理去重和排序。
6.3 异步脚本加载
function Analytics() {
return (
<script
async
src="https://analytics.example.com/script.js"
/>
);
}
React 19 原生支持异步脚本的加载去重和排序。
七、其他 API 改进
7.1 ref 作为普通 Prop
// ✅ React 19:ref 可以直接传递,不再需要 forwardRef
function Input({ ref, ...props }: { ref: React.Ref<HTMLInputElement> }) {
return <input ref={ref} {...props} />;
}
// 使用
function Parent() {
const inputRef = useRef<HTMLInputElement>(null);
return <Input ref={inputRef} placeholder="Enter text" />;
}
7.2 Context 直接作为 Provider
// ✅ React 19:Context 本身就是 Provider
function App() {
return (
<ThemeContext value="dark">
<Page />
</ThemeContext>
);
}
// 替代旧写法
// <ThemeContext.Provider value="dark">
7.3 新的 ref 回调清理
// React 19 的 ref 回调可以返回清理函数
function Component() {
return (
<div
ref={(element) => {
if (element) {
console.log('mounted', element);
return () => console.log('unmounted'); // 清理函数
}
}}
/>
);
}
八、升级路径与兼容性
8.1 React 18 → 19 升级检查清单
| 检查项 | 说明 | 操作 |
|---|---|---|
| 依赖兼容性 | 第三方库是否支持 React 19 | 检查 package.json / npm ls |
| TypeScript | 类型定义更新 | npm update @types/react |
| useId | 可能的变化 | 测试 SSR hydration |
| Context.Provider | 仍可用,但推荐简写 | 逐步替换为 <Context value={...}> |
| forwardRef | 仍可用 | 新代码直接用 ref prop |
| StrictMode | 新增第二次 render 行为 | 测试是否有副作用 |
| Compiler | 可选启用 | 建议在新项目尝试 |
8.2 渐进式迁移策略
# 1. 升级依赖
npm install react@latest react-dom@latest
# 2. 升级类型
npm install -D @types/react@latest @types/react-dom@latest
# 3. 运行测试套件,修复废弃警告
npm test
# 4. 逐步采用新特性
# - 新组件用 use() 替代 useContext
# - 新表单用 Actions
# - 实验项目启用 Compiler
九、React 19 vs 框架对比
| 特性 | React 19 | Vue 3.4 | Svelte 5 | Solid 1.0 |
|---|---|---|---|---|
| 自动记忆化 | ✅ Compiler | ✅ 响应式追踪 | ✅ 编译时优化 | ✅ 细粒度 |
| 服务端组件 | ✅ RSC + Actions | ⚠️ 实验性 | ⚠️ 有限 | ❌ |
| 编译器优化 | ✅ React Compiler | ✅ 编译器 | ✅ 编译器 | ❌ |
| 性能模型 | VDOM + Compiler | VDOM + 编译优化 | 无 VDOM | 无 VDOM |
| 学习曲线 | 中等 | 低 | 低 | 中等 |
常见问题(FAQ)
React 19 后还需要 useMemo/useCallback 吗?
- 短期(Compiler 成熟前):仍需在性能关键路径手动使用
- 长期(Compiler 普及后):绝大多数场景不需要,Compiler 会自动处理
- 特殊情况:自定义比较函数、跨库边界仍需手动控制
Actions 会替代 Redux/Zustand 吗?
不会。Actions 是异步操作的原生抽象,不是状态管理方案。Zustand/Redux 管理的是客户端状态树,Actions 处理的是异步副作用。两者互补——用 Zustand 管理状态,用 Actions 提交表单/调用 API。
Server Components 只能在 Next.js 用吗?
不是。RSC 是 React 的核心特性,但需要一个支持 RSC 的运行时。目前 Next.js App Router 是最成熟的实现。其他框架(Remix、Gatsby、Astro)也在集成中。
React Compiler 会取代手动优化吗?
在绝大多数场景下会。Compiler 的自动 memoization 覆盖约 95% 的优化场景。但以下情况仍需手动干预:
- ref.current 变异追踪不到
- 非 React 的副作用(直接操作 DOM、第三方库集成)
- 需要自定义比较逻辑的深度优化
相关阅读
- React 详解 — React 18 核心概念与演进路线
- React Hooks 完全指南 — useTransition、useDeferredValue、新 Hooks
- React Server Components 深度解析 — RSC 架构与实现细节
- React + TypeScript 实战指南 — Actions 和 use() 的类型写法
- React 状态管理指南 — Actions 与全局状态库的配合
- React 构建工具深度对比 — Compiler 的构建集成
- React 19 官方文档
- React Compiler 文档
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。