Cannot use "useLocation" outside of react-router <RouterProvider> Uncaught Error: useLocation() may be used only in the context of a <Router>

huangapple go评论62阅读模式
英文:

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 &#39;react&#39;;
import { useLocation } from &#39;react-router-dom&#39;;
import { connect } from &#39;react-redux&#39;;
import { useNProgress } from &#39;@tanem/react-nprogress&#39;;
import type { PayloadAction } from &#39;@reduxjs/toolkit&#39;;

import type { AppState } from &#39;@/store/app&#39;;
import { uiActions } from &#39;@/store/reducers/ui&#39;;

import EDProgressBarView from &#39;./EDProgressBar.view&#39;;

interface IPropsFromState {
	readonly isProgressBarVisible: boolean;
}

interface IPropsFromDispatch {
	readonly closeProgressBar: () =&gt; PayloadAction;
}

interface IProps extends IPropsFromState, IPropsFromDispatch {}

const EDProgressBar: React.FC&lt;IProps&gt; = (props: React.PropsWithChildren&lt;IProps&gt;) =&gt; {
	const { animationDuration, isFinished, progress } = useNProgress({
		isAnimating: props.isProgressBarVisible,
		incrementDuration: 200,
	});

	const location = useLocation();

	useEffect(() =&gt; {
		props.closeProgressBar();
	}, [location]);

	return (
		&lt;EDProgressBarView
			animationDuration={animationDuration}
			isFinished={isFinished}
			progress={progress}
		/&gt;
	);
};

EDProgressBar.displayName = &#39;EDProgressBar&#39;;
EDProgressBar.defaultProps = {};

const mapStateToProps = (state: AppState) =&gt; {
	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 &#39;react&#39;;
import { RouterProvider, createBrowserRouter } from &#39;react-router-dom&#39;;

import EDNotification from &#39;@/ui/EDNotification&#39;;
import EDProgressBar from &#39;@/ui/EDProgressBar&#39;;

import RouterBuilder from &#39;./App.router&#39;;

interface IProps {
	readonly isAuthenticated: boolean | null;
}

const AppView: React.FC&lt;IProps&gt; = (props: React.PropsWithChildren&lt;IProps&gt;) =&gt; {
	const routes = useMemo(() =&gt; {
		return RouterBuilder(props.isAuthenticated);
	}, [props.isAuthenticated]);

	return (
		&lt;Suspense fallback={null}&gt;
			&lt;RouterProvider router={createBrowserRouter(routes)} fallbackElement={null} /&gt;

			&lt;div id=&quot;backdrop-root&quot; /&gt;
			&lt;div id=&quot;overlay-root&quot; /&gt;

			&lt;EDNotification /&gt;
			&lt;EDProgressBar /&gt;
		&lt;/Suspense&gt;
	);
};

AppView.displayName = &#39;AppView&#39;;
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 &lt;Router&gt; 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 &#39;react&#39;;
import { type RouteObject } from &#39;react-router-dom&#39;;


const CliAuth = React.lazy(() =&gt; import(&#39;./pages/CliAuth&#39;));
const CliAuthenticated = React.lazy(() =&gt; import(&#39;./pages/CliAuthenticated&#39;));
const NotFound = React.lazy(() =&gt; import(&#39;./pages/NotFound&#39;));


const RouterBuilder = (isAuthenticated: boolean | null) =&gt; {

	const generalRoutes: RouteObject[] = [
		{
			path: &#39;cli-auth&#39;,
			element: &lt;CliAuth /&gt;,
		},
		{
			path: &#39;cli-authenticated&#39;,
			element: &lt;CliAuthenticated /&gt;,
		},
		{
			path: &#39;not-found&#39;,
			element: &lt;NotFound /&gt;,
		},
		{
			path: &#39;*&#39;,
			element: isAuthenticated === null ? null : &lt;NotFound /&gt;,
		},
	];

	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 钩子不能在路由器提供的任何路由上下文之外使用。我建议创建一个布局路由,用于渲染 EDNotificationEDProgressBar 组件以及背景和叠加层的 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 &#39;react-router-dom&#39;;

const AppLayout = () =&gt; (
  &lt;&gt;
    &lt;Outlet /&gt;
    &lt;div id=&quot;backdrop-root&quot; /&gt;
    &lt;div id=&quot;overlay-root&quot; /&gt;

    &lt;EDNotification /&gt;
    &lt;EDProgressBar /&gt;
  &lt;/&gt;
);

const RouterBuilder = (isAuthenticated: boolean | null) =&gt; {
  const generalRoutes: RouteObject[] = [
    {
      path: &#39;cli-auth&#39;,
      element: &lt;CliAuth /&gt;,
    },
    {
      path: &#39;cli-authenticated&#39;,
      element: &lt;CliAuthenticated /&gt;,
    },
    {
      path: &#39;not-found&#39;,
      element: &lt;NotFound /&gt;,
    },
    {
      path: &#39;*&#39;,
      element: isAuthenticated === null ? null : &lt;NotFound /&gt;,
    },
  ];

  const routes = [
    {
      element: &lt;AppLayout /&gt;
      children: [
        ...(isAuthenticated ? authorizedRoutes : unAuthorizedRoutes),
        ...generalRoutes
      ],
    }
  ];

  return routes;
};
const AppView: React.FC&lt;IProps&gt; = (props: React.PropsWithChildren&lt;IProps&gt;) =&gt; {
  const routes = useMemo(() =&gt; {
    return RouterBuilder(props.isAuthenticated);
  }, [props.isAuthenticated]);

  return (
    &lt;Suspense fallback={null}&gt;
      &lt;RouterProvider
        router={createBrowserRouter(routes)}
        fallbackElement={null}
      /&gt;
    &lt;/Suspense&gt;
  );
};

huangapple
  • 本文由 发表于 2023年2月24日 04:52:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/75550215.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定