blog @arfyasu

プログラミングとか趣味のこととか

React のサンプルで勉強中

はじめに

React をダウンロードすると付属している example を写経中。

ページを開いてからの時間を表示するサンプルとクリックするとカウントアップするサンプルとの2つを作ったので載せておきます。

経過時間表示

ポイント「Prop」

外部から値を渡す場合、Propを通して行います。

<RunningTime start={start}/> のようにして渡した値は、this.props.start のようにして参照することが出来ます。
注意点は、渡されたこの値は変更が出来ません(Immutable)。

クリックカウンタ

ポイント「State」

Prop と異なり、動的に値が変わる場合は State を使用します。

基本的には getIntialState で state の初期値を返し、データに変更がある場合には this.setState で更新をします。

this.setState({count: this.state.count + 1});

this.state.count += 1 のように、直接値を値を代入しても変更されません。
必ずthis.setStateを使用する必要があるんですね。

まとめ

JS自体苦手なので、JSの勉強をしながら React 触ってるので進まない進まないw
まぁ、少しずつ進めていこうと思います。

参考

http://qiita.com/koba04/items/4f874e0da8ebd7329701

React 始めました - Hello React

はじめに

npm が使えるようになったので、早速 React の Hello world をやりたいと思います。
npm コマンド使えない人は、ここ見て環境作るといいと思います。 arfyasu.hatenablog.com

手順は、React の Getting Started のページに沿って進めます。 facebook.github.io

Reactって何?美味しいの? って言う人は、以下の記事を読むといいと思います。 html5experts.jp

https://facebook.github.io/react/img/logo.svg

環境

Mac OS X Yosemite 10.10.5
npm 3.3.12
React 0.14.7

開発準備

まずは、React をインストールして開発環境を作ります。

package.json 作成

package.json は プロジェクトで使うパッケージを管理するためのファイルです。
Rails の Gemfile みたいなものです。

npm install を実行すると、package.json に記載されているパッケージをインストールしてくれます。

package.json は init コマンドで作成します。
途中、いくつか質問されるので適当に答えますw

$ npm init
...
Press ^C at any time to quit.
name: (hello_world)
version: (1.0.0)
description: hello world
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)

{
  "name": "hello_world",
  "version": "1.0.0",
  "description": "hello world",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

ちなみに、package.json を作らずにインストールすると以下のような警告が出ます。

npm WARN ENOENT ENOENT: no such file or directory, open '/path/to/package.json'
React のインストール

package.json が作成できたので、React をインストールします。

$ npm install --save react react-dom

インストールが完了すると、node_modules というディレクトリが作成されその中にパッケージが配置されています。

--save オプションをつけると、以下のように package.json にパッケージの情報が追記されます。

{
  ...
  "dependencies": {
    "react": "^0.14.7",
    "react-dom": "^0.14.7"
  }
}

Hello React

1. helloworld.html

以下の内容で helloworld.html を作成します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>Hello React!</title>
  <script src="node_modules/react/dist/react.js"></script>
  <script src="node_modules/react-dom/dist/react-dom.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
  ReactDOM.render(
  <h1>Hello, world!</h1>,
      document.getElementById('example')
  );
</script>
</body>
</html>

ブラウザで開くと、以下のページが表示されるはずです。 f:id:kanz-labs:20160130104459p:plain

2. html から js を別ファイルに分離

helloworld.html から React のコードを抜き出して src/helloworld.js を作成します。

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('example')
);

helloworld.html から上記コードを削除し、src/helloworld.js を読み込みます。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>Hello React!</title>
  <script src="node_modules/react/dist/react.js"></script>
  <script src="node_modules/react-dom/dist/react-dom.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
  <script src="src/helloworld.js" type="text/babel"></script>
</head>
<body>
<div id="example"></div>
</body>
</html>

Babel でトランスパイル

Babel を使って、helloworld.js を plain なJSとしてブラウザが読み込めるコードに変換します。

Babel は、ES6からES5のトランスパイル(変換)を行うことができるツールです。
と言いつつ、それ以上のことはあまり分かっていなくて、というか絶賛勉強中ですw

ググるとたくさん出てきますが、このページが分かりやすかったです。 html5experts.jp

Babel のインストール

npm で Babel をインストールします。
-g オプションを使い、グローバルなパッケージとしてインストールします。

$ npm install -g babel-cli
$ npm install babel-preset-react
コード変換

babel コマンドを使って、src/helloworld.js をトランスパイルします。
変換後のファイルは、build ディレクトリに作成されます。

$ babel --presets react src --watch --out-dir build

--watch オプションをつけると、変更を検知してトランスパイルしてくれます。
付けずに実行すると変換は1度のみです。

下記が変換されたコード(build/helloworld.js)です。

ReactDOM.render(React.createElement(
  'h1',
  null,
  'Hello, world!'
), document.getElementById('example'));
helloworld.html の修正

helloworld.html で変換されたファイルを読み込むように変更します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>Hello React!</title>
  <script src="node_modules/react/dist/react.js"></script>
  <script src="node_modules/react-dom/dist/react-dom.js"></script>
</head>
<body>
<div id="example"></div>
<script src="build/helloworld.js"></script>
</body>
</html>

気をつける点は以下の2つです。

  • トランスパイルしたので、Babel の browser.min.js は不要なので削除します。
- <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
  • build/helloworld.js の挿入位置
<head>
  ...
  <script src="build/helloworld.js"></script>
</head>

のように、head に入れると以下のエラーで動きません(´・ω・`)

Error: _registerComponent(...): Target container is not a DOM element.

操作対象のDOM要素以降に記述します。

まとめ

ということで、React で Hello world をやってみました。
npm や Babel を知っていればそんな躓くこともなかったんでしょうけど、ここまでやるのに結構な時間がかかりました。

npm も Babel も、当然 React もまだ全然理解が不十分。
っていうか、色々とパッケージが多すぎて覚えるの大変だわw

当面の目標は react-rails を使うことなので先は長いですが、少しづつ使いながら覚えていくしか無いかなって感じですね。

今回使ったソースは以下です。
https://github.com/arfyasu/react-hello-world

開閉可能な入力フォームが iOS で動作しなかった時の対処メモ

はじめに

開閉する入力フォームを作ったところ、PCでは正常に動作する開閉動作が iOSSafari で動きませんでした。
調査した所、開閉アイコンをクリックした際のイベントが発火していなかったことが原因でした。

調査に結構時間がかかったので、メモとして残しておきます。

問題のコード

該当部分を抜き出してJSFiddle で作成してみました。
原因分かりますか?

原因

原因をググっていると、以下のページに辿り着きました。

iOSにおけるclickイベントの発生条件まとめ - hifive

原因箇所を引用します。

div、span、strongなどHTMLで通常クリック対象になるような意味を持たない要素に対しては、通常clickイベントは発生しない

このため、documentあるいはbody要素にclickイベントハンドラをバインドしてもイベントハンドラは動作しない

今回、span タグを使っていたのでビンゴでした。
対策も幾つかページに記載されていました。

対策1. イベント登録をクラス要素に変更

対策箇所を引用します。

ただし、bodyより下の子孫要素にイベントハンドラをセットした場合、その要素及びその子孫要素ではclickイベントが発生するようになる

ということで、以下のように親クラスにイベントを登録することで動作するようになりました。

$('.e_form_title').on('click', '.glyphicon-chevron-down', function(e) {
  this.showInputArea(e);
}.bind(this));
$('.e_form_title').on('click', '.glyphicon-chevron-up', function(e) {
  this.hideInputArea(e);
}.bind(this));

これで、動作するようになりました!だがしかし、、、

クリックイベント発火するまで300ms程度のタイムラグが有るみたいですね。
タップしてから開閉するまでに間があって、正直嫌な感じです。

対策2. click イベントを touchstart イベントに変更

またまた、引用します。

"click"イベントではなく、"touchstart"イベントに対してイベントをバインドした場合、ハンドラは動作します。

click イベントと touchstart イベントが両方発火しないよう、touchstart が利用できる場合のみ touchstart で動作するようにするのが良さそうです。

touchstart が利用可能かは、以下のコードで判定できました。

('ontouchstart' in window)

[JS] タップイベントが実装されているのかを調べる方法(2つ) - YoheiM .NETより

これを考慮して修正したのが、以下のコードです。
これで、スマホでもいい感じで動くようになりました。

var eventName = ('ontouchstart' in window) ? 'touchstart' : 'click';
$(document).on(eventName, '.glyphicon-chevron-down', function(e) {
  this.showInputArea(e);
}.bind(this));
$(document).on(eventName, '.glyphicon-chevron-up', function(e) {
  this.hideInputArea(e);
}.bind(this));

まとめ

BtoB の Web サービスの開発が主だったので、スマホを意識した開発経験がなくはまりました。
ググると同じようなページがあって、結構有名な現象みたいですね。

いずれにしても、本当に助かりました。
こういう情報があることに感謝ですね。

参考

http://qiita.com/38kun/items/ce6a26c9c59612e6f515