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

form-spy.mddocs/

Form Spy

FormSpy component for observing form state changes without rendering form fields, perfect for external components that need form state updates.

Capabilities

FormSpy Component

Component that subscribes to form state changes and renders UI based on form state without rendering actual form fields.

/**
 * Component for observing form state without rendering form fields
 * @param props - FormSpy configuration and render props
 * @returns React element or null
 */
const FormSpy: <FormValues = Record<string, any>>(
  props: FormSpyProps<FormValues>
) => React.ReactElement;

interface FormSpyProps<FormValues = Record<string, any>>
  extends UseFormStateParams<FormValues>,
    RenderableProps<FormSpyRenderProps<FormValues>> {}

interface FormSpyRenderProps<FormValues = Record<string, any>>
  extends FormState<FormValues> {
  /** Form API instance for programmatic control */
  form: FormApi<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, FormSpy } from "react-final-form";

// Basic FormSpy usage
function BasicFormSpy() {
  return (
    <Form onSubmit={(values) => console.log(values)}>
      {({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <Field name="firstName" component="input" />
          
          <FormSpy>
            {({ values, dirty, invalid }) => (
              <div>
                <p>Form is {dirty ? "dirty" : "pristine"}</p>
                <p>Form is {invalid ? "invalid" : "valid"}</p>
                <pre>{JSON.stringify(values, null, 2)}</pre>
              </div>
            )}
          </FormSpy>
        </form>
      )}
    </Form>
  );
}

// FormSpy with subscription
function OptimizedFormSpy() {
  return (
    <Form onSubmit={(values) => console.log(values)}>
      {({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <Field name="email" component="input" type="email" />
          
          <FormSpy subscription={{ values: true, submitting: true }}>
            {({ values, submitting }) => (
              <div>
                {submitting && <p>Submitting...</p>}
                <p>Email: {values.email}</p>
              </div>
            )}
          </FormSpy>
        </form>
      )}
    </Form>
  );
}

// FormSpy with onChange callback
function CallbackFormSpy() {
  const handleFormChange = (state: any) => {
    console.log("Form state changed:", state);
    // Save to localStorage, send analytics, etc.
  };

  return (
    <Form onSubmit={(values) => console.log(values)}>
      {({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <Field name="username" component="input" />
          
          <FormSpy onChange={handleFormChange} />
        </form>
      )}
    </Form>
  );
}

FormSpy with Form Control

FormSpy provides access to the form API for programmatic form control.

/**
 * FormSpy render props include form API for programmatic control
 */
interface FormSpyRenderProps<FormValues = Record<string, any>>
  extends FormState<FormValues> {
  /** Complete form API instance */
  form: FormApi<FormValues>;
}

Usage Examples:

// FormSpy with form control buttons
function FormControlSpy() {
  return (
    <Form onSubmit={(values) => console.log(values)}>
      {({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <Field name="message" component="textarea" />
          
          <FormSpy>
            {({ form, pristine, invalid, values }) => (
              <div>
                <button
                  type="button"
                  onClick={() => form.reset()}
                  disabled={pristine}
                >
                  Reset Form
                </button>
                
                <button
                  type="button"
                  onClick={() => form.change("message", "Hello World")}
                >
                  Set Default Message
                </button>
                
                <button
                  type="button"
                  onClick={() => form.focus("message")}
                >
                  Focus Message
                </button>
                
                <pre>{JSON.stringify(values, null, 2)}</pre>
              </div>
            )}
          </FormSpy>
        </form>
      )}
    </Form>
  );
}

// FormSpy for conditional rendering
function ConditionalFormSpy() {
  return (
    <Form onSubmit={(values) => console.log(values)}>
      {({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <Field name="accountType">
            {({ input }) => (
              <select {...input}>
                <option value="">Select Account Type</option>
                <option value="personal">Personal</option>
                <option value="business">Business</option>
              </select>
            )}
          </Field>
          
          <FormSpy subscription={{ values: true }}>
            {({ values }) => (
              <>
                {values.accountType === "business" && (
                  <Field name="companyName" component="input" placeholder="Company Name" />
                )}
                {values.accountType === "personal" && (
                  <Field name="dateOfBirth" component="input" type="date" />
                )}
              </>
            )}
          </FormSpy>
        </form>
      )}
    </Form>
  );
}

FormSpy Subscription Configuration

FormSpy supports the same subscription configuration as Form components for performance optimization.

/**
 * FormSpy subscription options for optimized rendering
 */
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;
}

Usage Example:

// Optimized FormSpy with minimal subscriptions
function MinimalFormSpy() {
  return (
    <Form onSubmit={(values) => console.log(values)}>
      {({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <Field name="search" component="input" type="search" />
          
          {/* Only re-renders when values change */}
          <FormSpy subscription={{ values: true }}>
            {({ values }) => (
              <div>
                Search results for: {values.search}
              </div>
            )}
          </FormSpy>
          
          {/* Only re-renders when form state changes */}
          <FormSpy subscription={{ pristine: true, invalid: true, submitting: true }}>
            {({ pristine, invalid, submitting }) => (
              <button type="submit" disabled={submitting || pristine || invalid}>
                {submitting ? "Searching..." : "Search"}
              </button>
            )}
          </FormSpy>
        </form>
      )}
    </Form>
  );
}

External Form State Monitoring

FormSpy can be used outside of form render functions to monitor form state from parent components.

Usage Example:

// External form state monitoring
function ExternalMonitor() {
  const [formState, setFormState] = React.useState<any>(null);

  return (
    <div>
      <div>
        External form monitor:
        <pre>{JSON.stringify(formState, null, 2)}</pre>
      </div>
      
      <Form onSubmit={(values) => console.log(values)}>
        {({ handleSubmit }) => (
          <form onSubmit={handleSubmit}>
            <Field name="data" component="input" />
            
            <FormSpy onChange={setFormState} />
            
            <button type="submit">Submit</button>
          </form>
        )}
      </Form>
    </div>
  );
}

Performance Considerations

FormSpy is optimized for performance with subscription-based updates and minimal re-rendering.

/**
 * FormSpy performance characteristics:
 * - Only re-renders when subscribed form state changes
 * - Supports granular subscriptions for optimal performance
 * - Can be used for onChange callbacks without rendering
 * - Minimal memory footprint with automatic cleanup
 */

Usage Example:

// Performance-optimized FormSpy usage
function PerformantFormSpy() {
  return (
    <Form onSubmit={(values) => console.log(values)}>
      {({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <Field name="title" component="input" />
          <Field name="content" component="textarea" />
          
          {/* Separate spies for different concerns */}
          <FormSpy subscription={{ submitting: true }}>
            {({ submitting }) => submitting && <div>Saving...</div>}
          </FormSpy>
          
          <FormSpy subscription={{ values: true }}>
            {({ values }) => (
              <div>Character count: {(values.content || "").length}</div>
            )}
          </FormSpy>
          
          {/* Silent monitoring without render */}
          <FormSpy
            onChange={(state) => {
              // Auto-save draft every 30 seconds
              if (state.dirty) {
                console.log("Auto-saving draft...");
              }
            }}
          />
        </form>
      )}
    </Form>
  );
}

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