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

#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で管理しています。

```js: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/>`部分の表示が変わります。

```js: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でもこのような実装をしていました。

```js: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}/>
      }
  }
};
```

```js: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に通知するという形になるかと思います。

```js: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()
```

```js: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"を使ってみた](http://qiita.com/enu-kuro/items/395548ad41f4af1938d8)