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]; }
问题根源
核心问题是数组解构的顺序不匹配:
- Hook返回数组的顺序是
[ApiGetCatFactsComponent, ApiGetDogFactsComponent](猫组件在前,狗组件在后) - 当调用
useApiGetFacts("dog", ...)并解构[ApiGetDogFactsComponent]时,实际拿到的是数组第一个元素——也就是ApiGetCatFactsComponent - 猫组件的逻辑是读取
data?.fact,但狗接口返回的是data.facts数组,所以data?.fact为undefined,导致页面只渲染了<h3>和按钮,没有数据内容 - 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
相关产品推荐
相关产品推荐

