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

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

React使ってSPAを作るよ(11)の補足記事です。

いくつか補足と、新しく知ったことがあって、JSXのソースに少し修正が入りましたのでメモしておきます。

実は今回Reactのv0.14を使用しています。
0.13すら知らない状態で、0.13についての記事やサンプルを参考に作っているのに、ときどき0.14の記述が出てきたりしていろいろと混ざってしまいました。

0.14ではReactDOMが分離したので、react-domをインストールしなければなりませんでした。
React.renderをReactDOM.renderに置き換える必要もありました。

そのうえで、新規タグ追加フォームの挙動について。
処理の説明がざっくりすぎたので細かく見ていきましょう。

  handleEnterSubmit:function(e){
    e.stopPropagation();
    var tagName = this.refs.tagName.value.trim();
    if (e.which === 13 && tagName !== '') {
      tagName = tagName.replace(/\s+/g, "");
      var already = document.getElementById(tagName);
      if (!tagName){
        return;
      }
      else if(already){
        already.checked = true;
      }
      else if(!already){
        this.props.addTag(tagName);
      }
      this.refs.tagName.value = "";
      e.preventDefault();
    }
    else if (e.which === 13) {
      e.preventDefault();
    }    
  },

e.stopPropagation();

これは入力欄のinputでキーを押したときの処理です。
必要かどうかわかりませんが、stopPropagation()で通常のイベントについて親要素への伝播を止めています。

formの中に入っているために普通にEnter押すとURL送信ボタンのsubmitが発火しちゃうから・・・と思っていますが、せっかくReact使ってるんだし何か別の方法がありそうです。
しかも実際にURL送信するときにも遷移して画面リフレッシュしてしまうし。
せっかくajaxでPOSTしてるんだしそこはSPAとして、そういった挙動をなくしていけたらいいなと思っています。

今後調べて対応したい!

this.refs.tagName

次に、かなり大きな違いですが、

ReactDOM.findDOMNode(this.refs.ref属性名).value = "";

と書いていたところ。

これはもともと.getDOMNode()が廃止されてReactDOM.findDOMNode()になったそうで、新しい書き方のようですね。
そして、もちろん間違いではないのですが、さらに…

DOMコンポーネントであればReactDOM.findDOMNode()すら不要だそうです。

実際に、

this.refs.ref属性名.value = "";

としても問題なく動きました(゚д゚)!

これはずいぶんスッキリしますね~!

ここで、押されたキーがEnter(which===13)のとき、文字列が空でなければ、というif文で処理を進めていきます。

tagName.replace(/\s+/g, “”);

HTML/CSSくらい自分も書けると思ってる勘違いプログラマにありがちな行動【CSS編】の中でも書いていますが、HTML5ではid属性の仕様が変わりました。
今までは先頭に数字が使えなかったりしたのですが、

・空白文字を含んではいけない
・半角数字のみで構成されていても良い
・アンダースコアで始まっても良い
・句読点のみで構成されていても良い

という内容なので…( ゚ε゚;)ムムッ
スペースがはいっていなければなんでもオッケーってこと!?
スラッシュは…?スラッシュは、どうなるの…!

ハラハラしますが、実際に日本語のidにしてもバリデータはきれいに通ります。
このあたりでXSSとかを意識して入力制限をかけたりしないと危なそうですね。そういったものもエンジニアさんに相談しながらやっていこうと思います。

というわけで、入力された文字列をreplace(/\s+/g, “”);でスペースを置換(削除)してから次の処理に移ります。

入力された文字列によって分岐

入力された文字列のタグをチェックボックスの要素にしてリストに追加したいわけですが、すでにあるタグと重複するかもしれませんよね。
そこで、if文で同じtagNameがidになっているチェックボックスがないかを探します。

tagNameは、前項で「入力された文字列からスペースを削除した」モノです。
なんらかの理由でこれが存在しない場合、何も処理をせずすぐさまretrun;で抜けてしまいます。

tagNameが存在し、かつ、重複するidのついたチェックボックスがある場合。
これはタグを追加する必要はありませんので、既存のタグにチェックを入れます。すでに入っている場合はチェックしっぱなしで画面の動きがないので、もし、チェック済みであることにユーザーが気づかないと、「あれ?あれ?」ってなりますね。
(´ε`;)ウーン…

たとえばここで、「『○○』タグをチェックしました」と出すとか、このときだけ対象のタグに少し派手なアニメーションをさせてアイキャッチするとか、したほうがいいかもしれませんね。
こういうUIを考えるのもデザイナーの仕事。( ゚д゚)ウム

そして最後に、既存タグがない、新規の文字列が入力されていた場合。

this.props.addTag(tagName);

お待ちかねのaddTagです。

親要素のコンポーネントで

  handleAddTag:function(tagName){
    var data = this.state.tagData;
    data.push({tagName: tagName});
    this.setState({tagData: data});
  },
      <TagForm addTag={this.handleAddTag} tagData={this.state.tagData} />

というふうになっていますね。

これで入力された文字列tagNameの値を渡して、.push()で配列に追加しています。

ここからあとはJSXのソースを玉突きで見ていけばいいわけですね。
tagNameが追加されたことによって、子コンポーネントの

//新規タグリストコンポーネント
var TagInputList = React.createClass({
  propTypes:{
    tagData:React.PropTypes.arrayOf(React.PropTypes.object).isRequired
  },
  render:function(){
    var TagNodes = this.props.tagData.map(function(tag, index){
      return (
        <NewTag tagName={tag.tagName} key={index}/>
      );
    });
    return (
      <ul id="tag-input-list" className="check-list">
          {TagNodes}
      </ul>
    );
  }
});

TagNodesの中身が1件増えて、差分がレンダリングされます。

やっと、Reactらしい動きを作れた感じがしますねε-(´∀`*)

あとはこれ。

this.refs.tagName.value = "";

これで入力欄を空にして、連続入力が可能になりました~めでたしめでたし。

ReactDom以外にもいろいろ変わったReact v0.14

今回やったことを調べているうちに、そもそものReactの書き方を覚えるだけじゃなくて、0.13と0.14のバージョンの差異でソースがごちゃごちゃにならないように、どちらの知識も必要になってきました。

とても参考になる記事があったのでこちらを紹介します。

変更点がとっても詳しく書かれています!

ネスト子要素のフラット配列化とか、もうなんのためにあるの?
わからない(;´Д`)
どんなときに使うのか想像がつきません。

でも、もともとマークアップ済みのHTMLどおりに出力しやすくなりそうで、構造にこだわるコーダーにとっては非常に嬉しいアップデートではないでしょうか(;゚∀゚)=3

React使ってSPAを作るよ目次