Fork me on GitHub
#reagent
<
2022-10-26
>
jaide16:10:48

Is there an equivalent to React's hydrate function?

Sam Ritchie20:10:18

hey all… I’ve got a question to check my understanding. I have a component that creates a point on a chart. when I drag the point around I want it to update some state in a reagent atom. BUT, nothing about its properties depends on the atom value. still, the component is trying to re-render every time it successfully updates the state, which forces its position back to the initial spot.

Sam Ritchie20:10:03

the first point triggers an update; I only want the SECOND point to re-render, but I am seeing :component-did-update called in both points

Sam Ritchie20:10:46

this is a touch convoluted since I’m using it with #C035GRLJEP8 but here is the code for the component https://github.com/sicmutils/sicmutils-clerk/blob/sritchie/mathlive/src/demo/jsxgraph.cljs#L92-L146

Sam Ritchie20:10:00

I think what is happening is that the JSXGraph2 component is re-rendering constantly… which actually STOPS happening when I take away the call to [v/inspect @!state]. that is surprising since it looked like that was a parallel component in the tree.

Sam Ritchie20:10:43

so concretely — 1. why does the deref of !state passed to v/inspect cause a re-render of JSXGraph2 and all of its children? 2. is there some way I can stop this, if this is expected behavior?

p-himik20:10:30

> nothing about its properties depends on the atom value Right, but the containing component does depend on the ratom's value. And the first Point gets re-rendered because you pass it a different :on-drag every time the containing component's view function is called. After all, (not= #() #()).

Sam Ritchie20:10:07

> Right, but the containing component does depend on the ratom’s value. > > because everything is wrapped in with-let?

Sam Ritchie20:10:42

> And the first Point gets re-rendered because you pass it a different :on-drag every time the containing component’s view function is called. After all, (not= #() #()) yeah, that is why I was hoping to short circuit by having the containing view not be called in this case. Sorry I’m feeling dense; @U2FRKM4TW, which is the containing component here that depends on the ratom’s value:? The :<>, because one of its children does?

p-himik20:10:12

> because everything is wrapped in with-let? Because you deref the ratom within the component's view function. Doesn't matter if the value is used at all - what matters is the fact of deref'ing the ratom. > which is the containing component here that depends on the ratom’s value:? By the "containing component" I meant the one in the code block, the function. Not any particular Hiccup vector. You can try moving that lambda from that Point and into with-let.

p-himik20:10:59

And same should probably be done with the lambdas in the second point. But depends on what exactly points do internally.

Sam Ritchie20:10:58

wow, moving those functions to with-let solves this!

Sam Ritchie21:10:08

okay, that helps. I was fighting inside my component-did-update etc functions to prevent my points from getting removed and re-added to the scene, but this is more clear; the function values need to stop changing (ie getting created on every render) and then no more fight needed

Sam Ritchie21:10:09

thank you!

👍 1
Sam Ritchie21:10:23

is there by any chance some wrapper @U2FRKM4TW that STOPS the re-render propagation on deref

Sam Ritchie21:10:52

actually nm, I can rework this to avoid the user having to pass functions

p-himik21:10:52

Sure - pass the ratom itself into a Hiccup vector and deref it only within the component that the vector uses.

Sam Ritchie21:10:10

but for example if I want the component to update some key in the ratom, I think the way I have to do it @U2FRKM4TW is to pass the ratom AND a key, and then have the function internally build an update-fn in a with-let

p-himik05:10:19

Yeah, that should work.

Sam Ritchie21:10:37

hey all - for my custom component, if I want to pass something back into a :ref key that the user supplies, there is no special react machinery for that, right? I just look for a :ref prop and handle it?

Sam Ritchie21:10:15

I guess the question is, what are the “rules” on when a ref should be called; in render? in component-did-update? etc

Sam Ritchie22:10:00

This comes up in the stateful library from my question above. I have components that add some stateful thing (like a point) to the scene based on declarative arguments to the component; BUT, sometimes I need to get at the internal state of the stateful thing from some other component. For points I have been using an event listener to have them push their new positions into a ratom on a “drag” event; but some other components don’t emit events. Like an “integral” component that integrates under a curve.

Sam Ritchie22:10:47

SO my thought was if I could call “ref” then I could push the whole “integral” stateful piece into a ratom once and query it there from other components

Lone Ranger03:10:09

I was under the impression is called when the component is first attached to the dom, since I will often use it to do things like get the height and width or x/y offset of the element

Lone Ranger03:10:27

My unsolicited suggestion though is that may want to consider that the event which caused the component to come in to existence in the first place, Event = State->State’, therefore View -> View’ (assuming the component is mounted in State’), you would want E to transmit that data to the external atom rather than have the update function of the component do it. I often find myself surprised at how much these things remount and engineering the update functions to be idempotent with respect to a debounce period can be quite challenging. And even if you get it right initially, if you change something upstream and you forgot about your update logic, it may start to operate outside design spec.

Sam Ritchie13:10:26

@U3BALC2HH yes of course you’re right. the issue I’m trying to resolve is that the underlying library almost requires at this point that I remove-and-re-add elements like Point, Angle etc any time their properties (color, x-y coordinate functions etc) change. so if you want to, say, query a point’s x-y coordinates every time it changes, you can either • get the point via ref, and update your reference every time :ref is called and the underlying point changes • register a callback that gets the currently-in-play point as an argument… I am thinking the second one is best, since then I never have to worry about the user hanging on to stale references.

Lone Ranger13:10:35

ahhh okay so the underlying library is procedural construction rather than "React"-ive, got it. No, wait, :ref wouldn't apply here then :thinking_face:

Lone Ranger13:10:37

or are you putting a reactive wrapper on it?

Sam Ritchie13:10:40

yup, putting a reactive wrapper around it @U3BALC2HH

Lone Ranger13:10:07

Ahhh ok. So does the change data event propagate from the underlying library or do you control the bookkeeping? If you control the bookkeeping, one kinda hacky but easy to implement reactive Sussman-style propagator that's easy to implement is:

(def my-data (atom {}))
(def my-data-rx (reagent/atom {})) ;; this could be global application state, whatever
(defn mirror-changes [f]
  (add-watch my-data ::mirror-changes
     (fn [_ _ _ new-data]
       (swap! my-data-rx f new-data))))
(defn my-graph-data (reagent.ratom/make-reaction #(graph-data-of @my-data-rx)) 

Lone Ranger13:10:26

then your event logic should update my-data and send a message to the underlying library, and the propagator should do the work of updating the reactive part of your app state

Lone Ranger13:10:49

it's way easier to suggest solutions to other people than buckle down and do my own work so thanks for humoring me

Sam Ritchie13:10:52

@U3BALC2HH the events come from the underlying library, but there is an update event emitted by the board every time anything changes that I think I can use to good effect

Sam Ritchie13:10:15

haha anytime! thank you! this is helpful for sure. a night of sleep on the problem was helpful too…