Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement react-router PrivateRoute in Typescript project

Here is an example from react-router for how to add a component for protected routes:

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

I have tried to implement this functionality in my Typescript project, using the example above as inspiration.

components/Routes

import PrivateRoute from '../../connectors/PrivateRoute';
<PrivateRoute path="/codes" component={SomePage} />

connectors/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;

components/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;

Error

(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>>; }'.
like image 295
user1283776 Avatar asked Nov 01 '18 15:11

user1283776


People also ask

How do I add a react router to an existing project?

To install React Router, all you have to do is run npm install react-router-dom@6 in your project terminal and then wait for the installation to complete. If you are using yarn then use this command: yarn add react-router-dom@6 .


1 Answers

The error occurs because PrivateRouteProps has a required property location that isn't provided when you use PrivateRoute in components/Routes.tsx. I assume that this location should instead come from the routeProps that the router automatically passes to the render function of the route, as it did in the original example. Once this is fixed, another error is exposed: components/Routes.tsx is passing a paths property that isn't declared in PrivateRouteProps. Since PrivateRoute is passing any prop it doesn't know about to Route, PrivateRouteProps should extend RouteProps from react-router so that PrivateRoute accepts all props accepted by Route.

Here is components/PrivateRoute.tsx after both fixes:

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;
like image 157
Matt McCutchen Avatar answered Oct 11 '22 20:10

Matt McCutchen