You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React自定义CRUD useApi Hook调用list方法触发无限循环问题求助

问题根因

  • 第一个错误:在useApi的顶层渲染逻辑中直接调用setApi更新状态。setApi触发状态更新后会导致组件重渲染,重渲染时又会生成新的list、create函数实例,再次触发setApi更新,形成死循环的第一个触发源。
  • 第二个错误:业务组件的useEffect把api对象作为依赖项。由于上面的逻辑api对象每次渲染都会生成新的引用,useEffect会反复执行调用api.list(),list内部更新apiData、isLoading状态又触发重渲染,进一步加剧死循环。
  • 额外问题:create方法中fetch的传参写法错误,fetch仅接收两个参数:第一个是url,第二个是完整的配置对象,请求体需要放在配置对象的body字段中,你当前写的三个参数、直接重写headers的写法完全不生效。

修复方案

第一步:重构useApi Hook

把api方法集合用useCallback包裹方法,再用useMemo生成固定引用的对象,避免不必要的引用更新,同时修正fetch传参错误:

import { useState, useCallback, useMemo } from "react";

const useApi = (baseUrl, defaultHeaders = {}) => {
  const [isLoading, setIsLoading] = useState(false);
  const [apiData, setApiData] = useState(null);
  const [serverError, setServerError] = useState(null);

  const list = useCallback(async () => {
    setIsLoading(true);
    setServerError(null);
    try {
      const resp = await fetch(baseUrl, {
        method: "GET",
        headers: defaultHeaders
      });
      const data = await resp.json();
      setApiData(data);
    } catch (error) {
      setServerError(error);
    } finally {
      setIsLoading(false);
    }
  }, [baseUrl, defaultHeaders]);

  const create = useCallback(async (payload) => {
    setIsLoading(true);
    setServerError(null);
    try {
      const resp = await fetch(baseUrl, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          ...defaultHeaders
        },
        body: JSON.stringify(payload)
      });
      const data = await resp.json();
      setApiData(data);
      return data;
    } catch (error) {
      setServerError(error);
    } finally {
      setIsLoading(false);
    }
  }, [baseUrl, defaultHeaders]);

  // 用useMemo生成固定引用的api对象,只有依赖变化才更新
  const api = useMemo(() => ({
    list,
    create
  }), [list, create]);

  return { isLoading, apiData, serverError, api };
};

export default useApi;

第二步:修改业务组件的useEffect依赖

只在组件挂载时调用一次list即可:

import { useEffect } from "react";
import useApi from "./useApi";

export default function App() {
  const {
    isLoading: loading,
    apiData: students,
    serverError: error,
    api
  } = useApi("https://59f0f160ce72350012bec011.mockapi.io/students");

  useEffect(() => {
    api.list();
    // 空依赖数组代表仅挂载时执行一次
  }, []);

  if (error) return <div>请求出错:{error.message}</div>

  return (
    <div className="App">
      <h1>list</h1>
      {loading ? "loading" : students?.map((x) => x.name)}
    </div>
  );
}

内容的提问来源于stack exchange,提问作者rsilva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 00:24:03