CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-final-form

High performance subscription-based form state management for React

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

hooks.mddocs/

Hooks API

Modern React hooks for accessing form context, field state, and form state with customizable subscriptions and full TypeScript support.

Capabilities

useForm Hook

Hook to access the form API from within form components and field components.

/**
 * Hook to access form API from React context
 * @param componentName - Optional component name for error messages
 * @returns Form API instance
 * @throws Error if used outside of Form component
 */
function useForm<FormValues = Record<string, any>>(
  componentName?: string
): FormApi<FormValues>;

Usage Examples:

import React from "react";
import { Form, useForm } from "react-final-form";

// Basic useForm usage
function CustomFormButton() {
  const form = useForm();
  
  return (
    <button
      type="button"
      onClick={() => form.reset()}
    >
      Reset Form
    </button>
  );
}

// useForm with component name for better error messages
function CustomField() {
  const form = useForm("CustomField");
  
  const handleSetValue = () => {
    form.change("myField", "new value");
  };
  
  return (
    <div>
      <button onClick={handleSetValue}>Set Value</button>
      <button onClick={() => form.focus("myField")}>Focus Field</button>
    </div>
  );
}

// Using form API for programmatic control
function FormControls() {
  const form = useForm();
  
  const handleBatch = () => {
    form.batch(() => {
      form.change("firstName", "John");
      form.change("lastName", "Doe");
      form.change("email", "john.doe@example.com");
    });
  };
  
  return (
    <div>
      <button onClick={handleBatch}>Fill Form</button>
      <button onClick={() => form.restart()}>Restart</button>
      <button onClick={() => form.reset()}>Reset</button>
    </div>
  );
}

