Skip to content

Latest commit

 

History

History
67 lines (51 loc) · 2.73 KB

material-ui.md

File metadata and controls

67 lines (51 loc) · 2.73 KB

Material-UI

Example project

Material-UI TextField has for long time caused problems for Reagent users. The problem is that TextField wraps the input element inside a component so that Reagent is not able to enable input cursor fixes, which are required due to async rendering.

Note that this only happens when using controlled inputs. In some cases you can workaround the problem by using an uncontrolled input (i.e. :default-value). See also Reagent controlled inputs.

Good news is that Material-UI v1 has a property that can be used to provide the input component to TextField:

(ns example.material-ui
  (:require ["material-ui" :as mui]
            [reagent.core :as r]))

(def text-field (r/adapt-react-class mui/TextField))

(def value (r/atom ""))

(def input-component
  (r/reactify-component
    (fn [props]
      [:input (-> props
                  (assoc :ref (:inputRef props))
                  (dissoc :inputRef))])))

(def example []
  [text-field
   {:value @value
    :on-change #(reset! value (.. e -target -value))
    :InputProps {:inputComponent input-component}}])

reactify-component can be used to convert Reagent component into React component, which can then be passed into Material-UI. The component should be created once (i.e. on top level) to ensure it is not unnecessarily redefined, causing the component to be re-mounted. For some reason Material-UI uses different name for ref, so the inputRef property should be renamed by the input component.

Wrapping for easy use

Instead of providing :InputProps :inputComponent option to every TextField, it is useful to wrap the TextField component in a way that the option is added always:

(defn text-field [props & children]
  (let [props (-> props
                  (assoc-in [:InputProps :inputComponent] input-component)
                  rtpl/convert-prop-value)]
    (apply r/create-element mui/TextField props (map r/as-element children))))

Here r/create-element and reagent.impl.template/convert-prop-value achieve the same as what adapt-react-class does, but allows modifying the props.

Check the example project for complete code. Some additional logic is required to ensure option like :multiline and :select work correctly, as they affect how the inputComponent should work.

TODO: :multiline TextField without :rows (i.e. automatic height) doesn't work, because that requires Material-UI Input/Textarea, which doesn't work with Reagent cursor fix.