Unable to access App component when using React Router

Viewed 1229

I am trying to make my application more route driven/controlled. I am using React Router and an external Routes file in order to abstract the route logic out of my components. The other routes and components are working properly except for the App component itself. I am essentially trying to wrap all of the other routes under the root route ('/') who's component is App.js.

Below is my routes/index.js file.

import React from "react";
import PropTypes from "prop-types";
import { Router, Route, Redirect  } from "react-router";

import App from "../App.js";
import Home from "../components/Home/Home";
import Games from "../components/Games/containers/Games";
import LoginSignUp from "../components/LoginSignUp/LoginSignUp";
import NotFound from "../components/NotFound/NotFound";
import Account from "../components/Account/Account";
import Trivia from "../components/Trivia/Trivia";
import store from '../config/store';

const getGame = (nextState, replace, callback) => {
    var userGames = store.getState().auth.user.games;
    if (userGames.includes(parseInt(nextState.match.params.id))){
        return <Trivia/>
    } else {
        return <Redirect to="/404"/>
   }
};

const Routes = ({ history, store }) => {

    return (
         <Router history={history} store={store}>
            <Route path="/" component={App}>
                <Route exact path="/404" component={NotFound} />
                <Route exact path="/give/different/path" component={Home}/>
                <Route exact path="/games" component={Games}/>
                <Route path="/account" component={Account}/>
                <Route path="/games/:id" render={getGame}/>
                <Route path='/login' component={LoginSignUp}/>
            </Route>
        </Router>
    );
};

Routes.propTypes = {
    history: PropTypes.object.isRequired,
    store: PropTypes.object.isRequired
};

export default Routes;

I have also tried this route setup without <Route exact path="/" component={Home}/> and I've tried using <Route exact path="/" component={App}> to no avail.

Below is my App.js component.

import React from 'react';
import Header from './components/Header/Header.js';
import './App.css';
import {createAction} from "redux-actions";
import {connect} from "react-redux";

class App extends React.Component {

    state = {
        loggedIn: true
    }

    render(){
         debugger
         return (
             <div className="App">
                 <Header loggedIn={this.state.loggedIn} user={this.props.user}/>
             </div>
         );
     }
 }

 const mapStateToProps = ({games, auth}) => {
    return{
        user: auth.user,
        games: games.games
    }
 };

 const mapDispatchToProps = dispatch => {
    return {
        getGame(obj) {
            const actionCreator = createAction(
                "games/getGame"
            );
            const action = actionCreator(obj);
            dispatch(action);
        }
    }
};

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(App);

Here are my related dependencies and their versions.

"react-router": "^5.1.2",
"react-router-dom": "^5.1.0",
"react-router-redux": "^4.0.8",
3 Answers

It wont work like that, look that you have a path / for App and then / for Home, it can't match properly. Please review the React Router docs. Here is the link to the nested routes example.

To have you going somewhere look at this modification to your Router You have:

            <Route path="/" component={App}>
                <Route exact path="/404" component={NotFound} />
                <Route exact path="/" component={Home}/>
                <Route exact path="/games" component={Games}/>
                <Route path="/account" component={Account}/>
                <Route path="/games/:id" render={getGame}/>
                <Route path='/login' component={LoginSignUp}/>
            </Route>

but will work better

            <Route exact path="/" component={App} />
            <Route exact path="/home" component={Home}/>
            <Route exact path="/account" component={Account}/>
            <Route exact path="/games" component={Games}/>
            <Route exact path="/games/:id" render={getGame}/>
            <Route exact path='/login' component={LoginSignUp}/>
            <Route component={NotFound} />

I think you should change your code in this way. And also check path to app component when importing

<Router history={history} store={store}>
  <Switch>
    <Route path="/" component={App} />
    <Route exact path="/404" component={NotFound} />
    <Route exact path="/home" component={Home} />
    <Route exact path="/games" component={Games} />
    <Route path="/account" component={Account} />
    <Route path="/games/:id" render={getGame} />
    <Route path="/login" component={LoginSignUp} />
  </Switch>
</Router>;

With the insight provided by the other answers, I was able to come up with a solution to satisfy all of my needs.

Here are my updated files. The first is my root index file. As a precaution, I replaced all instances of import {Router} from 'react-router' with import {BrowserRouter as Router} from 'react-router-dom' which was suggested by this issue's answer. Replacing all instances of react-router with react-router-dom helped stop the You should not use <Switch> outside a <Router> or You should not use <Route> outside a <Router> errors.

    <Provider store={store}>
        <Router history={browserHistory} store={store}>
            <App/>
        </Router>
    </Provider>

My routes/index.

const Routes = ({ history, store }) => {

    return (
            <Switch>
                <Route exact path="/" component={Home} />
                <Route exact path="/404" component={NotFound} />
                <Route exact path="/games" component={Games} />
                <Route exact path="/account" render={accountDetails}/>
                <Route path="/games/:id" render={getGame} />
                <Route exact path="/login" component={LoginSignUp} />
                <Redirect to='404' />
            </Switch>
    );
};

And App now includes the below.

import Routes from './routes/index.js';

class App extends React.Component {

    state = {
        loggedIn: true
    };

    render(){
        return (
            <div className="App">
                <Header/>
                <Routes/>
            </div>
        );
    }
}

First, I decided to take App out of routes/index. I based my original route hierarchy based on another project that was not using react-router 4. That's why in the other project the nested routes under App's route was achieving what I was trying to. In react router 4 this was not possible, hence the Cannot use Link outside of Router error. So instead, I wrapped App in the Router in my root index file, then wrapped all other routes in a Switch in my routes/index file and imported them into App. App can now utilize Router for the Header component as well as for the other routes wrapped in the Switch.

This also solved my issue for having to sacrifice exact path for App. Since I wanted to use the Header on every page I wanted App to be on every page. Using exact path prevented me from doing this. It also prevented me from using '/' to render Home. When I made App's exact path to just path, I was able to accomplish this, but any random undeclared route could be put in. For example, localhost:3000/poop would not render NotFound as I intended, it would just render nothing.

In summary, by moving App out of routes/index it allowed me to wrap it directory in Router in the root index file. Then by importing Routes into App I was able to allow my app to be more route driven while maintaining the Header across all pages. Also, by wrapping all of my routes in a Switch and having the root path be an exact path, I could properly handle unknown paths and render the NotFound component by using <Redirect to='404' />.

Related