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.