LoginSignup
48
50

More than 5 years have passed since last update.

React & Parseでユーザー管理ができるアプリを作る

Last updated at Posted at 2015-04-24

Parseとは

ParseはBaas(Backend as a Service)分野の最大手で今はFacebook傘下にあります。
Parseはbackend、主にDB周りをいいようにやってくれるのですが、実はHostingやサーバー側のコードを書ける機能もあったりします。

どちらもFacebook製ということで最近ParseReactというライブラリも発表されました。
http://blog.parse.com/learn/parse-and-react-shared-chemistry/
https://github.com/ParsePlatform/ParseReact

セキュリティを考えると権限設定等は慎重に設定する必要があるのですが、単純なユーザー管理システムはParseを使えば簡単に実装できます。
試しにReactでSignup, Login, Logoutができるだけの簡単なアプリを作ってみました。

React & Parseでの実装

React, Fluxの実装については以下のサンプルを参考にしてみました。
https://github.com/auth0/react-flux-jwt-authentication-sample

これから紹介するParseを使った実装例のコードはこちらにあります。
https://github.com/enu-kuro/Parse-React-Login-Signup-Sample

ルーティング、初期化等

画面遷移はreact-routerで管理しています。

index.js
'use strict';

import React from 'react';
import { Parse } from 'parse';
import Router from 'react-router';
var { Route, DefaultRoute, NotFoundRoute} = Router;
import RouterContainer from './services/RouterContainer';

import App from './components/App.react';
import Login from './components/Login.react';
import Signup from './components/Signup.react';
import Main from './components/Main.react';

// Insert your app's keys here:
Parse.initialize('APPLICATION_ID', 'JAVASCRIPT_KEY');

var NotFound = React.createClass({
  render: function () {
    return <h2>Not found</h2>;
  }
});

var routes = (
  <Route handler={App}>
    <Route name="login" handler={Login}/>
    <Route name="signup" handler={Signup}/>
    <Route name="main" path="/" handler={Main}/>
    <NotFoundRoute handler={NotFound}/>
  </Route>
);

var router = Router.create({routes});
RouterContainer.set(router);

router.run(function (Handler) {
  React.render(<Handler/>, document.getElementById('container'));
});

親Component

react-routerに従って<RouteHandler/>部分の表示が変わります。

App.react.js
import React from 'react';
import { Parse } from 'parse';
import Router from 'react-router';
var { RouteHandler, Link } = Router;
import RouterContainer from '../services/RouterContainer';
import AuthService from '../services/AuthService';

export default class App extends React.Component {

  constructor(props){
    super(props);
  }

  render() {
    return (
      <div className="container">
        <header className="page-header">
          <h1>Parse & React Login/Signup Sample</h1>
        </header>
        <nav className="navbar navbar-default">
          <div className="collapse navbar-collapse">
            {this.headerItems}
          </div>
        </nav>
        <RouteHandler/>
      </div>
    );
  }

  get headerItems() {
    if (!Parse.User.current()) {
      return (
        RouterContainer.get().getCurrentPathname() === '/login' ?
        <Link to="signup"><button className="btn btn-link">Signup</button></Link>
      : <Link to="login"><button className="btn btn-link">Login</button></Link>
      )
    } else {
      return (
        <button className="btn btn-link" onClick={this.logout}>Logout</button>
      )
    }
  }

  logout() {
    AuthService.logout();
  }

}

Login判定

Loginをしていない状態でアクセスしたときにLogin画面に飛ばすような実装をしています。
Componentを引数に渡すとwillTransitionToでLoginチェックをしてくれるようになります。
Mixinで実装したほうがわかりやすいかなと思いましたが、react-routerのsampleでもこのような実装をしていました。

AuthCheck.react.js
import React from 'react';
import { Parse } from 'parse';

export default (Component) => {
  return class Authenticated extends React.Component {

      static willTransitionTo(transition) {
        if (!Parse.User.current()){
          transition.redirect('/login', {}, {'nextPath' : transition.path});
        }
      }

      render () {
        return <Component {...this.props}/>
      }
  }
};
Main.react.js
import React from 'react';
import AuthCheck from './AuthCheck.react';
import { Parse } from 'parse';

