Skip to content

Instantly share code, notes, and snippets.

@patrixr
Created January 29, 2021 03:16
Show Gist options
  • Save patrixr/037d73f4230d74ede610e44a4178a559 to your computer and use it in GitHub Desktop.
Save patrixr/037d73f4230d74ede610e44a4178a559 to your computer and use it in GitHub Desktop.
import React, { useState } from "react";
type Maybe<T> = T | null;
type AnyFunction = (...args: any[]) => any;
type Awaited<T> = T extends { then(onfulfilled: (value: infer U) => any): any }
? U
: T extends { then(...args: any[]): any }
? never
: T;
type HookMethod<FuncType extends AnyFunction> = FuncType & {
error: Maybe<Error>;
loading: boolean;
data: Awaited<ReturnType<FuncType>>;
};
interface GenericService {
[key: string]: AnyFunction;
}
type MappedService<T extends GenericService> = {
[K in keyof T]: HookMethod<T[K]>;
};
const useServiceMethod = <T extends AnyFunction>(
method: T,
scope: any = null
): HookMethod<T> => {
const [error, setError] = useState<Maybe<Error>>(null);
const [loading, setLoading] = useState<boolean>(false);
const [data, setData] = useState<Maybe<Awaited<ReturnType<T>>>>(null);
const hookMethod: any = async (...args: any[]) => {
try {
setLoading(true);
setError(null);
const data = await method.apply(scope, args);
setData(data);
} catch (e) {
setError(new Error(e));
} finally {
setLoading(false);
}
};
hookMethod.error = error;
hookMethod.loading = loading;
hookMethod.data = data;
return hookMethod as HookMethod<T>;
};
export const useService = <T extends GenericService>(
service: T
): MappedService<T> => {
return Object.keys(service).reduce((hook, key) => {
if (typeof service[key] === "function") {
hook[key] = useServiceMethod(service[key], service);
}
return hook;
}, {} as any) as MappedService<T>;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment