【React】今更ながらReactでフロント勉強してみる【create-react-app】【サンプル】

# はじめに
フロントサイドは苦手だったんですが、自分の幅を広げる意味でも触れるようになりたいと思い今更なんですがReact勉強してみました。ただ、サンプルが載っている記事が少なかったので、今回はサンプルを中心に入門される方の手助けになるような形にしたいと思います。

「WebデベロッパーのためのReact開発入門」の一部のサンプルをES6に書き直したものを載せていこうと思います。用語も覚書き程度にまとめます。

# create-react-app
reactを始めたてで環境は手っ取り早く作りたかったのでFacebook製のコマンドラインツールであるcreate-react-appで環境作りしました。

## インストール

```
npm install -g create-react-app
```

## アプリ作成
sample-appというアプリを作ります。以下のコマンドを実行すると必要なファイル群を自動で生成してくれます。アプリ名はキャメルケースではダメみたいです。


```
create-react-app sample-app
```

## サーバー起動
以下のコマンドで```localhost:3000```でサーバー開始されます。

```
npm start
```

# サンプル書いてみた
今回一部のサンプルを参考にした本はES５で書かれていたため、ES6に書き直しつつポイントを書いていきたいと思います。

## 複数の要素を配置するコンポーネントを利用したJSX
まず超シンプルなコンポーネントをJSX使って表現したサンプルです。

いじるファイルはindex.jsと新たなに作るmultielement.jsというファイルです。

index.htmlは```create-react-app```コマンドで作ったファイルをそのまま使います。index.jsは以下のようにします。

```src/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import {MultiElement} from './multielement';

ReactDOM.render(
  <MultiElement />,
  document.getElementById('root')
);
```

multielement.jsという別ファイルからMultiElementというカスタムコンポーネントをimportしています。これによって別ファイルのコンポーネントをindex.jsに配置することができます。

### ES6
カスタムコンポーネントには最低限renderを実装しておきます。

```src/multielement.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class MultiElement extends React.Component {
  
  render() {
    <div>
      <h2>First Element</h2>
      <h2>Secound Element</h2>
      <h2>Third Element</h2>
    </div> 
  } 
}

export {MultiElement}
```

カスタムコンポーネントとして定義したMultiElementをindex.jsで使うためにはexport文を使います。実行すると以下のように表示されます。

<img width="710" alt="スクリーンショット 2018-08-05 18.16.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/ff0379a1-a319-3825-86eb-51bb5a141958.png">

こんな感じでサンプルを載っけていきます。

### ES5
ちなみにES5で以下にように書かれています。

```src/multielement.js
var MultiElement = React.createClass({
  render: function() {
    return (
      <div>
        <h2>First Element</h2>
        <h2>Secound Element</h2>
        <h2>Third Element</h2>
      </div>
    )
  }
});
```

カスタムコンポーネントの定義の仕方が違いますね。

## ステート付きのコンポーネントを利用したJSX①
Reactはコンポーネントの内部の状態を表現する変数的なものを「ステート」で管理するみたいです。

ユーザーからの入力をステートで管理してみます。

### ES6
新たにevaluator.jsを作って以下のように書きます。index.jsに先ほどのように外部ファイルのimportをして、ReactDOM.renderの第一引数のタグを変更してください。

```src/evaluator.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Evaluator extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      expression: ''
    };
  }

  reCalValue(event) {
    if (event.key === 'Enter') {
      this.setState({
        expression: event.target.value
      });
    }
  }

  render() {
    return(
      <div>
        <input
          type='text'
          onKeyPress={this.reCalValue.bind(this)}
        />
        <h2>{eval(this.state.expression)}</h2>
      </div>
    )
  }
}

export {Evaluator}
```

constructorではステートの初期化を行います。辞書形式で定義します。
引数にeventを受け取った関数をイベントハンドラと呼ぶらしいです。このイベントハンドラ内で呼んでいるsetStateはステートを変更する関数です。

event.target.valueでテキストエリア内に入力された文字列を受け取って、初期化したステートの一つであるexpressionを更新します。

自分がはまった部分としては、イベントハンドラ内で```this```を使いたい場合はイベントハンドラを呼び出す部分で```.bind(this)```を追加しなければいけない点です。下記のES5ではこのような記述がないので書き直してる時にはまりました。

evalは組み込み関数？的なやつなので特に定義しなくても使えるようです。
以下が実行結果になります。

<img width="326" alt="スクリーンショット 2018-08-05 19.03.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/392bc980-3b55-a9fd-b69a-af3359bc7c22.png">

Enterを押すと・・・

<img width="385" alt="スクリーンショット 2018-08-05 19.04.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/39f8ddba-3263-9bcd-7aa1-233812ea4e3c.png">



