React 19 有哪些新特性?

一、是什么

React 19 是 React 的又一次重大版本更新,于 2024 年 12 月正式发布。它在 React 18 的并发渲染基础上,进一步简化了开发者体验并增强了性能优化能力。核心亮点包括 React Compiler(自动记忆化)、Actions(异步表单处理)、use() Hook、ref 作为 prop 传递、以及对文档元数据和资源加载的原生支持等。

二、React Compiler

2.1 告别手动 memo

React Compiler 是 React 19 最具革命性的特性。它在构建时自动分析组件,插入等价于 memouseMemouseCallback 的优化代码:

// React 18:需要手动优化
const ExpensiveList = memo(function ExpensiveList({ items, onSelect }) {
  const sorted = useMemo(
    () => [...items].sort((a, b) => a.name.localeCompare(b.name)),
    [items]
  );
  const handleSelect = useCallback(
    (id) => onSelect(id),
    [onSelect]
  );

  return sorted.map(item => (
    <Item key={item.id} data={item} onClick={handleSelect} />
  ));
});

// React 19 + Compiler:直接写,编译器自动优化
function ExpensiveList({ items, onSelect }) {
  const sorted = [...items].sort((a, b) => a.name.localeCompare(b.name));

  return sorted.map(item => (
    <Item key={item.id} data={item} onClick={(id) => onSelect(id)} />
  ));
}

2.2 编译器的工作原理

React Compiler 是一个 Babel 插件,它:

  1. 分析组件中每个值的依赖关系
  2. 自动为变量、JSX 和函数插入缓存逻辑
  3. 只在依赖变化时重新计算,否则返回缓存结果
  4. 遵循 React 的规则(如 Hook 规则、纯渲染),违反规则的组件会被跳过

三、Actions

3.1 什么是 Actions

Actions 是 React 19 对表单和异步操作的统一抽象。startTransition 现在支持异步函数,在 transition 中执行的异步函数被称为 Action:

function UpdateProfile() {
  const [isPending, startTransition] = useTransition();
  const [error, setError] = useState(null);

  async function handleSubmit(formData) {
    startTransition(async () => {
      const result = await saveProfile(formData);
      if (result.error) {
        setError(result.error);
      }
    });
  }

  return (
    <form action={handleSubmit}>
      <input name="name" />
      <input name="email" type="email" />
      <button disabled={isPending}>
        {isPending ? '保存中...' : '保存'}
      </button>
      {error && <p className="error">{error}</p>}
    </form>
  );
}

3.2 useActionState

useActionState 封装了 Action 的常见模式——管理 pending 状态、返回值和表单重置:

import { useActionState } from 'react';

async function addToCart(previousState, formData) {
  const itemId = formData.get('itemId');
  try {
    await api.addToCart(itemId);
    return { success: true, message: '已添加到购物车' };
  } catch (e) {
    return { success: false, message: e.message };
  }
}

function AddToCartButton({ itemId }) {
  const [state, submitAction, isPending] = useActionState(addToCart, null);

  return (
    <form action={submitAction}>
      <input type="hidden" name="itemId" value={itemId} />
      <button disabled={isPending}>
        {isPending ? '添加中...' : '加入购物车'}
      </button>
      {state?.message && (
        <p className={state.success ? 'success' : 'error'}>
          {state.message}
        </p>
      )}
    </form>
  );
}

3.3 useFormStatus

useFormStatus 在表单内部子组件中获取父级 <form> 的提交状态:

import { useFormStatus } from 'react-dom';

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

必须在 <form> 的子组件中调用,不能在包含 <form> 的同一组件中使用。

3.4 useOptimistic

useOptimistic 在异步操作进行时提供乐观 UI 更新:

import { useOptimistic } from 'react';

function TodoList({ todos, addTodoAction }) {
  const [optimisticTodos, addOptimistic] = useOptimistic(
    todos,
    (currentTodos, newTitle) => [
      ...currentTodos,
      { id: crypto.randomUUID(), title: newTitle, pending: true },
    ]
  );

  async function handleSubmit(formData) {
    const title = formData.get('title');
    addOptimistic(title);
    await addTodoAction(title);
  }

  return (
    <div>
      <form action={handleSubmit}>
        <input name="title" placeholder="新任务" />
        <button type="submit">添加</button>
      </form>
      <ul>
        {optimisticTodos.map(todo => (
          <li key={todo.id} style={{ opacity: todo.pending ? 0.6 : 1 }}>
            {todo.title}
          </li>
        ))}
      </ul>
    </div>
  );
}

