LoginSignup
4
0

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-06-12
[前回] Django+Reactで学ぶプログラミング基礎(19): Reactチュートリアル(準備とReact概要)

はじめに

前回は、ReactコンポーネントとJSX構文を勉強しました。
今回も、Reactチュートリアルが続きます。

今回の内容

  • スターターコードを作成
  • データを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

  • スターターコードに、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

  • VS Codeコマンドプロンプトで、Webサーバーを起動(起動しなかった場合)
    • 起動成功するまで待つ
C:\kanban\my-app>npm start

image.png

  • ブラウザで、URLhttp://localhost:3000/が開かれる
    image.png

データをProps経由で渡す

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

  • Reactでは、親から子へpropsを渡すことで

    • アプリ内で情報が流れていく
  • BoardrenderSquareメソッド内で、propsとしてvalue という名前の値をSquareに渡す

src/index.js
class Board extends React.Component {
  renderSquare(i) {
    return <Square value={i} />;
  }
}
  • Squarerenderメソッドで、渡された値を表示する
    • {/* TODO */}{this.props.value}に書き換える
class Square extends React.Component {
  render() {
    return (
      <button className="square">
        {this.props.value}
      </button>
    );
  }
}

image.png

  • ブラウザで、Reactアプリを確認
    • 描画されたそれぞれの正方形に番号が付与された
      image.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

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

  • 目的

    • タイプ量を減らす
    • thisの混乱しやすい挙動を回避
      • クラス内で定義されたメソッドをクラス内で実行するためには、呼び出しの際にthisをつける必要あり
      • イベントハンドラのコールバック関数が通常の関数の場合
        • thisundefinedとなる
        • 結果、エラーが起きる
      • イベントハンドラのコールバック関数がアロー関数の場合
        • アロー関数では、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>
    );
  }
}
  • Squarerenderメソッドを書き換え、クリックされた時にstate の現在値を表示するように
    • onClick={...}というイベントハンドラをonClick={() => this.setState({value: 'X'})}に書き換える
      • onClickハンドラ内でthis.setStateを呼び出すことで
        • <button>がクリックされたら常に再レンダリングするように、Reactに伝える
      • setStateをコンポーネント内で呼び出すと、Reactはその内部の子コンポーネントも自動的に更新
    • <button>タグ内のthis.props.valuethis.state.valueに置き換える
      • マス目をクリックしたら、データ更新される
        • このSquarethis.state.valueXになる
        • 盤面にXと表示される
    • 可読性のため、classNameonClickの両プロパティをそれぞれ独立した行に配置
    • 修正後、Squarerenderメソッドから返される<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

  • ブラウザで、マス目をクリック
    • Xが表示される
      image.png

おわりに

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

[次回] Django+Reactで学ぶプログラミング基礎(21): Reactアプリのデバッグ
4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0