Reactのチュートリアルの三目並べで少しRails使う #2

[前回](https://qiita.com/GodPhwng/items/0c3dd4599a83c3799451)の続きになります。

手順を履歴に保持します。

#履歴機能を作成

```erb:index.html.erb
<head>
  <!--<link rel="stylesheet" href="css/index.css" />-->
  <!--<script src="js/index.js"></script>-->
  <%= javascript_pack_tag 'tictactoes', 'data-turbolinks-track': 'reload' %>
</head>

<body id="tictactoes-body">
  <%= form_with(url: "/tictactoes/sqClick", method: "post") do |f| %>
    <div id="root">
      <div class="game">
        <div class="gmae-board">
          <div>
            <% @Squares.each.with_index do |sq, i| %>
              <% if i % 3 == 0%>
                <div class="board-row"></div>
              <% end %>
              <%= f.text_field '', :name => "item[]", :class => "square", :readonly => true , :value => sq.content%>              
              
            <% end %>
          </div>
        </div>
        <div class="game-info">
          <div><%= @nextStepMessage %></div>
          <div>
            <li><button type="button">Go to game start</button></li>
              <% (0...@buttonCount).each do |num| %>
                <li><button type="button">Go to move #<%= num + 1 %></button></li> 
              <% end %>
          </div>
        </div>
      </div>
    </div>
    <%= f.hidden_field :clickButtonIndex, :value => @clickButtonIndex %>
    <%= f.hidden_field :clickNo, :value => @clickNo %>    
    <%= f.hidden_field :stepNumber, :value => @stepNumber %>
  <% end %>
</body>
```
履歴ボタンのところはサーバーサイドでボタンの数を埋め込む形としました。
実装中に`@buttonCount`を定義し忘れることがあって、そのまま動かすとブラウザが固まって、CPUとメモリが上昇し続けるという状態になり、めっちゃハマりましたorz
PCの寿命かとも思いましたw

`0...@変数`みたいな使い方したらダメなんでしょうね多分
※SCSSは前回と同様です。

```javascript:tictactoes.js
(function() {
  window.addEventListener("DOMContentLoaded", () => {
    // 全square
    document.querySelectorAll(".square").forEach((element, index) => {
      // クリックイベント
      element.addEventListener("click", squareClick.bind(element, index));
    });

    // 全button
    document
      .querySelectorAll(".game-info li > button")
      .forEach((element, index) => {
        // クリックイベント
        element.addEventListener("click", historyButtonClick.bind(null, index));
      });

    let isClick = false;
    function squareClick(index) {
      // 2度押し防止、既に値が埋まっているか
      if (isClick || this.value) {
        return;
      } else {
        isClick = true;
      }

      document.getElementById("clickNo").value = index;
      document.forms[0].submit();
    }

    function historyButtonClick(index) {
      // 2度押し防止
      if (isClick) {
        return;
      } else {
        isClick = true;
      }

      document.getElementById("clickButtonIndex").value = index;
      document.forms[0].action = "/tictactoes/hysBtClick";
      document.forms[0].submit();
    }
  });
})();
```
js側はなんだかんだ結構記述しました・・・。

　

```ruby:routes.rb
Rails.application.routes.draw do
  get '/' => 'home#top'
  get 'posts/index' => 'posts#index'
  get 'top' => 'home#top'
  get 'about' => 'home#about'
  get 'tictactoes' => 'tictactoes#index'
  get 'tictactoes/sqClick' => 'tictactoes#index'
  get 'tictactoes/hysBtClick' => 'tictactoes#index'
  post 'tictactoes/sqClick' => 'tictactoes#sqClick'
  post 'tictactoes/hysBtClick' => 'tictactoes#hysBtClick'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
```
現状POSTした後のページでF5を押すと、CSRFチェック?に引っ掛かり、エラーになっていたのでルーティングを追加しました。
普通こんなやり方しないのでしょうか。
　


```ruby:tictactoes_controller.rb
class TictactoesController < ApplicationController
    #初期画面表示
    def index
        
        # 手順1以降を削除して初期化
        Square.where.not(stepNumber: 0).delete_all

        #手順0を全件取得
        @Squares = Square.all

        #手順の開始は0から
        @stepNumber = 0
        @clickButtonIndex = 0
        @buttonCount = 0

        #次の手番の文字列
        @nextStepMessage = "次の手番: X"
    end
    
    #マス目クリック
    def sqClick

        #現在の手順インクリメント
        stepNumber = params[:stepNumber].to_i
        @stepNumber = stepNumber.succ

        #マス目の配列
        items = params[:item]                
        sq = Square.new

        #既に勝敗が着いた      
        if sq.calculateWinner(items)
            return
        else 
            xIsNext = stepNumber % 2 == 0

            clickButtonIndex = params[:clickButtonIndex]

            #XとOを設定
            items[params[:clickNo].to_i] = xIsNext ? "X": "O"            
            isWinner = sq.calculateWinner(items)

            #現在より後ろの手順は削除
            Square.where("stepNumber > ?", clickButtonIndex).delete_all

            #新しい手順をDBに保存          
            sq.saveSquare(@stepNumber, items)

            #次の手番の文字列更新    
            if isWinner
                @nextStepMessage = "勝者：" + (xIsNext ? "X": "O")    
            else
                @nextStepMessage = xIsNext ? "次の手番: O": "次の手番: X"
            end
                
            #最新の手順を取り直す
            @Squares = Square.where(stepNumber: @stepNumber)            
        end

        @buttonCount = @stepNumber
        
        #indexのhtmlを使いまわす
        render action: :index
    end

    #履歴ボタン押下
    def hysBtClick
        @clickButtonIndex = params[:clickButtonIndex].to_i
        @Squares = Square.where(stepNumber: @clickButtonIndex)

        #contentの配列を作る
        items = @Squares.map { |s| s.content }

        sq = Square.new        
        if sq.calculateWinner(items)
            @nextStepMessage = "勝者：" + (@clickButtonIndex % 2 == 0 ? "O": "X")    
        else
            @nextStepMessage = @clickButtonIndex % 2 == 0 ? "次の手番: X": "次の手番: O"
        end        

        @stepNumber = @clickButtonIndex
        @buttonCount = Square.count / 9 - 1

        render action: :index
    end
end
```
jsでやっていた時のように`xIsNext`変数は削除し、`stepNumber`の余りで次の手順を判断するようにしました。

```ruby:Square.rb
class Square < ApplicationRecord

    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]
      ]

    def saveSquare(stepNumber, items)                
        #新しい手順のマス目保存処理
        (1..9).each do |i|
            Square.create(stepNumber: stepNumber, squareNumber: i, content: items[i - 1])
        end     
    end

    #勝敗判定関数(公式チュートリアルから拝借)
    def calculateWinner(items)

        Lines.each do |l|
            a, b, c = l

            if items[a] != "" && items[a] == items[b] && items[a] == items[c]
                return true
            end
        end

        return false
    end
end
```
モデルはほぼ変更はないです。
ネットで調べると、今の時代`for`はあまり使わないとのことなので`each do`にしました。
~~なぜ使わないのかをちゃんと理解しないとな・・・~~

結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/2413ca8f-ae35-968f-0d1a-8cfd784f5b4c.png)

#感想
formをsubmitしているんですんごいもっさりします。
いずれajaxで組んでみようと思います。
その場合はクライアントには`Vue.js`が使えたらいいなぁと思います。

Railsのお作法や知識が浅いだけかもしれませんが、今のところあまりRailsのメリットが感じられませんでした。
独特な記法すぎて、他のプログラミング言語と類似点が見つけにくく、習得しにくいし、習得しても他のプログラミングを覚えるときに役に立ちにくいと感じました。

これからも触っていっていつか本感想を振り返ってみたいと思います。