Reactでcheckboxの状態をステートで管理する

React (Facebook): managed state of controlled checkboxesのパクリと自分の理解のためのメモ

index.htmlの準備

<!DOCTYPE html>
<html>
  <head>
  ¦ <meta charset="UTF-8" />
  ¦ <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react.min.js"></script>
  ¦ <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react-dom.min.js"></script>
  ¦ <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
  </head>
  <body>
  ¦ <div id="content"></div>
    <script type="text/babel">
    // この中にReact書く
    </script>
  </body>
</html>

ReactクラスとState定義

2つあるinputのcheckboxがそれぞれチェックされているかどうかの状態を持つためのstateを準備。ついでにReactDOMレンダーも定義。

var CheckboxDemo = React.createClass({
  getInitialState: function () {
    return {
      data: [
        {id: 1, selected: false },
        {id: 2, selected: false }
      ]
    };
  },
  // この下にFormを書く
});
ReactDOM.render(
  <CheckboxDemo />,
  document.getElementById('content')
);

Form作成

Formの要素としてchecksを定義する。mapの中でCheckboxDemoオブジェクトにthisでアクセスできるようにbindしておく

render: function () {
  var checks = this.state.data.map(function (d) {
    return (
      <div>
        <input type="checkbox" checked={d.selected} onChange={this.__changeSelection.bind(this, d.id)} />
          {d.id}
          <br />
      </div>
    );
  }.bind(this));
  <form>
    {checks}
  </form>
}

まだ理解できてないところとして__changeSelection.bind(this, d.id)のthisはCheckboxDemoなのかdなのか

changeSelection定義

チェックボックスが押された時の__changeSelection関数を定義。チェックボックスが押されたらidを__changeSelectionを渡して呼び出して__changeSelectionの中でdataの中を全部チェックして、渡したidとdataのidが一致したらselectedの真偽値を変更してdataを丸ごと変更かける

__changeSelection: function (id) {
  var nextState = this.state.data.map(function (d) {
    return {
      id: d.id,
      selected: (d.id === id ? !d.selected: d.selected)
    };
  });

  console.log(nextState); // 確認用
  this.setState( {data: nextState });
},

全部チェックを実装

form内を修正

<form>
  <input type="checkbox" ref="globalSelector" onChange={this.__changeAllChecks } />CheckAll
  <br />
  {checks}
</form>

changeAllChecksの定義

globalSelectorのチェックボックスがチェックされているかどうかをgetDOMNodeでとってきてchecksに入れて、checksをdataの全てのselectedに反映する

__changeAllChecks: function () {
  var chekcs = this.refs.globalSelector.getDOMNode().checked;
  var nextState = this.state.data.map(function (d) {
    return {id: d.id, selected: chekcs };
  });

  console.log('change all checks -> ', nextState)
  this.setState( { data: nextState })
}

全体コード

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

    var CheckboxDemo = React.createClass({
      getInitialState: function () {
        return {
          data: [
            {id: 1, selected: false },
            {id: 2, selected: false }
          ]
        };
      },

      render: function () {
        var checks = this.state.data.map(function (d) {
          return (
            <div>
              <input type="checkbox" checked={d.selected} onChange={this.__changeSelection.bind(this, d.id)} />
                {d.id}
                <br />
            </div>
          );
        }.bind(this));
        return (
          <form>
            <input type="checkbox" ref="globalSelector" onChange={this.__changeAllChecks } />CheckAll
            <br />
            {checks}
          </form>
        )
      },

      __changeSelection: function (id) {
        var nextState = this.state.data.map(function (d) {
          return {
            id: d.id,
            selected: (d.id === id ? !d.selected: d.selected)
          };
        });

        console.log(nextState);
        this.setState( {data: nextState });
      },

      __changeAllChecks: function () {
        var chekcs = this.refs.globalSelector.getDOMNode().checked;
        var nextState = this.state.data.map(function (d) {
          return {id: d.id, selected: chekcs };
        });

        console.log('change all checks -> ', nextState)
        this.setState( { data: nextState })
      }

    });



    ReactDOM.render(
      <CheckboxDemo />,
      document.getElementById('content')
    );
    </script>
  </body>
