使用自定义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但没有效果,不确定是否用法有误。
问题核心原因
- request对象引用不稳定:App2组件每次渲染时,都会创建一个全新的
{url: '/pokemon/ditto', ...}对象传给useAxios。React依赖对比采用引用比较,每次新的request对象会触发useAxios内部useEffect重新执行,调用fetchData后更新state,又触发组件重新渲染,形成无限循环。 - axios实例重复创建:useAxios每次执行都会重新创建axios客户端实例,属于冗余操作,还可能引发潜在问题。
- 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
相关产品推荐
相关产品推荐

