React自定义useAPI Hook中useEffect无限循环问题排查与解决
问题分析与解决方案
问题根源
- 无限循环的核心逻辑错误:你把
data设为useEffect的依赖,但getDogCollection中会调用setData(result)更新data——data更新触发useEffect重新执行,再次发起请求并更新data,形成无限闭环。 - useCallback依赖缺失:
getDogCollection里用到了url和options,但useCallback的依赖数组是空的,这会导致函数始终捕获初始的url/options,如果这两个参数变化,请求不会使用新值;另外你在console.log中使用了data,但没把它加入依赖,这里拿到的是闭包中的旧data,属于隐性bug。 - 需求逻辑偏差:你想让请求在
data变化时执行,但API请求的触发条件应该是请求参数变化(如url/options)或手动触发,而非请求结果data变化。
修复方案
基础版:参数变化时自动请求
调整依赖项,让请求仅在url或options变化时触发,同时补全useCallback的依赖:
import { useCallback, useEffect, useState } from "react"; export const useAPI = (url, options) => { const [data, setData] = useState([]); const getDogCollection = useCallback(() => { fetch(url, options) .then((res) => { if (!res.ok) throw new Error('请求失败'); // 处理HTTP错误 return res.json(); }) .then((result) => { setData(result); }) .catch(err => console.error('请求出错:', err)); // 不要忽略错误 }, [url, options]); // 补全依赖,确保参数变化时函数更新 useEffect(() => { getDogCollection(); }, [getDogCollection]); // 依赖更新后的getDogCollection,触发请求 return data; };
进阶版:支持手动触发
如果需要手动触发请求(比如点击按钮刷新),可以新增一个trigger参数:
import { useCallback, useEffect, useState } from "react"; export const useAPI = (url, options, trigger = 0) => { const [data, setData] = useState([]); const getDogCollection = useCallback(() => { fetch(url, options) .then((res) => { if (!res.ok) throw new Error('请求失败'); return res.json(); }) .then((result) => { setData(result); }) .catch(err => console.error('请求出错:', err)); }, [url, options, trigger]); // 加入trigger,值变化时触发请求 useEffect(() => { getDogCollection(); }, [getDogCollection]); return data; }; // 使用示例: // const [refreshTrigger, setRefreshTrigger] = useState(0); // const data = useAPI('/api/dogs', {}, refreshTrigger); // 点击按钮时调用 setRefreshTrigger(prev => prev + 1) 触发刷新
内容的提问来源于stack exchange,提问作者Cnerd Mahadi
相关产品推荐
相关产品推荐

