React19-核心更新

3 阅读5分钟

React 19 核心更新全面解析

2024年12月 正式稳定版发布 | 📚 学习指南


📑 目录

  1. Actions - 革命性的数据提交方式
  2. 新 Hooks 详解
  3. use API - 渲染中读取资源
  4. React Server Components
  5. ref 作为 prop - 告别 forwardRef
  6. Context 作为 Provider
  7. ref 清理函数
  8. 文档元数据原生支持
  9. 样式表原生支持
  10. 资源预加载 API
  11. 其他重要改进

1. Actions - 革命性的数据提交方式

什么是 Actions?

Actions 是 React 19 引入的约定,用于在表单提交和数据变更时自动处理:

  • Pending 状态 - 自动管理加载状态
  • 乐观更新 - 即时反馈用户体验
  • 错误处理 - 自动显示错误边界
  • 表单重置 - 提交成功后自动重置

对比:Before vs After

以前的写法:

// 手动管理 pending、error 状态
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");
  };
}

React 19 的写法:

// 使用 Actions + useActionState
function ChangeName({}) {
  const [error, submitAction, isPending] = useActionState(
    async (previousState, formData) => {
      const error = await updateName(formData.get("name"));
      if (error) return error;
      redirect("/path");
      return null;
    },
    null
  );

  return (
    <form action={submitAction}>
      <input type="text" name="name" />
      <button type="submit" disabled={isPending}>Update</button>
      {error && <p>{error}</p>}
    </form>
  );
}

💡 关键点: Actions 本质上是在 useTransition 中执行的异步函数,React 会自动管理 pending 状态!


2. 新 Hooks 详解

useActionState - 处理 Actions 的常见场景

const [data, action, isPending] = useActionState(actionFn, initialState);

// 返回值:
// - data: Action 的返回结果
// - action: 包装后的 Action 函数
// - isPending: 是否正在处理

简化了 Actions 的状态管理,是之前 useFormState 的升级版。

useOptimistic - 乐观更新

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>Your name is: {optimisticName}</p>
      <input name="name" disabled={currentName !== optimisticName} />
    </form>
  );
}

在请求完成前就显示更新后的 UI,请求失败时自动回滚。

useFormStatus - 获取表单状态

import {useFormStatus} from 'react-dom';

function DesignButton() {
  const {pending} = useFormStatus();
  return <button type="submit" disabled={pending} />;
}

在子组件中获取父表单的 pending 状态,无需 props 传递!


3. use API - 渲染中读取资源

use() 可以在渲染中读取 Promise 和 Context

读取 Promise(自动 Suspense)
import {use} from 'react';

function Comments({commentsPromise}) {
  const comments = use(commentsPromise); // 会 Suspense
  return comments.map(c => <p key={c.id}>{c}</p>);
}

function Page({commentsPromise}) {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Comments commentsPromise={commentsPromise} />
    </Suspense>
  );
}
读取 Context(支持条件调用)
import {use} from 'react';
import ThemeContext from './ThemeContext';

function Heading({children}) {
  if (children == null) return null; // 早期返回
  
  const theme = use(ThemeContext); // ✅ 这里可以用 use()
  return <h1 style={{color: theme.color}}>{children}</h1>;
}

⚠️ 注意: use() 不支持在渲染中创建的 Promise!需要从外部传入已缓存的 Promise。


4. React Server Components

什么是 Server Components?

服务端运行的组件,优势包括:

  • 🔒 安全 - 敏感代码(如数据库查询)不会暴露给客户端
  • 性能 - 减少客户端 JavaScript 体积
  • 📦 直接访问后端资源 - 可以直接调用数据库、文件系统

Server Actions

// 定义 Server Action
async function addTodo(formData) {
  'use server'; // 标记为服务端函数
  const title = formData.get('title');
  await db.todos.create({ title });
}

// 在客户端使用
<form action={addTodo}>
  <input name="title" />
  <button type="submit">Add</button>
</form>

💡 注意: 'use server' 是用于 Server Actions,Server Components 没有特殊的 directive!


