React 19 引入了许多新特性和改进,旨在简化开发流程、提升性能,并增强与现代开发需求的兼容性。以下是对 React 19 新特性的详细解读:
1. useTransition
useTransition 是一个 Hook,用于在并发模式下管理过渡状态(transition state)。它允许你将状态更新标记为低优先级,从而优化用户体验,避免页面卡顿或延迟。
示例
import { useState, useTransition } from 'react';
function App() {
const [isPending, startTransition] = useTransition();
const [inputValue, setInputValue] = useState('');
const [list, setList] = useState([]);
const handleInputChange = (e) => {
const value = e.target.value;
setInputValue(value); // 高优先级更新,立即执行
// 使用 startTransition 将低优先级更新推迟
startTransition(() => {
const newList = [];
for (let i = 0; i < 20000; i++) {
newList.push(value);
}
setList(newList); // 低优先级更新,推迟执行
});
};
return (
<div>
<input
type="text"
value={inputValue}
onChange={handleInputChange}
placeholder="Type something..."
/>
{isPending ? (
<p>Loading...</p>
) : (
<ul>
{list.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
)}
</div>
);
}
2. React Compiler
React Compiler 是 React 19 中引入的一个重要特性,旨在通过自动优化 React 应用的渲染性能,减少开发者手动优化的工作量。
核心功能
- 自动记忆化:React Compiler 会自动将
React.memo、useMemo和useCallback等进行记忆化,避免不必要的重新渲染。 - 细粒度优化:编译器能够识别哪些部分的代码是静态的(即不会随状态变化而变化),并将其缓存起来。
- 回退机制:如果遇到无法优化的代码(例如非最佳实践或复杂逻辑),React Compiler 会回退到原始的转换器,确保代码的稳定性和正确性。
3. Form Action
formAction 是 React 19 中引入的一个新特性,用于增强表单处理的能力。它允许开发者在表单提交时执行自定义操作,例如异步数据提交、状态更新或导航。
示例
function MyForm() {
const handleSubmit = async (formData) => {
const response = await fetch('/api/submit', {
method: 'POST',
body: formData,
});
const result = await response.json();
console.log(result);
};
return (
<form action={handleSubmit}>
<input type="text" name="username" />
<button type="submit">Submit</button>
</form>
);
}
4. useActionState
useActionState 是一个新的 Hook,用于管理表单提交时的状态。它结合了 useState 和异步操作的能力,能够根据表单提交的结果更新状态,并自动处理加载状态(isPending)。
示例
import { useActionState } from "react";
async function submitForm(previousState, formData) {
const name = formData.get("name");
if (!name) {
return { error: "Name is required" };
}
return { message: `Hello, ${name}!` };
}
function NameForm() {
const [state, formAction, isPending] = useActionState(submitForm, null);
return (
<form action={formAction}>
<input type="text" name="name" />
<button type="submit" disabled={isPending}>
{isPending ? "Submitting..." : "Submit"}
</button>
{state?.error && <p style={{ color: "red" }}>{state.error}</p>}
{state?.message && <p>{state.message}</p>}
</form>
);
}
5. useOptimistic
useOptimistic 是一个新的 Hook,用于实现乐观更新(Optimistic Update)。它允许在异步操作完成之前立即更新 UI,从而提升用户体验。
示例
import { useOptimistic, useState } from "react";
function LikeButton({ initialLikes }) {
const [likes, setLikes] = useState(initialLikes);
const [optimisticLikes, setOptimisticLikes] = useOptimistic(likes, (state, newLike) => state + 1);
const handleClick = async () => {
setOptimisticLikes(1); // 立即更新 UI
await likePost(); // 发送请求
setLikes((prev) => prev + 1); // 更新实际状态
};
return (
<button onClick={handleClick}>
{optimisticLikes} Likes
</button>
);
}
6. useFormStatus
useFormStatus 是一个新的 Hook,用于在表单提交期间获取表单的状态信息,例如表单是否正在提交(pending 状态)。
示例
import { useFormStatus } from "react-dom";
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? "Submitting..." : "Submit"}
</button>
);
}
function MyForm() {
return (
<form action="/submit">
<input type="text" name="username" />
<SubmitButton />
</form>
);
}
7. Refs as Props
在 React 19 中,ref 可以直接作为函数组件的 props 传递,而不再需要通过 forwardRef 来转发。这一变化使得 ref 的使用更加直观和简洁。
示例
function MyInput({ ref, ...props }) {
return <input ref={ref} {...props} />;
}
function App() {
const inputRef = useRef(null);
return <MyInput ref={inputRef} />;
}
8. use
use 是一个新的 Hook,用于在渲染期间读取资源(如 Promise 或 Context)。它允许条件调用,并与 Suspense 结合使用。
示例
import { use } from "react";
function Comments({ commentsPromise }) {
const comments = use(commentsPromise);
return comments.map(comment => <p key={comment.id}>{comment}</p>);
}
9. Document Metadata
React 19 原生支持 <title>、<meta> 和 <link> 等文档元数据标签。这些标签可直接在组件中声明,React 会自动将它们提升至 <head>,并确保与服务端渲染和客户端渲染兼容。
示例
function BlogPost({ post }) {
return (
<article>
<h1>{post.title}</h1>
<title>{post.title}</title>
<meta name="author" content={post.author} />
<meta name="keywords" content={post.keywords} />
<link rel="canonical" href={post.canonicalUrl} />
<p>{post.content}</p>
</article>
);
}
10. useEffectEvent详解:React 副作用管理的革命性工具
useEffectEvent是 React 团队为解决 useEffect中长期存在的闭包陷阱和依赖数组管理难题而设计的官方解决方案。它让你能够将"读取最新值但不应该触发 Effect 重新执行"的逻辑与"真正需要响应变化的逻辑"分离开来。
核心概念
什么是 Effect Event?
Effect Event(效应事件)是指那些:
- 需要访问最新 props 或 state 的逻辑
- 但这些逻辑本身不应该成为
Effect 的依赖项,造成 effect 重复执行(如重复建立连接、重复订阅等) - 通常用于事件处理、回调执行、异步操作等场景
基本语法
const eventHandler = useEffectEvent((...args) => {
// 可以访问最新的状态和props
// 但不会触发Effect重新执行
});
工作原理
1. 闭包问题的传统解决方案对比
真正存在闭包陷阱的情况(忘记添加依赖):
function ChatRoom({ roomId, currentUser }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const connection = createConnection(roomId);
connection.onMessage((message) => {
// ❌ 真正的闭包陷阱:currentUser 不在依赖数组中
// 这里永远显示第一次渲染时的 currentUser
showNotification(`${currentUser.name}: ${message.text}`);
});
connection.connect();
return () => connection.disconnect();
}, [roomId]); // ❌ 忘记添加 currentUser 依赖, 目的减少不必要的重复建立链接
});
传统解决方案(useRef):
function ChatRoom({ roomId, currentUser }) {
const [messages, setMessages] = useState([]);
const currentUserRef = useRef(currentUser);
// 需要手动同步 ref
useEffect(() => {
currentUserRef.current = currentUser;
});
useEffect(() => {
const connection = createConnection(roomId);
connection.onMessage((message) => {
// ✅ 通过 ref 访问最新值
showNotification(`${currentUserRef.current.name}: ${message.text}`);
});
connection.connect();
return () => connection.disconnect();
}, [roomId]); // 依赖更简洁
});
useEffectEvent 解决方案:
function ChatRoom({ roomId, currentUser }) {
const [messages, setMessages] = useState([]);
// ✅ 使用 useEffectEvent 自动处理最新值
const onMessage = useEffectEvent((message) => {
showNotification(`${currentUser.name}: ${message.text}`);
});
useEffect(() => {
const connection = createConnection(roomId);
connection.onMessage(onMessage); // 总是能访问最新的 currentUser
connection.connect();
return () => connection.disconnect();
}, [roomId]); // 依赖简洁明了
});
2. 执行时机和机制
useEffectEvent的执行时机与普通的函数声明类似,但其内部对响应式值的访问具有特殊行为:
const [count, setCount] = useState(0);
const [text, setText] = useState('');
const handleEvent = useEffectEvent(() => {
console.log(count, text); // 总是访问最新的值
});
// 组件渲染时:handleEvent 函数被创建
// 调用 handleEvent() 时:自动捕获当前最新的 count 和 text
详细使用场景
场景1:表单自动保存
传统方案的问题:
function DocumentEditor() {
const [document, setDocument] = useState({ title: '', content: '' });
useEffect(() => {
// ❌ 依赖 document,导致频繁保存
const timer = setTimeout(() => {
saveDocument(document);
}, 1000);
return () => clearTimeout(timer);
}, [document]); // document 变化就重启定时器
}
useEffectEvent 解决方案:
function DocumentEditor() {
const [document, setDocument] = useState({ title: '', content: '' });
// ✅ 自动保存逻辑能访问最新 document,但不是 Effect 的依赖
const handleAutoSave = useEffectEvent(() => {
saveDocument(document);
});
useEffect(() => {
// 只管理定时器逻辑
const timer = setTimeout(handleAutoSave, 1000);
return () => clearTimeout(timer);
}, []); // 空依赖,定时器只设置一次
}
总结
React 19 通过引入 useTransition、React Compiler、formAction、useActionState、useOptimistic、useFormStatus、use 和 Document Metadata 等新特性,显著提升了开发体验和应用性能。这些新特性使得 React 开发更加高效和易用,同时也提供了更多的功能和灵活性。