Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

はじめに

この記事の続き。

公式チュートリアルの「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を使ってみる。

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした