为了账号安全,请及时绑定邮箱和手机立即绑定

React 19版本概览

React.js React 19 的更新概览

今年四月,React 核心团队宣布了 React 19 的发布候选版,这次更新带来了很多新功能和改进,让这个广受欢迎的库更加完善。

注意 : 在撰写本文时,React 19 仍处于测试版,还不够稳定。React 19 发布时,本文将会作出相应更新。

目录
  • 新功能
  • 更新内容
  • 附注
React 19 的新功能

React 19 真的是一个让人兴奋的更新,带来了能让开发人员和用户使用 React 更加愉快的功能。

React 服务端组件

React 服务端组件(RSC)是即将加入 React 的最令人兴奋和期待的功能之一。

RSC 允许在服务器上渲染组件以及它们所需的数据,这些数据可以直接在组件内部获取。这可以使得完全填充的组件以最短的时间渲染到用户界面,同时减少了客户端需要加载的 JavaScript 代码量,从而改善了初始页面加载时间,提高了 SEO(搜索引擎优化)等性能。

    export default async function Page() {  
      const res = await fetch("https://api.example.com/products");  
      const products = await res.json();  
      return (  
        <>  
          <h1>商品</h1>  
          {products.map((product) => (  
            <div key={product.id}>  
              <h2>{product.标题}</h2>  
              <p>{product.描述}</p>  
            </div>  
          ))}  
        </>  
      );  
    }

了解更多关于 React Server Components:

行动

操作是异步函数,可以在 React 过渡过程中使用,用于处理和更新状态信息,以及执行数据获取和变异查询。

动作替换了事件处理器,特别适合处理表单提交和API和数据查询,能够轻松处理错误、待处理状态和乐观情形。这些能力在下一节提到的钩子中进一步得到应用。

    function 更新名称({}) {  
      const [name, setName] = useState(""); // 设置名称状态
      const [error, setError] = useState(null); // 设置错误状态
      const [isPending, startTransition] = useTransition(); // 设置异步状态

      const handleSubmit = () => {  
        startTransition(async () => {  
          const error = await updateName(name); // 更新名称并检查错误
          if (error) {  
            setError(error);  
            return;  
          }   
          重定向("/path"); // 重定向到指定路径
        })  
      };  

      return (  
        <div>  
          <input value={name} onChange={(event) => setName(event.target.value)} placeholder="请输入名称" />  
          <button onClick={handleSubmit} disabled={isPending}>  
            更改  
          </button>  
          {error && <p>{error}</p>} // 显示错误信息
        </div>  
      );  
    }

在这个例子中,一个操作传递给startTransition函数(在表单提交时),用来处理错误和处理中的状态。

表单动作

React 19 允许通过 actionformAction 属性将 Actions 传递给表单元素,简化了表单提交处理流程。这对于即将讨论的钩子来说非常重要。

服务器行为

服务器动作允许客户端组件调用服务器上的异步函数,让客户端组件能够直接与服务器通信并执行操作,比如读取系统文件、查询数据库或修改数据库,从而无需为客户端设置显式的服务器功能端点。

服务器动作函数是带有 "use server" 指令标记的异步函数,可以作为属性传递给客户端组件,或者直接从文件导入。

导入一个服务器端操作并在此客户端组件中使用它的例子

    "使用服务器代码";  

    // 服务器操作  
    export async function 创建笔记() {  
      await db.notes.create();  
    }
    "use client";  
    import {createNoteAction} from "./actions";  

    // 客户端组件代码  
    function EmptyNote() {  
      console.log(createNoteAction);  
      // {$typeof: Symbol.for("react.server.reference"), $id: 'createNoteAction'}  
      return <button onClick={createNoteAction} />;
    }

一个如何在服务器组件中定义服务器动作,并将其作为属性传递给客户端组件的示例

import Button from ‘./Button’;  

// 服务器组件  
function EmptyNote () {  
  async function createNoteAction() {  
    // 服务器操作  
    'use server'; // 指定这是一个服务器端操作  

    await db.notes.create();  
  }  

  return <Button onClick={createNoteAction}/>;  
}
    "use client";  

    // 客户端组件:  
    export default function Button({onClick}) {     
      console.log(onClick);     
      // {$typeof: Symbol.for("react.server.reference"), $id: 'createNoteAction'}  
      return <button onClick={() => onClick()}>创建空白笔记</button>  
    }
新增功能

React 19 引入了多个新的 Hook,主要帮助处理表单及其周围的状态和数据管理。

**useActionState**

