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

React自定义Hook返回对象正常、返回数组失效问题咨询

React自定义Hook返回数组时组件无数据的问题解决

问题说明

需要用同一个自定义Hook发起不同API调用:

  • 当Hook返回对象时功能正常,能正常渲染数据
  • 当Hook返回数组时,组件仅渲染HTML结构,但无法展示API返回的数据
  • VSCode提示差异:返回对象时函数返回值标记为any,返回数组时标记为JSX.Element

代码对比

正常工作的写法(返回对象)

ApiCallPage.js

// Custom hook for API Facts
import { useApiGetFacts } from '../Hooks/useApiCalls';

export const SeventhPage = () => {
    const {ApiGetDogFactsComponent}  = useApiGetFacts("dog", "https://dog-api.kinduff.com/api/facts", "Dog");
    const {ApiGetCatFactsComponent}  = useApiGetFacts("cat", "https://catfact.ninja/fact", "Cat");

    return (<>
        ...
            <ApiGetDogFactsComponent />
            <ApiGetCatFactsComponent />
    </>)
}

useApiCalls.js

import { useQuery } from '@tanstack/react-query';

export const useApiGetFacts = (type, url, description) => {
    const {data, isLoading, isError, refetch, isRefetching} = useQuery([type], async () => {
        try { 
            const res = await fetch(url);
            const data = await res.json();
            return data;
        } catch (error) {
            throw error;
        }
    });

    const ApiGetCatFactsComponent = () => {
        if (isLoading || isRefetching) {
            return (<>
                <h1 style={{color: "white"}}>{"Loading ..."}</h1>
            </>)
        }

        if (isError) {
            return (<>
                <h1 style={{color: "white"}}>{"Error Processing ..."}</h1>
            </>)
        }

        return (<>
            <article>
                <h3>React Query</h3>
                <p>{data?.fact}</p>
                <button onClick={refetch}>{description} Fact</button>
            </article>
        </>)
    }

    const ApiGetDogFactsComponent = () => {
        if (isLoading || isRefetching) {
            return (<>
                <h1 style={{color: "white"}}>{"Loading ..."}</h1>
            </>)
        }

        if (isError) {
            return (<>
                <h1 style={{color: "white"}}>{"Error Processing ..."}</h1>
            </>)
        }
        
        return (<>
            <article>
                <h3>React Query</h3>
                <p>{data.facts[0]}</p>
                <button onClick={refetch}>{description} Fact</button>
            </article>
        </>)
    }
    

    return {ApiGetCatFactsComponent, ApiGetDogFactsComponent};
} 

无法工作的写法(返回数组)

ApiCallPage.js

export const SeventhPage = () => {
...
    const [ApiGetDogFactsComponent]  = useApiGetFacts("dog", "https://dog-api.kinduff.com/api/facts", "Dog");
    const [ApiGetCatFactsComponent]  = useApiGetFacts("cat", "https://catfact.ninja/fact", "Cat");
...
}

useApiCalls.js

export const useApiGetFacts = (type, url, description) => {
// 内部逻辑和正常写法一致
...
    return [ApiGetCatFactsComponent, ApiGetDogFactsComponent];
}

问题根源

核心问题是数组解构的顺序不匹配:

  1. Hook返回数组的顺序是[ApiGetCatFactsComponent, ApiGetDogFactsComponent](猫组件在前,狗组件在后)
  2. 当调用useApiGetFacts("dog", ...)并解构[ApiGetDogFactsComponent]时,实际拿到的是数组第一个元素——也就是ApiGetCatFactsComponent
  3. 猫组件的逻辑是读取data?.fact,但狗接口返回的是data.facts数组,所以data?.fact为undefined,导致页面只渲染了<h3>和按钮,没有数据内容
  4. VSCode的类型提示差异是因为返回数组时,类型推断识别到第一个元素是JSX组件,而返回对象时解构的属性类型未明确,所以标记为any

解决方案

方案1:修正数组解构的顺序

按照Hook返回数组的顺序来解构,调用狗接口时取数组第二个元素:

export const SeventhPage = () => {
    const [, ApiGetDogFactsComponent]  = useApiGetFacts("dog", "https://dog-api.kinduff.com/api/facts", "Dog");
    const [ApiGetCatFactsComponent]  = useApiGetFacts("cat", "https://catfact.ninja/fact", "Cat");

    return (<>
        <ApiGetDogFactsComponent />
        <ApiGetCatFactsComponent />
    </>)
}

方案2:优化Hook设计(更合理)

既然每次调用Hook只需要对应类型的组件,没必要同时创建两个组件,直接根据传入的type返回对应组件即可:

// useApiCalls.js
export const useApiGetFacts = (type, url, description) => {
    const {data, isLoading, isError, refetch, isRefetching} = useQuery([type], async () => {
        try { 
            const res = await fetch(url);
            const data = await res.json();
            return data;
        } catch (error) {
            throw error;
        }
    });

    // 通用加载/错误组件
    const renderStatus = () => {
        if (isLoading || isRefetching) {
            return <h1 style={{color: "white"}}>Loading ...</h1>
        }
        if (isError) {
            return <h1 style={{color: "white"}}>Error Processing ...</h1>
        }
        return null;
    }

    // 根据type返回对应组件
    const FactComponent = () => {
        const status = renderStatus();
        if (status) return status;

        return (
            <article>
                <h3>React Query</h3>
                <p>{type === 'cat' ? data?.fact : data?.facts?.[0]}</p>
                <button onClick={refetch}>{description} Fact</button>
            </article>
        )
    }

    return [FactComponent];
}

调用时:

export const SeventhPage = () => {
    const [DogFactComponent]  = useApiGetFacts("dog", "https://dog-api.kinduff.com/api/facts", "Dog");
    const [CatFactComponent]  = useApiGetFacts("cat", "https://catfact.ninja/fact", "Cat");

    return (<>
        <DogFactComponent />
        <CatFactComponent />
    </>)
}

内容的提问来源于stack exchange,提问作者João Fidalgo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 17:47:58