Reactチュートリアル『まるばつゲーム(三目並べ)』をcreate-react-appを土台にしてやってみる

React

Reactチュートリアルを、create react appで生成した上に乗せていくならどんな感じになるか試してみました(チュートリアルでは生成したあとにsrc配下をまるっと削除してしまうので)。React初心者なので間違いがあるかもしれませんので、どうか自己責任で。

成果物

こんなものが出来ます。

Reactチュートリアル

Tutorial: Tic-Tac-Toe – React
The library for web and native user interfaces

ここです。ちょくちょく制作状況・現状を共有してくれるのですごく分かりやすいチュートリアルになっています。

こんな感じでちょこちょこ、今の状態のコードを見てみるみたいなリンクが貼ってあって、そこでコードと実行結果が見れます。

 

また、最後に追加課題もあるのですごく勉強になりました。

create-react-app準備

npm install -g create-react-app
create-react-app react-test
cd react-test/

上は自分で環境構築する場合。なお、チュートリアル「Setup Option 1: Write Code in the Browser」にも書いてありますが、環境構築で困ったらなりふり構わずブラウザでのチュートリアルをオススメします。

ソース修正

チュートリアルの詳細は、頑張って英語を読んで下さい…

英語力中学生並の私でもなんとなく読めました。チュートリアルの作り方がとても親切です!長いから端折るといきなりわからなくなりがちなので、シッカリ上から読むといいです。

Tutorial: Tic-Tac-Toe – React
The library for web and native user interfaces

 

せっかくcreate-react-appを活かす形にしたいので、まずはGame用のCSSを設定しました。

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; }

 

あとは本体のApp.jsをいじります。ほとんど記述はチュートリアルママですが、react-create-appに合わせて、React.ComponentComponentに書き換えています。

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import './Game.css';

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

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] &amp;&amp; squares[a] === squares[b] &amp;&amp; squares[a] === squares) {
      return squares[a];
    }
  }
  return null;
}

class Board extends 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 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>



    );
  }
}


class App extends Component {
  render() {
    return (
      


<div className="App">
        


<header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          


<h1 className="App-title">Welcome to React</h1>



        </header>



        <Game />
      </div>



    );
  }
}

export default App;

続きの応用課題について

続きの応用課題もあります。雑ですがやってみたメモをこちらに残しています。宜しければご参考に。

Reactチュートリアル『まるばつゲーム(三目並べ)』の追加課題をやってみる
Reactチュートリアル『まるばつゲーム』には追加課題が6問あるので勉強がてらやってみた。内容はかなり雑な気がするので、...

コメント