Problems with React router doesn't load the routes of components

Viewed 31

I have this problem and I don't know how to solve it, the Approuter have 2 routes, the public (login) and the private (DashboardRoutes) who have the other routes. Dashboard only load the first component and its all.

the code is it


    import React, { useEffect } from 'react'
    import { useDispatch, useSelector } from 'react-redux'
    import {
      BrowserRouter as Router,
      
      Switch,
    } from "react-router-dom";
    
    import { LoginScreen } from '../components/LoginScreen';
    import { DashboardRoutes } from './DashboardRoutes';
    import { PrivateRoute } from './PrivateRoute';
    import { PublicRoute } from './PublicRoute';
    import { startChecking } from '../actions/auth'
    
    
    
    export const AppRouter = () => {
      
      const { checking, uid } = useSelector( state => state.auth );
      const dispatch = useDispatch();
      
      useEffect(() => {
          
        dispatch (startChecking());
    
      }, [dispatch])
      
      if (checking) {
        return <div>Checking...</div>
      }
    
      console.log(checking)
      console.log(uid)
      
      return (
      
        
        <Router>
          <div>
            <Switch>
              
              <PublicRoute 
                exact 
                path="/login" 
                component={LoginScreen} 
                isAuthenticated={ !!uid } 
                />
              
              <PrivateRoute 
                exact 
                path="/" 
                component={DashboardRoutes}
                isAuthenticated={ !!uid } 
                />
      
            </Switch>
          </div>
        </Router>
        
      )
    }

import React from 'react'
import { Redirect, Route, Switch } from 'react-router-dom'
import { CharacterScreen } from '../components/screen/CharacterScreen'
import { FavoritesScreen } from '../components/screen/FavoritesScreen'
import { FilmScreen } from '../components/screen/FilmScreen'
import { PlanetsScreen } from '../components/screen/PlanetsScreen'
import { StarHome } from '../components/StarHome'

import { Navbar } from '../components/ui/Navbar'


export const DashboardRoutes = () => {
   
  return (
        
    <>
      <Navbar />  

        <div className='container mt-2'>
          <Switch>
              <Route exact path='/starhome' component={ StarHome } />
              <Route exact path='/planets' component={ PlanetsScreen } />
              <Route exact path='/character' component={ CharacterScreen } />
              <Route exact path='/films' component={ FilmScreen } />
              <Route exact path='/favorites' component={ FavoritesScreen } />
              {/* <Route exact path='/' component={ StarHome } /> */}
               

          </Switch>
        </div>
      </>
    )
}

import React from 'react';
import PropTypes from 'prop-types';

import { Route, Redirect } from 'react-router-dom';

console.log("esto es el public")

export const PublicRoute = ({
    isAuthenticated,
    component: Component,
    ...rest
}) => {

    return (
        <Route { ...rest }
            component={ (props) => (
                ( isAuthenticated )
                    ? ( <Redirect to="/" /> )
                    : ( <Component { ...props } /> )
            )}
        
        />
    )
}

PublicRoute.propTypes = {
    isAuthenticated: PropTypes.bool.isRequired,
    component: PropTypes.func.isRequired
}

import React from 'react';
import PropTypes from 'prop-types';

import { Route, Redirect } from 'react-router-dom';

console.log("Este es el private")
export const PrivateRoute = ({
    isAuthenticated,
    component: Component,
    ...rest
}) => {

    return (
        <Route { ...rest }
            component={ (props) => (
                ( !isAuthenticated )
                    ? ( <Component { ...props } /> )
                    : ( <Redirect to="/login" /> )
            )}
        
        />
    )
}

PrivateRoute.propTypes = {
    isAuthenticated: PropTypes.bool.isRequired,
    component: PropTypes.func.isRequired
}

in case you need to see the dispatch

export const startChecking = () => {
    return async ( dispatch ) => {
    
    const resp = await fetchConToken( 'auth/renew');
    const body = await resp.json();

    if( body.ok ) {
        localStorage.setItem('token', body.token);
        localStorage.setItem('token-init-date', new Date().getTime() );
        
        dispatch( login ({
            uid: body.uid,
            name: body.name
        }) )
    } else {
        dispatch( checkingFinish() );
    }
        
    }
}

only charge the navbar now. I did other tests where it only loads the navbar with the starhome but I couldn't continue from there

I hope you can help me

2 Answers

Did you try in this way ?

  <Router>
    <Route path="" component={} />
  </Router>

Issue

The issue is with how you've declared the root route paths, both are only matched when the URL path is exactly "/" or "/login".

<Switch>
  <PublicRoute
    exact
    path="/login"
    component={LoginScreen}
    isAuthenticated={!!uid}
  />
  <PrivateRoute
    exact
    path="/"
    component={DashboardRoutes}
    isAuthenticated={!!uid}
  />
</Switch>

When exactly matching this necessarily excludes matching any sub-routes. For example, if the URL path was "/planets" this doesn't exactly match either "/" or "/login" and no route is matched and no content is rendered.

Solution

Remove the exact prop on the root routes, and any route that also renders descendent routes so the sub-routes and also be matched and rendered.

<Switch>
  <PublicRoute
    path="/login"
    component={LoginScreen}
    isAuthenticated={!!uid}
  />
  <PrivateRoute
    path="/"
    component={DashboardRoutes}
    isAuthenticated={!!uid}
  />
</Switch>

Note that it can sometimes be a little tricky when the "/" path isn't rendering any specific component, like a home page, and you are trying to either create a "catch all" NotFound route/component somewhere, so I often recommend giving those "sub-routes" and actual "subdomain" namespace.

Example:

<PrivateRoute
  path="/dashboard"
  component={DashboardRoutes}
  isAuthenticated={!!uid}
/>

...

export const DashboardRoutes = () => {
  const { path } = useRouteMatch();

  return (
    <>
      <Navbar />
      <div className='container mt-2'>
        <Switch>
          <Route path={[`${path}/starhome`, path]} component={StarHome} />
          <Route path={`${path}/planets`} component={PlanetsScreen} />
          <Route path={`${path}/character`} component={CharacterScreen} />
          <Route path={`${path}/films`} component={FilmScreen} />
          <Route path={`${path}/favorites`} component={FavoritesScreen} />
        </Switch>
      </div>
    </>
  );
}

The dashboard routes would now render on "/dashboard", "/dashboard/starhome", etc...

I suggest also a minor refactor on the custom route components so they aren't incorrectly using the component prop.

Examples:

export const PublicRoute = ({ isAuthenticated, ...props }) => {
  return isAuthenticated
    ? <Redirect to="/" />
    : <Routes {...props} />;
};

export const PrivateRoute = ({ isAuthenticated, ...props }) => {
  return isAuthenticated
    ? <Routes {...props} />
    : <Redirect to="/login" />;
}

Now the public/private routes can use all the Route component's render methods, i.e. component, and render and children function props.

Related