3

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",
Dostonbek Oripjonov
  • 981
  • 1
  • 9
  • 21
Matt Croak
  • 2,279
  • 2
  • 11
  • 22

3 Answers3

0

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} />
Lukasz 'Severiaan' Grela
  • 5,518
  • 4
  • 34
  • 69
  • Thanks for the response. I am able to hit the App component but I get an error saying `You should not use outside a ` which I assume is coming from my nav component which is in `App.js` but since `App` itself is wrapped in the `` wouldn't it make sense that it's child components (and any subsequent links) would also be wrapped in ``? – Matt Croak Oct 08 '19 at 02:25
  • It should, and if you;ve looked at the link you will see that this is the case. Look like it is done in the docs. – Lukasz 'Severiaan' Grela Oct 08 '19 at 08:55
  • I checked out the link and it was helpful. Technically my ``'s are in the `
    ` component but that is only being imported in `` and nowhere else. Like `` get's hit before `
    `. Unless the `` has to literally be in `` as opposed to a child of ``.
    – Matt Croak Oct 08 '19 at 15:03
  • Is there any way I can have the other components be wrapped in `` using routes? I want the `
    ` in `` to be present on all pages and I was able to do this by making `App`'s path inclusive (not exact path) but this creates an issue where the URL can be `/not-valid-url` and then it would render the `
    ` and a blank page.
    – Matt Croak Oct 08 '19 at 16:24
0

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>;
Dostonbek Oripjonov
  • 981
  • 1
  • 9
  • 21
  • Thanks for the response. I tried this way but I get an error saying `You should not use outside a ` which I assume is coming from my nav component which is in `App.js` but since `App` itself is wrapped in the `` wouldn't it make sense that it's child components (and any subsequent links) would also be wrapped in ``? Also what is the significance of using `exact path` for `Home` and `path` for `App`? – Matt Croak Oct 08 '19 at 02:27
  • https://stackoverflow.com/questions/49162311/react-difference-between-route-exact-path-and-route check it – Dostonbek Oripjonov Oct 08 '19 at 02:32
  • yeah u r right if your `````` is in app component then it should work – Dostonbek Oripjonov Oct 08 '19 at 02:34
  • Technically it's in the `
    ` component but that is only being imported in `` and nowhere else. Like `` get's hit before `
    `. Unless the `` has to literally be in `` as opposed to a child of ``.
    – Matt Croak Oct 08 '19 at 14:59
  • The switch doesn't maintain the `App` with the child components, it appears to show just `App`. When I removed the switch it worked better. – Matt Croak Oct 08 '19 at 15:57
0

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' />.

Matt Croak
  • 2,279
  • 2
  • 11
  • 22