do something once server action is completed

Is there a way to achieve this (see title)? I’m using useTransition to start the action and need to close my modal once the action is completed successfully.
R
rocawear321d ago
https://react.dev/reference/react/useTransition
const [isPending, startTransition] = useTransition()
const [isPending, startTransition] = useTransition()
can you take it from the isPending?
P
Perfect321d ago
@ruhap I am using ispending, but how do I close the modal when I know isPending goes from true to false? I might be drawing a blank if it’s obvious haha
R
rocawear321d ago
how do you open it? using state? so change state
P
Perfect321d ago
@ruhap yeah I know how to actually open and close it, it’s more of WHEN/WHERE do I close it? I want it to auto close once the action has completed
R
rocawear321d ago
And you have tried?
const [isPending, startTransition] = useTransition()

if(!isPending) {
hideModal()
}
const [isPending, startTransition] = useTransition()

if(!isPending) {
hideModal()
}
D
deforestor321d ago
Are you using React Query? Because if you are, this should be extremely trivial
P
Perfect321d ago
I am not @deforestor Just straight up server actions So when it’s initially not pending (user hasn’t submitted form inside modal yet) won’t it close? The form is inside the modal if that wasn’t clear my bad
R
rocawear321d ago
why would it if you havent started transition
D
deforestor321d ago
Right, that explains the confusion. I guess you could check for the status code, if it's 200, you set modal open to false on useEffect But just so you know, adding React Query is no more than about 5 lines and would make everything else easy and also wouldn't break what you already have
R
rocawear321d ago
console.log(isPending) and think from there
P
Perfect321d ago
I’m assuming that’s gonna show False (modal closed) True (action happening) False (action complete) I’ll take a look tho, thx I wasn’t sure if it would help with server actions yet but I’ll also consider adding it, thx
D
deforestor321d ago
Ah, forget about it. I haven't checked the new server stuff yet, thought you were onto something else
P
Perfect321d ago
Not sure if this is possible , need a callback that does not seem to exist.
D
deathandtaxes321d ago
Can you add a rough snippet of what your logic so far looks like? I get the idea, but there are many ways you could be instantiating the modal, for instance
P
Perfect321d ago
@deathandtaxes Yeah sure! So, I control if the modal is open/closed using an isOpen boolean state. According to the NextJS docs, if a server action mutates data and calls redirect, revalidatePath, or revalidateTag , you are supposed to use the useTransition hook to start the action. (not sure why exactly) This hook provides us with an isPending boolean that represents if the action is running and a startTransition function to start it. The form inside of the modal should be disabled and show a spinner indicating when the server action is running. (I am ok with the UI being blocked here during this time). My confusion is how to close the modal once the action is complete. I have it working as expected by just using it like an async function (see screenshot), but apparently its not the correct way. I am using react-hook-form and shadcn for my form validation so there is an onSubmit function I have available.
P
Perfect321d ago
seemingly working version but not correct way?
J
Josh321d ago
can you give the full component
P
Perfect321d ago
Yep
P
Perfect321d ago
P
Perfect321d ago
That is the form
J
Josh321d ago
aight lemme cook
P
Perfect321d ago
export default function NewLink({
createLink,
}: {
createLink: ({ title, url }: { title: string; url: string }) => Promise<void>;
}) {
const [isOpen, setIsOpen] = useState(false);
const { user, isLoaded } = useUser();

if (!isLoaded || !user || !user.username) {
return null;
}

return (
<Dialog open={isOpen} onOpenChange={(open) => setIsOpen(open)}>
<DialogTrigger className={buttonVariants({ variant: "outline" })}>
<LinkIcon className="mr-2 h-4 w-4" /> New Link
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Create New Link</DialogTitle>
<DialogDescription>
Add a new link to your page. Upon successful creation, it will be
publicly visible to anyone who visits{" "}
<Link
target="_blank"
href={`/${user.username}`}
className={cn(
buttonVariants({ variant: "link" }),
"inline-flex h-auto p-0"
)}
>
lone.link/{user?.username}
</Link>
</DialogDescription>
</DialogHeader>
<NewLinkForm createLink={createLink} setIsOpen={setIsOpen} />
</DialogContent>
</Dialog>
);
}
export default function NewLink({
createLink,
}: {
createLink: ({ title, url }: { title: string; url: string }) => Promise<void>;
}) {
const [isOpen, setIsOpen] = useState(false);
const { user, isLoaded } = useUser();

if (!isLoaded || !user || !user.username) {
return null;
}

return (
<Dialog open={isOpen} onOpenChange={(open) => setIsOpen(open)}>
<DialogTrigger className={buttonVariants({ variant: "outline" })}>
<LinkIcon className="mr-2 h-4 w-4" /> New Link
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Create New Link</DialogTitle>
<DialogDescription>
Add a new link to your page. Upon successful creation, it will be
publicly visible to anyone who visits{" "}
<Link
target="_blank"
href={`/${user.username}`}
className={cn(
buttonVariants({ variant: "link" }),
"inline-flex h-auto p-0"
)}
>
lone.link/{user?.username}
</Link>
</DialogDescription>
</DialogHeader>
<NewLinkForm createLink={createLink} setIsOpen={setIsOpen} />
</DialogContent>
</Dialog>
);
}
That is the modal
J
Josh321d ago
and createLink is a server action?
P
Perfect321d ago
Yep
J
Josh321d ago
this might actually be pretty simple. See if it still works when you do
<form onSubmit={() => startTransition(() => form.handleSubmit(onSubmit))} className="space-y-4">
<form onSubmit={() => startTransition(() => form.handleSubmit(onSubmit))} className="space-y-4">
mmm wait youll still need to pass the form params
P
Perfect321d ago
Hmm I think I see what ur doing Didn’t think of that
J
Josh321d ago
<form onSubmit={(e) => startTransition(() => form.handleSubmit(onSubmit))(e)} className="space-y-4">
<form onSubmit={(e) => startTransition(() => form.handleSubmit(onSubmit))(e)} className="space-y-4">
i think? try both On why we use startTransition: if you arent updating the client, you dont need to. Updating the client, as you outlined, includes doing any of redirect, revalidatePath, or revalidateTag.. If we ARE updating state, and therefore updating the client, this inherently means we are manipulating the client state in some way, since our UI is changing after the server action is complete. In react, we use startTransition to start non-blocking state updates. So, in theory, if you use a server action & dont wrap it in startTransition, your UI would completely block untill the server action is done. Since this is not what we want, we tell react to let the client continue to update and move around while the server is processing the request by wrapping it in the startTransition.
P
Perfect321d ago
Ok that’s very clear, unless I missed it this would be extremely helpful in the official docs. I haven’t tried what you sent yet. Will do in a few min
J
Josh321d ago
do be warned, this is just me reverse engineering how next is working internally on the fly so im not 100% sure this is exactly whats happening, but from what i can tell, looks right
P
Perfect321d ago
From the research I’ve put together that all checks out as well
J
Josh321d ago
im actually gonna drop this in one of the main chats and see what everyone thinks eh actually im just gonna open a whole new question
P
Perfect321d ago
P
Perfect321d ago
So I guess the UI is being blocked entirely, but it’s what I expect so idk
J
Josh321d ago
did the new approaches work?
P
Perfect321d ago
On mobile atm, going to give it a try in a sec
J
Josh321d ago
kk
P
Perfect321d ago
@Josh alright so neither working rn, getting a This expression is not callable.
J
Josh321d ago
ah for when you pass e thats fine, just remove the e
P
Perfect321d ago
yeah thats with the e one
J
Josh321d ago
that actually makes sense
P
Perfect321d ago
without e, I get
J
Josh321d ago
ah wait i see try this
<form onSubmit={() => startTransition(form.handleSubmit(onSubmit))} className="space-y-4">
<form onSubmit={() => startTransition(form.handleSubmit(onSubmit))} className="space-y-4">
err there, try that
P
Perfect321d ago
Ok no errors what did u change oh since form.handleSubmit returns a function no need to wrap again Let me give it a try Ok we are getting immediate page reload assuming because of preventdefault not happening
J
Josh321d ago
yep yep so
P
Perfect321d ago
just do that part myself maybe?
J
Josh321d ago
<form onSubmit={(e) => {e.preventDefault();startTransition(form.handleSubmit(onSubmit))}} className="space-y-4">
<form onSubmit={(e) => {e.preventDefault();startTransition(form.handleSubmit(onSubmit))}} className="space-y-4">
P
Perfect321d ago
alrightt I think thats working! let me do a few more
P
Perfect321d ago
P
Perfect321d ago
Looks like its gooood
J
Josh321d ago
huuuuge
P
Perfect321d ago
so huge, I have been stuck on this for a bit much much appreciated one more thing kinda unrelated
P
Perfect321d ago
P
Perfect321d ago
There is a layout shift when the revalidation is happening I believe, any idea on that? using next font
J
Josh321d ago
are you using tailwind
P
Perfect321d ago
yea
J
Josh321d ago
lemme see your tailwind config and your app.tsx file
P
Perfect321d ago
import "@/styles/globals.css";
import { ClerkProvider } from "@clerk/nextjs";
import type { Metadata } from "next";
import Container from "@/components/Container";
import Footer from "@/components/Footer";
import Header from "@/components/Header";
import { fontMono, fontSans } from "@/lib/fonts";
import { cn } from "@/lib/utils";

