React 19 的定位
如果说 React 18 的主题是"并发渲染",那么 React 19 的主题是"开发体验与服务端能力的深化"。
这个版本没有引入革命性的新概念,而是让 React 18 引入的能力更加实用、更加易用。Server Components 从"能用"变成"好用",表单处理从"繁琐"变成"优雅",异步数据获取从"各显神通"变成"统一方案"。
Actions:表单处理的范式变革
传统表单的痛点
React 中处理表单一直是痛点。一个简单的提交表单需要:
- 用 useState 管理表单字段
- 用 useState 管理 loading 状态
- 用 useState 管理错误状态
- 写 handleSubmit 函数处理提交
- 手动处理成功/失败后的 UI 更新
代码往往变成这样:
function ContactForm() {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
const [success, setSuccess] = useState(false)
async function handleSubmit(e) {
e.preventDefault()
setLoading(true)
setError(null)
try {
await submitForm({ name, email })
setSuccess(true)
} catch (err) {
setError(err.message)
} finally {
setLoading(false)
}
}
return (
<form onSubmit={handleSubmit}>
{/* ... */}
</form>
)
}
这还是最简单的情况,没考虑验证、重复提交防护、乐观更新...
Actions 的解法
React 19 的 Actions 让表单处理回归简洁:
async function submitAction(formData) {
'use server' // 在服务端执行
const name = formData.get('name')
const email = formData.get('email')
await saveToDatabase({ name, email })
}
function ContactForm() {
return (
<form action={submitAction}>
<input name="name" />
<input name="email" />
<button type="submit">提交</button>
</form>
)
}
不需要 useState,不需要 handleSubmit,不需要手动管理 loading——框架帮你处理。
useFormStatus:获取表单状态
提交按钮需要显示 loading 状态?用 useFormStatus:
function SubmitButton() {
const { pending } = useFormStatus()
return (
<button disabled={pending}>
{pending ? '提交中...' : '提交'}
</button>
)
}
这个 Hook 只能在 <form> 内部的组件中使用,自动获取最近表单的状态。
useActionState:完整的状态管理
需要更多控制?useActionState 提供完整的状态管理:
async function updateName(previousState, formData) {
const name = formData.get('name')
if (!name) {
return { error: '名称不能为空' }
}
await updateUser({ name })
return { success: true }
}
function UpdateNameForm() {
const [state, formAction, isPending] = useActionState(updateName, null)
return (
<form action={formAction}>
<input name="name" />
<button disabled={isPending}>更新</button>
{state?.error && <p className="error">{state.error}</p>}
{state?.success && <p className="success">更新成功</p>}
</form>
)
}
useOptimistic:乐观更新
用户点击后立即反馈,不等服务端响应:
function TodoItem({ todo, updateAction }) {
const [optimisticTodo, setOptimisticTodo] = useOptimistic(todo)
async function handleToggle() {
setOptimisticTodo({ ...todo, done: !todo.done })
await updateAction(todo.id, !todo.done)
}
return (
<div>
<input
type="checkbox"
checked={optimisticTodo.done}
onChange={handleToggle}
/>
{optimisticTodo.title}
</div>
)
}
点击后立即切换状态,如果服务端失败再回滚。用户感知到的响应速度极快。
use Hook:统一的资源读取
Promise 直接读取
之前读取异步数据需要 useEffect + useState 的组合。React 19 引入 use Hook,可以直接在组件中读取 Promise:
function UserProfile({ userPromise }) {
const user = use(userPromise)
return <h1>{user.name}</h1>
}
注意这不是在组件内发起请求,而是读取已存在的 Promise。请求应该在父组件或数据层发起。
Context 读取
use 也可以读取 Context,而且支持条件调用:
function Sidebar({ showSettings }) {
if (showSettings) {
const settings = use(SettingsContext) // 条件调用
return <SettingsPanel settings={settings} />
}
return <DefaultSidebar />
}
传统的 useContext 不能在条件语句中调用,use 打破了这个限制。
与 Suspense 配合
use 读取 Promise 时,会触发 Suspense:
function App() {
const userPromise = fetchUser(userId)
return (
<Suspense fallback={<Loading />}>
<UserProfile userPromise={userPromise} />
</Suspense>
)
}
在数据加载完成前显示 Loading,完成后自动渲染 UserProfile。
ref 改进
ref 作为普通 prop
之前传递 ref 需要 forwardRef:
// React 18
const Input = forwardRef((props, ref) => (
<input ref={ref} {...props} />
))
React 19 中,ref 可以直接作为 prop:
// React 19
function Input({ ref, ...props }) {
return <input ref={ref} {...props} />
}
forwardRef 仍然可用,但不再是唯一选择。
ref 回调清理函数
ref 回调现在支持返回清理函数:
<input
ref={(element) => {
// 元素挂载时执行
const instance = initializeLibrary(element)
return () => {
// 元素卸载时执行
instance.destroy()
}
}}
/>
之前需要额外的 useEffect 来处理清理,现在一个回调搞定。
Server Components 的成熟
Directives 稳定
'use client' 和 'use server' 指令正式稳定:
'use client' // 标记客户端组件边界
function InteractiveButton() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
'use server' // 标记服务端 Action
async function saveData(formData) {
await db.insert(formData)
}
错误处理改进
Server Components 的错误现在能被错误边界正确捕获:
<ErrorBoundary fallback={<ErrorMessage />}>
<ServerComponent />
</ErrorBoundary>
之前 Server Components 的错误可能导致整个页面崩溃,现在可以优雅降级。
流式传输优化
Server Components 的流式传输更加稳定:
- Suspense 边界的处理更合理
- 网络中断后的恢复更可靠
- 错误状态的传播更清晰
文档 Metadata 支持
原生 title 和 meta
React 19 内置了对文档元数据的支持:
function BlogPost({ post }) {
return (
<>
<title>{post.title}</title>
<meta name="description" content={post.excerpt} />
<meta name="author" content={post.author} />
<article>{post.content}</article>
</>
)
}
这些标签会自动提升到 <head> 中,无需 react-helmet 等第三方库。
link 和 script 管理
样式表和脚本的加载也有了统一管理:
function MyComponent() {
return (
<>
<link rel="stylesheet" href="/styles.css" precedence="default" />
<script src="/analytics.js" async />
<div>内容</div>
</>
)
}
precedence 属性控制样式表的优先级,框架会自动去重和排序。
开发体验提升
改进的错误提示
错误信息更加清晰:
- 组件栈追踪更准确
- 水合错误定位到具体元素
- 废弃 API 有明确的迁移指引
Strict Mode 增强
开发模式下的检查更严格:
- 检测更多的副作用问题
- 模拟组件的卸载重载
- 提前发现并发模式下的问题
迁移指南
破坏性变更
React 19 的破坏性变更主要是清理历史包袱:
已废弃的 API 移除:
propTypes运行时检查移除(建议用 TypeScript)defaultProps在函数组件中废弃(用默认参数)- 字符串 ref 移除(早在多年前就废弃了)
行为变更:
useRef必须传初始值useDeferredValue第二个参数改为选项对象- 部分类型定义调整
渐进式升级
- 先升级到 React 18.3:这是一个特殊版本,会警告将在 19 中移除的 API
- 修复所有警告:根据控制台提示修改代码
- 升级到 React 19:确保测试通过
- 采用新特性:在新代码中使用 Actions、use 等
常见问题
Q:需要重写所有表单吗? A:不需要。传统的 useState + handleSubmit 仍然有效,Actions 是额外选项。
Q:use Hook 会取代 useEffect 获取数据吗? A:不会。use 是读取已存在的资源,发起请求仍然需要其他方式(Server Components、数据加载库等)。
Q:Server Components 必须用吗? A:看框架。在 Next.js App Router 中是默认的,纯客户端 React 应用不涉及。
总结
React 19 的核心价值:
| 特性 | 解决的问题 |
|---|---|
| Actions | 简化表单状态管理 |
| useFormStatus | 无痛获取表单状态 |
| useActionState | 细粒度的 Action 控制 |
| useOptimistic | 乐观更新开箱即用 |
| use Hook | 统一的资源读取方式 |
| ref 改进 | 减少 forwardRef 样板 |
| Metadata 支持 | 原生文档元数据管理 |
React 19 体现了框架成熟期的特征:不追求颠覆性创新,而是让已有概念更易用、更实用。对于日常开发,这些改进的累积效应相当可观。
相关文章推荐:


