React使ってSPAを作るよ(13)の続きです。
今回はせっかくなので今までで出来たところまで、デモキャプチャをとりました。
(重たいのでPCで閲覧したほうがいいと思います)
Reactプロジェクトのデモ!
左右それぞれのウィンドウが各ユーザーだと思ってください。
同時に閲覧中、左のユーザーがURLを送信したら、右のユーザーの画面にも反映される、というものです。
最初にNeoPixelの記事のURLと、あらたに「ブログ」タグを設定して送信。
ajaxなので遷移はせず、更新ボタンを押さなくてもそのままの画面で再レンダリングされます。
同時に、右のユーザーの画面にも記事が表示されます。
開いてみると、追加した「ブログ」タグがちゃんと入っていますね。
次に、右のユーザーは記事を開いた状態のままで、今度はMilkcocoa絡みのURLを送信します。
このとき、あらたに「Milkcocoa」タグを追加し、さっき追加した「ブログ」タグもチェックを入れて設定します。
送信すると、自分の画面には当然反映されますが、右のユーザーは記事を開いたままでも、裏側でちゃんと再レンダリングを行っています。
この記事を開いてみると、ちゃんと「Milkcocoa」「ブログ」のタグが入っていますね。
いかがでしょうか!(;゚∀゚)=3(ドヤァ
前回からの変更点
このデモキャプチャを撮るまでに、気になっていた細かいところを修正したので解説します。
記事の昇順・降順
いきなりですが私の手柄ではない話です…
同僚に頼んで、APIから記事一覧のデータが返ってくるときに、新しいものが上に来るようにお願いしました。
フロントであれこれやるより簡単そう&確実そうなので。
各記事はDB側でIDを持っているのですが、必ずしもID順になるとも限らないので、IDはあくまでもReact用のkeyとして使用しています。
Reactのkey設定
React使ってSPAを作るよ(6)で、気づいていながらも放置していたkey、とうとう使うときがきました。
データが更新されたときにただのindex順でレンダリングしていると、要素の順番が変わってしまいます。
特に今回、リストの先頭に記事を追加することになるので、たとえばどれかの記事を開いているときにデータが更新されると確実にindexが変わってしまい(2番目の記事は3番目に移動する)、見ていた記事のひとつ隣の記事の内容に切り替わってしまいます。(3番目の記事を見ていたのに、再レンダリングされて突然2番目の内容が現れる)
これを防ぐために、データの順序ではなく、それぞれの要素を区別するためのkeyが必要になります。
DB側で持っているArticleIDをkeyとして渡すことによって、「このarticleは何番目にあろうと中身はこのデータだッ!」というふうに紐づけておくことができます。
<ArticleList articleTitle={article.articleTitle} articleUrl={article.articleUrl} articleDescription={article.articleDescription} articleImage={article.articleImage} articleTag={article.tagData} key={article.articleID}/>
こんな感じで、keyを設定。
タグのチェックボックス再レンダリング
記事リストと同じように、DB側でタグの一覧データに変更が入った時はリストを再レンダリングします。
前回と同じくcomponentDidMount()の中でsetInterval()ですね。
loadArticleFromServer: function() { var t = this; $.ajax({ url: './api/tag', dataType: 'json' }) .done(function(data) { t.setState({data: data}); }) .fail(function(xhr, status, err) { console.error(err); }); }, componentDidMount: function() { this.loadArticleFromServer(); setInterval(this.loadArticleFromServer, 2000); },
さらに、
<TagCheckList tagName={tagCheck.name} key={tagCheck.id} />
こうしてkey設定をしておきました。
UIの細かいとこ調整
たいしたことではないけど使い勝手に影響するところをいくつか直しました。
- 記事を開いたときに閉じるボタンを表示
今までオーバーレイの背景クリックでも閉じられましたが、小さいディスプレイだと面積がツライです… - URL送信中はスピナーを表示
- 送信成功したらタグチェックボックスリストは閉じる
デモでは新しいタグが反映されているのがわかりやすいように開いたままにしています。
あと、新規に追加したタグも放っておくと入力欄に残って重複してしまうので、.done()の中で
t.setState({tagData: []});
で初期化しています。
実際のURL送信ボタンを押したときの処理全体はこれです。
jQueryと混ざっててひどいですが(;´∀`)
まずは動けばいいのさ!
handleSubmit:function(e){ var t = this; $('#spinner').css('display', 'block'); e.preventDefault(); inputURL = $('#url-input').val(); if(inputURL===''){ $('#spinner').css('display', 'none'); alert('URLを入力してください'); return false; } tagArr = []; $.each($('[name="tag-check"]'),function(){ if($(this).is(':checked')){ tagArr.push($(this).attr('id')); } }); $.ajax({ url: './api/article', type: 'POST', data: {"url": inputURL, "tag": tagArr}, timeout:10000, }) .done(function() { $('#spinner').css('display', 'none'); $('#url-input').val(''); $('[name="tag-check"]').prop('checked', false); t.setState({tagData: []}); }) .fail(function(xhr, status, err) { $('#spinner').css('display', 'none'); alert(err); }); },
だいぶSPAっぽくなってきたんじゃないでしょうか~(*´∀`*)
タグごとの記事一覧ページも作りたい。
コメント機能もつけたいし、そのためにはユーザー認証とか、ユーザーの管理ページみたいなのも必要ですね。
まだまだやることたくさんですよ( ´Д`)=3 フゥ
所感
バックエンド側が全然説明できなくて申し訳ないんですが…
APIでJSON形式のデータさえ返ってくれば、あとはデザイナーでもViewを作れるのがいいですね。
フロントエンドとバックエンドで完全に作業の切り分けができます。
分業制だとデザインが終わったあとコーディング終わるまでチェックできなくて暇…とか、コーディング終わったあと実装してもらうまでチェックできなくて暇…とか、デザイン修正入ったらViewが変わるからいつまでも実装終わらない…とかって、どうしても作業フェーズで波ができちゃう。
デザイナー、HTMLコーダー、エンジニア(なぜか一括り)だとかなり作業ボリュームのバランスが悪くなりがち。
でもこれなら完全なウォーターフォールにする必要もなく、同時進行できますよね。
デザイン~コーディングの期間中にAPIだけ先に作っておく。
とか、
コーダーがコーディング終わった後コンポーネント化までしておく。
とかってやることで、バックエンド側を受け持つエンジニアさんに工期・工数のしわ寄せがいきにくくなるのではないかと思います。
今はフロント:私、バック:同僚、の2人でちょうど半分ずつ作業できているのでいい感じに思えますが、プロジェクトの規模によってはそううまくもいかないのかな~
実際にはデザイナー、フロントエンドエンジニア、バックエンドエンジニア、という構成になるのかなと思います。