or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

field-registration.mdform-actions.mdform-creation.mdindex.mdstate-management.mdutilities.mdvalidation.md
tile.json

form-actions.mddocs/

Form Actions

Form action methods for programmatic form manipulation including field changes, focus management, form lifecycle operations, and configuration updates.

Capabilities

Field Value Management

Methods for programmatically changing field values and managing field state.

interface FormApi<FormValues, InitialFormValues> {
  /** Change the value of a specific field */
  change<F extends keyof FormValues>(name: F, value?: FormValues[F]): void;
  
  /** Reset a specific field to its initial state */
  resetFieldState(name: keyof FormValues): void;
}

Usage Examples:

import { createForm } from "final-form";

const form = createForm({
  onSubmit: (values) => console.log(values),
  initialValues: {
    firstName: '',
    lastName: '',
    email: '',
    age: null
  }
});

// Change field values programmatically
form.change('firstName', 'John');
form.change('lastName', 'Doe');
form.change('email', 'john.doe@example.com');
form.change('age', 30);

// Clear a field value
form.change('firstName', undefined);

// Reset specific field to initial state
form.resetFieldState('email'); // Resets value, errors, touched, etc.

// Bulk value changes using batch
form.batch(() => {
  form.change('firstName', 'Jane');
  form.change('lastName', 'Smith');
  form.change('email', 'jane.smith@example.com');
}); // Only triggers one update notification

Focus Management

Methods for programmatically managing field focus states.

interface FormApi<FormValues, InitialFormValues> {
  /** Focus a specific field */
  focus(name: keyof FormValues): void;
  
  /** Blur a specific field */
  blur(name: keyof FormValues): void;
}

Usage Examples:

// Focus management for user experience
const handleNextButton = () => {
  const state = form.getState();
  
  if (!state.values?.firstName) {
    form.focus('firstName');
    return;
  }
  
  if (!state.values?.lastName) {
    form.focus('lastName');
    return;
  }
  
  if (!state.values?.email) {
    form.focus('email');
    return;
  }
  
  // All required fields filled, proceed
  form.submit();
};

// Auto-focus first field with error
const focusFirstError = () => {
  const state = form.getState();
  if (state.errors) {
    const firstErrorField = Object.keys(state.errors)[0];
    if (firstErrorField) {
      form.focus(firstErrorField);
    }
  }
};

// Blur field to trigger validation
const validateAndBlur = (fieldName) => {
  form.blur(fieldName); // This will trigger validation if validateOnBlur is true
};

// Custom focus flow
const customFocusFlow = {
  next: (currentField) => {
    const fieldOrder = ['firstName', 'lastName', 'email', 'phone'];
    const currentIndex = fieldOrder.indexOf(currentField);
    const nextField = fieldOrder[currentIndex + 1];
    if (nextField) {
      form.focus(nextField);
    }
  },
  
  previous: (currentField) => {
    const fieldOrder = ['firstName', 'lastName', 'email', 'phone'];
    const currentIndex = fieldOrder.indexOf(currentField);
    const previousField = fieldOrder[currentIndex - 1];
    if (previousField) {
      form.focus(previousField);
    }
  }
};

Form Lifecycle Management

Methods for managing the form's lifecycle including initialization, reset, and restart operations.

interface FormApi<FormValues, InitialFormValues> {
  /** Initialize form with new values */
  initialize(
    data: InitialFormValues | ((values: FormValues) => InitialFormValues)
  ): void;
  
  /** Reset form to initial values */
  reset(initialValues?: InitialFormValues): void;
  
  /** Restart form (reset + clear history) */
  restart(initialValues?: InitialFormValues): void;
}

Usage Examples:

// Initialize form with user data
const loadUserData = async (userId) => {
  const userData = await fetchUserData(userId);
  
  form.initialize({
    firstName: userData.firstName,
    lastName: userData.lastName,
    email: userData.email,
    dateOfBirth: userData.dateOfBirth
  });
};

// Initialize with computed values
form.initialize((currentValues) => ({
  ...currentValues,
  fullName: `${currentValues.firstName} ${currentValues.lastName}`,
  slug: generateSlug(currentValues.firstName, currentValues.lastName)
}));

// Reset form to original state
const handleReset = () => {
  if (confirm('Are you sure you want to reset all changes?')) {
    form.reset(); // Reset to original initialValues
  }
};

// Reset with new initial values
const handleResetWithNewData = () => {
  form.reset({
    firstName: 'New',
    lastName: 'Values',
    email: 'new@example.com'
  });
};

// Restart form (clears all history including dirty, touched, etc.)
const handleRestart = () => {
  form.restart(); // Complete fresh start
};

// Restart with new initial values
const handleRestartWithNewValues = () => {
  form.restart({
    firstName: '',
    lastName: '',
    email: '',
    preferences: {
      theme: 'dark',
      notifications: true
    }
  });
};

Form Submission

Methods for handling form submission programmatically.

interface FormApi<FormValues, InitialFormValues> {
  /** Submit the form programmatically */
  submit(): Promise<FormValues | undefined> | undefined;
}

Usage Examples:

