Manuel Lucas
Manuel Lucas

Reputation: 532

React Typescript Error: Invariant failed: You should not use <withRouter(App) /> outside a <Router>

I was developing an App where I use Firebase as an Authentication system of the App, an when I try to implement the routes of the app, I start to get the above title error.

I'm using withRouter funtion, to encapsulate my App component.

So the code of my App.tsx file is the following:

import React, { FC, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
  Route,
  Switch,
  useHistory,
  withRouter,
  BrowserRouter as Router,
} from "react-router-dom";
import "./App.css";

import Header from "./components/sections/Header";
import SignUp from "./components/pages/SignUp";
import SignIn from "./components/pages/SignIn";
import ForgotPassword from "./components/pages/ForgotPassword";
import Homepage from "./components/pages/Homepage";
import Dashboard from "./components/pages/Dashboard";
import PrivateRoute from "./components/auth/PrivateRoute";
import PublicRoute from "./components/auth/PublicRoute";
import Loader from "./components/UI/Loader";
import firebase from "./firebase/config";
import {
  getUserById,
  setLoading,
  setNeedVerification,
} from "./store/actions/authActions";
import { RootState } from "./store";

const App: FC = () => {
  const dispatch = useDispatch();
  const { loading } = useSelector((state: RootState) => state.auth);
  let history = useHistory();
  // Check if user exists
  // App.tsx
  useEffect(() => {
    dispatch(setLoading(true));
    const unsubscribe = firebase.auth().onAuthStateChanged(async (user) => {
      if (user) {
        await dispatch(getUserById(user.uid));
        if (user.emailVerified) {
          history.push("/homepage");
        } else {
          history.push("/signin");
          dispatch(setNeedVerification());
        }
      }
      dispatch(setLoading(false));
    });

    return () => {
      unsubscribe();
    };
  });

  if (loading) {
    return <Loader />;
  }

  function Routes() {
    return (
      <Switch>
        <PublicRoute path="/signup" component={SignUp} exact />
        <PublicRoute path="/signin" component={SignIn} exact />
        <PublicRoute path="/forgot-password" component={ForgotPassword} exact />
        <PrivateRoute path="/dashboard" component={Dashboard} exact />
        <PublicRoute path="/homepage" component={Homepage} exact />
      </Switch>
    );
  }

  return (
    <Router>
      <Header />
      <Routes />
    </Router>
  );
};

export default withRouter(App);

`So I think that have to be realated with the configuration of Route library into the main component of the app.

What I missing??

Take thankss in advance !

Upvotes: 3

Views: 1038

Answers (1)

Drew Reese
Drew Reese

Reputation: 202751

App is the component rendering the Router so App itself can't use anything that requires a Router context higher up in the React tree.

The solution is to move the Router higher in the React tree, i.e. wrap App in the Router. Once App is being rendered by a Router and since you are using the useHistory hook there will be no need to decorate with the withRouter HOC.

App

const App: FC = () => {
  ...

  function Routes() {
    return (
      <Switch>
        <PublicRoute path="/signup" component={SignUp} exact />
        <PublicRoute path="/signin" component={SignIn} exact />
        <PublicRoute path="/forgot-password" component={ForgotPassword} exact />
        <PrivateRoute path="/dashboard" component={Dashboard} exact />
        <PublicRoute path="/homepage" component={Homepage} exact />
      </Switch>
    );
  }

  return (
    <>
      <Header />
      <Routes />
    </>
  );
};

export default App;

index where App is rendered.

import { BrowserRouter as Router } from "react-router-dom";
import App from '../App';

...

<Router>
  <App />
</Router>

Upvotes: 1

Related Questions