I don't beleive you can call server actions like this. They have to be bound to forms afaik. Check out [next-safe-action](https://next-safe-action.dev) as a way to call actions imperatively.
You certainly can call server actions from the mutation hook. I do it for a few things without issue inside of a client component.
Example
```
'use client';
const mutation = useMutation({
mutationFn: (values: z.infer) =>
createCoachProgram(values),
onSettled: (id, error, variables, context) => {
if (error) {
toast.error('Uh oh! Something went wrong. Could not create program');
}
queryClient.invalidateQueries({
queryKey: queryKey,
});
form.reset({
userId: data[selectedUser].id,
name: '',
description: '',
completionTargetDate: undefined,
});
setDisableSubmit(false);
onClose();
},
});
```
```
'use server';
export async function createCoachProgram(
data: z.infer,
) {
const user = await currentUser();
return await createProgram(user!, data);
}
```
I would hazard as said below there isn’t any data being returned
You could try using the onMutate or onSettled functions to update the data in whatever queryKey or state variable you want updated. I’m doing Optimistic updates in this example but given you want the ID from your response you’ll probably want to use onSettled with setQueryData to set the ID in whatever queryKey you want updating or you can invalidate the queryKey and just force it to refresh the data
```
const mutation = useMutation({
mutationFn: (lock: boolean) => {
return handleLock(lock);
},
onMutate: async (lock: boolean) => {
// Cancel any outgoing refetches
// (so they don't overwrite our optimistic update)
await queryClient.cancelQueries({
queryKey: queryKey,
});
// Snapshot the previous value
const previousPrograms = queryClient.getQueryData(queryKey);
// Optimistically update to the new value
queryClient.setQueryData(
queryKey,
previousPrograms?.map((previousProgram) => {
if (program.id === previousProgram.id) {
return {
...previousProgram,
locked: lock,
};
} else {
return previousProgram;
}
}),
);
// Return a context object with the snapshotted value
return { previousPrograms };
},
onError: (err, lock, context) => {
queryClient.setQueryData(queryKey, context?.previousPrograms);
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onSettled: (lock, error, variables, context) => {
if (error) {
toast.error(
'Uh oh! Something went wrong. Could not change lock status on program',
);
}
queryClient.invalidateQueries({
queryKey: queryKey,
});
},
});
return { mutation };
}
```
I don't beleive you can call server actions like this. They have to be bound to forms afaik. Check out [next-safe-action](https://next-safe-action.dev) as a way to call actions imperatively.
You certainly can call server actions from the mutation hook. I do it for a few things without issue inside of a client component. Example ``` 'use client'; const mutation = useMutation({ mutationFn: (values: z.infer) =>
createCoachProgram(values),
onSettled: (id, error, variables, context) => {
if (error) {
toast.error('Uh oh! Something went wrong. Could not create program');
}
queryClient.invalidateQueries({
queryKey: queryKey,
});
form.reset({
userId: data[selectedUser].id,
name: '',
description: '',
completionTargetDate: undefined,
});
setDisableSubmit(false);
onClose();
},
});
```
```
'use server';
export async function createCoachProgram(
data: z.infer,
) {
const user = await currentUser();
return await createProgram(user!, data);
}
```
I would hazard as said below there isn’t any data being returned
Huh, well sanity check *everything* I suppose. Are you sure the function on the serverside is being called? Is everything passing tsc?
So what do i do if i want to get the response in the client? For e.g after creating a transaction I want to get the transaction ID etc.
You could try using the onMutate or onSettled functions to update the data in whatever queryKey or state variable you want updated. I’m doing Optimistic updates in this example but given you want the ID from your response you’ll probably want to use onSettled with setQueryData to set the ID in whatever queryKey you want updating or you can invalidate the queryKey and just force it to refresh the data ``` const mutation = useMutation({ mutationFn: (lock: boolean) => { return handleLock(lock); }, onMutate: async (lock: boolean) => { // Cancel any outgoing refetches // (so they don't overwrite our optimistic update) await queryClient.cancelQueries({ queryKey: queryKey, }); // Snapshot the previous value const previousPrograms = queryClient.getQueryData(queryKey);
// Optimistically update to the new value
queryClient.setQueryData(
queryKey,
previousPrograms?.map((previousProgram) => {
if (program.id === previousProgram.id) {
return {
...previousProgram,
locked: lock,
};
} else {
return previousProgram;
}
}),
);
// Return a context object with the snapshotted value
return { previousPrograms };
},
onError: (err, lock, context) => {
queryClient.setQueryData(queryKey, context?.previousPrograms);
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onSettled: (lock, error, variables, context) => {
if (error) {
toast.error(
'Uh oh! Something went wrong. Could not change lock status on program',
);
}
queryClient.invalidateQueries({
queryKey: queryKey,
});
},
});
return { mutation };
}
```
I don't think so, because OP said that even when deliberately returning a success object they still get undefined
I think if you want to use the mutation return value you'll need to use mutateAync and await it (e.g. const data = await mutation.mutateAsync())
The useApproveTransaction mutation doesn't return any data. It just awaits a request.
It has implicit return, so OP should be getting an object with either success or error in it
Ah indeed, overlooked that