Home > Mobile >  You should not use Route outside a router
You should not use Route outside a router

Time:06-07

I've got a .net 5 app with a ReactJS front-end (v16.14.0), created from the Visual Studio ReactJS Redux template. The app will be accessible from MS Teams and I want to implement single sign on, so I am copying some code from another project that does exactly that (the ProtectedRoute class) however I get the following errors even though I have stripped the project back to bare bones

Uncaught Error: Invariant failed: You should not use <Route> outside a <Router>
    at invariant (tiny-invariant.esm.js:12:1)
    at Route.js:35:1
    at updateContextConsumer (react-dom.development.js:18304:1)
    at beginWork (react-dom.development.js:18661:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237:1)
    at invokeGuardedCallback (react-dom.development.js:292:1)
    at beginWork$1 (react-dom.development.js:23203:1)
    at performUnitOfWork (react-dom.development.js:22154:1)
    at workLoopSync (react-dom.development.js:22130:1)
The above error occurred in the <Context.Consumer> component:
    in Route (at ProtectedRoute.tsx:25)
    in ProtectedRoute (created by Connect(ProtectedRoute))
    in Connect(ProtectedRoute) (at App.tsx:28)
    in Switch (at App.tsx:26)
    in div (at Layout.tsx:11)
    in div (created by Container)
    in Container (at Layout.tsx:10)
    in Layout (at App.tsx:25)
    in App (at src/index.tsx:40)
    in Router (created by ConnectedRouter)
    in ConnectedRouter (created by Context.Consumer)
    in ConnectedRouterWithContext (created by Connect(ConnectedRouterWithContext))
    in Connect(ConnectedRouterWithContext) (at src/index.tsx:39)
    in Provider (at src/index.tsx:38)
Uncaught Error: Invariant failed: You should not use <Route> outside a <Router>
    at invariant (tiny-invariant.esm.js:12:1)
    at Route.js:35:1
    at updateContextConsumer (react-dom.development.js:18304:1)
    at beginWork (react-dom.development.js:18661:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237:1)
    at invokeGuardedCallback (react-dom.development.js:292:1)
    at beginWork$1 (react-dom.development.js:23203:1)
    at performUnitOfWork (react-dom.development.js:22154:1)
    at workLoopSync (react-dom.development.js:22130:1)

The error says that the Route should not be outside the Router, but from the 2nd stack trace and the code below I think it is inside of a ConnectedRouter in the index.tsx.

Index.tsx

import 'bootstrap/dist/css/bootstrap.css';

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'connected-react-router';

import configureStore from './store/configureStore';
import { App } from './App';
import registerServiceWorker from './registerServiceWorker';
import { history } from './helpers';

// Get the application-wide store instance, prepopulating with state from the server where available.
const store = configureStore(history);

ReactDOM.render(
    <Provider store={store}>
        <ConnectedRouter history={history}>
            <App />
        </ConnectedRouter>
    </Provider>,
    document.getElementById('root'));

registerServiceWorker();

App.tsx

import * as React from 'react';
import { Route, Switch } from 'react-router';

import Layout from './components/Layout';
import ProtectedRoute from './components/ProtectedRoute';
import LoginPage from './pages/LoginPage';
import WizardPage from './pages/WizardPage';
import RequestsPage from './pages/RequestsPage';

import './App.scss'

export class App extends React.Component {
    public render() {
        const sharedRouteProps = { exact: true, authenticationPath: '/login' };
        const wizardRouteProps = { ...sharedRouteProps, path: "/", component: WizardPage };
        const requestsRouteProps = { ...sharedRouteProps, path: "/requests", component: RequestsPage };

        return (
            <Layout>
                <Switch>
                    <Route exact path='/login' component={LoginPage} />
                    <ProtectedRoute {...wizardRouteProps} />
                    <ProtectedRoute {...requestsRouteProps} />
                </Switch>
            </Layout>
        );
    }
}

ProtectedRoute.tsx

import * as React from 'react'
import { connect } from 'react-redux';
import { Redirect, Route } from 'react-router-dom';
import { ApplicationState } from '../store';
import * as AppConfigStore from '../store/AppConfig';

export interface IProtectedRouteProps {
    authenticationPath: string;
    component: any;
    path?: string;
    exact?: boolean;
    user: AppConfigStore.User | undefined;
}

class ProtectedRoute extends React.Component<IProtectedRouteProps> {
    public render() {
        if (this.props.user) {
            return (
                <Route {...this.props} />
            );
        } else {
            const redirectComponent = () => <Redirect to={{ pathname: this.props.authenticationPath }} />;

            return (
                <Route {...this.props} component={redirectComponent} render={undefined} />
            );
        }
    }
}

const mapStateToProps = (state: ApplicationState) => ({
    user: state.appConfig ? (state.appConfig as AppConfigStore.AppConfigState).user : undefined
});

export default connect(
    mapStateToProps,
    AppConfigStore.actionCreators
)(ProtectedRoute as any); // eslint-disable-line @typescript-eslint/no-explicit-any

why does it think the Route is outside the Router?

CodePudding user response:

        <Layout>
            <Router>
                <Switch>
                    <Route exact path='/login' component={LoginPage} />
                    <ProtectedRoute {...wizardRouteProps} />
                    <ProtectedRoute {...requestsRouteProps} />
                </Switch>
            </Router>
        </Layout>

As you can see, you missed Router

CodePudding user response:

Initially you need to import Router from react-router-dom and then wrap all routes inside the <Router>.

App.tsx

import * as React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import Layout from './components/Layout';
import ProtectedRoute from './components/ProtectedRoute';
import LoginPage from './pages/LoginPage';
import WizardPage from './pages/WizardPage';
import RequestsPage from './pages/RequestsPage';

import './App.scss'

export class App extends React.Component {
    public render() {
        const sharedRouteProps = { exact: true, authenticationPath: '/login' };
        const wizardRouteProps = { ...sharedRouteProps, path: "/", component: WizardPage };
        const requestsRouteProps = { ...sharedRouteProps, path: "/requests", component: RequestsPage };

        return (
            <Layout>
              <Router>
                <Switch>
                    <Route exact path='/login' component={LoginPage} />
                    <ProtectedRoute {...wizardRouteProps} />
                    <ProtectedRoute {...requestsRouteProps} />
                </Switch>
              </Router>
            </Layout>
        );
    }
}
  • Related