初心者
React

reactで碁盤アプリを作る

この記事はLivesense Advent Calendar 2017-学- 16日目です。

新卒1年目のエンジニアの @asadaman です。
最近reactを勉強し始めたので、それについて書こうと思います。
react tutorialはまるばつゲームだったので、それを少しだけ拡張して9x9の碁盤アプリを作ることにしました。
最初に断っておきますが、ひどいコードをを修正しながら完成に近づけていった過程を書いていきます。

やること

  • 9x9の碁盤
  • 黒石と白石を交互に打てる
  • create react app と react tutorial をベースに作る

あきらめたこと

  • 勝敗の判断
  • (相手の石を囲んだ場合に)石をとる
  • typescript(当初はtypescriptで作る予定でしたが型をどうやってつければいいかわからず断念)

やったこと

1. create react app でひな形をつくる

create-react-app の Quick Overview に書いている通りですが、以下のコマンドでひな形を作ります。

$ npm install -g create-react-app

$ create-react-app react-go-game

2. react tutorialのコードをそのまま持ってくる

まずは真似するところからということで、自分が作りたいものに一番近い状態の react tutorial のコード(まるばつ交互に打てるような実装が完了したところ)をそのまま持ってきました。

3. 9x9に拡張する

9x9の碁盤を作りたいので、該当箇所を変更します。

3x3で合計9個だったArrayは9x9で合計81個にします。
before

squares: Array(9).fill(null),

after

squares: Array(81).fill(null),

次に,renderの箇所を変更します。
before

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

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

after

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

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

81個並べるのはダルいです。(普通81個も書かないと思います。真似しないでください。)

あと、まるとばつから黒丸と白丸に変更しました。
before

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

after

const status = 'Next player: ' + (this.state.xIsNext ? '●' : '○');

4. コンポーネント化する

renderSquareというのが81行も並んだ残念なコードを見て悲しみにくれていると、同じチームの親切な先輩からアドバイスをもらいました。
先輩:「reactの基本的な概念はコンポーネントを作ってそれを組み立ててビューを作るんだよ」
(正確には忘れました)
チュートリアルにもそんなことが書いてあったなとようやく気がついた僕はちゃんと頭を使ってreactっぽいコードを書くことにしました。

四角を9個描画するrenderBoardRowを作成しその中でrenderSquareを呼ぶ、さらにそれを9行分描画するrenderBoard()を作成しその中でrenderBoardRowを呼ぶ。そうすると、あのひどく長かったrenderの箇所がスッキリしました。

  render() {
    return (
      <div className="App">
        <div className="App-header">
          <h2>Go App</h2>
        </div>
        <div className="background">
      {this.renderBoard()}
        </div>
      </div>
    );
  }

5. CSSを修正する

このままだと、9x9のマスに黒と白の丸を並べるオセロになってしまうので、画像をあてて碁盤と碁石っぽい見た目にします。

6. できたもの

なんとかそれっぽい感じになりました。
スクリーンショット 2017-12-18 22.53.05.png
最終的なjsのコードも貼っておきます。
https://github.com/asadaman/react-go-game/blob/master/src/App.js

今後やりたいこと

  • 今回あきらめたこと
  • 棋譜記録・再生

まずはtypescriptで型をつけるところから始めたいと思います。