0

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
    *"When I return with {}, it works. When I return with [], it doesn't"* - Are you showing us the one that "works" or the one that "doesn't"? It's not really clear to me. – David Mar 17 '23 at 15:29
  • The one that works, sorry for unclear. – João Fidalgo Mar 17 '23 at 15:30
  • 1
    Can you update the question to include an example of the code which *isn't working*? I don't see how we can help you with code you're not showing us. – David Mar 17 '23 at 15:31
  • 2
    You really shouldn't be rendering components in this way. Anything that causes SeventhPage to rerender will cause your Dog and Cat components to lose the fetched data and be rerendered from scratch, with new data. – TKoL Mar 17 '23 at 15:36
  • @David Updated the question for clarification. Thank you. TKoL So I should separate the SeventhPage into two different components, correct? To avoid losing data. – João Fidalgo Mar 17 '23 at 15:39

2 Answers2

1

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 useApiGetFacts... All 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.

David
  • 208,112
  • 36
  • 198
  • 279
  • will you add to your answer the way that Joao could make array-destructuring work for this usecase? – TKoL Mar 17 '23 at 15:44
  • Thanks for the help David and TKoL, from your comments, the problem is a fundamental misunderstanding of array destructuring on my part, could you give an example of how to make it work in this use-case, David? Thank you for helping me understand the differences between when to use a Hook and a Component! Following your comments, I should separate the SeventhPage, and make a call to hooks for data, and render the data with components, correct? Thank you, – João Fidalgo Mar 17 '23 at 16:07
  • @JoãoFidalgo: I guess what are the specifics of the use case? If "make it work in this use-case" involves scrapping your components/hooks and re-writing them then that's certainly out of scope for a Stack Overflow answer. But if you're just asking how to destructure each of your separate hook calls then doesn't that already successfully happen when using the curly braces instead of the square brackets? – David Mar 17 '23 at 16:17
  • Yes it does, David, I wanted to know how to make it work using square brackets, but it might not be possible because of the terrible way I made the code. Will be studying more, thanks for the help ! – João Fidalgo Mar 17 '23 at 16:31
  • I made it work, but it is not a good way to do it, will post an answer, but will be accepting David and point to TKol comment, thank you both. – João Fidalgo Mar 17 '23 at 16:35
1

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.