React使ってSPAを作るよ(18)

React使ってSPAを作るよ(18)

React使ってSPAを作るよ目次

React使ってSPAを作るよ(17)の続きです。

なんとなくコンテンツはできてきたので、そろそろユーザー認証のあたりを考えていきたいです。
あとで変えるかもしれませんが、今のところ仕様は下記のような感じを想定しています。

ゲストユーザーの場合

  • 記事一覧は見ることができる
  • タグ絞り込み機能も使える
  • 読んだ人リスト、コメント、コメント入力フォームは表示されない
  • 記事を追加することはできない
  • navの記事追加ボタンを押したらログインフォームが表示される
  • navのアカウントボタンを押したらログインフォームが表示される
  • アカウントアイコンはデフォルトの画像が表示される

spa
モックを用意するのが面倒なので実装済みの動作をGIFにしました!

ログインユーザーの場合

  • 記事一覧を見ることができる
  • タグ絞り込み機能も使える
  • 読んだ人リスト、コメント、コメント入力フォームも表示される
  • 記事にコメントを投稿できる
  • 自分のコメントは削除ボタンが表示され、削除できる
  • (コメントの編集は検討する)
  • 自分の追加した記事は削除ボタンが表示され、削除できる
  • 記事を追加できる
  • navの記事追加ボタンを押したら記事追加フォームが表示される
  • navのアカウントボタンを押したらアカウント情報設定フォームが表示される
  • アカウントアイコンの画像を登録できる

spa

というふうに分けたいと思います。

あ、minamiという架空のサービス名は、以前このページのイメージカラーをピンクにしたのと同じく、AKBファンの同僚がたかみな神推しだからです。

Reactコンポーネントのrender内でif文を使う

まだ認証部分の実装はしていませんが、ひとまずログインフォームのパーツを作っていきます。

ログインユーザーの記事追加フォームはこれまでと同じですね。
URLFormコンポーネントが入っています。
ログインしていないユーザーのときはここにURLFormのかわりにログインフォームのコンポーネントを入れます。

React使ってSPAを作るよ(15)で作ったナビゲーションコンポーネントに、minamiロゴやアカウントボタンを追加しました。

//ナビゲーションコンポーネント
var Navigation = React.createClass({
  propTypes:{
    text: React.PropTypes.string
  },
  render: function() {
    var test = true;
    var accountImage = {
      backgroundImage : "url(" + this.props.accountImage + ")" 
    };
    return (
      <nav id="menu">
        <input type="radio" name="menu" id="menu00" defaultChecked="checked" /><label htmlFor="menu00" className="overlay-close">×</label>
        <input type="radio" name="menu" id="menu01"/><label htmlFor="menu01">
          <svg viewBox="0 0 31 37">
            <path d="M26.5,36.5h-22c-2.2,0-4-1.8-4-4v-28c0-2.2,1.8-4,4-4h22
          c2.2,0,4,1.8,4,4v28C30.5,34.7,28.7,36.5,26.5,36.5z M27.5,18.5v-12c0-1.1-0.9-2-2-2h-20c-1.1,0-2,0.9-2,2v12c0,1.1,0.9,2,2,2h20
          C26.6,20.5,27.5,19.6,27.5,18.5z M4.475,26.291h22 M4.475,31.291h22"/>
          </svg>
        </label>
        {(() => {
          if (test === true) {
            return <URLForm />;
          }
          else {
            return <LoginBox text={'記事を追加するにはログインしてください。'} />;
          }
        })()}        
        <input type="radio" name="menu" id="menu02" /><label htmlFor="menu02">
          <svg viewBox="0 0 42.387 43.79">
            <path d="M30.5,15.5c0,8.284-6.716,15-15,15s-15-6.716-15-15s6.716-15,15-15
              S30.5,7.216,30.5,15.5z M15.5,5.5c-5.523,0-10,4.477-10,10s4.477,10,10,10s10-4.477,10-10S21.023,5.5,15.5,5.5z M37.966,39.972
              l-7.221-9.585c-0.993-1.318-2.883-1.584-4.201-0.591l0,0c-1.318,0.993-1.584,2.883-0.591,4.201l7.221,9.585
              c0.993,1.318,2.883,1.584,4.201,0.591l0,0C38.693,43.18,38.959,41.29,37.966,39.972z"/>
          </svg>
        </label>
        <TagList data={this.props.data} />
        <input type="radio" name="menu" id="menu03" /><label htmlFor="menu03" style={accountImage}>
          <svg version="1.1" x="0px" y="0px" viewBox="0 0 27 32.5">
            <path d="M13.5,0.5c-4.971,0-9,4.029-9,9s4.029,9,9,9s9-4.029,9-9
              S18.471,0.5,13.5,0.5z M26.5,32c0-6-7.477-10-13-10s-13,4-13,10H26.5z"/>
          </svg>
        </label>
        {(() => {
          if (test === true) {
            return <Account />;
          }
          else {
            return <LoginBox text={'ログイン'} />;
          }
        })()}        
      </nav>
    );
  }
});

分岐用の記述が増えていますので、ひとつずつ説明していきますね。

    var test = true;
          if (test === true) {

このtestというのは、ログイン状態かどうかということの判定のかわりに使っています。
本来だったらここは「誰がログインしているか」っていうデータまで渡さないといけないところなのですが、今は認証機能が実装されていないので、このtrue/falseを切り替えて画面の確認をしています。

で、ご覧のとおり

        {(() => {
          if (test === true) {
            return <URLForm />;
          }
          else {
            return <LoginBox text={'記事を追加するにはログインしてください。'} />;
          }
        })()}        
        {(() => {
          if (test === true) {
            return <Account />;
          }
          else {
            return <LoginBox text={'ログイン'} />;
          }
        })()}        

この2箇所で、ログイン中ならURLFormコンポーネント/Accountコンポーネントを表示、ログインしていなければLoginBoxコンポーネントを表示しています。

さて、今回新たにLogin.jsxというファイルを用意しました。

var React = require('react');

//ログイン
var LoginBox = React.createClass({
  render: function() {
    return (
      <form id="login-box">
        <p>{this.props.text}</p>
        <input type="mail" placeholder="example@example.com" />
        <input type="password" placeholder="password" />
        <input type="submit" className="button01" value="ログイン" />
        <div className="checkbox-list"><input type="checkbox" id="login-save" /><label htmlFor="login-save">ログイン情報を保存</label></div>
        <button className="button02">新規アカウント登録</button>
      </form>
    );
  }
});

module.exports = LoginBox;

ログインしていないときに表示されるログインフォームです。
先ほどナビゲーションコンポーネントから渡されたtextがp要素の中に入るわけですね。
同じパーツでも、呼び出す場所によって「ここだけテキストを変えたい」っていうことが可能です。

Account.jsxも新しく作りました。

var React = require('react');

//アカウント
var Account = React.createClass({
  render: function() {
    return (
      <form id="account-config">
        <h2>アカウント情報</h2>
        <input type="text" id="account-name" placeholder="ユーザー名" defaultValue="やまだたろー"/>
        <input type="mail" placeholder="example@example.com" defaultValue="example@example.com" />
        <input type="password" placeholder="password" defaultValue="password" />
        <input type="submit" className="button01" defaultValue="アカウント情報を変更" />
        <button className="button02">アカウントを削除</button>
      </form>
    );
  }
});

module.exports = Account;

さすがにこのくらいのコンポーネントなら簡単に作れるようになってきましたね。
将来的にはここにアイコン画像を登録するためのパーツを入れていくことになります。

とりあえず今回はここまで!

React使ってSPAを作るよ目次