Django+Reactで学ぶプログラミング基礎(20): Reactチュートリアル(データ受渡しと状態記憶)

##### [前回] [Django+Reactで学ぶプログラミング基礎(19): Reactチュートリアル(準備とReact概要)](https://qiita.com/mingchun_zhao/items/cbcee727c4509b948d85)

## はじめに

前回は、ReactコンポーネントとJSX構文を勉強しました。
今回も、[Reactチュートリアル](https://ja.reactjs.org/tutorial/tutorial.html#inspecting-the-starter-code)が続きます。

## 今回の内容

- スターターコードを作成
- データを`Props`経由で渡す
- インタラクティブなコンポーネントを作る

## スターター(starter)コードを作成

- ゲームアプリのベースとなるスターターコードを作成
    - VS Codeで、`MY-APP`プロジェクトフォルダにある`src/index.js`を開く

- CSS(スタイルシート)を作成
```src/index.css
body {
  font: 14px "Century Gothic", Futura, sans-serif;
  margin: 20px;
}

ol, ul {
  padding-left: 30px;
}

.board-row:after {
  clear: both;
  content: "";
  display: table;
}

.status {
  margin-bottom: 10px;
}

.square {
  background: #fff;
  border: 1px solid #999;
  float: left;
  font-size: 24px;
  font-weight: bold;
  line-height: 34px;
  height: 34px;
  margin-right: -1px;
  margin-top: -1px;
  padding: 0;
  text-align: center;
  width: 34px;
}

.square:focus {
  outline: none;
}

.kbd-navigation .square:focus {
  background: #ddd;
}

.game {
  display: flex;
  flex-direction: row;
}

.game-info {
  margin-left: 20px;
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/ec2ccc86-2945-1565-12ba-7202a9a1bd06.png)

- スターターコードに、3つのReactコンポーネントを追加
    - Square(正方形のマス目)
        - 1つの`<button>`をレンダリング
    - Board(盤面)
        - 9個のマス目をレンダリング
    - Game
        - 盤面とプレースホルダー(後述)を描画
```src/index.js
class Square extends React.Component {
  render() {
    return (
      <button className="square">
        {/* TODO */}
      </button>
    );
  }
}

class Board extends React.Component {
  renderSquare(i) {
    return <Square />;
  }

  render() {
    const status = 'Next player: X';

    return (
      <div>
        <div className="status">{status}</div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}

class Game extends React.Component {
  render() {
    return (
      <div className="game">
        <div className="game-board">
          <Board />
        </div>
        <div className="game-info">
          <div>{/* status */}</div>
          <ol>{/* TODO */}</ol>
        </div>
      </div>
    );
  }
}

// ========================================

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Game />);
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/c77eac4f-c58c-1a69-bdec-17d7f1905c78.png)

- VS Codeコマンドプロンプトで、Webサーバーを起動(起動しなかった場合)
    - 起動成功するまで待つ
```
C:\kanban\my-app>npm start
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/e2636324-748f-ae1d-d4da-7d36991a6f5d.png)

- ブラウザで、URL`http://localhost:3000/`が開かれる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/27b06108-6172-2c47-8e49-9a3663f9a7b1.png)

## データを`Props`経由で渡す

### 親である`Board`コンポーネントから、子である`Square`コンポーネントに`props`を渡す

- Reactでは、親から子へ`props`を渡すことで
    - アプリ内で情報が流れていく

- `Board`の`renderSquare`メソッド内で、`props`として`value` という名前の値を`Square`に渡す
```src/index.js
class Board extends React.Component {
  renderSquare(i) {
    return <Square value={i} />;
  }
}
```
    
- `Square`の`render`メソッドで、渡された値を表示する
    - `{/* TODO */}`を`{this.props.value}`に書き換える
```
class Square extends React.Component {
  render() {
    return (
      <button className="square">
        {this.props.value}
      </button>
    );
  }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/2c8409cf-d69f-21cc-7bf7-6589f6161fe5.png)

- ブラウザで、Reactアプリを確認
    - 描画されたそれぞれの正方形に番号が付与された
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/59e093e9-f31c-65ad-0831-9a4cca3d2a5a.png)

## インタラクティブなコンポーネントを作る

### マス目がクリックされた場合、`X`と表示

- まず、`Square`コンポーネントの`render()`関数から返される`button`タグを変更
```
class Square extends React.Component {
  render() {
    return (
      <button className="square" onClick={function() { console.log('click'); }}>
        {this.props.value}
      </button>
    );
  }
}
```

- ブラウザで、デベロッパーツールを開く(F12)
    - `Square`のマス目をクリックする
        - コンソールに`click`と表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/94c0f256-ad2c-96cb-9d7f-1b3a0709a5c8.png)

## アロー関数構文を用いて、イベントハンドラを記述

- 目的
    - タイプ量を減らす
    - `this`の混乱しやすい挙動を回避
        - クラス内で定義されたメソッドをクラス内で実行するためには、呼び出しの際に`this`をつける必要あり
        - イベントハンドラのコールバック関数が通常の関数の場合
            - `this`は`undefined`となる
            - 結果、エラーが起きる
        - イベントハンドラのコールバック関数がアロー関数の場合
            - アロー関数では、`this`が束縛されず
            - 正しく、コールバック関数外のスコープである、クラスコンポーネントのインスタンスを指してくれる

- ※ 注意
    - `onClick={() => console.log('click')}`の記載で、`onClick`プロパティに渡されるのは関数
        - Reactはクリックされるまでこの関数を実行しない
        - よくある間違い: `onClick={console.log('click')}`
```src/index.js
class Square extends React.Component {
 render() {
   return (
     <button className="square" onClick={() => console.log('click')}>
       {this.props.value}
     </button>
   );
 }
}
```

### `state`を用いて、コンポーネントの状態を記憶

- `Square`コンポーネントがクリックされたら
    - `state`を使用し、自分がクリックされたことを`覚えさせる`
    - その後、`X`マークでマス目を埋める

- Reactコンポーネントは、コンストラクタで`this.state`を設定することで、状態を持つことができる
    - `this.state`は、定義されているコンポーネント内でプライベートと見なされる
    - 現在の`Square`の状態を`this.state`に保存
        - マス目がクリックされた時に、`this.state`が変更される

#### コードを修正

- まず、クラスにコンストラクタを追加し、`state`を初期化
    - `constructor`を持つReactのクラスコンポーネントで、コンストラクタは`super(props)`の呼び出しから始める
        - JavaScriptのクラスで、サブクラスのコンストラクタを定義時、常に`super`関数を呼び出す
```src/index.js
class Square extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: null,
    };
  }

  render() {
    return (
      <button className="square" onClick={() => console.log('click')}>
        {this.props.value}
      </button>
    );
  }
}
```

- `Square`の`render`メソッドを書き換え、クリックされた時に`state` の現在値を表示するように
    - `onClick={...}`というイベントハンドラを`onClick={() => this.setState({value: 'X'})}`に書き換える
        - `onClick`ハンドラ内で`this.setState`を呼び出すことで
            - `<button>`がクリックされたら常に再レンダリングするように、Reactに伝える
        - `setState`をコンポーネント内で呼び出すと、Reactはその内部の子コンポーネントも自動的に更新
    - `<button>`タグ内の`this.props.value`を`this.state.value`に置き換える
        - マス目をクリックしたら、データ更新される
            - この`Square`の`this.state.value`は`X`になる
            - 盤面に`X`と表示される
    - 可読性のため、`className`と`onClick`の両プロパティをそれぞれ独立した行に配置
    - 修正後、`Square`の`render`メソッドから返される`<button>`タグのコード
        - 修正ファイルの保存を忘れず(Ctrl+S)
```src/index.js
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>
    );
  }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/c17e6bec-3482-cb67-8417-67ec65d029e8.png)

- ブラウザで、マス目をクリック
    - `X`が表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/404135bd-ceaa-52d1-267e-477ff6270785.png)

## おわりに

Reactコンポーネント間のデータ受渡しと、
コンポーネント状態の保持方法を勉強しました。
次回も続きます。お楽しみに。

##### [次回] [Django+Reactで学ぶプログラミング基礎(21): Reactアプリのデバッグ](https://qiita.com/mingchun_zhao/items/4c690fc5071ddb2b9da1)