【问题标题】:Meteor/React - How to wait for Meteor.user()Meteor/React - 如何等待 Meteor.user()
【发布时间】:2021-07-26 22:30:35
【问题描述】:

我正在努力弄清楚如何在反应组件中等待Meteor.user() 订阅。我知道用户已登录,因为Meteor.userId() 返回_id,但尝试访问电子邮件地址显示Meteor.user() 返回未定义。我猜是因为它现在还不可用。

唉,因为 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);

【问题讨论】:

    标签: meteor meteor-react


    【解决方案1】:

    公认的答案很好,但是过一段时间你会注意到你必须在很多组件上重复这个很多次。或者,您可以在使用Tracker 渲染顶级组件之前,确保Accounts.loginServicesConfigured() 为真。引用流星文档:“函数 Accounts.loginServicesConfigured() 是一个反应性数据源,一旦配置了登录服务,它将返回 true”。我正在使用流星、反应、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>
        );
      }
    }
    

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

    【讨论】:

    • 如果您在 react 路由器中使用重定向但希望在尚未加载用户数据时避免加载路由器,这个答案是理想的,非常感谢!
    • 很高兴我能帮上忙!
    【解决方案2】:

    你可以使用三元运算

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

    【讨论】:

    • 我喜欢它的答案是显而易见的,只是希望我能看到它!非常感谢您提供有关解构的提示,我可以看到它将如何保持标记更清洁和更少依赖。再次感谢!
    • 很高兴我能帮助@DMX 谢谢!
    【解决方案3】:

    添加数据加载:!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(),在这种情况下无需同时调用两者。

    【讨论】:

      【解决方案4】:

      这是我的 2 美分

      请注意,如果您使用 Meteor.user 在服务器重新启动或其他一些情况下可能未定义,因此您需要检查它是对象还是 null check meteor docs

      所以如果我接受里姆斯基的回答,它应该是这样的。

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

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

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-05-08
          • 1970-01-01
          • 2016-05-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多