Search code examples
javascriptreactjsreact-hooksreactn

Use function as react hook?


I wanted to use a function as a react hook to wrap fetch requests to an API.

My current hook:

export function useAPI(url, options={}) {
    const [auth, setAuth] = useGlobal('auth');
    const [call, setCall] = useState(undefined);

    const apiFetch = async () => {
        const res = await fetch(url, {
            ...options,
        });
        if (!res.ok)
            throw await res.json();
        return await res.json();
    };

    function fetchFunction() {
        fetch(url, {
            ...options,
        });
    }

    useEffect(() => {
        // Only set function if undefined, to prevent setting unnecessarily
        if (call === undefined) {
            setCall(fetchFunction);
            //setCall(apiFetch);
        }

    }, [auth]);

    return call
}

That way, in a react function, I could do the following...

export default function LayoutDash(props) {
    const fetchData = useAPI('/api/groups/mine/'); // should return a function

    useEffect(() => {
        fetchData(); // call API on mount
    }, []);

    render(...stuff);
}

But it seems react isn't able to use functions in hooks like that. If I set call to fetchFunction, it returns undefined. If I set it to apiFetch, it executes and returns a promise instead of a function that I can call when I want to in the other component.

I initially went for react hooks because I can't use useGlobal outside react components/hooks. And I would need to have access to the reactn global variable auth to check if the access token is expired.

So what would be the best way to go about this? The end goal is being able to pass (url, options) to a function that will be a wrapper to a fetch request. (It checks if auth.access is expired, and if so, obtains a new access token first, then does the api call, otherwise it just does the API call). If there's another way I should go about this other than react hooks, I'd like to know.


Solution

  • Instead of putting your function into useState, consider using useCallback. Your code would look something like this:

    export function useAPI(url, options={}) {
        const [auth, setAuth] = useGlobal('auth');
    
        function fetchFunction() {
            fetch(url, {
                ...options,
            });
        }
    
        const call = useCallback(fetchFunction, [auth]);
    
    
        const apiFetch = async () => {
            const res = await fetch(url, {
                ...options,
            });
            if (!res.ok)
                throw await res.json();
            return await res.json();
        };
    
        return call
    }
    

    The returned function is recreated whenever auth changes, therefore somewhat mimicking what you tried to do with useEffect