ReactでTrelloみたいなToDoリスト＜2＞ state


## 関連記事

* [Reactのスタート](http://qiita.com/park-jh/items/63bf0f013c998c5572aa)
* [Reactのプロジェクト](http://qiita.com/park-jh/items/488d43e2d0b87991207e)
* [Reactで挨拶](http://qiita.com/park-jh/items/bffaec5d05a96850e91b)
* [ReactでTrelloみたいなToDoリスト＜1＞ props](http://qiita.com/park-jh/items/f3f6162cee982eb9e6f3)
* ReactでTrelloみたいなToDoリスト＜2＞ state
* [ReactでTrelloみたいなToDoリスト＜3＞ prop-types](http://qiita.com/park-jh/items/6a8eac233e3f3ae61f32)
* [ReactでTrelloみたいなToDoリスト＜4＞ immutability-helper](http://qiita.com/park-jh/items/6166434f80bc186f8c77)

## stateについて

コンポーネント間データの受け渡しに使ったpropsは変更不可（immutable）だった。
変更可能（mutable）なstateについて学んでみよう。

Reactのコンポーネントはthis.stateを用いて、色んなデータを持ちられる。
this.stateは、this.setState()を呼び出して変えられる。
stateが変えたらトリガーが発動されて、該当コンポーネントとその子コンポーネントが再びレンダリングされる。
Reactは仮想DOMを使っているので、この作業が早い。

### toggleされるカード

```diff:src/components/Card.js
diff --git a/src/components/Card.js b/src/components/Card.js
index 61629cf..3905582 100644
--- a/src/components/Card.js
+++ b/src/components/Card.js
@@ -1,26 +1,38 @@
 import React, { Component } from 'react';
 import CheckList from './CheckList';

 class Card extends Component {
+  constructor(...args) {
+    super(...args);
+    this.state = {
+      showDetails: false,
+    };
+  }
+
   render() {
-    return (
-      <div className="card">
-        <div className="card__title">{this.props.title}</div>
+    let cardDetails;
+    if (this.state.showDetails) {
+      cardDetails = (
         <div className="card__details">
           {this.props.description}
           <CheckList cardId={this.props.id} tasks={this.props.tasks} />
         </div>
+      );
+    }
+
+    return (
+      <div className="card">
+        <div
+          className="card__title"
+          onClick={() => this.setState({ showDetails: !this.state.showDetails })}
+          role="presentation"
+        >
+          {this.props.title}
+        </div>
+        {cardDetails}
       </div>
     );
   }
 }
```

本のサンプルではroleを指定していないが、この状態ではeslintエラーが出る。

```vim
src/components/Card.js|25 col 9 error| Static HTML elements with event handlers require a role. (jsx-a11y/no-static-element-interactions)
```

button、link、checkboxのような行動要素についてはroleを明記しないといけない。
詳細は下記のリンクで確認しよう。

* https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-static-element-interactions.md#how-do-i-resolve-this-error

\:bulb: HTMLのroleについて
<details>
\:point_right: role
HTML要素に役割を与えるための属性である。
role="presentation"というのはこのHTML要素が装飾目的で使われていると言う意味だ。
例えば、imgタグの場合はalt="代替テキスト"でこのタグが何なのか説明しているが、こんなときにrole="presentation"を使える。
詳細は次のリンクで確認しよう。
* http://blog.e-riverstyle.com/2011/03/rolepresentationwaiaria.html
* http://www.webcreativepark.net/html/wai-aria/role/
</details>

### イベントハンドラの作成

onClickというイベントについてインライン関数で作成したが、これを別途の関数で取り出してみよう。

```diff:src/components/Card.js
diff --git a/src/components/Card.js b/src/components/Card.js
index 3905582..e13542e 100644
--- a/src/components/Card.js
+++ b/src/components/Card.js
@@ -9,6 +9,10 @@ class Card extends Component {
     };
   }

+  toggleDetails() {
+    this.setState({ showDetails: !this.state.showDetails });
+  }
+
   render() {
     let cardDetails;
     if (this.state.showDetails) {
@@ -24,7 +28,7 @@ class Card extends Component {
       <div className="card">
         <div
           className="card__title"
-          onClick={() => this.setState({ showDetails: !this.state.showDetails })}
+          onClick={this.toggleDetails.bind(this)}
           role="presentation"
         >
           {this.props.title}
```

\:bulb: this.toggleDetailsではなくthis.toggleDetails.bind(this)の理由
<details>
\:point_right: this
thisはオブジェクト自分自身を指しているからthis.toggleDetailsでいいではないかと思うかもしれない。
それは嘘ではない。javascriptでも同じだ。しかし、javascriptはイベントの発生元もthisで指す場合がある。
onClickというイベントをハンドルするので、ここでのthisはonClickイベントを指すことになる。
onClickのイベントにはsetStateという関数がないので、「TypeError: Cannot read property 'setState' of null」というエラーが発生する。
bind関数を利用することで、関数toggleDetailsの中のthisがbind関数で指定した引数(ここではthis)に紐付けされる。
</details>

またeslintエラーが出る。
以前と同じくpropsに関するエラーは無視する。
しかし、エラーの中ではこんなエラーもあった。

```vim
src/components/Card.js|31 col 11 error| JSX props should not use .bind() (react/jsx-no-bind)
```

jsxのpropsではbindを使うな！とね。
これを直してみよう。

```diff:src/components/Card.js
diff --git a/src/components/Card.js b/src/components/Card.js
index 3905582..5f891ac 100644
--- a/src/components/Card.js
+++ b/src/components/Card.js
@@ -7,6 +7,11 @@ class Card extends Component {
     this.state = {
       showDetails: false,
     };
+    this.toggleDetails = this.toggleDetails.bind(this);
+  }
+
+  toggleDetails() {
+    this.setState({ showDetails: !this.state.showDetails });
   }

   render() {
@@ -24,7 +29,7 @@ class Card extends Component {
       <div className="card">
         <div
           className="card__title"
-          onClick={() => this.setState({ showDetails: !this.state.showDetails })}
+          onClick={this.toggleDetails}
           role="presentation"
         >
           {this.props.title}
```

指摘された通りにjsxのpropsでbindを使わずにconstructorで使ったら、エラーが消えた。

\:bulb: なぜeslintではjsxのpropsにbindを使うとエラーになるのか？
<details>
constructorに書くのがもっと冗長に見える。
しかし、同じ関数をいろんなイベントで使うことになるなら話は別だ。
jsxのpropsにいちいち書くよりはconstructorの1ヶ所に書いた方が重複を避けられる。
</details>

### カードが開いているかを確認

```diff:src/components/Card.js
diff --git a/src/components/Card.js b/src/components/Card.js
index 5f891ac..8b3671e 100644
--- a/src/components/Card.js
+++ b/src/components/Card.js
@@ -28,7 +28,7 @@ class Card extends Component {
     return (
       <div className="card">
         <div
-          className="card__title"
+          className={this.state.showDetails ? 'card__title card__title--is-open' : 'card__title'}
           onClick={this.toggleDetails}
           role="presentation"
         >
```

### 動的HTMLレンダリング

ReactはXSS攻撃を防ぐためにjsxでHTMLタグをレンダリングすることを禁止している。
しかし、HTMLレンダリングが必要な場合もある。
Markdown表記もそのような場合だ。
テストのためにcardsListのデータにMarkdown表記で入れて確認してみよう。

```diff:src/index.js
diff --git a/src/index.js b/src/index.js
index eeb27fe..c7f23ee 100644
--- a/src/index.js
+++ b/src/index.js
@@ -8,14 +8,14 @@ const cardsList = [
   {
     id: 1,
     title: 'Read the Book',
-    description: 'I should read the whole book',
+    description: 'I should read the **whole** book',
     status: 'in-progress',
     tasks: [],
   },
   {
     id: 1,
     title: 'Write some code',
-    description: 'Code along with the samples in the book',
+    description: 'Code along with the samples in the book. The complete source can be found at [github](https://github.com/pro-react)',
     status: 'todo',
     tasks: [
       {
```

Markdownを使うために関連パッケージを追加して、Cardコンポーネントから使うように修正する。

```sh
% yarn add marked
```

本のサンプルでは「import marked from 'marked'」で出ているがmarkedというmoduleがないよ！と怒られる。
「import Marked from 'marked'」の誤字なので修正しよう。

```diff:src/components/Card.js
diff --git a/src/components/Card.js b/src/components/Card.js
index 8b3671e..48ad868 100644
--- a/src/components/Card.js
+++ b/src/components/Card.js
@@ -1,4 +1,5 @@
 import React, { Component } from 'react';
+import Marked from 'marked';
 import CheckList from './CheckList';

 class Card extends Component {
@@ -19,7 +20,7 @@ class Card extends Component {
     if (this.state.showDetails) {
       cardDetails = (
         <div className="card__details">
-          {this.props.description}
+          {Marked(this.props.description)}
           <CheckList cardId={this.props.id} tasks={this.props.tasks} />
         </div>
       );
```

ここまでにしてブラウザで確認すると、こうなる。
![스크린샷 2017-06-23 18.09.16.png](https://qiita-image-store.s3.amazonaws.com/0/93752/ea41787e-7a9b-ed80-14de-29da8db8e252.png "스크린샷 2017-06-23 18.09.16.png")

__Reactはjsxの中でHTMLタグをレンダリングすることを禁止しているので、タグがテキストとして扱われる。__
__しかし、dangerouslySetInnerHTMLを使えば、HTMLタグのレンダリングが可能になる。__

```diff:src/components/Card.js
diff --git a/src/components/Card.js b/src/components/Card.js
index 8b3671e..4c2d3c1 100644
--- a/src/components/Card.js
+++ b/src/components/Card.js
@@ -1,4 +1,5 @@
 import React, { Component } from 'react';
+import Marked from 'marked';
 import CheckList from './CheckList';

 class Card extends Component {
@@ -19,7 +20,7 @@ class Card extends Component {
     if (this.state.showDetails) {
       cardDetails = (
         <div className="card__details">
-          {this.props.description}
+          <span dangerouslySetInnerHTML={{ __html: Marked(this.props.description) }} />
           <CheckList cardId={this.props.id} tasks={this.props.tasks} />
         </div>
       );
```

正常に表示されることを確認しよう。
![스크린샷 2017-06-23 18.35.29.png](https://qiita-image-store.s3.amazonaws.com/0/93752/0c762b66-3170-c710-c416-f7dd08f7732c.png "스크린샷 2017-06-23 18.35.29.png")

### インラインスタイルを使ってカードの色を指定

データに色を追加する。

```diff:src/index.js
diff --git a/src/index.js b/src/index.js
index eeb27fe..dd395b8 100644
--- a/src/index.js
+++ b/src/index.js
@@ -8,14 +8,16 @@ const cardsList = [
   {
     id: 1,
     title: 'Read the Book',
     description: 'I should read the **whole** book',
+    color: '#BD8D31',
     status: 'in-progress',
     tasks: [],
   },
   {
     id: 1,
     title: 'Write some code',
     description: 'Code along with the samples in the book',
+    color: '#3A7E28',
     status: 'todo',
     tasks: [
       {
```

propsでCardコンポーネントに色も伝えるように修正する。

```diff:src/components/List.js
diff --git a/src/components/List.js b/src/components/List.js
index 238333e..a3bf1cd 100644
--- a/src/components/List.js
+++ b/src/components/List.js
@@ -8,6 +8,7 @@ class List extends Component {
         id={card.id}
         title={card.title}
         description={card.description}
+        color={card.color}
         tasks={card.tasks}
       />
       );
```

インラインスタイルを持つオブジェクトを生成して、このオブジェクトをインラインで使うdivタグを生成する。

```diff:src/components/Card.js
diff --git a/src/components/Card.js b/src/components/Card.js
index 4c2d3c1..5e30838 100644
--- a/src/components/Card.js
+++ b/src/components/Card.js
@@ -26,8 +26,19 @@ class Card extends Component {
       );
     }

+    const sideColor = {
+      position: 'absolute',
+      zIndex: -1,
+      top: 0,
+      bottom: 0,
+      left: 0,
+      width: 7,
+      backgroundColor: this.props.color
+    };
+
     return (
       <div className="card">
+        <div style={sideColor} />
         <div
           className={this.state.showDetails ? 'card__title card__title--is-open' : 'card__title'}
           onClick={this.toggleDetails}
```

結果
![스크린샷 2017-06-23 18.46.41.png](https://qiita-image-store.s3.amazonaws.com/0/93752/51b39e28-a2ca-8308-5668-a5060f74f203.png "스크린샷 2017-06-23 18.46.41.png")

### カードに入力フィールドを追加

```diff:src/components/CheckList.js
diff --git a/src/components/CheckList.js b/src/components/CheckList.js
index 5ffe6ba..3b878a7 100644
--- a/src/components/CheckList.js
+++ b/src/components/CheckList.js
@@ -14,13 +14,14 @@ class CheckList extends Component {
     return (
       <div className="checklist">
         <ul>{tasks}</ul>
+        <input
+          type="text"
+          className="checklist--add-task"
+          placeholder="Type then hit Enter to add a task"
+        />
       </div>
     );
   }
 }

 export default CheckList;
```

結果
![스크린샷 2017-06-23 22.44.02.png](https://qiita-image-store.s3.amazonaws.com/0/93752/9f29be98-3f67-9282-9244-7ea26386c0cf.png "스크린샷 2017-06-23 22.44.02.png")

### Keys

ブラウザのdeveloper toolsで確認すると、Consoleで以下のようなWarningが表示される。

```
Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `List`. See https://fb.me/react-warning-keys for more information.
```

arrayやiterator中の子ノードはuniqueなkey属性を持たなければならない。
識別子（uniqueなkey）があれば、特定のノードの探索が早くなる。
Reactにてレンダリングのコストは低くない。
そんなコストを少しでも減らすためには識別子が必要なのだ。
識別子を入れてみよう。

```diff:src/components/CheckList.js
diff --git a/src/components/CheckList.js b/src/components/CheckList.js
index 3b878a7..991d734 100644
--- a/src/components/CheckList.js
+++ b/src/components/CheckList.js
@@ -4,10 +4,10 @@ import PropTypes from 'prop-types';
 class CheckList extends Component {
   render() {
     const tasks = this.props.tasks.map((task) => (
-      <li className="checklist__task">
+      <li key={task.id} className="checklist__task">
         <input type="checkbox" defaultChecked={task.done} />
         {task.name}{' '}
         <span className="checklist__task--remove" />
       </li>
     ));
```

```diff:src/components/List.js
diff --git a/src/components/List.js b/src/components/List.js
index a3bf1cd..38b60f9 100644
--- a/src/components/List.js
+++ b/src/components/List.js
@@ -5,6 +5,7 @@ class List extends Component {
   render() {
     const cards = this.props.cards.map((card) => {
       return (<Card
+        key={card.id}
         id={card.id}
         title={card.title}
         description={card.description}
```

developer toolsにてWarningが消えた。

\:bulb: よく使うarrayのコールバック関数

\:point_right: forEach

```javascript
array.forEach(callback [,that])
	array: 配列オブジェクト
	callback: 個々の要素を処理するための関数
	that: 関数callbackの中でthisが示すオブジェクト
	戻り値: 個々の要素
```

\:point_right: map

```javascript
array.map(callback [,that])
	array: 配列オブジェクト
	callback: 個々の要素を加工するための関数
	that: 関数callbackの中でthisが示すオブジェクト
	戻り値: 配列
```

\:point_right: some

```javascript
array.some(callback [,that])
	array: 配列オブジェクト
	callback: 個々の要素を判定するための関数
	that: 関数callbackの中でthisが示すオブジェクト
	戻り値: １つでも条件に合致した場合（=関数callbackが一度でもtrueを返した場合）はtrue、そうではない場合はfalse
```

\:point_right: filter

```javascript
array.filter(callback [,that])
	array: 配列オブジェクト
	callback: 個々の要素を判定するための関数
	that: 関数callbackの中でthisが示すオブジェクト
	戻り値: 条件に合致した要素だけを取り出した配列
```