【React】子コンポーネントから親コンポーネントへの値受け渡し…まとめ（クラスコンポーネントの場合）

Reactで子コンポーネントに関数を受け渡す方法は以下のようにpropsで橋渡しをすることがわかりました。

- [React/React Nativeにおけるコンポーネント間での値・関数・中身の受け渡し](https://qiita.com/wktq/items/c079db14e6246e49fa48)

ですが、今回のように子コンポーネントの中に引数が存在して、それを処理したい場合どうするかというのが課題で、調べてみた結果、親コンポーネントから子コンポーネントへ値を受け渡すのは可能なのに対し、ReactにはVueやAngularのように**子コンポーネントから親コンポーネントへ値を受け渡すemitメソッドのようなものは存在しない**ようです。

ところが、今回実装したいのはイベントが子コンポーネントのJSXにあり、しかも親コンポーネントから引っ張ってきた値を子コンポーネントで処理して、親コンポーネントにある結果欄に値を出力する操作です。…その方法を探ってみた結果、Reactでの解釈は

- 子コンポーネントで行われるべきイベント処理を受け渡し、親コンポーネントで同期する

これで、次のようなデータ処理（サンプルとして電卓機能を作成）が可能になります。

※次章では、子コンポーネントしか持っていない値を親コンポーネントに受け渡す方法も載せています。

# １:イベントのみが子コンポーネントに存在する場合
## 値を表示させる
元になったサイトは以下の通りで、コンポーネントを使って簡易な電卓ツールを作成していきます。

- [Reactコンポーネント間の値の受け渡し](https://qiita.com/s_harada/items/bfc9fc61654fe99b9b11)

これを応用して電卓のキーを作成します。

では、このキーを打鍵すると文字が表示されるようにして、以下のように振り分けます。

- 文字が数字の場合は、数値に変換する。引き続き数字の場合は桁をずらし、計算記号が打たれた場合は被序数を決定。
- 文字が計算記号の場合は、被序数と序数を確定し、計算を行う
- 文字が＝の場合は合計を計算する。
- 文字がCの場合は全てリセットする。

このようにして作ったサンプルが以下のプログラムとなります。
```Calc.js
import React from "react";
import Setkey from "./Setkey"


export default class Calc extends React.Component {
	constructor(props){
		super(props)
		this.state = {
			lnum: null, //今までの値
			cnum: 0, //追加の値
			sum: 0, //合計値
			str: '', //打ち込んだ文字
			sign: '', //計算記号
      vals:[
        [['7','7'],['8','8'],['9','9'],['div','÷']],
        [['4','4'],['5','5'],['6','6'],['mul','×']],
        [['1','1'],['2','2'],['3','3'],['sub','-']],
        [['0','0'],['eq','='],['c','C'],['add','+']],
			],
			
		}
		this.getCharstate = this.getChar.bind(this) //メソッドのバインド
	}
	
	getChar(chr,str){
		let lnum = this.state.lnum　//被序数
		let cnum = this.state.cnum //序数
		let sum = this.state.sum //合計
		let sign = this.state.sign
		str = this.state.str + str
		if(chr.match(/[0-9]/g)!== null){
			let num = parseInt(chr)
			cnum = cnum * 10 + num //数値が打ち込まれるごとに桁をずらしていく
		}else if(chr.match(/(c|eq)/g) == null){
			if(lnum != null){
				lnum = this.calc(sign,lnum,cnum)
			}else{
				if(chr == "sub"){
					lnum = 0
				}
				lnum = cnum
			}
			sign = chr
			cnum = 0
		}else if( chr == "eq"){
			lnum = this.calc(sign,lnum,cnum)
			sum = lnum
		}else{
			lnum = null
			cnum = 0
			sum = 0
			str = ''
		}
		this.setState({lnum: lnum})
		this.setState({cnum: cnum})
		this.setState({sum: sum})
		this.setState({str: str})
		this.setState({sign: sign})
	}
	
	//計算処理
	calc(mode,lnum,cnum){
			switch(mode){
				case "add": lnum = cnum + lnum
				break;
				case "sub": lnum = lnum - cnum
				break;
				case "mul": lnum = lnum * cnum
				break;
				case "div": lnum = lnum / cnum
				break;
			}
			return lnum
	}

	render(){
		return (
			<>
			{
				this.state.vals.map((val,key)=>{
					return (
						<div key={key}>
						{
							val.map((v,i)=>{
								return(<Setkey key={i} value={v[1]} getChar={()=>{this.getChar(v[0],v[1])} }/>)
							})
						}
						</div>
					)
				})
			}
			<div>
				<p>打ち込んだ文字:{this.state.str}</p>
				<p>合計:{this.state.sum}</p>
			</div>
			</>
		)
  }
}
```

```Setkey.js
import React from "react";

export default class Setkey extends React.Component {
  render() {
    return (
      <button className="square" onClick={()=>{this.props.getChar()}}>
        {this.props.value}
      </button>
    );
  }
}
```


これで最低限の電卓として機能していますが、ここからが本題です。クリックイベントとそれによるgetCharメソッドは子コンポーネントで生成されたJSX内に存在しているのですが、だからといって前述したように子コンポーネントに処理用メソッドを作って計算したとしても、親コンポーネントに値を受け渡せません。そこで、親コンポーネントに子コンポーネントと同じ関数を橋渡ししておき、子コンポーネントからイベントが実行されても、**親コンポーネントがそのイベントを受けたように見せかけ、それを同期させる**ことで実現できます。

なので、まずはテンプレートの生成部分をこのように記述する必要があります。

```js
    <Setkey key={i} value={v[1]} getChar={()=>{this.getChar(v[0],v[1])} }/>
```

ここで注意しなければいけないのは、厳密には式左側のgetCharとメソッドのthis.getCharは別物で、あくまで式の左側は、子コンポーネントに橋渡ししている、同期用のメソッド（コールバック関数）になります。

なので、書き分けてみるとこのようになり、海外サイトではhogeStateをコールバック関数に、hogeをメソッドとしていることが多く見られます。

```Calc.js
return(<Setkey key={i} value={v[1]} getCharstate={()=>{this.getChar(v[0],v[1])} }/>)
```

```Setkey.js
export default class Setkey extends React.Component {
  render() {
    return (
      <button className="square" onClick={()=>{this.props.getCharState()}}>
        {this.props.value}
      </button>
    );
  }
}
```

# ２：子コンポーネントからイベントと値を受け渡す
子コンポーネントからイベントと値を受け渡す場合は、先程使用しなかった子コンポーネント内のコールバック関数に引数を代入し、それを親コンポーネント内、無名関数の引数に設定、その値をそのままメソッドの引数に代入することで実現できます。

先程の電卓のキー配置部分も子コンポーネントに移動し、キーの値も子コンポーネントから受け渡すように改造してみました。

```Calc2.js
import React from "react";
import Setkey from "./Setkey2"

export default class Calc extends React.Component {
	constructor(){
		super()
		this.state = {
			lnum: null, //今までの値
			cnum: 0, //追加の値
			sum: 0, //合計値
			str: '', //打ち込んだ文字
			sign: '', //計算記号
		}
		this.getCharstate = this.getChar.bind(this) //メソッドのバインド
	}
	
	getChar(chr,str){
		let lnum = this.state.lnum　//被序数
		let cnum = this.state.cnum //序数
		let sum = this.state.sum //合計
		let sign = this.state.sign
		str = this.state.str + str
		if(chr.match(/[0-9]/g)!== null){
			let num = parseInt(chr)
			cnum = cnum * 10 + num //数値が打ち込まれるごとに桁をずらしていく
		}else if(chr.match(/(c|eq)/g) == null){
			if(lnum != null){
				lnum = this.calc(sign,lnum,cnum)
			}else{
				if(chr == "sub"){
					lnum = 0
				}
				lnum = cnum
			}
			sign = chr
			cnum = 0
		}else if( chr == "eq"){
			lnum = this.calc(sign,lnum,cnum)
			sum = lnum
		}else{
			lnum = null
			cnum = 0
			sum = 0
			str = ''
		}
		this.setState({lnum: lnum})
		this.setState({cnum: cnum})
		this.setState({sum: sum})
		this.setState({str: str})
		this.setState({sign: sign})
	}
	
	//計算処理
	calc(mode,lnum,cnum){
			switch(mode){
				case "add": lnum = cnum + lnum
				break;
				case "sub": lnum = lnum - cnum
				break;
				case "mul": lnum = lnum * cnum
				break;
				case "div": lnum = lnum / cnum
				break;
			}
			return lnum
	}

	render(){
		return (
			<>
			{
				<Setkey
					getCharstate = { (c,s)=>this.getChar(c,s)}
				/>
			}
			<div>
				<p>打ち込んだ文字:{this.state.str}</p>
				<p>合計:{this.state.sum}</p>
					これまでの値{this.state.lnum}<br/>
					追加の値{this.state.cnum}<br/>
					打ち込んだ文字{this.state.str}<br/>
			</div>
			</>
		)
  }
}
```

```Setkey2.js
import React from "react";

export default class Setkey extends React.Component {
	constructor(props){
		super(props)
		this.state = {
      vals:[
        [['7','7'],['8','8'],['9','9'],['div','÷']],
        [['4','4'],['5','5'],['6','6'],['mul','×']],
        [['1','1'],['2','2'],['3','3'],['sub','-']],
        [['0','0'],['eq','='],['c','C'],['add','+']],
			]
		};
	}
	
  render() {
    return (
			<>
			{this.state.vals.map((val,key)=>{
					return (
						<div key={key}>
						{
							val.map((v,i)=>{
								return(
								<React.Fragment key={i}>
								<button className="square" onClick={()=>{this.props.getCharstate(v[0],v[1])}}>
									{v[1]}
								</button>
								</React.Fragment>
								)
							})
						}
						</div>
					)
				})
			}
			</>
    )
  }
}
```

ポイントは以下の部分です。この引数cとsは子コンポーネントのイベントの引数に入っていたもので、それを直接プッシュキーを操作するgetCharメソッドの引数に渡しています。

```Calc2.js
    <Setkey
        getCharstate = { (c,s)=>this.getChar(c,s)}
    />
```

# ３：子コンポーネントから値のみを受け渡す
子コンポーネントからロード時に値を受け渡すにはライフサイクルメソッドの一つ、**componentDidMount**メソッドを利用します。先程のシステムからタイトルを子コンポーネントから取得するように改造してみました。

注意点は親コンポーネントにも受け皿となる変数を定義しておく必要があります。また、何も処理を施さず親コンポーネントで値を表示させるだけなら、コールバック関数をsetStateメソッドで受け取ることもできます。

```Calc3.js
import React from "react";
import Setkey from "./Setkey3"

export default class Calc extends React.Component {
	constructor(){
		super()
		this.state = {
			lnum: null, //今までの値
			cnum: 0, //追加の値
			sum: 0, //合計値
			str: '', //打ち込んだ文字
			sign: '', //計算記号
			title: '', //タイトル格納用
		}
		this.getCharstate = this.getChar.bind(this) //メソッドのバインド
	}
	
　　/*中略*/


	render(){
		return (
			<>	
			<h4>{this.state.title}</h4>
			{
				<Setkey
					setTitle = {(t)=>this.setState({title:t})}
					getCharstate = { (c,s)=>this.getChar(c,s)}
				/>
			}
			<div>
				<p>打ち込んだ文字:{this.state.str}</p>
				<p>合計:{this.state.sum}</p>
			</div>
			</>
		)
  }
}
```

```Setkey3.js
import React from "react";

export default class Setkey extends React.Component {
	constructor(props){
		super(props)
		this.state = {
      vals:[
        [['7','7'],['8','8'],['9','9'],['div','÷']],
        [['4','4'],['5','5'],['6','6'],['mul','×']],
        [['1','1'],['2','2'],['3','3'],['sub','-']],
        [['0','0'],['eq','='],['c','C'],['add','+']],
			]
		};
	}
    //このメソッドがライフサイクルメソッドで、ロード時に実行される
	componentDidMount(){
		const title = "クラスコンポーネントで電卓演習"
		this.props.setTitleState(title) //同期用メソッド
	}
	
  render() {
    return (
			<>
			{this.state.vals.map((val,key)=>{
					return (
						<div key={key}>
						{
							val.map((v,i)=>{
								return(
								<React.Fragment key={i}>
								<button className="square" onClick={()=>{this.props.getCharstate(v[0],v[1])}}>
									{v[1]}
								</button>
								</React.Fragment>
								)
							})
						}
						</div>
					)
				})
			}
			</>
    )
  }
}
```



# 関数コンポーネントの場合
関数コンポーネントの場合は、新しい記事で独立させました。

- [【React】子コンポーネントから親コンポーネントへの値受け渡し…まとめ（関数コンポーネントの場合）](https://qiita.com/BRSF/items/a29ee3544ad3d1b900d1)