5. ref 作为 prop - 告别 forwardRef

直接接收 ref 作为 prop

// React 19 - 不需要 forwardRef
function MyInput({placeholder, ref}) {
  return <input placeholder={placeholder} ref={ref} />
}

// 使用
<MyInput ref={inputRef} />

React 19 会自动处理 ref,不需要 forwardRef 包装。未来版本将废弃 forwardRef


6. Context 作为 Provider

直接使用 Context 作为 Provider

// 以前
<ThemeContext.Provider value="dark">
  {children}
</ThemeContext.Provider>

// React 19 - 直接使用
<ThemeContext value="dark">
  {children}
</ThemeContext>

7. ref 清理函数

ref 回调可以返回清理函数

<input
  ref={(ref) => {
    // 创建时执行
    console.log('ref created:', ref);
    
    // 返回清理函数 - 组件卸载时执行
    return () => {
      console.log('ref cleanup');
    };
  }}
/>

⚠️ 重要变化: 如果 ref 回调返回了清理函数,React 不会再用 null 调用它。未来将废弃用 null 清理 ref 的方式。


8. 文档元数据原生支持

可以在组件中直接使用 meta 标签

function BlogPost({post}) {
  return (
    <article>
      <h1>{post.title}</h1>
      <title>{post.title}</title>           {/* 页面标题 */}
      <meta name="author" content="Josh" />  {/* Meta 信息 */}
      <link rel="author" href="..." />      {/* 链接资源 */}
      <meta name="keywords" content={post.keywords} />
      <p>{post.content}</p>
    </article>
  );
}

React 会自动把这些标签提升<head> 区域,无需手动管理!


9. 样式表原生支持

组件内联样式表,自动管理加载顺序

function ComponentOne() {
  return (
    <Suspense fallback="loading...">
      <link rel="stylesheet" href="foo.css" precedence="default" />
      <link rel="stylesheet" href="bar.css" precedence="high" />
      <article class="foo-class bar-class">...</article>
    </Suspense>
  );
}
  • 📌 precedence 属性控制样式优先级
  • 🔒 SSR 时确保样式加载完成后再显示内容
  • 📦 自动去重,不会重复加载

10. 资源预加载 API

提前告诉浏览器加载资源

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

function MyComponent() {
  // 预连接第三方域
  preconnect('https://cdn.example.com');
  
  // 预加载字体
  preload('/fonts/font.woff', { as: 'font' });
  
  // 预加载样式
  preload('/styles.css', { as: 'style' });
  
  // 立即加载并执行脚本
  preinit('https://analytics.com/script.js', { as: 'script' });
}
  • preconnect - 建立网络连接
  • preload - 预加载资源(但不会执行)
  • preinit - 立即加载并执行(适合关键脚本)
  • prefetchDNS - 预解析域名

11. 其他重要改进

改进的 Hydration 错误提示

现在会显示具体的差异 diff,而不是只显示匹配失败。

第三方脚本/扩展兼容

Hydration 时会跳过第三方脚本和浏览器扩展插入的内容,避免无谓的错误。

自定义元素完整支持

React 19 完整支持 Web Custom Elements,解决了之前属性/属性不匹配的问题。

useDeferredValue initialValue

// 首次渲染使用 initialValue,然后后台更新
const value = useDeferredValue(deferredValue, '');

SSR 静态 API

import { prerender } from 'react-dom/static';

const {prelude} = await prerender(<App />, {
  bootstrapScripts: ['/main.js']
});

用于静态站点生成,等待所有数据加载后再输出 HTML。


总结

🎉 React 19 核心主题

  • 🎯 Actions - 简化数据提交和表单处理
  • 🔮 use() - 渲染中直接读取异步资源
  • 🖥️ RSC - 服务端组件生态完善
  • 性能 - 资源预加载、样式表管理
  • 📝 DX - ref 作为 prop、Context provider 简化

🚀 升级建议

新项目可以直接使用 React 19。旧项目建议阅读官方 升级指南,注意 breaking changes。


📚 来源:React 19 官方博客 | 🦎 整理:小龙