Next.js + TypeScript自定义useFetch钩子URL类型错误解决
解决Next.js + TypeScript中useFetch钩子的类型不匹配问题
问题场景
使用Next.js搭配TypeScript开发时,自定义useFetch钩子调用API时出现类型不匹配错误,错误提示:
Argument of type 'string' is not assignable to parameter of type 'FetchParams'.ts(2345)
错误出现在组件调用useFetch的语句中:
const { data }: any = useFetch(`https://api.aladhan.com/v1/calendarByCity?city=${city}&country=${country}&method=5&month=${month}&year=${year}`)
错误原因
- 参数类型定义错误:自定义钩子
useFetch的参数被定义为FetchParams类型(一个包含url属性的对象),但实际调用时传入的是字符串格式的URL,导致类型不匹配。 - React钩子规范问题:
useFetch被定义为async函数,违反React钩子不能是异步函数的规则。 - 状态类型不明确:
useState初始化时使用空字符串,与API返回的JSON数据类型不匹配,且大量使用any类型失去TypeScript的类型校验优势。
修复方案
1. 修正useFetch钩子的类型与实现
将钩子的参数改为string类型,移除冗余的FetchParams类型;改用useEffect处理异步请求,遵循React钩子规则;给状态指定泛型类型:
"use client" import { useState, useEffect } from "react"; // 定义API返回数据的基础类型,建议后续替换为具体接口结构 type ApiResponse = any; export default function useFetch<T = ApiResponse>(url: string) { const [data, setData] = useState<T | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState<Error | null>(null); useEffect(() => { const fetchData = async () => { try { const res = await fetch(url); if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`); const result = await res.json() as T; setData(result); } catch (err) { setError(err as Error); console.log("error", err); } finally { setLoading(false); } }; fetchData(); }, [url]); return { data, loading, error }; }
2. 修正Pray组件的调用逻辑
移除组件内冗余的状态,正确在组件顶层调用useFetch,并明确API返回数据的类型:
import useFetch from '@/Hooks/useFetch'; import { useState, useEffect } from 'react'; // 定义API返回的具体类型,替代any以获得类型校验 type PrayerCalendarResponse = { data: Array<{ date: { gregorian: { date: string } }, timings: { Fajr: string; Dhuhr: string; Asr: string; Maghrib: string; Isha: string } }> }; export default function Pray() { // Date methods const date = new Date(); const month = date.getMonth() + 1; const year = date.getFullYear(); const [country, setCountry] = useState('eg'); const [city, setCity] = useState('cairo'); // 动态生成请求URL const apiUrl = `https://api.aladhan.com/v1/calendarByCity?city=${city}&country=${country}&method=5&month=${month}&year=${year}`; // 在组件顶层调用useFetch,传入明确的类型泛型 const { data, loading, error } = useFetch<PrayerCalendarResponse>(apiUrl); // 监听数据变化做后续处理 useEffect(() => { if (data) { console.log(data); // 这里可添加数据处理逻辑 } }, [data]); if (loading) return <div>加载中...</div>; if (error) return <div>请求出错:{error.message}</div>; return ( <div> {data?.data.map((item, index) => ( <div key={index}> <h3>{item.date.gregorian.date}</h3> <p>晨礼:{item.timings.Fajr}</p> <p>晌礼:{item.timings.Dhuhr}</p> <p>晡礼:{item.timings.Asr}</p> <p>昏礼:{item.timings.Maghrib}</p> <p>宵礼:{item.timings.Isha}</p> </div> ))} </div> ); }
关键说明
- 类型明确化:定义
PrayerCalendarResponse类型约束API返回数据,替代any,提升代码可维护性和类型安全性。 - 钩子规范:确保
useFetch遵循React钩子规则,仅在组件顶层调用。 - 状态扩展:添加
loading和error状态,便于处理请求的不同阶段,提升用户体验。
内容的提问来源于stack exchange,提问作者Ahmed Essam
相关产品推荐
相关产品推荐

