前置知识: JavaScriptHTML5CSS

React19新特性

4 minAdvanced2026/6/14

React Server Components、use() Hook、Actions、useFormStatus、useOptimistic、useActionState、Suspense 进阶与流式 SSR。

1. React Server Components (RSC)

React Server Components 是 React 19 最重要的特性,允许组件在服务端渲染,减少客户端 JavaScript 体积。

1.1 Server Components vs Client Components

特性Server ComponentClient Component
运行环境服务端客户端(浏览器)
获取数据直接访问数据库/文件系统通过 API/fetch
交互性无(无状态、无事件有(useState、onClick 等)
Bundle 体积零(不发送到客户端)包含在客户端 Bundle 中
文件后缀.tsx(默认).tsx + 'use client'

1.2 Server Components 示例

// app/posts/page.tsx — 默认是 Server Component
import { db } from '@/lib/db';

// 直接访问数据库,无需 API
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>
        </article>
      ))}
    </div>
  );
}

export default PostsPage;

1.3 Client Components

'use client'; // 声明为客户端组件

import { useState } from 'react';

export function LikeButton({ postId }: { postId: string }) {
  const [liked, setLiked] = useState(false);
  const [count, setCount] = useState(0);

  const handleLike = async () => {
    setLiked(!liked);
    setCount((c) => (liked ? c - 1 : c + 1));
    await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
  };

  return (
    <button onClick={handleLike}>
      {liked ? '' : ''} {count}
    </button>
  );
}

1.4 组合模式

// Server Component 可以导入和渲染 Client Component
import { LikeButton } from './LikeButton'; // Client Component
import { getPost } from '@/lib/db';

async function PostPage({ id }: { id: string }) {
  const post = await getPost(id); // 服务端数据获取

  return (
    <article>
      <h1>{post.title}</h1>
      <div>{post.content}</div>
      {/* Client Component 嵌入 Server Component */}
      <LikeButton postId={id} />
    </article>
  );
}

注意:Client Component 不能导入 Server Component,但可以通过 children prop 传入。

2. use() Hook

use() 是 React 19 新增的 Hook,用于读取 Promise 或 Context 的值。

2.1 读取 Promise

import { use, Suspense } from 'react';

// 在组件外部创建 Promise
const userPromise = fetch('/api/user').then((res) => res.json());

function UserProfile() {
  // use() 会挂起组件直到 Promise resolve
  const user = use(userPromise) as { name: string; email: string };

  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

// 必须配合 Suspense 使用
function App() {
  return (
    <Suspense fallback={<p>加载用户信息...</p>}>
      <UserProfile />
    </Suspense>
  );
}

2.2 读取 Context

import { use, createContext } from 'react';

const ThemeContext = createContext<'light' | 'dark'>('light');

// use() 可以在条件语句中调用(与 useContext 不同)
function ThemedComponent({ showTheme }: { showTheme: boolean }) {
  if (showTheme) {
    const theme = use(ThemeContext); //  use() 可以在条件中调用
    return <p>当前主题:{theme}</p>;
  }
  return <p>未显示主题</p>;
}

2.3 use() 与 useContext 的区别

特性useContextuse()
条件中调用不可以可以
读取 Promise不可以可以
读取 Context可以可以
需要 Suspense不需要读取 Promise 时需要

3. Actions

Actions 是 React 19 引入的异步状态管理模式,简化表单提交和异步操作。

3.1 表单 Action

async function createUser(formData: FormData) {
  'use server'; // Next.js Server Action

  const name = formData.get('name') as string;
  const email = formData.get('email') as string;

  await db.user.create({ data: { name, email } });
  redirect('/users');
}

function CreateUserForm() {
  return (
    <form action={createUser}>
      <input name="name" placeholder="姓名" required />
      <input name="email" type="email" placeholder="邮箱" required />
      <button type="submit">创建用户</button>
    </form>
  );
}

3.2 客户端 Action

function SearchForm() {
  const [results, setResults] = useState([]);

  async function handleSearch(formData: FormData) {
    const query = formData.get('query') as string;
    const res = await fetch(`/api/search?q=${query}`);
    const data = await res.json();
    setResults(data);
  }

  return (
    <form action={handleSearch}>
      <input name="query" />
      <button type="submit">搜索</button>
      <ul>
        {results.map((r) => (
          <li key={r.id}>{r.title}</li>
        ))}
      </ul>
    </form>
  );
}

4. useFormStatus

useFormStatus 获取父级 <form> 的提交状态,无需传递 Props。

import { useFormStatus } from 'react-dom';

function SubmitButton() {
  const { pending, data, method, action } = useFormStatus();

  return (
    <button type="submit" disabled={pending}>
      {pending ? '提交中...' : '提交'}
    </button>
  );
}

function ContactForm() {
  async function handleSubmit(formData: FormData) {
    await sendEmail(formData);
  }

  return (
    <form action={handleSubmit}>
      <input name="email" type="email" required />
      <textarea name="message" required />
      <SubmitButton /> {/* 自动获取表单状态 */}
    </form>
  );
}

注意useFormStatus 必须在 <form> 内部的组件中调用,且该组件必须是 <form> 的子组件,不能是 <form> 本身。

5. useOptimistic

useOptimistic 实现乐观更新,在异步操作完成前先展示预期结果。

import { useOptimistic, useState } from 'react';

interface Message {
  id: string;
  text: string;
  sending?: boolean;
}

function Chat({
  messages,
  onSend,
}: {
  messages: Message[];
  onSend: (text: string) => Promise<void>;
}) {
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (currentMessages, newText: string) => [
      ...currentMessages,
      { id: crypto.randomUUID(), text: newText, sending: true },
    ]
  );

  const [input, setInput] = useState('');

  async function handleSubmit(formData: FormData) {
    const text = formData.get('message') as string;
    setInput('');
    addOptimisticMessage(text); // 立即显示乐观消息
    await onSend(text); // 实际发送
  }

  return (
    <div>
      <ul>
        {optimisticMessages.map((msg) => (
          <li key={msg.id} style={{ opacity: msg.sending ? 0.5 : 1 }}>
            {msg.text} {msg.sending && '(发送中...)'}
          </li>
        ))}
      </ul>
      <form action={handleSubmit}>
        <input name="message" value={input} onChange={(e) => setInput(e.target.value)} />
        <button type="submit">发送</button>
      </form>
    </div>
  );
}

