React公式チュートリアルを図解

React.js(React)の公式サイトにあるチュートリアル(三目並べゲーム, tic-tac-toe)を図解してみました。チュートリアルの理解の助けになれば幸いです。

React公式サイト チュートリアルへのリンク

React公式チュートリアルの図解

チュートリアルのファイル関係は下図のようになっています。トップページのindex.htmlにあるdiv要素(id=”root”)に対してindex.jsに定義したコンポーネントをレンダリングする仕組みになっています。

Reactチュートリアル説明図1

Reactチュートリアル ファイル関係図

最終的に出来上がるindex.jsに定義したコンポーネント間の関係は下図のようになっています。図は1マスだけクリックした後の状態を図示しています。最終コードでは最上位に位置するGameコンポーネントにstateとアクションメソッドを持たせています。子コンポーネントではprops経由でstate情報とアクションメソッドのトリガーを受け取る構造になっています。

Reactチュートリアル_コンポーネント関係図

Reactチュートリアル コンポーネント関係図

チュートリアル最終コード

図解は最終的に出来上がる下記のコードを基に作っています。

index.html

<!DOCTYPE html>
<html lang=”en”>
    <head>
        (省略)
    </head>
    <body>
        <div id=”root”></div>
        (省略)
    </body>
</html>

 

index.js

import React from ‘react’;
import ReactDOM from ‘react-dom’;
import ‘./index.css’;

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

class Board extends React.Component {
    renderSquare(i) {
       return (
           <Square
               value={this.props.squares[i]}
               onClick={() => this.props.onClick(i)}
           />
       );
    }

    render() {
       return (
          <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 {
    constructor(props) {
       super(props);
       this.state = {
          history: [
             {
                squares: Array(9).fill(null)
             }
          ],
          stepNumber: 0,
          xIsNext: true
       };
    }

    handleClick(i) {
       const history = this.state.history.slice(0, this.state.stepNumber + 1);
       const current = history[history.length – 1];
       const squares = current.squares.slice();
       if (calculateWinner(squares) || squares[i]) {
          return;
       }
       squares[i] = this.state.xIsNext ? “X” : “O”;
       this.setState({
          history: history.concat([
             {
                squares: squares
             }
          ]),
          stepNumber: history.length,
          xIsNext: !this.state.xIsNext
       });
    }

    jumpTo(step) {
       this.setState({
          stepNumber: step,
          xIsNext: (step % 2) === 0
       });
    }

    render() {
       const history = this.state.history;
       const current = history[this.state.stepNumber];
       const winner = calculateWinner(current.squares);

       const moves = history.map((step, move) => {
          const desc = move ?
             ‘Go to move #’ + move :
             ‘Go to game start’;
          return (
             <li key={move}>
                <button onClick={() => this.jumpTo(move)}>{desc}</button>
             </li>
          );
       });

       let status;
       if (winner) {
          status = “Winner: ” + winner;
       } else {
          status = “Next player: ” + (this.state.xIsNext ? “X” : “O”);
       }

       return (
          <div className=”game”>
             <div className=”game-board”>
                <Board
                   squares={current.squares}
                   onClick={i => this.handleClick(i)}
                />
             </div>
             <div className=”game-info”>
                <div>{status}</div>
                <ol>{moves}</ol>
             </div>
          </div>
       );
    }
}

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

ReactDOM.render(<Game />, document.getElementById(“root”));

function calculateWinner(squares) {
    const lines = [
       [0, 1, 2],
       [3, 4, 5],
       [6, 7, 8],
       [0, 3, 6],
       [1, 4, 7],
       [2, 5, 8],
       [0, 4, 8],
       [2, 4, 6]
    ];
    for (let i = 0; i < lines.length; i++) {
       const [a, b, c] = lines[i];
       if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
          return squares[a];
       }
    }
    return null;
}

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です