Add blocker to prevent changes being lost#197
Add blocker to prevent changes being lost#197ryangittings wants to merge 3 commits intopages-cms:developmentfrom
Conversation
There was a problem hiding this comment.
Can't we leave the ThemeProvider as the top parent?
components/entry/entry-form.tsx
Outdated
| } from "@dnd-kit/modifiers"; | ||
| import { CSS } from "@dnd-kit/utilities"; | ||
| import { ChevronLeft, GripVertical, Loader, Plus, Trash2 } from "lucide-react"; | ||
| import { Blocker } from "../navigation-block"; |
There was a problem hiding this comment.
Absolute path please. I would only use a relative path in a self contained module (e.g. field).
|
|
||
| return ( | ||
| <Form {...form}> | ||
| {isDirty && <Blocker />} |
There was a problem hiding this comment.
Not sure I understand the conditional on <Blocker />. When would <Bocker /> be falsy?
There was a problem hiding this comment.
When there are no changes to save, we don't need to block navigation away? This is what this is doing.
| 'use client'; | ||
| import { startTransition } from 'react'; | ||
| import NextLink from 'next/link'; | ||
| import { useRouter } from 'next/navigation'; | ||
| import { useIsBlocked } from './navigation-block'; | ||
|
|
||
| /** | ||
| * A custom Link component that wraps Next.js's next/link component. | ||
| */ | ||
| export function Link({ | ||
| href, | ||
| children, | ||
| replace, | ||
| ...rest | ||
| }: Parameters<typeof NextLink>[0]) { | ||
| const router = useRouter(); | ||
| const isBlocked = useIsBlocked(); | ||
|
|
||
| return ( | ||
| <NextLink | ||
| href={href} | ||
| onClick={(e) => { | ||
| e.preventDefault(); | ||
|
|
||
| // Cancel navigation | ||
| if (isBlocked && !window.confirm('Do you really want to leave?')) { | ||
| return; | ||
| } | ||
|
|
||
| startTransition(() => { | ||
| const url = href.toString(); | ||
| if (replace) { | ||
| router.replace(url); | ||
| } else { | ||
| router.push(url); | ||
| } | ||
| }); | ||
| }} | ||
| {...rest} | ||
| > | ||
| {children} | ||
| </NextLink> | ||
| ); | ||
| } No newline at end of file |
There was a problem hiding this comment.
Where is this coming from and where is it used? I don't see it imported in your other commits..?
There was a problem hiding this comment.
components/providers.tsx
Outdated
| </TooltipProvider> | ||
| </UserProvider> | ||
| </ThemeProvider> | ||
| <NavigationBlockerProvider> |
There was a problem hiding this comment.
Can we not leave the ThemeProvider as the top parent? Can't remember it clearly, but pretty sure ThemeProvider had to be top parent (you can check the shadcn/ui documentation).
| // components/navigation-block.tsx | ||
| 'use client'; | ||
|
|
||
| import { | ||
| Dispatch, | ||
| SetStateAction, | ||
| createContext, | ||
| useContext, | ||
| useEffect, | ||
| useState, | ||
| } from 'react'; | ||
|
|
||
| const NavigationBlockerContext = createContext< | ||
| [isBlocked: boolean, setBlocked: Dispatch<SetStateAction<boolean>>] | ||
| >([false, () => { }]); | ||
|
|
||
| export function NavigationBlockerProvider({ | ||
| children, | ||
| }: { | ||
| children: React.ReactNode; | ||
| }) { | ||
| // [isBlocked, setBlocked] | ||
| const state = useState(false); | ||
| return ( | ||
| <NavigationBlockerContext.Provider value={state}> | ||
| {children} | ||
| </NavigationBlockerContext.Provider> | ||
| ); | ||
| } | ||
|
|
||
| export function useIsBlocked() { | ||
| const [isBlocked] = useContext(NavigationBlockerContext); | ||
| return isBlocked; | ||
| } | ||
|
|
||
| export function Blocker() { | ||
| const [isBlocked, setBlocked] = useContext(NavigationBlockerContext); | ||
| useEffect(() => { | ||
| setBlocked(() => { | ||
| return true; | ||
| }); | ||
| return () => { | ||
| setBlocked(() => { | ||
| return false; | ||
| }); | ||
| }; | ||
| }, [isBlocked, setBlocked]); | ||
| return null; | ||
| } | ||
|
|
||
| export function BlockBrowserNavigation() { | ||
| const isBlocked = useIsBlocked(); | ||
| useEffect(() => { | ||
| console.log({ isBlocked }); | ||
| if (isBlocked) { | ||
| const showModal = (event: BeforeUnloadEvent) => { | ||
| event.preventDefault(); | ||
| }; | ||
|
|
||
| window.addEventListener('beforeunload', showModal); | ||
| return () => { | ||
| window.removeEventListener('beforeunload', showModal); | ||
| }; | ||
| } | ||
| }, [isBlocked]); | ||
|
|
||
| return null; | ||
| } |
There was a problem hiding this comment.
Where did you get this solution from? Can you explain me what it does?
There was a problem hiding this comment.
vercel/next.js#41934 (comment) - explains it there
|
Can you test against the latest version? |
No description provided.