6. useActionState

useActionState 管理表单 Action 的状态(返回值、加载状态)。

import { useActionState } from 'react';

interface FormState {
  message: string;
  success: boolean;
}

async function submitOrder(prevState: FormState, formData: FormData): Promise<FormState> {
  try {
    const item = formData.get('item') as string;
    const quantity = parseInt(formData.get('quantity') as string);

    await createOrder({ item, quantity });

    return { message: '订单创建成功!', success: true };
  } catch (error) {
    return { message: `创建失败:${(error as Error).message}`, success: false };
  }
}

function OrderForm() {
  const [state, submitAction, isPending] = useActionState(submitOrder, {
    message: '',
    success: false,
  });

  return (
    <form action={submitAction}>
      <input name="item" placeholder="商品名称" required />
      <input name="quantity" type="number" min="1" required />
      <button type="submit" disabled={isPending}>
        {isPending ? '提交中...' : '下单'}
      </button>
      {state.message && <p style={{ color: state.success ? 'green' : 'red' }}>{state.message}</p>}
    </form>
  );
}

7. Suspense 进阶

7.1 嵌套 Suspense

import { Suspense } from 'react';

function Dashboard() {
  return (
    <div>
      <h1>仪表盘</h1>
      {/* 每个区域独立加载 */}
      <Suspense fallback={<ChartSkeleton />}>
        <SalesChart />
      </Suspense>

      <Suspense fallback={<TableSkeleton />}>
        <RecentOrders />
      </Suspense>

      <Suspense fallback={<ListSkeleton />}>
        <Notifications />
      </Suspense>
    </div>
  );
}

7.2 Suspense 与数据获取

// 封装数据获取函数
function fetchUser(id: string) {
  let status = 'pending';
  let result: User;
  let error: Error;

  const promise = fetch(`/api/users/${id}`)
    .then((res) => res.json())
    .then((data) => {
      status = 'success';
      result = data;
    })
    .catch((err) => {
      status = 'error';
      error = err;
    });

  return {
    read() {
      if (status === 'pending') throw promise;
      if (status === 'error') throw error;
      return result;
    },
  };
}

// 在组件中使用
function UserProfile({ id }: { id: string }) {
  const user = fetchUser(id).read(); // 挂起直到数据就绪
  return <div>{user.name}</div>;
}

8. 流式 SSR

React 19 支持流式服务端渲染,允许逐步发送 HTML 到客户端。

8.1 Node.js 流式渲染

import { renderToPipeableStream } from 'react-dom/server';
import { App } from './App';

app.get('/', (req, res) => {
  const { pipe } = renderToPipeableStream(<App />, {
    bootstrapScripts: ['/client.js'],
    onShellReady() {
      res.setHeader('content-type', 'text/html');
      pipe(res);
    },
    onError(error) {
      console.error('SSR 错误:', error);
    },
  });
});

8.2 Next.js 中的流式渲染

Next.js App Router 默认使用流式 SSR:

// app/page.tsx
import { Suspense } from 'react';

async function SlowData() {
  const data = await fetch('https://api.example.com/slow', {
    next: { revalidate: 60 },
  });
  const json = await data.json();
  return <div>{json.content}</div>;
}

export default function Page() {
  return (
    <div>
      <h1>快速内容</h1>
      {/* 快速内容立即显示,SlowData 流式加载 */}
      <Suspense fallback={<p>加载中...</p>}>
        <SlowData />
      </Suspense>
    </div>
  );
}

9. 其他 React 19 改进

9.1 文档元数据

// React 19 支持在组件中声明 <title>、<meta> 等标签
function BlogPost({ post }: { post: Post }) {
  return (
    <article>
      <title>{post.title}</title>
      <meta name="description" content={post.excerpt} />
      <link rel="canonical" href={`https://example.com/posts/${post.id}`} />
      <h1>{post.title}</h1>
      <div>{post.content}</div>
    </article>
  );
}

9.2 样式表支持

function Component() {
  return (
    <>
      {/* 通过 precedence 控制样式表加载顺序 */}
      <link rel="stylesheet" href="reset.css" precedence="default" />
      <link rel="stylesheet" href="styles.css" precedence="high" />
      <div className="styled">内容</div>
    </>
  );
}

9.3 异步脚本支持

function MapComponent() {
  return (
    <>
      <script async src="https://maps.googleapis.com/maps/api/js" />
      <div id="map">地图容器</div>
    </>
  );
}

9.4 ref 回调清理

function Input() {
  const ref = useCallback((node: HTMLInputElement | null) => {
    if (node) {
      // 挂载时
      node.focus();
    }
    return () => {
      // 卸载时清理(React 19 新增)
    };
  }, []);

  return <input ref={ref} />;
}