React 版本中文API: https://zh-hans.react.dev/versions
纯血文档: https://react.dev/blog/2024/12/05/react-19
React 19 的核心更新
1. 内置的“Actions”支持(用于表单和数据处理
异步数据更新一直是 React
应用中的难点之一。React 19 引入了 Actions,通过支持异步函数来管理数据变更、加载状态、错误处理和乐观更新(optimistic updates),使复杂逻辑的处理变得更加简单。
- 新增
<form action={someFunction}>
的支持,让表单处理变得和原生一样直观。 - 类似于 Remix 中的功能:你可以直接将一个 JS 函数作为 action 传入。
- React 会自动处理表单提交、状态、刷新逻辑
在 React 19 中,可以将表单直接提交到一个函数(action),而不需要再写 onSubmit + preventDefault + fetch 那些繁琐逻辑。
传统表单写法(React 18 及之前)
function App() {
const [name, setName] = useState('');
function handleSubmit(e) {
e.preventDefault(); // 阻止默认行为
fetch('/api/save', {
method: 'POST',
body: JSON.stringify({ name })
});
}
return (
<form onSubmit={handleSubmit}>
<input value={name} onChange={(e) => setName(e.target.value)} />
<button type="submit">提交</button>
</form>
);
}
React 19 新写法:直接绑定 action 函数
function App() {
async function saveName(formData) {
const name = formData.get('name');
await fetch('/api/save', {
method: 'POST',
body: JSON.stringify({ name }),
});
}
return (
<form action={saveName}>
<input name="name" placeholder="输入名字" />
<button type="submit">提交</button>
</form>
);
}
变化点:
- 不需要
e.preventDefault()
; - 不需要
useState
控制input
; - 表单的
action
属性就是 JS 函数; - 参数是
FormData
,与原生行为一致; - 内置与
useFormStatus
、useFormState
联动;
2. 新的 useOptimistic Hook
useOptimistic
是 React 19 新增的一个 hook
,专门用来处理“乐观更新”的场景。
useOptimistic
让你在还没收到服务器响应时,先“假装”数据已经更新,从而让用户界面更流畅。
举个粒子
社交平台点了一个“点赞”按钮,正常流程为 点击按钮 -> 发送请求 -> 等服务器确认点赞成 -> UI 显示点赞状态
, 但这个过程会有延迟,感觉卡了一下。
用 useOptimistic
的方式
可以在点击后 立即更新界面 -> 显示已经点赞 -> 后台慢慢发请求 -> 请求成功后再真正更新本地状态(防止出错)
, 就更丝滑了。
代码对比
没有 useOptimistic
const [likes, setLikes] = useState(0);
async function handleLike() {
const res = await fetch('/api/like'); // 等很久
const newLikes = await res.json();
setLikes(newLikes);
}
使用 useOptimistic
立即让用户看到结果
const [likes, setLikes] = useState(0);
const [optimisticLikes, addOptimisticLike] = useOptimistic(likes, (prev, delta) => prev + delta);
async function handleLike() {
addOptimisticLike(1); // 立刻显示 +1 的结果
const res = await fetch('/api/like');
const confirmedLikes = await res.json();
setLikes(confirmedLikes); // 最终确认真实数据
}
参数解析
const [optimisticValue, addOptimisticValue] = useOptimistic(
baseValue, // 初始真实值
updateFunction // 如何从 baseValue 推出新值
);
可以不断调用 addOptimisticValue(data)
来触发"假装"
的 UI 更新。
适用场景:
- 添加评论、点赞、发帖
- 商品加入购物车
- 输入框内快速反馈(如自动补全)
- 改名/改邮箱这类提交后刷新 UI 的操作
3. 新生命周期钩子:use
React 19 引入的新生命周期钩子 use()
, 让我们可以在组件内部直接”等待”异步数据或资源,不再需要复杂的 useEffect + useState
或手动管理状态。
举粒说明
直接等待数据
// App.jsx
import React, { Suspense, use } from 'react';
// 模拟异步数据请求
function fetchUser() {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
name: '三三',
age: 28,
email: 'zhangsan@example.com',
});
}, 2000); // 模拟2秒延迟
});
}
// 组件:使用 use() 等待数据
function UserInfo() {
const user = use(fetchUser()); // use 会挂起直到 Promise resolve
return (
<div>
<h2>用户信息</h2>
<p>姓名:{user.name}</p>
<p>年龄:{user.age}</p>
<p>邮箱:{user.email}</p>
</div>
);
}
// 包装 Suspense 来处理挂起时的 fallback UI
export default function App() {
return (
<div>
<h1>欢迎来到用户中心</h1>
<Suspense fallback={<p>加载用户信息中...</p>}>
<UserInfo />
</Suspense>
</div>
);
}
不需要 useEffect、不需要状态管理,更像是同步代码那样读取数据,但底层是自动 Suspense
要点:
use(fetchUser())
会自动挂起,直到 Promise 完成;- 外层需要用
<Suspense fallback={...}>
包裹; - 更适合
Server Components
或客户端Suspense
流程; - 适合在组件内部加载一次数据的情况。
4. 新生命周期钩子:use
Transitions 是一种标记 非紧急更新 的方式,比如:搜索输入、分页切换、排序、筛选等不会马上影响用户操作的 UI 更新。
为什么需要 Transitions
在 React
中,所有状态更新默认是同步、高优先级的。但有些更新其实不需要立即完成,比如:
- 筛选列表
- 搜索输入结果展示
- 标签页切换
- 加载大量内容的视图切换
如果它们不被降级,就会和输入焦点、按钮点击抢资源,造成卡顿、掉帧、响应慢的问题。
React 提供了一个内置 API:
import { startTransition } from 'react';
startTransition(() => {
// 非紧急更新:如重新渲染大表格、更新搜索结果等
setState(...);
});
举例说明
import React, { useState, useTransition } from 'react';
const bigList = Array.from({ length: 10000 }, (_, i) => `项目 ${i + 1}`);
export default function App() {
const [input, setInput] = useState('');
const [list, setList] = useState(bigList);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
setInput(value); // 紧急更新,立即显示输入内容
// 非紧急更新:过滤数据
startTransition(() => {
const filtered = bigList.filter(item => item.includes(value));
setList(filtered);
});
};
return (
<div style={{ padding: '1rem' }}>
<h2>搜索列表(共 {bigList.length} 项)</h2>
<input
type="text"
value={input}
onChange={handleChange}
placeholder="输入关键字..."
style={{ padding: '0.5rem', width: '300px' }}
/>
{isPending && <p>筛选中,请稍候...</p>}
<ul>
{list.slice(0, 100).map((item, index) => (
<li key={index}>{item}</li> // 只渲染前 100 项避免卡顿
))}
</ul>
</div>
);
}
为什么 useTransition
更好
一个栗子:
const handleChange = (e) => {
setInput(e.target.value); // 用户输入,UI立即更新
setFilteredList(bigList.filter(...)); // 同步过滤大列表 → 可能很慢
};
当快速打字时:
- setFilteredList 会触发列表重渲染;
- 渲染一万个
- 可能非常慢;
- 键盘输入会卡顿、掉帧,造成很差的用户体验。
改进方式:
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
setInput(e.target.value); // 仍然是高优先级,立即响应
startTransition(() => {
setFilteredList(bigList.filter(...)); // 降级,等闲了再渲染
});
};
使用 startTransition()
:
- 先快速显示用户输入;
- 然后低优先级处理列表渲染;
- React 会在浏览器空闲时处理这部分 UI 更新;
- 页面不卡顿,用户感觉丝滑!
那小伙伴们一定有这样的疑问,这玩意和·有什么区别呢,其实 React 的 useTransition() ≠ debounce/throttle,它们的目标和实现机制是完全不同的。可以对比理解:
debounce/throttle
:控制函数多久执行一次,防止太频繁调用。useTransition
:告诉 React 哪些更新可以“等一下再做”,保证页面不掉帧不卡顿。
所以最好的实践往往是两者结合
// 节流输入触发次数
const handleInput = debounce((value) => {
setInput(value);
startTransition(() => {
setFilteredList(filterBigList(value));
});
}, 300);
这样既避免频繁触发函数,也让 React 渲染流畅不卡。
5. 原生支持 Document Metadata
React 19 原生支持 <title>
、<meta>
和 <link>
等文档元数据标签。这些标签可直接在组件中声明,React 会自动将它们提升至 <head>
,并确保与服务端渲染和客户端渲染兼容。
这样可以直接 简化 SEO
和元数据管理
逻辑,并且不需要像以前一样手动插入标签了
function BlogPost({ post }) {
return (
<article>
<h1>{post.title}</h1>
<title>{post.title}</title>
<meta name="author" content={post.author} />
</article>
);
}
剩下的感觉没那么重要,看文档吧~