React 用 Suspense 与 use() 做组件级数据请求:把异步资源写在组件里
test12026-10-200 次阅读
为什么把数据请求写进组件
过去的习惯是把请求放在父组件的 useEffect 里,再用 props 往下传。问题是加载状态散落在各处,子组件想单独取数就得一层层 prop drilling。React 19 的 use() 配合 Suspense,让组件自己声明"我需要这个资源",由框架在渲染时挂起、在最近一层 Suspense 显示占位。
用 use() 读取 Promise 资源
use() 可以在渲染期间直接读取一个 Promise,挂起时把它交给外层 Suspense。关键是 Promise 必须稳定(同一参数复用同一个实例),否则每次渲染都重新请求。
import { use, Suspense } from "react";
const cache = new Map();
function fetchUser(id) {
let p = cache.get(id);
if (!p) {
p = fetch("/api/users/" + id).then((r) => r.json());
cache.set(id, p);
}
return p;
}
function UserCard({ id }) {
const user = use(fetchUser(id)); // 渲染里直接读取 Promise
return <h2>{user.name}</h2>;
}
function Page() {
return (
<Suspense fallback={<p>加载中…</p>}>
<UserCard id="42" />
</Suspense>
);
}
错误边界兜底
Suspense 只管"加载中",请求失败要靠错误边界。use() 抛出的 Promise 如果被 reject,最近的 ErrorBoundary 会接住。
import { Component } from "react";
class ErrorBoundary extends Component {
state = { error: null };
static getDerivedStateFromError(error) {
return { error };
}
render() {
if (this.state.error) {
return <p>加载失败:{String(this.state.error)}</p>;
}
return this.props.children;
}
}
预取消除闪烁
在用户点进去之前(比如 hover 列表项)就发起请求,等真正渲染时缓存已就绪,Suspense 几乎不闪占位。
function UserList({ ids }) {
return (
<ul>
{ids.map((id) => (
<li key={id} onMouseEnter={() => fetchUser(id)}>
<a href={"/users/" + id}>用户 {id}</a>
</li>
))}
</ul>
);
}
它和 TanStack Query 的关系
use() 加 Suspense 是框架内建的轻量方案,适合"组件自取数、就近挂起"的场景;跨组件缓存、失效策略、轮询等仍是 TanStack Query 的强项。两者不冲突,可以在 Query 外层再包 Suspense。
小结
- use() 让组件在渲染期直接读取 Promise,由 Suspense 接住加载态。
- 资源必须按参数缓存,避免每次渲染重复请求。
- 失败交给 ErrorBoundary,加载交给 Suspense,职责清晰。
- 预取能把占位闪烁压到最低。
T
test1
文章作者
为什么把数据请求写进组件 过去的习惯是把请求放在父组件的 useEffect 里,再用 props 往下传。问题是加载...
- 分类
- 技术
- 发布时间
- 2026-10-20
- 字数
- 约 1788 字
- 阅读
- 0 次
