3

I am trying to setup my react app.

I am trying to understand how to integrate the routes with the app.

When I try to use AppRouter in my ReactDOM, I get an error message that says I shouldn't use Route outside the Router.

I don't understand what the error means. I can get rid of the error message when I remove the AppRouter line from my Provider, but that only creates a new error with the provider. I can't find an example of how to get started.

My app.js has:

import React from 'react';
import ReactDOM from 'react-dom';

import { Provider } from 'react-redux';
import AppRouter from './routers/AppRouter.js';
import { BrowserRouter } from 'react-router-dom';
import configureStore from './store/configureStore.js';
// import { startSetUsers } from './actions/users';


import 'normalize.css/normalize.css';
import './styles/styles.scss';
import './firebase/firebase';
// import * as firebaseui from 'firebaseui'

//import './playground/promises';


const store = configureStore();

const jsx = (
    <Provider store={store}>
        <AppRouter />
    </Provider>
);

ReactDOM.render(jsx, document.getElementById('app'));

My AppRouter has:

import React from 'react';
import { connect } from 'react-redux';
import { BrowserRouter, Route, Switch, Link, NavLink, withRouter } from 'react-router-dom';
import Header from '../components/Header.js';
import Footer from '../components/Footer.js';

import Dashboard from '../components/home/Dashboard.js';
import Landing from '../components/home/Landing.js';
import ErrorNotice from '../components/ErrorNotice.js';
import SignIn from '../components/auth/RegisterPage.js';
import Pending from '../components/auth/PendingPage.js';
import SignInSuccess from '../components/auth/SignInSuccess.js';


import Users from '../components/users/UserDashboard.js';

// this Higher Order Component wraps the app and listens for Firebase auth change state event
// when this state changes, it updates the store
import withAuthentication from '../hoc/withAuthentication';
import AuthenticatedRoute from  '../components/auth/AuthenticatedRoute';

const AppRouter = () => {
    return (
            <div>
                <Header />
                <Switch>
                    <Route path="/" exact={true} component={Landing} />
                    {/* Authentication Related routes */}
                    <Route path="/Signin" component={SignIn} />
                    {/* This route no longer required. Was used when uiConfig has a redirect URL */}
                    {/* <Route path="/Loading" component={SignInSuccess} /> */}
                    <Route path="/Pending" component={Pending} />
                    {/* PUBLIC ROUTES */}
                    <Route path="/Users" component={Users} />
                    <Route path="/Menu" component={Menu} />
                    {/* AUTHENTICATED ROUTES */}
                    {/* Places routes that require authenitcation here and use the AuthenticatedRoute */}
                    <AuthenticatedRoute path="/Dashboard" component={Dashboard} />
                    <Route component={ErrorNotice} />
                </Switch>
                <Footer />
            </div>
    )
}



// set up passing of store state as component props
const mapStateToProps = state => ({
    authUser: state.sessionState.authUser,
});

// connect this component to the store
// wrap withRouter to ensure that Links work: => https://reacttraining.com/react-router/core/guides/redux-integration/blocked-updates
export default withRouter(connect(mapStateToProps)(AppRouter));

Can anyone see where I'm going wrong?

revised AppRouter.js

import React from "react";
import { connect } from "react-redux";
import {
  BrowserRouter,
  Route,
  Switch,
  Link,
  NavLink,
  withRouter
} from "react-router-dom";
import Header from "../components/Header.js";
import Footer from "../components/Footer.js";

import Dashboard from "../components/home/Dashboard.js";
import Landing from "../components/home/Landing.js";
import ErrorNotice from "../components/ErrorNotice.js";
import SignIn from "../components/auth/RegisterPage.js";
import Pending from "../components/auth/PendingPage.js";
import SignInSuccess from "../components/auth/SignInSuccess.js";

import About from "../components/footerlinks/company/About.js";
import Users from "../components/users/UserDashboard.js";

// this Higher Order Component wraps the app and listens for Firebase auth change state event
// when this state changes, it updates the store
import withAuthentication from "../hoc/withAuthentication";
import AuthenticatedRoute from "../components/auth/AuthenticatedRoute";

const AppRouter = () => {
  <BrowserRouter>
    <div>
      <Header />
      <Switch>
        <Route path="/" exact={true} component={Landing} />
        {/* Authentication Related routes */}
        <Route path="/Signin" component={SignIn} />
        {/* This route no longer required. Was used when uiConfig has a redirect URL */}
        {/* <Route path="/Loading" component={SignInSuccess} /> */}
        <Route path="/Pending" component={Pending} />
        {/* PUBLIC ROUTES */}
        <Route path="/About" component={About} />
        <Route path="/Users" component={Users} />
        <Route path="/Menu" component={Menu} />
        {/* AUTHENTICATED ROUTES */}
        {/* Places routes that require authenitcation here and use the AuthenticatedRoute */}
        <AuthenticatedRoute path="/Dashboard" component={Dashboard} />
        <Route component={ErrorNotice} />
      </Switch>
      <Footer />
    </div>
  </BrowserRouter>;
};

