3

I am trying to use react-router but I am not able to propagate children components.

index.js

import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';

import App from './App';

import Login from './containers/Login';

const rootElement = document.getElementById('app');

render((
 <Router history={browserHistory}>
  <Route path="/" component={App}>
   <Route path="login" component={Login}/>
  </Route>
 </Router>
), rootElement);
App.js

import React, { Component, PropTypes } from 'react';
import { Login } from './containers';

export default class App extends Component {

 constructor(props) {
  super(props);
 }

 render() {
  const { children } = this.props;
  return (
   <div className="content">
    {children}
   </div>
  );
 }
}

App.propTypes = {
 children: PropTypes.any,
};
LoginPage.js

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';

import { Login } from '../components';

export default class LoginPage extends Component {

 constructor(props, context) {
  super(props, context);
 }

 handleSubmit(event) {
  event.preventDefault();
 }

 render() {
  const { handleSubmit, redirect } = this.props;
  return (
   <Login handleSubmit={handleSubmit}
          redirect={redirect}
   />
  );
 }
}
LoginComponent.js

import React, { Component, PropTypes } from 'react';

export default class Login extends Component {

 constructor(props, context) {
  super(props, context);

  this.state = {
   email: '',
   password: '',
  };
 }

 handleChange(field, event) {
  const nextState = this.state;

  nextState[field] = event.target.value;

  this.setState(nextState);
 }

 handleSubmit(event) {
  event.preventDefault();

  this.props.handleSubmit(this.state);
 }

 render() {
  return (
   <form onSubmit={event => this.handleSubmit(event)}>
    <input
     type="text" placeholder="Email"
     value={this.state.email}
     onChange={this.handleChange.bind(this, 'email')}
    />
    <input
     type="password" placeholder="Password"
     value={this.state.password}
     onChange={this.handleChange.bind(this, 'password')}
    />
    <input type="submit" value="Submit"/>
   </form>
  );
 }
}

Login.propTypes = {
 handleSubmit: PropTypes.func.isRequired,
};

If I just import LoginPage directly into App.js where I try to render {children} it works perfectly fine. On inspection it simply says children is undefined

react@0.14.6

react-dom@0.14.6

react-router@2.0.0-rc5

As a side note, I ran npm list react-router and I got this back

