关于 react19-patterns
面向启用 React Compiler 的 React 19 项目的编码技能,介绍 Context 与 `use()` 写法、由编译器处理优化,以及 ViewTransition 与 Suspense 的边界配置。
主要功能
- 01React 19 Context provider 语法
- 02使用 `use()` 替代 `useContext()`
- 03依靠编译器执行记忆化
- 04避免不必要的手动优化
- 05协调 ViewTransition 与 Suspense
适用场景
- 01在 React 19 组件中使用 Context
- 02移除多余的手动记忆化
- 03通过 Suspense 避免水合不匹配
- 04先分析性能再添加手动优化
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ qingqishi/ shiqingqi.com --skill react19-patterns安装后在 Claude Code 中输入/react19-patterns确认已加载。
详细介绍
React 19 模式
概述
本项目使用启用了 React Compiler 的 React 19。这会改变 React 代码的编写方式,尤其是 Context 和优化方面。
React 19 Context 模式
使用简写语法
React 19 为 Context provider 引入了简写语法。
✅ 正确(React 19):
<MyContext value={someValue}>
<ChildComponents />
</MyContext>
❌ 错误(旧模式):
<MyContext.Provider value={someValue}>
<ChildComponents />
</MyContext.Provider>
使用 use() Hook,而不是 useContext()
React 19 引入了用于使用 context 的 use() Hook。
✅ 正确(React 19):
import { use } from "react";
import { MyContext } from "./MyContext";
function MyComponent() {
const value = use(MyContext);
return <div>{value}</div>;
}
❌ 错误(旧模式):
import { useContext } from "react";
import { MyContext } from "./MyContext";
function MyComponent() {
const value = useContext(MyContext);
return <div>{value}</div>;
}
已启用 React Compiler
无需手动记忆化
React Compiler 会自动优化组件并处理记忆化。不要使用手动记忆化模式。
✅ 正确(由 React Compiler 处理):
function MyComponent({ items }) {
// React Compiler automatically memoizes this computation
const filteredItems = items.filter((item) => item.active);
// React Compiler automatically stabilizes this function reference
const handleClick = (id) => {
console.log(id);
};
return (
<div>
{filteredItems.map((item) => (
<button key={item.id} onClick={() => handleClick(item.id)}>
{item.name}
</button>
))}
</div>
);
}
❌ 错误(无需手动记忆化):
import { useMemo, useCallback, memo } from "react";
function MyComponent({ items }) {
// ❌ Don't use useMemo - React Compiler handles this
const filteredItems = useMemo(
() => items.filter((item) => item.active),
[items],
);
// ❌ Don't use useCallback - React Compiler handles this
const handleClick = useCallback((id) => {
console.log(id);
}, []);
return <div>...</div>;
}
// ❌ Don't use memo() - React Compiler handles this
export default memo(MyComponent);
React 19 ViewTransition + Suspense 模式
水合问题
使用 <ViewTransition> 包装包含 Suspense 边界的内容时,如果部分子项位于 Suspense 中,而其他子项不在其中,可能会遇到水合错误。
错误消息:
A tree hydrated but some attributes of the server rendered HTML didn't match the client properties.
This won't be patched up. This can happen if a SSR-ed Client Component used...
Specifically: style={{view-transition-name:"_T_0_"}}
根本原因:
- ViewTransition 使用“即时”机制,仅在触发转换时应用
view-transition-name样式 - SSR 水合期间,非 Suspense 子项中的内容显示会触发 ViewTransition 激活
- 这会导致客户端在水合期间应用服务器 HTML 中不存在的样式
- React 检测到不匹配,并记录水合警告
解决方案:保持 Suspense 边界一致
✅ 正确(所有内容都放在 Suspense 中):
<ViewTransition>
<Suspense fallback={<HeaderSkeleton />}>
<Header />
</Suspense>
<Suspense fallback={null}>{children}</Suspense>
</ViewTransition>
❌ 错误(混合使用 Suspense 和非 Suspense):
<ViewTransition>
<Suspense fallback={<HeaderSkeleton />}>
<Header />
</Suspense>
{children} {/* NOT in Suspense - causes hydration error! */}
</ViewTransition>
替代方案(将 Suspense 放在 ViewTransition 外部):
<Suspense fallback={<LoadingSkeleton />}>
<ViewTransition>
<Header />
{children}
</ViewTransition>
</Suspense>
注意:如果 Header 和子项需要独立加载,此替代方案会强制所有内容进入相同的加载状态,可能并不理想。
此修复的原理
将所有子项都包装在 Suspense 边界中:
- 内容显示会通过 React 的 Suspense 机制进行协调
- ViewTransition 不会在水合期间过早激活
- 服务器端和客户端渲染保持一致
- 不会发生水合不匹配
关键规则
- Context 简写:始终使用
<Context value={...}>,而不是<Context.Provider value={...}> - use() Hook:始终使用
use(Context),而不是useContext(Context) - 不使用 useMemo:React Compiler 会自动记忆化开销较大的计算
- 不使用 useCallback:React Compiler 会自动稳定函数引用
- 不使用 memo():React Compiler 会自动优化组件重新渲染
- 信任编译器:让 React Compiler 处理优化,而不是使用手动模式
- ViewTransition + Suspense:将 ViewTransition 与 Suspense 搭配使用时,确保所有子项都位于 Suspense 边界内,以避免水合错误
可能需要手动优化的情况
在少数情况下,你可能仍需要手动优化:
- 集成需要稳定引用的外部库
- 性能分析显示存在 React Compiler 无法发现的特定问题
添加手动优化之前,务必先进行性能分析。React Compiler 非常有效。