続: SPA！温泉！（行きたい...

## せっかくなので

人数と金額を入力するコンポーネントと、計算して結果を出すコンポーネントに分けてみよう。

![スクリーンショット 2018-12-01 15.46.45.png](https://qiita-image-store.s3.amazonaws.com/0/105644/81be4a85-46c6-659b-4246-863b563a376d.png)

## 人数と金額を入力するコンポーネントの作成

NumberInputという名前で作成してみる（いいネーミングが思いつかず...
componentsフォルダに作成する。

```jsx:components/NumberInput.js
import React, { Component } from 'react';

class NumberInput extends Component {
  render() {
    return (
      <>
        <div><input type="text" placeHolder="人数を入力" /></div>
        <div><input type="text" placeHolder="合計金額を入力" /></div>
        <div><button>割り勘計算</button></div>
      </>
    );
  }
}

export default NumberInput;
```
外部コンポーネントであるNumberInputを参照するようにApp.jsも変更する。

```jsx:components/App.js
import React, { Component } from 'react';
import NumberInput from './NumberInput'
import './App.scss';

class App extends Component {
  render() {
    return (
      <div className="center">
        <NumberInput />
        <div>結果</div>
      </div>
    );
  }
}

export default App;
```
propsを使って、テキスト入力フィールドの初期値を設定してみる

```jsx:components/NumberInput.js
        <div><input type="text" value={this.props.number} placeHolder="人数を入力" /></div>
        <div><input type="text" value={this.props.money} placeHolder="合計金額を入力" /></div>
```

このままだと、プレースホルダーが表示されているが、App.jsを変更してみると

```jsx:App.js
        <NumberInput number="1" money="1000" />
```

![スクリーンショット 2018-12-03 11.49.33.png](https://qiita-image-store.s3.amazonaws.com/0/105644/e6aa58b5-803f-37bc-ceb5-3331e03ab837.png)

想像通り表示された。ただ、このpropsですが、実は初期値が設定出来て、いわゆる省略したときの値となる模様
以下を追記する。

```jsx:components/NumberInput.js
(中略)

NumberInput.defaultProps = {
    number: 2,
    money: 500,
};

export default NumberInput;
```

改めて、App.jsのプロパティ指定を削除してみた。

```jsx:App.js
        <NumberInput />
```

![スクリーンショット 2018-12-03 15.18.56.png](https://qiita-image-store.s3.amazonaws.com/0/105644/9c3b84a3-2106-507a-63a6-7279524554aa.png)

これも、想定通り。さて、割り勘計算ボタンをクリックしたときに、それぞれのテキスト入力フィールドに入っている値を取りたいわけです。という事で、テキスト入力フィールドの値を保持しておけばいいのでは！

onchange属性使って、入力文字の変更を捕らえて保持してみる！（長くなるので縦に書いてみる

```jsx:components/NumberInput.js
        <div>
          <input
            type="text"
            value={this.props.number}
            placeHolder="人数を入力"
            onChange={(e) => this.fireOnChangeNumber(e.target.value)}
          />
        </div>
```

これで、fireOnChangeNumberメソッドが呼び出されるので、実装する。propsのnumberに入れてみる。

```jsx:components/NumberInput.js
  fireOnChangeNumber(number) {
    console.log(number);
    this.props.number = number;
  }
```

いざ、入力文字を変更してみると

![スクリーンショット 2018-12-03 15.34.20.png](https://qiita-image-store.s3.amazonaws.com/0/105644/afe27d3f-6844-4161-f425-47b78e795573.png)

まじか！ダメなのか！

> 公式サイトより(powered by Google翻訳)
>
> 「Props」は読み取り専用です
>
> コンポーネントを関数として宣言しても、クラスとして宣言しても、それ自身のpropsを変更してはなりません。

書いてあった...orz

> 公式サイトより(powered by Google翻訳)
>
> 「props」と「state」は、ともに単純なJavaScriptオブジェクトです。両方ともレンダリングに影響する情報を保持しますが、1つの重要な点で異なります。
> 「state」は関数内で宣言された変数と同様にコンポーネント内で管理されるのに対して、「props」は関数のパラメータと同様にコンポーネントに渡されます。

変数的に使いたい場合は、stateを使えと... propsをstateに変更していく...

```jsx:components/NumberInput.js
        <div>
          <input
            type="text"
            value={this.state.number}
            placeHolder="人数を入力"
            onChange={(e) => this.fireOnChangeNumber(e.target.value)}
          />
        </div>
```
```jsx:components/NumberInput.js
  fireOnChangeNumber(number) {
    console.log(number);
    this.state.number = number;
  }
```

だが、しかし

![スクリーンショット 2018-12-03 16.39.25.png](https://qiita-image-store.s3.amazonaws.com/0/105644/ea4dcf67-06bc-5327-f40d-4d8098a4fd90.png)

stateがnullだと...そう、ないっていってる。

これは、クラスのコンストラクターで初期化しないとダメらしい。

```jsx:components/NumberInput.js
class NumberInput extends Component {

  constructor(props) {
    super(props);
    this.state = {
      number: this.props.number,
      money: this.props.money,
    };
  }

  (後略)

```

だが、しかし、テキスト入力フィールドが変更されない...
ブラウザのコンソール出力を見てみると

![スクリーンショット 2018-12-03 16.33.26.png](https://qiita-image-store.s3.amazonaws.com/0/105644/835e8f04-3f58-412a-be58-552d404233e7.png)

直接変更するなと。setStateメソッドを使えと。

```jsx:components/NumberInput.js
  fireOnChangeNumber(number) {
    console.log(number);
    this.setState({
      number: number
    });
  }
```

ぉぉぉお

実は、numberが同じなので、こうも書ける。

```jsx:components/NumberInput.js
  fireOnChangeNumber(number) {
    console.log(number);
    this.setState({ number });
  }
```

同様に金額のテキスト入力フィールドも変更すると、こんな感じになる。

```jsx:components/NumberInput.js
import React, { Component } from 'react';

class NumberInput extends Component {

  constructor(props) {
    super(props);
    this.state = {
      number: this.props.number,
      money: this.props.money,
    };
  }

  fireOnChangeNumber(number) {
    this.setState({ number });
  }

  fireOnChangeMoney(money) {
    this.setState({ money });
  }

  render() {
    return (
      <>
        <div>
          <input
            type="text"
            value={this.state.number}
            placeHolder="人数を入力"
            onChange={(e) => this.fireOnChangeNumber(e.target.value)}
          />
        </div>
        <div>
          <input
            type="text"
            value={this.state.money}
            placeHolder="合計金額を入力"
            onChange={(e) => this.fireOnChangeMoney(e.target.value)}
          />
        </div>
        <div><button>割り勘計算</button></div>
      </>
    );
  }
}

NumberInput.defaultProps = {
    number: 2,
    money: 500,
};

export default NumberInput;
```

あとは、ボタンをクリックしたときに、計算するのではなく入力値を外部へ渡せるように、onclick属性をつかう。

```jsx:components/NumberInput.js
        <div>
          <button onClick={(e) => this.fireOnClick(e)}>割り勘計算</button>
        </div>
```

そして、カスタムなイベントハンドラーを作成してみる。propsに追加すればいい。人数と金額を渡すようにする。

```jsx:components/NumberInput.js
  fireOnClick(e) {
    this.props.onCalc(this.state.number, this.state.money);
  }
```

App.jsでこれを受け取ってみる。

```jsx:components/App.js
        <NumberInput onCalc={(number, money) => this.fireCalc(number, money)}/>
```

onCalcに対するハンドラーを追加する。

```jsx:components/App.js
  fireCalc(number, money) {
    console.log(number);
    console.log(money);
  }
```

割り勘計算ボタンをクリックすると、ブラウザのコンソールに、値が出たらOK!

[この記事](https://qiita.com/tkymgr/items/d8aa399e7068dd7de696)のつづきでした。