这个新钩子通过处理常见情况来简化表单状态管理和提交过程,利用Actions减少了自定义状态管理逻辑的需求。

    import { useActionState } from "react";  

    async function increment(previousState, formData) {  
      return previousState + 1;  
    }  

    function StatefulForm({}) {  
      const [state, formAction, pending] = useActionState(increment, 0);  
      return (  
        <form action={formAction}>  
          {state}  
          <button type="submit" disabled={pending}>增加</button>  
        </form>  
      )  
    }

你可以看到,这个钩子接收一个动作(increment)和一个初始状态(0),并返回一个状态、可以直接传递给表单的表单动作,以及一个用于控制表单提交过程的挂起状态。

**useFormStatus(表单状态)**

这个新的 react-dom 钩子引入了使子组件能够无需通过 prop 传递或上下文(Context)传递,访问它所在的 <form> 的相关信息。

    import { useFormStatus } from "react-dom";  
    import action from './actions';  

    function 提交Button() {  
      const { pending, data, method } = useFormStatus();  
      return <button disabled={pending}>提交</button>;  
    }  

    export default function App() {  
      return (  
        <form action={action}>  
          <提交Button />  
        </form>  
      );  
    }

请注意useFormStatus 钩子不会提供在同一组件中渲染的 <form> 的状态。要让 useFormStatus 能正常运作,它需要在表单内的子组件中使用。

