High performance subscription-based form state management for React
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Modern React hooks for accessing form context, field state, and form state with customizable subscriptions and full TypeScript support.
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>
);
}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>
);
}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>
);
}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;
}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>
);
}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