React use() Hook 使用指南
概述
use()
是 React 19 引入的新 Hook,它允许你在组件内部直接使用 Promise、Context 和其他可订阅的值。它是一个更通用的数据获取和订阅机制。
基本语法
typescript
const value = use(resource);
主要用途
1. Promise 处理
typescript
function UserDetails({ userId }: { userId: string }) {
const user = use(fetchUser(userId));
// 如果 Promise 还未解决,组件会被挂起
// 一旦 Promise resolved,组件会重新渲染
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
2. Context 使用
typescript
function Button() {
const theme = use(ThemeContext);
// 等同于 useContext(ThemeContext)
return (
<button className={theme.buttonClass}>
Click me
</button>
);
}
3. 条件性使用
typescript
function DataView({ shouldFetch }: { shouldFetch: boolean }) {
if (shouldFetch) {
// use() 可以在条件语句中使用
const data = use(fetchData());
return <div>{data.content}</div>;
}
return <div>Not fetching</div>;
}
高级用法
1. 并行数据获取
typescript
function UserProfile({ userId }: { userId: string }) {
const [user, posts, friends] = use(Promise.all([
fetchUser(userId),
fetchUserPosts(userId),
fetchUserFriends(userId)
]));
return (
<div>
<UserInfo user={user} />
<PostList posts={posts} />
<FriendList friends={friends} />
</div>
);
}
2. 嵌套组件中使用
typescript
function Comments({ postId }: { postId: string }) {
return (
<Suspense fallback={<Spinner />}>
<AsyncComments postId={postId} />
</Suspense>
);
}
function AsyncComments({ postId }: { postId: string }) {
const comments = use(fetchComments(postId));
return (
<ul>
{comments.map(comment => (
<CommentItem key={comment.id} comment={comment} />
))}
</ul>
);
}
3. 自定义资源
typescript
function createResource<T>(promise: Promise<T>) {
let status = 'pending';
let result: T;
let error: Error;
const suspender = promise.then(
(data) => {
status = 'success';
result = data;
},
(err) => {
status = 'error';
error = err;
}
);
return {
read() {
switch (status) {
case 'pending':
throw suspender;
case 'error':
throw error;
case 'success':
return result;
}
}
};
}
function DataComponent() {
const resource = useMemo(() => createResource(fetchData()), []);
const data = use(resource);
return <div>{data}</div>;
}
最佳实践
1. 错误边界处理
typescript
function ErrorBoundary({ children }: { children: React.ReactNode }) {
return (
<Suspense fallback={<Spinner />}>
<ErrorBoundaryInner>
{children}
</ErrorBoundaryInner>
</Suspense>
);
}
function DataComponent() {
return (
<ErrorBoundary>
<AsyncContent />
</ErrorBoundary>
);
}
2. 缓存和预加载
typescript
const cache = new Map();
function fetchWithCache(key: string) {
if (!cache.has(key)) {
cache.set(key, fetchData(key));
}
return cache.get(key);
}
function PreloadedData({ id }: { id: string }) {
// 预加载数据
const data = use(fetchWithCache(id));
return <div>{data}</div>;
}
注意事项
-
使用限制:
- 只能在组件内部使用
- 需要配合 Suspense 使用
- 不能在事件处理器中使用
-
性能考虑:
- 合理使用缓存机制
- 避免重复创建资源
- 考虑数据预加载
-
错误处理:
- 使用错误边界捕获异常
- 提供合适的加载状态
- 实现优雅的降级处理
总结
-
use() 的优势:
- 简化异步数据获取
- 支持条件性使用
- 更好的类型推断
- 统一的资源使用方式
-
适用场景:
- 数据获取
- Context 消费
- 自定义订阅
- 并行数据加载
-
使用建议:
- 配合 Suspense 使用
- 实现适当的错误处理
- 注意性能优化
- 合理组织代码结构