React中自定义useGetApi钩子参数需预处理时的正确调用方案
解决方案:复用现有自定义钩子,无需新增
首先,你遇到的错误是因为违反了React钩子的核心规则:钩子必须在函数组件的顶层作用域或自定义钩子的顶层作用域调用,不能嵌套在useEffect、条件判断等回调函数内部。
不用单独编写新钩子,只需要调整现有钩子的实现和使用方式,就能完美复用。
第一步:修复原useGetApi的bug
你的原钩子存在两个问题:
useEffect的依赖是JSON.stringify(data),这会导致每次data更新就重新触发请求,进而再次更新data,形成无限循环;- 没有处理组件卸载时的请求取消,可能造成内存泄漏。
修改后的useGetApi:
import { useState, useEffect } from 'react'; import axios from 'axios'; export const useGetApi = (link) => { const [data, setData] = useState(); const [loading, setLoading] = useState(true); useEffect(() => { // 没有link时直接返回,避免无效请求 if (!link) return; const cancelToken = axios.CancelToken.source(); const handleFetch = async () => { try { setLoading(true); const response = await axios.get(link, { cancelToken: cancelToken.token }); setData(response.data.data); } catch (err) { // 忽略取消请求的错误,只处理真实异常 if (!axios.isCancel(err)) { console.error('API请求失败:', err); } } finally { // 请求无论成功失败,都结束loading状态 setLoading(false); } }; handleFetch(); // 组件卸载或link变化时,取消未完成的请求 return () => cancelToken.cancel('请求已取消'); }, [link]); // 依赖改为link,只有link变化时才重新发起请求 return [data, loading]; };
第二步:正确使用钩子(同步生成modifiedLink场景)
不需要把钩子放到useEffect里,直接在组件顶层调用,传入处理后的modifiedLink即可。只要modifiedLink发生变化,钩子会自动重新请求:
export const App = () => { const link = '/helloworld'; // 假设getModifiedLink是纯函数,基于link生成新的链接 const modifiedLink = getModifiedLink(link); // 直接在组件顶层调用钩子 const [data, loading] = useGetApi(modifiedLink); // 根据状态渲染UI if (loading) return <div>加载中...</div>; return <div>{JSON.stringify(data)}</div>; };
第三步:异步生成modifiedLink场景的处理
如果modifiedLink需要通过异步操作生成(比如先请求其他接口获取参数),可以用状态存储最终的链接,再传给钩子:
export const App = () => { const [finalLink, setFinalLink] = useState(null); const baseLink = '/helloworld'; // 异步获取并生成最终链接 useEffect(() => { const generateFinalLink = async () => { // 模拟异步获取参数 const params = await fetchRequiredParams(); const modified = `${baseLink}?${new URLSearchParams(params)}`; setFinalLink(modified); }; generateFinalLink(); }, [baseLink]); // 只有finalLink存在时才发起请求 const [data, loading] = useGetApi(finalLink); if (!finalLink) return <div>准备请求中...</div>; if (loading) return <div>加载中...</div>; return <div>{JSON.stringify(data)}</div>; };
这样既复用了原有的useGetApi钩子,又解决了不同场景下的链接处理需求,完全不需要新增重复代码。
内容的提问来源于stack exchange,提问作者Tank
相关产品推荐
相关产品推荐

