meteormeteor-react

Meteor/React - How to wait for Meteor.user()


I'm struggling to figure out how to wait for the Meteor.user() subscription in a react component. I know the user is logged in because Meteor.userId() returns the _id, but trying to access the email address show Meteor.user() returns undefined. I assume because it's not available just yet.

Alas, since Meteor.user() isn't a publication I manually subscribe to, I'm not sure how to wait for it in a React component. Can someone point me towards an example?

import { Meteor } from 'meteor/meteor';
import React from 'react';
import { Link } from 'react-router';
import { createContainer } from 'meteor/react-meteor-data';

import './BaseLayout.scss';


class BaseLayout extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            isAuthenticated: (Meteor.userId() !== null) ? true : false,
        }
    }

    componentWillMount() {
        if (!this.state.isAuthenticated) {
            this.context.router.push('/login');
        }
    }

    componentDidUpdate(prevProps, prevState) {
        if (!this.state.isAuthenticated) {
            this.context.router.push('/login');
        }
    }

    toggleUserMenu() {
        this.refs.userMenu.style.display = (this.refs.userMenu.style.display === 'block') ? 'none' : 'block';
    }

    logout(e) {
        e.preventDefault();
        Meteor.logout();
        this.context.router.push('/login');
    }

    render() {
        return(
            <div id="base">
                <div id="header">
                    <div id="top-bar" className="clear-fix">
                        <div className="float-left" id="logo"></div>
                        <div className="float-right" id="user-menu">
                            <div onClick={this.toggleUserMenu.bind(this)}>
                                <span>{this.props.currentUser.emails[0].address}</span>
                                <div className="arrow-down"></div>
                            </div>
                            <div id="user-menu-content" ref="userMenu">
                                <a onClick={this.logout.bind(this)}>Sign Out</a>
                            </div>
                        </div>
                    </div>
                </div>
                <div id="bodyContainer">
                    {this.props.children}
                </div>
                <div id="footer">
                    <ul>
                        <li><a href="mailto:test@example.com">Made by Us</a></li>
                    </ul>
                </div>
            </div>
        )
    }
}
BaseLayout.contextTypes = {
    router: React.PropTypes.object.isRequired,
}


export default BaseLayoutContainer = createContainer(() => {

    var handle = Meteor.subscribe("user.classrooms");

    return {
        currentUser: Meteor.user(),
        dataLoading: !handle.ready(),
        classrooms: Classrooms.find({}).fetch(),
    };
}, BaseLayout);

Solution

  • You could use a ternary operation

    currentUser ? true : flase
    

    Also a great tip to keep components organized is to always destructure for example instead of doing const currentUser = this.props.currentUser you can do const { currentUser } = this.props; Have a look:

    render() {
    const { currentUser, children } = this.props;
      return (
      <div id="base">
        <div id="header">
          <div id="top-bar" className="clear-fix">
            <div className="float-left" id="logo"></div>
            <div className="float-right" id="user-menu">
              <div onClick={this.toggleUserMenu.bind(this)}>
               {currentUser ?
                 <span>{currentUser.emails[0].address}</span> :
                 <span>Loading...</span>
               }
                <div className="arrow-down"></div>
              </div>
              <div id="user-menu-content" ref="userMenu">
                <a onClick={this.logout.bind(this)}>Sign Out</a>
              </div>
            </div>
          </div>
        </div>
        <div id="bodyContainer">
          {children}
        </div>
        <div id="footer">
          <ul>
            <li><a href="mailto:test@example.com">Made by Us</a></li>
          </ul>
        </div>
      </div>
    )
    }