meteor-react

Meteor subscription in React using a passed prop errors if page is refreshed


Im using React with Meteor. In my top level component I have my main subscriptions:

export default withTracker(() => {
    let groupsSub = Meteor.subscribe('groups');
    let eventsSub = Meteor.subscribe('events');
    let userSub = Meteor.subscribe('currentUser');
    return {
        groups: Groups.find({}).fetch(),
        events: Events.find({}).fetch(),
        user: Meteor.user() || false,
    };
})(App);

The subscription data is passed as props to a child component which is on a new page (using React Router 4).

This is working so far. On the child page I also need to get an ID from the props and use it as part of an additional subscription called CommentsApiSub:

export default withTracker(props => {
    const attachedTo = props.group[0]._id;
    let CommentsApiSub = Meteor.subscribe('comments', { attachedTo });
    return {
        comments: CommentsApi.find({}).fetch(),
    };
})(Child);

And this is the CommentsApi publication:

Meteor.publish('comments', function({ attachedTo }) {
    console.log(attachedTo);
    return CommentsApi.find(
        { attachedTo },
        { fields: { date: 1, body: 1, user: 1 } },
    );
});

If I navigate to the page it works fine, but if I refresh the page I get an error:

Uncaught TypeError: Cannot read property '_id' of undefined

I know this is because props.group hasnt loaded but im not sure how to delay calling my comments subscription?


Solution

  • You need to check if if Meteor subscription is ready before attempting to load your components. Meteor.subscribe() returns a subscription handle, which contains a reactive data source called ready(). As an example by using your code;

    export default withTracker(() => {
        const groupsSub = Meteor.subscribe('groups');
        // Remaining of the code here 
        return {
            loading: !groupsSub.ready()
            groups: Groups.find({}).fetch(),
            // Other props 
        };
    })(App);
    

    And in your render() method, you can use that loading prop to check if the subscription ready;

    render () {
            const { loading } = this.props;
            if (loading) {
                return (
                    <h2>Loading Page ...</h2>
                );
            }
           // Remaining of the code
    }
    

    You can refer to the official Meteor documentation here.