Milos
Milos

Reputation: 1253

React router's switch statement doesn't render component

I'm having an issue of when I click a link in the navbar that's in the header, instead of the component loading below, the whole page changes (and is blank. No error is visibly thrown, but not even the component that's supposed to render on that route shows up.

I'll explain how this site setup works.

const initialState = {};
const history = createHistory();
const store = configureStore(initialState, history);
const MOUNT_NODE = document.getElementById('app');

const render = (messages) => {
  ReactDOM.render(
    <Provider store={store}>
      <LanguageProvider messages={messages}>
        <ConnectedRouter history={history}>
          <Switch>
            <Route exact path="/" component={Splash} />
            <Route path="/main" component={App} />
          </Switch>
        </ConnectedRouter>
      </LanguageProvider>
    </Provider>,
    MOUNT_NODE
  );
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

When a user first enters the site, they see the splash page. Then they click a button that gets them to the main homepage. Here's the code for that:

const AppWrapper = styled.div`
  max-width: calc(768px + 16px * 2);
  margin: 0 auto;
  display: flex;
  min-height: 100%;
  padding: 0 16px;
  flex-direction: column;
`;

class App extends React.Component {
  render() {
    return (
      <div>
        <Header />
        <AppWrapper>
          Below this are where the new components should render.
          <Switch>
            <Route path="/aboutme" component={AboutMe} />
            <Route path="/models" component={Models} />
            <Route path="/landscapes" component={Landscapes} />
          </Switch>
          <Footer />
        </AppWrapper>
      </div>
    );
  }
}

export default App;

As you can see I'd like the header to remain on top regardless of what component is selected by the route.

The header's navbar looks like this:

class NavBar extends React.Component {

  render() {
    return (
      <div>

        <AppBar position="static" color="default" >
          <Toolbar>
            <Typography type="title" color="inherit">
              <Link to="/models">Models</Link>
            </Typography>
            <Typography type="title" color="inherit">
              <Link to="/landscapes">Adventures</Link>
            </Typography>
            <Typography type="title" color="inherit">
              <Link to="/aboutme">About Me</Link>
            </Typography>
          </Toolbar>
        </AppBar>
      </div>
    );
  }
}

export default NavBar;

When I click a link here, the route in the address bar changes appropriately, but a blank page shows up without even the header/toolbar. Does anyone have any ideas on what I'm doing wrong here? I'm trying to use a react/redux boilerplate and am still learning.

Upvotes: 0

Views: 1972

Answers (3)

Tr1et
Tr1et

Reputation: 895

You are mapping route /main to App component, and the route /models,/landscapes, /aboutme is rendered inside the App component, so your routes look like this:

  • / (exact match)
  • /main
    • /models
    • /landscapes
    • /aboutme

See the problem? A route can never match both the /main and /models,... at the same time. So those routes never got rendered.

Solution

Change the route of App component to / (not exact match), so the code in the index.js will look like this:

...
<ConnectedRouter history={history}>
  <Switch>
    <Route exact path="/" component={Splash} />
    <Route path="/" component={App} />
  </Switch>
</ConnectedRouter>
...

Explanation

The react-router's Switch will use the first route to match with the current route (e.g. https://mywebsite.com/path/to/some/where). A route is match if the URL begin with the path value (e.g. /, /models,...), with / character at the begin indicate the root of the website (so /models is equivalent to https://mywebsite.com/models, this is called short absolute path).

So, when you are in https://mywebsite.com/models, react-router matching the path /models with the routes:

  • / exact route`: not match because exact route requires the route to be exactly the same
  • /main route: not match, /models not start with /main

Then the react-router renders nothing because no route is match.

When you change /main to /, the result will be like this:

The router then renders the App component, then, in the inner switch:

  • /aboutme route: not match,
  • /models route: match, renders the Models component and skip the following routes.

Upvotes: 1

Travis Cook
Travis Cook

Reputation: 171

When you update the route with NavBar component, the new route (ex: /models) will not be caught in your render of the MOUNT_NODE.

The routes handled by MOUNT_NODE are only “/“ and “/main”. Which is why any route besides them renders a blank page.

To make it work in your situation you would have to make all the Links in the NavBar begin with “/main” and the routes in the App component match.

Here is some good info on react-router v4. CSS-Tricks React Router 4

Upvotes: 1

Abundance Oshianor
Abundance Oshianor

Reputation: 184

This is how i set it up to work for me. Using React-router v4

my index file

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App.jsx';
import 'bootstrap/dist/css/bootstrap.css';
import registerServiceWorker from './registerServiceWorker';
import 'font-awesome/css/font-awesome.min.css';
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import appReducer from './containers/reducers/index.js';

const store = createStore(appReducer, applyMiddleware(thunk));
ReactDOM.render(
     <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('app'));
 registerServiceWorker();

    store.subscribe(() => {
      console.log("Store Changed, ", store.getState());
     });

my App file where i setup my routes

import React, { Component } from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';

import Splash from './containers/home/Home.jsx';
import Model from './containers/home/About.jsx';

export default class Routes extends Component{
    render(){
       return (
         <BrowserRouter>
           <div>
            <Header />
            <Switch>
                <Route exact path="/" component={Splash} />
                <Route path="/models" component={Models} />
               <Route path="/landscapes" component={Landscapes} />
            </Switch>
            <Footer />
           </div>
         </BrowserRouter>
       )
      }
     }


       <Typography type="title" color="inherit">
          <Link to={"/models"} >Models</Link>
        </Typography>

NOTE: There is a <Header /> Component and a <Footer /> component. I did this becasue my header and footer are constant through out my app. This should work, just follow the basic and edit to work for you.

Upvotes: 0

Related Questions