NextJS可复用API Fetch函数报错排查求助
问题排查:Readarr API调用函数的fetch错误与优化方案
问题描述
我希望创建一个可重复调用API的函数,但当前代码始终出现fetch错误。使用Readarr API获取作者信息,预期返回指定格式的数据。疑问:是否不能使用useEffect而需改用get-server-side-props?
相关代码
useFetcher.js
import { useState, useEffect } from "react"; export function Fetch( query, api, apikey ) { const [data, setData] = useState([]); const [isLoading, setLoading] = useState(true); var url = query; if( api ) { apikey = '&apikey=' + apikey; url = query + apikey; } useEffect(() => { fetch( url ) .then(( res ) => res.json() ) .then(( data ) => { setData( data ) setLoading( false ) } ) }, []); if( isLoading ) { return ( <p>Loading...</p> ) } if( !data ) { return ( <p>No profile data</p> ) } console.log( data ); return { data } }
page.tsx
var authorId = '5'; var authorLink = "https://example.com/api/v1/author/" + authorId; var author = Fetch( authorLink, true, 'ApiKeyHere' );
预期Readarr API返回格式
{ "authorMetadataId": 3, "status": "continuing", "ended": false, "authorName": "Sarah J. Maas", "authorNameLastFirst": "Maas, Sarah J.", "foreignAuthorId": "3433047", "titleSlug": "3433047", "overview": "Sarah J. Maas is the #1 New York Times and internationally bestselling author of the Crescent City, A Court of Thorns and Roses, and the Throne of Glass series. Her books have sold more than twelve million copies and are published in thirty-seven languages. A New York native, Sarah lives in Philadelphia with her husband, son, and dog. To find out more, visit sarahjmaas.com or follow @therealsjmaas on Instagram.", "links": [ { "url": "http://www.goodreads.com/author/show/3433047.Sarah_J_Maas", "name": "Goodreads" } ], "images": [ { "url": "/MediaCover/5/poster.jpg?lastWrite=637177342560000000", "coverType": "poster", "extension": ".jpg" } ], "path": "/data", "qualityProfileId": 1, "metadataProfileId": 1, "monitored": true, "monitorNewItems": "all", "rootFolderPath": "/data", "genres": [], "cleanName": "sarahjmaas", "sortName": "sarah j. maas", "sortNameLastFirst": "maas, sarah j.", "tags": [], "added": "2024-02-09T09:10:50Z", "ratings": { "votes": 11962351, "value": 4.36, "popularity": 52155850.36000001 }, "statistics": { "bookFileCount": 17, "bookCount": 26, "availableBookCount": 17, "totalBookCount": 26, "sizeOnDisk": 22554283, "percentOfBooks": 65.38461538461539 }, "id": 5 }
问题根源分析
- 调用方式违规:
Fetch本质是React组件/Hook,但你直接在组件外部像普通函数调用,违反了React规则——组件和Hook必须在组件渲染流程内调用,不能脱离React上下文执行。 - Hook命名不规范:自定义Hook必须以
use开头(比如useFetch),否则React无法识别其Hook身份,会触发规则校验错误。 - URL拼接逻辑错误:当原query不含
?时,直接拼接&apikey=xxx会生成无效URL(如https://example.com/api/v1/author/5&apikey=xxx),应该先判断是否需要添加?分隔符。 - 缺少错误处理:
fetch请求未处理网络失败、HTTP非200状态码等情况,一旦出错会直接导致组件崩溃。 - 初始状态不匹配:预期返回单个对象,但初始化
data为数组[],导致!data判断永远为假,无法正确处理无数据场景。
解决方案
1. 修复自定义Hook(重命名为useFetch)
import { useState, useEffect } from "react"; // 自定义Hook必须以use开头,符合React规范 export function useFetch(query, api, apikey) { const [data, setData] = useState(null); // 初始化为null,匹配预期的单个对象结构 const [isLoading, setLoading] = useState(true); const [error, setError] = useState(null); // 新增错误状态,捕获请求异常 // 正确拼接URL,处理有无查询参数的情况 let url = query; if (api) { const separator = query.includes('?') ? '&' : '?'; url = `${query}${separator}apikey=${apikey}`; } useEffect(() => { const fetchData = async () => { try { const res = await fetch(url); if (!res.ok) { throw new Error(`请求失败: ${res.status} ${res.statusText}`); } const result = await res.json(); setData(result); } catch (err) { setError(err.message); } finally { setLoading(false); } }; fetchData(); }, [url]); // 依赖项加入url,确保URL变化时自动重新请求 return { data, isLoading, error }; }
2. 在组件内正确使用Hook
修改page.tsx,确保在React组件内部调用自定义Hook:
import { useFetch } from './useFetcher'; export default function AuthorPage() { const authorId = '5'; const authorLink = `https://example.com/api/v1/author/${authorId}`; // 在组件渲染流程内调用Hook const { data: author, isLoading, error } = useFetch(authorLink, true, 'ApiKeyHere'); if (isLoading) return <p>Loading...</p>; if (error) return <p>请求错误: {error}</p>; if (!author) return <p>No profile data</p>; // 渲染作者数据 return ( <div> <h1>{author.authorName}</h1> <p>{author.overview}</p> {/* 其他需要展示的字段 */} </div> ); }
3. 关于get-server-side-props的选择
如果你的项目是Next.js,两种方案的适用场景:
- 客户端请求(useEffect):适合需要动态更新数据、用户交互触发请求的场景,但首次加载有空白期,且API密钥会暴露在客户端,存在安全风险。
- 服务端渲染(get-server-side-props):适合SEO优化、首次加载直接展示数据的场景,请求在服务器端完成,不会暴露API密钥,安全性更高。
服务端渲染示例:
export async function getServerSideProps() { const authorId = '5'; const apiKey = 'ApiKeyHere'; const url = `https://example.com/api/v1/author/${authorId}?apikey=${apiKey}`; try { const res = await fetch(url); if (!res.ok) throw new Error(`请求失败: ${res.status}`); const author = await res.json(); return { props: { author } }; } catch (err) { return { props: { error: err.message } }; } } export default function AuthorPage({ author, error }) { if (error) return <p>请求错误: {error}</p>; if (!author) return <p>No profile data</p>; return ( <div> <h1>{author.authorName}</h1> <p>{author.overview}</p> </div> ); }
内容的提问来源于stack exchange,提问作者Anake.me
相关产品推荐
相关产品推荐

