Scott
Scott

Reputation: 3344

Large React Application Structure

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

Answers (1)

Scott
Scott

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

Related Questions