Pass State through CreateContainer and child component?


#1

Hey,

I’m using Meteor with React, I have a main container for a Task component where I subscribe on Tasks. Now, on child components, I display the tasks but I would like to have a dropdown to sort the tasks (createdAt, Name, etc …)

I did this using Session with Blaze, but in this, I’m using React and it looks not working the same. I don’t know how to make the sorting tasks list reactive. What I’m trying to do now (I don’t know if it’s the good method) is to set state in App Component and send it to the container and children.

AppClass

constructor(props){
    super(props);
    this.state = this.getMeteorData();
    this.sortTasks = this.sortTasks.bind(this);
}

getMeteorData(){
    return { 
        sorting: { createdAt: -1 },
    };
}

sortTasks(sorting) {
    this.setState({ sorting: sorting });
}


render(){
    const childrenWithProps = React.Children.map(this.props.children, (child) => 
        React.cloneElement(child, {
            sorting: this.state.sorting,
            sortTasks: this.sortTasks.bind(this),
        })
    );
    return <div>{childrenWithProps}</div>
}

Container

export default TodosContainer = createContainer(({props}) => {

  Meteor.subscribe('tasks');

  return {
    tasks: Tasks.find({}, { sort: props.sorting }).fetch(),
    sorting: props.sorting,
    sortTasks: props.sortTasks,
  };

}, TodosPage);

TodosPage

const propTypes = {
  ....
  sorting: PropTypes.string.isRequired,
}

render() {
  return (
    ...
     {(() => {
        if (this.state.listSelected) {  
            return (
                <TaskPanel 
                    tasks = {this.props.tasks} 
                    sorting = {this.props.sorting}
                    sortTasks = {this.props.sortTasks}
                />
            );
        }
    })()}

And after, the Task Panel, child of the TodosPage:


const propTypes = {
  ....
  sorting: PropTypes.string.isRequired,
}

handleSelect(event, sorting) {
  switch(event) {
        case 1: return this.props.sortTasks({ createdAt: -1 });
        case 2: return this.props.sortTasks({ createdAt: 0 });
  };
}

render() {
  return (
    <DropdownButton 
        onSelect={(event, sorting) => this.handleSelect(event, this.props.sorting)}
        <MenuItem eventKey="1">Created Date Asc</MenuItem>
        <MenuItem eventKey="2">Created Date Desc</MenuItem>
    </DropdownButton>
    ...
    {this.renderTasks()}
    ...

last point, the routes:

    <Route path="/admin" component={AdminAppContainer}>
        <IndexRoute component={AdminMainContainer}/>
        <Route path="todos" component={TasksContainer}>
            <Route path=":listId" component={TodosPage}/>
        </Route>
    </Route>

Here is the imbricated components … Also, I need just the state “sorting” in this TasksContainer, but not in the other children of AppClass … Can I distribute this state only to this child ?

Thanks for the help :slight_smile:


#2

Also, is the best way would to leave this with state or using a session ?

Thanks


#3

Perhaps you can :

  1. return from your routes a top-level container (perhaps passing in some route-derived props such as current view, item id, etc.). This top-level container stores the React state.
  2. In the top-level container, render a Meteor data fetching container, passing down the React state, callback functions as props
  3. Within the Meteor data container, use CreateContainer (see Meteor guide) to make reactive Meteor subscriptions based on the props passed in as {params}
  4. Combine the React state props with the returned Meteor data, and pass all data down as props to your presentational components.