Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

ryangittings
Copy link
Collaborator

No description provided.

@ryangittings ryangittings requested a review from hunvreus March 12, 2025 12:01
@ryangittings ryangittings linked an issue Mar 12, 2025 that may be closed by this pull request
@ryangittings ryangittings changed the base branch from main to development March 16, 2025 09:24
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we leave the ThemeProvider as the top parent?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

} from "@dnd-kit/modifiers";
import { CSS } from "@dnd-kit/utilities";
import { ChevronLeft, GripVertical, Loader, Plus, Trash2 } from "lucide-react";
import { Blocker } from "../navigation-block";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolute path please. I would only use a relative path in a self contained module (e.g. field).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!


return (
<Form {...form}>
{isDirty && <Blocker />}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand the conditional on <Blocker />. When would <Bocker /> be falsy?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When there are no changes to save, we don't need to block navigation away? This is what this is doing.

Comment on lines +1 to +44
'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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this coming from and where is it used? I don't see it imported in your other commits..?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

</TooltipProvider>
</UserProvider>
</ThemeProvider>
<NavigationBlockerProvider>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Comment on lines +1 to +68
// 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;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did you get this solution from? Can you explain me what it does?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vercel/next.js#41934 (comment) - explains it there

@ryangittings ryangittings requested a review from hunvreus April 8, 2025 09:00
@hunvreus
Copy link
Member

Can you test against the latest version?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Don't discard unsaved changes or warn users when navigating away

2 participants