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.
Top comments (1)
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):
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