omcljs / om

ClojureScript interface to Facebook's React
6.65k stars 364 forks source link

Multimethods and IWillUnmount #178

Closed kahlin closed 10 years ago

kahlin commented 10 years ago

I modified the unmount example to use multimethods:

(ns examples.unmount.core
  (:require [om.core :as om :include-macros true]
            [om.dom :as dom :include-macros true]))

(enable-console-print!)

(defmulti widget (fn [data owner] (:widget data)))

(defmethod widget :a [data owner]
  (reify
    om/IWillUnmount
    (will-unmount [_]
      (println "umounting Widget A"))
    om/IRender
    (render [_]
      (dom/div nil "Widget A"))))

(defmethod widget :b [data owner]
  (reify
    om/IWillUnmount
    (will-unmount [_]
      (println "umounting Widget B"))
    om/IRender
    (render [_]
      (dom/div nil "Widget B"))))

(defn app [data owner]
  (reify
    om/IRender
    (render [_]
      (dom/div nil
               (om/build widget data)
        (dom/button
          #js {:onClick (fn [e] (om/transact! data :widget {:a :b :b :a}))}
          "Switch!")))))

(om/root app {:widget :a}
  {:target (.getElementById js/document "app")})

Now, IWillUnmount is never called. That seems to go for IWillMount as well, except IWillMount is called once when the page is loaded and widget A is rendered and never after that.

danielytics commented 10 years ago

I recently hit the opposite scenario: where components were being mounted/unmounted when I wasn't expecting them to. What I learned is that build checks if the component function has changed to determine if the component needs to be mounted or not. Basically it does something like is if (= cached-component new-component) then simply render, otherwise unmount cached-component, mount new-component and render new-component.

Because you are using a multimethod, even though the widget changes, the function remains the same, so Om thinks its still the same component and does not unmount it. This is because the multimethod dispatch function actually does remain the same - its just where it dispatches to that changes.

swannodette commented 10 years ago

This is a known limitation, a component is identified by its function. If you are going to use multimethods you need to return the top level function that will actually construct the component.

danielytics commented 10 years ago

I will amend the documentation for build to make note of this.

kahlin commented 10 years ago

I figured it had something to do with the function not changing but wasn't sure how to get around it. Thanks!