I'm wrapping a JavaScript library that expects react components as paramters. I can use reactify-component to pass reagent components in, but I then have to convert the arguments of the reagent-component with js->clj. Is there a way to wrap and generalize this conversion, so the end-user of this cljs library doesn't have to worry about the underlying js?
(defn- react-flowify-component [component] (r/reactify-component (fn [args] (component (flowjs->clj args)))))
something like this works for type-1 components, but not type-2
I guess it would require a specialized compiler
Why do you call component as a function instead of using []?
So I'm using refs for the first time and I have some questions. This is my adaptation of the example from https://react.dev/reference/react/forwardRef.
(def my-input
(forwardRef (fn label [props ref]
(let [props (assoc (js->clj props) :ref ref)]
(r/as-element [:label (get props "label") [:input props]])))))
(defn test-form []
(let [ref (useRef nil)
handle-click (fn [] (.. ref -current (focus)))]
[:form
[:> my-input {:label "Enter your name: " :ref ref}]
[:button {:type :button :on-click handle-click} "Edit"]]))
(defn main []
[:div [:f> test-form]])
It works, but several things are unclear to me. Why do I need to create a class-based component with :> to then pass into a functional component? I assume there must be a better/more correct way to do it. Why does this blow up if I pass ref in like [:> my-input {:label "Enter your name: "} ref] , which is how I would expect it to work?> Why do I need to create a class-based component with :> to then pass into a functional component?
forwardRef returns a React component. To make React components work inside Reagent Hiccup, you need to use :>. It has nothing to do with the fact that test-from is a function component. You could've made it a class component and it would still have to use :>.
> Why does this blow up if I pass ref in like [:> my-input {:label "Enter your name: "} ref] , which is how I would expect it to work?
Because React expects for the ref to be a part of the props object. Hard to add more details without carefully going through the implementation (again, heh), but Reagent does some props/args juggling to make Reagent Hiccup work in a nice way. Seamless interop with React ecosystem is a second-class citizen.
Wouldn't using a class-based component cause the Invalid hook call error? Point nonetheless received about the props stuff, thanks.
You're using the hook in test-form, which is a function component.
I see, misinterpreted your above comment. Thanks for the clarification.
I'm a bit confused on how the :value and :default-value attributes of input elements in reagent / re-frame really work. For form elements that need an initial value, we have successfully followed this pattern [:input {:type :text :default-value @(rf/subscribe [:val]) :on-change (fn [ev] (rf/dispatch [:update-val (-> ev .-target .-value)}]
As far as I understand, using :default-value here, prevents any flickering and delay when typing into the input, because the input does not actually "listen" for changes to the :val sub. However, I can't clear the input through a re-frame event, for that very reason.
OTOH, I could use :value @(rf/subscribe [:val]) and then the value in app-db and the input are synced, and can be reset by an re-frame event. But if the dispatch or subscription are slow for some reason, one may experience lagging when typing into the input.
Re-frame's re-com seems to handle the lagging using reagent atoms to represent internal and external values of the input. But re-com components come with other stuff that I don't want to use.
My question is: what is the preferred and simplest way to manage inputs in a re-frame app? Should I use :default-value when I don't need two-way updates of the value and :value if I want external events to be able to change the value, but I will then risk laggy inputs? Or do I need to imitate re-com's more complex way of handling inputs?
Have you tried dispatch-sync?
So, I have actually not experienced laggy typing even when using :value. But I guess that :dispatch-sync would solve that?
My question is more a principal one. Is there a "gold standard" to handle inputs? If no, what are the options under what scenarios?
That's actually in the FAQ. :) https://day8.github.io/re-frame/FAQs/laggy-input/
Doh! I actually read that precise FAQ entry when I started re-frame development.
But thank you for reminding me. 馃槈
May I ask what option you use of the ones listed there? Does it depend on context?
2 or 3, depending on the context.
Not sure why, but often plain dispatch works just fine as well.
You never use :default-value?
Nope.
Right. I鈥檝e tried to read the React docs to understand what defaultValue does but since I don鈥檛 understand React, I don鈥檛 understand that either.
Maybe I shouldn鈥檛 concern my brain with this if it is not important in a reagent / re-frame context
An alternative that isnt suggested in the docs: use uncontrolled state and hold a ref; then you can dispatch the values to your app-db on blur. This has the benefit of preventing re-renders per key press and is theoretically faster than using a local ratom or even hooks.
Of course this assumes there is a possibility to use uncontrolled state; otherwise this is not an option
Isn't that exactly what the docs mean by "don't use on-change, and instead use on-blur"? And you don't really need refs for that.
the ref is to be able to access the actual dom node so you can pull values and/or do things like focus on error without holding a ratom or some data in app-db
and i guess you're right the docs do imply what i mentioned regarding on-blur