`-- (empty)

npm ERR! code 1

Any help would be great!!

Edit: I edited the first code snippet to be import Login from './containers/Login'; from import { Login } from './containers/Login';

That was a type from simplifying the problem. I had it the other way originally because I am actually using an index.js for containers and was calling import { Login } from './containers';

I have stepped through the code and it shows that Login is NOT undefined in index.js but children is when I get to App.js

Below is a screenshot of a breakpoint in index.js and App.js in the same run. index.js shows Login as being initialized but then children is undefined.

index.js

[App.js[2]

Okay I have simplified the whole thing as much as possible now into a single file and it still doesn't work

import 'babel-polyfill';
import React, { Component } from 'react';
import { render } from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';

class App extends Component {

 constructor(props) {
  super(props);
 }

 render() {
  const { children } = this.props;
  return (
   <div className="content">
    {children}
   </div>
  );
 }
}

class Child extends Component {
 render() {
  return (
   <p>I am a child</p>
  );
 }
}

const rootElement = document.getElementById('app');

render((
 <Router history={browserHistory}>
  <Route path="/" component={App}>
   <Route path="login" component={Child}/>
  </Route>
 </Router>
), rootElement);

I then ran it and got the following

enter image description here

Then I added <Child /> directly into the render property of App and got this

enter image description here

So this is not a problem with how I am importing files etc.

aray12
  • 1,833
  • 1
  • 16
  • 22
  • In your index.js file, were is the code for this --> `import { Login } from './containers/Login';` looks like you need to import the `LoginPage.js` file there? – deowk Jan 21 '16 at 09:16
  • @deowk The name of the file was actually `containers/Login.js` but I exported it as `class LoginPage`. I realize this is confusing and refactored so it is as simple as possible with no imports from custom files and I am still having this problem. – aray12 Jan 21 '16 at 18:06
  • Works out of the box for me, when i copy/paste it. Could you post your package.json and the output of npm list here? – larrydahooster Jan 21 '16 at 23:25

2 Answers2

2

The solution is quite simple. Replace

import { Login } from './containers/Login';

with

import  Login  from './containers/Login';

in your index.js

The reason why your child property was always 'undefined' is because the passed over component was 'undefined':

enter image description here

If you have questions regarding the import syntax i can recommend this SO Question "using brackets with javascript import syntax"

See full code: index.js

import React from 'react';
import { render } from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';

import App from './App';
import Login from './containers/LoginPage';

const rootElement = document.getElementById('app');

render((
    <Router history={browserHistory}>
        <Route path="/" component={App}>
            <Route path="login" component={Login}/>
        </Route>
    </Router>
), rootElement);

App.js

import React, { Component, PropTypes } from 'react';

export default class App extends Component {

    constructor(props) {
        super(props);
    }

    render() {
        const { children } = this.props;
        return (
            <div className="content">
                {children}
            </div>
        );
    }
}

App.propTypes = {
    children: PropTypes.any,
};

./containers/LoginPage.js

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';

import Login from '../components/Login';

export default class LoginPage extends Component {

    constructor(props, context) {
        super(props, context);
    }

    handleSubmit(event) {
        event.preventDefault();
    }

    render() {
        const { handleSubmit, redirect } = this.props;
        return (
            <Login handleSubmit={handleSubmit}
                   redirect={redirect}
            />
        );
    }
}

./components/Login.js

import React, { Component, PropTypes } from 'react';

export default class Login extends Component {

    constructor(props, context) {
        super(props, context);

        this.state = {
            email: '',
            password: '',
        };
    }

    handleChange(field, event) {
        const nextState = this.state;

        nextState[field] = event.target.value;

        this.setState(nextState);
    }

    handleSubmit(event) {
        event.preventDefault();

        this.props.handleSubmit(this.state);
    }

    render() {
        return (
            <form onSubmit={event => this.handleSubmit(event)}>
                <input
                    type="text" placeholder="Email"
                    value={this.state.email}
                    onChange={this.handleChange.bind(this, 'email')}
                />
                <input
                    type="password" placeholder="Password"
                    value={this.state.password}
                    onChange={this.handleChange.bind(this, 'password')}
                />
                <input type="submit" value="Submit"/>
            </form>
        );
    }
}

Login.propTypes = {
    handleSubmit: PropTypes.func.isRequired,
};

Proof with react 0.14.6 and react-router 2.0.0-rc5 enter image description here

Community
  • 1
  • 1
larrydahooster
  • 4,114
  • 4
  • 40
  • 47
  • By the way, i get the same output when i run npm list react-router, but don't know what this is about. But somehow this is not related to your issue – larrydahooster Jan 21 '16 at 09:43
  • @larrydohooster Thanks for the response. That was a typo when trying to simplify the problem and remove a bunch of redux code. But that isn't the problem. – aray12 Jan 21 '16 at 16:09
  • That's strange, I setup your code locally and ran it with your given setup and this worked for me – larrydahooster Jan 21 '16 at 16:22
  • @larrydohooster Sorry I didn't mean to send that last comment. I have edited my original question. That doesn't seem to be the problem. I have called Login directly into `App.js` the same way I do in `index.js` and placed it in there manually and it works. I have also added some stuff showing how it is initialized in `index.js` at the bottom of my question. What version of react-router are you using? – aray12 Jan 21 '16 at 16:29
  • Wait, i will share my code. It totally runs for me with your given setup – larrydahooster Jan 21 '16 at 16:35
  • There were some confusing imports and namings that needed to be fixed too – larrydahooster Jan 21 '16 at 16:42
  • From what i see in your code shared here, which is not your full setup, i can only tell, that you should take a deeper look into the module system. Especially what '{ }' and 'export default' and stuff is for – larrydahooster Jan 21 '16 at 16:49
  • @larrydohooster again thank you for your help. This has been driving me insane for a couple of days. I have edited the question again with an example in a single file that does not import any of my own custom files (only from required libraries). I promise I understand how to use the es2015 module system and realize that my original example was poorly written and didn't reflect that. Based on what you see in the last file is there another problem you can see. – aray12 Jan 21 '16 at 18:03
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/101330/discussion-between-aray12-and-larrydahooster). – aray12 Jan 21 '16 at 18:09
1

Ok so answering my own question. Basically a really stupid mistake but maybe someone will benefit. I was using localhost/#/child because I thought this is what it was supposed to say and localhost/child hits an registered route on my server. So the fix was to make my server-side route handler

router.get('/*', (req, res) => {
  res.render(view);
});

And then navigate to localhost/child

aray12
  • 1,833
  • 1
  • 16
  • 22