Reputation: 3344
I'm architecting a large application and am hamstrung on a react routing issue. I would love to know how to work through this routing/props/params conflict.
Here's the basic procedure:
Login ->
Choose classroom (get classroom._id) ->
Load BaseLayout (this retrieves the classroom from the DB) ->
Load RosterLayout (or any other subdivision of the application (schedule, photos, etc.)
Since each page of the classroom (schedule, roster, etc.) is it's own mini application, I think I would load those layout components using react-router. However, I can't figure out how to structure it so that I can use the classroom
loaded inside the BaseLayout
component inside of the child components. In this example I've tried passing the _id
via URL, but then I still have to retrieve the classroom again which doesn't seem like good architecture and I won't have classroom_loading
available to know when it's ready. Is there a better or different way of handling this scenario besides URL params? Can I pass props to a child component loaded via react-router?
Here's the basic code:
Meteor.startup(() => {
render((
<Router history={browserHistory}>
<Route path="/classroom/:classroomId" component={BaseLayoutContainer}>
<Route path="roster" component={RosterLayout} />
</Route>
</Router>
), document.getElementById('app'));
});
class BaseLayout extends React.Component {
render() {
return(
<div id="base">
<div id="headerContainer">
</div>
<div id="navContainer">
<div className="classroomHeader">
{this.props.classroom_loading ?
<h3>loading...</h3> :
<h2>{this.props.classroom.name}</h2>
}
</div>
</div>
<div id="bodyContainer">
{this.props.children}
</div>
</div>
)
}
}
export default BaseLayoutContainer = createContainer((params) => {
let classroom_loading = Meteor.subscribe("classroom.byId", params.params.classroomId);
return {
currentUser: Meteor.user(),
classroom_loading: !classroom_loading.ready(),
classroom: Classrooms.findOne({}),
};
}, BaseLayout);
export class RosterLayout extends React.Component {
render() {
return (
<div id="rosterLayout">
{this.props.children_loading ?
<span>loading...</span> :
<ul>
{this.props.children.map((child) => {
return <li>child.name</li>;
})}
</ul>
}
</div>
);
}
}
export default RosterLayoutContainer = createContainer((params) => {
let children_loading = Meteor.subscribe("children.byClassroom", params.params.classroomId);
return {
children_loading: !children_loading.ready(),
children: Children.findOne({}),
};
}, RosterLayout);
Upvotes: 1
Views: 140
Reputation: 3344
I've found a great solution thanks to @slightlytyler in the #reactiflux server of discord. Thanks Tyler!
Instead of blindly loading children to the BaseLayout like this:
<div id="bodyContainer">
{this.props.children}
</div>
...you can clone the incoming child and append your own props to it, which worked perfectly in this scenario.
<div id="bodyContainer">
{React.Children.map(this.props.children, (
return React.cloneElement(child, {
classroom_loading: this.props.classroom_loading,
classroom: this.props.classroom
});
})}
</div>
Upvotes: 1