useFormControl()
React-хук для подписки на состояние формы (FieldNode или ArrayNode).
Обеспечивает реактивную связь между состоянием формы и React-компонентами.
Использует useSyncExternalStore для оптимальной интеграции с React 18+
и Concurrent Mode.
Основные возможности
- Автоматическая подписка на все сигналы контрола
- Оптимизация ре-рендеров - компонент обновляется только при реальных изменениях
- Поддержка SSR через
useSyncExternalStore - Типобезопасность - возвращаемый тип зависит от типа контрола
Когда использовать
Используйте useFormControl когда компоненту нужен доступ к нескольким
свойствам состояния (value, errors, touched и т.д.).
Для подписки только на значение используйте useFormControlValue - это предотвратит лишние ре-рендеры при изменении других свойств.
Type Param
Тип значения (для FieldNode) или элемента (для ArrayNode)
Param
FieldNode, ArrayNode или undefined
Examples
import { useFormControl } from '@reformer/core';
import type { FieldNode } from '@reformer/core';
interface TextFieldProps {
control: FieldNode<string>;
label: string;
}
function TextField({ control, label }: TextFieldProps) {
const {
value,
disabled,
shouldShowError,
errors,
pending
} = useFormControl(control);
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>
{shouldShowError && errors[0] && (
<span className="error" role="alert">
{errors[0].message}
</span>
)}
</div>
);
}
interface CheckboxProps {
control: FieldNode<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.setComponentProps({
label: 'Accept terms and conditions',
hint: 'Required to continue'
});
interface SelectProps {
control: FieldNode<string>;
}
function Select({ control }: SelectProps) {
const { value, disabled, componentProps, shouldShowError, errors } = useFormControl(control);
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>
{shouldShowError && <span className="error">{errors[0]?.message}</span>}
</div>
);
}
interface Address {
street: string;
city: string;
}
interface AddressListProps {
control: ArrayNode<Address>;
}
function AddressList({ control }: AddressListProps) {
const { length, valid, dirty, errors } = useFormControl(control);
const handleAdd = () => {
control.push({ street: '', city: '' });
};
const handleRemove = (index: number) => {
control.remove(index);
};
return (
<div className="address-list">
<div className="header">
<h3>Addresses ({length})</h3>
{dirty && <span className="badge">Modified</span>}
</div>
{errors.length > 0 && (
<div className="array-errors">
{errors.map((e, i) => <p key={i}>{e.message}</p>)}
</div>
)}
{control.map((item, index) => (
<AddressItem
key={item.id}
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 errors before submitting</p>
)}
</div>
);
}
interface FormProps {
optionalField?: ArrayNode<string>;
}
function Form({ optionalField }: FormProps) {
// При undefined возвращается дефолтное состояние
const { length } = useFormControl(optionalField);
if (!optionalField) {
return null;
}
return <div>Items: {length}</div>;
}
See
- useFormControlValue - для подписки только на значение
- FieldControlState - тип состояния для FieldNode
- ArrayControlState - тип состояния для ArrayNode
Call Signature
function useFormControl<T>(control): ArrayControlState<T>;
Defined in: hooks/useFormControl.ts:106
React-хук для подписки на состояние ArrayNode.
Type Parameters
T
T extends FormFields
Тип элемента массива
Parameters
control
ArrayNode<T> | undefined
ArrayNode или undefined
Returns
Состояние массива ArrayControlState
Call Signature
function useFormControl<T>(control): FieldControlState<T>;
Defined in: hooks/useFormControl.ts:119
React-хук для подписки на состояние FieldNode.
Type Parameters
T
T extends FormValue
Тип значения поля
Parameters
control
FieldNode<T>
FieldNode для подписки
Returns
Состояние поля FieldControlState