React 入門チュートリアル②

一応この記事の続きです。[React 入門チュートリアル①](http://qiita.com/stivan622/items/ef4a0b9d5c5190b0e150)
react触ったことない方は①、stateとporpsにつまずいている方はこの記事からでも大丈夫だと思います。

今回は簡単なTODOリストアプリを作りながら、stateとporpsとカスタムコンポーネントの作成について理解してもらえればなと思います。

実際に作るのはこんな感じ
![スクリーンショット 2017-01-02 22.18.23.png](https://qiita-image-store.s3.amazonaws.com/0/157657/1e8517a2-5ee0-fa11-3f3b-455de77c8afe.png)


## 今回の目標
- stateとpropsの概念を理解する
- カスタムコンポーネントの作成の仕方

## 用意するもの
- [index.html](http://qiita.com/stivan622/items/ef4a0b9d5c5190b0e150#%E3%81%A7%E3%81%AF%E3%81%95%E3%81%A3%E3%81%9D%E3%81%8Fhello-world%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%8B%E3%82%89)

## いざ、実践

### リストの部分を作る

リスト部分を作りながら`props`と`カスタムコンポーネントの作成`について
説明しようと思います。

カスタムコンポーネント!!ていうとすごいように聞こえますが、オリジナルのコンポーネントという意味です。
それ以上でもそれ以下でもなくて、今回の例で言うとリストを表示する(配列からリストを作成する)コンポーネントを作成するて感じです。

では早速、`<script type="text/babel"></script>`内を編集していきます。

#### 関数でカスタムコンポーネントを定義する

まずはjs(javascript)の関数で定義して、表示までやっていきます。

```
const TodoList = () => {
  return (
    <ul>
      <li>ほげ</li>
      <li>ほげ</li>
      <li>ほげ</li>
    </ul>
  );
}

ReactDOM.render(
  <TodoList />,
  document.getElementById('content')
);
```

新しいところは、カスタムコンポーネントはjsの関数できるということだけです。

少し補足すると、`const TodoList = () => {}`は `function TodoList () {}`と同じ意味で、Reactコンポーネントは`<ul></ul>`とか`<li></li>`のJSXで書かれたHTMLを作成ものです。* [React 入門チュートリアル①](http://qiita.com/stivan622/items/ef4a0b9d5c5190b0e150)で詳しく説明してます。

これだと、表示するリストの内容が変えられないので、配列をこの`TodoListコンポーネント`に渡すとリスト化してくれるように改善しましょう。

#### propsで外から与えられた変数にアクセスする

ここで、`props`と`map()`を使って編集すると

```
const items = ["hoge","hogehoge","fugaga"];

const TodoList = (props) => {
  return (
    <ul>
      {props.items.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  );
}

ReactDOM.render(
  <TodoList items={items} />,
  document.getElementById('content')
);
```
`items`は`TodoListコンポーネント`に渡す配列を作成していて

`map()`の部分を少し補足すると

```
<ul>
  {items.map((item, index) => (
    <li key={index}>{item}</li>
  ))}
</ul>

# propsの部分を省きました
```
`map()`がわからない方はjsの内容になるのでググってもらって、
`<li key={index}>{item}</li>`の`key`はアイテムの変更の識別に必要なもので、
keyを設定しないと表示には成功しますが、コンソールに警告が出ます。  
アイテムの削除や変更機能などをつけていくとエラーやバグの原因になると思うので、Reactで配列を扱うときはkeyを設定しましょう。
*keyについては他の記事で追記する予定

次にporpsの説明です。

```
const TodoList = (props) => {
  return (
    <ul>
      {props.items}
    </ul>
  );
}

ReactDOM.render(
  <TodoList items={変数} />,
  document.getElementById('content')
);

*propsに関連する部分だけ抽出したもの、多分動かない
```

「propsで外から与えられた変数にアクセスする」でわかる人もいると思いますが
`<TodoList items={変数} />`で`TodoList`を呼び出す際にHTMLの属性の設定のように、変数を渡すことができて
`const TodoList = (props) => {}`のようにTodoList関数に引数を設定して
TodoList関数内では`{props.items}`で渡された変数にアクセスできます。

これであとはフォームを作成して、入力した内容を`配列item`に追加するロジックを作れば完成です!

### フォーム部分を作る

#### 関数でカスタムコンポーネントを定義する

またもやカスタムコンポーネントの定義から、`TodoList関数`はそのままにして、以下のように編集します。

```
class TodoApp extends React.Component {
  render() {
    return (
      <div>
        <h3>TODOアプリ</h3>
        <form>
          <input/>
          <button>Add</button>
        </form>
      </div>
    );
  }
}


ReactDOM.render(
  <TodoApp />,
  document.getElementById('content')
);
```

ReactではES6のclass構文を使用して、コンポーネントを定義するほうがメジャーだと思います。class構文を使うことによって、class内で独自の変数`state`を保持したり、メソッドを定義できるので、class構文のほうができる幅は広いです。

ではなぜ、`TodoList`は関数で定義したかというと、簡単に言うとclassを作るほどのコンポーネントでないからです。もちろんclassで作っても動作します。
propsしか使わない(関数では`state`は使用不可)ですし、将来的に関数で定義したほうが早く動作する可能性がある、シンプルにかけるので関数で定義しました。

説明するところがあるとしたら、classが呼び出されると`renderメソッド`が`return部分`を返してくれるので、以前と同じく`<TodoApp />`という形で呼び出しができます。

####　classにstate(ステータス)を持たせる

配列`items`を削除して、`TodoApp`を以下のように編集します。

```
class TodoApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {items: ["hoge","hogehoge","fugaga"]};
  }

  render() {
    return (
      <div>
        <h3>TODOアプリ</h3>
        <form>
          <input/>
          <button>Add</button>
        </form>
        <TodoList items={this.state.items} />
      </div>
    );
  }
}
```
これで、ブラウザを更新すると、フォームとリストが表示できるはずです。
ここでは、stateの設定と`<TodoList />`で前半で定義したコンポーネントを呼び出しています。

```
constructor(props) {
 super(props);
 this.state = {items: ["hoge","hogehoge","fugaga"]};
}
```
上の２行は構文で、stateの内容は`this.state = {items: ["hoge","hogehoge","fugaga"]};`で設定しています。
そして設定した内容は`{this.state.items}`で呼出せます。

itemsの初期値は空配列にしたいので、`this.state = {items: [ ]};`に修正します。

####　フォームに入力されたものを取得する

フォームに入力に入力されたもの、取得してtextというstate(ステータス)として保持させます。

まず、新しいstateを追加します。

```
constructor(props) {
  super(props);
  this.state = {items: [], text: ''};
}
```

次にinputタグのonChangeイベントを使って、`ステータスtext`を更新する`handleChangeメソッド`を定義します。

```
render() {
    return (
      <div>
        <h3>TODOアプリ</h3>
        <form>
          <input onChange={this.handleChange.bind(this)} />
          <button>Add</button>
        </form>
        <TodoList items={this.state.items} />
      </div>
    );
  }
  
  handleChange(e) {
    this.setState({text: e.target.value});
  }
}
```

ここからはReactというよりかはjsの実装て感じですね。

ステータスを更新するには`setState()メソッド`を使用します。
`setState({ステータス名: 更新する内容})`のように、書きます。

少しだけ補足すると`bind(this)`の部分は

```
constructor(props) {
  super(props);
  this.handleChange = this.handleChange.bind(this);
  this.state = {items: [], text: ''};
}
省略
   <input onChange={this.handleChange} />
```
みたいな書き方が推奨されてます。できるだけruturn内をシンプルに保つためでしょうか。

実際に`text`が更新されているかは、適当な場所に(h3タグの下とか)に`<p>{this.state.text}</p>`を追加して、実際に入力すれば確認してださい。

####　エンターを押したら、入力内容をitemsに追加する

入力内容をitemsに追加するメソッドを定義します。
`this.state.text`で入力内容を取得して、`setState`でitemsに追加して、更新という流れです。

```
handleSubmit(e) {
  e.preventDefault();
  const newItem = this.state.text;
  this.setState((prevState) => ({
    items: prevState.items.concat(newItem)
  }));
}
```

それをformタグのonSubmitイベントに割り当てます。

```
<form　onSubmit={this.handleSubmit.bind(this)}>
  <input onChange={this.handleChange.bind(this)} />
  <button>Add</button>
</form>
```

これで、入力したものがリスト化されるはずです!!
実際に動かしてみましょう。

いよいよ最後です！
動かしてみると、フォーム入力したものが残ったままなので、`handleSubmitメソッド`が動く段階でついでに`ステータスtext`を空の文字列に更新しましょう。
また、inputタグのvalue属性にこれを渡してあげないと見た目上は反映されないので編集してましょう。

ついでに全体のコードを載せるとこんな感じになると思います。

```
<script type="text/babel">
  class TodoApp extends React.Component {
    constructor(props) {
      super(props);
      this.state = {items: [], text: ''};
    }
    render() {
      return (
        <div>
          <h3>TODOアプリ</h3>
          <form　onSubmit={this.handleSubmit.bind(this)}>
            <input onChange={this.handleChange.bind(this)} value={this.state.text} />
            <button>Add</button>
          </form>
          <TodoList items={this.state.items} />
        </div>
      );
    }

    handleChange(e) {
      this.setState({text: e.target.value});
    }

    handleSubmit(e) {
      e.preventDefault();
      const newItem = this.state.text;
      this.setState((prevState) => ({
        items: prevState.items.concat(newItem),
        text: ''
      }));
    }
  }


  const TodoList = (props) => {
    return (
      <ul>
        {props.items.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    );
  }

  ReactDOM.render(
    <TodoApp />,
    document.getElementById('content')
  );
</script>
```

## おわり
疲れました。でも、ここまでくるとだいぶ作れるUIの幅が広がると思います。
自分はjsがちょっと怪しかったのです。何回もエラーを吐いて大変でしたが
なんとか完成してよかったです。
お疲れ様でした。

### 記事作成TODO
- [ ] React 入門チュートリアル③
- [ ] key(React)について
- [ ] bind(this)
- [ ] concat