Meteor/React - 如何等待 Meteor.user()
Meteor/React - How to wait for Meteor.user()
我正在努力弄清楚如何在 React 组件中等待 Meteor.user()
订阅。我知道用户已登录,因为 Meteor.userId()
returns _id
,但尝试访问电子邮件地址显示 Meteor.user()
returns 未定义。我想是因为它还不可用。
唉,由于 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);
你可以使用三元运算
currentUser ? true : flase
保持组件井井有条的一个很好的技巧是总是 destructure
例如,您可以 const { currentUser } = this.props;
而不是 const currentUser = this.props.currentUser
看看:
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>
)
}
添加数据加载:!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()
,在这种情况下无需调用两者。
接受的答案很好,但过一会儿您会发现您必须在许多组件上重复多次。或者,您可以在使用 Tracker
渲染顶级组件之前收听以确保 Accounts.loginServicesConfigured()
为真。引用流星文档:"The function Accounts.loginServicesConfigured() is a reactive data source that will return true once the login service is configured"。我正在使用 meteor、react、redux 和 react 路由器。
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 美分
请注意,如果您使用的 Meteor.user 可能在服务器重启或其他一些情况下未定义,因此您需要检查它是对象还是空
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);
此外,我认为如果用户为空,则将用户重定向到登录或禁止页面可能是个好主意。
这是我的解决方案,希望对你有帮助。
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 />
}}
/>
}
我正在努力弄清楚如何在 React 组件中等待 Meteor.user()
订阅。我知道用户已登录,因为 Meteor.userId()
returns _id
,但尝试访问电子邮件地址显示 Meteor.user()
returns 未定义。我想是因为它还不可用。
唉,由于 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);
你可以使用三元运算
currentUser ? true : flase
保持组件井井有条的一个很好的技巧是总是 destructure
例如,您可以 const { currentUser } = this.props;
而不是 const currentUser = this.props.currentUser
看看:
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>
)
}
添加数据加载:!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()
,在这种情况下无需调用两者。
接受的答案很好,但过一会儿您会发现您必须在许多组件上重复多次。或者,您可以在使用 Tracker
渲染顶级组件之前收听以确保 Accounts.loginServicesConfigured()
为真。引用流星文档:"The function Accounts.loginServicesConfigured() is a reactive data source that will return true once the login service is configured"。我正在使用 meteor、react、redux 和 react 路由器。
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 美分
请注意,如果您使用的 Meteor.user 可能在服务器重启或其他一些情况下未定义,因此您需要检查它是对象还是空 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);
此外,我认为如果用户为空,则将用户重定向到登录或禁止页面可能是个好主意。
这是我的解决方案,希望对你有帮助。
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 />
}}
/>
}