</html>

ReactをGulpでbabelifyするとUnexpected tokenでこけたら

React書いてGulpでビルドしようとしたらUnexpected tokenエラーが出るのでググると babel-preset-react 入れないとダメだった。

こけた後に出来るだけ最小構成にしてReactの書き方がまずかったかgulpfile.jsの書き方がまずかったのか確認した内容の備忘録。

エラー内容

% gulp
[03:57:42] Using gulpfile ~/react-gulp/gulpfile.js
[03:57:42] Starting 'default'...
events.js:142
      throw er; // Unhandled 'error' event
      ^

SyntaxError: ~/react-gulp/src/app.js: Unexpected token (6:2)
  4 |
  5 | ReactDOM.render(
> 6 |   <Child />,
    |   ^
  7 |   document.getElementById('container')
  8 | );
  9 |
    at Parser.pp.raise (~/react-gulp/node_modules/babylon/lib/parser/location.js:22:13)
    at Parser.pp.unexpected (~/react-gulp/node_modules/babylon/lib/parser/util.js:91:8)
    at Parser.pp.parseExprAtom (~/react-gulp/node_modules/babylon/lib/parser/expression.js:510:12)
    at Parser.pp.parseExprSubscripts (~/react-gulp/node_modules/babylon/lib/parser/expression.js:265:19)
    at Parser.pp.parseMaybeUnary (~/react-gulp/node_modules/babylon/lib/parser/expression.js:245:19)
    at Parser.pp.parseExprOps (~/react-gulp/node_modules/babylon/lib/parser/expression.js:176:19)
    at Parser.pp.parseMaybeConditional (~/react-gulp/node_modules/babylon/lib/parser/expression.js:158:19)
    at Parser.pp.parseMaybeAssign (~/react-gulp/node_modules/babylon/lib/parser/expression.js:121:19)
    at Parser.pp.parseExprListItem (~/react-gulp/node_modules/babylon/lib/parser/expression.js:988:16)
    at Parser.pp.parseCallExpressionArguments (~/react-gulp/node_modules/babylon/lib/parser/expression.js:341:20)

プロジェクト構成

.
├── gulpfile.js
├── index.html
├── node_modules
├── package.json
└── src
    ├── app.js
    └── child.react.js

node_modules

入れたものリスト

"devDependencies": {
  "babelify": "^7.2.0",
  "browserify": "^12.0.1",
  "gulp": "^3.9.0",
  "vinyl-source-stream": "^1.1.0"
},
"dependencies": {
  "react": "^0.14.3",
  "react-dom": "^0.14.3"
}

gulpfile.js

var gulp = require('gulp');
var browserify = require('browserify');
var babelify = require('babelify');
var source = require('vinyl-source-stream');

gulp.task('default', function () {
  return browserify('./src/app.js')
    .transform(babelify)
    .bundle()
    .pipe(source('bundle.js'))
    .pipe(gulp.dest('./'));
});

index.html

<!DOCTYPE html>
<html>
  <body>
    <div id="container"></div>
    <script src="bundle.js"></script>
  </body>
</html>

src/app.js

var React = require('react');
var ReactDOM = require('react-dom');
var Child = require('./child.react.js');

ReactDOM.render(
  <Child />,
  document.getElementById('container')
);

src/child.react.js

var React = require('react');

var Child = React.createClass({
  render: function () {
    return (
      <div id="child">
        <h2>Child</h2>
      </div>
    );
  }
});

module.exports = Child;

解決策

これで gulp を実行すると冒頭のエラーが出るので冒頭に書いているように babel-preset-react をインストール(npm install --save-dev babel-preset-react)してgulpfile.jsを修正すればビルド成功する。

インストール後のdevDependencies(package.js)

"devDependencies": {
  "babel-preset-react": "^6.3.13",
  "babelify": "^7.2.0",
  "browserify": "^12.0.1",
  "gulp": "^3.9.0",
  "vinyl-source-stream": "^1.1.0"
}

修正後のgulpfile.js

var gulp = require('gulp');
var browserify = require('browserify');
var babelify = require('babelify');
var source = require('vinyl-source-stream');

