React使ってSPAを作るよ(17)の続きです。
なんとなくコンテンツはできてきたので、そろそろユーザー認証のあたりを考えていきたいです。
あとで変えるかもしれませんが、今のところ仕様は下記のような感じを想定しています。
ゲストユーザーの場合
- 記事一覧は見ることができる
- タグ絞り込み機能も使える
- 読んだ人リスト、コメント、コメント入力フォームは表示されない
- 記事を追加することはできない
- navの記事追加ボタンを押したらログインフォームが表示される
- navのアカウントボタンを押したらログインフォームが表示される
- アカウントアイコンはデフォルトの画像が表示される
モックを用意するのが面倒なので実装済みの動作をGIFにしました!
ログインユーザーの場合
- 記事一覧を見ることができる
- タグ絞り込み機能も使える
- 読んだ人リスト、コメント、コメント入力フォームも表示される
- 記事にコメントを投稿できる
- 自分のコメントは削除ボタンが表示され、削除できる
- (コメントの編集は検討する)
- 自分の追加した記事は削除ボタンが表示され、削除できる
- 記事を追加できる
- navの記事追加ボタンを押したら記事追加フォームが表示される
- navのアカウントボタンを押したらアカウント情報設定フォームが表示される
- アカウントアイコンの画像を登録できる
というふうに分けたいと思います。
あ、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;
さすがにこのくらいのコンポーネントなら簡単に作れるようになってきましたね。
将来的にはここにアイコン画像を登録するためのパーツを入れていくことになります。
とりあえず今回はここまで!