1
votes

I'm running react-router-dom 4.1.1, I followed multiple React Router guides, even doing the react-router-tutorial which worked on my computer (though it was using react-router v2 or something similar). When I attempt to use react-router-dom v4 on a simple application, I run into many errors.

Scroll Down for Current Code & Error

//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory'

import App from './components/App';
import About from './components/pages/about';

const customHistory = createBrowserHistory()

ReactDOM.render((
  <Router history={customHistory}>

    <Switch>
      <Route path='/' component={App} />
      <Route path='/about' component={About}/>
    </Switch>



  </Router>
), document.getElementById('root'))

This code alone works and renders my 'App' component

But when I try to add a 'Link' component in my App component, it won't recognize it.

//App.js
import React from 'react';
import Header from './Header';
import { Link } from 'react-router-dom'

class App extends React.Component {

  render() {
    return (
      <div className="App">

        <Header className="Header" header="Header" />


        <main className='main'>
            <Link to='about'>About</Link>
        </main>

      </div>
    );
  }
}

export default App;

If I run this, I get the error:

TypeError: Cannot read property 'history' of undefined at Link.render (/Users/Ryan/Desktop/df/grnd/node_modules/react-router-dom/Link.js:76:35) at /Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactCompositeComponent.js:795:21 at measureLifeCyclePerf (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactCompositeComponent.js:75:12) at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactCompositeComponent.js:794:25) at ReactCompositeComponentWrapper._renderValidatedComponent (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactCompositeComponent.js:821:32) at ReactCompositeComponentWrapper.performInitialMount (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactCompositeComponent.js:361:30) at ReactCompositeComponentWrapper.mountComponent (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactCompositeComponent.js:257:21) at Object.mountComponent (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactReconciler.js:45:35) at ReactDOMComponent.mountChildren (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactMultiChild.js:236:44) at ReactDOMComponent._createContentMarkup (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactDOMComponent.js:659:32)

If I comment out the 'Link' component in App.js, the program runs and loads 'App' from index.js.

This is only one error I've received out of many as I try to figure out why I can't run it. I have also received errors in which it says 'Route' is undefined or that 'Router' can't have nested children and so on. I find this problem to be at it's simplest.

The history I used for this example was taken from the example given on: https://reacttraining.com/react-router/web/api/Router/history-object

3

3 Answers

0
votes

First of all, you use a BrowserRouter, this one creates its own history object and uses this one. Therefore in your case you shouldn't be passing a history object to it. Actually it should even print a warning in your console after I looked at the code of BrowserRouter. And if I were you I'd keep it named as BrowserRouter, it makes it less error prone and clearer when you read the code.

Then, in v4, the Router can only have 1 child. But in the case of the Switch, it actually mounts only the matching route. I'm not sure if it's intended to be like this, try to change your hierchy to something different, and have the child of Router to always be mounted. Here would be an example taking your code :

//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter, Route, Switch} from 'react-router-dom';

import App from './components/App';

ReactDOM.render((
  <BrowserRouter>
    <App />    
  </BrowserRouter>
), document.getElementById('root'));

And then in your App you build your routing logic, that even makes more sense, since your App is probably the application, therefore it should always be present.

//App.js
import React from 'react';
import Header from './Header';

class App extends React.Component {

  render() {

    return (
      <div className="App">
        <Header className="Header" header="Header" />
        <Switch>
          <Route exact path='/' component={Home} />
          <Route path='/about' component={About}/>
        </Switch>
      </div>
    );
  }

}

And your links would probably be inside the Header I suppose, or you may want to build a special Nav component for this, that may be included in the Header or some place else. Let's do it in the Header for now and see how that would work:

//Header.js
import React from 'react';
import {Link} from 'react-router-dom';

class Header extends React.Component {

  render() {

    return (
      <header className="header">
          <Link to='/'>Home</Link>
          <Link to='/about'>About</Link>
      </header >
    );

  }

}
0
votes

Current Code

//index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './components/App';

ReactDOM.render((
  <BrowserRouter>
    <App/>
  </BrowserRouter>
), document.getElementById('root'))

//App.js

import React from 'react';
import Header from './Header';
import Main from './Main';

class App extends React.Component {

  render() {
    return (
      <div className="App">

        <Header className="Header" header="Header" />

        <Main className="Main" />

      </div>
    );
  }
}

export default App;

//Main.js

import React from 'react';
import { Switch, Route } from 'react-router-dom'
import Home from './Home';
import About from './About';

class Main extends React.Component {
    render() {
        return (
            <div className='Main-Container'>
                <Switch>
                    <Route exact path='/' component={Home} />
                    <Route path='/about' component={About} />
                </Switch>
            </div>
        )
    }
}

export default Main;

Most Recent Error

Warning: Failed context type: The context router is marked as required in Switch, but its value is undefined. in Switch (created by Main) in div (created by Main) in Main (created by App) in div (created by App) in App TypeError: Cannot read property 'route' of undefined at Switch.render (/Users/Ryan/Desktop/df/grnd/node_modules/react-router/Switch.js:48:36) at /Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactCompositeComponent.js:795:21 at measureLifeCyclePerf (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactCompositeComponent.js:75:12) at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactCompositeComponent.js:794:25) at ReactCompositeComponentWrapper._renderValidatedComponent (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactCompositeComponent.js:821:32) at ReactCompositeComponentWrapper.performInitialMount (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactCompositeComponent.js:361:30) at ReactCompositeComponentWrapper.mountComponent (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactCompositeComponent.js:257:21) at Object.mountComponent (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactReconciler.js:45:35) at ReactDOMComponent.mountChildren (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactMultiChild.js:236:44) at ReactDOMComponent._createContentMarkup (/Users/Ryan/Desktop/df/grnd/node_modules/react-dom/lib/ReactDOMComponent.js:659:32)

0
votes

I found out what's causing the error: Server Rendering

Currently, I am using this piece of code

 //server.js

 server.get('/', (req, res) => {
      res.render('index', {
        content: ReactDOMServer.renderToString(<App />)
      });
    });

Which renders to my ejs template

//index.ejs

<%- include('header') -%>
   <div id="root"><%- content -%></div>
<%- include('footer') -%>

When I comment out content: ReactDOMServer.renderToString(<App />)and switch it to something like content: 'Hello' .... <Route/> and <Link/> work.