【React】 公式チュートリアル三目並べをやってみた 要点まとめ

チュートリアル:React の導入 – React
ユーザインターフェース構築のための JavaScript ライブラリ

こちらのReact公式チュートリアルをやってみて、要点をまとめました!

データを渡す

BoardコンポーネントからSquareコンポーネントにデータを渡す。

propsとしてvalueという名前の値をSquareに渡すときの、渡す側、受け取り側のそれぞれの記述は以下のようになります。

渡す側

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

Squareコンポーネントにvalueを渡す。

受け取り側

Squareのrenderメソッドで、渡された値が表示されるよう{this.props.value}と指定

class Square extends React.Component {
  render() {
    return (
      <button className="square">
        {this.props.value}
      </button>
    );
  }
}

これで、BoardコンポーネントからSquareコンポーネントにpropsを渡すことができました。

クリックイベントを作成する

<button className="square" onClick={function() {console.log('click');}}>

これによりSquareをクリックすると、アラートclickが表示される。

これをアロー関数で書き換えると

<button className="square" onClick={() => {console.log('click');}}>

onClick={console.log(‘click’);}とすると何がいけないのか?

クリックされたときに関数を実行させたい。この記述だと、再レンダリングされる度によびだされてしまう。

state

stateを使ってコンポーネントが何かを覚えさせる。

コンストラクタでthis.stateとすることで、状態を持つことができる。

現在のsquareの状態をthis.stateに保存して、クリックされたときにそれを変更するようにする。

①そのためにまずはコンストラクタを追加

  constructor(props) {
    super(props);
    this.state = {
      value: null,
    };
  }

コンストラクタを定義する際は常にsuperを呼ぶ必要がある。

そのため、constructrでは全てsuper(props)の呼び出しから始まる。

②Squareのrenderメソッドを書き換えて、クリック時にstateの値が表示されるようにする

  • this.propsをthis.stateに書き換え
  • onClickイベントを変更
    alert表示から、Xという値の表示に変更する。
onClick={() => {alert('click');}}

   ↓

onClick={() => this.setState({value: 'X'})}

stateのリフトアップ

現時点では、それぞれのSquareコンポーネントが、ゲームの状態を保持している。

9個のマス目の値を一箇所で管理したい。

そのためには親のBoardコンポーネントで保持する。

このように複数の子要素からデータを集める場合は、親コンポーネント内で共有のstateを宣言し、親コンポーネントはpropsを使うことで子に情報を返す。

これを、stateを親コンポーネントにリフトアップするという。

①初期stateとして9個のマス全てにnullをセットする。

class Board extends React.Component {
  constructor(props);
    super(props);
    this.state = {
      squares: Array(9).fill(null),
    };
}

renderSquare(i) {
    return <Square value={i} />;
  }

②Boardコンストラクタ内で定義した配列squaresを読み込む

  renderSquare(i) {
    return <Square value={this.state.squares[i]} />;
  }

どのマスに何が入っているのかを管理しているのはBoardだが、

SquareがBoardのstateを更新できるようにしたい。

しかし、SquareからBoardのstateを直接書き換えることはできない。

そのため、BoardからSquareに関数を渡し、クリック時にSquareにその関数を読んでもらうようにする。

  renderSquare(i) {
    return (
      <Square
        value={this.state.squares[i]}
        onClick={() => this.handleClick(i)}
      />
    );
  }

③Squareに以下変更を加える。

  • this.stateをthis.propsに変更
  • this.setStateをthis.props.onClickに変更
  • ゲームの状態を管理していた以下のconstructorが不要のため削除
  constructor(props) {
    super(props);
    this.state = {
      value: null,
    };
  }

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

上記コードは何をしているか?

  • buttonへのonClickプロパティの設定により、イベントリスナを設定する
    イベントリスナ:イベント発生で動作するようにした関数のこと
  • ボタンクリックで、onClickのイベントハンドラがコールされる
    イベントハンドラ:特定のイベントに反応して特定の処理を行うもの
  • これがthis.props.onClick()をコールする

④handleClickを定義する

  handleClick(i) {
    const squares = this.state.squares.slice();
    squares[i] = 'X';
    this.setState({squaews: squares});
  }

これでどうなったか?

状態が個々の Square コンポーネントではなく Board コンポーネント内に保存されるようになった。

Board の state が変更になると、個々の Square コンポーネントも自動的に再レンダーされる。

Board が Square コンポーネントを全面的に制御している状態になった。

.slice()というメソッドを使ってsquare配列のコピーを作ることができる。

このように、変化するデータの扱いには2種類のアプローチがある。

  1. データの値を直接いじってミューテートする(ミュータブル)
  2. 新しいデータのコピーで古いデータを書き換える(イミュータブル)

ミューテートしないことによるメリット

  • 以前のヒストリを保って再利用することができることで、実装が楽になる
  • 中身が直接書き換えられ、変更があったかどうかの検出が困難といったことがなくなる
  • 変更があったかが簡単にわかることによりコンポーネントをいつ再レンダーすべきなのか決定しやすくなる

関数コンポーネントへの書き換え

関数コンポーネントとは、自分のstateを持たないコンポーネントをよりシンプルに書く方法

以下のclassによる書き方を書き換える

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

関数コンポーネント では以下のように書くことができる

function Square(props) {
  return (
    <button className="square" onClick={props.onClick}>
      {props.value}
    </button>
  );
}

クリックでXとOを切り替えられるようにする

目標物

  • どちらのプレーヤの手番なのかをxIsNextで決める
  • xIsNext: trueとすることで、先手を”X”にする
class Board extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      squares: Array(9).fill(null),
      xIsNext: true,
    };
  }

  handleClick(i) {
    const squares = this.state.squares.slice();
    squares[i] = this.state.xIsNext ? 'X' : 'O';
    this.setState({
      squares: squares,
      xIsNext: !this.state.xIsNext,
    });
  }

Next playerの表示も切り替わるようにする。

  render() {
    const status = 'Next player: ' +(this.state.xIsNext ? 'X' : 'O');

タイムトラベル機能の追加

やりたいこと

トップレベルの Game コンポーネント内で過去の着手の履歴を表示したい

方法

Game コンポーネントが history にアクセスできる必要があるので、history という state を Game コンポーネントに置く。

それにより、squares の state を、子である Board コンポーネントから取り除くことができる。

BoardにあるstateをGameコンポーネントにリフトアップすることにより Game コンポーネントは Board のデータを完全に制御でき、history 内の過去のデータを Board にレンダーさせることができる。

まとめると、、、

Game(親)

history(state)をここに設置。

Board(子)

historyがGameにあるので、squaresのstateが不要

と、ここで挫折。。。

チュートリアルを最後まで完遂出来ず、一旦他の方法で学習します。

コメント