skip to Main Content

The point is to use the same custom hook to make different API calls.

The problem:

When I return with {}, it works.
When I return with [], it doesn’t:

It does not enter the ApiGetDogFactsComponent function, it renders the html element, but does not return data.

Note: vscode highlights the return of the function inside {} as any, when inside [] as JSX.Element.
Note: there is a similar post here regarding the subject, but I couldn’t understand the differences.

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 = () => {
        ...samey...
        
        return (<>
            <article>
                <h3>React Query</h3>
                <p>{data.facts[0]}</p>
                <button onClick={refetch}>{description} Fact</button>
            </article>
        </>)
    }
    

    return {ApiGetCatFactsComponent, ApiGetDogFactsComponent};
} 

Adding, this also works.

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

Update for clarification:

This does not work

SeventhPage.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) => {
(same code)
...
    return [ApiGetCatFactsComponent, ApiGetDogFactsComponent];
}

2

Answers


  1. Chosen as BEST ANSWER

    This is not a good approach, read both David and TKol comments, but to make it work:

    SeventhPage.js

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

    UseApiCalls.js

    return [ApiGetCatFactsComponent, ApiGetDogFactsComponent];
    

    From my understanding, I should:

    1. Separate the SeventhPage in three different Components on a top container, so if a part of SeventhPage re-renders, it doesn't refresh the all page (as in, if I fetch dog data, it doesn't refresh cat data)!
    2. Hooks should be used to handle data, not returning JSX or UI elements!
    3. Return a different component with each data, from the hook
    4. Regarding the hook itself, it does repeate alot of things, when the only change is either dog or cat. According to David, a better solution would be to have a fixed list of items with a unique property (maybe coming from a database), and reutilize the code. Or have two hooks.
    5. Get a better understanding of array destructuring!

    Thank you both.


  2. In both of these lines of code:

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

    You are only selecting the first element of the returned array:

    return [ApiGetCatFactsComponent, ApiGetDogFactsComponent];
    

    Regardless of what you name them, both of them are rferences to ApiGetCatFactsComponent.

    Basically you’ve simply discovered that object properties are named and array elements are positional. If the original/returned name matters, use object properties.


    As an aside… If you have to call your hook twice depending on what arguments you pass to it, you might consider a different design. Maybe two different hooks, maybe the hook internally knows the URLs and returns both functions, maybe something else. It’s hard to tell based on what you’ve built.

    But, just looking at each call to useApiGetFactsAll three of the function parameters are telling it whether you want a "cat" or a "dog". That’s a lot of repetition.

    Taking it a step further… hooks shouldn’t really return components in the first place. Hooks are great for providing useful helper functions, maintaining state in those functions, etc. But this feels like an unintuitive mixture of hooks and components.

    Think of hooks and components as structurally similar, but the former returns data (or functions which return data) whereas the latter returns JSX.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search