Page is not rendering after using `history.push` in Ionic/React with `IonTabs` as nested routes

I have an Ionic/React v5.0.7 app with IonReactRouter to manage routing:

// App.tsx

return (
  <IonApp>
    <IonReactRouter history={history}>
      <IonRouterOutlet>
        <Route exact path={ROUTES.HOME} component={Home} />
        <Route exact path={ROUTES.LOGIN} component={Login} />
        <Route path={ROUTES.CONTRACTOR} component={ContractorDashboard} />
      </IonRouterOutlet>
    </IonReactRouter>
  </IonApp>
)

In one of my routes (path={ROUTES.CONTRACTOR}) I’m trying to implement IonTabs which needs to have its own IonRouterOutlet, so I have the tabs as nested routes like so:

// ContractorDashboard.tsx

<IonContent>
  <IonTabs>
    <IonTabBar slot="top">
      <IonTabButton href="/contractor/overview" tab="contractor-overview">
        <IonLabel>{t('ContractorDashboard.contractorOverview')}</IonLabel>
      </IonTabButton>
      <IonTabButton href="/contractor/contractor-configs" tab="contractor-configs">
        <IonLabel>{t('ContractorDashboard.contractorConfig')}</IonLabel>
      </IonTabButton>
      <IonTabButton href="/contractor/workers-management" tab="workers-management">
        <IonLabel>{t('ContractorDashboard.workersManagement')}</IonLabel>
      </IonTabButton>
      <IonTabButton href="/contractor/email-responses" tab="email-responses">
        <IonLabel>Some label</IonLabel>
      </IonTabButton>
    </IonTabBar>

    <IonRouterOutlet>
      <Route path="/contractor/contractor-configs" render={() => <ContractorConfigs userId={authUser.uid} />} />
      <Route path="/contractor/workers-management" component={WorkersManagement} />
      <Route path="/contractor/email-responses" render={() => <EmailResponses userId={authUser.uid} />} />
      <Route path="/contractor/contractor-overview" render={() => <IonTitle>Title</IonTitle>} />
      <Route exact path="/contractor" render={() => <Redirect to="/contractor/overview" />} />
    </IonRouterOutlet>
          
  </IonTabs>
</IonContent>

Everything works fine until I try to create dynamic navigation to /contractor route, which is the root of the tabs’ router, using history.push('/contractor') or <Redirect to="/contractor" />.

In my login component I want to redirect automatically to the ContractorDashboard if a user is authenticated. I.e. after a successful login the user should be redirected to the dashboard. I implemented the following:

// Login.tsx

useEffect(() => {
  if (authUser) {
    history.push('/contractor')
  }
}, [authUser, history])

What happens is the URL is changed in the address bar but the login page is still visible instead of the ContractorDashboard. I also tried this:

// Login.tsx

if (authUser) {
  return <Redirect to="/contractor"/>
}

return (
// ...
)

The result is the same. I can see the contractor page in the dev tools but it’s not rendered.
A few notes though:

  • The result is a bit different when I try different things, like pushing to /contractor/overview insted of just /contractor. Then the contractor dashboard is visible for a sec and then it’s the login page again.
  • Sometimes I do get the dashboard, but the tabs are not visible. Its in the DOM and the cursor events are there but still not visible.
  • Everything was working before using IonTabs in the dashboard.
  • What’s weird is that when I try to navigate to the login page via the address bar with an authenticated user I’m redirected to the dashboard like I should and everything is working.

I did try replacing IonReactRouter with Router from react-router like suggested in this answer. Then redirecting from the login page works but the tabs trigger a full page refresh on every tab change, which is just awful UX.

Here is my ionic info output:

Ionic:

   Ionic CLI : 6.11.0

Utility:

   cordova-res : not installed
   native-run  : not installed

System:

   NodeJS : v12.18.3
   npm    : 6.14.6
   OS     : Windows 10

and here’s my package.json:

{
  "name": "application",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "@capacitor/android": "^2.4.6",
    "@capacitor/core": "2.4.6",
    "@ionic/react": "^5.0.7",
    "@ionic/react-router": "^5.0.7",
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.4.0",
    "@testing-library/user-event": "^8.0.3",
    "@types/jest": "^24.0.25",
    "@types/node": "^12.12.24",
    "@types/react": "^16.9.17",
    "@types/react-dom": "^16.9.4",
    "@types/react-router": "^5.1.4",
    "@types/react-router-dom": "^5.1.3",
    "firebase": "^8.2.3",
    "i18next": "^19.8.4",
    "i18next-http-backend": "^1.0.22",
    "ionicons": "^5.0.0",
    "react": "^16.13.0",
    "react-dom": "^16.13.0",
    "react-i18next": "^11.8.5",
    "react-redux": "^7.2.2",
    "react-router": "^5.1.2",
    "react-router-dom": "^5.1.2",
    "react-scripts": "^4.0.1",
    "redux": "^4.0.5",
    "redux-localstorage-simple": "^2.3.1",
    "redux-thunk": "^2.3.0",
    "typescript": "3.8.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "appium": "appium --chromedriver-executable C:\\Users\\Ori\\code\\chromedrivers\\chromedriver-83-0-4103.exe",
    "e2e:android": "protractor e2e/android-e2e.conf.js",
    "e2e:web": "protractor e2e/web-e2e.conf.js"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@capacitor/cli": "2.4.6",
    "@types/jasmine": "^3.6.3",
    "@types/jasminewd2": "^2.0.8",
    "@types/react-redux": "^7.1.15",
    "@types/redux-logger": "^3.0.8",
    "appium": "^1.20.2",
    "jasmine": "^3.6.4",
    "jasmine-spec-reporter": "^6.0.0",
    "protractor": "^7.0.0",
    "redux-logger": "^3.0.6",
    "ts-node": "^9.1.1"
  },
  "description": "An Ionic project"
}

So is there a way to properly implement this and how?
Please let me know if any info or code is missing here.
Any help will be greatly appreciated!