【React】ToDoアプリを作ってみよう【後編】

#:star: 関連
* [Reactの環境構築を最短で行う](http://qiita.com/mikan3rd/items/496a87fc8b76263d0a91)
* [【React】ToDoアプリを作ってみよう【前編】](http://qiita.com/mikan3rd/items/20152cdd63a708264a9e)
* [【React】ToDoアプリを作ってみよう【後編】](http://qiita.com/mikan3rd/items/0a1be8fa9f349735cf4a)←ここ
* [【React】ToDoアプリを発展させよう【非同期処理】](http://qiita.com/mikan3rd/items/41e4a94a50fa6676542a)

#:star: 目標
* Reactで画像のようなToDoアプリを作ります。
* 今回はTodoを投稿する機能をつけましょう。
* 見本：[GitHub](https://github.com/mikan3rd/todo-list.react.js)

![todo-app4.gif](https://qiita-image-store.s3.amazonaws.com/0/178077/e23e6b23-4ed7-1ee0-df6b-372c382463ec.gif)


#:star: 手順
##:pencil: フォームの作成
###:pencil2: App.js
* `import Form from './Form'`で`Form.js`を読み込む記述をします。
* `render(){}`の`return()`の中に`<Form />`を記述し、Formを呼び出します。。この時点ではForm.jsに何も記述していないのでエラーが出ます。

###:pencil2: Form.js
````js:src/Form.js
import React, { Component } from 'react'
import './css/form.css'

class Form extends Component {
  render() {
    return (
      <div className="form">
        <form>
          <input name="title" type="text" placeholder="タイトル ※必須" defaultValue="reactの勉強" /><br/>
          <textarea name="desc" placeholder="説明を入力" defaultValue="todoアプリを作っています！"></textarea><br/>
          <button type="submit">todoを作成</button>
        </form>
      </div>
    )
  }
}

export default Form
````
* formタグを使って通常のHTML同様にフォームを作りましょう
* 入力するのが面倒な場合は`defaultValue`を設定しておくと楽です

##:pencil: 投稿機能の実装
###:pencil2: App.js
````js:src/App.js
import React, { Component } from 'react'
import TodoList from './TodoList'
import Form from './Form'
import './css/App.css'


class App extends Component {

  constructor() {
    super()
    const todos = [
      {
        id: 1,
        title: "Hello, React!",
        desc: "React始めました",
        done: false
      },
      {
        id: 2,
        title: "Hello, Redux!",
        desc: "Reduxも始めました",
        done: false
      },
    ]
    this.state = {
      todos: todos,
      countTodo: todos.length + 1,
    }
  }

  handleSubmit(e) {
    e.preventDefault();
    const title = e.target.title.value;
    const desc = e.target.desc.value;
    const todos = this.state.todos.slice()
    const countTodo = this.state.countTodo

    todos.push({
      id: countTodo,
      title: title,
      desc: desc,
      done: false,
    });

    this.setState({ todos })
    this.setState({ countTodo: countTodo + 1 })


    e.target.title.value = '';
    e.target.desc.value = '';
  }


  render() {
    return (
      <div className="app">
        <h1>todoアプリを作ってみた</h1>
        <Form handleSubmit={this.handleSubmit.bind(this)} />
        <TodoList
          todos={this.state.todos}
          />
      </div>
    );
  }
}

export default App
````
* まず、constructorを修正します。初期のtodosの中身は`const todos`とし、`this.state`に入れるようにしました。ややこしいですが、`this.state`は`state名: stateの内容`のように定めます。
* **countTodo**というTodoの数をカウントするstateを作ります。これをidの代わりに使用します。
* これらconstructorで定められたstateの内容は最初の一度しか呼ばれないため、Todoを投稿して増えていっても`countTodo: todos.length`は増えていきません。Todoを作成する時にcountTodoを増やす記述が必要です。
* `<Form handleSubmit={this.handleSubmit.bind(this)} />`とし、Formコンポーネントで**handleSubmit()**という関数が使えるようにします。
* `this.handleSubmit(this)`とすると、このthisは呼び出された先(この場合はFormコンポーネント)になってしますため、**.bind(this)**をつけることで、thisをこのコンポーネントに束縛できます。
* 参考：[JavaScriptの「this」は「4種類」？？](http://qiita.com/takeharu/items/9935ce476a17d6258e27)
* 関数handleSubmit(e)を定義します。`e.preventDefault()`で画面の更新がされないようにできます。また、`e.target.(name属性).value`でフォームの中身を取り出すことができます。
* **.slice()**でコピーされた新しい配列を作り、stateが直接変更されることを防ぎます。
* 参考：[【Javascript】値渡しと参照渡しについてあらためてまとめてみる](http://qiita.com/migi/items/3417c2de685c368faab1)
* 配列todosに新しいTodoの中身をpushし、**setState({})**でstateを更新します。本来は`setState({todos: todos})`の様に変更前と変更後のオブジェクトを指定する必要がありますが、同じ名前の場合は今回のように省略できます。

###:pencil2: Form.js
* <form>に`<form onSubmit={this.props.handleSubmit}>`を記述し、propsとして受け取ったhandleSubmitをonSubmit時に発火するようにします。この時、App.jsで定義したhandleSubmit()の処理が動き、Todoの投稿ができるようになりました。

![todo-app78.gif](https://qiita-image-store.s3.amazonaws.com/0/178077/c6f6bece-1620-1bbc-0ca2-a0e19dac151d.gif)


##:pencil: Todoの完了/未完了の切り替え実装
###:pencil2: App.js
````js:src/App.js
...

  setTodoStatus(clickTodo) {
    const todos = this.state.todos.slice();
    const todo = todos[clickTodo.id - 1];
    todo.done = !todo.done;
    todos[clickTodo.id - 1] = todo;

    this.setState({ todos });
  }

...

        <TodoList
          todos={this.state.todos}
          setTodoStatus={this.setTodoStatus.bind(this)}
          />

````
* Todoの完了/未完了を切り替える関数setTodoStatus()を定義します。handleSubmit（）の時と同じように.slice()で新しくコピーした配列を使います。
* 配列は0から、idは１から始まるので、idから１を引いた数字で配列のTodoを取り出します。
* todo.doneはtrue/falseのBoolean型なので、**!**マークで真偽を反転させることができます。これをtodosに入れ直し、setState()で更新します。
* 関数setTodoStatusを、まずはTodoListに渡しましょう。.bind()をお忘れなく。

###:pencil2: TodoList.js
````js:src/TodoList.js
...

  render() {
    const todos = this.props.todos.map( todo =>
      <Todo
        key={todo.id}
        {...todo}
        setTodoStatus={this.props.setTodoStatus}
      />
    )
...
````
* 関数setTodoStatus()をさらにTodoに渡します。

###:pencil2: Todo.js
````js:src/Todo.js
...

  render() {
    const className = this.props.done ? 'done' : 'undone';
    const link = this.props.done ? '元に戻す' : '完了！'
    return(
      <li className={className}>
        <span>{this.props.id}</span>
        <span>：{this.props.title}　　</span>
        <a href="" onClick={(e) => { e.preventDefault(); this.props.setTodoStatus(this.props)}}>{link}</a>　　
        <p>{this.props.desc}</p>
      </li>
    );
  }

...
````
* onClick時にsetTodoStatusが発火するようにします。**`(  ) => {  }`**は**アロー関数**というES6の新しい記法です。
* 参考：[【JavaScript】アロー関数式を学ぶついでにthisも復習する話](http://qiita.com/mejileben/items/69e5facdb60781927929)
* `className`もdoneのtrue/falseに合わせて切り替わるよう、三項演算子で記述しています。

![todo-app8.gif](https://qiita-image-store.s3.amazonaws.com/0/178077/451427e0-59db-407e-97e1-d6e54e584c90.gif)


###:star2: 以上で基本編は終了です！
* 途中の難しい部分について理解を深めるには参考リンクをご参照ください。
* 内容に不備等ありましたら、お手数ですがコメントにてお願いします。

#:star: 参考
* [一人React.js Advent Calendar](http://qiita.com/advent-calendar/2014/reactjs)
* [ES6版React.jsチュートリアル](http://qiita.com/nownabe/items/2d8b92d95186c3941de0)
* [JavaScriptの「this」は「4種類」？？](http://qiita.com/takeharu/items/9935ce476a17d6258e27)
* [【Javascript】値渡しと参照渡しについてあらためてまとめてみる](http://qiita.com/migi/items/3417c2de685c368faab1)
* [【JavaScript】アロー関数式を学ぶついでにthisも復習する話](http://qiita.com/mejileben/items/69e5facdb60781927929)