react-19
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact 19
React 19
Overview of New Features
新特性概述
| Feature | Description |
|---|---|
| Actions | Async functions in transitions for data mutations |
| Handle action state with pending/error |
| Access parent form status |
| Optimistic UI updates |
| Read resources in render (Promises, Context) |
| Server Components | Components that render on server |
| Server Actions | Server functions called from client |
| React Compiler | Automatic memoization |
| Ref as prop | Pass ref directly without forwardRef |
| 特性 | 描述 |
|---|---|
| Actions | 用于数据变更的过渡态异步函数 |
| 处理包含等待/错误状态的动作状态 |
| 访问父表单状态 |
| 乐观UI更新 |
| 在渲染中读取资源(Promises、Context) |
| Server Components | 在服务器端渲染的组件 |
| Server Actions | 从客户端调用的服务器端函数 |
| React Compiler | 自动记忆化处理 |
| Ref as prop | 直接传递ref,无需forwardRef |
Actions
Actions
Actions are async functions used in transitions to handle mutations:
tsx
import { useTransition } from 'react';
function UpdateProfile() {
const [isPending, startTransition] = useTransition();
async function handleSubmit(formData: FormData) {
startTransition(async () => {
const result = await updateProfile(formData);
if (result.error) {
showToast(result.error);
}
});
}
return (
<form action={handleSubmit}>
<input name="name" type="text" />
<button type="submit" disabled={isPending}>
{isPending ? 'Saving...' : 'Save'}
</button>
</form>
);
}Actions是用于过渡态中处理数据变更的异步函数:
tsx
import { useTransition } from 'react';
function UpdateProfile() {
const [isPending, startTransition] = useTransition();
async function handleSubmit(formData: FormData) {
startTransition(async () => {
const result = await updateProfile(formData);
if (result.error) {
showToast(result.error);
}
});
}
return (
<form action={handleSubmit}>
<input name="name" type="text" />
<button type="submit" disabled={isPending}>
{isPending ? '保存中...' : '保存'}
</button>
</form>
);
}useActionState
useActionState
Replaces the experimental . Manages form action state including pending status:
useFormStatetsx
import { useActionState } from 'react';
interface FormState {
message: string | null;
errors: Record<string, string[]>;
}
async function createPost(prevState: FormState, formData: FormData): Promise<FormState> {
const title = formData.get('title') as string;
if (!title || title.length < 3) {
return { message: null, errors: { title: ['Title must be at least 3 characters'] } };
}
try {
await savePost({ title });
return { message: 'Post created!', errors: {} };
} catch (error) {
return { message: 'Failed to create post', errors: {} };
}
}
function CreatePostForm() {
const [state, formAction, isPending] = useActionState(createPost, {
message: null,
errors: {}
});
return (
<form action={formAction}>
<input name="title" type="text" />
{state.errors.title && <p className="error">{state.errors.title[0]}</p>}
<button type="submit" disabled={isPending}>
{isPending ? 'Creating...' : 'Create Post'}
</button>
</form>
);
}替代实验性的,管理包含等待状态的表单动作状态:
useFormStatetsx
import { useActionState } from 'react';
interface FormState {
message: string | null;
errors: Record<string, string[]>;
}
async function createPost(prevState: FormState, formData: FormData): Promise<FormState> {
const title = formData.get('title') as string;
if (!title || title.length < 3) {
return { message: null, errors: { title: ['标题长度至少为3个字符'] } };
}
try {
await savePost({ title });
return { message: '帖子创建成功!', errors: {} };
} catch (error) {
return { message: '帖子创建失败', errors: {} };
}
}
function CreatePostForm() {
const [state, formAction, isPending] = useActionState(createPost, {
message: null,
errors: {}
});
return (
<form action={formAction}>
<input name="title" type="text" />
{state.errors.title && <p className="error">{state.errors.title[0]}</p>}
<button type="submit" disabled={isPending}>
{isPending ? '创建中...' : '创建帖子'}
</button>
</form>
);
}useFormStatus
useFormStatus
Access the status of a parent from any child component:
<form>tsx
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending, data, method, action } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Submitting...' : 'Submit'}
</button>
);
}
// Usage - must be child of <form>
function ContactForm() {
return (
<form action={submitContact}>
<input name="email" type="email" required />
<SubmitButton />
</form>
);
}从任意子组件访问父的状态:
<form>tsx
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending, data, method, action } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? '提交中...' : '提交'}
</button>
);
}
// 使用方式 - 必须是<form>的子组件
function ContactForm() {
return (
<form action={submitContact}>
<input name="email" type="email" required />
<SubmitButton />
</form>
);
}Reusable Form Components
可复用表单组件
tsx
'use client';
import { useFormStatus } from 'react-dom';
export function FormButton({ children, pendingText = 'Submitting...', ...props }) {
const { pending } = useFormStatus();
return (
<button {...props} disabled={pending || props.disabled}>
{pending ? pendingText : children}
</button>
);
}Full Reference: See hooks.md for useOptimistic, use() hook, Ref handling, Document Metadata.
tsx
'use client';
import { useFormStatus } from 'react-dom';
export function FormButton({ children, pendingText = '提交中...', ...props }) {
const { pending } = useFormStatus();
return (
<button {...props} disabled={pending || props.disabled}>
{pending ? pendingText : children}
</button>
);
}完整参考:查看hooks.md获取useOptimistic、use() hook、Ref处理、文档元数据的相关内容。
Server Components & Server Actions
Server Components & Server Actions
tsx
// app/users/page.tsx (Server Component by default)
export default async function UsersPage() {
const users = await fetchUsers();
return <UserList users={users} />;
}
// app/actions.ts
'use server';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
await db.post.create({ data: { title } });
revalidatePath('/posts');
redirect(`/posts`);
}Full Reference: See server.md for Server Components patterns, React Compiler, Migration Guide.
tsx
// app/users/page.tsx(默认是Server Component)
export default async function UsersPage() {
const users = await fetchUsers();
return <UserList users={users} />;
}
// app/actions.ts
'use server';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
await db.post.create({ data: { title } });
revalidatePath('/posts');
redirect(`/posts`);
}完整参考:查看server.md获取Server Components模式、React Compiler、迁移指南的相关内容。
Best Practices
最佳实践
- ✅ Use Actions for form submissions and mutations
- ✅ Use for immediate feedback
useOptimistic - ✅ Use instead of useEffect for data fetching
use() - ✅ Let React Compiler handle memoization
- ✅ Use Server Components for static content
- ✅ Use Server Actions for secure mutations
- ❌ Don't create Promises inside components for
use() - ❌ Don't mix client/server code without proper boundaries
- ✅ 使用Actions处理表单提交和数据变更
- ✅ 使用实现即时反馈
useOptimistic - ✅ 使用替代useEffect进行数据获取
use() - ✅ 让React Compiler处理记忆化
- ✅ 使用Server Components处理静态内容
- ✅ 使用Server Actions处理安全的变更操作
- ❌ 不要在组件内部为创建Promises
use() - ❌ 不要在未设置正确边界的情况下混合客户端/服务器端代码
When NOT to Use This Skill
不适用本技能的场景
- React 18 and earlier features - Use skill instead
react - General form handling - Use or
react-formsskillsreact-hook-form - Performance optimization - Use skill
react-performance
- React 18及更早版本特性 - 请使用技能
react - 通用表单处理 - 请使用或
react-forms技能react-hook-form - 性能优化 - 请使用技能
react-performance
Anti-Patterns
反模式
| Anti-Pattern | Problem | Solution |
|---|---|---|
| Creating Promises in render for use() | New promise every render | Create promise outside component |
| Using useFormState instead of useActionState | Deprecated in React 19 | Use useActionState with isPending |
| Missing 'use server' directive | Code runs on client | Add 'use server' at top of file |
| Not validating Server Action inputs | Security vulnerability | Always validate with Zod |
| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 在渲染中为use()创建Promises | 每次渲染都会生成新的Promise | 在组件外部创建Promise |
| 使用useFormState而非useActionState | React 19中已废弃 | 使用带isPending的useActionState |
| 缺少'use server'指令 | 代码在客户端运行 | 在文件顶部添加'use server' |
| 未验证Server Action输入 | 安全漏洞 | 始终使用Zod进行验证 |
Quick Troubleshooting
快速故障排除
| Issue | Likely Cause | Fix |
|---|---|---|
| Action not working | Missing action attribute | Add action={serverAction} to form |
| isPending not available | Using old useFormState | Switch to useActionState |
| Serialization errors | Passing functions/classes | Only pass serializable data |
| ref not working | Using old forwardRef pattern | Pass ref as regular prop |
| 问题 | 可能原因 | 修复方案 |
|---|---|---|
| Action无法工作 | 缺少action属性 | 为form添加action={serverAction} |
| isPending不可用 | 使用了旧版useFormState | 切换为useActionState |
| 序列化错误 | 传递了函数/类 | 仅传递可序列化的数据 |
| ref无法工作 | 使用了旧版forwardRef模式 | 将ref作为常规属性传递 |
Reference Files
参考文件
| File | Content |
|---|---|
| hooks.md | useOptimistic, use(), Ref handling, Metadata |
| server.md | Server Components, Server Actions, Compiler, Migration |
| 文件 | 内容 |
|---|---|
| hooks.md | useOptimistic、use()、Ref处理、元数据 |
| server.md | Server Components模式、React Compiler、迁移指南 |