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

使用自定义useAxios Hook调用Poke API时出现无限循环求助

React调用Poke API无限循环问题排查与解决

问题描述

我正在学习React,尝试调用Poke API时出现无限循环问题,不清楚问题所在。以下是我的代码:

useAxios.ts

import axios from 'axios';
import { useState, useEffect } from 'react';
import Request from '../interfaces/request';

const useAxios = (request: Request) => {
    const client = axios.create({
        baseURL: "https://pokeapi.co/api/v2" 
     });
    const [response, setResponse] = useState(null);
    const [error, setError] = useState('');
    const fetchData = async () => {
        const response = await client[request.method](request.url,request.body) 
        if(response.status <= 399){
            setResponse(response.data);
        }
        else{
            setError(response.statusText);
        }
    };
    useEffect(() => {
        fetchData();
    }, [request]);
    return { response, error };
};
export default useAxios;

视图组件App2.tsx

import Pokecard from "../components/pokecard/Pokecard";
import { Methods } from "../interfaces/request";
import axios from '../plugins/Useaxios';

function App2() {
  const response = axios({
    url: '/pokemon/ditto',
    method: Methods['get'],
    body: JSON.stringify({}),
    headers: JSON.stringify({})
  })
  console.log(response)
  return (
    <>
      <Pokecard />;
    </>
  );
}

export default App2;

我尝试过使用useEffect但没有效果,不确定是否用法有误。


问题核心原因

  1. request对象引用不稳定:App2组件每次渲染时,都会创建一个全新的{url: '/pokemon/ditto', ...}对象传给useAxios。React依赖对比采用引用比较,每次新的request对象会触发useAxios内部useEffect重新执行,调用fetchData后更新state,又触发组件重新渲染,形成无限循环。
  2. axios实例重复创建:useAxios每次执行都会重新创建axios客户端实例,属于冗余操作,还可能引发潜在问题。
  3. GET请求参数错误:GET请求不需要传递body,你传入JSON.stringify({})不符合HTTP规范,可能导致请求异常。

解决方案

1. 稳定request对象引用

在App2中用useMemo缓存request对象,确保仅在依赖变化时重新创建:

import { useMemo, useEffect } from 'react';
import Pokecard from "../components/pokecard/Pokecard";
import { Methods } from "../interfaces/request";
import useAxios from '../plugins/Useaxios'; // 自定义Hook命名需以use开头,规范命名

function App2() {
  // 用useMemo缓存请求配置,避免每次渲染生成新引用
  const request = useMemo(() => ({
    url: '/pokemon/ditto',
    method: Methods['get'],
    headers: {} // 直接传对象,无需JSON.stringify
  }), []); // 空依赖数组,仅初始化一次

  const { response, error } = useAxios(request);
  
  // 避免每次渲染都打印,用useEffect监听状态变化
  useEffect(() => {
    console.log(response, error);
  }, [response, error]);

  return (
    <>
      <Pokecard />
    </>
  );
}

export default App2;

2. 优化useAxios自定义Hook

  • 将axios实例移到Hook外部,避免重复创建
  • 增加错误捕获逻辑,处理网络异常
  • 新增加载状态,优化UI交互
import axios from 'axios';
import { useState, useEffect } from 'react';
import Request from '../interfaces/request';

// 全局初始化axios实例,仅创建一次
const client = axios.create({
  baseURL: "https://pokeapi.co/api/v2" 
});

const useAxios = (request: Request) => {
  const [response, setResponse] = useState(null);
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(true); // 新增加载状态

  const fetchData = async () => {
    setLoading(true);
    try {
      // 根据请求方法区分参数传递方式:GET用params,POST/PUT用data
      const res = await client[request.method](request.url, 
        request.method.toLowerCase() === 'get' 
          ? { params: request.params } 
          : request.body
      );
      if(res.status <= 399){
        setResponse(res.data);
        setError('');
      } else {
        setError(res.statusText);
      }
    } catch (err) {
      // 捕获网络错误或其他异常
      setError(err instanceof Error ? err.message : '请求失败');
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchData();
  }, [request]);

  return { response, error, loading };
};

export default useAxios;

3. 额外注意事项

  • 自定义Hook必须以use开头,避免和普通函数混淆,同时符合React Hook规范。
  • GET请求如需传参,使用params字段传递URL查询参数,而非body。
  • 建议在UI中根据loading状态展示加载提示,提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 18:05:45