Build forms in React, without the tears
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Components and utilities for managing dynamic arrays of form fields with helper methods for manipulation.
Component for managing dynamic arrays of form fields with comprehensive manipulation methods.
/**
* Component for managing dynamic arrays of fields
* @param props - FieldArray configuration
* @returns JSX element with array manipulation capabilities
*/
function FieldArray(props: FieldArrayConfig): JSX.Element;
interface FieldArrayConfig {
/** Name of the array field in form values */
name: string;
/** Children render function */
children?: (props: FieldArrayRenderProps) => React.ReactNode;
/** Component to render */
component?: React.ComponentType<FieldArrayRenderProps>;
/** Render prop function */
render?: (props: FieldArrayRenderProps) => React.ReactNode;
}
interface FieldArrayRenderProps extends ArrayHelpers {
/** Current form object */
form: FormikProps<any>;
/** Field name */
name: string;
/** Helper functions for array manipulation */
push: (obj: any) => void;
swap: (indexA: number, indexB: number) => void;
move: (from: number, to: number) => void;
insert: (index: number, value: any) => void;
unshift: (value: any) => number;
remove: (index: number) => any | undefined;
pop: () => any | undefined;
replace: (index: number, value: any) => void;
}Usage Examples:
import { Formik, Form, Field, FieldArray } from "formik";
// Basic array management
const TodoList = () => (
<Formik
initialValues={{ todos: [''] }}
onSubmit={values => console.log(values)}
>
<Form>
<FieldArray name="todos">
{({ insert, remove, push }) => (
<div>
<button
type="button"
onClick={() => push('')}
>
Add Todo
</button>
{values.todos.length > 0 &&
values.todos.map((todo, index) => (
<div key={index}>
<Field name={`todos.${index}`} />
<button
type="button"
onClick={() => remove(index)}
>
Remove
</button>
</div>
))}
</div>
)}
</FieldArray>
<button type="submit">Submit</button>
</Form>
</Formik>
);
// Complex nested objects
const ContactForm = () => (
<Formik
initialValues={{
contacts: [{ name: '', email: '', phone: '' }]
}}
onSubmit={values => console.log(values)}
>
{({ values }) => (
<Form>
<FieldArray name="contacts">
{({ push, remove }) => (
<div>
{values.contacts.map((contact, index) => (
<div key={index}>
<Field name={`contacts.${index}.name`} placeholder="Name" />
<Field name={`contacts.${index}.email`} placeholder="Email" />
<Field name={`contacts.${index}.phone`} placeholder="Phone" />
<button type="button" onClick={() => remove(index)}>
Remove Contact
</button>
</div>
))}
<button
type="button"
onClick={() => push({ name: '', email: '', phone: '' })}
>
Add Contact
</button>
</div>
)}
</FieldArray>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);
// With validation
const ValidatedList = () => (
<Formik
initialValues={{ items: [''] }}
validate={values => {
const errors = {};
if (!values.items || values.items.length === 0) {
errors.items = 'At least one item is required';
} else {
const itemErrors = [];
values.items.forEach((item, index) => {
if (!item) {
itemErrors[index] = 'Item is required';
} else if (item.length < 3) {
itemErrors[index] = 'Item must be at least 3 characters';
}
});
if (itemErrors.length) {
errors.items = itemErrors;
}
}
return errors;
}}
onSubmit={values => console.log(values)}
>
{({ values, errors, touched }) => (
<Form>
<FieldArray name="items">
{({ push, remove }) => (
<div>
{values.items.map((item, index) => (
<div key={index}>
<Field name={`items.${index}`} />
{errors.items && errors.items[index] && touched.items && touched.items[index] && (
<div>{errors.items[index]}</div>
)}
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
))}
<button type="button" onClick={() => push('')}>
Add Item
</button>
</div>
)}
</FieldArray>
{typeof errors.items === 'string' && <div>{errors.items}</div>}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);Helper methods available for array field manipulation.
interface ArrayHelpers<T = any> {
/** Add item to end of array */
push: (obj: T) => void;
/** Remove and return last item from array */
pop: () => T | undefined;
/** Remove and return item at index */
remove: (index: number) => T | undefined;
/** Insert item at specific index */
insert: (index: number, value: T) => void;
/** Add item to beginning of array */
unshift: (value: T) => number;
/** Move item from one index to another */
move: (from: number, to: number) => void;
/** Swap items at two indices */
swap: (indexA: number, indexB: number) => void;
/** Replace item at index with new value */
replace: (index: number, value: T) => void;
}Standalone utility functions for array manipulation that can be used outside of FieldArray context.
/**
* Move array element from one index to another
* @param array - Source array to modify
* @param from - Source index
* @param to - Destination index
* @returns New array with element moved
*/
function move<T>(array: T[], from: number, to: number): T[];
/**
* Swap two elements in an array
* @param array - Source array to modify
* @param indexA - First element index
* @param indexB - Second element index
* @returns New array with elements swapped
*/
function swap<T>(array: T[], indexA: number, indexB: number): T[];
/**
* Insert element at specific index
* @param array - Source array to modify
* @param index - Index to insert at
* @param value - Value to insert
* @returns New array with element inserted
*/
function insert<T>(array: T[], index: number, value: T): T[];
/**
* Replace element at specific index
* @param array - Source array to modify
* @param index - Index to replace at
* @param value - New value
* @returns New array with element replaced
*/
function replace<T>(array: T[], index: number, value: T): T[];Usage Examples:
import { move, swap, insert, replace } from "formik";
// Standalone usage outside of Formik
const items = ['a', 'b', 'c', 'd'];
// Move element from index 1 to index 3
const moved = move(items, 1, 3); // ['a', 'c', 'd', 'b']
// Swap elements at indices 0 and 2
const swapped = swap(items, 0, 2); // ['c', 'b', 'a', 'd']
// Insert element at index 2
const inserted = insert(items, 2, 'x'); // ['a', 'b', 'x', 'c', 'd']
// Replace element at index 1
const replaced = replace(items, 1, 'y'); // ['a', 'y', 'c', 'd']
// Using with useFormik
const ListManager = () => {
const formik = useFormik({
initialValues: { items: ['item1', 'item2', 'item3'] },
onSubmit: values => console.log(values),
});
const handleMoveUp = (index) => {
if (index > 0) {
const newItems = move(formik.values.items, index, index - 1);
formik.setFieldValue('items', newItems);
}
};
const handleMoveDown = (index) => {
if (index < formik.values.items.length - 1) {
const newItems = move(formik.values.items, index, index + 1);
formik.setFieldValue('items', newItems);
}
};
return (
<form onSubmit={formik.handleSubmit}>
{formik.values.items.map((item, index) => (
<div key={index}>
<input
name={`items.${index}`}
value={item}
onChange={formik.handleChange}
/>
<button type="button" onClick={() => handleMoveUp(index)}>
↑
</button>
<button type="button" onClick={() => handleMoveDown(index)}>
↓
</button>
</div>
))}
<button type="submit">Submit</button>
</form>
);
};Complex array manipulation patterns and nested array handling.
Nested Arrays:
const NestedArrayForm = () => (
<Formik
initialValues={{
sections: [
{
title: '',
items: ['']
}
]
}}
onSubmit={values => console.log(values)}
>
{({ values }) => (
<Form>
<FieldArray name="sections">
{({ push: pushSection, remove: removeSection }) => (
<div>
{values.sections.map((section, sectionIndex) => (
<div key={sectionIndex}>
<Field name={`sections.${sectionIndex}.title`} placeholder="Section Title" />
<FieldArray name={`sections.${sectionIndex}.items`}>
{({ push: pushItem, remove: removeItem }) => (
<div>
{section.items.map((item, itemIndex) => (
<div key={itemIndex}>
<Field name={`sections.${sectionIndex}.items.${itemIndex}`} />
<button type="button" onClick={() => removeItem(itemIndex)}>
Remove Item
</button>
</div>
))}
<button type="button" onClick={() => pushItem('')}>
Add Item
</button>
</div>
)}
</FieldArray>
<button type="button" onClick={() => removeSection(sectionIndex)}>
Remove Section
</button>
</div>
))}
<button type="button" onClick={() => pushSection({ title: '', items: [''] })}>
Add Section
</button>
</div>
)}
</FieldArray>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);Conditional Array Items:
const ConditionalArrayForm = () => (
<Formik
initialValues={{
enableFeatures: false,
features: []
}}
onSubmit={values => console.log(values)}
>
{({ values, setFieldValue }) => (
<Form>
<Field type="checkbox" name="enableFeatures" />
<label>Enable Features</label>
{values.enableFeatures && (
<FieldArray name="features">
{({ push, remove }) => (
<div>
{values.features.map((feature, index) => (
<div key={index}>
<Field name={`features.${index}.name`} placeholder="Feature Name" />
<Field name={`features.${index}.enabled`} type="checkbox" />
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
))}
<button
type="button"
onClick={() => push({ name: '', enabled: false })}
>
Add Feature
</button>
</div>
)}
</FieldArray>
)}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);