// set up passing of store state as component props
const mapStateToProps = state => ({
  authUser: state.sessionState.authUser
});

// connect this component to the store
// wrap withRouter to ensure that Links work: => https://reacttraining.com/react-router/core/guides/redux-integration/blocked-updates
export default connect(mapStateToProps)(AppRouter);

console errors after removing withRouter from the import statement:

Warning: Failed prop type: Invalid prop `component` of type `object` supplied to `Route`, expected `function`.
    in Route (created by AppRouter)
    in AppRouter (created by Connect(AppRouter))
    in Connect(AppRouter)
    in Provider

Warning: AppRouter(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
printWarning @ warning.js:33
warning @ warning.js:57
warnIfInvalidElement @ ReactCompositeComponent.js:51
mountComponent @ ReactCompositeComponent.js:193
mountComponent @ ReactReconciler.js:45
performInitialMount @ ReactCompositeComponent.js:370
mountComponent @ ReactCompositeComponent.js:257
mountComponent @ ReactReconciler.js:45
performInitialMount @ ReactCompositeComponent.js:370
mountComponent @ ReactCompositeComponent.js:257
mountComponent @ ReactReconciler.js:45
performInitialMount @ ReactCompositeComponent.js:370
mountComponent @ ReactCompositeComponent.js:257
mountComponent @ ReactReconciler.js:45
mountComponentIntoNode @ ReactMount.js:104
perform @ Transaction.js:143
batchedMountComponentIntoNode @ ReactMount.js:126
perform @ Transaction.js:143
batchedUpdates @ ReactDefaultBatchingStrategy.js:62
batchedUpdates @ ReactUpdates.js:97
_renderNewRootComponent @ ReactMount.js:319
_renderSubtreeIntoContainer @ ReactMount.js:401
render @ ReactMount.js:422
(anonymous) @ app.js:29
__webpack_require__ @ bootstrap 8dde10c53183363cc06e:19
(anonymous) @ bundle.js:50261
__webpack_require__ @ bootstrap 8dde10c53183363cc06e:19
module.exports @ bootstrap 8dde10c53183363cc06e:62
(anonymous) @ bootstrap 8dde10c53183363cc06e:62
invariant.js:42 Uncaught Error: AppRouter(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object
Mel
  • 2,481
  • 26
  • 113
  • 273
  • Use the`BrowserRouter` you have imported from `react-router-dom` as the topmost component in `AppRouter` to wrap everything inside it and it should work. `const AppRouter = () => {/* ... */}` – Tholle Jul 16 '18 at 00:45
  • Hi @Tholle - I tried it, but I get the same error. – Mel Jul 16 '18 at 00:52
  • Do that and also remove the `withRouter` HOC you are using on the export and try again. – Tholle Jul 16 '18 at 00:53
  • Do you mean like this: export default(connect(mapStateToProps)(AppRouter)); ? I know its wrong - there is a long list of errors generated by trying this, but I'm lost for where to start in figuring out what is required. – Mel Jul 21 '18 at 01:23
  • Can you create an example on https://codesandbox.io/s/new with `BrowserRouter` added? – Roy Wang Jul 21 '18 at 01:44
  • I added an AppRouter.js to this code sandbox https://codesandbox.io/s/xlnjv9zn4p showing the browser router, I've also copied it above – Mel Jul 21 '18 at 01:50
  • @Mel, can you create a reproducible demo of your issue, Your updated code seems correct. Also not that you shouldn't connect AppRouter with withRouter since Router is defined inside of AppRouter – Shubham Khatri Jul 21 '18 at 08:01
  • @ShubhamKhatri - the only change I made to try your advice is removing 'withRouter' from my import statement, but I still have lots of console errors - which are copied above. – Mel Jul 22 '18 at 01:35
  • Your problem comes from one of your component : Landing / Dashboard / SignIn / Pending / About / Users / Menu or ErrorNotice. To find wich one is causing troubles, try to comment all `` components, and add one Route at a time to check your compilation errors. – Pintouch Jul 26 '18 at 10:11
  • And you should return your AppRouter : `const AppRouter = () => ()` and `const AppRouter = () => { return }` is correct but `const AppRouter = () => {}` is not returning your component, maybe you did that on one of your component that I listed above ? – Pintouch Jul 26 '18 at 10:20

4 Answers4

2

I think the problem is that your AppRouter is not returning anything. In the arrow function if you write () => {statement} it will execute the statement but if you write () => statement it will return it. So you should modify the AppRouter to:

const AppRouter = () => (
  <BrowserRouter>
    ...
  </BrowserRouter>
);

More info about the arrow functions:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Hope it helps.

peetya
  • 3,578
  • 2
  • 16
  • 30
  • Hi @peetya, thanks very much for this. I tried your suggestion, but I get an error that says: Failed prop type: Invalid prop `component` of type `object` supplied to `Route`, expected `function`. Although the landing page does load. This could be a step towards a solution. I'll read more into it. – Mel Jul 29 '18 at 01:16
  • You're welcome, let me know if you need further help, I'll do my best :) – peetya Jul 30 '18 at 09:38
1

Hi @Mel I faced similar issue a few days back, and i resolved it as following:

  1. In your index.html, check that your id is app and not root.

  2. Modify your app.js to have:

    const jsx = (
     <Provider store={store}>
        <BrowserRouter>
          <AppRouter />
        </BrowserRouter>
     </Provider>
    );
    
  3. Now, your AppRouter needs to be a class based component, so it would become as following after modifying:

// All your imports come here

  class AppRouter extends Component {
     render() {
       let routes = (
          <Switch>
            <Route path="/" exact={true} component={Landing} />
            <Route path="/Signin" component={SignIn} />
            <Route path="/Pending" component={Pending} />
            <Route path="/Users" component={Users} />
            <Route path="/Menu" component={Menu} />
            <AuthenticatedRoute path="/Dashboard" component={Dashboard} />
            <Route component={ErrorNotice} />
          </Switch>
       );
       return (
         <div>
            <Header />
            {routes}
            <Footer />
        </div>
       );
     }
   }
   const mapStateToProps = state => ({
      authUser: state.sessionState.authUser
   });
   export default withRouter(connect(mapStateToProps)(AppRouter));

If you still face issues, let me know, I can share more code.

Hope it helps! Happy Coding! ;)

