CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-final-form

Framework-agnostic, high-performance form state management library with subscription-based updates.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

field-registration.mddocs/

Field Registration

Field registration and management system for dynamic form field handling with subscription capabilities and lifecycle management.

Capabilities

Register Field

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();
};

Field State Access

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);

Field State Interface

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;
}

Field Subscription Interface

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;
}

Field Configuration Interface

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' } }
  }
);

Field Subscriber Type

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

Field Subscription Items

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"]

Dynamic Field Management

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());
  };
};

docs

field-registration.md

form-actions.md

form-creation.md

index.md

state-management.md

utilities.md

validation.md

tile.json