// Complete form example with useForm
function MyForm() {
  return (
    <Form onSubmit={(values) => console.log(values)}>
      {({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <input name="firstName" />
          <CustomFormButton />
        </form>
      )}
    </Form>
  );
}

useField Hook

Hook for field-level state management that returns field input props and metadata.

/**
 * Hook for field state management and input props
 * @param name - Field name (required)
 * @param config - Field configuration options
 * @returns Field render props with input and meta
 */
function useField<
  FieldValue = any,
  T extends HTMLElement = HTMLElement,
  FormValues = Record<string, any>
>(
  name: string,
  config?: UseFieldConfig
): FieldRenderProps<FieldValue, T, FormValues>;

interface UseFieldConfig extends UseFieldAutoConfig {
  /** Field state subscription configuration */
  subscription?: FieldSubscription;
}

interface FieldRenderProps<
  FieldValue = any,
  T extends HTMLElement = HTMLElement,
  FormValues = any
> {
  /** Input props to spread on form elements */
  input: FieldInputProps<FieldValue, T>;
  /** Field metadata and state information */
  meta: FieldMeta;
}

Usage Examples:

import React from "react";
import { Form, useField } from "react-final-form";

// Basic useField usage
function CustomTextField({ name, ...props }: { name: string }) {
  const { input, meta } = useField(name);
  
  return (
    <div>
      <input {...input} {...props} />
      {meta.error && meta.touched && <span>{meta.error}</span>}
    </div>
  );
}

// useField with validation
function ValidatedInput({ name, validate, ...props }: any) {
  const { input, meta } = useField(name, { validate });
  
  return (
    <div className={meta.error ? "error" : ""}>
      <input {...input} {...props} />
      {meta.error && meta.touched && (
        <div className="error-message">{meta.error}</div>
      )}
      {meta.validating && <div className="validating">Validating...</div>}
    </div>
  );
}

// useField with formatting and parsing
function CurrencyInput({ name }: { name: string }) {
  const format = (value: number) => {
    if (value === undefined) return "";
    return `$${value.toFixed(2)}`;
  };
  
  const parse = (value: string) => {
    const number = parseFloat(value.replace(/[^0-9.-]/g, ""));
    return isNaN(number) ? undefined : number;
  };
  
  const { input, meta } = useField(name, { format, parse });
  
  return (
    <div>
      <input {...input} type="text" placeholder="$0.00" />
      {meta.error && meta.touched && <span>{meta.error}</span>}
    </div>
  );
}

// useField with subscription optimization
function OptimizedField({ name }: { name: string }) {
  const { input, meta } = useField(name, {
    subscription: { value: true, error: true, touched: true }
  });
  
  return (
    <div>
      <input {...input} />
      {meta.error && meta.touched && <span>{meta.error}</span>}
    </div>
  );
}

// Custom checkbox component
function CustomCheckbox({ name, label }: { name: string; label: string }) {
  const { input, meta } = useField(name, { type: "checkbox" });
  
  return (
    <label>
      <input {...input} type="checkbox" />
      {label}
    </label>
  );
}

// Form using custom field components
function MyForm() {
  const required = (value: any) => (value ? undefined : "Required");
  
  return (
    <Form onSubmit={(values) => console.log(values)}>
      {({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <CustomTextField name="firstName" placeholder="First Name" />
          <ValidatedInput
            name="email"
            type="email"
            validate={required}
            placeholder="Email"
          />
          <CurrencyInput name="salary" />
          <CustomCheckbox name="subscribe" label="Subscribe to newsletter" />
          <button type="submit">Submit</button>
        </form>
      )}
    </Form>
  );
}

useFormState Hook

Hook for subscribing to form state changes with customizable subscriptions.

/**
 * Hook for form state subscription with performance optimization
 * @param params - Configuration for form state subscription
 * @returns Current form state based on subscription
 */
function useFormState<FormValues = Record<string, any>>(
  params?: UseFormStateParams<FormValues>
): FormState<FormValues>;

interface UseFormStateParams<FormValues = Record<string, any>> {
  /** Callback when form state changes */
  onChange?: (formState: FormState<FormValues>) => void;
  /** Form state subscription configuration */
  subscription?: FormSubscription;
}

Usage Examples:

import React from "react";
import { Form, Field, useFormState } from "react-final-form";

// Basic useFormState usage
function FormStatus() {
  const { dirty, invalid, submitting } = useFormState();
  
  return (
    <div>
      <p>Form is {dirty ? "dirty" : "pristine"}</p>
      <p>Form is {invalid ? "invalid" : "valid"}</p>
      {submitting && <p>Submitting...</p>}
    </div>
  );
}

// useFormState with subscription
function OptimizedFormStatus() {
  const { submitting, pristine, invalid } = useFormState({
    subscription: { submitting: true, pristine: true, invalid: true }
  });
  
  return (
    <button type="submit" disabled={submitting || pristine || invalid}>
      {submitting ? "Submitting..." : "Submit"}
    </button>
  );
}

// useFormState with onChange callback
function FormMonitor() {
  const formState = useFormState({
    onChange: (state) => {
      console.log("Form state changed:", state);
      // Auto-save, analytics, etc.
    }
  });
  
  return (
    <div>
      <h3>Form Monitor</h3>
      <pre>{JSON.stringify(formState, null, 2)}</pre>
    </div>
  );
}

// useFormState for conditional rendering
function ConditionalFields() {
  const { values } = useFormState({ subscription: { values: true } });
  
  return (
    <div>
      {values.showAdvanced && (
        <div>
          <Field name="advancedOption1" component="input" />
          <Field name="advancedOption2" component="input" />
        </div>
      )}
    </div>
  );
}

// Complete form with hooks
function HookBasedForm() {
  return (
    <Form onSubmit={(values) => console.log(values)}>
      {({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <Field name="name" component="input" placeholder="Name" />
          <Field name="showAdvanced" component="input" type="checkbox" />
          
          <ConditionalFields />
          <FormStatus />
          <FormMonitor />
          
          <button type="submit">Submit</button>
        </form>
      )}
    </Form>
  );
}

Hook Configuration Options

All hooks support comprehensive configuration options for optimization and customization.

/**
 * Field subscription configuration for useField hook
 */
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;
}

/**
 * Form subscription configuration for useFormState hook
 */
interface FormSubscription {
  active?: boolean;
  dirty?: boolean;
  dirtyFields?: boolean;
  dirtySinceLastSubmit?: boolean;
  error?: boolean;
  errors?: boolean;
  hasSubmitErrors?: boolean;
  hasValidationErrors?: boolean;
  initialValues?: boolean;
  invalid?: boolean;
  modified?: boolean;
  modifiedSinceLastSubmit?: boolean;
  pristine?: boolean;
  submitError?: boolean;
  submitErrors?: boolean;
  submitFailed?: boolean;
  submitSucceeded?: boolean;
  submitting?: boolean;
  touched?: boolean;
  valid?: boolean;
  validating?: boolean;
  values?: boolean;
  visited?: boolean;
}

Hook Error Handling

Hooks provide clear error messages when used incorrectly.

Usage Examples:

// Error handling example
function SafeHookUsage() {
  try {
    const form = useForm("SafeComponent");
    return <div>Form API available</div>;
  } catch (error) {
    return <div>Error: Component must be used inside a Form</div>;
  }
}

// Conditional hook usage (NOT RECOMMENDED - breaks rules of hooks)
// Instead, always use hooks at the top level and handle conditions in render
function CorrectConditionalUsage() {
  const form = useForm(); // Always call hooks at top level
  const { values } = useFormState();
  
  if (!values.needsField) {
    return <div>Field not needed</div>;
  }
  
  // Use hook results conditionally, not the hooks themselves
  return (
    <div>
      <button onClick={() => form.reset()}>Reset</button>
    </div>
  );
}

Performance Optimization with Hooks

Hooks support performance optimization through subscriptions and memoization.

Usage Examples:

import React from "react";

// Optimized field component
function OptimizedField({ name }: { name: string }) {
  const { input, meta } = useField(name, {
    subscription: { value: true, error: true, touched: true }
  });
  
  return React.useMemo(() => (
    <div>
      <input {...input} />
      {meta.error && meta.touched && <span>{meta.error}</span>}
    </div>
  ), [input.value, meta.error, meta.touched]);
}

// Memoized form status component
const FormStatusMemo = React.memo(() => {
  const { dirty, invalid, submitting } = useFormState({
    subscription: { dirty: true, invalid: true, submitting: true }
  });
  
  return (
    <div>
      Status: {dirty ? "Modified" : "Unchanged"} | 
      {invalid ? "Invalid" : "Valid"} |
      {submitting ? "Submitting" : "Ready"}
    </div>
  );
});

Install with Tessl CLI

npx tessl i tessl/npm-react-final-form

docs

field.md

form-spy.md

form.md

hooks.md

index.md

typescript.md

tile.json