remix-run / react-router

Declarative routing for React
https://reactrouter.com
MIT License
52.91k stars 10.25k forks source link

[Bug]: Uncaught Error: useNavigate() may be used only in the context of a <Router> component. #12099

Open Flaain opened 6 hours ago

Flaain commented 6 hours ago

What version of React Router are you using?

6.22.3

Steps to Reproduce

I know this error happen cuz of calling hook outside of router context. But it's actually inside context.

Here is my main.tsx:

ReactDOM.createRoot(document.getElementById('root')!).render(
    <React.StrictMode>
        <Providers>
            <RouterProvider router={router} />
        </Providers>
    </React.StrictMode>
);

this is my router:

export const router = createBrowserRouter([
    { element: baseLayout, children: pages },
    {
        ...AuthPage,
        element: (
            <Guard type='auth' fallback={<ScreenLoader />}>
                {AuthPage.element}
            </Guard>
        )
    }
]);

baseLayout:

export const baseLayout = (
    <Guard type='guest' fallback={<ScreenLoader />}>
        <React.Suspense fallback={<ScreenLoader />}>
            <LayoutProvider>
                <Layout />
            </LayoutProvider>
        </React.Suspense>
    </Guard>
);

layout:

export const Layout = () => {
    const isSheetOpen = useLayout((state) => state.isSheetOpen);

    return (
        <main className='flex h-full dark:bg-primary-dark-200 w-full'>
            <Toaster />
            {isSheetOpen &&
                ReactDOM.createPortal(
                    <Sheet withHeader={false} closeHandler={() => useLayout.setState({ isSheetOpen: false })}>
                        <React.Suspense fallback={<LayoutSheetSkeleton />}>
                            <LayoutSheetView />
                        </React.Suspense>
                    </Sheet>,
                    document.querySelector('#modal-root')!
                )}
            <SidebarProvider>
                <Sidebar />
            </SidebarProvider>
            <Outlet />
        </main>
    );
};

and finally this is my LayoutSheet where error appears when i call create group:

export const LayoutSheet = () => {
    // another code

    return (
              //another jsx
          <li className='first:my-4 first:py-1 first:border-y dark:first:border-primary-dark-50 first:border-primary-dark-200'>
              <Button
                  variant='ghost'
                  className='rounded-none flex items-center justify-start gap-4 w-full'
                  onClick={() =>
                      onSheetAction({
                          withHeader: false,
                          content: (
                              <CreateGroupProvider>
                                  <CreateGroup />
                              </CreateGroupProvider>
                          ),
                          bodyClassName: 'w-[450px] p-3 h-auto'
                      })
                  }
              >
                  <Users className={listIconStyle} />
                  <Typography weight='medium'>New Group</Typography>
              </Button>
          </li>
    );
};

So this is CreateGroupProvider where i call useNavigate and get error cuz of it:


export const CreateGroupProvider = ({ children }: { children: React.ReactNode }) => {
    // another code
    const navigate = useNavigate();

    return (
        <CreateGroupContext.Provider value={value}>
            {children}
        </CreateGroupContext.Provider>
    )
}
``

### Expected Behavior

useNavigate should work

### Actual Behavior

useNavigate doesn't work
Flaain commented 3 hours ago

p.s. i upgraded version to latest ( 6.26.2 ) and error still appears. I really have no idea why. p.p.s. It's looks like i loss context somehow cuz i can useNavigate inside Layout and LayoutSheet but inside CreateGroupProvider it throw error

Flaain commented 3 hours ago

For temporary use i decided to provide navigate function as a prop to CreateGroupProvider. It looks ugly but it works and i really don't know what to do with this error.


export const LayoutSheet = () => {
    const onOpenModal = useModal((state) => state.actions.onOpenModal);
    const navigate = useNavigate();
    const profile = useProfile((state) => state.profile);
    const theme = useTheme((state) => state.theme);

    const onSheetAction = (modal: ModalConfig) => {
        useLayout.setState({ isSheetOpen: false });
        onOpenModal(modal);
    };

    return (
        // another jsx
        <li className='first:my-4 first:py-1 first:border-y dark:first:border-primary-dark-50 first:border-primary-dark-200'>
            <Button
                variant='ghost'
                className='rounded-none flex items-center justify-start gap-4 w-full'
                onClick={() =>
                    onSheetAction({
                        withHeader: false,
                        content: (
                            <CreateGroupProvider navigate={navigate}>
                                <CreateGroup />
                            </CreateGroupProvider>
                        ),
                        bodyClassName: 'w-[450px] p-3 h-auto'
                    })
                }
            >
                <Users className={listIconStyle} />
                <Typography weight='medium'>New Group</Typography>
            </Button>
        </li>
    );
};