export const metadata: Metadata = {
title: "Lone Link",
description: "Lone Link is a blazingly fast link in bio tool.",
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className="dark">
<ClerkProvider>
<body
className={cn(
fontSans.variable,
fontMono.variable,
"flex min-h-screen flex-col font-sans"
)}
>
<Header />
<Container className="my-10">{children}</Container>
<Footer />
</body>
</ClerkProvider>
</html>
);
}
import "@/styles/globals.css";
import { ClerkProvider } from "@clerk/nextjs";
import type { Metadata } from "next";
import Container from "@/components/Container";
import Footer from "@/components/Footer";
import Header from "@/components/Header";
import { fontMono, fontSans } from "@/lib/fonts";
import { cn } from "@/lib/utils";

export const metadata: Metadata = {
title: "Lone Link",
description: "Lone Link is a blazingly fast link in bio tool.",
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className="dark">
<ClerkProvider>
<body
className={cn(
fontSans.variable,
fontMono.variable,
"flex min-h-screen flex-col font-sans"
)}
>
<Header />
<Container className="my-10">{children}</Container>
<Footer />
</body>
</ClerkProvider>
</html>
);
}
P
Perfect321d ago
I assume you mean the root layout? There are both
J
Josh321d ago
and your @/lib/fonts
P
Perfect321d ago
Ah right
import {
Inter as FontSans,
JetBrains_Mono as FontMono,
} from "next/font/google";

