Skip to main content

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

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<T>

Состояние массива 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<T>

Состояние поля FieldControlState