英文:
Cannot use "useLocation" outside of react-router <RouterProvider> Uncaught Error: useLocation() may be used only in the context of a <Router>
问题
我试图在我的组件中使用useLocation
钩子:
import React, { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { connect } from 'react-redux';
import { useNProgress } from '@tanem/react-nprogress';
import type { PayloadAction } from '@reduxjs/toolkit';
import type { AppState } from '@/store/app';
import { uiActions } from '@/store/reducers/ui';
import EDProgressBarView from './EDProgressBar.view';
interface IPropsFromState {
readonly isProgressBarVisible: boolean;
}
interface IPropsFromDispatch {
readonly closeProgressBar: () => PayloadAction;
}
interface IProps extends IPropsFromState, IPropsFromDispatch {}
const EDProgressBar: React.FC<IProps> = (props: React.PropsWithChildren<IProps>) => {
const { animationDuration, isFinished, progress } = useNProgress({
isAnimating: props.isProgressBarVisible,
incrementDuration: 200,
});
const location = useLocation();
useEffect(() => {
props.closeProgressBar();
}, [location]);
return (
<EDProgressBarView
animationDuration={animationDuration}
isFinished={isFinished}
progress={progress}
/>
);
};
EDProgressBar.displayName = 'EDProgressBar';
EDProgressBar.defaultProps = {};
const mapStateToProps = (state: AppState) => {
return {
isProgressBarVisible: state.ui.isProgressBarVisible,
};
};
export default connect(mapStateToProps, {
closeProgressBar: uiActions.closeProgressBar,
})(React.memo(EDProgressBar));
我将这个组件放在这里:
import React, { Suspense, useMemo } from 'react';
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
import EDNotification from '@/ui/EDNotification';
import EDProgressBar from '@/ui/EDProgressBar';
import RouterBuilder from './App.router';
interface IProps {
readonly isAuthenticated: boolean | null;
}
const AppView: React.FC<IProps> = (props: React.PropsWithChildren<IProps>) => {
const routes = useMemo(() => {
return RouterBuilder(props.isAuthenticated);
}, [props.isAuthenticated]);
return (
<Suspense fallback={null}>
<RouterProvider router={createBrowserRouter(routes)} fallbackElement={null} />
<div id="backdrop-root" />
<div id="overlay-root" />
<EDNotification />
<EDProgressBar />
</Suspense>
);
};
AppView.displayName = 'AppView';
AppView.defaultProps = {};
export default React.memo(AppView);
然后我收到一个错误:
未捕获的错误:useLocation()只能在<Router>组件的上下文中使用。
但我不明白如果RouterProvider
组件不接受类似组件的子元素或类似组件的东西来注入它,我如何可以做到这一点。我需要EDProgressBar
组件存在于应用程序中,无论活动路由如何。我需要它存在于每个页面。
我该如何做?
这是我的RouterBuilder
:
import React from 'react';
import { type RouteObject } from 'react-router-dom';
const CliAuth = React.lazy(() => import('./pages/CliAuth'));
const CliAuthenticated = React.lazy(() => import('./pages/CliAuthenticated'));
const NotFound = React.lazy(() => import('./pages/NotFound'));
const RouterBuilder = (isAuthenticated: boolean | null) => {
const generalRoutes: RouteObject[] = [
{
path: 'cli-auth',
element: <CliAuth />,
},
{
path: 'cli-authenticated',
element: <CliAuthenticated />,
},
{
path: 'not-found',
element: <NotFound />,
},
{
path: '*',
element: isAuthenticated === null ? null : <NotFound />,
},
];
return [...(isAuthenticated ? authorizedRoutes : unAuthorizedRoutes), ...generalRoutes];
};
export default RouterBuilder;
请注意,我删除了一些路由,因为它们与此无关,但重要的是我在某些路由中使用了loader
关键字,所以我需要这个构建器。
英文:
I'm trying to use the useLocation
hook in my component:
import React, { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { connect } from 'react-redux';
import { useNProgress } from '@tanem/react-nprogress';
import type { PayloadAction } from '@reduxjs/toolkit';
import type { AppState } from '@/store/app';
import { uiActions } from '@/store/reducers/ui';
import EDProgressBarView from './EDProgressBar.view';
interface IPropsFromState {
readonly isProgressBarVisible: boolean;
}
interface IPropsFromDispatch {
readonly closeProgressBar: () => PayloadAction;
}
interface IProps extends IPropsFromState, IPropsFromDispatch {}
const EDProgressBar: React.FC<IProps> = (props: React.PropsWithChildren<IProps>) => {
const { animationDuration, isFinished, progress } = useNProgress({
isAnimating: props.isProgressBarVisible,
incrementDuration: 200,
});
const location = useLocation();
useEffect(() => {
props.closeProgressBar();
}, [location]);
return (
<EDProgressBarView
animationDuration={animationDuration}
isFinished={isFinished}
progress={progress}
/>
);
};
EDProgressBar.displayName = 'EDProgressBar';
EDProgressBar.defaultProps = {};
const mapStateToProps = (state: AppState) => {
return {
isProgressBarVisible: state.ui.isProgressBarVisible,
};
};
export default connect(mapStateToProps, {
closeProgressBar: uiActions.closeProgressBar,
})(React.memo(EDProgressBar));
And I place this component here:
import React, { Suspense, useMemo } from 'react';
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
import EDNotification from '@/ui/EDNotification';
import EDProgressBar from '@/ui/EDProgressBar';
import RouterBuilder from './App.router';
interface IProps {
readonly isAuthenticated: boolean | null;
}
const AppView: React.FC<IProps> = (props: React.PropsWithChildren<IProps>) => {
const routes = useMemo(() => {
return RouterBuilder(props.isAuthenticated);
}, [props.isAuthenticated]);
return (
<Suspense fallback={null}>
<RouterProvider router={createBrowserRouter(routes)} fallbackElement={null} />
<div id="backdrop-root" />
<div id="overlay-root" />
<EDNotification />
<EDProgressBar />
</Suspense>
);
};
AppView.displayName = 'AppView';
AppView.defaultProps = {};
export default React.memo(AppView);
So then I get an error:
Uncaught Error: useLocation() may be used only in the context of a <Router> component.
But I don't understand how can I do such thing if RouterProvider
component does not accept any children of something like a component to inject it.
I need this EDProgressBar
component to exist in the application regardless of the active route. I need it to exist in every page.
How can I do so?
This is my RouterBuild:
import React from 'react';
import { type RouteObject } from 'react-router-dom';
const CliAuth = React.lazy(() => import('./pages/CliAuth'));
const CliAuthenticated = React.lazy(() => import('./pages/CliAuthenticated'));
const NotFound = React.lazy(() => import('./pages/NotFound'));
const RouterBuilder = (isAuthenticated: boolean | null) => {
const generalRoutes: RouteObject[] = [
{
path: 'cli-auth',
element: <CliAuth />,
},
{
path: 'cli-authenticated',
element: <CliAuthenticated />,
},
{
path: 'not-found',
element: <NotFound />,
},
{
path: '*',
element: isAuthenticated === null ? null : <NotFound />,
},
];
return [...(isAuthenticated ? authorizedRoutes : unAuthorizedRoutes), ...generalRoutes];
};
export default RouterBuilder;
Note that I removed some routes because they are irrelevant here, but the important thing is that I use the loader
key in some, so I need this builder.
答案1
得分: 1
以下是翻译好的部分:
"The useLocation
and other RRD hooks can't be used outside any routing context provided by a router. I suggest creating a layout route that renders the EDNotification
and EDProgressBar
components and the backdrop and overlay divs. This layout route will be included in the routes returned by RouterBuilder
. This allows the components accessing the routing context to have the router higher than them in the ReactTree."
中文翻译:
"useLocation
和其他 RRD 钩子不能在路由器提供的任何路由上下文之外使用。我建议创建一个布局路由,用于渲染 EDNotification
和 EDProgressBar
组件以及背景和叠加层的 div 元素。这个布局路由将包含在由 RouterBuilder
返回的路由中。这样,能够访问路由上下文的组件将在 React 树中位于它们之上的路由之上。"
示例部分未提供明确的需要翻译的内容,因此没有进一步的翻译。
英文:
The useLocation
and other RRD hooks can't be used outside any routing context provided by a router. I suggest creating a layout route that renders the EDNotification
and EDProgressBar
components and the backdrop and overlay divs. This layout route will be included in the routes returned by RouterBuilder
. This allows the components accessing the routing context to have the router higher than them in the ReactTree.
Example:
import { Outlet } from 'react-router-dom';
const AppLayout = () => (
<>
<Outlet />
<div id="backdrop-root" />
<div id="overlay-root" />
<EDNotification />
<EDProgressBar />
</>
);
const RouterBuilder = (isAuthenticated: boolean | null) => {
const generalRoutes: RouteObject[] = [
{
path: 'cli-auth',
element: <CliAuth />,
},
{
path: 'cli-authenticated',
element: <CliAuthenticated />,
},
{
path: 'not-found',
element: <NotFound />,
},
{
path: '*',
element: isAuthenticated === null ? null : <NotFound />,
},
];
const routes = [
{
element: <AppLayout />
children: [
...(isAuthenticated ? authorizedRoutes : unAuthorizedRoutes),
...generalRoutes
],
}
];
return routes;
};
const AppView: React.FC<IProps> = (props: React.PropsWithChildren<IProps>) => {
const routes = useMemo(() => {
return RouterBuilder(props.isAuthenticated);
}, [props.isAuthenticated]);
return (
<Suspense fallback={null}>
<RouterProvider
router={createBrowserRouter(routes)}
fallbackElement={null}
/>
</Suspense>
);
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论