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

ReactJS & Typescript: Creating a private route using router dom

我已经看到很多关于此的问题,但 none 似乎对我有用,因为它们已经过时了。软件包已更新,并且已删除解决方案。我不想使用功能组件来解决这个问题。

我正在尝试使用 ReactJS 和 Typescript 创建私有路由:

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

但是当我尝试扩展路线时:

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

我收到一条错误消息,提示我无法扩展 Route:

Type '(_props: PathRouteProps | LayoutRouteProps | IndexRouteProps) => ReactElement<any, string | JSXElementConstructor> | null' is not a constructor function type.ts(2507)

当我尝试围绕 Route 包装一个组件时:

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

它让我构建甚至 运行 但我得到一个 运行 时间错误:

Uncaught Error: A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.

有正确的方法吗?如果没有,是否有解决此问题的方法而不使用功能组件

编辑#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>

编辑#2:

供将来参考:我已经使用@SlothOverlord 的回答解决了这个问题,但是从功能组件翻译过来的。另外,我正在使用 redux 来填充道具。

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:

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
  }
}

我保留的路线与答案中的相同。

编辑#3:

关于[重复]状态:链接的问题没有解决post开头所述版本的问题。正如@SlothOverlord 在他的回答开头所说:

Routes work different in v6.

不仅如此,Redirect 版本 6 中也不存在。很多事情都不同了。将其标记为重复的决定完全基于标记触发器 happy mod.

的关键字匹配。

这个网站正在成为自以为是、厌恶人类的恶霸的避风港,他们在mod化解中找到了一种惩罚人们挫败感的方法。

路由在 v6 中的工作方式不同。

注意:仅功能组件示例:

您不再需要将道具传递给您的路线。有两种方法。将路线渲染为出口或 child。 Outlet 基本上是您的嵌套路线,而 children 是直接 children 在您的 组件

在路由内部检查身份验证状态,而不是像 v5 那样在路由外部检查。

下面有两个例子。 Private outlet 在登录时呈现嵌套路由,public 路由在未登录时呈现 children。

对于typescript,只需要FC类型就可以通过children,没有别的。


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>
  );
};


您还可以从其他组件渲染嵌套路由。只需将 "<Outlet/>" 放入其中,它就会在该位置呈现嵌套路由。这样一来,您只能将一个仪表板组件放在路线的根部。