Closed andresgutgon closed 3 months ago
I don't know what more details I can give. Basically moving back to older versions the code execute and redirect to /
Hi, happy to help here. Checking out the code now
Wow! awesome response time!
Are you using useServerAction
on the client to get the isPending
state?
Yes
Are you possibly redirecting to the same page ? IE, is this form on /
?
No, it's on another page: /signin/onboarding
. Here is the form
Just for background, the new version uses useTransition
to wait for the redirect to finish. Before it would be awkward because redirect was still going on but isPending
would go to false. In your case it would mean the sign in button would be enabled but in the browser is still loading. Now, isPending
remains true until revalidatePath
or redirect
are done.
Just for background, the new version uses useTransition to wait for the redirect to finish. So before it would be awkward because redirect was still going on but isPending would go to false. Now, isPending remains true until revalidatePath or redirect are done.
So I can't do the redirect
on the server action?
Anyway it fails even if there is an error and the redirect
is not called
You can do redirect on the server action, the new updates should have fixed weird behavior with it and the isPending
state. There is also #97 that seems to be the same issue, but I haven't been able to replicate locally.
Yes, that issue looks similar. I have to go. I'll keep investigating. My code is open so feel free to check if you want: https://github.com/andresgutgon/readfort/pull/10
This lib looks awesome. I have a bit more of feedback but first I'll try to fix this one
I think I know the issue, will report back in ~ 10 min
By the way, .input() supports async parsing so in theory you don't need the validateUniqueUsername
function and can just do:
[update] I was wrong, user
will not be available in this scope. Will think about how to enable this
export const onboarding = authProcedure
.createServerAction()
.input(
input.extend({
username: input.shape.username.refine(async (username) => {
return await checkUsername(username, user)
},
{ message: 'Username is already taken.' }
),
}),
{ type: 'formData' }
)
.output(input)
.handler(async ({ input, ctx: { user } }) => {
await validateUniqueUsername({ data: input, user })
const result = await finishOnboarding({ user, data: input })
const value = result.unwrap()
// NOTE: This ends in JWT callback in auth/index.ts
await updateSession({
user: { name: value.name, username: value.username },
})
redirect('/')
})
I think the issue here is you can't use useServerAction
execute method as the action
in a form. We have a lot of documentation about forms here. I will add this warning to the docs. Our bad on not already including this and creating confusion.
You have two options:
1) Run execute
via the onSubmit handler
2) If you want to support progressive enhancement, you can use useActionState
. We have docs on this as well. Although, to keep your toast showing you will need to create a custom action function.
By the way, .input() supports async parsing so in theory you don't need the validateUniqueUsername function and can just do:
yes! that was another piece of feedback. I can't use your example. my validation use user
which is in the context of the action. I didn't found a way to pass context to input
Oh true! Didn't catch that. Good point, will think about how to help in that situation. I guess input can also be a function that takes in the ctx
and returns the schema.
I think the issue here is you can't use useServerAction execute method as the action in a form.
Sorry I'm trying to understand why using in this way doesn't work. it worked in the old version. Would be awesome to understand what changed
The change was that execute now internally wraps the action in startTransition
from the useTransition
hook. I just did some testing with this and its pretty interesting why it isn't working.
Basically, the useServerAction
execute function waits for isPending
from the transition to be set to false before it resolves. However, when you use <form action={execute} />
weird case arises:
isPending
from the transition will only be false once the form's action resolves. However, the form can't resolve until isPending
is false. So this creates a state where isPending
is stuck on true.
"use client";
import { useCallback, useEffect, useRef, useTransition } from "react";
import { zsaAction } from "./action";
export default function useExecute() {
const [isPending, startTransition] = useTransition();
const resolveRef = useRef<any>(undefined);
const execute = useCallback(async (formData: FormData) => {
return await new Promise((resolve) => {
resolveRef.current = resolve;
startTransition(() => {
zsaAction(formData);
});
});
}, []);
useEffect(() => {
if (isPending) return;
// resolve once the action is done
resolveRef.current?.();
return () => {
resolveRef.current = undefined;
};
}, [isPending]);
return {
isPending,
execute,
};
}
We can't do zsaAction(formData).then(resolve)
because the transition still may be running and the only way we have found to figure out when the transition actually ends is to useEffect
#91. This creates a lock state with the form.
Hopefully this makes sense! Going to try to think of a solution so maybe using it with execute is possible.
Hi, wanted to check back in and report an update in the latest version of zsa-react@0.1.5
. You can now use useServerAction
alongside <form action={...} />
using the new function executeFormAction
Here are the docs
Working on the input functions next, will update when that is done.
executeFormAction
it works wonderfully
Looking forward the input
context : )
What is the error?
Hi. I'm was working on a project using older versions and I moved to latest to have new ZSAError and the code that's working in older versions now leaves my UI loading
This is my action