useFormControl()
React-хук для подписки на состояние формы (FormControl или FormArray).
Обеспечивает реактивную связь между состоянием формы и React-компонентами.
Использует useSyncExternalStore для оптимальной интеграции с React 18+
и Concurrent Mode.
Основные возможности
- Автоматическая подписка на все сигналы контрола
- Оптимизация ре-рендеров - компонент обновляется только при реальных изменениях
- Поддержка SSR через
useSyncExternalStore - Типобезопасность - возвращаемый тип зависит от типа контрола
Когда использовать
Используйте useFormControl когда компоненту нужен доступ к нескольким
свойствам состояния (value, issues, touched и т.д.).
Для подписки только на значение используйте useFormControlValue - это предотвратит лишние ре-рендеры при изменении других свойств.
Type Param
Тип значения (для FormControl) или элемента (для FormArray)
Param
FormControl, FormArray или undefined
Examples
import { useFormControl, useIssueMessage } from '@reformer/core/react';
import type { FormControl } from '@reformer/core';
interface TextFieldProps {
control: FormControl<string>;
label: string;
}
function TextField({ control, label }: TextFieldProps) {
const {
value,
disabled,
shouldShowError,
shouldShowIssues,
issues,
pending
} = useFormControl(control);
// Текст ошибки даёт резолвер: у правил без своего сообщения `message` пуст.
const message = useIssueMessage();
return (
<div className="field">
<label>{label}</label>
<div className="input-wrapper">
<input
type="text"
value={value}
disabled={disabled}
onChange={e => control.setValue(e.target.value)}
onBlur={() => control.markAsTouched()}
aria-invalid={shouldShowError}
/>
{pending && <Spinner />}
</div>
{shouldShowIssues && issues[0] && (
<span className="error" role="alert">
{message(issues[0])}
</span>
)}
</div>
);
}
interface CheckboxProps {
control: FormControl<boolean>;
}
function Checkbox({ control }: CheckboxProps) {
const { value, disabled, componentProps } = useFormControl(control);
return (
<label className="checkbox">
<input
type="checkbox"
checked={value}
disabled={disabled}
onChange={e => control.setValue(e.target.checked)}
/>
<span>{componentProps.label}</span>
{componentProps.hint && (
<small>{componentProps.hint}</small>
)}
</label>
);
}
// Использование
control.updateComponentProps({
label: 'Accept terms and conditions',
hint: 'Required to continue'
});
interface SelectProps {
control: FormControl<string>;
}
function Select({ control }: SelectProps) {
const { value, disabled, componentProps, shouldShowIssues, issues } = useFormControl(control);
const message = useIssueMessage(); // из '@reformer/core/react'
const options = componentProps.options as Array<{ value: string; label: string }>;
return (
<div>
<select
value={value}
disabled={disabled}
onChange={e => control.setValue(e.target.value)}
onBlur={() => control.markAsTouched()}
>
<option value="">Select...</option>
{options?.map(opt => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
{shouldShowIssues && <span className="error">{message(issues[0])}</span>}
</div>
);
}
interface Address {
street: string;
city: string;
}
interface AddressListProps {
control: FormArray<Address>;
}
function AddressList({ control }: AddressListProps) {
const { length, valid, dirty, issues } = useFormControl(control);
const message = useIssueMessage(); // из '@reformer/core/react'
const handleAdd = () => {
control.push({ street: '', city: '' });
};
const handleRemove = (index: number) => {
control.removeAt(index);
};
return (
<div className="address-list">
<div className="header">
<h3>Addresses ({length})</h3>
{dirty && <span className="badge">Modified</span>}
</div>
{issues.length > 0 && (
<div className="array-issues">
{issues.map((issue) => <p key={issue.code}>{message(issue)}</p>)}
</div>
)}
{control.map((item, index) => (
<AddressItem
key={control.keyAt(index)}
control={item}
onRemove={() => handleRemove(index)}
/>
))}
{length === 0 && (
<p className="empty">No addresses added yet</p>
)}
<button
onClick={handleAdd}
disabled={length >= 5}
>
Add Address
</button>
{!valid && (
<p className="warning">Please fix issues before submitting</p>
)}
</div>
);
}
interface FormProps {
optionalField?: FormArray<string>;
}
function Form({ optionalField }: FormProps) {
// При undefined возвращается дефолтное состояние
const { length } = useFormControl(optionalField);
if (!optionalField) {
return null;
}
return <div>Items: {length}</div>;
}
See
- useFormControlValue - для подписки только на значение
- FieldControlState - тип состояния для FormControl
- ArrayControlState - тип состояния для FormArray
Call Signature
function useFormControl<T>(control): ArrayControlState<T>;
Defined in: platforms/react/hooks/useFormControl.ts:135
React-хук для подписки на состояние FormArray.
Type Parameters
T
T extends object
Тип элемента массива
Parameters
control
FormArray или undefined
FormArray<T> | undefined
Returns
Состояние массива ArrayControlState
Call Signature
function useFormControl<T>(control): FieldControlState<T>;
Defined in: platforms/react/hooks/useFormControl.ts:148
React-хук для подписки на состояние FormControl.
Type Parameters
T
T
Тип значения поля
Parameters
control
FormControl<T>
FormControl для подписки
Returns
Состояние поля FieldControlState