gulp.task('default', function () {
  return browserify('./src/app.js')
    .transform(babelify,{presets: ["react"]})
    .bundle()
    .pipe(source('bundle.js'))
    .pipe(gulp.dest('./'));
});

ビルド結果

これでgulpを実行するとちゃんとbundle.jsができあがってブラウザで<h2>Child</h2>のレンダリング結果が表示される。

% gulp
[04:11:17] Using gulpfile ~/react-gulp/gulpfile.js
[04:11:17] Starting 'default'...
[04:11:20] Finished 'default' after 3.67 s

ビルド後の構成

.
├── bundle.js
├── gulpfile.js
├── index.html
├── node_modules
├── package.json
└── src
    ├── app.js
    └── child.react.js

参考

4歳の娘のお気に入り絵本集

Advent Calendar 2015のお子さん、どんな本読んでる? Advent Calendar 2015の22日目の記事です。

昨日はmasahikoofjoytoさんの記事でした。

2013年の投稿から2年ぶりの投稿等いうことで、当時2歳だった娘も4歳になり文字数が増え、毎晩2冊以上読み聞かせをしていますが1冊は自分で読めるようになりました。

そんな4歳の娘が特に気に入っているレパートリーをご紹介します。

みんなでたのしいクリスマス

2年たっても色褪せぬ魅力のようです。この本は装飾も含めてベストバイの1冊です。読んでると心が温まります。

ゆきうさぎのねがいごと

クリスマスシリーズ第2弾です。これは昨年のクリスマス時期に旅行先のホテルでサンタさんが部屋にプレゼントを届けに来てくれるサービスを見つけてサンタさんから渡してもらったインパクトが強かったのか、宝物の1つのようです。

一度保育園で他の園児に私が読み聞かせをする機会があり娘がこの本を選んだんですが、結構長めの物語にもかかわらずみんなしっかり聞いてくれていたので3〜5歳位にぴったりな本なんだと思います。

からすのパンやさん

この本をきっかけに娘のパン屋好きが始まりました。かなり長いので眠たい時に読み聞かせをするのはちょっとしんどい時があります。(^ ^;)

でんしゃはうたう

この本のポイントはいかに顔と声で笑いをとるか!

何しろ擬音が延々と続くだけの本なので正直何が面白いのか全くわからないんですが、娘は毎回大爆笑です。

おおきくなりたいの

うさぎ好きの娘らしいチョイスです。絵もすごく可愛いです。

最後のオチはあえて説明せずに娘が気づく日を待ってます。

くまのこうちょうせんせい

大きな声で挨拶しましょう!を理屈ではなく感情で伝えてくれる良本です。

のせてのせて100かいだてのバス

これは面白いですよ!中のしかけがすごくて、100階建てバスの大きさをイメージさせてくれます。
こんなバスがあるなら一度乗ってみたいです。

はじめてのずかん

これは娘が私にいろいろな名前を教えるための教科書的に使っているようです。
この本の時は娘が先生役、私は園児役になります。

まだ他にも色々あるんですが、特に娘が気に入っている絵本をチョイスしました。

ちなみに、絵本の入手手段は以前は私が毎月1冊娘に選んでもらって買っていたんですが、この2年間は毎月実家に娘と二人で泊まりに行った時に毎晩私の父が寝る前に娘に手渡してくれているので毎月色んな種類の本が増えてます。

絵本を買い揃えようとすると意外とお金がかかるので助かってます。

これから1年、娘の絵本の趣味がどう変わっていくのかますます楽しみです。

ポジティブな割り切りと諦め

この記事は子育てプログラマ・ITエンジニア・Webデザイナー Advent Calendar 201522日目の記事です。
昨日はchezouの小さい子供が自分から薬を飲みたくなる「おくすり飲めたね」 #childadvent記事でした。
このAdvent Calenderは思わず「そうそう!」「へ〜」「なるほど!」ととても参考になる記事ばかりです
そんななか、今回はどうやって子供との時間を確保しようとしているかを自分でも振り返るためにまとめてみようと思います。
家族構成