### ES5
上記で書いた```.bind(this)```やステートの初期化などかなり書き方に違いがでてきましたね。

```src/evaluator.js
var Evaluator = React.createClass({
  getInitialState: function() {
    return {
      express: ''
    }
  },
  reCalcValue: function(e) {
    if (e.key === 'Enter') 
      this.setState ({
        expression: e.target.value
      });
  },
  render () {
    return (
      <div>
        <input
          type='text'
          onKeyPress={this.reCalValue}
        />
      </div>
    );
  }
});
```

## スプレッド属性を利用したJSX
タグの属性をまとめて定義する仕方があるようです。
今回はindex.jsのみを変更します。

```src/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

var myAttributes = {
  type: 'checkbox',
  checked: 'true',
  size: 8
}

ReactDOM.render(
  <p><input {...myAttrubutes}></p>,
  document.getElementById('root')
);
```

htmlのようにタグに属性をゴリゴリ書くよりもスッキリまとめることができますね。{...~}という書き方には違和感がありますが(笑)
以下、実行結果です。

<img width="295" alt="スクリーンショット 2018-08-05 21.05.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/ac4eeff8-0301-eaac-cecc-fd5e012aed53.png">



同じような感じでstyleの定義もできます。

```src/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

var myStyle = {
  fontStyle: 'normal',
  fontFamily: 'Courier',
  color: 'brown'
}

ReactDOM.render(
  <h2 style={{...myStyle}}>Lets Spread!</h2>,
  document.getElementById('root')
);
```

<img width="328" alt="スクリーンショット 2018-08-05 21.08.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/1cd09c91-a150-dee3-1129-94b015c63c73.png">

## カスタムコンポーネントでスプレッド属性を利用したJSX
上記ではReactDOM.render内でhtml要素に対して属性を設定しました。これをカスタムコンポーネントでも使うサンプルになります。

### ES6
新たにcolorbutton.jsを作ります。

```src/colorbutton.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class ColorButton extends React.Component {
  render() {
    return (
      <button {...this.props}>
        {this.props.children}
      </button>
    )
  }
}

export {ColorButton}
```

Reactのコンポーネントが外部からデータを受け取るために利用するのが「プロパティ」と言われるものです。このプロパティをpropsという変数を通して取得できます。

なのでプロパティとして定義するのは、buttonの属性と文字列になります。

```src/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import {ColorButton} from './colorbutton';

var myStyle = {
  color: '#A04020',
  backgroudColor: '#90C0A0',
  fontFamily: 'Serif',
  fontSize: 22
}

ReactDOM.render(
  <ColorButton
    style={{...myStyle}}
    type='button'
    disabled={false}
  >Clicked Me!</ColorButton>,
  document.getElementById('root')
)
```

カスタムコンポーネントでスプレット属性を利用し、子コンポーネントではプロパティにpropsを通して値を取得しました。その結果が以下になります。

<img width="302" alt="スクリーンショット 2018-08-05 21.59.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/8bd2570c-f6d1-9d25-b20c-1a1fe930cb7b.png">

## ステート付きコンポーネントを利用したJSX②
上記のステート付きコンポーネント①ではボタンのクリックをきっかけにステートの更新をしました。次にユーザーからの入力に対して随時状態を更新するJSXのサンプルを書いてみます。

### ES6
新たにtextinput.jsを作ります。

```src/textinput.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class TextInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      dispText: '入力欄'
    }
  }
　　　　
  handleInput(event) {
    this.setState({
      dispText: event.target.value
    })
  }　　

  render() {
    return (
      <div>InputText:
        <input
          type='text'
          value={this.state.dispText}
          onChange={this.handleInput.bind(this)}
        />
        <h2>{this.state.dispText}</h2>
      </div>
    )
  }
}
```
textの初期値として、入力欄という文字列を出力させたいため、ステートの初期化時に「入力欄」という文字列を設定しておきます。

inputタグのonChange属性は入力欄に変化があるたび呼び出されるもの定義します。今回はhandleInputというイベントハンドラを設定しました。ここでもイベントハンドラ内でthisを使用したいため```.bind(this)```を追加しています。

これで随時、入力欄の値をステートとして管理することができます。コンソール出力とかで確認してみるとわかりやすいです。

実行結果が以下になります。

<img width="346" alt="スクリーンショット 2018-08-05 22.58.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/36c1f3b3-5426-21da-55a4-e13e50b0e61c.png">


<img width="402" alt="スクリーンショット 2018-08-05 23.00.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/6b9c1f2f-47d8-2ab0-eaf3-e87f2400a416.png">

こんな感じでイベントハンドラ,プロパティ,ステートなどを使いこなしてReactの書いてきました。

このあとはサンプルと出力結果のみを載せていきます。

# サンプル集

## 複数選択できるチェックボックス

