React自定义TS Fetch Hook报错:Cannot read properties of null (reading 'useState')求助
解决方案:自定义Fetch Hook报错问题
一、核心报错原因排查(对应截图中的Hook调用错误)
你遇到的是React Hook的非法调用错误,常见触发场景及解决:
禁止在非顶层调用Hook:确保
useCustomFetch只能在函数组件的顶层或其他自定义Hook的顶层调用,不能放在if/else、for循环、嵌套函数或类组件中。
错误示例:// 错误:条件判断内调用Hook function MyComponent() { if (isLoggedIn) { const { data } = useCustomFetch('/api/user', {}); } return <div>...</div>; }正确示例:
// 正确:组件顶层调用Hook function MyComponent() { const { data, loading, error } = useCustomFetch('/api/user', {}); if (loading) return <div>加载中...</div>; if (error) return <div>错误:{(error as Error).message}</div>; return <div>{JSON.stringify(data)}</div>; }检查React版本与依赖一致性:
- 确保
react和react-dom版本均≥16.8(Hook最低支持版本),且版本一致:npm list react react-dom - 若版本不一致或存在多实例,执行以下命令修复:
npm install react@latest react-dom@latest npm dedupe
- 确保
二、修复自定义Hook的潜在问题
虽然不是当前报错的直接原因,但优化代码可避免后续问题:
- 启用
customBase参数:原代码未使用该参数,补充URL拼接逻辑:const fullUrl = customBase ? `${customBase}${url}` : url; const response = await fetch(`${fullUrl}?${new URLSearchParams(params)}`); - 添加正确的useEffect依赖项:当
url/params/customBase变化时自动重新请求:
注意:若每次渲染都传递新的useEffect(() => { getData(); }, [url, params, customBase]);params对象,需用useMemo包裹避免无限重渲染:function MyComponent() { const params = useMemo(() => ({ page: '1', size: '10' }), []); const { data } = useCustomFetch('/api/list', params); } - 避免组件卸载后的状态更新:添加请求取消逻辑防止内存泄漏:
useEffect(() => { const controller = new AbortController(); const signal = controller.signal; const getData = async () => { setLoading(true); try { const fullUrl = customBase ? `${customBase}${url}` : url; const response = await fetch(`${fullUrl}?${new URLSearchParams(params)}`, { signal }); const json = await response.json(); setStatus(response.status); setStatusText(response.statusText); setData(json); } catch (error) { // 忽略主动取消的请求错误 if ((error as Error).name !== 'AbortError') { setError(error); } } setLoading(false); }; getData(); return () => controller.abort(); // 组件卸载时取消请求 }, [url, params, customBase]); - 优化TypeScript类型:替换
any为泛型,提升类型安全性:
使用时可指定返回数据类型:export type FetchResponse<T = unknown> = { status: number; statusText: string; data: T | undefined; error: unknown; loading: boolean; }; export const useCustomFetch = <T = unknown>( url: string, params: Record<string, string>, customBase?: string ): FetchResponse<T> => { const [status, setStatus] = useState<number>(0); const [statusText, setStatusText] = useState<string>(''); const [data, setData] = useState<T>(); const [error, setError] = useState<unknown>(); const [loading, setLoading] = useState<boolean>(false); // 其余逻辑不变 return { data, loading, error, status, statusText }; };interface User { id: number; name: string; } const { data } = useCustomFetch<User>('/api/user', {});
三、最后排查步骤
- 重启开发服务器,清除缓存可能解决偶发性的配置异常
- 检查项目webpack配置(非Create React App项目),确保React的resolve路径正确
内容的提问来源于stack exchange,提问作者Malice
相关产品推荐
相关产品推荐

