v1.0.2
New APIs: unstable_useControl
and <unstable_Control />
In v1, the useInputControl
hook is introduced with ability to insert an hidden input for you. Unfortunately, this is found to be problematic in situations where you need to dynamically render the input. After some discussions, we believe it would be better stop supporting this and have developers deciding how they wanna render the hidden input instead. To avoid breaking changes on the useInputControl
hook, a new useControl
hook is introduced which works similar to useInputControl()
except you are required to register the input element yourself:
Here is an example with Headless UI Listbox
function Select({
name,
options,
placeholder,
}: {
name: FieldName<string>;
placeholder: string;
options: string[];
}) {
const [field] = useField(name);
const control = useControl(field);
return (
<Listbox
value={control.value ?? ''}
onChange={value => control.change(value)}
>
{/* Render a select element manually and register with a callback ref */}
<select
className="sr-only"
aria-hidden
tabIndex={-1}
ref={control.register}
name={field.name}
defaultValue={field.initialValue}
>
<option value="" />
{options.map((option) => (
<option key={option} value={option} />
))}
</select>
<div className="relative mt-1">
<Listbox.Button className="...">
<span className="block truncate">
{control.value ?? placeholder}
</span>
<span className="...">
<ChevronUpDownIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</span>
</Listbox.Button>
<Listbox.Options className="...">
{options.map((option) => (
<Listbox.Option
key={option}
className="..."
value={option}
>
{({ selected, active }) => (
<>
<span className="...">
{option}
</span>
{option !== '' && selected ? (
<span className="...">
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</div>
</Listbox>
);
}
You might also find the <Control />
component in the render-props style useful when working with checkbox group in which we can use it to register each individual checkbox element without creating an additional component. Here is an example based on Radix Checkbox:
function CheckboxGroup({
name,
options,
}: {
name: FieldName<string[]>;
options: string[];
}) {
const [field] = useField(name);
// The initialValue can be a string or string array depending on how it was submitted before.
// To make it easy working with the initial value, we make sure it is always an array
const initialValue =
typeof field.initialValue === 'string'
? [field.initialValue]
: field.initialValue ?? [];
return (
<div className="py-2 space-y-4">
{options.map((option) => (
<Control
key={option}
meta={{
key: field.key,
initialValue: initialValue.includes(option) ? option : '',
}}
render={(control) => (
<div
className="flex items-center"
ref={(element) => {
// Radix does not expose the inner input ref. That's why we query it from the container element
control.register(element?.querySelector('input'))
}}
>
<RadixCheckbox.Root
type="button"
className="flex h-[25px] w-[25px] appearance-none items-center justify-center rounded-[4px] bg-white outline-none shadow-[0_0_0_2px_black]"
id={`${field.id}-${option}`}
name={field.name}
value={option}
checked={control.value === option}
onCheckedChange={(state) =>
control.change(state.valueOf() ? option : '')
}
onBlur={control.blur}
>
<RadixCheckbox.Indicator>
<CheckIcon className="w-4 h-4" />
</RadixCheckbox.Indicator>
</RadixCheckbox.Root>
<label
htmlFor={`${field.id}-${option}`}
className="pl-[15px] text-[15px] leading-none"
>
{option}
</label>
</div>
)}
/>
))}
</div>
);
}
Feel free to open a discussion if you have any issue using the new APIs. We will deprecate useInputControl
and remove the unstable
prefix once we are confident with the new APIs.
Improvements
- Improved type inference with nested discriminated union (#459)
- Fixed an issue with zod not coercing the value of a multi select correctly (#447)
- Fixed an issue with react devtool inspecting the state of the
useForm
hook. - Fixed an issue with file input never marked as dirty (#457)
- Fixed several typos on the docs. Thanks to @ngbrown (#449) and @Kota-Yamaguchi (#463)
New Contributors
- @ngbrown made their first contribution in #449
- @Kota-Yamaguchi made their first contribution in #463
Full Changelog: v1.0.1...v1.0.2