LoginSignup
0
0

More than 5 years have passed since last update.

ClojureScriptでReactチュートリアルやってみた (3)

Posted at

はじめに

この記事の続き。

公式チュートリアルの「Wrapping Up」の章(最後)まで進めてみました。historyに保存した盤面を再生できるようになりました。

全ソースはこちら

1. 実装

(defn square [{:keys [on-click value]}]
  [:button.square
    {:on-click #(on-click)}
    value])

(defn board [& {:keys [squares on-click]}]
  (letfn
    [(render-square [i]
       [square {
         :value (squares i)
         :on-click #(on-click i)
       }])]
        [:div
          [:div.board-row
            (render-square 0)
            (render-square 1)
            (render-square 2)]
          [:div.board-row
            (render-square 3)
            (render-square 4)
            (render-square 5)]
          [:div.board-row
            (render-square 6)
            (render-square 7)
            (render-square 8)]]))

(defn game []
  (let [state (r/atom {:history (vec [{:squares (vec (repeat 9 ""))}])
                       :step-number 0
                       :x-is-next? true})]
    (fn []
      (letfn
        [(handle-click [i]
          (let [history (vec (take (inc (get @state :step-number)) (get @state :history)))
                current (last history)
                squares (get current :squares)
                x-is-next? (get @state :x-is-next?)]
            (when (and (= (calculate-winner squares) nil) (= (squares i) ""))
                  (swap! state
                         assoc :history
                         (conj history
                               (assoc-in current [:squares i] (if x-is-next? "X" "O"))))
                  (swap! state assoc :step-number (count history))
                  (swap! state assoc :x-is-next? (not x-is-next?)))))
          (jump-to [step]
            (swap! state assoc :step-number step)
            (swap! state assoc :x-is-next? (if (= (mod step 2) 0) true false)))]
            (let [history (get @state :history)
                  current (get history (get @state :step-number))
                  squares (get current :squares)
                  winner (calculate-winner squares)
                  moves (map-indexed (fn [move _]
                                       (let [desc (if (= move 0)
                                                      (str "Go to game start")
                                                      (str "Go to move #" move))]
                                         [:li {:key move} [:button {:on-click #(jump-to move)} desc]]))
                                     history)
                  status (if (= winner nil)
                              (str "Next player: " (if (get @state :x-is-next?) "X" "O"))
                              (str "Winner: " winner))]
                    [:div.game
                      [:div.game-board
                        [board :squares squares
                               :on-click handle-click]
                        ]
                    [:div.game-info
                      [:div status]
                    [:ol
                      moves
                    ]]])))))

2. ClojureScriptコーディングあれこれ

2.1. take, inc

(vec (take (inc (get @state :step-number)) (get @state :history))

Vanilla JSではsliceで配列の部分取得ができますが、cljsではtakeで先頭の要素を個数分取得できます。また、incで1インクリメントできます。

2.2. map-indexed

moves (map-indexed (fn [move _]
                     (let [desc (if (= move 0)
                                (str "Go to game start")
                                (str "Go to move #" move))]
                       [:li {:key move} [:button {:on-click #(jump-to move)} desc]]))
                    history)

cljsでmapのindexを取得するためには、map-indexedを使用します。第1引数に設定した即時関数の第1引数がindex, 第2引数がitemです。この処理でitemは使用しないため、_で無効化しています。

2.3. count

(count history)

countでcollectionの要素数を取得しています。

3. まとめ

なんだかReactというよりはVanilla JSとcljsの仕様差異を吸収するほうが大変でした。
書いてみて以下のことを感じました。

  • 括弧だらけに見えたけど書く分にはあまり気にならない。
  • 配列操作の豊富な関数群がスマートな書き方を実現できる。ある程度どういうラインナップがあるか事前に勉強要。
  • 名前空間、letスコープなどにより、汚染の少ない書き方が可能。
  • HicCup記法はタグの閉じ忘れが起きにくいのでJSX記法より書きやすいかもしれない。
  • 書き方がある程度制約されている事によって、誰が書いても同じになる→誰のコードでも読みやすいような仕組みになっている。
  • Collectionの種類を理解せず雰囲気でやってしまっているのでしっかりと把握要。

次は
- Clojure自体の基礎知識を本で補強する。
- Reduxに相当するre-frameを使ってみる。

あたりをやってみましょうかね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0