React 19已正式发布,这是自React 18以来最重要的版本迭代。React 19引入了诸多革命性的新特性,包括Actions表单处理、全新的useAPI、Ref清理函数、Server Components支持等,同时清理了大量废弃API,让React更加精简高效。
本文将全面解析React 19的所有新特性和重大变化,并提供详细的代码示例,帮助你快速掌握这个最新版本。
# 安装与环境准备
# 安装React 19
# 使用npm安装
npm install --save-exact react@^19.0.0 react-dom@^19.0.0
# 使用yarn安装
yarn add --save-exact react@^19.0.0 react-dom@^19.0.0
@前端进阶之旅: 代码已经复制到剪贴板
如果使用TypeScript,还需要更新类型定义:
npm install --save-exact @types/react@^19.0.0 @types/react-dom@^19.0.0
@前端进阶之旅: 代码已经复制到剪贴板
# 新JSX Transform
React 19要求使用新的JSX Transform。如果未启用,会看到警告:
Your app (or one of its dependencies) is using an outdated JSX transform.
Update to the modern JSX transform for faster performance.
@前端进阶之旅: 代码已经复制到剪贴板
大多数项目不受影响,因为新Transform已在大多数环境中默认启用。
# 一、Actions:表单处理的革命
Actions是React 19最核心的新特性,专门用于处理表单提交和数据变更。它能自动管理pending状态、错误处理和乐观更新。
# 传统写法 vs Actions写法
// 传统写法:手动管理所有状态
function UpdateName({}) {
const [name, setName] = useState("");
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);
const handleSubmit = async () => {
setIsPending(true);
const error = await updateName(name);
setIsPending(false);
if (error) {
setError(error);
return;
}
redirect("/path");
};
return (
<div>
<input value={name} onChange={(e) => setName(e.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
{error && <p>{error}</p>}
</div>
);
}
// React 19 Actions写法:简洁优雅
function UpdateName({}) {
const [name, setName] = useState("");
const [isPending, startTransition] = useTransition();
const handleSubmit = () => {
startTransition(async () => {
const error = await updateName(name);
if (error) {
setError(error);
return;
}
redirect("/path");
});
};
return (
<div>
<input value={name} onChange={(e) => setName(e.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
</div>
);
}
@前端进阶之旅: 代码已经复制到剪贴板
Actions会自动管理:
- Pending状态:请求开始时自动设为true,完成后自动重置
- 乐观更新:配合
useOptimistic显示即时反馈 - 错误处理:自动显示Error Boundary并回滚乐观更新
- 表单重置:成功提交后自动重置表单
# 二、useActionState:简化Actions
useActionState是专门为Actions设计的Hook,简化了常见的数据提交场景:
import { useActionState } from 'react';
function ChangeName({ name, setName }) {
const [error, submitAction, isPending] = useActionState(
async (previousState, formData) => {
const error = await updateName(formData.get("name"));
if (error) {
return error; // 返回错误信息
}
redirect("/path");
return null; // 成功返回null
},
null // 初始状态
);
return (
<form action={submitAction}>
<input type="text" name="name" />
<button type="submit" disabled={isPending}>
{isPending ? '提交中...' : '更新'}
</button>
{error && <p style={{ color: 'red' }}>{error}</p>}
</form>
);
}
@前端进阶之旅: 代码已经复制到剪贴板
返回值的结构:
error:Action的返回结果(成功为null,失败为错误信息)submitAction:包装后的Action函数isPending:是否处于提交中状态
# 三、useOptimistic:乐观更新
乐观更新让用户看到即时的UI反馈,无需等待服务器响应:
import { useOptimistic } from 'react';
function ChangeName({ currentName, onUpdateName }) {
// 立即显示新名字,同时发起请求
const [optimisticName, setOptimisticName] = useOptimistic(currentName);
const submitAction = async (formData) => {
const newName = formData.get("name");
// 立即显示乐观更新
setOptimisticName(newName);
// 等待服务器响应
const updatedName = await updateName(newName);
onUpdateName(updatedName);
};
return (
<form action={submitAction}>
<p>当前名字: {optimisticName}</p>
<input
type="text"
name="name"
disabled={currentName !== optimisticName}
/>
<button type="submit">修改</button>
</form>
);
}
@前端进阶之旅: 代码已经复制到剪贴板
当请求进行中时显示optimisticName,请求完成后自动切换回currentName。
# 四、useFormStatus:表单状态共享
在设计系统中,子组件常常需要知道父表单的状态,useFormStatus让这变得简单:
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending, data, method, action } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? '提交中...' : '提交'}
</button>
);
}
function MyForm() {
return (
<form action={async () => { /* ... */ }}>
<input type="text" name="username" />
{/* 子组件可以获取表单状态,无需props传递 */}
<SubmitButton />
</form>
);
}
@前端进阶之旅: 代码已经复制到剪贴板
# 五、全新use API:渲染中读取资源
# use Promise
use是React 19引入的全新API,可以在渲染阶段读取Promise和Context:
import { use, Suspense } from 'react';
function Comments({ commentsPromise }) {
// use会Suspend直到Promise resolved
const comments = use(commentsPromise);
return comments.map(comment => (
<p key={comment.id}>{comment.text}</p>
));
}
function Page({ commentsPromise }) {
return (
<Suspense fallback={<div>加载中...</div>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
);
}
@前端进阶之旅: 代码已经复制到剪贴板
# use Context
use还可以读取Context,且支持在条件语句后使用(这是useContext做不到的):
import { use } from 'react';
import ThemeContext from './ThemeContext';
function Heading({ children }) {
if (children == null) {
return null;
}
// useContext无法在early return后使用,use可以
const theme = use(ThemeContext);
return <h1 style={{ color: theme.color }}>{children}</h1>;
}
@前端进阶之旅: 代码已经复制到剪贴板
# 六、Ref清理函数
React 19支持从ref回调返回清理函数,这是社区呼吁已久的特性:
对比之前必须用null判断:
// React 18及之前的写法
<input
ref={(ref) => {
inputRef.current = ref;
return () => {
// 清理逻辑
};
}}
/>
// 或者
<input
ref={(ref) => {
inputRef.current = ref;
}}
/>
// 组件卸载时React会调用ref(null)
@前端进阶之旅: 代码已经复制到剪贴板