Prompts-MCP 686 skills · ai / design / design-pattern / framework / fundamentals / habit / lang / tech-selection /mcp/sse
framework framework/react/hook/no-fetch-in-use-effect.md medium

react-no-fetch-in-use-effect

禁在 useEffect 拉数据 — 用 TanStack Query;useEffect 仅做 DOM 同步/订阅。Use when 写 React 组件 / 改 .tsx 文件 / 评审涉及 `no-fetch-in-use-effect` 的 PR。

useeffectfetchusequery拉数据仅做同步
paths
  • frontend/src/**/*.tsx
  • frontend/src/**/*.ts

React · 禁在 useEffect 拉数据

规则

useEffect 不能用来拉服务端数据。所有 API 请求一律走 TanStack Query 的 useQuery / useMutation

反例 → 正例

// ❌ useEffect + fetch
function TextbookList() {
  const [data, setData] = useState<Textbook[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    setLoading(true);
    fetch("/api/textbooks")
      .then(r => r.json())
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, []);
  // 缺:缓存、去重、retry、cancel、stale 标记
}

// ✅ useQuery
function TextbookList() {
  const { data, isLoading, error } = useQuery({
    queryKey: ["textbooks"],
    queryFn: () => textbooksApi.list(),
    staleTime: 5 * 60 * 1000,
  });
  // useQuery 自动处理:缓存 / 去重 / retry / cancel / stale / focus refetch
}

useEffect 仅用于这些场景

用途 例子
DOM 同步 操作 ref / 第三方 lib 初始化
订阅 / 取消订阅 WebSocket / EventEmitter / window.addEventListener
计时器 setInterval / setTimeout(含 cleanup)
副作用:localStorage / 第三方 SDK analytics / sentry
真正与 React 状态无关的全局协调 路由变化时的标题更新
// ✅ DOM 同步
useEffect(() => {
  document.title = `编辑 - ${title}`;
}, [title]);

// ✅ 订阅
useEffect(() => {
  const handler = (e: KeyboardEvent) => { ... };
  window.addEventListener("keydown", handler);
  return () => window.removeEventListener("keydown", handler);
}, []);

SSE 例外

SSE 是流式订阅,不是普通 fetch,继续用 useEffect 实现(参考 useSSE.ts),不用 useQuery。

自检

  • [ ] 没有 useEffect + fetch 拉数据?
  • [ ] 服务端状态用 useQuery?
  • [ ] useEffect 仅做 DOM / 订阅 / 计时 / 副作用?
  • [ ] useEffect 都有 cleanup 函数?

相关