// Programmatic submission
const handleSubmitButton = async () => {
  try {
    const result = await form.submit();
    if (result) {
      console.log('Form submitted successfully:', result);
      showSuccessMessage('Form submitted!');
    } else {
      console.log('Form submission failed due to validation errors');
      focusFirstError();
    }
  } catch (error) {
    console.error('Submission error:', error);
    showErrorMessage('Submission failed');
  }
};

// Conditional submission
const handleConditionalSubmit = async () => {
  const state = form.getState();
  
  if (!state.valid) {
    showErrorMessage('Please fix validation errors before submitting');
    focusFirstError();
    return;
  }
  
  if (state.submitting) {
    console.log('Form is already being submitted');
    return;
  }
  
  await form.submit();
};

// Auto-save functionality
let autoSaveTimer;
const setupAutoSave = () => {
  form.subscribe(
    (state) => {
      if (state.dirty && state.valid) {
        clearTimeout(autoSaveTimer);
        autoSaveTimer = setTimeout(() => {
          form.submit();
        }, 2000); // Auto-save after 2 seconds of inactivity
      }
    },
    { dirty: true, valid: true }
  );
};

Configuration Management

Methods for updating form configuration at runtime.

interface FormApi<FormValues, InitialFormValues> {
  /** Update a specific configuration option */
  setConfig<K extends ConfigKey>(
    name: K,
    value: Config<FormValues, InitialFormValues>[K]
  ): void;
  
  /** Set custom callback scheduler */
  setCallbackScheduler(scheduler?: (callback: () => void) => void): void;
}

type ConfigKey = 
  | "debug"
  | "destroyOnUnregister"
  | "initialValues"
  | "keepDirtyOnReinitialize"
  | "mutators"
  | "onSubmit"
  | "validate"
  | "validateOnBlur"
  | "callbackScheduler";

Usage Examples:

// Update validation function at runtime
const updateValidation = (strictMode) => {
  if (strictMode) {
    form.setConfig('validate', (values) => {
      const errors = {};
      
      // Strict validation rules
      if (!values.firstName?.trim()) errors.firstName = 'First name is required';
      if (!values.lastName?.trim()) errors.lastName = 'Last name is required';
      if (!values.email?.trim()) errors.email = 'Email is required';
      if (!values.phone?.trim()) errors.phone = 'Phone is required';
      
      return errors;
    });
  } else {
    form.setConfig('validate', (values) => {
      const errors = {};
      
      // Lenient validation rules
      if (!values.email?.trim()) errors.email = 'Email is required';
      
      return errors;
    });
  }
};

// Toggle validation on blur
const toggleValidateOnBlur = (enabled) => {
  form.setConfig('validateOnBlur', enabled);
};

// Update submit handler
const updateSubmitHandler = (newHandler) => {
  form.setConfig('onSubmit', newHandler);
};

// Toggle debug mode
const toggleDebugMode = (enabled) => {
  if (enabled) {
    form.setConfig('debug', (formState, fieldStates) => {
      console.log('Form State:', formState);
      console.log('Field States:', fieldStates);
    });
  } else {
    form.setConfig('debug', undefined);
  }
};

// Custom callback scheduler for React 18 concurrent features
const setupReactScheduler = () => {
  form.setCallbackScheduler((callback) => {
    // Use React's scheduler for better performance
    if (typeof window !== 'undefined' && window.scheduler?.postTask) {
      window.scheduler.postTask(callback, { priority: 'user-blocking' });
    } else {
      // Fallback to immediate execution
      callback();
    }
  });
};

// Remove custom scheduler
const removeCustomScheduler = () => {
  form.setCallbackScheduler(undefined);
};

Batch Operations

Methods for batching multiple operations to prevent excessive notifications.

interface FormApi<FormValues, InitialFormValues> {
  /** Batch multiple operations to prevent multiple notifications */
  batch(fn: () => void): void;
}

Usage Examples:

// Batch multiple field changes
const populateUserForm = (userData) => {
  form.batch(() => {
    form.change('firstName', userData.firstName);
    form.change('lastName', userData.lastName);
    form.change('email', userData.email);
    form.change('phone', userData.phone);
    form.change('address', userData.address);
    form.change('city', userData.city);
    form.change('state', userData.state);
    form.change('zipCode', userData.zipCode);
  }); // Only one notification sent after all changes
};

// Batch form operations
const resetAndPopulate = (newData) => {
  form.batch(() => {
    form.reset(); // Reset form
    Object.keys(newData).forEach(key => {
      form.change(key, newData[key]); // Set new values
    });
  });
};

// Batch with focus management
const setupFormForEditing = (data) => {
  form.batch(() => {
    form.initialize(data);
    form.focus('firstName'); // Focus first field
  });
};

// Complex batch operation with conditional logic
const handleBulkUpdate = (updates) => {
  form.batch(() => {
    updates.forEach(({ field, value, condition }) => {
      if (!condition || condition(form.getState().values)) {
        form.change(field, value);
      }
    });
    
    // Focus first field that has an error after updates
    const state = form.getState();
    if (state.errors) {
      const firstErrorField = Object.keys(state.errors)[0];
      if (firstErrorField) {
        form.focus(firstErrorField);
      }
    }
  });
};