四、use() Hook

use() 是一个可以在条件语句和循环中调用的特殊 Hook,用于读取 Promise 和 Context:

4.1 读取 Promise

import { use, Suspense } from 'react';

function UserProfile({ userPromise }) {
  const user = use(userPromise);

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

function ProfilePage({ userId }) {
  const userPromise = fetchUser(userId);

  return (
    <Suspense fallback={<ProfileSkeleton />}>
      <UserProfile userPromise={userPromise} />
    </Suspense>
  );
}

4.2 条件读取 Context

import { use, createContext } from 'react';

const ThemeContext = createContext('light');

function StatusIcon({ isActive }) {
  if (isActive) {
    const theme = use(ThemeContext);
    return <span className={`icon icon-${theme}`}>●</span>;
  }
  return <span className="icon">○</span>;
}

useContext 不同,use() 可以在 if 语句和 for 循环内调用。

五、ref 作为 prop

React 19 中函数组件可以直接接收 ref 作为 prop,不再需要 forwardRef

// React 18:需要 forwardRef
const Input = forwardRef(function Input(props, ref) {
  return <input ref={ref} {...props} />;
});

// React 19:ref 就是一个普通的 prop
function Input({ ref, ...props }) {
  return <input ref={ref} {...props} />;
}

// 使用方式不变
function Form() {
  const inputRef = useRef(null);
  return (
    <div>
      <Input ref={inputRef} placeholder="输入内容" />
      <button onClick={() => inputRef.current.focus()}>聚焦</button>
    </div>
  );
}

ref 回调的清理函数

React 19 中 ref 回调支持返回清理函数:

function MeasuredBox() {
  return (
    <div
      ref={(node) => {
        if (node) {
          const observer = new ResizeObserver(entries => {
            console.log('尺寸变化:', entries[0].contentRect);
          });
          observer.observe(node);
          return () => observer.disconnect();
        }
      }}
    >
      可观测尺寸的容器
    </div>
  );
}

六、文档元数据支持

React 19 原生支持在组件中渲染 <title><meta><link> 等标签,React 会自动将它们提升到 <head> 中:

function BlogPost({ post }) {
  return (
    <article>
      <title>{post.title} - 我的博客</title>
      <meta name="description" content={post.summary} />
      <meta property="og:title" content={post.title} />
      <link rel="canonical" href={`https://myblog.com/posts/${post.slug}`} />

      <h1>{post.title}</h1>
      <div>{post.content}</div>
    </article>
  );
}

不再需要 react-helmet 等第三方库来管理文档头信息。

七、资源加载与文档元数据

React 19 提供了预加载资源的 API(preloadpreinitpreconnectprefetchDNS),用于优化页面性能:

import { preload, preinit } from 'react-dom';

function App() {
  preinit('/libs/analytics.js', { as: 'script' });
  preload('/fonts/inter.woff2', { as: 'font', type: 'font/woff2' });
  return <MainContent />;
}

八、其他改进

  • 错误报告createRoot 新增 onCaughtErroronUncaughtErroronRecoverableError 回调,消除重复错误日志
  • Web Components:完整支持 Custom Elements,正确区分 attribute/property 并映射事件

九、总结

特性说明
React Compiler自动记忆化,告别手动 memo/useMemo/useCallback
Actions异步 transition,统一表单提交模式
useActionState管理 Action 的状态和返回值
useFormStatus获取父级表单提交状态
useOptimistic乐观 UI 更新
use()在任意位置读取 Promise 和 Context
ref as prop不再需要 forwardRef
文档元数据原生 <title>/<meta> 支持
资源预加载preload/preinit/preconnect API
错误处理根级错误回调,消除重复日志
Web Components完整 Custom Elements 支持

React 19 的核心理念是"让正确的事情变得简单"——通过编译器消除手动优化负担,通过 Actions 简化异步交互,通过 use() 统一异步数据消费模式。