Utility functions and constants for deep object manipulation, form state management, and error handling in Final Form applications.
Utility functions for safely getting and setting nested object properties using dot notation and array syntax.
/**
* Safely retrieves nested values from objects using dot notation or array syntax
* @param state - Object to retrieve value from
* @param complexKey - Path to the value using dot notation (e.g., "user.address.city" or "items[0].name")
* @returns The value at the specified path, or undefined if path doesn't exist
*/
function getIn(state: object, complexKey: string): any;
/**
* Immutably sets nested values in objects using dot notation or array syntax
* @param state - Object to set value in
* @param key - Path where to set the value using dot notation (e.g., "user.address.city" or "items[0].name")
* @param value - Value to set at the specified path
* @param destroyArrays - Whether to remove array elements when setting undefined (default: false)
* @returns New object with the value set at the specified path
*/
function setIn(
state: object,
key: string,
value: any,
destroyArrays?: boolean
): object;Usage Examples:
import { getIn, setIn } from "final-form";
// Getting nested values
const user = {
profile: {
name: 'John Doe',
address: {
city: 'New York',
coordinates: [40.7128, -74.0060]
}
},
preferences: {
theme: 'dark',
notifications: true
}
};
// Simple nested access
const userName = getIn(user, 'profile.name'); // 'John Doe'
const city = getIn(user, 'profile.address.city'); // 'New York'
const theme = getIn(user, 'preferences.theme'); // 'dark'
// Array access
const latitude = getIn(user, 'profile.address.coordinates[0]'); // 40.7128
const longitude = getIn(user, 'profile.address.coordinates[1]'); // -74.0060
// Safe access - returns undefined for non-existent paths
const missing = getIn(user, 'profile.address.country'); // undefined
const deepMissing = getIn(user, 'profile.social.twitter.handle'); // undefined
// Setting nested values
const initialState = {
users: [],
config: {
theme: 'light'
}
};
// Set simple nested value
const newState1 = setIn(initialState, 'config.theme', 'dark');
// Result: { users: [], config: { theme: 'dark' } }
// Set array values
const newState2 = setIn(newState1, 'users[0]', { id: 1, name: 'Alice' });
// Result: { users: [{ id: 1, name: 'Alice' }], config: { theme: 'dark' } }
// Set nested object in array
const newState3 = setIn(newState2, 'users[0].profile.email', 'alice@example.com');
// Result: { users: [{ id: 1, name: 'Alice', profile: { email: 'alice@example.com' } }], config: { theme: 'dark' } }
// Remove values by setting undefined
const newState4 = setIn(newState3, 'config.theme', undefined);
// Result: { users: [...], config: {} }
// Destroy arrays when setting undefined
const arrayState = { items: ['a', 'b', 'c'] };
const withoutDestroy = setIn(arrayState, 'items[1]', undefined, false);
// Result: { items: ['a', undefined, 'c'] }
const withDestroy = setIn(arrayState, 'items[1]', undefined, true);
// Result: { items: ['a', 'c'] }Special constants for handling form-level and array-level errors.
/** Special key for form-level errors */
const FORM_ERROR: string;
/** Special key for array field errors */
const ARRAY_ERROR: string;Usage Examples:
import { createForm, FORM_ERROR, ARRAY_ERROR } from "final-form";
// Form-level error handling
const form = createForm({
onSubmit: async (values) => {
try {
await submitUserData(values);
} catch (error) {
if (error.code === 'DUPLICATE_EMAIL') {
// Return field-specific error
return { email: 'Email address is already registered' };
} else if (error.code === 'SERVER_ERROR') {
// Return form-level error
return { [FORM_ERROR]: 'Server error occurred. Please try again later.' };
} else {
// Generic form-level error
return { [FORM_ERROR]: 'An unexpected error occurred' };
}
}
},
validate: (values) => {
const errors = {};
// Field validation
if (!values.email) {
errors.email = 'Email is required';
}
// Business logic validation with form-level error
if (values.age < 18 && !values.parentalConsent) {
errors[FORM_ERROR] = 'Parental consent is required for users under 18';
}
return errors;
}
});
// Array field error handling
const arrayForm = createForm({
onSubmit: (values) => console.log(values),
validate: (values) => {
const errors = {};
if (values.items && Array.isArray(values.items)) {
// Validate individual array items
const itemErrors = values.items.map((item, index) => {
const itemError = {};
if (!item.name) itemError.name = 'Name is required';
if (!item.price || item.price <= 0) itemError.price = 'Price must be positive';
return Object.keys(itemError).length > 0 ? itemError : undefined;
});
if (itemErrors.some(error => error)) {
errors.items = itemErrors;
}
// Array-level validation
if (values.items.length === 0) {
errors.items = { [ARRAY_ERROR]: 'At least one item is required' };
} else if (values.items.length > 10) {
errors.items = { [ARRAY_ERROR]: 'Maximum 10 items allowed' };
}
}
return errors;
}
});
// Error display helper
const displayErrors = (formState) => {
const { error, errors } = formState;
// Display form-level error
if (error) {
showFormLevelError(error);
}
// Display field errors
if (errors) {
Object.keys(errors).forEach(fieldName => {
if (fieldName === FORM_ERROR) {
showFormLevelError(errors[fieldName]);
} else if (Array.isArray(errors[fieldName])) {
// Handle array field errors
errors[fieldName].forEach((itemError, index) => {
if (itemError) {
Object.keys(itemError).forEach(itemFieldName => {
if (itemFieldName === ARRAY_ERROR) {
showArrayLevelError(fieldName, itemError[itemFieldName]);
} else {
showFieldError(`${fieldName}[${index}].${itemFieldName}`, itemError[itemFieldName]);
}
});
}
});
} else if (typeof errors[fieldName] === 'object' && errors[fieldName][ARRAY_ERROR]) {
showArrayLevelError(fieldName, errors[fieldName][ARRAY_ERROR]);
} else {
showFieldError(fieldName, errors[fieldName]);
}
});
}
};Arrays containing all available subscription properties for form and field subscriptions.
/** Array of all available form subscription properties */
const formSubscriptionItems: readonly string[];
/** Array of all available field subscription properties */
const fieldSubscriptionItems: readonly string[];Usage Examples:
import { formSubscriptionItems, fieldSubscriptionItems } from "final-form";
// Create subscription objects programmatically
const createFullFormSubscription = () => {
return formSubscriptionItems.reduce((subscription, item) => {
subscription[item] = true;
return subscription;
}, {});
};
const createFullFieldSubscription = () => {
return fieldSubscriptionItems.reduce((subscription, item) => {
subscription[item] = true;
return subscription;
}, {});
};
// Create selective subscriptions
const createValidationSubscription = () => {
const validationItems = ['valid', 'invalid', 'errors', 'validating'];
return validationItems.reduce((subscription, item) => {
if (formSubscriptionItems.includes(item)) {
subscription[item] = true;
}
return subscription;
}, {});
};
const createSubmissionSubscription = () => {
const submissionItems = ['submitting', 'submitSucceeded', 'submitFailed', 'submitError'];
return submissionItems.reduce((subscription, item) => {
if (formSubscriptionItems.includes(item)) {
subscription[item] = true;
}
return subscription;
}, {});
};
// Subscription utilities
const createConditionalSubscription = (conditions) => {
const subscription = {};
formSubscriptionItems.forEach(item => {
if (conditions[item]) {
subscription[item] = true;
}
});
return subscription;
};
// Example usage
const validationOnlySubscription = createConditionalSubscription({
valid: true,
invalid: true,
errors: true,
validating: true
});
const dirtyStateSubscription = createConditionalSubscription({
dirty: true,
pristine: true,
dirtySinceLastSubmit: true
});
// Field subscription utilities
const createFieldValidationSubscription = () => {
const validationItems = ['valid', 'invalid', 'error', 'validating'];
return validationItems.reduce((subscription, item) => {
if (fieldSubscriptionItems.includes(item)) {
subscription[item] = true;
}
return subscription;
}, {});
};
const createFieldInteractionSubscription = () => {
const interactionItems = ['active', 'touched', 'visited'];
return interactionItems.reduce((subscription, item) => {
if (fieldSubscriptionItems.includes(item)) {
subscription[item] = true;
}
return subscription;
}, {});
};Array of valid configuration option keys for runtime validation.
/** Array of valid configuration option keys */
const configOptions: ConfigKey[];
type ConfigKey =
| "debug"
| "destroyOnUnregister"
| "initialValues"
| "keepDirtyOnReinitialize"
| "mutators"
| "onSubmit"
| "validate"
| "validateOnBlur"
| "callbackScheduler";Usage Example:
import { configOptions } from "final-form";
// Validate configuration keys
const validateConfig = (config) => {
const invalidKeys = Object.keys(config).filter(key =>
!configOptions.includes(key)
);
if (invalidKeys.length > 0) {
console.warn('Invalid configuration keys:', invalidKeys);
}
return invalidKeys.length === 0;
};
// Dynamic configuration
const createDynamicConfig = (options) => {
const config = {};
configOptions.forEach(key => {
if (options[key] !== undefined) {
config[key] = options[key];
}
});
return config;
};Current version of the Final Form library.
/** Current version of Final Form */
const version: string;Usage Example:
import { version } from "final-form";
console.log('Final Form version:', version); // "5.0.0-1"
// Version-based feature detection
const supportsNewFeatures = version >= '5.0.0';
// Debug information
const getLibraryInfo = () => ({
library: 'final-form',
version: version,
timestamp: new Date().toISOString()
});