Framework-agnostic, high-performance form state management library with subscription-based updates.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Field registration and management system for dynamic form field handling with subscription capabilities and lifecycle management.
Register a field with the form and subscribe to its state changes.
type RegisterField<FormValues = Record<string, any>> = <F extends keyof FormValues>(
name: F,
subscriber: FieldSubscriber<FormValues[F]>,
subscription: FieldSubscription,
config?: FieldConfig<FormValues[F]>
) => Unsubscribe;
interface FormApi<FormValues, InitialFormValues> {
/** Register a field with subscription and optional configuration */
registerField: RegisterField<FormValues>;
}Usage Examples:
import { createForm } from "final-form";
const form = createForm({
onSubmit: (values) => console.log(values)
});
// Basic field registration
const unsubscribe = form.registerField(
'firstName',
(fieldState) => {
console.log('First name:', fieldState.value);
console.log('Has error:', fieldState.error);
console.log('Is touched:', fieldState.touched);
},
{ value: true, error: true, touched: true }
);
// Field with configuration
const unsubscribeEmail = form.registerField(
'email',
(fieldState) => {
updateEmailInput(fieldState);
},
{ value: true, error: true, touched: true, valid: true },
{
initialValue: '',
validateFields: ['confirmEmail'], // Validate confirmEmail when email changes
getValidator: () => (value) => {
if (!value) return 'Email is required';
if (!/\S+@\S+\.\S+/.test(value)) return 'Invalid email format';
}
}
);
// Cleanup when component unmounts
return () => {
unsubscribe();
unsubscribeEmail();
};Methods for accessing field state without subscribing to changes.
interface FormApi<FormValues, InitialFormValues> {
/** Get current state of a specific field */
getFieldState<F extends keyof FormValues>(
field: F
): FieldState<FormValues[F]> | undefined;
/** Subscribe to field state changes with callback approach */
subscribeFieldState<F extends keyof FormValues>(
name: F,
onChange: () => void,
subscription: FieldSubscription
): Unsubscribe;
/** Get a snapshot of current field state */
getFieldSnapshot<F extends keyof FormValues>(
name: F
): FieldState<FormValues[F]> | undefined;
/** Get names of all currently registered fields */
getRegisteredFields(): string[];
}Usage Examples:
// Get current field state
const emailState = form.getFieldState('email');
if (emailState?.error) {
showFieldError('email', emailState.error);
}
// Callback-based field subscription
const unsubscribeCallback = form.subscribeFieldState(
'firstName',
() => {
const snapshot = form.getFieldSnapshot('firstName');
console.log('First name changed:', snapshot?.value);
},
{ value: true }
);
// Get all registered fields
const registeredFields = form.getRegisteredFields();
console.log('Registered fields:', registeredFields);Complete field state object with all available properties and methods.
interface FieldState<FieldValue = any> {
/** Whether field is currently active (focused) */
active?: boolean;
/** Function to blur the field */
blur: () => void;
/** Function to change the field value */
change: (value: FieldValue | undefined) => void;
/** Custom data attached to the field */
data?: AnyObject;
/** Whether field value differs from initial value */
dirty?: boolean;
/** Whether field has been modified since last submission */
dirtySinceLastSubmit?: boolean;
/** Field validation error */
error?: any;
/** Function to focus the field */
focus: () => void;
/** Initial value of the field */
initial?: FieldValue;
/** Whether field has validation errors */
invalid?: boolean;
/** Array length (for array fields) */
length?: number;
/** Whether field has been modified from initial value */
modified?: boolean;
/** Whether field has been modified since last submission */
modifiedSinceLastSubmit?: boolean;
/** Field name */
name: string;
/** Whether field is in its initial, unmodified state */
pristine?: boolean;
/** Field submission error */
submitError?: any;
/** Whether last submission failed for this field */
submitFailed?: boolean;
/** Whether last submission succeeded */
submitSucceeded?: boolean;
/** Whether form is currently being submitted */
submitting?: boolean;
/** Whether field has been touched (focused and blurred) */
touched?: boolean;
/** Whether field passes validation */
valid?: boolean;
/** Whether field is currently being validated */
validating?: boolean;
/** Current field value */
value?: FieldValue;
/** Whether field has been visited (focused) */
visited?: boolean;
}Subscription object for controlling which field state properties trigger updates.
interface FieldSubscription {
active?: boolean;
data?: boolean;
dirty?: boolean;
dirtySinceLastSubmit?: boolean;
error?: boolean;
initial?: boolean;
invalid?: boolean;
length?: boolean;
modified?: boolean;
modifiedSinceLastSubmit?: boolean;
pristine?: boolean;
submitError?: boolean;
submitFailed?: boolean;
submitSucceeded?: boolean;
submitting?: boolean;
touched?: boolean;
valid?: boolean;
validating?: boolean;
value?: boolean;
visited?: boolean;
}Configuration options when registering a field.
interface FieldConfig<FieldValue = any> {
/** Callback executed after form submission */
afterSubmit?: () => void;
/** Callback executed before form submission, can prevent submission by returning false */
beforeSubmit?: () => void | false;
/** Custom data to attach to the field */
data?: any;
/** Default value when field value is undefined */
defaultValue?: any;
/** Function that returns a validator for this field */
getValidator?: GetFieldValidator<FieldValue>;
/** Initial value for the field */
initialValue?: any;
/** Custom equality function for determining if field value changed */
isEqual?: IsEqual;
/** Whether field changes should trigger notifications (default: false) */
silent?: boolean;
/** Names of other fields to validate when this field changes */
validateFields?: string[];
/** Whether field validation is asynchronous */
async?: boolean;
}Field Configuration Examples:
// Field with custom validator
form.registerField(
'age',
(fieldState) => updateAgeField(fieldState),
{ value: true, error: true },
{
getValidator: () => (value) => {
if (!value) return 'Age is required';
if (value < 18) return 'Must be 18 or older';
if (value > 120) return 'Invalid age';
}
}
);
// Field with cross-field validation
form.registerField(
'password',
(fieldState) => updatePasswordField(fieldState),
{ value: true, error: true },
{
validateFields: ['confirmPassword'], // Re-validate confirmPassword when password changes
getValidator: () => (value) => {
if (!value) return 'Password is required';
if (value.length < 8) return 'Password must be at least 8 characters';
}
}
);
// Field with lifecycle callbacks
form.registerField(
'specialField',
(fieldState) => updateSpecialField(fieldState),
{ value: true },
{
beforeSubmit: () => {
// Perform validation or cleanup before submission
const isValid = performSpecialValidation();
return isValid; // Return false to prevent submission
},
afterSubmit: () => {
// Cleanup or reset state after submission
resetSpecialFieldState();
}
}
);
// Field with custom equality
form.registerField(
'objectField',
(fieldState) => updateObjectField(fieldState),
{ value: true },
{
isEqual: (a, b) => JSON.stringify(a) === JSON.stringify(b), // Deep comparison
initialValue: { nested: { value: 'default' } }
}
);Type definition for field state subscriber functions.
type FieldSubscriber<FieldValue = any> = (state: FieldState<FieldValue>) => void;
type GetFieldValidator<FieldValue = any> = () => FieldValidator<FieldValue> | undefined;
type FieldValidator<FieldValue = any> = (
value: FieldValue,
allValues: object,
meta?: FieldState<FieldValue>
) => any | Promise<any>;List of all available field subscription properties.
const fieldSubscriptionItems: readonly string[];The fieldSubscriptionItems array contains: ["active", "data", "dirty", "dirtySinceLastSubmit", "error", "initial", "invalid", "length", "modified", "modifiedSinceLastSubmit", "pristine", "submitError", "submitFailed", "submitSucceeded", "submitting", "touched", "valid", "value", "visited", "validating"]
Examples of managing fields dynamically:
// Dynamic field registration based on conditions
const conditionalField = (fieldName: string, shouldRegister: boolean) => {
if (shouldRegister) {
return form.registerField(
fieldName,
(fieldState) => console.log(`${fieldName}:`, fieldState.value),
{ value: true }
);
}
return () => {}; // No-op unsubscribe function
};
// Array field management
const registerArrayField = (arrayName: string, index: number) => {
const fieldName = `${arrayName}[${index}]`;
return form.registerField(
fieldName,
(fieldState) => updateArrayItem(arrayName, index, fieldState),
{ value: true, error: true },
{
getValidator: () => (value) => validateArrayItem(value, index)
}
);
};
// Bulk field registration
const registerMultipleFields = (fieldNames: string[]) => {
const unsubscribeFunctions = fieldNames.map(fieldName =>
form.registerField(
fieldName,
(fieldState) => updateField(fieldName, fieldState),
{ value: true, error: true, touched: true }
)
);
// Return cleanup function
return () => {
unsubscribeFunctions.forEach(unsubscribe => unsubscribe());
};
};