David Alonso
David Alonso4mo ago

`withOptimisticUpdate` with custom (auth) mutations

I have the following custom mutation:
export function useAuthMutation<Mutation extends AuthFunction<"mutation">>(
name: Mutation
) {
const organization = useOrganization();
const clerkOrgId = organization?.organization?.id;
const originalMutation = useMutation(name);

return useCallback(
async (
...args: AuthArgsArray<Mutation>
): Promise<FunctionReturnType<Mutation>> => {
const newArgs = {
...(args[0] ?? {}),
clerkOrgId,
} as FunctionArgs<Mutation>;

return originalMutation(...([newArgs] as OptionalRestArgs<Mutation>));
},
[clerkOrgId, originalMutation]
);
}
export function useAuthMutation<Mutation extends AuthFunction<"mutation">>(
name: Mutation
) {
const organization = useOrganization();
const clerkOrgId = organization?.organization?.id;
const originalMutation = useMutation(name);

return useCallback(
async (
...args: AuthArgsArray<Mutation>
): Promise<FunctionReturnType<Mutation>> => {
const newArgs = {
...(args[0] ?? {}),
clerkOrgId,
} as FunctionArgs<Mutation>;

return originalMutation(...([newArgs] as OptionalRestArgs<Mutation>));
},
[clerkOrgId, originalMutation]
);
}
which automatically injects the clerkOrgId into the args. I'm unfortunately not able to do useAuthMutation().withOptimisticUpdate(). Can anyone who has worked on convex-helpers offer suggestions? 🙏
7 Replies
David Alonso
David AlonsoOP4mo ago
@erquhart
ballingt
ballingt4mo ago
You can take the .withOptimisticUpdate method and pass it along in your wrapper; that's what happens in the real client, it's just a function tacked on there https://github.com/get-convex/convex-js/blob/533f0705af13fb790be4b58ea10735aa94ee8d7c/src/react/client.ts#L88
GitHub
convex-js/src/react/client.ts at 533f0705af13fb790be4b58ea10735aa94...
TypeScript/JavaScript client library for Convex. Contribute to get-convex/convex-js development by creating an account on GitHub.
ballingt
ballingt4mo ago
so instead of returning the useCallback result directly, tack this method on first and then return it. Or use a useMemo instead so you can modify that function object inside the hook. Then you might enforce that it satisfies ReactMutation<F> (or cast it) to convince TypeScript that this method exists
erquhart
erquhart4mo ago
export function useAuthMutation<Mutation extends AuthFunction<"mutation">>(
name: Mutation,
optimisticUpdate: OptimisticUpdate<FunctionArgs<Mutation>>,
) {
const organization = useOrganization();
const clerkOrgId = organization?.organization?.id;
const originalMutation = useMutation(name);

return useCallback(
async (
...args: AuthArgsArray<Mutation>
): Promise<FunctionReturnType<Mutation>> => {
const newArgs = {
...(args[0] ?? {}),
clerkOrgId,
} as FunctionArgs<Mutation>;

const mutation = originalMutation(...([newArgs] as OptionalRestArgs<Mutation>));
if (!optimisticUpdate) {
return mutation
}
return mutation.withOptimisticUpdate(optimisticUpdate)
},
[clerkOrgId, originalMutation, optimisticUpdate]
);
}
export function useAuthMutation<Mutation extends AuthFunction<"mutation">>(
name: Mutation,
optimisticUpdate: OptimisticUpdate<FunctionArgs<Mutation>>,
) {
const organization = useOrganization();
const clerkOrgId = organization?.organization?.id;
const originalMutation = useMutation(name);

return useCallback(
async (
...args: AuthArgsArray<Mutation>
): Promise<FunctionReturnType<Mutation>> => {
const newArgs = {
...(args[0] ?? {}),
clerkOrgId,
} as FunctionArgs<Mutation>;

const mutation = originalMutation(...([newArgs] as OptionalRestArgs<Mutation>));
if (!optimisticUpdate) {
return mutation
}
return mutation.withOptimisticUpdate(optimisticUpdate)
},
[clerkOrgId, originalMutation, optimisticUpdate]
);
}
I'm using something like this ^^ I don't know what the AuthMutation type is, so it may need some tweaking
David Alonso
David AlonsoOP4mo ago
I had to add an await for TS to not complain about Property 'withOptimisticUpdate' does not exist on type 'Promise<FunctionReturnType<Mutation>>'.ts(2339) With this
const mutation = await originalMutation(...([newArgs] as OptionalRestArgs<Mutation>));
if (!optimisticUpdate) {
return mutation
}
return mutation.withOptimisticUpdate(optimisticUpdate)
const mutation = await originalMutation(...([newArgs] as OptionalRestArgs<Mutation>));
if (!optimisticUpdate) {
return mutation
}
return mutation.withOptimisticUpdate(optimisticUpdate)
and this front-end code:
const updateMultipleBlocks = useAuthMutation(
api.mutations.blocks.blocks.auth.updateMultipleBlockGridLayouts,
OptimisticUpdate.updateMultipleBlocksGridLayout
);
const updateMultipleBlocks = useAuthMutation(
api.mutations.blocks.blocks.auth.updateMultipleBlockGridLayouts,
OptimisticUpdate.updateMultipleBlocksGridLayout
);
there's no TS errors but when I run the mutation I get the following in the console: TypeError: mutation.withOptimisticUpdate is not a function
erquhart
erquhart4mo ago
Ah, yep, updated:
export function useAuthMutation<Mutation extends AuthFunction<"mutation">>(
name: Mutation,
optimisticUpdate: OptimisticUpdate<FunctionArgs<Mutation>>,
) {
const organization = useOrganization();
const clerkOrgId = organization?.organization?.id;
const originalMutation = useMutation(name);

return useCallback(
async (
...args: AuthArgsArray<Mutation>
): Promise<FunctionReturnType<Mutation>> => {
const newArgs = {
...(args[0] ?? {}),
clerkOrgId,
} as FunctionArgs<Mutation>;

const mutation = optimisticUpdate
? originalMutation
: originalMutation.withOptimisticUpdate(optimisticUpdate)

return mutation(...([newArgs] as OptionalRestArgs<Mutation>));
},
[clerkOrgId, originalMutation, optimisticUpdate]
);
}
export function useAuthMutation<Mutation extends AuthFunction<"mutation">>(
name: Mutation,
optimisticUpdate: OptimisticUpdate<FunctionArgs<Mutation>>,
) {
const organization = useOrganization();
const clerkOrgId = organization?.organization?.id;
const originalMutation = useMutation(name);

return useCallback(
async (
...args: AuthArgsArray<Mutation>
): Promise<FunctionReturnType<Mutation>> => {
const newArgs = {
...(args[0] ?? {}),
clerkOrgId,
} as FunctionArgs<Mutation>;

const mutation = optimisticUpdate
? originalMutation
: originalMutation.withOptimisticUpdate(optimisticUpdate)

return mutation(...([newArgs] as OptionalRestArgs<Mutation>));
},
[clerkOrgId, originalMutation, optimisticUpdate]
);
}
David Alonso
David AlonsoOP4mo ago
beautiful, that works 🔥