reactjs - Får fel med createBrowserRouter: useNavigate() får endast användas i kontexten av en <Router>-komponent
Jag försöker byta från BrowserRoute-komponenten till createBrowserRouter i React Router 6.4.3 men får ett fel:
useNavigate() får endast användas i kontexten av en komponent.
Hur kan jag åtgärda det? Felet kommer från AuthProvider-komponenten och jag tror att jag behöver omsluta den med RouterProvider, men det är inte klart hur jag kan göra det.
Index.tsx-komponent
const container = document.getElementById('root')!;
const root = createRoot(container);
root.render(
<React.StrictMode>
<Provider store={store}>
<AuthProvider>
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
</AuthProvider>
</Provider>
</React.StrictMode>
);
App.tsx-komponent
const router = createBrowserRouter([
{
element: <ProtectedRoute />,
children: [
{
path: '/',
element: <DashboardLayout />,
children: [
{
index: true,
element: <HomePage />,
},
{
path: 'about',
element: <AboutPage />,
},
],
},
],
},
{
path: '/auth',
element: <Auth />,
},
]);
const App = () => {
const { isLoading } = useAuth0();
if (isLoading) {
return <LoaderPage />;
}
return (
<>
<Box>
<CssBaseline />
<RouterProvider router={router} />
</Box>
</>
);
};
Fel kommer från AuthProvider.tsx
export const AuthProvider = ({ children }: PropsWithChildren<Auth0ProviderWithConfigProps>): JSX.Element | null => {
const domain = process.env.REACT_APP_AUTH0_DOMAIN;
const clientId = process.env.REACT_APP_AUTH0_CLIENT_ID;
const navigate = useNavigate();
if (!(domain && clientId)) {
return null;
}
const onRedirectCallback = (appState: any) => {
navigate(appState?.returnTo || window.location.pathname);
};
return (
<Auth0Provider
domain={domain}
clientId={clientId}
redirectUri={window.location.origin}
onRedirectCallback={onRedirectCallback}
>
{children}
</Auth0Provider>
);
};
UPD
ProtectedRoute.tsx
export const ProtectedRoute: React.FC = () => {
const location = useLocation();
const { isAuthenticated } = useAuth0();
return isAuthenticated? <Outlet /> : <Navigate to="/auth" state={{ from: location }} replace />;
};
Om jag återställer, ändrar dessa komponenter tillbaka till BrowserRouter så fungerar allt:
index.tsx
root.render(
<React.StrictMode>
<BrowserRouter>
<AuthProvider>
<Provider store={store}>
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
</Provider>
</AuthProvider>
</BrowserRouter>
</React.StrictMode>
);
App.tsx
const App = () => {
const { isLoading } = useAuth0();
const routes = useRoutes([
{
element: <ProtectedRoute />,
children: [
{
path: '/',
element: <DashboardLayout />,
children: [
{
index: true,
element: <HomePage />,
},
{
path: 'about',
element: <AboutPage />,
},
],
},
],
},
{
path: '/auth',
element: <Auth />,
},
]);
if (isLoading) {
return <LoaderPage />;
}
return (
<>
<Box>
<CssBaseline />
{routes}
</Box>
</>
);
};
Codesandbox: https://codesandbox.io/s/nice-morning-oxwwj6?file=/src/App.tsx
Svar #1
Komponenten AuthProvider behöver renderas inom routing-kontexten för att kunna nå den och använda någon av react-router-dom-hookarna. Skapa en layout-rutt som renderar AuthProvider och omsluter en Outlet-komponent.
Exempel :
import {
createBrowserRouter,
Outlet
} from 'react-router-dom';
const AuthLayout = () => (
<AuthProvider>
<Outlet />
</AuthProvider>
);
const router = createBrowserRouter([
{
element: <AuthLayout />,
children: [
{
element: <ProtectedRoute />,
children: [
{
path: '/',
element: <DashboardLayout />,
children: [
{
index: true,
element: <HomePage />,
},
{
path: 'about',
element: <AboutPage />,
},
],
},
],
},
{
path: '/auth',
element: <Auth />,
},
]
},
]);
const App = () => {
const { isLoading } = useAuth0();
if (isLoading) {
return <LoaderPage />;
}
return (
<>
<Box>
<CssBaseline />
<RouterProvider router={router} />
</Box>
</>
);
};
...
const container = document.getElementById('root')!;
const root = createRoot(container);
root.render(
<React.StrictMode>
<Provider store={store}>
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
</Provider>
</React.StrictMode>
);
