在 Typescript 项目中实现 react-router PrivateRoute
Posted
技术标签:
【中文标题】在 Typescript 项目中实现 react-router PrivateRoute【英文标题】:Implement react-router PrivateRoute in Typescript project 【发布时间】:2019-04-05 20:20:49 【问题描述】:以下是 react-router 的示例,说明如何为受保护的路由添加组件:
function PrivateRoute( component: Component, ...rest )
return (
<Route
...rest
render=props =>
fakeAuth.isAuthenticated ? (
<Component ...props />
) : (
<Redirect
to=
pathname: "/login",
state: from: props.location
/>
)
/>
);
https://reacttraining.com/react-router/web/example/auth-workflow
我已尝试在我的 Typescript 项目中实现此功能,并以上面的示例为灵感。
组件/路线
import PrivateRoute from '../../connectors/PrivateRoute';
<PrivateRoute path="/codes" component=SomePage />
连接器/PrivateRoute
import connect from 'react-redux';
import AppState from 'app-types';
import PrivateRouteComponent from '../../components/PrivateRoute';
const mapStateToProps = (state: AppState) =>
const isSignedIn = state.user.isSignedIn;
return
isSignedIn
;
;
const PrivateRoute = connect(
mapStateToProps,
null
)(PrivateRouteComponent);
export default PrivateRoute;
组件/PrivateRoute
import * as React from 'react';
import
Route,
Redirect,
from 'react-router-dom';
interface PrivateRouteProps
// tslint:disable-next-line:no-any
component: any;
isSignedIn: boolean;
// tslint:disable-next-line:no-any
location: any;
const PrivateRoute = (props: PrivateRouteProps) =>
const component: Component, isSignedIn, location, ...rest = props;
return (
<Route
...rest
render=(routeProps) =>
isSignedIn ? (
<Component ...routeProps />
) : (
<Redirect
to=
pathname: '/signin',
state: from: location
/>
)
/>
);
;
export default PrivateRoute;
错误
(105,18): Type ' path: string; component: ConnectedComponentClass<typeof SomePage, Pick<SomePageProps, never>>; ' is not assignable to type 'Readonly<Pick<PrivateRouteProps, "location" | "component">>'.
Property 'location' is missing in type ' path: string; component: ConnectedComponentClass<typeof SomePage, Pick<SomePageProps, never>>; '.
【问题讨论】:
【参考方案1】:发生该错误是因为PrivateRouteProps
有一个必需的属性location
,当您在components/Routes.tsx
中使用PrivateRoute
时未提供该属性。我假设这个位置应该来自路由器自动传递给路由的render
函数的routeProps
,就像在原始示例中所做的那样。修复此问题后,会出现另一个错误:components/Routes.tsx
正在传递未在PrivateRouteProps
中声明的paths
属性。由于PrivateRoute
将任何它不知道的道具传递给Route
,PrivateRouteProps
应该从react-router
扩展RouteProps
,以便PrivateRoute
接受Route
接受的所有道具。
这里是components/PrivateRoute.tsx
两次修复后:
import * as React from 'react';
import
Route,
Redirect,
RouteProps,
from 'react-router-dom';
interface PrivateRouteProps extends RouteProps
// tslint:disable-next-line:no-any
component: any;
isSignedIn: boolean;
const PrivateRoute = (props: PrivateRouteProps) =>
const component: Component, isSignedIn, ...rest = props;
return (
<Route
...rest
render=(routeProps) =>
isSignedIn ? (
<Component ...routeProps />
) : (
<Redirect
to=
pathname: '/signin',
state: from: routeProps.location
/>
)
/>
);
;
export default PrivateRoute;
【讨论】:
这太棒了!如果我可能会问一个后续问题:我为什么需要使用 RouteProps 扩展 PrivateRouteProps,而我通常不需要在我的其他组件中这样做? 在这种情况下扩展RouteProps
的目的是接受path
属性,以便可以将其传递给Route
。如果您正在谈论的其他组件包装Route
并以与PrivateRoute
相同的方式传递道具,我希望它们的道具类型需要扩展RouteProps
;如果这些组件不包装Route
,那么RouteProps
不相关。【参考方案2】:
我发现Matt's answer 非常有用,但需要它为children
和component
工作,所以调整如下:
import * as React from 'react';
import Route, Redirect, RouteProps from 'react-router-dom';
import fakeAuth from '../api/Auth';
interface PrivateRouteProps extends RouteProps
// tslint:disable-next-line:no-any
component?: any;
// tslint:disable-next-line:no-any
children?: any;
const PrivateRoute = (props: PrivateRouteProps) =>
const component: Component, children, ...rest = props;
return (
<Route
...rest
render=routeProps =>
fakeAuth.isAuthenticated ? (
Component ? (
<Component ...routeProps />
) : (
children
)
) : (
<Redirect
to=
pathname: '/signin',
state: from: routeProps.location ,
/>
)
/>
);
;
export default PrivateRoute;
注意:这恰好是使用 fakeAuth
就像原来的 training article 而不是 user1283776 的 isSignedIn
redux 的东西,但你明白了。
【讨论】:
【参考方案3】:目前的答案有效,但我想发布我的解决方案,因为我认为它有几个优点:
不使用any
类型覆盖属性 component
。
利用 render method from the library 来支持 <Route>
组件和子道具 - 无需重新实现已经存在的框架逻辑/代码。
使用来自官方react-redux
文档的Recipe: Static Typing
例子:
import * as React from 'react';
import connect, ConnectedProps from 'react-redux';
import
Redirect,
Route,
RouteProps,
from 'react-router-dom';
import AppState from '../store';
const mapState = (state: AppState) => (
loggedIn: state.system.loggedIn,
);
const connector = connect(
mapState,
);
type PropsFromRedux = ConnectedProps<typeof connector>;
type Props = PropsFromRedux & RouteProps &
;
const PrivateRoute: React.FC<Props> = props =>
const loggedIn, ...rest = props;
return ( !loggedIn ? <Redirect to="/login/" /> :
<Route ...rest />
);
;
export default connector(PrivateRoute);
【讨论】:
【参考方案4】:我对子组件使用“React.ReactNode”类型而不是任何类型。
【讨论】:
您好,欢迎来到 ***!请考虑更新您的答案,以更清楚和具体地了解在答案中发布的原始代码中要修改的内容。考虑修改代码并直接发布更新和工作代码,或更改的特定行的前后,以及它们来自哪个文件的引用。这将大大改善这个答案。以上是关于在 Typescript 项目中实现 react-router PrivateRoute的主要内容,如果未能解决你的问题,请参考以下文章
我想在辅助函数中操作 React 类组件中存在的状态。我想在 Typescript 中实现相同的目标
如何将 SignaturePad 与 React TypeScript 一起使用