※Reactを学習したい人は「Reactチュートリアル」で学習してみてください。このブログはこのチュートリアルで感じたことや自分のメモを記述していきます。
マス目をクリックするとコンポーネントSquareが実行される。その時にクリック情報を集中管理しているBoardへ知らせる必要がある。チュートリアルによるとコールバックの手法で対応するようだ。
コールバック
JavaScriptでは関数(メソッド)を変数に格納することができる。例えば次のようなコードだ。
1 2 3 4 |
var print = function() { console.log("実行"); } print(); |
これは変数「print」に「実行」と表示する関数を格納している。そして4行目で変数を利用し関数を実行している。
関数が格納された変数は通常の変数と同様に引数として他関数に受け渡すことができる。これを利用し呼び出された方から呼び出し元の関数を呼び出す手法がコールバックだ。
1 2 3 4 5 6 7 8 9 10 11 12 |
first(); function first(){ var print = function(x) { console.log("実行:"+x); } second(print); } function second(print){ print("second call"); } |
こちらのコードは関数first内で変数printに関数を格納している。そして、変数printを引数として関数secondを呼び出している。関数secondでは受け取った変数を利用し関数を実行ている。コンソールの表示結果は「実行:second call」だ。
ソース変更
コンポーネントBoardからSquareを呼び出す際に関数を受け渡すため次のように追加/変更する。
1 2 3 4 5 6 7 8 9 10 11 12 |
class Board extends React.Component { renderSquare(i) { return ( <Square value={this.state.squares[i]} onClick={() => this.handleClick(i)} /> ); } ・ ・ ・ |
Squareへ変数onClickを引き渡す。この変数には関数を格納している。関数の内容はBoardの関数(メソッド)handleClickを呼び出している。といっても関数handleClickはまだ記述されていないので、コンポーネントBoardに追加する。
1 2 3 4 5 6 7 8 9 10 11 12 |
class Board extends React.Component { ・ ・ ・ handleClick(i) { const squares = this.state.squares.slice(); squares[i] = 'X'; this.setState({squares: squares}); } ・ ・ ・ |
この関数がSquareから呼び出されることになる。内容はBoardで管理しているクリック情報を更新している。ポイントは直接該当配列を変更せずに、slice命令でクリック情報の配列をコピーし、コピーした内容を変更し、さらにコピーした内容でクリック情報を上書きしている。直接該当配列を変更した方がシンプルだが、Reactではこちらの方が都合が良いらしい。その理由をチュートリアルに細かく記述されているので、参照して欲しい。
コンポーネントSquareを次のように変更する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
class Square extends React.Component { constructor(props) { super(props); this.state = { value: null, }; } render() { return ( <button className="square" onClick={() => this.setState({value: 'X'})} > {this.state.value} </button> ); } } //次のように変更 class Square extends React.Component { render() { return ( <button className="square" onClick={() => this.props.onClick()} > {this.props.value} </button> ); } } |
Squareでクリック情報を保持する必要がなくなったので、コンストラクタでの変数初期化を削除する。そして、マス目がクリックされた際に変数onClickに格納されている関数を実行する。
stateの取り扱い
これでやっとゲームの終了判定ができるようになった。しかし他コンポーネントからstateのアクセスが簡単にできないことが、プログラムを複雑化させているように感じる。これを解決するために「Redux」というモノが存在するようだが、これは別のお話とする。