pitch-io / uix

Idiomatic ClojureScript interface to modern React.js
https://github.com/pitch-io/uix?tab=readme-ov-file#docs
Eclipse Public License 2.0
359 stars 21 forks source link

react issue for text inputs when using reagent/re-frame as a store (callback issue) #122

Closed alex314159 closed 11 months ago

alex314159 commented 11 months ago

Hello,

Not exactly a uix bug but a pain point trying to migrate a project from reagent/re-com, with a react issue:

https://github.com/facebook/react/issues/955#issuecomment-281802381

and the normal workarounds don't work because of, I think, the call-back.

Minimal example:

(defui custom-text-field [{:keys [ratom]}]
  ($ :text-input { :value (rds/use-reaction ratom) :onChange (fn [e] (reset! ratom (event-value e))) }))

(def test-ratom (r/atom ""))

($ custom-text-field {:ratom test-ratom} )

This will create an input-box where, everytime you type, the cursor goes to the end, making it very difficult to correct something at the beginning of the box.

The source problem is I want to be able to change the input of the field based on some event, and then the user to override the field.

Thank you

roman01la commented 11 months ago

It's not React bug, but rather Reagent's. The problem here is that RAtoms are updated async, which is not how user input should be handled on the web. Reagent has a workaround for that and UIx is using this hack when it detects usage of UIx inputs in Reagent components. This case is different though, I'm not sure there's a good fix for it and if it even makes sense to find a solution to this problem. I'd recommend to migrate the state ratom to local state hook all together.

roman01la commented 11 months ago

btw I've just tried it and it works fine

(def value (r/atom ""))

(defui app []
  ($ :input {:value (use-reaction value)
             :on-change #(reset! value (-> % .-target .-value))}))
alex314159 commented 11 months ago

thanks for your helpful comments. When you say it works fine - does the cursor not jump to the end in your example, when you type text in the middle of the field?

alex314159 commented 11 months ago

https://stackoverflow.com/a/68928267/1045479 this sorted my problem. Thanks again for looking.

(def ratom (r/atom "initial value"))

(defui controlled-input [{:keys [value on-change]}]
 (let [[cursor setCursor] (use-state nil)
 ref (use-ref nil)
 x (use-effect [ref cursor value]
 (let [input (aget ref "current")]
 (if (some? input)
 (.setSelectionRange input cursor cursor))))
 handleChange (fn [e] (setCursor (-> e .-target .-selectionStart)) (on-change e))]
 ($ :input {:ref ref :value value :onChange handleChange})))

[v-box :children [($ controlled-input {:value @ratom :on-change #(reset! ratom (-> % .-target .-value))})
 [:div {} @ratom]
 [button :label "click me" :on-click #(reset! ratom "changing input")]]]
roman01la commented 11 months ago

thanks for your helpful comments. When you say it works fine - does the cursor not jump to the end in your example, when you type text in the middle of the field?

yep!