[使用乐观更新](https://19.react.dev/reference/react/useOptimistic#noun-labs-1201738)

这个新的钩子允许在异步请求进行时在状态更新时乐观地显示状态。
该钩子旨在简化实现这种常见的UI模式,而无需额外编写代码或引入外部库。

function 修改名字({currentName, onUpdateName}) {  
  const [optimisticName, setOptimisticName] = useOptimistic(currentName);  

  const 提交处理 = async formData => {  
    const newName = formData.get("name");  
    setOptimisticName(newName);  
    const updatedName = await updateName(newName);  
    onUpdateName(updatedName);  
  };  

  return (  
    <form onSubmit={提交处理}>  
      <p>你的名字现在是:{optimisticName}</p>  
      <p>  
        <label>更改名字:</label>  
        <input  
          type="text"  
          name="name"  
          disabled={currentName !== optimisticName}  
          placeholder="请输入新的名字:"  
          title="(如果当前名字与乐观名字不同则不可用)"  
        />  
      </p>  
    </form>  
  );  
}

在这个例子中,当 updateName 请求尚未完成时,optimisticName 会立即渲染。无论更新是完成还是出错时,React 会自动回退到 currentName 的值。这样,React 会自动回退到 currentName 的值。

使用use() API

即将推出的 use 函数用于在渲染期间读取资源,包括承诺(promise)和上下文(context)。
例如,你可以使用 use 读取一个承诺,React 会暂停直到承诺解析。
你还可以用 use 读取上下文,从而有条件地读取上下文,例如,在提前返回之后。

**use**读承诺对象的示例

import {use} from 'react';

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

function 页面组件({commentsPromise}) {  
  return (  
    <Suspense fallback={<div>加载中...</div>}>  
      <Comments commentsPromise={commentsPromise} />  
    </Suspense>  
  )  
}

利用use来读取上下文信息的示例

导入 {use} from "react";  
导入 ThemeContext from "./ThemeContext";  

定义一个名为 Heading 的函数 ({children}) {  
  如果 children 为 null {  
    返回 null;  
  }  

  const theme = use(ThemeContext);  
  返回 (  
    <h1 style={{color: theme.color}}>  
      {children}  
    </h1>  
  );  
}

请注意,因为有早期的返回,这个例子无法利用useContext,这一点显示了use与其他钩子的不同之处——use可以在早期返回、条件判断和循环中使用。

了解更多关于如何使用 [use](https://react.dev/reference/react/use)

React 19 新特性

React 19 带来了许多新功能和改进,从而提升开发人员的使用体验,同时也增强了用户的体验。

ref 作为属性和清理 ref 的方法

ref 可以像普通属性一样传递给组件,这样就不用再使用 forwardRef

函数 MyInput({placeholder, ref}) {  
  return <input placeholder={placeholder} ref={ref} />  
}  

<MyInput ref={ref} />

支持通过回调 ref 返回一个清理函数,该函数将在组件卸载时自动执行。

<input  
  ref={(ref) => {  
    // ref 设置

    return () => {  
      // 清除ref  
    };  
  }}  
/>
<Context> 作为服务商

可以直接用 <Context> 作为提供者,无需使用 <Context.Provider>

const ThemeContext = createContext("");  

function App({children}) {  
  return (  
    <ThemeContext value="暗模式">  
      {children}  
    </ThemeContext>  
  );    
}
useDeferredValue 的初始值

useDeferredValue 可以接收一个额外的 initialValue 参数。当提供 initialValue 时,useDeferredValue 在组件的首次渲染中返回 value,并在后台使用返回的 deferredValue 进行重新渲染。

    function 搜索函数({延迟值参数}) {  
      // 在初始渲染时值为''。  
      // 然后计划进行重新渲染。  
      const 当前值 = useDeferredValue(延迟值参数, "");  

      return (  
        <结果组件 查询参数={当前值} />  
      );  
    }

在初始渲染时,value 将是 initialValue""),随后将安排重新渲染使用 deferredValue

资源预加载

React 19支持预加载浏览器资源来解决由于资源加载效率低下导致的性能问题。

    import { preconnect, preload, preinit, prefetchDNS } from 'react-dom';  
    function MyComponent() {  
      preinit('https://.../path/to/some/script.js', {as: 'script' }) // 立即加载并执行此脚本的内容  
      preload('https://.../path/to/font.woff', { as: 'font' }) // 预加载此字体  
      preload('https://.../path/to/stylesheet.css', { as: 'style' }) // 预加载此CSS样式表  
      prefetchDNS('https://...') // 当你可能不会实际从该主机请求任何资源时  
      preconnect('https://...') // 当你不确定将请求该主机上的具体资源时  
    }
    <!-- 上面会生成如下的DOM/HTML结构 -->  
    <html>  
      <head>  
        <!-- 链接和脚本的加载优先级根据它们对早期加载的重要性来排序,而不是调用顺序 -->  
        <link rel="prefetch-dns" href="https://..."> <!-- 预取DNS -->  
        <link rel="preconnect" href="https://..."> <!-- 预连接 -->  
        <link rel="preload" as="font" href="https://.../path/to/font.woff"> <!-- 预加载字体 -->  
        <link rel="preload" as="style" href="https://.../path/to/stylesheet.css"> <!-- 预加载样式表 -->  
        <script async="" class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="https://.../path/to/some/script.js"></script> <!-- 异步加载脚本 -->  
      </head>  
      <body>  
        ...  
      </body>  
    </html>
支持文档的元数据

在 React 19 中,支持在组件内添加元数据标签,例如 <title><link><meta>,这些标签将被正确提升到 HTML 页面的 <head> 部分,从而无需手动通过效果将其插入到 <head> 中,也不需要使用外部库来完成这个任务。

    function BlogPost({post}) {  
      return (  
        <article>  
          <h1>{post.title}</h1>  
          <meta name="author" content="Josh" />  
          <link rel="author" href="https://twitter.com/joshcstory/" />  
          <meta name="keywords" content={post.keywords} />  
          <p>  
            遵循质能方程,E 等于 mc平方...  
          </p>  
        </article>  
      );  
    }
支持样式表

在 React 19 版本中,将支持将样式表添加到 React 组件。通过使用 precedence 属性,React 可以管理样式表的插入顺序,确保样式表在依赖这些样式规则的内容显示之前加载,并确保样式表按照优先级规则正确插入到 DOM 中。

    function ComponentOne() {  
      return (  
        <Suspense fallback="加载中...">  
          <link rel="stylesheet" href="foo" precedence="default" />  
          <link rel="stylesheet" href="bar" precedence="high" />  
          <article class="foo-class bar-class">  
            {...}  
          </article>  
        </Suspense>  
      )  
    }  

    function ComponentTwo() {  
      return (  
        <div>  
          <p>{...}</p>  
          <link rel="stylesheet" href="baz" precedence="default" />  <-- 这里会把 baz 插入到 foo 和 bar 之间  
        </div>  
      )  
    }
后记
升级到 React 19

React 19 引入了多项重大变更。要了解如何升级代码库,请参阅 React 19 RC 升级指引

资源和推荐内容

本文是对即将推出的 React 19 的简单介绍。想了解更多更新详情,可以查看 React 19 RC 文章(React 19 的候选发布版)和 React 团队的成员的文章。请注意,本文中的大多数示例均来自 React 团队的文章和其他 react.dev 文档页面,并稍作调整。

我也推荐迈克尔·诺沃蒂尼(Michael Novotny)在Vercel 博客上写的一篇很棒的文章《React 19 新功能》

栈学 🎓

感谢你读到最后,希望下面的话对你还有用。在你离开前,

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消