class Main extends React.Component {

  render() {
    return (<h1>Hello {Parse.User.current().get('nickname')}!</h1>);
  }

}

export default AuthCheck(Main);

状態変化の通知

AuthServiceの中でParseにアクセスして、その結果でActionを分けています。
この例ではStoreを使っていませんが、Loading表示やError処理する場合はStoreにActionを送ってComponentに通知するという形になるかと思います。

AuthService.js
import AuthActions from '../actions/AuthActions';
import { Parse } from 'parse';

class AuthService {

  login(user) {
    AuthActions.login();
    Parse.User.logIn(user.email, user.password).then(function() {
      AuthActions.loginSucceeded();
    }, function(err) {
      console.log(err);
      AuthActions.loginFailed();
    });
  }

  logout() {
    AuthActions.logout();
    Parse.User.logOut().then(function() {
      AuthActions.logoutSucceeded();
    }, function(err) {
      console.log(err);
      AuthActions.logoutFailed();
    });
  }

  signup(user) {
    AuthActions.signup();
    //for login with email/password, the email is set as the username.
    var u = new Parse.User({
        username: user.email,
        email: user.email,
        password: user.password,
        nickname: user.nickname
      });

    u.signUp().then(function() {
      AuthActions.signupSucceeded();
    }, function(err) {
      console.log(err);
      AuthActions.signupFailed();
    });
  }

}

export default new AuthService()
AuthActins.js
import AppDispatcher from '../dispatcher/AppDispatcher.js';
import AuthService from '../services/AuthService';
import RouterContainer from '../services/RouterContainer';

export default {

  login: (user) => {
    console.log("login");
  },

  loginSucceeded: () => {
    RouterContainer.get().transitionTo('/payment', {});
    console.log("loginSucceeded");
  },

  loginFailed: () => {
    console.log("loginFailed");
  },

  logout: () => {
    console.log("logout");
  },

  logoutSucceeded: () => {
    RouterContainer.get().transitionTo('/login', {});
    console.log("logoutSucceeded");
  },

  logoutFailed: () => {
    console.log("logoutFailed");
  },

  signup: () => {
    console.log("signup");
  },

  signupSucceeded: () => {
    RouterContainer.get().transitionTo('/');
    console.log("signupSucceeded");
  },

  signupFailed: () => {
    RouterContainer.get().transitionTo('/signup', {});
    console.log("signupFailed");
  }

}

その他

ActionCreatorsという命名について

ActionCreatorsという名前が長いなぁと以前から感じていたのですが、参考にしたコードがActionsと書いていたのでそれを真似してみました。
やっぱりCreatorsが正しいんですかね?
[追記]
以前はActionsと呼ばれていたようですが、それ自体はActionではなくActionを作っているものなのでActionCreatorsの方が適切な名前だということで改名されたようです。
参考:http://martyjs.org/guides/action-creators/actions-vs-action-creators.html

routerへのアクセス方法

上で紹介した実装例ではRouterContainer.get()でrouterにアクセスしているのですが、この方法は"react-flux-jwt-authentication-sample"でしか見たことがないです。
シンプルでいいと思います。
詳しくはコードを参照してみてください。

emailとpasswordでのログイン実装

Parseの仕様上usernameとpasswordというセットでのLoginしかサポートしていないので、emailとpasswordでのLoginを実現するためにusernameにemailを入れて、名前はnicknameという別カラムを作って対応しています。

ParseReact (https://github.com/ParsePlatform/ParseReact) を使うとParseのデータが更新されると自動的にComponentのStateも更新されるようになるのですが、親Componetを介さずに変更されると困る場合もあるのでStoreを使って自前で実装したほうがいいのかなと思います。

別のParse&Reactサンプル (https://github.com/enu-kuro/Parse-React-Questions-Answers) ではParseReactを使ってデータの更新をしていますが、データ更新の反映はStoreを通すように実装しています。

[追記]ParseReactについて書きました

"Parse and React"を使ってみた

48
50
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
48
50