Meteor / React - 如何等待Meteor.user()

时间:2016-09-18 16:54:37

标签: meteor meteor-react

我正在努力弄清楚如何在react组件中等待Meteor.user()订阅。我知道用户已登录,因为Meteor.userId()返回_id,但尝试访问电子邮件地址show Meteor.user()会返回undefined。我假设因为它还没有出现。

唉,既然Meteor.user()不是我手动订阅的出版物,我不知道如何在React组件中等待它。有人能指出我的榜样吗?

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);

5 个答案:

答案 0 :(得分:4)

您可以使用三元操作

currentUser ? true : flase

另外,保持组件有条理的一个很好的提示是始终destructure,而不是const currentUser = this.props.currentUser你可以做const { currentUser } = this.props;看看:

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>
)
}

答案 1 :(得分:4)

接受的答案是好的但是过了一段时间后你会注意到你将不得不在许多组件上重复这么多次。或者,您可以在使用Accounts.loginServicesConfigured()进行顶级组件渲染之前,先确保Tracker为真。引用meteor文档:“函数Accounts.loginServicesConfigured()是一个被动数据源,一旦配置了登录服务就会返回true”。我正在使用meteor,react,redux和路由器。

import React, { Component } from 'react';
import { Accounts } from 'meteor/accounts-base';
import { Route, Switch } from 'react-router-dom';
import { Tracker } from 'meteor/tracker';

class App extends Component {
  constructor(props) {
    super(props);

    this.state = { loading: true };
  }

  componentWillMount() {
    Tracker.autorun(() => {
      if (Accounts.loginServicesConfigured()) {
        this.setState({ loading: false });
      }
    });
  }

  render() {
    return (
      <div className="app">
        {
          this.state.loading ? (<Spinner />) : (
            <Route exact path="/" component={HomePage} />
          )
        }
      </div>
    );
  }
}

希望这个替代方案可以帮助其他人。祝你好运!

答案 2 :(得分:1)

添加dataLoading:!handle.ready()|| !Meteor.user()到createContainer。

export default BaseLayoutContainer = createContainer(() => {

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

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

Meteor.user()将在组件内部未定义,因此请务必通过添加dataLoading ? "" : Meteor.user()等条件来阻止其进行调用。

您还可以检查用户是否使用!!Meteor.user()登录。通常情况下Meteor.userId()加载速度更快,但如果您需要访问用户的数据(如电子邮件),则需要在此情况下调用Meteor.user(),无需同时调用这两种数据。

答案 3 :(得分:0)

这是我的2美分

请注意,如果您正在使用Meteor.user,则在服务器重启或其他情况下无法定义in,因此您需要检查它是object还是null check meteor docs

因此,如果我接受Rimskys的回答,它应该看起来像这样。

export default BaseLayoutContainer = createContainer(() => {

  var handle = Meteor.subscribe("user.classrooms");
  var user = Meteor.user()
  return {
    currentUser: Meteor.user(),
    dataLoading: !handle.ready() || !(Accounts.loginServicesConfigured() && (user || user === null)),
    classrooms: Classrooms.find({}).fetch(),
  };
}, BaseLayout);

此外,如果用户为null,我认为将用户重定向到登录页面或禁止页面可能是个好主意。

答案 4 :(得分:0)

这是我的解决方案,希望对您有所帮助。

const Loading = () => <div>LOADING</div>;

const PrivateRoute = ({ component: Component, user, ...props }) =>
  <Route
    {...props}
    render={props => {
      if (user) return <Component {...props} />;
      if (user === null)
        return <Redirect to={{ pathname: '/', state: { from: props.location }}} />

      return <Loading />
    }}
  />;

const AdminRoute = ({ component: Component, user, ...props }) => {
  const userData = user || {};
  const admin = userData.admin || false;

  return <Route
    {...props}
    render={props => {
      if (user && admin) return <Component {...props} />;
      if (user && !admin) return <Redirect to={{ pathname: '/', state: { from: props.location }}} />;
      return <Loading />
    }}
  />
}