Meteor comes with an accounts system and a drop-in login user interface that lets you add multi-user functionality to your app in minutes.
Currently, this UI component uses Blaze, Meteor's default UI engine. In the future, there might also be a React-specific component for this.
To enable the accounts system and UI, we need to add the relevant packages. In your app directory, run the following command:
meteor add accounts-ui accounts-password
Wrapping a Blaze component in React
To use the Blaze UI component from the accounts-ui package, we need to wrap it in a React component. To do so, let's create a new component called AccountsUIWrapper in a new file:
8.2 Create Accounts UI wrapper component
import React, { Component } from 'react';import ReactDOM from 'react-dom';import { Template } from 'meteor/templating';import { Blaze } from 'meteor/blaze';export default class AccountsUIWrapper extends Component {componentDidMount() {// Use Meteor Blaze to render login buttonsthis.view = Blaze.render(Template.loginButtons,ReactDOM.findDOMNode(this.refs.container));}componentWillUnmount() {// Clean up Blaze viewBlaze.remove(this.view);}render() {// Just render a placeholder container that will be filled inreturn <span ref="container" />;}}
Let's include the component we just defined inside App:
8.3 Include sign in form
import { Tasks } from '../api/tasks.js';import Task from './Task.js';import AccountsUIWrapper from './AccountsUIWrapper.js';// App component - represents the whole appclass App extends Component {...some lines skipped...Hide Completed Tasks</label><AccountsUIWrapper /><form className="new-task" onSubmit={this.handleSubmit.bind(this)} ><inputtype="text"
Then, add the following code to configure the accounts UI to use usernames instead of email addresses:
8.4 Configure accounts-ui
import { Accounts } from 'meteor/accounts-base';Accounts.ui.config({passwordSignupFields: 'USERNAME_ONLY',});
We also need to import that configuration code in our client side entrypoint:
8.5 Import accounts configuration
import { Meteor } from 'meteor/meteor';import { render } from 'react-dom';import '../imports/startup/accounts-config.js';import App from '../imports/ui/App.js';Meteor.startup(() => {
Adding user-related functionality
Now users can create accounts and log into your app! This is very nice, but logging in and out isn't very useful yet. Let's add two features:
- Only display the new task input field to logged in users
- Show which user created each task
To do this, we will add two new fields to the tasks collection:
- owner - the _id of the user that created the task.
- username - the username of the user that created the task. We will save the username directly in the task object so that we don't have to look up the user every time we display the task.
First, let's add some code to save these fields into the handleSubmit event handler:
8.6 Update insert to save username and owner
import React, { Component } from 'react';import ReactDOM from 'react-dom';import { Meteor } from 'meteor/meteor';import { withTracker } from 'meteor/react-meteor-data';import { Tasks } from '../api/tasks.js';...some lines skipped...Tasks.insert({text,createdAt: new Date(), // current timeowner: Meteor.userId(), // _id of logged in userusername: Meteor.user().username, // username of logged in user});// Clear form
Modify the data container to get information about the currently logged in user:
8.7 Update data container to return data about user
return {tasks: Tasks.find({}, { sort: { createdAt: -1 } }).fetch(),incompleteCount: Tasks.find({ checked: { $ne: true } }).count(),currentUser: Meteor.user(),};})(App);
Then, in our render method, add a conditional statement to only show the form when there is a logged in user:
8.8 Wrap new task form to only show when logged in
<AccountsUIWrapper />{ this.props.currentUser ?<form className="new-task" onSubmit={this.handleSubmit.bind(this)} ><inputtype="text"ref="textInput"placeholder="Type to add new tasks"/></form> : ''}</header><ul>
Finally, add a statement to display the username field on each task right before the text:
8.9 Update Task component to show username
onClick={this.toggleChecked.bind(this)}/><span className="text"><strong>{this.props.task.username}</strong>: {this.props.task.text}</span></li>);}
In your browser, add some tasks and notice that your username shows up. Old tasks that we added before this step won't have usernames attached; you can just delete them.
Now, users can log in and we can track which user each task belongs to. Let's look at some of the concepts we just discovered in more detail.
Automatic accounts UI
If our app has the accounts-ui package, all we have to do to add a login dropdown is render the included UI component. This dropdown detects which login methods have been added to the app and displays the appropriate controls. In our case, the only enabled login method is accounts-password, so the dropdown displays a password field. If you are adventurous, you can add the accounts-facebook package to enable Facebook login in your app - the Facebook button will automatically appear in the dropdown.
Getting information about the logged-in user
In your data container, you can use Meteor.user() to check if a user is logged in and get information about them. For example, Meteor.user().username contains the logged in user's username. You can also use Meteor.userId() to just get the current user's _id.
In the next step, we will learn how to make our app more secure by doing data validation on the server.