Fork me on GitHub
#reagent
<
2021-04-26
>
Michaël Salihi12:04:46

How can I pass static property to a Reagent component. I'm trying to reproduce persistent layout from this example:

const Home = ({ user }) => {
  return (
    <>
      <h1>Welcome</h1>
      <p>Hello {user.name}, welcome to your first Inertia app!</p>
    </>
  )
}

Home.layout = page => <Layout children={page} title="Welcome" />

simongray12:04:38

@admin055 I’m not totally up-to-speed on modern JS and terminology. What is a static property?

simongray12:04:01

So Home is a function and Home.layout is another function?

simongray12:04:29

What’s the point??

simongray12:04:46

AFAIK you can’t add fields to Clojure Fns which is what reagent uses as functions, but why not simply have two separate functions? Why does it need to be embedded inside a JS object?

Michaël Salihi12:04:18

Interesting. Maybe a lead towards a solution that I don't think of.

Michaël Salihi12:04:51

Inertia.js use it for persistent layouts https://inertiajs.com/pages#persistent-layouts and I'm trying to reproduce with Reagent.

simongray12:04:47

I’m not even sure what a persistent layout even is (I’m not familiar with intertia.js), but I have a hard time seeing why it needs to be structured like that to work.

jkxyz12:04:29

This is useful in JSX when you want to accept a generic component as children, which should implement some interface that can be accessed by getting the component type within the parent and calling its static methods

jkxyz12:04:58

If it’s for interop with a JS library then I think that it might work like so (def MyComponent (reagent.core/reactify-component my-reagent-component)) (set! (.-layout MyComponent) (fn {} ,,,))

simongray12:04:57

^^ makes sense

Michaël Salihi13:04:02

Thanks @josh604 for your suggestion, I try.

jkxyz13:04:21

Since MyComponent is now a plain React component, you would have to use the form [:> MyComponent] . You might be able to achieve the same thing in a form-3 Reagent component using reagent.core/create-class and calling set! as above on the class object, inside of the form-3 function

Stuart21:04:11

Can someone help me with what I'm doign wrong here?

(defn test-scroll []
  (let [scroll-position (reagent.core/atom 0)]
    [:div
     [:textarea.text-editor {:on-scroll (fn [^js e]
                                            (js/console.log (.. e -target -scrollTop))
                                            (reset! scroll-position (.. e -target -scrollTop)))}]
     [:div (str "Scroll pos: " @scroll-position)]]))
I just want to scroll the textarea and update scroll-position when it scrolls. When I scroll the editor, the console updates with the scrollTop value. But I think I'm doing something wrong with the atom. It always shows 0 in the test div at the end. When I scroll the console shows this:

p-himik22:04:55

Replace let with reagent.core/with-let.

p-himik22:04:10

Otherwise, the ratom gets reset on each render.

Stuart22:04:28

thank you! that works 😄

Stuart22:04:07

Do I need this because I'm doing (reset!), if I was using (swap!) would I be ok with just let ?

p-himik22:04:14

Any change to a ratom that's deref'ed inside a view will cause that view to be re-rendered.

simongray07:04:08

@U013YN3T4DA If you have a stateful component it needs to be either form-2 or form-3. with-let just allows you to make a form-2 component while skipping the inner render function, saving you a few characters and a level of indentation. I recommend reading this: https://purelyfunctional.tv/guide/reagent/

👍 3
p-himik10:04:36

with-let works like a form-2 component in most of the cases, but it's not entirely like it. It's a form-1 component with cache. And you can explicitly make it work differently.

Stuart10:04:04

yeah, i had read about form-1, form-2 then I started using re-frame and it doesn't need to the form-1, form-2 stuff and for some reason I just completely forgot that local state still needs a form-2. It just completely went out of my head 😞 I'm still learning this stuff and am definitely still in the idiot stage 😄

Stuart10:04:17

thanks guys

Stuart10:04:00

Would you say getting the scroll position of a textbox and scrolling a second text box to keep them in sync is a good use for component state ?

Stuart10:04:30

or would it be better as a re-frame reg-fx ?

Stuart10:04:46

feels like local state makes more sense.

p-himik10:04:07

With an atom, you might also notice lags in scroll position synchronization. If that's an issue, you might want to combine two text boxes in a single component and sync their scrolling position via regular imperative JS.

Stuart10:04:53

Yeah, that's a good point. I do actually see a tiny lag... This seems to work ok though:

:on-scroll (fn [^js e]
              (let [scroll-pos (.. e -target -scrollTop)]
                  (-> js/document
                      (.getElementById "lineNos")
                      (.-scrollTop)
                      (set! scroll-pos))))
Is that reasonable ?

Stuart10:04:08

I've put both textboxes in the same component / function

p-himik10:04:38

(.getElementById "lineNos") - not a great thing, impossible to reuse. Use React refs instead.

p-himik10:04:26

Otherwise, seems reasonable, yeah.

Stuart10:04:51

you mean `

React.findDOMNode(
?

Stuart10:04:33

thanks! I'll have a read

p-himik10:04:32

(reagent/with-let [ref2 (reagent/atom nil)]
  [:div
    [:textarea {:on-scroll (fn [^js e] (when-some [node2 @ref2] ...))}]
    [:textarea {:ref #(reset! ref2 %)}]])