React.jsのコンポーネントの書き心地を試してみた(仮)

初投稿です。嘘です。
ヒロです。

今回はReact.jsのコンポーネントについて、簡単なサンプルを書きつつ、書き心地を試してみました。
例に漏れず、今回も備忘録です。

今回作ったサンプルはこちらにあります。
https://github.com/takahiro-saeki/react-basic-todo/tree/module-test

毎度雑ですみません。

`$npm i`した後にhttp://localhost:8080/template/ にアクセスして頂ければサンプルが見れると思います。

##ディレクトリ構成
ディレクトリ構成については以下のような感じです。

```lang:ディレクトリ構成
.
├── README.md
├── assets
│   └── js
│       ├── common.js
│       └── component
│           ├── content.jsx
│           ├── counter.jsx
│           └── list.jsx
├── npm-debug.log
├── package.json
├── template
│   ├── index.html
│   └── js
│       └── main.js
└── webpack.config.js
```

今回は`assets/js`ディレクトリのみ使用します(簡単に説明します。)

##今回のサンプルのゴール

- コンポーネント単位で分割した際に一つのイベントに対し、複数の箇所に影響が及ぶ場合の設計パターンを知る(考える)
- propsの伝搬を再度把握する(したい)
- jQueryで行っていた.show()や.hide()みたいなのってどうやるのか知りたい。

本当に超簡単なことしかしていないので、ざっくり説明します。

##クリックすると要素が消えてカウントアップするサンプル
まずはメインとなるファイル。ここにクリックした時のイベントやらカウントアップを設定しています。

```js:js/component/content.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import Counter from './counter';
import List from './list';

export default class Main extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      list: [
        { id: 0, name: 'hoge' },
        { id: 1, name: 'foo' },
        { id: 2, name: 'bar' },
        { id: 3, name: 'ruki' },
        { id: 4, name: 'reita' },
        { id: 5, name: 'uruha' },
        { id: 6, name: 'kai' },
        { id: 7, name: 'aoi' },
        { id: 8, name: 'yune' }
      ],
      count: 0
    }
    this.hide = this.hide.bind(this);
  }

  hide(e) {
    e.target.style.display = 'none';
    this.setState({
      count: this.state.count + 1
    })
    if (this.state.count === 8) {
      alert('終了！')
      this.setState({
        count: 0
      })
      const list = document.querySelectorAll('li');
      for (var i = 0; i < list.length; i++) {
        list[i].style.display = 'block';
      }
    }
  }

  render() {
    const lists = this.state.list.map((li, i) => {
      return <List onClick={this.hide} key={li.id} name={li.name} />
    })

    return (
      <section>
      <Counter count={this.state.count} title='スーパーカウンター' />
      <ul>{lists}</ul>
      </section>
    )
  }
}
```
`constructor`内のthis.state内にクリックする為の要素のデータとカウンターのデータを設定しています。
`hide()`メソッド内の`e.target.style.display = 'none';`でクリックした要素の`display`を`none`に変え、非表示にし、`setState()`で`this.state.count`の値を加算しています。
これにより、リストで並んでいる要素をクリックするとその要素が非表示になり、カウンターの値を1追加しています。
また要素が全てなくなった際に`this.state.count`の値を0に戻し、再度リストを表示させています。
`render`については`.map`でリストの数だけ`<li />`を生成しています。
また、`  <Counter count={this.state.count} title='スーパーカウンター' />`とすることで、MainクラスのstateをCounterコンポーネントのpropsとしてデータの伝搬をしています。そうすることにより、、、

```js:component/counter.jsx
import React from 'react';

export default class Counter extends React.Component {
  static defaultProps = {
    count: 0,
    title: 'カウンター'
  }

  static propTypes = {
    count: React.PropTypes.number.isRequired,
    title: React.PropTypes.string.isRequired
  }

  render() {
    return (
      <div>{this.props.title}：{this.props.count}</div>
    )
  }
}
```
counterコンポーネントの中でpropsとしてデータを受け取ることが出来ます。また`<List />`も同様です。

```js:component/list.jsx
import React from 'react';

export default class List extends React.Component {
  static defaultProps = {
    name: 'デフォルト'
  }

  static propTypes = {
    name: React.PropTypes.string.isRequired
  }

  render() {
    return (
      <li onClick={this.props.onClick}>{this.props.name}</li>
    )
  }
}
```
コンポーネントのクリックイベントってどうやって持たせたら良いんだろう？とちょっと悩んだのですが、`this.props.onClick`とすることで、親側のクリックイベント(今回の場合だと`hide()`)を実行させています。
何処に何を持たせるが良いかみたいなルールが自分の中で把握し切ってないので、まだあやふやな部分も多いですが、なるべく子コンポーネントにはデータやイベントは持たせずに親から流し込むように設計するのが良いかなと思います。

そして最後に`common.js`でrenderしています。

```js:js/common.js
import React from 'react';
import ReactDOM from 'react-dom';
import Main from './component/content';

ReactDOM.render(
  <Main />,
  document.getElementById('app')
)
```

というわけで以上になります。
jQueryを書いていたノリで解決しようとするとどうしても解決できず、モヤモヤしてしまうのは僕だけではないはず、、、？？？笑

でもこれも慣れな気がするので、継続して小さいサンプルを作りつつ、理解して行きたいと思います。
間違っている点や、気になる点などありましたら、お気軽にご指摘ください。

それではまたヽ(`･ω･´)ﾉ