export const fontSans = FontSans({
subsets: ["latin"],
variable: "--font-sans",
});

export const fontMono = FontMono({
subsets: ["latin"],
variable: "--font-mono",
});
import {
Inter as FontSans,
JetBrains_Mono as FontMono,
} from "next/font/google";

export const fontSans = FontSans({
subsets: ["latin"],
variable: "--font-sans",
});

export const fontMono = FontMono({
subsets: ["latin"],
variable: "--font-mono",
});
J
Josh321d ago
wait are you in app dir or pages
P
Perfect321d ago
app dir
J
Josh321d ago
oh i se i have my clerk provider outside the html
P
Perfect321d ago
Oh I could probably do that, I think I saw it in the clerk docs like I have it but dont think it matters
J
Josh321d ago
the only thing i could imagine is removing the ...fontFamily.sans, i dont have that in my tailwind config and im also using those fonts and i dont have artifacts when serveractions render it could also be that on revalidation "Lone Link" is straight up gone
P
Perfect321d ago
Yeah its quite odd I have no idea why thats happening
J
Josh321d ago
id have to inspect your actual repo to get an idea on that, however im guessing that 'Lone Link" is in a client compoent, and that text is expanding the box bigger than what its SSR height is so when it refreshes, it re-executes the react, and then readjusts its size when it loads in ^ i just had this same issue, you can fix it pretty easily with max-height just do max-h-[whatever]
P
Perfect321d ago
I have an explicit height on it rn actually
J
Josh321d ago
can you see your font flickering during load? if not, then its not tailwind and it would be some CSS somewhere
P
Perfect321d ago
nope only during that revalidation hmm also header is server comp Oh wait "Lone Link" is a Next link so that is probably a client comp if I had to guess
J
Josh321d ago
then its server iirc (mine dont flicker)
P
Perfect321d ago
P
Perfect321d ago
No flickers on reloads, I will have to see what makes the revalidation any different
J
Josh321d ago
ah, i can confirm its your tailwind config if you play back that video super slow, you can see the font changes
P
Perfect321d ago
The last one I just sent?
J
Josh321d ago
the one i replied
P
Perfect321d ago
Oh right
J
Josh321d ago
try using
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter',
display: 'swap',
});
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter',
display: 'swap',
});
theme: {
extend: {
fontFamily: {
default: ['var(--font-inter)'],
mono: ['var(--font-roboto-mono)'],
},
},
},
theme: {
extend: {
fontFamily: {
default: ['var(--font-inter)'],
mono: ['var(--font-roboto-mono)'],
},
},
},
P
Perfect321d ago
P
Perfect321d ago
damn same thing
J
Josh321d ago
I dont see it in this
P
Perfect321d ago
video didnt catch it
J
Josh321d ago
hahaha
P
Perfect321d ago
lol I swear it happened its so quick what is display swap?
J
Josh321d ago
not 100%, its in here tho
J
Josh321d ago
Optimizing: Fonts
Optimize your application's web fonts with the built-in next/font loaders.
P
Perfect321d ago
P
Perfect321d ago
No idea what this means lol
J
Josh321d ago
same hahahah
P
Perfect321d ago
They use swap everywhere in docs so I will roll with it haha Maybe I am using revalidate wrong I passed "/dashboard" to it since thats the path of the page containing the data I need to refresh, thats good right?
J
Josh321d ago
i believe so thats what i do
P
Perfect321d ago
hmm and you dont get this with ur server actions?
J
Josh321d ago
nah mine run fine
P
Perfect321d ago
hmm weird 🤷‍♂️
P
Perfect321d ago
I can replicate it by toggling the font-mono variable
P
Perfect321d ago
J
Josh321d ago
are you in a docker container by chance
P
Perfect321d ago
Nope
J
Josh321d ago
hm its something with tailwind for sure
P
Perfect321d ago
Yeah I agree, I’m gonna play around with how I’m setting the font up Maybe the variable is not instantly ready or something
Want results from more Discord servers?
Add your server
More Posts
How to use discord.js in Nextjs app dirHello, I am trying to use discord.js in my app but getting alot of errors, is this even possible?: `best way to fetch data in nextjsI was wondering what's the best way at the moment to query for external apis in NextJS? I know that @next/font turbo t3You might be using incompatible version of `@next/font` (13.4.4) and `next` (13.1.6). what version sTypescript issue causing trpc functions to not have type checkingHey, I'm in a create-t3-turbo app and have been banging my head against the wall trying to figure ouClerk middleware breaks Discord URL preview (embed)Anyone know how to fix this ...?T3 env lint error on Github CIRun npm run lint > chirp@0.1.0 lint > next lint ❌ Invalid environment variables: { PUSHER_APP_IDT3 Expo - react-native-gesture-handlerAnyone used this package with t3 turbo on expo app? I'm getting a package not found error on compileServer side caching with Next APIUnsure if this pattern is common, or if I am approaching this incorrectly. I am using an api in my [vite-plugin-sass] Unexpected token u in JSON at position 0 errorI just migrated my react sass application from webpack to Vite but I haven’t been able to build sincuseInfiniteQueryBug - queries to the end of pageI set up infinite scroll using the infinite query function in trpc. I have this bug right when the OpenId Connect ProviderHey Guys, im trying to implement a custom oidc auth provider, i've checked the Auth.js / NextAuth doruntimeEnv property in env.mjs in create-t3-appHey all, Was playing around entering some vars in the `.env` file and noticed that my app was builESLintrc.cjs config setupUnder Create T3 I setup ESLint in rules with: ``` rules: { '@typescript-eslint/no-unsafe-member-acce/app dir slower than pages?Hey, is the /app dir actually slower than the regular pages dir from next 12? If so, is it even a pVercel - SubdomainsHello hello, I wanted to ask whether there's a way for me to set-up two different projects (which arT3 Env Not Showing Type ErrorsHello, I have the following `env.mjs` file but there is no type error be shown in my IDE even thoughMy prisma var is any typeHello, i use the create t3 turbo repo and the prisma variable is any : ```ts import { PrismaClientTypeError: Cannot read properties of undefined (reading 'status')What am I doing wrong? Nextjs: `^13.2.4` Nodejs: `18.5.0`Stuck with a TS error for `Object.keys` 😞Hello all, got a pretty nasty issue that I need to fix otherwise I will not be able to move on with SSR with NextJs layouts (page dir)So I have a shared layout in my app which looks like this ```js const Page: NextPageWithLayout = ()