今年二月,React 官方宣布了即将发布的 19 版本,这个版本号已经静静躺了两年。这一次,React 团队成员 Andrew Clark 确认新版本将在三月或四月发布。果然,正如他们所说,“截止日期是生产力的第一驱动力。” 在四月底,React 19.0.0-Beta 准时与大家见面了。
尽管目前只是 Beta 版本,但它已经在社区引起了相当大的轰动:
Dan 评论道:“他们做到了。”Andrew Clark 评论道:“React 19: 再也不用 forwardRef 了。”Josh W. Comeau 评论道:“这里有很多不错的生活质量提升!”唯一让人失望的是,React 成员 Lauren 确认 React 编译器再次延期。原本被命名为 “React Forget” 的项目,现在看来这个名字倒是很贴切。
那么,React 19.0.0-Beta 版本带来了哪些新特性呢?让我们一起来看看:
全新 Actions 特性让开发更简单Actions 并不是某个特定的 API,而是一个广义的术语,用于描述简化请求中数据处理的方法。一个有效的 Actions 设置应该简化异步操作,让开发者能够更多地关注业务逻辑,而不是状态管理。
使用场景示例
举个例子,假设我们有一个表单,用户可以在其中更新他们的名字。之前,我们可能会使用 useState 来手动管理表单状态、错误状态和提交状态。代码可能如下:
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"); }; return ( <div> <input value={name} onChange={(event) => setName(event.target.value)} /> <button onClick={handleSubmit} disabled={isPending}> Update </button> {error && <p>{error}</p>} </div> );}使用 React 19 的 Actions 特性优化后的代码
有了 React 19 的 Actions 特性后,我们可以使用 useTransition Hook 来处理表单提交,这会自动管理挂起状态,使我们的代码更加简洁:
function UpdateName() { 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; } redirect("/path"); }); }; return ( <div> <input value={name} onChange={(event) => setName(event.target.value)} /> <button onClick={handleSubmit} disabled={isPending}> Update </button> {error && <p>{error}</p>} </div> );}在 handleSubmit 函数中,异步请求的逻辑封装在传递给 startTransition 的回调函数中。当调用 startTransition 时,React 立即将 isPending 设置为 true,表示请求正在进行中。React 然后在后台异步执行这个回调函数。一旦请求完成,React 会自动将 isPending 切换回 false。
这种功能将 isPending 绑定到提交按钮的 disabled 属性上,因此在请求期间按钮会自动变为禁用状态,防止重复提交。
Actions 的命名约定和功能总结
命名约定:使用异步转换的函数可以被称为 “Actions”。挂起状态:Actions 自动管理数据提交的挂起状态。当请求发起时,挂起状态自动激活,并在最终状态更新后重置。这确保用户在数据提交时得到反馈,并在请求完成后清除挂起状态。乐观更新:Actions 支持乐观更新,在请求挂起时向用户显示正确的提交结果。如果请求最终失败,乐观更新会自动恢复到原始状态。错误处理:Actions 包含内置的错误处理。当请求失败时,可以使用错误边界显示错误消息。表单支持:元素现在支持将函数传递给 action 和 formAction 属性。通过将函数传递给 action 属性,可以使用 Actions 处理表单提交,并在提交后自动重置表单。这简化了处理表单的过程,使其更加直观和高效。React 19 全新 Hook 解读:简化状态处理React 19 的发布不仅带来了 Actions 特性,还引入了三个全新的 Hook,用于简化开发者在处理状态时的复杂操作。今天我们将详细介绍这些新 Hook,包括它们的用途和使用方法。
一、useOptimistic 基本用法(乐观更新)
useOptimistic 主要用于在异步操作中假设成功,并在等待实际结果的同时更新状态。以下是一个基本的使用示例:
import { useOptimistic } from 'react';function AppContainer() { const [state, setState] = useState([ { id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }, { id: 3, name: 'Item 3' }, ]); const updateFn = (currentState, optimisticId) => { return currentState.filter(item => item.id !== optimisticId); }; const [optimisticState, addOptimistic] = useOptimistic(state, updateFn); const deleteItem = async (itemId) => { // 乐观更新UI addOptimistic(itemId); // 模拟API请求延迟 setTimeout(() => { // 假设这是API删除调用,完成后更新实际状态 setState(currentItems => currentItems.filter(item => item.id !== itemId)); }, 2000); }; return ( <div> <h1>乐观删除项目</h1> <ul> {optimisticState.map(item => ( <li key={item.id}> {item.name} <button onClick={() => deleteItem(item.id)}>删除</button> </li> ))} </ul> </div> );}export default AppContainer;useOptimistic 设置详解
在这个示例中,我们定义了一个基本的数据列表,并使用 useOptimistic Hook 处理项目删除操作。
state: 初始状态,在没有进行任何操作时返回的状态。updateFn(currentState, optimisticValue): 一个纯函数,接收当前状态和传递给 addOptimistic 的乐观值,返回合并后的乐观状态。optimisticState: 乐观状态。如果没有操作进行中,它等于 state;否则,它等于 updateFn 的返回结果。addOptimistic: 一个触发乐观更新的函数,接收一个任意类型的乐观值并传递给 updateFn。二、useActionState 基本用法
在 React 19 中,useActionState 代替了之前的 useFormState,并且返回参数也有所变化。虽然 React 官方文档尚未更新,但我们可以通过示例来了解它的最新用法。
useActionState 提供了一个简化的方式来处理表单操作和异步状态管理。其基本使用方式如下:
const [state, action, pending] = useActionState(fn, initialState, permalink?);返回参数
state: 代表当前状态。首次渲染时,它等于初始状态 initialState。在操作之后,它将是最近一次操作的结果。
action: 用于执行操作的函数。当调用此函数时,它将触发 fn 的执行并更新状态。
pending: 一个新的布尔值参数,表示当前是否有操作正在进行。如果操作正在进行,则为 true;否则为 false。
参数说明
fn: 这是一个函数,在调用 action 时触发,并返回一个新值。
initialState: 这是初始值,如果没有初始值则设置为 null。
permalink: 这是一个可选的字符串参数,通常与服务器操作结合使用。
使用示例:表单提交更新名字
下面是一个使用 useActionState 处理表单提交以更新名字的示例。如果更新失败,页面会显示错误信息;如果成功,页面将重定向到更新后的页面。
import { useActionState } from 'react';function ChangeName({ name, setName }) { const [error, submitAction, isPending] = useActionState( async (previousState, formData) => { const error = await updateName(formData.get("name")); if (error) { return error; } redirect("/path"); }, null ); return ( <form action={submitAction}> <input type="text" name="name" defaultValue={name} /> <button type="submit" disabled={isPending}> Submit </button> {error && <p>{error}</p>} </form> );}在这个例子中,useActionState 被用来创建一个与表单操作相关的状态。具体流程如下:
定义 useActionState,传入表单操作函数和初始状态。表单操作函数接收两个参数:previousState 和 formData。previousState: 上一个状态,初始值为 null,然后是上一次操作的返回值。formData: 表单数据对象,可以使用 formData.get("name") 获取表单字段。执行表单操作函数,更新名字,并根据结果返回错误消息或执行重定向。返回表单及相关状态和操作。三、useFormStatus 简化表单状态管理
React 19 引入的 useFormStatus Hook 为开发者提供了一种简单的方法来检索表单提交的状态信息。下面,我们将详细介绍它的用法和适用场景。
useFormStatus 基本用法
useFormStatus 主要用于获取父 <form> 元素的提交状态信息。其基本使用方式如下:
const { pending, data, method, action } = useFormStatus();返回参数
pending: 一个布尔值,指示父 <form> 是否正在提交。如果正在提交,则为 true;否则为 false。data: 一个实现了 FormData 接口的对象,包含正在提交的父 <form> 的数据。如果没有进行中的提交或没有父 <form>,则为 null。method: 一个字符串值,指示父 <form> 使用的 HTTP 方法,可以是 get 或 post。action: 一个引用父 <form> 的 action 属性中传递的函数。如果没有父 <form>,则此属性为 null。使用示例
下面是一个使用 useFormStatus 来检索表单状态的示例:
import { useFormStatus } from "react-dom";import action from './actions';function Submit() { const status = useFormStatus(); return <button disabled={status.pending}>Submit</button>}export default function App() { return ( <form action={action}> <Submit /> </form> );}在这个例子中,我们定义了一个 Submit 组件,通过 useFormStatus Hook 获取父 <form> 的提交状态,并根据状态来禁用提交按钮。
注意事项
使用 useFormStatus 时需要注意以下两点:
useFormStatus Hook 必须在渲染于 <form> 内部的组件中调用。useFormStatus 只返回父 <form> 的状态信息,而不会返回同一组件中其他 <form> 或其子组件的状态信息。useFormStatus 的优点
useFormStatus 提供了一种更简洁的语法来替代某些上下文提供者的功能,让表单状态管理变得更加直观和简便。这种方法对于处理复杂表单提交逻辑尤为有用,尤其是在涉及异步操作时。
结束由于篇幅有限,本篇文章就介绍到这里。剩下的内容将会在下篇文章中详细介绍,包括:
全新 API — use:从 Hook 到 API 的转变,如何在组件中读取资源的值,特别是在高级框架中的应用。简化的 ref 和 context 使用:如何抛弃 forwardRef,让 refs 作为 props 传递,以及直接使用 <Context> 作为 Provider 的新方法。其他更新:包括 useDeferredValue 的新参数、在 React 代码中编写文档元数据和样式表的支持、预加载资源的新功能等。敬请期待下篇文章的发布,继续了解 React 19 带来的更多惊喜和优化。如果你有任何疑问或想法,欢迎在评论区与我们交流讨论!