You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
}

问题根源分析

  1. 调用方式违规:Fetch本质是React组件/Hook,但你直接在组件外部像普通函数调用,违反了React规则——组件和Hook必须在组件渲染流程内调用,不能脱离React上下文执行。
  2. Hook命名不规范:自定义Hook必须以use开头(比如useFetch),否则React无法识别其Hook身份,会触发规则校验错误。
  3. URL拼接逻辑错误:当原query不含?时,直接拼接&apikey=xxx会生成无效URL(如https://example.com/api/v1/author/5&apikey=xxx),应该先判断是否需要添加?分隔符。
  4. 缺少错误处理:fetch请求未处理网络失败、HTTP非200状态码等情况,一旦出错会直接导致组件崩溃。
  5. 初始状态不匹配:预期返回单个对象,但初始化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 13:52:04