Create own react route class in typescript
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 logicexport 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)} />}
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.
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;