RadixUI Dialog with router.refresh() cause a flicker
Unanswered
PepeW posted this in #help-forum
PepeWOP
I have a RadixUI Dialog inside of which I have a form. When my form submission AND the
It's working as expected except for one thing: After the
Here's the code:
router.refresh() are done, I'm closing the modal.It's working as expected except for one thing: After the
router.refresh(), the modal close then re-open then re-close very fast causing a flicker.Here's the code:
"use client"
export function ChangeAddressForm({ addresses, setOpenModal }: ChangeAddressFormProps) {
const [isLoading, setIsLoading] = useState(false)
const [isSubmitDone, setIsSubmitDone] = useState(false)
const router = useRouter()
const [isPending, startTransition] = useTransition()
useEffect(() => {
if (isSubmitDone && !isPending) {
if (setOpenModal) setOpenModal(false)
}
}, [isPending, isSubmitDone, setOpenModal])
async function onSubmit(formValues: z.infer<typeof formSchema>) {
setIsLoading(true)
try {
await updateAddress.mutateAsync(formValues.newAddress)
startTransition(() => {
router.refresh()
})
setIsLoading(false)
setIsSubmitDone(true)
} catch {
setIsLoading(false)
return
}
setIsLoading(false)
}
return (
<>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="id"
render={({ field }) => (
<FormItem>
<FormControl>
<RadioGroup onValueChange={field.onChange} defaultValue={field.value}>
{addresses.items.map((address) => (
...
))}
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" isLoading={isLoading || isPending}>
Choose this address
</Button>
</form>
</Form>
</>
)
}27 Replies
@PepeW I have a RadixUI Dialog inside of which I have a form. When my form submission AND the `router.refresh()` are done, I'm closing the modal.
It's working as expected except for one thing: After the `router.refresh()`, the modal close then re-open then re-close very fast causing a flicker.
Here's the code:
javascript
"use client"
export function ChangeAddressForm({ addresses, setOpenModal }: ChangeAddressFormProps) {
const [isLoading, setIsLoading] = useState(false)
const [isSubmitDone, setIsSubmitDone] = useState(false)
const router = useRouter()
const [isPending, startTransition] = useTransition()
useEffect(() => {
if (isSubmitDone && !isPending) {
if (setOpenModal) setOpenModal(false)
}
}, [isPending, isSubmitDone, setOpenModal])
async function onSubmit(formValues: z.infer<typeof formSchema>) {
setIsLoading(true)
try {
await updateAddress.mutateAsync(formValues.newAddress)
startTransition(() => {
router.refresh()
})
setIsLoading(false)
setIsSubmitDone(true)
} catch {
setIsLoading(false)
return
}
setIsLoading(false)
}
return (
<>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="id"
render={({ field }) => (
<FormItem>
<FormControl>
<RadioGroup onValueChange={field.onChange} defaultValue={field.value}>
{addresses.items.map((address) => (
...
))}
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" isLoading={isLoading || isPending}>
Choose this address
</Button>
</form>
</Form>
</>
)
}
try comment out this
// useEffect(() => {
// if (isSubmitDone && !isPending) {
// if (setOpenModal) setOpenModal(false)
// }
// }, [isPending, isSubmitDone, setOpenModal])PepeWOP
If I do this the modal never closes by itself after the refresh is done because I'm not using
setOpenModal(false) anywhere in my componentthe modal should close after
router.refresh()?or not?
PepeWOP
Yes the modal should close after the
router.refresh()so you don't need to call
setOpenModal(false), right?PepeWOP
As stated is the NextJS doc, when doing a
router.refresh(), the states of the client components are not touched, rsulting is the modal staying open even after the router.refresh()router.refresh(): Refresh the current route. Making a new request to the server, re-fetching data requests, and re-rendering Server Components. The client will merge the updated React Server Component payload without losing unaffected client-side React (e.g. useState) or browser state (e.g. scroll position).could you show the code where the modal state is?
PepeWOP
"use client"
export function ModalChangeAddress({ addresses }: ModalChangeAddressProps) {
const [openModal, setOpenModal] = useState(false)
return (
<Dialog open={openModal} onOpenChange={setOpenModal}>
<DialogTrigger asChild>
<Button>Change address</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>
<span>My addresses</span>
<DialogClose>
<IoClose />
</DialogClose>
</DialogTitle>
<DialogDescription asChild>
<ChangeAddressForm
addresses={addresses}
setOpenModal={setOpenModal}
/>
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
)
}@PepeW javascript
"use client"
export function ModalChangeAddress({ addresses }: ModalChangeAddressProps) {
const [openModal, setOpenModal] = useState(false)
return (
<Dialog open={openModal} onOpenChange={setOpenModal}>
<DialogTrigger asChild>
<Button>Change address</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>
<span>My addresses</span>
<DialogClose>
<IoClose />
</DialogClose>
</DialogTitle>
<DialogDescription asChild>
<ChangeAddressForm
addresses={addresses}
setOpenModal={setOpenModal}
/>
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
)
}
async function onSubmit(formValues: z.infer<typeof formSchema>) {
setIsLoading(true)
try {
await updateAddress.mutateAsync(formValues.newAddress)
//startTransition(() => {
router.refresh()
//})
setIsLoading(false)
setIsSubmitDone(true)
} catch {
setIsLoading(false)
return
}
setIsLoading(false)
}try without
startTransition and call setOpenModalI know you need
isPending for the UI but try with it firstPepeWOP
If I don't call
router.refresh(), the data on the page will not be refreshed with the new data I just submitted@Ray ts
async function onSubmit(formValues: z.infer<typeof formSchema>) {
setIsLoading(true)
try {
await updateAddress.mutateAsync(formValues.newAddress)
//startTransition(() => {
router.refresh()
//})
setIsLoading(false)
setIsSubmitDone(true)
} catch {
setIsLoading(false)
return
}
setIsLoading(false)
}
try without `startTransition` and call `setOpenModal`
oh sorry, should call
router.refresh() therejust call it without
startTransitionPepeWOP
async function onSubmit(formValues: z.infer<typeof formSchema>) {
setIsLoading(true)
try {
await updateAddress.mutateAsync(formValues.newAddress)
router.refresh()
setIsLoading(false)
setOpenModal(false) // need this to close the modal
} catch {
setIsLoading(false)
return
}
setIsLoading(false)
}Do you mean like this ?
"use client"
export function ChangeAddressForm({ addresses, setOpenModal }: ChangeAddressFormProps) {
const [isLoading, setIsLoading] = useState(false)
const [isSubmitDone, setIsSubmitDone] = useState(false)
const router = useRouter()
const [isPending, startTransition] = useTransition()
useEffect(() => {
if (isSubmitDone && !isPending) {
if (setOpenModal) setOpenModal(false)
}
}, [isPending, isSubmitDone, setOpenModal])
async function onSubmit(formValues: z.infer<typeof formSchema>) {
setIsLoading(true)
try {
await updateAddress.mutateAsync(formValues.newAddress)
//startTransition(() => {
router.refresh()
//})
setIsLoading(false)
setIsSubmitDone(true)
} catch {
setIsLoading(false)
return
}
setIsLoading(false)
}
return (
<>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="id"
render={({ field }) => (
<FormItem>
<FormControl>
<RadioGroup onValueChange={field.onChange} defaultValue={field.value}>
{addresses.items.map((address) => (
...
))}
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" isLoading={isLoading || isPending}>
Choose this address
</Button>
</form>
</Form>
</>
)
}like this
PepeWOP
Ok so the modal closes after the submission but before the router.refresh()
So the modal closes, then I can see my previous address, then the refresh() is done, then my new address is displayed
@PepeW So the modal closes, then I can see my previous address, then the refresh() is done, then my new address is displayed
are the result you expecting if you don't call
setOpenModal?PepeWOP
I'm not sure to understand.
- If I don't call
- If I do what you just show (basically not using
- If I use
- If I don't call
setOpenModal(false), the modal never closes by itslef => Not what I want- If I do what you just show (basically not using
startTransition()), the modal closes after submission but before router.refresh(), resulting in the user seeing the ui change "magically" => Not what I want- If I use
startTransition(), the modal closes after the router.refresh() (good) but somehow I have this weird flickering of the modal => Not what I want@PepeW I'm not sure to understand.
- If I don't call `setOpenModal(false)`, the modal never closes by itslef => Not what I want
- If I do what you just show (basically not using `startTransition()`), the modal closes after submission but before router.refresh(), resulting in the user seeing the ui change "magically" => Not what I want
- If I use `startTransition()`, the modal closes after the `router.refresh()` (good) but somehow I have this weird flickering of the modal => Not what I want
I mean this
"use client"
export function ChangeAddressForm({ addresses, setOpenModal }: ChangeAddressFormProps) {
const [isLoading, setIsLoading] = useState(false)
const [isSubmitDone, setIsSubmitDone] = useState(false)
const router = useRouter()
const [isPending, startTransition] = useTransition()
// useEffect(() => {
// if (isSubmitDone && !isPending) {
// if (setOpenModal) setOpenModal(false)
// }
// }, [isPending, isSubmitDone, setOpenModal])
async function onSubmit(formValues: z.infer<typeof formSchema>) {
setIsLoading(true)
try {
await updateAddress.mutateAsync(formValues.newAddress)
startTransition(() => {
router.refresh()
})
setIsLoading(false)
setIsSubmitDone(true)
} catch {
setIsLoading(false)
return
}
setIsLoading(false)
}
return (
<>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="id"
render={({ field }) => (
<FormItem>
<FormControl>
<RadioGroup onValueChange={field.onChange} defaultValue={field.value}>
{addresses.items.map((address) => (
...
))}
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" isLoading={isLoading || isPending}>
Choose this address
</Button>
</form>
</Form>
</>
)
}does the modal close?
PepeWOP
No :/
@PepeW No :/
oh well, could you use server action instead of router.refresh?
PepeWOP
I've not used them in my app yet, maybe it's the right moment ^^
@PepeW I've not used them in my app yet, maybe it's the right moment ^^
yeah try it, you could use
revalidatePath with server action so you don't need to use router.refreshPepeWOP
Ok I'll give it a go, thank you !