I have a problem using the Material UI's Autocomplete
with Reagent (ClojureScript). The element renders fine, but when I try to click on it, I get the following exceptions:
Uncaught TypeError: Cannot read property 'focus' of null
at handleClick (useAutocomplete.js:938)
at HTMLUnknownElement.callCallback (react-dom.development.js:189)
at Object.invokeGuardedCallbackImpl (react-dom.development.js:238)
at invokeGuardedCallback (react-dom.development.js:293)
at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:307)
at executeDispatch (react-dom.development.js:390)
at executeDispatchesAndReleaseTopLevel (react-dom.development.js:412)
at forEachAccumulated (react-dom.development.js:3260)
at runEventsInBatch (react-dom.development.js:3305)
at handleTopLevel (react-dom.development.js:3515)
useAutocomplete.js:322 Uncaught TypeError: Cannot read property 'removeAttribute' of null
at eval (useAutocomplete.js:322)
at eval (useEventCallback.js:26)
at eval (useAutocomplete.js:433)
at eval (useEventCallback.js:26)
at eval (useAutocomplete.js:463)
at eval (useAutocomplete.js:528)
at commitHookEffectListMount (react-dom.development.js:19765)
at commitPassiveHookEffects (react-dom.development.js:19803)
at HTMLUnknownElement.callCallback (react-dom.development.js:189)
at Object.invokeGuardedCallbackImpl (react-dom.development.js:238)
Breaking in JS debugger, I see that inputRef.current
is null (which is what focus
and removeAttribute
are called on. (Oddly enough, the only place where inputRef
is set in the file is by calling useRef(null)
, which is what results in inputRef.current
being null.)
In my code, I define the Autocomplete field as follows:
(ns my-product-redacted.views.atoms
(:require [reagent.core :as r]
["@material-ui/lab/Autocomplete" :default Autocomplete]
;; other requires
))
(def autocomplete-field (r/adapt-react-class Autocomplete))
Then, in a React component, it is used as follows:
[a/autocomplete-field {:render-input (fn [js-params]
(let [clj-params (js->clj js-params)
params {:label label
:width width
:select select?
:Input-label-props {:shrink true}
:Select-props {:native true}}
all-params (into clj-params params)]
(js/console.log (clj->js all-params))
(r/as-element [a/text-field all-params])))
:options (when select? (cons {:value "" :label ""} options))
:get-option-label (fn [option] (or (get (js->clj option) "label") ""))
:default-value (when (not select?) value-override)
:value (when select? value)
:disabled disabled?
:on-focus #(re-frame/dispatch [::forms/on-focus path])
:on-blur #(re-frame/dispatch [::forms/on-blur path])
:on-change #(re-frame/dispatch (conj on-change (-> % .-target .-value)))})]
(Here, a/text-field
is also defined in the same namespace as a/autocomplete-field
and in a similar way.)
The JS console log (from the (js/console.log (clj->js params))
call) shows that inputProps.ref.current
is set to null. However, InputProps.ref
is not null. Even so, I tried to manually associate the same function as is passed with InputProps.ref
to inputProps.ref.current
, but that made no difference.
I have also tried the workaround suggested in https://github.com/mui-org/material-ui/issues/21245 (although that issue is with the Gestalt library, not with Reagent, it suggests that there may be a problem with ref forwarding). But wrapping the text-field
into a div with the ref
taken from InputProps.ref
also made no difference.
Any suggestions?
Calling js->clj
on the render-input js params breaks them.
I put a quick demo of the material UI autocomplete component in my demo repository here.
But it's mainly taken from the official reagent docs/examples here:
(defn autocomplete-example []
[:> mui/Grid
{:item true}
[:> Autocomplete {:options ["foo" "bar" "foobar"]
:style {:width 300}
;; Note that the function parameter is a JS Object!
;; Autocomplete expects the renderInput value to be function
;; returning React elements, not a component!
;; So reactify-component won't work here.
:render-input (fn [^js params]
;; Don't call js->clj because that would recursively
;; convert all JS objects (e.g. React ref objects)
;; to Cljs maps, which breaks them, even when converted back to JS.
;; Best thing is to use r/create-element and
;; pass the JS params to it.
;; If necessary, use JS interop to modify params.
(set! (.-variant params) "outlined")
(set! (.-label params) "Autocomplete")
(r/create-element mui/TextField params))}]])
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With