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>
);
}
}