Meteor user data not always ready in render()

I’ve got a layout component:

MyLayout = React.createClass({
  displayName: 'MyLayout',
  mixins: [ReactMeteorData],

  getMeteorData() {
    return {
      currentUser: Meteor.user()
    }
  },

  render() {
    return (
      <div className="my-layout">
        <Toolbar />

        <div className="content">
          <div className="container-fluid">
            {React.cloneElement(this.props.content, this.data)}
          </div>
        </div>
      </div>
    )
  }
});

And this is the component passed into that layout via Flow Router:

Home = React.createClass({
  displayName: 'Home',

  render(p = this.props) {
    if (!p.currentUser) {
      console.error("We're not ready yet");
      return false;
    }

    return (
      <div>
        <div className="welcome-message">
          Welcome back, {p.currentUser.profile.firstName}.
        </div>
        <p>
        </p>
      </div>
    )
  }
});

I’m wondering why there’s a scenario where p.currentUser is undefined. A subscription I understand, but as a logged-in user, isn’t that data immediately available? Is there a better way to do this?

I’ve modified the render method of MyLayout:

  render() {
    if (!this.data.currentUser) {
      return false;
    }

    return (
      <div className="my-layout">
        <Toolbar />

        <div className="content">
          <div className="container-fluid">
            {React.cloneElement(this.props.content, this.data)}
          </div>
        </div>
      </div>
    )
  }

The package you are looking for is meteorhacks:fast-render. In addition to sending down configured data with the first page load, it will also send down data for “null” publications. Since the data for the logged in user is sent via a null publication, it will be sent down and available right away.

1 Like

Thanks! I’ll give that a shot.

I came across this old post trying to overcome the same problem as the original poster. I checked out fast-render but it seemed like overkill to basically deal with a problem that only happens when you manually reload the browser. I’m also using react-redux and react-router, and tried so very many things that ended up not working. I tried so very hard to get all the logic out of my presentation component, but nothing worked. So here’s my solution:

I have an App component that is pretty normal, and then a Meteor data component that wraps it and delivers the Meteor.user() object in its various incarnations via a prop, as usual.

The container component:

// AppContainer.jsx
import { Meteor } from 'meteor/meteor';
import { createContainer } from 'meteor/react-meteor-data';
import App from '../components/App.jsx';

export default createContainer(({ params }) => {
  const currentUser = Meteor.user();

  return {
    currentUser,
  };
}, App);

And the presentation component:

// App.jsx
import React, { Component } from 'react';
import { Meteor } from 'meteor/meteor';
import { createContainer } from 'meteor/react-meteor-data';
import MyNavbar from './Navbar.jsx';
import Hero from './Hero.jsx';

export default class App extends Component {
  render() {
    // This seems hacky, but this.props.currentUser is subscribed
    // to Meteor.user(). When Meteor.user() changes, the prop updates and this
    // component re-renders. Uncomment the log statement to see it happen.
    // This test below deals with the fact that Meteor.user() is undefined
    // during the first rendering of this component, and we should wait for it
    // to update before we display user-level components other than the navbar
    // rather than briefly show the logged out homepage before the logged in
    // page.
    // console.log(this.props);

    var userDataAvailable = true;
    var currentUser = this.props.currentUser;
    if (currentUser === undefined) {
      userDataAvailable = false;
    }

    var loggedOut = (!currentUser && userDataAvailable);
    var loggedIn = (currentUser && userDataAvailable);

    children = this.props.children;
    return (
      <div>
        <MyNavbar />
        {loggedOut ? <Hero /> : null }
        {loggedIn ? children : null}
      </div>
    );
  }
}

console.log reloading when signed in:

App.jsx:18 this.props.currentUser: undefined
App.jsx:18 this.props.currentUser: [object Object]

console.log reloading when signed out:

App.jsx:18 this.props.currentUser: null

I hope this helps someone out. Cheers!

3 Likes

@jerimiah797 You are awesome man! I was exactly in the same circumstances as you. Thanks for posting this! :grinning:

1 Like