React 19 新特性总结
具体详见官网:
中文:React 19 新特性
英文:React 19 新特性
核心新特性
1. Actions
解决问题:简化数据变更和状态更新流程
- 以前需要手动处理待定状态、错误、乐观更新和顺序请求
- 需要维护多个状态变量(isPending, error 等)
新特性:
function UpdateName() {
const [state, submitAction, isPending] = useActionState(
async (prevState, formData) => {
const error = await updateName(formData.get("name"));
if (error) return error;
redirect("/path");
return null;
},
null
);
return (
<form action={submitAction}>
<input name="name" />
<button disabled={isPending}>Update</button>
{state?.error && <p>{state.error}</p>}
</form>
);
}
主要改进:
- 自动处理待定状态
- 内置错误处理
- 支持乐观更新
- 简化表单处理
2. useFormStatus
解决问题:简化表单组件状态访问
- 避免通过 props 传递表单状态
- 提供统一的表单状态访问方式
function SubmitButton() {
const { pending, data, method } = useFormStatus();
return (
<button disabled={pending}>
{pending ? 'Submitting...' : 'Submit'}
</button>
);
}
3. useOptimistic
解决问题:提供更好的用户体验
- 立即显示操作结果
- 处理异步操作的状态更新
function LikeButton({ id }) {
const [likes, setLikes] = useState(0);
const [optimisticLikes, addOptimisticLike] = useOptimistic(
likes,
(state, increment) => state + increment
);
async function handleLike() {
addOptimisticLike(1); // 立即更新 UI
await updateLikes(id); // 后台进行实际更新
}
}
4. use() Hook
解决问题:统一资源使用方式
- 简化 Promise 和 Context 的使用
- 支持条件性使用
- 提供更好的类型推断
function Comments({ commentsPromise }) {
const comments = use(commentsPromise); // 自动处理 Suspense
return comments.map(comment => <p>{comment}</p>);
}
架构改进
1. Document 流式渲染
解决问题:改善首次加载体验
- 支持 HTML 流式传输
- 优化资源加载顺序
function AsyncPage() {
return (
<Document>
<Suspense fallback={<Loading />}>
<AsyncContent />
</Suspense>
</Document>
);
}
2. 资源处理优化
样式表支持
解决问题:简化样式管理
- 自动处理样式表加载顺序
- 支持组件级样式声明
function Component() {
return (
<>
<link rel="stylesheet" href="styles.css" precedence="default" />
<div className="styled-content">...</div>
</>
);
}
异步脚本支持
解决问题:优化脚本加载
- 自动处理脚本去重
- 优化加载优先级
function MyComponent() {
return (
<div>
<script async={true} src="widget.js" />
<div>Widget Content</div>
</div>
);
}
开发体验改进
1. 错误处理增强
解决问题:提供更清晰的错误信息
- 消除重复错误日志
- 提供更详细的错误上下文
createRoot(container, {
onCaughtError: (error) => {
// 错误边界捕获的错误
},
onUncaughtError: (error) => {
// 未被捕获的错误
},
onRecoverableError: (error) => {
// 可恢复的错误
}
});
2. 自定义元素支持
解决问题:改善与 Web Components 的集成
- 完整支持自定义元素
- 正确处理属性和属性传递
最佳实践建议
-
渐进式采用
- 优先使用新的表单处理方式
- 在关键交互中使用乐观更新
- 利用新的资源加载优化
-
性能优化
- 使用流式渲染改善加载体验
- 合理使用资源预加载
- 优化并发更新
-
错误处理
- 使用新的错误边界
- 实现适当的降级策略
- 监控错误模式
服务器组件
1. 服务器组件基础
解决问题:优化应用性能和开发体验
- 减少客户端 bundle 大小
- 直接访问后端资源
- 改善数据获取模式
// 服务器组件
async function Notes() {
// 直接访问数据库,无需 API 层
const notes = await db.notes.getAll();
return (
<div>
{notes.map(note => (
<Expandable key={note.id}>
<p>{note.content}</p>
</Expandable>
))}
</div>
);
}
2. 服务器组件与客户端组件集成
解决问题:平滑处理服务器和客户端组件交互
- 支持渐进式增强
- 保持交互性
- 优化数据流
// 服务器组件
import Expandable from './Expandable'; // 客户端组件
async function NotesContainer() {
const notes = await db.notes.getAll();
return (
<div>
{/* 服务器组件可以渲染客户端组件 */}
<Expandable>
<NotesList notes={notes} />
</Expandable>
</div>
);
}
// 客户端组件
'use client'
function Expandable({ children }) {
const [expanded, setExpanded] = useState(false);
return (
<div>
<button onClick={() => setExpanded(!expanded)}>
{expanded ? 'Collapse' : 'Expand'}
</button>
{expanded && children}
</div>
);
}
3. 异步组件
解决问题:简化异步数据处理
- 支持 async/await 语法
- 自动处理 Suspense 集成
- 优化加载状态
// 服务器组件中的异步数据获取
async function Page({ id }) {
const note = await db.notes.get(id);
// 开始获取评论但不等待
const commentsPromise = db.comments.get(id);
return (
<div>
<h1>{note.title}</h1>
<Suspense fallback={<Loading />}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
</div>
);
}
Refs 作为 Props
1. 将 ref 作为 prop
从 React 19 开始,你现在可以在函数组件中将 ref 作为 prop 进行访问:
function MyInput({placeholder, ref}) {
return <input placeholder={placeholder} ref={ref} />
}
//...
<MyInput ref={ref} />
新的函数组件将不再需要 forwardRef,我们将发布一个 codemod 来自动更新你的组件以使用新的 ref prop。在未来的版本中,我们将弃用并移除 forwardRef。
2. Ref 稳定性改进
解决问题:优化 ref 更新和同步
- 更可预测的 ref 更新时机
- 更好的并发模式支持
- 改进的性能特性
function AutoFocusInput() {
const inputRef = useRef<HTMLInputElement>(null);
// ref 回调模式的改进
const setRef = useCallback((element: HTMLInputElement | null) => {
if (element) {
element.focus();
}
}, []);
return <input ref={setRef} />;
}
服务器组件最佳实践
-
数据获取策略
- 在服务器组件中直接访问数据源
- 使用流式传输处理大量数据
- 实现适当的缓存策略
-
组件分割
- 将有状态逻辑的组件标记为客户端组件
- 保持服务器组件纯粹
- 优化组件边界
-
性能优化
- 使用服务器组件减少客户端 bundle
- 实现渐进式加载
- 优化数据预取
// 示例:优化的服务器组件结构
async function BlogPost({ id }) {
const post = await db.posts.get(id);
const authorPromise = db.authors.get(post.authorId);
const commentsPromise = db.comments.get(id);
return (
<article>
<h1>{post.title}</h1>
<Suspense fallback={<AuthorSkeleton />}>
<Author authorPromise={authorPromise} />
</Suspense>
<Content>{post.content}</Content>
<Suspense fallback={<CommentsSkeleton />}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
</article>
);
}
注意事项
-
服务器组件限制
- 不能使用浏览器 API
- 不能使用状态和生命周期
- 需要正确处理数据获取错误
-
Ref 使用考虑
- 注意 ref 的生命周期
- 合理处理 ref 清理
- 避免过度依赖 ref
-
性能注意事项
- 平衡服务器和客户端渲染
- 合理使用 Suspense 边界
- 优化数据预加载策略