React 19 有哪些新特性?
一、是什么
React 19 是 React 的又一次重大版本更新,于 2024 年 12 月正式发布。它在 React 18 的并发渲染基础上,进一步简化了开发者体验并增强了性能优化能力。核心亮点包括 React Compiler(自动记忆化)、Actions(异步表单处理)、use() Hook、ref 作为 prop 传递、以及对文档元数据和资源加载的原生支持等。
二、React Compiler
2.1 告别手动 memo
React Compiler 是 React 19 最具革命性的特性。它在构建时自动分析组件,插入等价于 memo、useMemo、useCallback 的优化代码:
// React 18:需要手动优化
const ExpensiveList = memo(function ExpensiveList({ items, onSelect }) {
const sorted = useMemo(
() => [...items].sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
const handleSelect = useCallback(
(id) => onSelect(id),
[onSelect]
);
return sorted.map(item => (
<Item key={item.id} data={item} onClick={handleSelect} />
));
});
// React 19 + Compiler:直接写,编译器自动优化
function ExpensiveList({ items, onSelect }) {
const sorted = [...items].sort((a, b) => a.name.localeCompare(b.name));
return sorted.map(item => (
<Item key={item.id} data={item} onClick={(id) => onSelect(id)} />
));
}
2.2 编译器的工作原理
React Compiler 是一个 Babel 插件,它:
- 分析组件中每个值的依赖关系
- 自动为变量、JSX 和函数插入缓存逻辑
- 只在依赖变化时重新计算,否则返回缓存结果
- 遵循 React 的规则(如 Hook 规则、纯渲染),违反规则的组件会被跳过
三、Actions
3.1 什么是 Actions
Actions 是 React 19 对表单和异步操作的统一抽象。startTransition 现在支持异步函数,在 transition 中执行的异步函数被称为 Action:
function UpdateProfile() {
const [isPending, startTransition] = useTransition();
const [error, setError] = useState(null);
async function handleSubmit(formData) {
startTransition(async () => {
const result = await saveProfile(formData);
if (result.error) {
setError(result.error);
}
});
}
return (
<form action={handleSubmit}>
<input name="name" />
<input name="email" type="email" />
<button disabled={isPending}>
{isPending ? '保存中...' : '保存'}
</button>
{error && <p className="error">{error}</p>}
</form>
);
}
3.2 useActionState
useActionState 封装了 Action 的常见模式——管理 pending 状态、返回值和表单重置:
import { useActionState } from 'react';
async function addToCart(previousState, formData) {
const itemId = formData.get('itemId');
try {
await api.addToCart(itemId);
return { success: true, message: '已添加到购物车' };
} catch (e) {
return { success: false, message: e.message };
}
}
function AddToCartButton({ itemId }) {
const [state, submitAction, isPending] = useActionState(addToCart, null);
return (
<form action={submitAction}>
<input type="hidden" name="itemId" value={itemId} />
<button disabled={isPending}>
{isPending ? '添加中...' : '加入购物车'}
</button>
{state?.message && (
<p className={state.success ? 'success' : 'error'}>
{state.message}
</p>
)}
</form>
);
}
useFormStatus 在表单内部子组件中获取父级 <form> 的提交状态:
import { useFormStatus } from 'react-dom';
function SubmitButton({ children }) {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? '提交中...' : children}
</button>
);
}
必须在 <form> 的子组件中调用,不能在包含 <form> 的同一组件中使用。
3.4 useOptimistic
useOptimistic 在异步操作进行时提供乐观 UI 更新:
import { useOptimistic } from 'react';
function TodoList({ todos, addTodoAction }) {
const [optimisticTodos, addOptimistic] = useOptimistic(
todos,
(currentTodos, newTitle) => [
...currentTodos,
{ id: crypto.randomUUID(), title: newTitle, pending: true },
]
);
async function handleSubmit(formData) {
const title = formData.get('title');
addOptimistic(title);
await addTodoAction(title);
}
return (
<div>
<form action={handleSubmit}>
<input name="title" placeholder="新任务" />
<button type="submit">添加</button>
</form>
<ul>
{optimisticTodos.map(todo => (
<li key={todo.id} style={{ opacity: todo.pending ? 0.6 : 1 }}>
{todo.title}
</li>
))}
</ul>
</div>
);
}
四、use() Hook
use() 是一个可以在条件语句和循环中调用的特殊 Hook,用于读取 Promise 和 Context:
4.1 读取 Promise
import { use, Suspense } from 'react';
function UserProfile({ userPromise }) {
const user = use(userPromise);
return (
<div className="profile">
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
function ProfilePage({ userId }) {
const userPromise = fetchUser(userId);
return (
<Suspense fallback={<ProfileSkeleton />}>
<UserProfile userPromise={userPromise} />
</Suspense>
);
}
4.2 条件读取 Context
import { use, createContext } from 'react';
const ThemeContext = createContext('light');
function StatusIcon({ isActive }) {
if (isActive) {
const theme = use(ThemeContext);
return <span className={`icon icon-${theme}`}>●</span>;
}
return <span className="icon">○</span>;
}
与 useContext 不同,use() 可以在 if 语句和 for 循环内调用。
五、ref 作为 prop
React 19 中函数组件可以直接接收 ref 作为 prop,不再需要 forwardRef:
// React 18:需要 forwardRef
const Input = forwardRef(function Input(props, ref) {
return <input ref={ref} {...props} />;
});
// React 19:ref 就是一个普通的 prop
function Input({ ref, ...props }) {
return <input ref={ref} {...props} />;
}
// 使用方式不变
function Form() {
const inputRef = useRef(null);
return (
<div>
<Input ref={inputRef} placeholder="输入内容" />
<button onClick={() => inputRef.current.focus()}>聚焦</button>
</div>
);
}
ref 回调的清理函数
React 19 中 ref 回调支持返回清理函数:
function MeasuredBox() {
return (
<div
ref={(node) => {
if (node) {
const observer = new ResizeObserver(entries => {
console.log('尺寸变化:', entries[0].contentRect);
});
observer.observe(node);
return () => observer.disconnect();
}
}}
>
可观测尺寸的容器
</div>
);
}
六、文档元数据支持
React 19 原生支持在组件中渲染 <title>、<meta>、<link> 等标签,React 会自动将它们提升到 <head> 中:
function BlogPost({ post }) {
return (
<article>
<title>{post.title} - 我的博客</title>
<meta name="description" content={post.summary} />
<meta property="og:title" content={post.title} />
<link rel="canonical" href={`https://myblog.com/posts/${post.slug}`} />
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
);
}
不再需要 react-helmet 等第三方库来管理文档头信息。
七、资源加载与文档元数据
React 19 提供了预加载资源的 API(preload、preinit、preconnect、prefetchDNS),用于优化页面性能:
import { preload, preinit } from 'react-dom';
function App() {
preinit('/libs/analytics.js', { as: 'script' });
preload('/fonts/inter.woff2', { as: 'font', type: 'font/woff2' });
return <MainContent />;
}
八、其他改进
- 错误报告:
createRoot 新增 onCaughtError、onUncaughtError、onRecoverableError 回调,消除重复错误日志
- Web Components:完整支持 Custom Elements,正确区分 attribute/property 并映射事件
九、总结
React 19 的核心理念是"让正确的事情变得简单"——通过编译器消除手动优化负担,通过 Actions 简化异步交互,通过 use() 统一异步数据消费模式。