Kapil Sharma
  • 141
  • 1
  • 15
  • thanks so much for this. I tried it, but I still get 2 errors, as follows:1. failed prop type: Invalid prop `component` of type `object` supplied to `Route`, expected `function` 2.A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object. – Mel Jul 29 '18 at 01:11
  • Hi @Mel, with reference to you current errors: 1. This error has something to do with returning multiple statements in some of your component, please [see this](https://stackoverflow.com/questions/42528273/a-valid-react-element-or-null-must-be-returned-you-may-have-returned-undefine) for reference. 2. Second error means that some component of yours passed in is not correct. Please check [this link](https://jsfiddle.net/kapil5harma/0e4zopf3/9/), I have created a JSFiddle example for syntax of functional and class based components. – Kapil Sharma Jul 30 '18 at 04:12
  • I have updated the Fiddle example to be more informative: Here's the updated link: https://jsfiddle.net/kapil5harma/0e4zopf3/23/ – Kapil Sharma Jul 30 '18 at 04:27
0

index.js or appRouter.js should contain this type of routes written

import ReactDOM from 'react-dom';
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom';
import store from './redux-state.js';


ReactDOM.render(
  <Provider store={store}>
    <BrowserRouter>
      <App>
        <Switch>
          <Route path="/" exact={true} component={Landing} />
          <Route exact path="/" component={Home}/>
          <Route component={Error404} />
        </Switch>
      </App>
    </BrowserRouter>
  </Provider>,
  document.getElementById('appRoot'),
  renderCommon
);

while in your app.js you can write following and it should work fine

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';

function mapStateToProps(state) {
  return {};
}

function mapDispatchToProps(dispatch) {
  return {};
}

class App extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      {this.props.children}
    )
  }
}
-1

Just copy and paste the code. It will work. Message me if you have any issue

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Header from '../components/Header.js';
import Footer from '../components/Footer.js';
import NotFound from "../../ui/pages/notfound";

import Dashboard from '../components/home/Dashboard.js';
import Landing from '../components/home/Landing.js';
import ErrorNotice from '../components/ErrorNotice.js';
import SignIn from '../components/auth/RegisterPage.js';
import Pending from '../components/auth/PendingPage.js';
import SignInSuccess from '../components/auth/SignInSuccess.js';


import Users from '../components/users/UserDashboard.js';

// this represent ur actions
import { togglemenu } from "../../../actions/index";
import { bindActionCreators } from 'redux';


class AppRouter extends Component {
  render() {
    return (
      <BrowserRouter>
        <Header />
          <div>
            <Switch>
              <Route path="/" exact={true} component={Landing} />
              <Route path="/Signin" exact={true} component={SignIn} />
              <Route path="/Pending" exact={true} component={Pending} />
              {/* PUBLIC ROUTES */}
              <Route path="/Users" exact={true} component={Users} />
              <Route path="/Menu" exact={true} component={Menu} />
              <Route component={NotFound} />
            </Switch>
          </div>
        <Footer />
     </BrowserRouter>
   )
 }
}


function mapStateToProps(state) {
  return {
   // ur redux state
   home: state.home
  }
}
function mapDispatchToProps(dispatch) {
  return bindActionCreators({
  // ur redux action
  togglemenu: togglemenu
  }, dispatch)
}

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