Reputation:
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!
Upvotes: 0
Views: 543
Reputation:
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:
Upvotes: 0
Reputation: 7550
In your APP
component you need to include the following:
{React.cloneElement(this.props.children, {status: this.state.status })}
Then in your audience
component you'll have that available as this.props.status
.
Edit:
I've just noticed that you have a cyclic dependency. I'd recommend getting rid of that so that the dependency is only in one direction:
routes.js --> app.js --> audience.js
Have a look at this example. This could be broken up into three files by extracting the two React classes:
main.js
this renders the routesApp.js
this renders the app and includes the child routesTaco.js
this renders the taco.This can then be represented as follows:
main.js --> App.js --> Taco.js
Upvotes: 1