Fork me on GitHub
#reagent
<
2016-04-06
>
joshkh10:04:22

question about managing state in reagent while avoiding rendering loops. i have a component that receives a prop called "service". when the component receives the prop it calls another function to update its internal state with the results. but when the internal state is dereferenced it causes the component to re-render, which triggers "component-did-update" which then calls the ajax function again causing a loop. is there an obvious pattern that i'm missing? example:

(defn refresh-data [state component]
  (let [service (-> component reagent/props :service)]
   (go  (reset! state  (<! (io/fetch-some-data service)))))

(defn data-displayer []
  (let [state (reagent/atom nil)]
    (reagent/create-class
      {:component-did-mount (partial refresh-data state)
       :component-did-update (partial refresh-data state)
       :reagent-render (fn [] [:div (str "data" @state)])})))

joshkh10:04:40

i suppose i could replace :component-did-update with :component-will-receive-props, but this requires getting the props via reagent.impl.util/extract-props and handling the lifecycles differently and that feels a bit clumsy to me.

mikethompson12:04:45

@rodeorockstar: in Reagent, you want your components to act like functions which turn data into Hiccup. Simple functions. But your component is trying to do/be more than that, and that's the fundamental mistake IMO. So I think you have an architecture problem which is manifesting as a technical problem (a loop). You may want to consider using something like re-frame (shocking bias alert, I'm the author). Even if you don't end up using it, it might give you some ideas about possible Reagent architectures.

joshkh12:04:46

thanks. i'm actually using re-frame although probably in an abusive/crazy way. i'm pulling in various reagent "tools" that are purposefully ignorant of subscriptions but are reactive to their input properties, and each tool is wrapped in a container that handles subscriptions/dispatches. the reason being that we can include external react based packages that manage their own state.

joshkh12:04:41

which is no excuse for architecture problems. simple_smile i think you're right and i'll need to take a different approach. thanks for the advice.

mikethompson12:04:12

If you are trying to use re-frame with React components that are reactive to their props, you might want to look at https://github.com/Day8/re-frame/wiki/Using-Stateful-JS-Components

mikethompson12:04:30

That shows you a pattern - create an outer component which sources data (via subscriptions) and an inner component (React component?) to which data is passed as props.

mikethompson12:04:31

That page talks about D3 and Google Maps, but I'd guess it is just as applicable to your case.

joshkh12:04:21

thanks, i'll give it a go!

fabrao22:04:52

Hello all, I started learning reagent and I asked to @gadfly361 how to use reagent with Semantic-UI Modal stuff. And he told me to use something like this https://github.com/gadfly361/soda-ash/issues/2 . So, I changed it but the error still the same "Uncaught Error: Invariant Violation: findComponentRoot(..., .6.0.0.2.0): Unable to find element. This probably means the DOM was unexpectedly mutated (e.g., by the browser), usually due to forgetting a when using tables, nesting tags like<form>, <p>, or <a>, or using non-SVG elements in an parent. Try inspecting the child nodes of the element with React ID". So, can´t I use a button inside modal with on-click event catch? What I´m doing wrong? Regards

gadfly36122:04:16

Hey @fabrao nice to see you here! I provided a minimal working example based on a clean/new app. My recommendation would be to verify that you can get the minimal example working, then try and build it up into your specific use case. I notice you have a helper function for jquery, id be curious to see if that ends up working when you modify the minimal working example to use that.

fabrao22:04:16

@gadfly361: I did what you said but I changed to this: (defn modal [] [:div {:class "ui basic modal"} [:div {:class "header"} "Modal"] [:p "This is some content"] [:button {:on-click #(js/console.log "EOEOEOEO")} "Log"] ]) and when I click to button, it screwed up with that sh*t message simple_smile , not showing the console message. So, you can not use events to button inside modal?

gadfly36122:04:11

I was able to reproduce your error with the button's on-click ... not sure what is up. Gotta run for now, but added a snippet above for how to reproduce