目录
- [React Actions 与 useActionState](#React Actions 与 useActionState "#react-actions")
- [现代 Suspense 组件](#现代 Suspense 组件 "#modern-suspense")
- [React 服务器组件(RSC)](#React 服务器组件(RSC) "#server-components")
- [服务器函数(Server Functions)](#服务器函数(Server Functions) "#server-functions")
- 最佳实践与使用场景
React Actions 与 useActionState {#react-actions}
useActionState Hook
useActionState
是 React 19 引入的新 Hook,用于处理表单提交和异步操作的状态管理。
基础语法
javascript
const [state, formAction, isPending] = useActionState(actionFunction, initialState);
基础示例
javascript
'use client';
import { useActionState } from 'react';
async function updateName(previousState, formData) {
const name = formData.get('name');
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 1000));
if (name.length < 2) {
return { error: '姓名至少需要2个字符' };
}
return { success: `Hello ${name}!` };
}
export default function NameForm() {
const [state, formAction, isPending] = useActionState(updateName, null);
return (
<form action={formAction}>
<input type="text" name="name" placeholder="输入您的姓名" />
<button type="submit" disabled={isPending}>
{isPending ? '提交中...' : '提交'}
</button>
{state?.error && <p style={{color: 'red'}}>{state.error}</p>}
{state?.success && <p style={{color: 'green'}}>{state.success}</p>}
</form>
);
}
服务器端 Actions
服务器端 Actions 允许客户端直接调用服务器函数,无需手动设置 API 端点。
定义服务器 Action
javascript
// app/actions.js
'use server';
import { redirect } from 'next/navigation';
import { revalidatePath } from 'next/cache';
export async function createPost(prevState, formData) {
const title = formData.get('title');
const content = formData.get('content');
// 验证数据
if (!title || !content) {
return {
error: '标题和内容都是必需的'
};
}
try {
// 保存到数据库
const post = await db.post.create({
data: { title, content }
});
// 重新验证相关页面
revalidatePath('/posts');
// 重定向到新文章
redirect(`/posts/${post.id}`);
} catch (error) {
return {
error: '创建文章失败'
};
}
}
export async function deletePost(postId) {
try {
await db.post.delete({
where: { id: postId }
});
revalidatePath('/posts');
return { success: true };
} catch (error) {
return { error: '删除失败' };
}
}
客户端使用服务器 Action
javascript
'use client';
import { useActionState } from 'react';
import { createPost } from './actions';
export default function PostForm() {
const [state, formAction, isPending] = useActionState(createPost, null);
return (
<form action={formAction}>
<div>
<label htmlFor="title">标题:</label>
<input
type="text"
id="title"
name="title"
required
/>
</div>
<div>
<label htmlFor="content">内容:</label>
<textarea
id="content"
name="content"
rows={5}
required
/>
</div>
<button type="submit" disabled={isPending}>
{isPending ? '发布中...' : '发布文章'}
</button>
{state?.error && (
<div style={{color: 'red', marginTop: '10px'}}>
{state.error}
</div>
)}
</form>
);
}
渐进式增强
Actions 支持渐进式增强,即使 JavaScript 被禁用也能正常工作:
javascript
// app/actions.js
'use server';
export async function addComment(prevState, formData) {
const comment = formData.get('comment');
const postId = formData.get('postId');
if (!comment.trim()) {
return { error: '评论不能为空' };
}
await db.comment.create({
data: {
content: comment,
postId: parseInt(postId)
}
});
revalidatePath(`/posts/${postId}`);
return { success: '评论已添加' };
}
javascript
// 组件中使用
export default function CommentForm({ postId }) {
const [state, formAction, isPending] = useActionState(addComment, null);
return (
<form action={formAction}>
<input type="hidden" name="postId" value={postId} />
<textarea
name="comment"
placeholder="写下您的评论..."
required
/>
<button type="submit" disabled={isPending}>
{isPending ? '提交中...' : '添加评论'}
</button>
{state?.error && <p className="error">{state.error}</p>}
{state?.success && <p className="success">{state.success}</p>}
</form>
);
}
现代 Suspense 组件 {#modern-suspense}
Suspense 的核心概念
现代 Suspense 主要用于:
- 数据获取:等待异步数据加载
- 代码分割:动态导入组件
- 服务器端渲染:流式渲染
数据获取与 Suspense
使用 use Hook(React 19)
javascript
import { Suspense, use } from 'react';
function UserProfile({ userPromise }) {
const user = use(userPromise);
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
<p>加入时间:{user.joinDate}</p>
</div>
);
}
export default function App() {
const userPromise = fetch('/api/user/123').then(res => res.json());
return (
<div>
<h1>用户信息</h1>
<Suspense fallback={<div>加载用户信息中...</div>}>
<UserProfile userPromise={userPromise} />
</Suspense>
</div>
);
}
服务器组件中的 Suspense
在服务器组件中,async 组件会自动与 Suspense 集成:
javascript
// app/page.js
import { Suspense } from 'react';
async function SlowComponent() {
// 模拟慢速数据获取
await new Promise(resolve => setTimeout(resolve, 2000));
const data = await fetch('https://api.example.com/data');
return <div>慢速组件数据:{JSON.stringify(await data.json())}</div>;
}
async function FastComponent() {
// 快速数据获取
await new Promise(resolve => setTimeout(resolve, 500));
return <div>快速组件已加载</div>;
}
export default function Page() {
return (
<div>
<h1>我的页面</h1>
{/* 快速组件 */}
<Suspense fallback={<div>加载快速内容...</div>}>
<FastComponent />
</Suspense>
{/* 慢速组件 */}
<Suspense fallback={<div>加载慢速内容...</div>}>
<SlowComponent />
</Suspense>
</div>
);
}
嵌套和并发 Suspense
javascript
function App() {
return (
<div>
<Suspense fallback={<div>加载应用...</div>}>
<Header />
<main>
<Suspense fallback={<div>加载主要内容...</div>}>
<MainContent />
<aside>
<Suspense fallback={<div>加载侧边栏...</div>}>
<Sidebar />
</Suspense>
</aside>
</Suspense>
</main>
<Suspense fallback={<div>加载页脚...</div>}>
<Footer />
</Suspense>
</Suspense>
</div>
);
}
错误边界与 Suspense
javascript
import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
function ErrorFallback({ error, resetErrorBoundary }) {
return (
<div role="alert">
<h2>出错了:</h2>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>重试</button>
</div>
);
}
export default function App() {
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<Suspense fallback={<div>加载中...</div>}>
<DataComponent />
</Suspense>
</ErrorBoundary>
);
}
React 服务器组件(RSC) {#server-components}
服务器组件基础
服务器组件在服务器上执行,可以直接访问服务器资源。
基本服务器组件
javascript
// app/posts/page.js(服务器组件)
import { db } from '@/lib/database';
export default async function PostsPage() {
// 直接在服务器上查询数据库
const posts = await db.post.findMany({
orderBy: { createdAt: 'desc' },
take: 10
});
return (
<div>
<h1>最新文章</h1>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
<time>{new Date(post.createdAt).toLocaleDateString()}</time>
</article>
))}
</div>
);
}
服务器和客户端组件混合
服务器组件中嵌入客户端组件
javascript
// app/post/[id]/page.js(服务器组件)
import { db } from '@/lib/database';
import LikeButton from './LikeButton';
import Comments from './Comments';
export default async function PostPage({ params }) {
const post = await db.post.findUnique({
where: { id: params.id },
include: {
author: true,
_count: { select: { likes: true, comments: true } }
}
});
return (
<article>
<h1>{post.title}</h1>
<p>作者:{post.author.name}</p>
<div>{post.content}</div>
{/* 客户端交互组件 */}
<LikeButton
postId={post.id}
initialLikes={post._count.likes}
/>
{/* 混合组件:服务器获取数据,客户端交互 */}
<Comments postId={post.id} />
</article>
);
}
// app/post/[id]/LikeButton.js(客户端组件)
'use client';
import { useState, useTransition } from 'react';
import { likePost } from './actions';
export default function LikeButton({ postId, initialLikes }) {
const [likes, setLikes] = useState(initialLikes);
const [isPending, startTransition] = useTransition();
const handleLike = () => {
startTransition(async () => {
const result = await likePost(postId);
if (result.success) {
setLikes(result.likes);
}
});
};
return (
<button onClick={handleLike} disabled={isPending}>
👍 {likes} {isPending ? '...' : ''}
</button>
);
}
数据获取模式
串行数据获取
javascript
// 依赖关系的数据获取
export default async function UserDashboard({ params }) {
// 首先获取用户信息
const user = await getUser(params.userId);
// 基于用户信息获取相关数据
const userPreferences = await getUserPreferences(user.id);
const userPosts = await getUserPosts(user.id);
return (
<div>
<UserProfile user={user} />
<UserSettings preferences={userPreferences} />
<UserPosts posts={userPosts} />
</div>
);
}
并行数据获取
javascript
export default async function Dashboard() {
// 并行获取独立的数据
const [stats, recentPosts, users] = await Promise.all([
getStats(),
getRecentPosts(),
getUsers()
]);
return (
<div>
<StatsCards stats={stats} />
<RecentPosts posts={recentPosts} />
<UsersList users={users} />
</div>
);
}
流式数据获取
javascript
import { Suspense } from 'react';
async function ExpensiveComponent() {
// 耗时操作
const data = await fetchExpensiveData();
return <div>{data}</div>;
}
export default function Page() {
return (
<div>
<h1>快速加载的内容</h1>
{/* 这部分会流式渲染 */}
<Suspense fallback={<div>加载昂贵数据中...</div>}>
<ExpensiveComponent />
</Suspense>
</div>
);
}
服务器函数(Server Functions) {#server-functions}
Server Actions
Server Actions 是可以在客户端调用的服务器函数。
基础用法
javascript
// app/actions.js
'use server';
import { redirect } from 'next/navigation';
import { revalidateTag } from 'next/cache';
export async function createUser(formData) {
const userData = {
name: formData.get('name'),
email: formData.get('email')
};
// 服务器端验证
if (!userData.email.includes('@')) {
return { error: '请输入有效的邮箱地址' };
}
try {
const user = await db.user.create({
data: userData
});
// 重新验证缓存
revalidateTag('users');
// 重定向到用户列表
redirect('/users');
} catch (error) {
return { error: '创建用户失败' };
}
}
export async function updateUserProfile(userId, formData) {
const updates = {
name: formData.get('name'),
bio: formData.get('bio')
};
await db.user.update({
where: { id: userId },
data: updates
});
revalidateTag(`user-${userId}`);
return { success: true };
}
内联 Server Actions
javascript
export default function TodoForm() {
async function addTodo(formData) {
'use server';
const title = formData.get('title');
if (!title.trim()) {
return;
}
await db.todo.create({
data: { title, completed: false }
});
revalidatePath('/todos');
}
return (
<form action={addTodo}>
<input type="text" name="title" placeholder="添加待办事项" />
<button type="submit">添加</button>
</form>
);
}
Server Functions 在组件中的使用
javascript
// app/profile/page.js
import { getUser } from './actions';
async function ProfileForm({ userId }) {
const user = await getUser(userId);
async function updateProfile(formData) {
'use server';
const updates = {
name: formData.get('name'),
email: formData.get('email'),
bio: formData.get('bio')
};
await db.user.update({
where: { id: userId },
data: updates
});
revalidatePath('/profile');
}
return (
<form action={updateProfile}>
<input
type="text"
name="name"
defaultValue={user.name}
placeholder="姓名"
/>
<input
type="email"
name="email"
defaultValue={user.email}
placeholder="邮箱"
/>
<textarea
name="bio"
defaultValue={user.bio || ''}
placeholder="个人简介"
/>
<button type="submit">更新资料</button>
</form>
);
}
高级服务器函数模式
带验证的服务器函数
javascript
'use server';
import { z } from 'zod';
const CreatePostSchema = z.object({
title: z.string().min(1, '标题不能为空'),
content: z.string().min(10, '内容至少10个字符'),
category: z.enum(['tech', 'lifestyle', 'travel'])
});
export async function createPost(prevState, formData) {
// 验证数据
const validatedFields = CreatePostSchema.safeParse({
title: formData.get('title'),
content: formData.get('content'),
category: formData.get('category')
});
if (!validatedFields.success) {
return {
errors: validatedFields.error.flatten().fieldErrors,
};
}
const { title, content, category } = validatedFields.data;
try {
await db.post.create({
data: { title, content, category }
});
revalidatePath('/posts');
return { success: true };
} catch (error) {
return {
errors: {
_form: ['创建文章失败']
}
};
}
}
带权限检查的服务器函数
javascript
'use server';
import { auth } from '@/lib/auth';
import { redirect } from 'next/navigation';
export async function deletePost(postId) {
const session = await auth();
if (!session?.user) {
redirect('/login');
}
const post = await db.post.findUnique({
where: { id: postId },
select: { authorId: true }
});
if (post?.authorId !== session.user.id) {
throw new Error('无权限删除此文章');
}
await db.post.delete({
where: { id: postId }
});
revalidatePath('/posts');
return { success: true };
}
最佳实践与使用场景 {#best-practices}
何时使用服务器组件
✅ 适合使用服务器组件:
- 数据获取(数据库查询、API 调用)
- 访问敏感信息(API 密钥、数据库凭证)
- 减少客户端 bundle 大小
- SEO 优化需求
javascript
// ✅ 好的服务器组件使用
export default async function ProductList({ category }) {
const products = await db.product.findMany({
where: { category },
include: { reviews: true }
});
return (
<div>
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}
何时使用客户端组件
✅ 适合使用客户端组件:
- 交互性(事件处理、状态管理)
- 浏览器 API(localStorage、geolocation)
- React Hooks 使用
- 实时更新
javascript
// ✅ 好的客户端组件使用
'use client';
import { useState, useEffect } from 'react';
export default function SearchBox({ onSearch }) {
const [query, setQuery] = useState('');
useEffect(() => {
const debounced = setTimeout(() => {
onSearch(query);
}, 300);
return () => clearTimeout(debounced);
}, [query, onSearch]);
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="搜索..."
/>
);
}
Suspense 最佳实践
javascript
// ✅ 合理的 Suspense 边界
export default function BlogPage() {
return (
<div>
{/* 快速加载的导航 */}
<Navigation />
{/* 主要内容使用 Suspense */}
<main>
<Suspense fallback={<PostListSkeleton />}>
<PostList />
</Suspense>
<aside>
<Suspense fallback={<SidebarSkeleton />}>
<Sidebar />
</Suspense>
</aside>
</main>
</div>
);
}
Server Actions 最佳实践
javascript
// ✅ 良好的错误处理和验证
'use server';
export async function updateSettings(prevState, formData) {
try {
// 验证用户权限
const session = await getSession();
if (!session) {
return { error: '请先登录' };
}
// 验证数据
const settings = validateSettings(formData);
if (!settings.success) {
return { error: settings.error };
}
// 更新数据
await db.userSettings.update({
where: { userId: session.userId },
data: settings.data
});
// 重新验证相关页面
revalidatePath('/settings');
return { success: '设置已更新' };
} catch (error) {
return { error: '更新失败,请重试' };
}
}
性能优化策略
- 合理使用缓存
javascript
// 使用 Next.js 缓存
export async function getStaticData() {
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 } // 1小时缓存
});
return data.json();
}
- 流式渲染优化
javascript
export default function Page() {
return (
<div>
<Header />
{/* 立即显示的内容 */}
<QuickContent />
{/* 流式渲染的慢内容 */}
<Suspense fallback={<Skeleton />}>
<SlowContent />
</Suspense>
</div>
);
}
- 并行数据获取
javascript
export default async function Dashboard() {
// 所有数据并行获取
const [user, posts, stats] = await Promise.all([
getUser(),
getPosts(),
getStats()
]);
return (
<div>
<UserInfo user={user} />
<PostList posts={posts} />
<Statistics stats={stats} />
</div>
);
}
这些现代 React 特性的组合使用能够创建更高性能、更好用户体验的应用程序,同时保持代码的简洁性和可维护性。