Controlled input loses cursor under ShadowRoot in React 18
kimo-k opened this issue · 3 comments
This controlled-input cursor bug appears when I try to use the new react.dom.client
API to render an :input
component within a shadow root. The text cursor skips to the end every time I insert a character.
The same component works fine with the react.dom
API.
Is this expected, and is there anything I can do? I'm reticent to go with the react-native workaround, since my codebase is pretty large, with everything inside a shadow root (i.e. re-frame-10x).
Minimal repro: master...kimo-k:reagent:shadow-root-controlled-input-bug
Not sure if this fixes the issue but that code doesn't seem quite right.
It's creating a new input element every render cycle instead of updating an existing one. You could try adding a :key
property to see if that improves the tracking. When I've ran into similar issues, it was usually because it was deleting the previous dom element and replacing it with a new one instead of updating it.
Lastly it seems like it's better to use with-let
to initialize your atom in the repro
component definition so you don't have to return a function. That way the atom is created only once in the component's lifecycle.
(defn repro []
(with-let [text (r/atom "")]
[:input {:on-change #(reset! text (.. % -target -value))
:value @text}]))
Not sure that will entirely fix it, but it's a place to start.
Hey @jaidetree, thanks for taking a look. Were you able to run the repro without bugs?
I think your hypothesis might be incorrect, though. As far as I know, repro
is an idiomatic form-2 component, :key
would have no effect outside a seq, and using with-let
will macroexpand to the same code.
@kimo-k Just note about :key
. It isn't specific to sequences at all, any React component can have a key and React will use it to detect if the component identity changes. If identity changes, the previous component is unmounted and a new one created. This is different to just updating properties for same component instance. This can be useful outside of seqs when you need to reinitialize component local state (ratoms or hook state).
with-let
implementation also does other stuff than just macroexpand into a basic form-2 component. Though it does indeed wrap the with-let
body into (fn [] ...)
, i.e. expand into form-2 component.
Reagents controlled input cursor hacks are a huge mess and our async rendering batching for ratom changes is really problematic with React 18 update batching so there is probably no easy fix for this.