React: Adding createQueryHook and createMutationHook helpers
#9972
Unanswered
msanchezdev
asked this question in
Ideas
Replies: 1 comment
-
|
Below is the code for
import {
type DefaultError,
type MutationFunction,
type MutationKey,
type UseMutationOptions,
useMutation,
} from "@tanstack/react-query";
type BoundMutationHandlers<
Options extends UseMutationOptions<any, any, any, any>,
> = {
[K in keyof Options as K extends `on${infer Event}`
? `overrideOn${Event}` | `prependOn${Event}`
: never]?: Options[K] extends (...args: infer Args) => infer Return
? (...args: Args) => Return
: never;
};
type MutationKeyFn = (...args: any[]) => MutationKey;
type BoundMutationKey<TMutationKey extends MutationKey | MutationKeyFn> =
TMutationKey extends MutationKey
? {
mutationKey?: TMutationKey;
}
: {};
export function createMutationHook<
Options extends Omit<
UseMutationOptions<TData, TError, TVariables, TOnMutateResult>,
"mutationKey"
>,
TMutationKey extends MutationKey | MutationKeyFn = MutationKey,
TData = unknown,
TError = DefaultError,
TVariables = void,
TOnMutateResult = unknown,
>(
boundOptions: {
mutationKey?: TMutationKey;
mutationFn: MutationFunction<TData, TVariables>;
} & Options,
) {
type BoundMutationHookOptions = Omit<
UseMutationOptions<TData, TError, TVariables, TOnMutateResult>,
"mutationFn" | "mutationKey"
> &
BoundMutationKey<TMutationKey> &
BoundMutationHandlers<Options>;
type BoundMutationHookOptionsKeys = keyof BoundMutationHookOptions & string;
type BoundMutationParameters = TMutationKey extends (
...args: infer Args
) => any
? [...Args, options?: BoundMutationHookOptions]
: [options?: BoundMutationHookOptions];
function hook(...args: BoundMutationParameters) {
let mutationKey: MutationKey | undefined;
let options = {} as BoundMutationHookOptions;
if (typeof boundOptions.mutationKey === "function") {
const mutationKeyFnArgCount = boundOptions.mutationKey.length;
console.log("mutationKeyFnArgCount", mutationKeyFnArgCount);
console.log("args", args);
if (args.length >= mutationKeyFnArgCount) {
options = args[mutationKeyFnArgCount];
mutationKey = boundOptions.mutationKey(
...args.slice(0, mutationKeyFnArgCount),
);
} else {
mutationKey = boundOptions.mutationKey(...args);
}
}
for (const handler of Object.keys(
options,
) as BoundMutationHookOptionsKeys[]) {
if (handler.startsWith("overrideOn") && options[handler]) {
const event = handler.replace(/^overrideOn/, "");
// @ts-expect-error: tricky indexing
options[`on${event}`] = options[handler];
delete options[handler];
} else if (handler.startsWith("prependOn") && options?.[handler]) {
const event = handler.replace(/^prependOn/, "");
const handlerToPrepend = options[handler] as (...args: any[]) => any;
// @ts-expect-error: tricky indexing
options[`on${event}`] = (...args: any[]) => {
handlerToPrepend?.(...args);
// @ts-expect-error: tricky indexing
boundOptions[`on${event}`]?.(...args);
};
delete options[handler];
} else if (/^on[A-Z]/.test(handler)) {
const handlerToAppend = options[handler] as (...args: any[]) => any;
// @ts-expect-error: tricky indexing
options[handler] = (...args: any[]) => {
// @ts-expect-error: tricky indexing
boundOptions[handler]?.(...args);
handlerToAppend?.(...args);
};
}
}
return useMutation<TData, TError, TVariables, TOnMutateResult>({
...boundOptions,
...options,
mutationKey,
});
}
return hook;
} |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I have been thinking about opening a pull request adding the following helpers I am using in my projects:
createQueryandcreateMutation.Usually, we want to create a custom hook which wraps Tanstack Query's
useQueryhook but still being able to customize the properties passed to it, force a type-safe query key array, have default options or assign one-off overrides:I have been using the following helper in many of my projects:
login) outside for cases when I need to call it outside of components.createMutationhelper which generates the hook with options bound to it, in this case meta, an onSuccess handler and custom retry logic.Which when used it would be like this:
onSuccesshandler (it gets appended to the pre-bound handler), unless you pass it asoverrideOnSuccessorprependOnSuccessinstead. (Typescript keeps track of what handlers you pre-bound and marks error appropiately).useQueryanduseMutationtyped options are preserved and remain overridable on each call.Those are the main two hooks I use, haven't used
useInfiniteQuery,usePrefetch, etc, but this was simple to implement so can be implemented for those as well. (Around 55 LOC each, will share the code tomorrow, making some adjustments tonight)Beta Was this translation helpful? Give feedback.
All reactions