That refactoring removes the need to have the multimethod implementaiton 
provide the react key:

[code]
(defmethod layout/render-right-component ::consultant
  [{:keys [owner data]}]
  (om/build
    c/filter-header-1
    {:id :consultants
     :main-component-cursor
         {:node         {:id -1 :text "Urgh :(" :children (om/observe owner 
(state/all-consultants))}
          :selected-ids (-> data :filter :consultants :selected-ids)
          :expanded-ids (-> data :filter :consultants :expanded-ids)
          :local-search (-> data :filter :consultants :local-search)}
     }
    {:opts {:main-component   tree/bound-tree
            :main-component-m {:opts {:change-fn
                                      #(search data true)
                                      }}}}))
[/code]

It is a bit more verbose (as the arguments for main-component have to be passed 
to and then re-stitched together by the common component, but that is fine.

Thanks all.

On Thursday, 4 December 2014 13:10:20 UTC, Lucas Bradstreet  wrote:
> That's what I had in mind. If your current implementation is similar to 
> what's suggested in many react examples, it may not be as bad as I thought. 
> It still feels a little nasty to me though.
> 
> Daniel's point about your om version is good too. I had forgotten about the 
> past issues with multimethods.
> 
> > On 4 Dec 2014, at 21:04, Colin Yates <[email protected]> wrote:
> > 
> >> On Thursday, 4 December 2014 12:53:27 UTC, Lucas Bradstreet  wrote:
> >> If I understand this correctly, you're actually passing in a om/build'ed 
> >> component as a part of a cursor to another component? I'm not sure whether 
> >> this is the cause but I think this is very likely bad form.
> > Yes, that is starting to become my intuition as well.
> > 
> > What would be the analog though of the multitude of React examples where 
> > they compose their components? AIUI those components will be passed through 
> > as props.children, hence I started out this way here.
> > 
> > A workaround as you say is you pass just the dependant's required cursor 
> > and the dependant builder's var via opts, something like:
> > 
> > [code]
> > (defn build-child [cursor owner]...)
> > (defn wrap-it-up [...]
> > (om/build some-common-wrapper childs-cursor {:opts {:child-builder 
> > build-child}})
> > [/code]
> > 
> > Is that what you meant?
> > 
> > (I won't comment on the stylistic qualities of my code, it was under a 
> > comment further up as ";; TODO - prototype" which if I recall correctly is 
> > a license to hack away and then revist "when there is time" :)!)
> > 
> >> 
> >> If you want to do something like that I would suggest you passed in the 
> >> cursor for the subcomponent that you wish to build, and a fn to build the 
> >> component as an opt. Note, you shouldn't pass in anything via opts that 
> >> should cause a rerender when changed, so this would preclude creating a 
> >> closure over data (your cursor) and passing it via the opts. I would 
> >> probably move all of the cursor building out of :fn and make it happen in 
> >> the multimethod, passing the result to filter-header, along with 
> >> bound-tree fn as an opt.
> >> 
> >> Then filter-header will call build on bound-tree with the data you passed 
> >> in. 
> >> 
> >> That's all assuming you can't refactor it to avoid this. I've generally 
> >> only used this pattern if I want to wrap components with a generic 
> >> component (e.g. a drag drop component). 
> >> 
> >> Lucas
> >> 
> >> 
> >> 
> >>> On 4 Dec 2014, at 20:29, Colin Yates <[email protected]> wrote:
> >>> 
> >>> I have isolated the code in question and it was written very early on 
> >>> (e.g. woooh, here be dragons :)):
> >>> 
> >>> Essentially I have a component which calls a multimethod to render a 
> >>> child. This is the implementation of that multimethod. This itself uses a 
> >>> common component and passes in components to that common component.
> >>> 
> >>> Adding a key to the returned component (that isn't a keyword but a 
> >>> string(!)) gives React enough help so it doesn't unmount it. 
> >>> 
> >>> For clarity, filter-header simply displays a nice formatted title and 
> >>> puts "main-component" into the body.
> >>> 
> >>> [code]
> >>> (defmethod layout/render-right-component ::consultant
> >>> [{:keys [owner data]}]
> >>> (om/build
> >>>   c/filter-header
> >>>   {:id :consultants
> >>>    :main-component
> >>>    (om/build
> >>>      tree/bound-tree
> >>>      nil
> >>>      {:fn (fn [_] {:nodes (om/observe owner (state/all-consultants))
> >>>                    :selected-ids (-> data :filter :consultants 
> >>> :selected-ids)
> >>>                    :expanded-ids (-> data :filter :consultants 
> >>> :expanded-ids)
> >>>                    :local-search (-> data :filter :consultants 
> >>> :local-search)})
> >>>       :opts      {:change-fn
> >>>                   #(search data true)
> >>>                   }})
> >>>    }
> >>>   ;; WITH THIS REACT NO LONGER UNMOUNTS
> >>>   {:react-key "Consultants"})
> >>> )
> >>> [/code]
> >>> 
> >>> As to the reduced problem, I need to keep poking, and I still don't 
> >>> understand why it needs the "Consultants" key...
> >>> 
> >>> This realising components in state (e.g. the :main-component) isn't doing 
> >>> it for me so I think I might utilise more multi-methods. The problem is 
> >>> always how to deliver the focused cursor into the defmethod (e.g. 
> >>> render-right-component really wants to take in the cursor at (:filter 
> >>> :consultants). But that is another experiment.
> >>> 
> >>> Any advice is welcome.
> >>> 
> >>> On Thursday, 4 December 2014 12:13:16 UTC, Lucas Bradstreet  wrote:
> >>>>> On 4 Dec 2014, at 20:02, Colin Yates <[email protected]> wrote:
> >>>>> 
> >>>>> Hi Lucas, thanks for the info.
> >>>>> 
> >>>>> You are right - I should have been clearer. My scenario is that the 
> >>>>> component hierarchy itself isn't changing only the app-state that the 
> >>>>> hierarchy is mapped to.
> >>>>> 
> >>>>> In its simplest form, think of a combo-box. The app-state is {:text "" 
> >>>>> :results []}. The component has an input field mapped to :text and a 
> >>>>> <ol> mapped to :results. Changing :text populates :results accordingly.
> >>>>> 
> >>>>> Your point about keys is worth investigating. Where I am setting keys 
> >>>>> (mainly on dynamic children) they aren't changing. I'm not setting them 
> >>>>> elsewhere though, which might be worth a look.
> >>>> 
> >>>> Hmm. Maybe the lack of keys on some components may be the issue. Perhaps 
> >>>> react can't assume that the components are the same and thus unmounts 
> >>>> and remounts them (the dom diffing ensures this is still fast) e.g 
> >>>> imagine an unordered list where a list item is inserted mid list. I 
> >>>> haven't tested whether this is true. 
> >>>> 
> >>>>> 
> >>>>> Another clarity question - if an ancestor component is remounted does 
> >>>>> that cause all children to be remounted? It might be something much 
> >>>>> higher up in the hierarchy causing a remount...
> >>>> 
> >>>> I would think so. Again, I haven't tested this. 
> >>>> 
> >>>>> Thanks for confirming my assumptions anyway, I will dig in a bit deeper.
> >>>> 
> >>>> Please let us know the result. 
> >>>> 
> >>>> Cheers
> >>>>> 
> >>>>>> On Thursday, 4 December 2014 11:39:44 UTC, Lucas Bradstreet  wrote:
> >>>>>> Hi,
> >>>>>> 
> >>>>>> Changes to app state *can* cause components to be unmounted. Imagine a 
> >>>>>> component that renders another component, if a boolean value in the 
> >>>>>> app state is true, otherwise it renders and empty div. When the 
> >>>>>> boolean changes from true to false and the component is re-rendered, 
> >>>>>> the subcomponent will be unmounted. 
> >>>>>> 
> >>>>>> However, if the subcomponent should be re-rendered in both cases, it 
> >>>>>> should not be unmounted. 
> >>>>>> 
> >>>>>> Thinking about it, you're probably mistakenly using a different 
> >>>>>> react-key between the renders. This will cause a new component to be 
> >>>>>> mounted as it is not treated as a continuation of the previous 
> >>>>>> component. 
> >>>>>> 
> >>>>>> Lucas
> >>>>>> 
> >>>>>> 
> >>>>>>> On 4 Dec 2014, at 19:29, Colin Yates <[email protected]> wrote:
> >>>>>>> 
> >>>>>>> Hi all,
> >>>>>>> 
> >>>>>>> I am seeing a component consistently unmount/remount every time the 
> >>>>>>> application state changes. If I change the local state instead then 
> >>>>>>> it doesn't umount but simply re-renders as I expected.
> >>>>>>> 
> >>>>>>> I didn't expect changes in app-state to cause an unmount/remount only 
> >>>>>>> a re-render (as that is my understanding of React's lifecycle). 
> >>>>>>> Further, since I am talking about this my assumption (and the 
> >>>>>>> behaviour I am seeing) is that om will only call the render protocol 
> >>>>>>> method if the values have actually changed.
> >>>>>>> 
> >>>>>>> If my assumptions are correct I will see if I can reduce the problem 
> >>>>>>> into a simple gist.
> >>>>>>> 
> >>>>>>> The reason this is a pain, performance aside, is because a re-mount 
> >>>>>>> loses focus and this specific component is a text field. I notice in 
> >>>>>>> the discussion on text-fields in the Basic tutorial "text" is 
> >>>>>>> component local, maybe now I understand why as this seemed to me to 
> >>>>>>> go against the grain of what is advised to go into local state...
> >>>>>>> 
> >>>>>>> Can somebody please clarify?
> >>>>>>> 
> >>>>>>> Thanks!
> >>>>>>> 
> >>>>>>> -- 
> >>>>>>> Note that posts from new members are moderated - please be patient 
> >>>>>>> with your first post.
> >>>>>>> --- 
> >>>>>>> You received this message because you are subscribed to the Google 
> >>>>>>> Groups "ClojureScript" group.
> >>>>>>> To unsubscribe from this group and stop receiving emails from it, 
> >>>>>>> send an email to [email protected].
> >>>>>>> To post to this group, send email to [email protected].
> >>>>>>> Visit this group at http://groups.google.com/group/clojurescript.
> >>>>> 
> >>>>> -- 
> >>>>> Note that posts from new members are moderated - please be patient with 
> >>>>> your first post.
> >>>>> --- 
> >>>>> You received this message because you are subscribed to the Google 
> >>>>> Groups "ClojureScript" group.
> >>>>> To unsubscribe from this group and stop receiving emails from it, send 
> >>>>> an email to [email protected].
> >>>>> To post to this group, send email to [email protected].
> >>>>> Visit this group at http://groups.google.com/group/clojurescript.
> >>> 
> >>> -- 
> >>> Note that posts from new members are moderated - please be patient with 
> >>> your first post.
> >>> --- 
> >>> You received this message because you are subscribed to the Google Groups 
> >>> "ClojureScript" group.
> >>> To unsubscribe from this group and stop receiving emails from it, send an 
> >>> email to [email protected].
> >>> To post to this group, send email to [email protected].
> >>> Visit this group at http://groups.google.com/group/clojurescript.
> > 
> > -- 
> > Note that posts from new members are moderated - please be patient with 
> > your first post.
> > --- 
> > You received this message because you are subscribed to the Google Groups 
> > "ClojureScript" group.
> > To unsubscribe from this group and stop receiving emails from it, send an 
> > email to [email protected].
> > To post to this group, send email to [email protected].
> > Visit this group at http://groups.google.com/group/clojurescript.

-- 
Note that posts from new members are moderated - please be patient with your 
first post.
--- 
You received this message because you are subscribed to the Google Groups 
"ClojureScript" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/clojurescript.

Reply via email to