         (.removeAttribute element attr-name)))))
 
 (defn- bind-lifecycle-callback! [node node-state cb-name cb-value]
-  (if (identical? cb-name "disposed")
+  (when (identical? cb-name "disposed")
     (set! (.-disposed-callback node-state) cb-value)
-    (aset node-state (str "node-" cb-name) cb-value)))
+    ;; other callbacks automatically included in attr map
+    ))
 
 (def ^:private attr-ns-lookup
   #js
-  {:node bind-lifecycle-callback!})
+  {:node bind-lifecycle-callback!
+   :state (fn []) ;; automatically handled in attr map
+   })
 
 (defn register-attr-prefix! [prefix xml-ns-or-handler]
   (aset attr-ns-lookup prefix xml-ns-or-handler))

(defn get-data-state [element]
  (.getAttribute element "data-state"))

(defn- enter-data-state! [element state old-state]
  (when-let [enter-transition (get-transition element (keyword (str "on-" state)))]
    (enter-transition element old-state)))

(defn set-data-state!
  ([element state]
    (let [cur-state (get-data-state element)
          state (when state (name state))]
      (when-not (identical? cur-state state)
        (do-set-data-state! element state)
        (enter-data-state! element state cur-state)))))

(defn- bind-prop-attr! [set-fn element attr-name attr-value node-state]
  (if (satisfies? cljs.core/IDeref attr-value)

                                    (get-node-id new-elem) state)
        state))))

(defn- get-state-attr [state attr-str]
  (when-let [attrs (when state (.-attrs state))]
    (aget attrs attr-str)))

(defn- on-attached [state node]
  (when-let [node-attached (get-state-attr state "node/on-attached")]
    (node-attached node)))

(defn- replace-node-completely [parent new-elem-spec cur-dom-node top-level]
  (let [new-elem
