问题
I found this (reacttraining.com) site, which explains react-router with some examples. But I am not be able to do this with a typescript class. What I want to do is extend the Route class to build my own one. Right now I want to implement it in typescript for authentication as in the following example from the site.
const PrivateRoute = ({ component, ...rest }) => (
<Route {...rest} render={props => (
fakeAuth.isAuthenticated ? (
React.createElement(component, props)
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location }
}}/>
)
)}/>
)
I searched a lot, but couldn't find a site that explains the function to implement and which typed properties to call to nested routes. An ES6 class will be also helpful, thank you.
回答1:
Here's my best shot so far, although there's still one any remaining :)
import * as React from "react"
import {Redirect, Route, RouteComponentProps, RouteProps} from "react-router-dom"
type RouteComponent = React.StatelessComponent<RouteComponentProps<{}>> | React.ComponentClass<any>
const AUTHENTICATED = false // TODO: implement authentication logic
export const PrivateRoute: React.StatelessComponent<RouteProps> = ({component, ...rest}) => {
const renderFn = (Component?: RouteComponent) => (props: RouteProps) => {
if (!Component) {
return null
}
if (AUTHENTICATED) {
return <Component {...props} />
}
const redirectProps = {
to: {
pathname: "/auth/sign-in",
state: {from: props.location},
},
}
return <Redirect {...redirectProps} />
}
return <Route {...rest} render={renderFn(component)} />
}
回答2:
Regarding Redux ...
Jacka's answer helped me alot, but i had a difficult time connecting the PrivateRoute component to redux. Furthermore i wanted to abstract the resulting Route component to work e.g. as a LoggedInRoute, NotLoggedInRoute or in general a Route which presents it's component if a condition is fulfilled or redirects to a specified location otherwise:
Note: Written with redux 4, react-router-dom 4 and typescript 2.9.
import * as H from 'history';
import * as React from 'react';
import { connect, MapStateToPropsParam } from 'react-redux';
import { Redirect, Route, RouteComponentProps, RouteProps } from 'react-router';
export interface ConditionalRouteProps extends RouteProps {
routeCondition: boolean;
redirectTo: H.LocationDescriptor;
}
export class ConditionalRoute extends React.Component<ConditionalRouteProps> {
public render() {
// Extract RouteProps without component property to rest.
const { component: Component, routeCondition, redirectTo, ...rest } = this.props;
return <Route {...rest} render={this.renderFn} />
}
private renderFn = (renderProps: RouteComponentProps<any>) => {
if (this.props.routeCondition) {
const { component: Component } = this.props; // JSX accepts only upprcase.
if (!Component) {
return null;
}
return <Component {...renderProps} />
}
return <Redirect to={this.props.redirectTo} />;
};
}
export function connectConditionalRoute<S>(mapStateToProps: MapStateToPropsParam<ConditionalRouteProps, RouteProps, S>) {
return connect<ConditionalRouteProps, {}, RouteProps, S>(mapStateToProps)(ConditionalRoute);
}
You can either use the ConditionalRoute component without connecting it and use your component's local state, e.g.:
interface RootState {
loggedIn: boolean;
}
export class Root extends React.Component<RootProps, RootState> {
/* skipped initialState and setState(...) calls */
public render() {
return (
<Switch>
<ConditionalRoute
path="/todos"
component={TodoPage}
routeCondition={this.state.loggedIn}
redirectTo="/login" />
<ConditionalRoute
path="/login"
component={LoginPage}
routeCondition={!this.state.loggedIn}
redirectTo="/" />
<Redirect to="/todos" />
</Switch>
);
}
}
Or use the utility function connectConditionalRoute<S>(...) to use your redux store:
const loginRoute = '/login';
const todosRoute = '/todos';
const LoggedInRoute = connectConditionalRoute<RootState>(state => ({
redirectTo: loginRoute,
routeCondition: state.isLoggedIn,
}));
const NotLoggedInRoute = connectConditionalRoute<RootState>(state => ({
redirectTo: todosRoute,
routeCondition: !state.isLoggedIn
}));
const Root: React.SFC = () => (
<Switch>
<LoggedInRoute path="/todos" component={TodoPage} />
<NotLoggedInRoute path="/login" component={LoginPage} />
<Redirect to="/todos" />
</Switch>
);
The behaviour in the provided sample: Unauthorized users visit /todos, get redirected to /login, authorized users visit /login, get redirected to /todos. Whenever the redux store's isLoggedIn changes, the connected components are updated and redirect the user automatically.
回答3:
I was looking for same thing. The question is old but maybe someone is still looking for it. Here is what I come up with (All types properly used from react-router 4):
interface PrivateRouteProps extends RouteProps {
component: React.ComponentType<RouteComponentProps<any>> | React.ComponentType<any>
}
type RenderComponent = (props: RouteComponentProps<any>) => React.ReactNode;
export class PrivateRoute extends Route<PrivateRouteProps> {
render () {
const {component: Component, ...rest}: PrivateRouteProps = this.props;
const renderComponent: RenderComponent = (props) => (
AuthenticationService.isAuthenticated()
? <Component {...props} />
: <Redirect to='/login' />
);
return (
<Route {...rest} render={renderComponent} />
);
}
}
回答4:
here is my solution using "react-router-dom": "^4.4.0-beta.6" and "typescript": "3.2.2"
import React, { FunctionComponent } from "react";
import {
Route,
Redirect,
RouteProps,
RouteComponentProps
} from "react-router-dom";
interface PrivateRouteProps extends RouteProps {
component:
| React.ComponentType<RouteComponentProps<any>>
| React.ComponentType<any>;
}
const PrivateRoute: FunctionComponent<PrivateRouteProps> = ({
component: Component,
...rest
}) => {
return (
<Route
{...rest}
render={props =>
true ? ( //put your authenticate logic here
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/signin"
}}
/>
)
}
/>
);
};
export default PrivateRoute;
回答5:
You could use any.
const PrivateRoute = ({component: Component, ...rest }: any) => (
<Route {...rest} render={PrivateRender(Component)} />
);
const PrivateRender = (Component: any) => {
return (props: any) => {
return <Component {...props}/>;
};
};
来源:https://stackoverflow.com/questions/42309708/create-own-react-route-class-in-typescript