React 19 `use()` 来了:以后数据加载可以不用 useEffect?

问题场景

假设你有一个普通的 React 组件,要从 API 加载用户数据:

tsx 复制代码
function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setLoading(true);
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(setUser)
      .finally(() => setLoading(false));
  }, [userId]);

  if (loading) return <Skeleton />;
  return <div>{user.name}</div>;
}

这段代码写了 15 行,就为干一件事:等数据回来再渲染。而且你还要处理:

  • 竞态(后发的请求覆盖了先发的)
  • 错误边界
  • 内存泄漏(卸载后还在 setState)

这些问题在过去几年催生了 React Query、SWR 等库。但 React 19 说:这活我能干了。

原因分析

React 团队在 19 版本引入了一个全新的 API------use()。它不是 Hooks,不能在条件/循环外的那套规则限制它(虽然实际上最好也别那么干)。它的签名是:

ts 复制代码
function use<T>(promise: Promise<T>): T;
function use<T>(context: Context<T>): T;

use() 做了什么?

  • 传入一个 Promise → 挂起组件 直到 resolve,配合 <Suspense> 展示 fallback
  • 传入一个 Context → 等价于 useContext,但可以在条件语句和循环里用

关键区别:use() 在渲染期间直接消费 Promise,React 会帮你处理"等待→重渲染"的流程,不再需要 useEffect + useState 那套样板代码。

解决方案

1. 用 use() 替代 useEffect 数据加载

先写一个获取 Promise 的工具:

tsx 复制代码
// fetchUser.ts
let cache = new Map<string, Promise<User>>();

export function fetchUser(id: string): Promise<User> {
  if (!cache.has(id)) {
    cache.set(id, fetch(`/api/users/${id}`).then(r => r.json()));
  }
  return cache.get(id)!;
}

然后在组件里直接用 use()

tsx 复制代码
import { use, Suspense } from 'react';

function UserProfile({ userId }: { userId: string }) {
  const user = use(fetchUser(userId)); // 👈 直接等
  return <div>{user.name} --- {user.email}</div>;
}

// 父组件包 Suspense
function Page({ userId }: { userId: string }) {
  return (
    <Suspense fallback={<Skeleton />}>
      <UserProfile userId={userId} />
    </Suspense>
  );
}

15 行 → 3 行 。竞态问题?React Suspense 天然解决------每次渲染都 use(fetchUser(userId)),如果 userId 变了,老的 Promise 被丢弃,新的挂起,不存在覆盖问题。

2. 那缓存怎么办?

上面的 fetchUser 用了最简的 Map 缓存。生产环境建议配合 React 19 的 cache()

ts 复制代码
import { cache } from 'react';

export const fetchUser = cache(async (id: string) => {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
});

cache() 保证同样的参数只调用一次函数,结果自动 dedupe。Server Components 里用这个极其丝滑。

3. use(context) --- 条件中使用 Context

过去你不能在 if 里用 useContext,但用 use() 可以:

tsx 复制代码
function Sidebar({ showTheme }: { showTheme: boolean }) {
  // ✅ 条件中读 Context
  const theme = showTheme ? use(ThemeContext) : 'light';

  // ✅ 循环中读 Context
  return items.map(item => {
    const config = use(ConfigContext);
    return <Item key={item.id} config={config} />;
  });
}

这对高阶组件、渲染函数场景非常实用。

要点总结

对比 传统写法 (useEffect) use() + Suspense
代码量 12--20 行 3--5 行
竞态处理 手动 cleanup 天然解决
加载状态 手动 state Suspense fallback
错误处理 手动 try/catch + state ErrorBoundary
条件中读 Context ❌ 不行 ✅ 可以

⚠️ 注意:

  • use() 还在 Experimental,可以用 React 19 RC 尝鲜。生产环境等正式版再上。
  • use(promise) 必须在 <Suspense> 内部才能挂起,没有 Suspense 兜底会抛错。
  • 客户端组件中 use() + cache() 需要搭配 React 19 的数据获取方案,目前 Server Components 体验最成熟。
  • use() 不是什么都能替代------事件处理、定时器、副作用逻辑还是得用 useEffect

一句话总结: 如果你的组件只做一件事------"等数据,然后渲染",use() 是过去 5 年 React 数据加载最优雅的解法。再也不用为了一个 fetch 写一整套生命周期体操了。

相关推荐
ℋᙚᵐⁱᒻᵉ鲸落5 小时前
移动端滑动手势冲突:overflow: auto导致外层横向滑动失效
前端·javascript·css·vue.js·html
默_笙6 小时前
🎃 前端学了 Next.js,后端该学啥?NestJS 就是 Node 版的蜜雪冰城
前端·javascript
前端snow7 小时前
ai agent --- 实现 openclaw 定时间效果
前端
码云之上7 小时前
换模型之后,Chatbot 为什么要自己做 compact?
前端·agent·前端工程化
星栈7 小时前
自定义 UI-UX-Pro-Max 的 CSV 知识库,把 AI 生成的后台拉回行业该有的样子
前端·agent·weui
研☆香8 小时前
简单的图片上传 删除 预览
前端
ITresearchGuest8 小时前
我用 AI 一小时写了一个世界杯数据可视化平台|前端 VibeCoding 初体验
前端·人工智能·信息可视化
慧一居士9 小时前
Vite项目中使用Less步骤,详细使用示例
前端·css
花间相见9 小时前
【LangChain组件03】—— LangChain Agents 执行与状态:工作流程与状态管理实战
java·前端·langchain
kyriewen10 小时前
面试官让我用 AI 重构一个 8 年陈的 React 组件——他说他不看代码,只看我会不会拆
前端·javascript·面试