Search code examples
javascriptreactjsreact-routerreact-router-component

React-Router 1.0.0RC1 - Passing state as properties to child routes


I am working off Alex Bank's "Building a Polling App with Socket IO and React.js" (Lynda.com) , but I am trying to upgrade it to react-router 1.0.0-RC1.

Solution below, just skip all this

Please do not send me to documentation, it is not working for me, I guess I must be too thick to understand the documentation's "pithiness".

I have a main APP with 3 child routes, (Audience, Speaker & Board).

My code so far:

APP.js

import React, { Component } from 'react';
import io from 'socket.io-client';
import Header from './parts/Header';
import Routes from '../router/routes';
import { createHistory, useBasename } from 'history';

const history = useBasename(createHistory)({
  basename: '/'
});

export default class APP extends Component {

  constructor() {
    super();
    this.state = ({
      status: 'disconnected',
      title: ''
    });
  }

  componentWillMount() {
    this.socket = io('http://localhost:3000');
    this.socket.on('connect', this.connect.bind(this));
    this.socket.on('disconnect', this.disconnect.bind(this));
    this.socket.on('welcome', this.welcome.bind(this));
  }

 connect() {
    this.setState({status: 'connected'});
 }

 disconnect() {
    this.setState({status: 'disconnected'});
 }

 welcome(serverState) {
    this.setState({title: serverState.title});
 }

render() {
   return (
     <div>
       <Header title={ this.state.title } status={ this.state.status }/>
       { /* I WANT TO PASS THIS.STATE.STATUS TO CHILD ROUTES  */}
       <Routes history={ history } />
     </div>
    );
  }
}

Routes.js

import React, { Component } from 'react';
import Route from 'react-router';
import APP from '../components/APP';
import Audience from '../components/Audience';
import Board from '../components/Board';
import Speaker from '../components/Speaker';
import NotFound from '../components/NotFound';


export default class Routes extends Component {
  constructor() {
    super();
  }

  render() {
    return (
      <Route history={ this.props.history } component={ APP }>
        <Route path="/" component={ Audience } />
        <Route path="audience" component={ Audience } />
        <Route path="board" component={ Board } />
        <Route path="speaker" component={ Speaker } />
        <Route path="*" component={ NotFound } />
      </Route>
    );
  }
}

Audience.js

import React, { Component } from 'react';

export default class Audience extends Component {

 constructor() {
   super();
 }


 render() {
   return (
     <div>
      Audience - STUCK HERE!! - How to pass APP's this.state.status as a prop????        
     </div>
   );
 }

}

Although the app runs, and I have read the documentation, I am still unable to pass APP's this.state.status as a property to the Audience app.

I have been at this for 2 days to no avail and it is becoming frustrating. TGIF.

Desired Result:

When a browser is opened to localhost:3000, the default page (Audience.js), should read as:

 Untitled Presentation - connected

 Audience - connected

I cannot get the status of connected passed to the Audience component so the word 'connected' is not showing up next to Audience. I am connected as evidenced by Header's "Untitled Presentation - connected"

Can someone assist me here.

Many Thanks!


Solution

  • SOLUTION:

    As Clarkie mentioned, I did have cyclic dependancy because I was following a legacy setup which used react-router 0.13 and had APP as the entry point.

    Much of the help for this problem came from iam4x/isomorphic-flux-boilerplate

    It is 'sad' detailed assistance could not have been found directly from the react-router documentation.


    My new entry point is now:

    Index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import Router from 'react-router';
    import createBrowserHistory from 'history/lib/createBrowserHistory';
    
      const routerProps = {
        routes: require('./router/routes'),
        history: createBrowserHistory(),
        createElement: (component, props) => {
          return React.createElement(component, { ...props});
        }
      };
    
      ReactDOM.render(
        React.createElement(Router, { ...routerProps }),
        document.getElementById('root')
      );
    

    Routes.js:

    Note: I especially like how they did the routes, because I can see quite clearly how to turn this dynamic (w/ data from Db) for large apps.

    import React from 'react';
    import { Route } from 'react-router';
    import { generateRoute } from '../utils/localized-routes';
    
    export default (
      <Route component={ require('../components/APP') }>
        { generateRoute({
          paths: ['/', '/audience'],
          component: require('../components/Audience')
        }) }
        { generateRoute({
          paths: ['/speaker'],
          component: require('../components/Speaker')
        }) }
        { generateRoute({
          paths: ['board'],
          component: require('../components/Board')
        }) }
        <Route path="*" component={ require('../components/NotFound') } />
      </Route>
    );
    

    localized-routes.js:

    import React from 'react';
    import { Route } from 'react-router';
    
    export function generateRoute({ paths, component }) {
      return paths.map(function(path) {
        const props = { key: path, path, component };
        // Static `onEnter` is defined on
        // component, we should pass it to route props
        if (component.onEnter) props.onEnter = component.onEnter;
        return <Route {...props} />;
      });
    }
    
    // SWEET!!! Nice touch.
    export function replaceParams(route, params) {
      let parsedRoute = route.trim();
      Object.keys(params).forEach(function(paramKey) {
        const param = ':' + paramKey;
        const paramValue = params[paramKey];
        if (parsedRoute && parsedRoute.match(param)) {
          parsedRoute = parsedRoute.replace(param, paramValue);
        }
      });
      return parsedRoute;
    }
    

    APP.js:

    import React, { Component, PropTypes } from 'react';
    import io from 'socket.io-client';
    import Header from './parts/Header';
    
    export default class APP extends Component {
      static propTypes = {
        children: PropTypes.element
      }
    
      constructor(props, context) {
       super(props, context);
       this.state = ({
        status: 'disconnected',
        title: ''
      });
     }
    
     componentWillMount() {
      this.socket = io('http://localhost:3000');
      this.socket.on('connect', this.connect.bind(this));
      this.socket.on('disconnect', this.disconnect.bind(this));
      this.socket.on('welcome', this.welcome.bind(this));
    }
    
     connect() {
       this.setState({ status: 'connected' });
    }
    
     disconnect() {
       this.setState({ status: 'disconnected' });
    }
    
     welcome(serverState) {
       this.setState({ title: serverState.title });
    }
    
     renderChild = () =>
       React.cloneElement(this.props.children, { status: this.state.status });
    
     render() {
       return (
          <div>
             <Header title={ this.state.title } status={ this.state.status }/>
            { React.Children.map(this.props.children, this.renderChild) }
          </div>
        );
      }
    }
    

    Audience.js:

    import React, { Component } from 'react';
    import Display from './Display';
    
    export default class Audience extends Component {
      constructor(props) {
        super(props);
      }
    
      render() {
        return (
          <div>
           Audience - { this.props.status }
            <Display if={ this.props.status === 'connected' }>
              <h1>Join the session</h1>
            </Display>
          </div>
         );
       }
     }
    

    Display.js:

    import React, { Component } from 'react';
    
    export default class Display extends Component {
      render() {
        return (
            <div>
            { this.props.if ? <div> { this.props.children } </div> : null }
            </div>
            );
        }
    }
    

    DESIRED RESULT:

    enter image description here