読者です 読者をやめる 読者になる 読者になる

レガシーコード生産ガイド

私に教えられることなら

Omで外から状態を変化させる

問題

以下のようなOmを使ったコードを書くと、Uncaught Error: Assert failed: (transactable? cursor)というエラーが出る

(ns hoge.client
  (:require [om.core :as om :include-macros true]))

(def app-state {:log []})

(defn add-message [text]
    (om/transact! app-state :log #(cons text %)))

(add-message "yo!")

結論

om/transact!で変更(と通知?)を行えるのは、omのコンポーネントに渡してITransactプロトコルなどを実装した状態に限る。

無理やり外から状態を変化させるとしたら、以下のようにom/rootやコンポーネントに渡す関数に渡ってきた、加工済みの状態(root-widgetの引数data)か、それを操作する関数を外に出して、それを使う。

(def app-state {:log ()})
(def app-state-transact! (atom (fn [_ _])))

(defn root-widget [data owner]
  (reset! app-state-transact! (fn [tag fn] (om/transact! data tag fn)))
  (reify
    om/IRender
    (render [this]
      (om/build widget-logs data))))

(om/root root-widget app-state {:target app})

(defn add-message [text]
  (@app-state-transact! :log #(cons text %)))

調査

omのソースを見てみると、core.cljs内のsetup関数で加工してるっぽい。プライベートなので使えないし、いろいろ他のデータも必要みたいなのでrootでsetupされたものを使ったほうが無難。というより、om/IWillMountなどに状態変更のタイミングを集める設計にしたほうが良さそう。ドキュメント読み込まないとだ。

広告を非表示にする