私(父) : 結婚式運営会社の社内SE
妻(母) : IT系専門学校教員
娘: 4歳・今年4月から保育園
私自身は社内のヘルプデスクを中心に社内NWの運用・提案を主にやっていまして、基本的には定時18時で退社します。趣味で最近はReact.jsにはまっています。
妻は教員ということもあり仕事も遅くまであり、家に帰っても休みの日も授業準備があるので、基本的に家事・育児は夫である私が担当です。
そんな私の一日の流れは以前のポストちゃんと君を愛してるを継続しています。
この生活の中で家を回すことと娘との関わりを深めることのバランスをとるために諦めたことと割り切るようになったことを今回はご紹介します。
書いた後で大層なアイデア・工夫でもないことに気づきました…
諦めと割り切り

手料理のこだわりをギブアップ

当初、ご飯は娘と一緒に温かいご飯を食べる、にすごくこだわっていました。
ただ、仕事が終わって娘を迎えに行って、家に帰ってご飯の準備を、となると娘としては保育園のことを話したい、遊びたい、が先に来るけど料理中はどうしても娘の希望は後回しになるし、「早くご飯を作ってしまいたい」思いと焦りで苛々するようになってしまったので「これはいかん」と手作り料理を毎日出すことを諦めました。
なので、我が家の夜ご飯はワタミの宅食お弁当です。これでどんなに頑張っても15分はかかるご飯準備がわずかレンジでチンの2分ですよ!
少なくともコンビニ弁当を毎日食べるよりはいいだろうし、娘は保育園で栄養のあるものをバランスよく食べさせてもらっているので、と割り切ることにしました。
その代わり、朝はちゃんと作って娘と二人で食べるようにしています。
洗濯物をたためなくても気にしない

それから毎日毎日やってくる洗濯と洗濯物のたたみ。雨が2,3日降るようなことがあれば洗濯物の山。それを頑張って夜、朝と駆使して洗えば洗ってしまった洗濯物の山。
この洗濯物をたたむ毎日のルーティンを少しでも軽減してあわよくばたたまずに済ませるためにやったことが1つ。
自分の服を極限まで減らす

会社用のシャツは3セット。プライベートの服は夏・冬それぞれ2セット。パジャマ代わりの服は1セット、になるところまで断捨離とミニマリストをやっている人を参考にスパっと捨ててしまい、基本的に「洗う」→「着る」のサイクルで回せるようにしています。
子供の服

毎日保育園に着ていくものは綺麗なものを着せてあげたい、と思うので、これも基本的にたたまず「洗う」→「着る」で済ませています。
問題は私服。家に帰ってきてお風呂に入るまでのつなぎの服。
パジャマ着といて、と思うんですが着替えたいというのでこの分の洗濯もバカにならなかったんですが、そこは女の子!
娘の洋服は元々私の本棚だったものの中に入れるようにしていて自分で選ばせるようにするとだいたい毎回同じものを選ぶ、ということが分かったので傾向を見ながらあまり着ない服は別のタンスに直して、よく着るものは洗った後に部屋の中に吊るして目につくようにすることでたたむ時間をサボれるようにしています。
その他

タオルやそれ以外の洗濯物も、以前は毎回たたまないと気がすまなかったんですが、シワが付いたら困るものはハンガーポールへ、それ以外のものは山積みにしてしまって、休みの日にまとめてたたむ、と割り切るようにしています。
掃除

基本的に潔癖症なんです。なので掃除ができない空間は気持ちが悪いんですが、子供は容赦なく部屋におもちゃや食べかすを撒き散らします。
かといって、早朝や夜に掃除機をかけるとご近所迷惑なので、掃除機かけは休みの日だけに割りきりました。その代わりにフローリングだけはどうしても我慢ができない時に娘がトイレに行っている間なんかにクイックルワイパーでゴミを見えにくくしています。
それと、テーブル。寝るときにテーブルの上の物を必ず直すを根気よく言い聞かせることでテーブルの上だけは朝一に拭きあげるだけですむようになりました。
それから娘にも断捨離を半強制しています。
おもちゃはここ、本はここ、お菓子はここ、に入らなくなったら自分でいらないものを捨てないと新しいのを買ったらダメ、どうしても捨てられないけど、場所を開けたいならこの中、と置いていいスペースに限りがあることを理解してもらうことで掃除する場所を増やさないだけでなく、結果として子供が自分で片付ける様になりました。
この片付けをするとき、一切「これはいる」「これはいらない」は言わずに横に一緒につくことで今何が好きなのか、何に対する関心は卒業したのかを把握しやすくなりました。
そんなこんなで、どうしてもやらなきゃ気がすまない、という掃除箇所以外は週に1回か2回でまとめてやることで子供に「ちょっと待っとって」とお願いする時間を減らすようにしています。
今年1年で変わったこと

まとめると、娘と一緒にいて会話、遊び、読み聞かせの時間を十分とれるように私自身が変えたことは、
自分のこだわりに固執しない
物をへらす
物と時間の使い方をルーティン化する
今回は書いていませんが、私自身も晴れの日と雨の日のタイムスケジュールを別パターンでもっておいて、よほどのことがない限りはその時間通りに動く、娘にやってほしいこと(朝と夜2個づつだけ決めています)もこの時間からこの時間までにと決めておいて、それ以外は娘とのフリータイムとして満喫するようにしています。
それ以外は基本的に休みの日にまとめてやってしまう、と割り切ることで最初のほうで紹介した時間よりも1時間近く勤務日でも娘と向き合える時間が確保できています。
結局のところ

今のような生活をするためには定時で帰れるようにしてくれている職場の理解とチームのサポートが必要不可欠なので、感謝の気持ちを忘れず、出来る限り仕事の時間は集中してチームの為にできることに専念します。
そして、自分自身を変えるための一番の要素は二度とない今の娘と向き合いたいと本心で思えるかどうかだと思います。
(じゃないと、せっかくあけた時間を自分のために、などと思うと本末転倒なので・・・)
明日はserimaさんの記事です

Immutable.jsチュートリアル用環境構築

Immutable.jsチュートリアル用環境構築

Immutable.jsのチュートリアルをCLIですすめるために行った環境構築手順です。
1. Nodeのインストール

割愛…。nodebrewを使ってnodeを入れています。
2. チュートリアル用フォルダ作成

mkdir ./immutableTutorial
3. package.json生成

npm init
4. Immutable.jsのインストール

npm install –save-dev immutable
以上!
実行方法

とりあえず、entry.jsなどのJSファイル作ってチュートリアルのサンプルコードかいて、node entry.jsとやれば結果が返されます。(下のサンプルコードを実行した場合、2と50が改行されて表示されます)
// entry.js
var Immutable = require(‘immutable’);

var map1 = Immutable.Map({a:1, b:2, c:3});
var map2 = map1.set(‘b’, 50);

console.log(map1.get(‘b’));
console.log(map2.get(‘b’));
引き続きチュートリアルをやるために

The case for Immutability

assertモジュールを使うところが出てきたので、assertモジュールを入れます
npm install –save-dev node
で、チュートリアルのコードをこう書き換えてnode entry.jsを実行します。
var assert = require(‘assert’);
var Immutable = require(‘immutable’);

var map1 = Immutable.Map({a:1, b:2, c:3});
var map2 = map1.set(‘b’, 2);

try {
assert(map1 === map2);
console.log(‘true’);
} catch(e) {
console.log(‘false’);
console.log(e);
}

webpackのチュートリアルエラー回避

webpackのチュートリアルをやってるとcss-loaderのところでwebpackを実行すると
% webpack ./entry.js bundle.js –module-bind “css=style!.css”
zsh: event not found: .css
とエラーが出るんだけど、GitHubにも出てるようにダブルクォテーションをシングルクォーテーションにすれば解決する。
% webpack ./entry.js bundle.js –module-bind ‘css=style!css’
Hash: aa51824889799250cc68
Version: webpack 1.12.9
Time: 3593ms
Asset Size Chunks Chunk Names
bundle.js 11.8 kB 0 [emitted] main
[0] ./entry.js 64 bytes {0} [built]
[5] ./content.js 44 bytes {0} [built]
+ 4 hidden modules