React 19 核心特征
React 19 核心特征
Action 表单交互的革命
使用<form> 的 action 属性简化数据提交
在 React 19 中,我们可以直接将一个函数(即 Action)传递给原生的<form> 元素的action属性,当你提交这个表单的时候,React 会自动拦截提交事件,处理表单数据的序列化(FormData), 并调用你提供的 Action 函数,这意味着,我们可以告别onSubmit事件处理器和preventDefault()。
const OldForm = () => {
const handleSubmit = (e) => {
e.preventDefault();
const formData = new FormData(e.target);
// 手动处理表单数据
};
return (
<form onSubmit={handleSubmit}>
<input type="text" name="username"/>
<input type="password" name="password"/>
<button type="submit">提交</button>
</form>
);
};
// react 19中的新方式
const NewForm = () => {
const handleSubmit = async (formData) => {
// 自动处理表单数据
};
return (
<form action={handleSubmit}>
<input type="text" name="username"/>
<input type="password" name="password"/>
<button type="submit">提交</button>
</form>
);
};使用 useActionState 处理 Pending/Error/Success 状态
Action 的真正威力在于它内置了对异步流程状态的管理能力。useActionState 是专门为此设计的。它接收一个 Action 函数和初始状态,然后返回了一个包含当前状态、一个可被调用的新 Action 以及一个 pedding 状态的数组。
import {useActionState} from "react";
const AddToCartForm = async (previousState, formData) => {
const quantity = formData.get("quantity");
const result = await addToCart({
productId: formData.get("productId"),
quantity: Number(quantity),
});
if (result.success) {
return {success: true};
}
};使用 useFormState 优化用户体验
useActionState 管理整个表单的状态,但有时我们希望表单内的某个子组件(比如提交按钮)能够独立的响应表单的提交状态,而无需通过 props 逐层传递 isPending。useFormStatus Hook 正事为了解决这个问题而生。
它只能在 form 组件的字组件中使用,并且会返回器所在表单的当前状态信息,包括 pending,data method 等
import {useFormStatus} from "react";
const SubmitButton = () => {
const {pending} = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? "提交中" : "提交"}
</button>
);
};
const MyForm = () => {
const submitAction = async (formData) => {
const result = await AddToCartForm(undefined, formData);
if (result.success) {
return {success: true};
}
};
return (
<form action={submitAction}>
<input type="text" name="productId"/>
<input type="number" name="quantity"/>
<SubmitButton/>
</form>
);
};通过 useFormStatus,我们创建了一个高度解耦且可复用的提交按钮组件,它可以独立于表单组件工作,同时又能响应表单的提交状态。
并发与 use Hook
并发是 React 近年来最重要的底层升级,他允许 React 在渲染过程中处理多个状态更新,并根据优先级终端盒恢复渲染任务。在 React19 中,并发特性通过一个全新的、强大的 use Hook 得到了最直观的体现。
use Hook 可以在渲染中读取 Promise 和 Context
use Hook 是一个可以在渲染期间解包数据源的 Hook。目前它支持两种数据源:Promise 和 Context
与其他 Hooks 不同,use 可以在条件语句、循环或普通函数中调用,这赋予了它前所未有的灵活性。
当 use 被用于一个 Promise 时,他会做一件神奇的事情:
- 如果 Promise 正在 pending 他会抛出这个 Promise
- 这个抛出的行为会被最近的
<Suspense>组件捕获,并显示 fallback UI - 当 Promise resolve 后,React 会重新尝试渲染该组件,此时 use Hook 会返回 Promise 的结果值
- 如果 Promise reject,则错误会被最近的 ErrorBoundary 组件捕获。
结合 Suspense 实现优雅的数据加载 UI
use 和 Suspense 的结合是 React 官方推荐的、用于在客户端获取数据的方式,它彻底改变了 Fetch-on-render 的模式
import {Suspense, use} from "react";
import {ErrorBoundary} from "react-error-boundary";
const fetchMessage = () => {
return new Promise((resolve) =>
setTimeout(() => resolve("Hello, world!"), 1000)
);
};
const Message = ({messagePromise}) => {
const message = use(messagePromise);
return (
<div>
<p>{message}</p>
</div>
);
};
const App = () => {
const messagePromise = fetchMessage();
return (
<ErrorBoundary fallbackRender={() => <p>Error</p>}>
<Suspense fallback={<p>Loading...</p>}>
<Message messagePromise={messagePromise}/>
</Suspense>
</ErrorBoundary>
);
};其他特性
useOptimistic: 实现乐观更新, 提升交互体验
useOptimistic 是一个用于实现乐观更新的 Hook。它可以在用户触发一个操作后,立即更新 UI 以展示操作的结果,而无需等待服务器响应。这在一些需要快速反馈的场景(如实时聊天、评论系统等)中非常有用。
import {useOptimistic} from "react";
const AddToCartForm = async (previousState, formData) => {
const quantity = formData.get("quantity");
const result = await addToCart({
productId: formData.get("productId"),
quantity: Number(quantity),
});
if (result.success) {
return {success: true};
}
};Asset Loading: 优化资源加载,提升用户体验
Asset Loading 是 React 19 中引入的一个新特性,它允许我们在组件渲染时,异步加载外部资源(如图片、字体等)。这在一些需要延迟加载的场景(如图片懒加载、字体预加载等)中非常有用。
import {useAsset} from "react";
const MyComponent = () => {
const image = useAsset("https://example.com/image.jpg");
return (
<div>
<img src={image} alt="Example"/>
</div>
);
};ref 作为 Prop: 简化 forwardRef
forwardRef 是 React 中用于将 ref 从父组件转发到子组件内部 Dom 节点的 API ,但它的写法相对冗长和不直观。在 19 中,这个过程被大大简化了,现在 ref 可以像普通的 prop 一样传递给函数式组件,无需在用 forwardRef 进行包装