简体   繁体   English

ReactJS & Typescript:使用路由器dom创建私有路由

[英]ReactJS & Typescript: Creating a private route using router dom

I have seen many questions about this but none seem to work for me because they are old.我已经看到很多关于此的问题,但似乎没有一个对我有用,因为它们已经过时了。 The packages have been updated and the solutions for this have been stripped out.软件包已更新,并且已删除解决方案。 I do not want to use functional components to solve this.我不想使用功能组件来解决这个问题。

I'm trying to create a private route with ReactJS and Typescript:我正在尝试使用 ReactJS 和 Typescript 创建一条私人路线:

"react": "^17.0.2",
"react-router-dom": "^6.2.1",
"typescript": "^4.5.5",

But when I try to extend a Route:但是当我尝试扩展路线时:

export class PrivateRoute extends Route<PrivateRouteProps> {
  public render(): JSX.Element {
    return (
      // ...
    );
  }
}

I get an error saying that I can't extend Route :我收到一条错误消息,说我无法扩展Route

Type '(_props: PathRouteProps | LayoutRouteProps | IndexRouteProps) => ReactElement<any, string |输入 '(_props: PathRouteProps | LayoutRouteProps | IndexRouteProps) => ReactElement<any, string | JSXElementConstructor> | JSXElementConstructor> | null' is not a constructor function type.ts(2507) null' 不是构造函数 function type.ts(2507)

And when I try to wrap a component around Route :当我尝试在Route周围包装一个组件时:

export class PrivateRoute extends Component<PrivateRouteProps> {
  public render(): JSX.Element {
    return (
      // ...
      <Route />
      // ...
    );
  }
}

It let's me build and even run but I get a runtime error:它让我构建甚至运行,但我收到运行时错误:

Uncaught Error: A <Route> is only ever to be used as the child of <Routes> element, never rendered directly.未捕获的错误:<Route> 只能用作 <Routes> 元素的子元素,永远不会直接呈现。 Please wrap your <Route> in a <Routes>.请将您的 <Route> 包裹在 <Routes> 中。

Is there a correct way to do this?有没有正确的方法来做到这一点? If not, is there a workaround for this without using functional components ?如果没有,是否有不使用功能组件的解决方法?

EDIT #1:编辑#1:

<Provider store={authStore}>
  <BrowserRouter>
    <Routes>
      <Route path='/' element={<MainPage />} />
      <Route path='/login' element={<LoginPage />} />
      <PrivateRoute path='/dashboard' element={<DashboardPage />} />
      <Route path='*' element={<NotFoundPage />} />
    </Routes>
  </BrowserRouter>
</Provider>

EDIT #2:编辑#2:

For future reference: I have solved this using @ SlothOverlord 's answer but translated from functional component.供将来参考:我已经使用@SlothOverlord的答案解决了这个问题,但是是从功能组件翻译而来的。 Also, I'm using redux to fill the props.另外,我正在使用 redux 来填充道具。

private.outlet.tsx: private.outlet.tsx:

export class PrivateOutlet extends Component<AuthenticationProps> {
  public render(): JSX.Element {
    const user = this.props.user;
    if (user) { // Check if logged in
      return (
        <>
          {this.props.children}
          <Outlet />
        </>
      );
    }
    return <Navigate to="/login" replace />; // Go back to login if not logged in  
  }
}

public.outlet.tsx: public.outlet.tsx:

export class PublicOutlet extends Component<AuthenticationProps> {
  public render(): JSX.Element {
    const user = this.props.user;
    if (!user) { // Check if logged in
      return (
        <>
          {this.props.children}
          <Outlet />
        </>
      );
    }
    return <Navigate to="/" replace />; // Go to protected route if logged in
  }
}

The routes I kept the same as in the answer.我保留的路线与答案中的路线相同。

EDIT #3:编辑#3:

Regarding the [duplicate] status: The question linked does not solve the problem for the versions stated at the beginning of the post.关于 [重复] 状态:链接的问题不能解决帖子开头所述版本的问题。 As @SlothOverlord stated at the beginning of his answer:正如@SlothOverlord 在他的回答开头所说:

Routes work different in v6.路由在 v6 中的工作方式不同。

Not only that but Redirect does not exist in version 6. Many things are different.不仅如此, Redirect在版本 6 中也不存在。很多事情都不同了。 The decision to mark it duplicate was based on nothing except keyword matching of a flagging trigger happy mod.将其标记为重复的决定完全基于标记触发器快乐 mod 的关键字匹配。

This site is becoming a haven for self-important, misanthropic bullies that find in moderation a way to punish people for their own frustrations.这个网站正在成为自以为是、厌恶人类的恶霸的避风港,这些恶霸找到了一种适度的方式来惩罚人们对自己的挫败感。

Routes work different in v6.路由在 v6 中的工作方式不同。

Note: Functional components example only:注意:仅功能组件示例:

You don't need to pass props to your routes anymore.您不再需要将道具传递给您的路线。 There are two ways.有两种方法。 Render route as outlet or a child.将路线渲染为出口或孩子。 Outlet is basically your nested route, while children are direct children in your <Private/Public Outlet> component Outlet 基本上是您的嵌套路线,而 children 是您的 <Private/Public Outlet> 组件中的直接子级

Auth status is checked inside the route, not outside of it as in v5.在路由内部检查身份验证状态,而不是像 v5 那样在路由外部检查。

There are two examples below.下面有两个例子。 Private outlet renders nested routes if logged in, public route renders children if not logged in.如果登录,私人出口呈现嵌套路线,如果未登录,公共路线呈现孩子。

For typescript, only FC type is needed to pass children, nothing else.对于 typescript,只需要 FC type 传递孩子,没有别的。


const PrivateOutlet: FC = ({ children }) => {
  const { user } = useContext(ContextProvider); //Auth context
  return user?.id ? ( //Check if logged in
    <>
      {children} //This is your children
      <Outlet /> //This is your nested route
    </>
  ) : (
    <Navigate to="/login" replace /> //Go back to login if not logged in
  );
};

const PublicOutlet: FC = ({ children }) => {
  const { user } = useContext(ContextProvider); //Auth context
  return !user?.id ? ( //Check if logged in
    <>
      {children} //This is your children
      <Outlet /> //This is your nested route
    </>
  ) : (
    <Navigate to="/" replace /> //Go to protected route if logged in
  );
};

export const MainRouter = () => {
  return (
    <BrowserRouter>
      <Routes>
      <Route path="/" element={<PrivateOutlet />}> //Check auth for all nested routes
          <Route index element={<MainPage />} /> //Outlet
          <Route path="private" element={<MainPage2 />} /> //Outlet
        </Route>

        <Route
          path="login" //Not nested in "/" Check for auth individually
          element={
            <PublicOutlet>
              <LoginPage /> //Children
            </PublicOutlet>
          }
        />

//alternative for login
 <Route path="login" element={ <PublicOutlet/> }> 
       <Route index element={<Login />} /> //Render as outlet.
 </Route>


        <Route path="*" element={<NotFoundPage />} />
  
      </Routes>
    </BrowserRouter>
  );
};


You can also render nested routes from your other components.您还可以从其他组件渲染嵌套路由。 Just put "<Outlet/>" inside of it and it will render nested routes at that position. That way you can only place one dashboard component at the root of your route.只需将"<Outlet/>"放入其中,它就会在该 position 处呈现嵌套路由。这样一来,您只能将一个仪表板组件放在路由的根部。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM