Sean
Sean

Reputation: 2577

React hooks with async fetch

I'm new to React hooks, but I'm trying to use a useEffect with a useCallback, but getting the notorious React Hook "useList" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks error.

This file holds the makeRequest:

function useConnections = () => {
    const makeRequest = React.useCallback(async (props) => {
        // Determine base url, determine headers here
        const response = fetch(url, options);
        
        return response;
    }

    return { makeRequest };
}

This file is my useListProvider:

function useListProvider = () => {
    const { makeRequest } = useConnections();

    const useList = React.useCallback(async (props) => {
        // makerequest is just a wrapper for fetch with a bunch of needed headers.
        const response = await makeRequest(props);

        return { body: response.body };
    }

    return { useList };
}

This is the rendered page:

function MainPage() {
    const [isBusy, setIsBusy] = React.useStore(false);
    const { useList } = useListProvider();

    React.useEffect(() => {
        if (!isBusy) { useList(); setIsBusy(false); } // ERROR HERE!
    }, [useList]);

    return (
        <React.Fragment>
            IsBusy: {isBusy}
        </React.Fragment>
    );
}

Maybe I'm not getting it, but I only want to grab the useList data when the state says it's not busy. However, doing it this way, I get the error listed above. I understand I can't think of this the same way as Component classes, but how would you approach single and multiple renders from a callback?

I'm not entirely sure what is happening here because I'm doing something similar in the useConnections, etc. and not getting the same error?

Upvotes: 28

Views: 92072

Answers (1)

Nicholas Tower
Nicholas Tower

Reputation: 84912

The lint rule for hooks assumes that things which start with "use" are hooks. Thus it thinks that useList is a hook and trying to enforce the rules of hooks on it. But it's not actually a hook, it's just a normal function, so you just need to give it a different name and the lint rule will be satisfied.

function useListProvider = () => {
    const { makeRequest } = useConnections();

    const callback = React.useCallback(async (props) => {
        const response = await makeRequest(props);

        return { body: response.body };
    }, [makeRequest])

    return { callback };
}

// elsewhere:

const { callback } = useListProvider();

React.useEffect(() => {
    if (!isBusy) { 
      callback(); 
      setIsBusy(false);
    }
}, [callback]);

Why is it not a hook? Well, a hook is either one of the built in hooks, or a function that calls one of the built in hooks. Your callback doesn't meet those criteria. It was created by useCallback, but that just means it's a memoized function, not a hook.

Upvotes: 46

Related Questions