I need to create a React High-Order component to protect routes in my app against users that don't have an access token. Then using that HOC wrap a Component like this on parent component:
<Route
exact
path={INDEX_URL}
render={isAuthenticated((props: any) => (<LandingPage {...props} />))}
/>
I'm using Typescript and I'm having some issues with the connect function and withRouter from react-router-dom.
Can someone help me, please? Thank you and have a great weekend!
I've already tried to extend my interface with RouterProps and/or withRouter but none worked. It seems that is some typing related issue, but I can't figure out exactly what it is.
import * as React from 'react';
import { Redirect, withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import * as H from 'history';
// Constants
import { LOGIN_URL } from '../../../data/constants/index.constants';
interface IAuthenticatedProps {
accessToken: string | null;
location: H.Location;
}
interface IAuthenticatedState {
isAuthenticated: boolean | undefined;
}
/**
* @description Higher-order component (HOC) to wrap Authenticated pages
* @date 2019-01-18
* @class Authenticated
* @extends {React.Component<IAuthenticatedProps, any>}
*/
const isAuthenticated = (ComposedComponent: any) => {
class Authenticated extends React.Component<
IAuthenticatedProps,
IAuthenticatedState
> {
(...)
function mapStateToProps(state: any) {
return {
accessToken: state.authentication.accessToken,
};
}
return withRouter(connect(mapStateToProps)(Authenticated));
};
export default isAuthenticated;
I expected no issues, but Typescript is giving me this:
[ts]
Argument of type 'ConnectedComponentClass<typeof Authenticated, Pick<IAuthenticatedProps, "location">>' is not assignable to parameter of type 'ComponentType<RouteComponentProps<any, StaticContext, any>>'.
Type 'ConnectedComponentClass<typeof Authenticated, Pick<IAuthenticatedProps, "location">>' is not assignable to type 'ComponentClass<RouteComponentProps<any, StaticContext, any>, any>'.
Type 'Component<Pick<IAuthenticatedProps, "location">, any, any>' is not assignable to type 'Component<RouteComponentProps<any, StaticContext, any>, any, any>'.
Types of property 'props' are incompatible.
Type 'Readonly<{ children?: ReactNode; }> & Readonly<Pick<IAuthenticatedProps, "location">>' is not assignable to type 'Readonly<{ children?: ReactNode; }> & Readonly<RouteComponentProps<any, StaticContext, any>>'.
Type 'Readonly<{ children?: ReactNode; }> & Readonly<Pick<IAuthenticatedProps, "location">>' is missing the following properties from type 'Readonly<RouteComponentProps<any, StaticContext, any>>': history, match [2345]
答案 0 :(得分:1)
withRouter()
的作用是为其包装的组件提供道具。因此,要使用它,要包装的组件必须期望。在这种情况下,Authenticated
仅期望IAuthenticatedProps
,但是withRouter()
提供了更多。所以TypeScript抱怨是因为withRouter()
试图向Authenticated
提供Authenticated
没有说期望的道具。
withRouter()
提供的道具在界面RouteComponentProps
中定义。尝试将其添加到Authenticated
的道具类型中:
class Authenticated extends React.Component<
IAuthenticatedProps & RouteComponentProps<{}>,
IAuthenticatedState
> {
// ...
}
您还可以从location
界面中删除IAuthenticatedProps
,因为它也包含在RouteComponentProps
界面中。
另一个问题是render
中的Route
属性需要一个函数,而不是任何旧的React组件类型。由于isAuthenticated()
返回了React.ComponentClass
,因此您需要执行以下操作:
const Authenticated = isAuthenticated(LandingPage);
// ...
<Route
exact
path={INDEX_URL}
render={(props: any) => <Authenticated {...props} />}
/>