DEV Community

F.P
F.P

Posted on • Edited on

1 1

Generic React Hook for async functions

I came up with the following function to create a React Hook of any async function (if it matches a simple interface with just one input parameter), which turns it into a GraphQL/Apollo-like hook.

type HookableFunc<T, P> = (param: P) => Promise<T|null>; 
type HookedFuncResult<T> = {
    result: T|null;
    err: any;
    pending: boolean;
};

export const asHook = <T, P>(func: HookableFunc<T, P>, param: P): HookedFuncResult<T> => {
    const [result, setResult] = useState<T|null>(null);
    const [err, setErr] = useState<any>();
    const [pending, setPending] = useState<boolean>(false);

    useEffect(() => {
        setPending(true);
        func(param)
            .then(setResult)
            .catch(setErr)
            .finally(() => setPending(false));
    }, [JSON.stringify(param)]);

    return {
        result,
        err,
        pending,
    };
};

Usage:

const myFunc = async(param: string): string[] => {
    // fetch data, use IO, etc.
};

export const useMyFuncHook = (param: string) => asHook(myFunc, param);

// elsewhere
export const MyComponent: React.FC<MyProps> = (props) => {
    const { pending, err, result } = useMyFuncHook(props.param);

    if (pending) return "Loading...";

    if (err) return "Some error occurred: " + JSON.stringify(err);

    if (!result) return "No data found!";

    return <Result data={result} />;
};

For an electron app, I needed to fetch data from the local file system in many different components.

But as I exclusively use Function Components (I find the whole concept of classes in JavaScript a bit weird), and these doesn't play nice together with async functions (which any function that uses external IO of any sort should be naturally anyways), I needed a proper solution to call those functions.

The solution is of course React Effects. Finding and reading this great article, I was happy to have solved my problem.

However the solution proposed there lacks generality and type-safety which I definitely wanted, as I needed this in lots of places and don't want to repeat what boils down to basically the same code over and over again, so I came up with my asHook function.

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (1)

Collapse
 
flopes89 profile image
F.P

While I think this is a neat way to using async functions in React Function Components, there are some problems with it which I cannot fully get rid of (happy for suggestions):

  • I don't like how err can be anything. I realize this is more or less baked into the exception handling of JavaScript, but I'd still love to have more precise typing here
  • If you want to hookify functions that take more than one parameter, you need to create new types for every amount of parameters you want to support, which seems completely ridiculous

Postgres on Neon - Get the Free Plan

No credit card required. The database you love, on a serverless platform designed to help you build faster.

Get Postgres on Neon