```src/checkbox.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class CheckBox extends React.Component{
  constructor(props) {
    super(props);
    this.state = {
      selection: ['first']
    };
  }

  handleChange(event) {
    console.log(this.state.selection);
    var selection = this.state.selection;
    var position = selection.indexOf(event.target.value);
    if (position === -1) {
      selection.push(event.target.value);
    } else {
      selection.splice(position, 1);
    }
    this.setState({selection: selection});
    console.log(this.state);
  };

  render() {
    return(
      <div>いくつでも選んでください
      <div><input
        type='checkbox'
        value='first'
        checked={this.state.selection.indexOf('first') !== -1}
        onChange={this.handleChange.bind(this)}
      />最初の選択肢</div>
      <div><input
        type='checkbox'
        value='secound'
        checked={this.state.selection.indexOf('secound') !== -1}
        onChange={this.handleChange.bind(this)}
      />二番目の選択肢</div>
      <div><input
        type='checkbox'
        value='third'
        checked={this.state.selection.indexOf('third') !== -1}
        onChange={this.handleChange.bind(this)}
      />三番目の選択肢</div>
      </div>
    );
  }
}

export {CheckBox};
```

<img width="408" alt="スクリーンショット 2018-08-05 23.25.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/e077077d-3520-e0c8-875d-915898bb2d5f.png">

<img width="343" alt="スクリーンショット 2018-08-05 23.27.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/ee732f9d-a291-4af5-de4d-5b0ab6ed886a.png">

チェックされている状態をステートとして定義したselectionの配列で判定していますね。

## ステート値による結果分岐

```src/formsubmit.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class ForSubmit extends Component {
  constructor(props) {
    super(props);
    this.state = {
      selection: this.props.selection
    }
  }

  handleChange(event) {
    var selection = this.state.selection
    var position = selection.indexOf(event.target.value)
    if (event.target.checked) {
      selection.push(event.target.value)
    } else {
      selection.splice(position, 1)
    }
    this.setState({selection: selection})
  };

  handleSubmit(event) {
    event.preventDefault();
    if(this.state.selection.length < 2) {
      return;
    }
    console.log('Submitting：', this.state.selection)
    this.setState({selection: []})
  };

  render() {
    return(<form onSubmit={this.handleSubmit.bind(this)}>２つ以上を選んでください：
        <div>
          <input
            type='checkbox'
            value='first'
            checked={this.state.selection.indexOf('first') !== -1}
            onChange={this.handleChange.bind(this)}
          />最初の選択肢
        </div>
        <div>
          <input
            type='checkbox'
            value='secound'
            checked={this.state.selection.indexOf('secound') !== -1}
            onChange={this.handleChange.bind(this)}
          />次の選択肢
        </div>
        <div>
          <input
            type='checkbox'
            value='third'
            checked={this.state.selection.indexOf('third') !== -1}
            onChange={this.handleChange.bind(this)}
          />最後の選択肢
        </div>
        <input type='submit' value='決定'　/>
      </form>)
  }
}

export {ForSubmit}
```
<img width="301" alt="スクリーンショット 2018-08-05 23.38.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/735f8a8d-e418-0eb8-d427-6424e423b0bf.png">

決定押すと、、

<img width="354" alt="スクリーンショット 2018-08-05 23.37.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/e400e0bb-28b7-5257-6f91-38e96b69f52d.png">

2つ以上選択されていると出力され、2つ未満である場合は変化しない。

## 配列要素の自動展開

```src/maptable.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class DispTable extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    var tableBody = this.props.data.map(function(person){
      return (<tbody><tr key={person.id}>
        <th>{person.name}</th>
        <th>{person.area}</th>
        <th>{person.number}</th>
      </tr></tbody>)
    })

    return (<table className='regularTable'>
      <thread>
        <tr>
          <th>名前</th>
          <th>地域</th>
          <th>番号</th>
        </tr>
      </thread>
      {tableBody}
    </table>)
  }
}

export {DispTable};
```

```src/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import {DispTable} from './maptable';

var tableData = [
  {id:1, name:'山田太郎', area:'東京都港区', number:'123123'},
  {id:2, name:'山田太郎', area:'東京都港区', number:'123123'},
  {id:3, name:'山田太郎', area:'東京都港区', number:'123123'}
]

ReactDOM.render(
  <DispTable data={tableData}/>,
  document.getElementById('content')
)
```

<img width="367" alt="スクリーンショット 2018-08-05 23.43.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/f66c3bab-5da6-b650-3c69-9df87b356a3a.png">

プロパティとして与えた配列のmapメソッドで自動展開が可能のようです。

# おわりに
React始めるにあたって、基本知識であるステートやプロパティ,コンポーネントという単語が理解しづらかったのでこういったサンプルを通してReactを触り始めるの方の手助けになれば幸いです。