React 19 在 2024 年 12 月横空出世,距离上次大版本整整两年。本文帮你提炼“最有用”的更新,读完即可决定要不要升级、怎么升级。
一、Actions:异步交互的“官方范式”
过去,异步逻辑(提交表单、调用接口)的 loading、error、乐观更新全部要自己写。React 19 把这一整套收归官方,起名 Actions。
import { useActionState } from 'react';
function UpdateName() {
const [error, submitAction, isPending] = useActionState(
async (prev, formData) => {
const err = await updateName(formData.get('name'));
if (err) return err; // 返回错误即显示
redirect('/profile'); // 成功就跳转
},
null // 初始错误值
);
return (
<form action={submitAction}>
<input name="name" />
<button disabled={isPending}>Update</button>
{error && <p>{error}</p>}
</form>
);
}
关键点
- • 把异步函数直接塞给
action属性,React 自动追踪isPending。 - • 错误可返回任意值,组件内直接渲染。
- • 与
<form>原生行为深度整合,CSR/SSR 同构体验一致。
二、三大新 Hook 速览
| Hook | 作用 | 旧替代品 |
| | | |
| useActionState | 拿到 action 的 error / pending / submit 函数 | useFormState(已废弃) |
| useFormStatus | 在任意子组件里读取父表单提交状态,告别“prop 钻井” | 无 |
| useOptimistic | 乐观更新:先改 UI,再等服务端响应 | 手动 setState |
useOptimistic 示例:
function Thread({ messages }) {
const [optimisticMsgs, addOptimistic] = useOptimistic(
messages,
(state, newMsg) => [...state, { text: newMsg, pending: true }]
);
async function sendMessage(formData) {
const msg = formData.get('message');
addOptimistic(msg); // 立即渲染
await deliverMessage(msg);
}
return (
<>
{optimisticMsgs.map((m, i) => (
<div key={i} style={{ opacity: m.pending ? 0.5 : 1 }}>
{m.text}
</div>
))}
<form action={sendMessage}>
<input name="message" />
</form>
</>
);
}
三、“use” 新钩子:可条件调用的“超级钩子”
use 既能读取 Promise,也能读 Context,而且可以写在条件分支里,突破“Hooks 不能写在 if” 的铁律。
function Message({ messagePromise }) {
const message = use(messagePromise); // 暂停直到 resolve
return <p>{message}</p>;
}
四、ref 直接当 prop,forwardRef 正式退休
function Input({ ref, ...props }) {
return <input ref={ref} {...props} />;
}
无需再包一层 forwardRef,代码更扁平。
五、文档元数据“组件化”
在任意组件里写 <title>、<meta>、<link>,React 会自动 hoist 到 <head>,服务端渲染同样适用。
function BlogPost({ post }) {
return (
<article>
<title>{post.title}</title>
<meta name="description" content={post.excerpt} />
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
);
}
六、样式与脚本加载更智能
- •
<link rel="stylesheet">与<script>放在 Suspense 边界内不会阻塞渲染。 - • 新增
precedence属性,控制多样式表优先级。
function ComponentWithStyle() {
return (
<>
<link rel="stylesheet" href="styles.css" precedence="default" />
<article>内容等样式加载完才显示</article>
</>
);
}
七、预加载 API 集合
React 19 提供四个显式预加载函数,放在组件顶层即可:
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';
function AppRoot() {
preinit('https://example.com/script.js', { as: 'script' });
preload('https://example.com/font.woff', { as: 'font' });
return <App />;
}
八、删除的 API(迁移清单)
| 被删 API | 替代方案 |
| | |
| propTypes | TypeScript |
| defaultProps | 函数默认参数 |
| contextTypes / getChildContext | Context.Provider |
| 字符串 ref | callback ref / createRef |
| createFactory | 直接 JSX |
| react-test-renderer/shallow | @testing-library/react |
九、Server Components 正式毕业
异步组件可在服务端直接跑,返回 HTML 给客户端。
async function BlogPost({ id }) {
const post = await db.posts.find(id);
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
);
}
目前完整支持需借助 Next.js、Remix 等全栈框架。
十、React Compiler(实验性):“告别手动 memo”
以前:
const filtered = useMemo(() => todos.filter(...), [todos, filter]);
const handleClick = useCallback(id => {...}, []);
以后(开启 Compiler):
const filtered = todos.filter(...);
const handleClick = id => {...};
编译器自动帮你做等值依赖分析,只在“真”变化时重新计算。
安装方式(独立插件):
npm install babel-plugin-react-compiler
目前 Instagram 已全量上线,普通项目建议先灰度。
十一、Breaking Changes 速查
-
- 渲染阶段抛出的错误现在会被最近的 Error Boundary 捕获,不再静默打印。
-
useEffect的清理函数即使组件未挂载也会执行(与严格模式一致)。
-
- ref callback 在卸载时会收到
null,记得判空。
- ref callback 在卸载时会收到
十二、要不要升级?官方建议
- • 新项目:直接上 React 19,享受 Actions + 新 Hooks。
- • 老项目:非强制,但升级成本不高;可先在一个分支验证 Compiler 收益。
- • Compiler:可选且实验,生产环境请逐步灰度。
结语
React 19 没有颠覆式语法,却把“日常最烦人的异步状态”做成了官方范式,再加上 Compiler 的“自动 memo”,让业务代码几乎告别 useMemo/useCallback。
如果你想体验“写完就下班”的快乐,现在就是最佳时机。