Skip to content

Controlled inputs

Writing controlled inputs is a bit different than in React. There are two parts to this.

function App() {
const refs = useRefProxy();
const [get, set] = useTextInput(refs.input);
function handleChange(event) {
/* ... */
}
return (
<input ref={refs.input} onChange={handleChange} value="defaultValue" />
);
}

You have several handles on this element:

  • Set its default value with the value prop
  • Get its current value with get
  • Change its current value with set
  • Respond to user changes with onChange

To replicate these handles for custom components, expose get and set functions through a context, then consume them with useControlledInput.

Start with a simple input component that wraps a native input:

function MyInput({ value, onChange }) {
return <input value={value} onChange={onChange} />;
}

Expose its get and set functions through context:

function MyInput({ value, onChange }) {
const refs = useRefProxy();
const [get, set] = useTextInput(refs.input);
return (
<MyInput.Context.Provider value={{ get, set }}>
<input value={value} onChange={onChange} ref={refs.input} />
</MyInput.Context.Provider>
);
}
MyInput.Context = createContext();

Then use it like any controlled input:

function App() {
const refs = useRefProxy();
const [get, set] = useControlledInput(refs.input);
function handleChange(event) {
/* ... */
}
return (
<MyInput ref={refs.input} onChange={handleChange} value="defaultValue" />
);
}

useControlledInput works with any component that exposes get and set through its context, not just wrappers of native inputs.

Controlled custom radio group
Source
import './examples.css';
import { useTextContent, useControlledInput, useRefProxy } from 'domstatejsx';
import Radio from './Radio.jsx';

export default function App() {
  const refs = useRefProxy();

  const [getRadio, setRadio] = useControlledInput(refs.radio);
  const [, setSpan] = useTextContent(refs.span);

  function refreshSpan() {
    setSpan(getRadio());
  }

  setTimeout(refreshSpan, 0);

  return (
    <>
      <Radio
        defaultValue={3}
        onChange={setSpan}
        options={[
          [0, 'Zero'],
          [1, 'One'],
          [2, 'Two'],
          [3, 'Three'],
        ]}
        ref={refs.radio}
      />
      <p>
        <button
          class="dx-btn"
          onClick={() => {
            setRadio(2);
            refreshSpan();
          }}
        >
          Select "two"
        </button>
      </p>
      <p>
        Selected Value: <span ref={refs.span} />
      </p>
    </>
  );
}