List
Lists are a powerful feature in react-form-krafter that allow you to manage collections of items with ease. They provide a structured way to handle dynamic data inputs, such as adding or removing items from a list.
The List component is designed to work seamlessly with Form and Register, allowing you to build complex, dynamic forms with repeating sections.
import { List } from "@lib/list";
import Register from "@lib/register/registerContext";
import type {
ListAddRowComponentProps,
ListApi,
ListItemRowComponentProps,
RegisterComponent,
RegisterFieldRenderProps,
} from "@lib/types";
import { lazy, useCallback, useRef, type ComponentType } from "react";
import { z } from "zod";
import { BASIC_FIELDS_EXAMPLE } from "./fields";
type FieldsValue = number | string;
const COMPONENTS: RegisterComponent<FieldsValue>[] = [
{
type: "text",
render: lazy(() => import("./components/text")) as ComponentType<
RegisterFieldRenderProps<FieldsValue>
>,
},
{
type: "number",
render: lazy(() => import("./components/number")) as ComponentType<
RegisterFieldRenderProps<FieldsValue>
>,
},
];
const schema = z.object({
name: z.string().min(3, "Name must be at least 3 characters long"),
surname: z.string().min(3, "Surname must be at least 3 characters long"),
age: z
.number()
.min(18, "Age must be at least 18")
.max(100, "Age must be less than 100"),
about: z.string().max(500, "About must be less than 500 characters"),
});
type Schema = typeof schema;
type Validator = z.infer<Schema>;
function ExampleV0List() {
const listApi = useRef<ListApi<Validator> | null>(null);
const addRowComponent = useCallback(
({ add, form }: ListAddRowComponentProps<Validator>) => (
<div>
{form}
<button onClick={add}>Add Row</button>
</div>
),
[]
);
const itemRowComponent = useCallback(
({ index, remove, form }: ListItemRowComponentProps<Validator>) => (
<div className="item-row">
{form}
<button onClick={() => remove(index)}>Remove Row</button>
</div>
),
[]
);
return (
<div className="example-wrapper">
<Register<FieldsValue> components={COMPONENTS}>
<List<Validator, Schema>
listApi={listApi}
itemsProps={{
rowComponent: itemRowComponent,
onUpdate: async ({ item, index, currentState }) => {
console.log("Item updated:", item, "at index:", index);
console.log("Current state:", currentState);
const fakeDelay = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));
await fakeDelay(1000); // Simulate a delay for the update
if (currentState.age < 18) {
return {
preventUpdate: true,
};
}
},
}}
addProps={{
rowComponent: addRowComponent,
onError: (error) => {
console.error("Error adding item:", error);
},
onSuccess: (item) => {
console.log("Item added successfully:", item);
},
}}
schema={schema}
fields={BASIC_FIELDS_EXAMPLE}
formProps={{
formClassName: "fields",
}}
/>
</Register>
</div>
);
}
export default ExampleV0List;
Base Properties
fields: An array of registered fields to be rendered for each item in the list.schema: A validation schema (e.g., usingzod) to validate the form data for each item.listApi: A ref to the list API, allowing you to programmatically control the list (e.g., add, remove, update items).initialItems: An array of initial items to populate the list.formProps: Props to be passed to the underlyingFormcomponents.-
formClassName: A custom class name for the form elements. -
fieldWrapper: A function that wraps each field component. This is useful for adding custom wrappers or async loading states. For example, you can use React'sSuspenseto show a loading indicator while a field component is being loaded asynchronously:fieldWrapper={(fieldComp, fieldProps) => (
<Suspense
fallback={
<div className={fieldProps.wrapperClassName}>
Loading {fieldProps.label}...
</div>
}
>
{fieldComp}
</Suspense>
)}
-
children: A function that receives thelistApiand allows you to render additional components.
addProps
The addProps prop configures the component for adding new items to the list.
rowComponent: A component that renders the UI for adding a new item. It receives the following props:add: A function to call when the user wants to add the item.form: TheFormcomponent to be rendered for the new item.formApi: The API for the "add row" form.
onSuccess: A callback function that is called when a new item is successfully validated and added. It receives the new item as an argument. You can return a modified item to be added to the list.onError: A callback function that is called when adding a new item fails validation. It receives the validation errors.
itemsProps
The itemsProps prop configures the component for rendering and managing existing items in the list.
rowComponent: A component that renders the UI for each item in the list. It receives the following props:item: The data for the current item.index: The index of the current item in the list.remove: A function to call to remove the item from the list.form: TheFormcomponent for the current item.formApi: The API for the item's form.
onChange: A callback that is triggered whenever a field's value changes in any of the item forms. It receives an object with details about the change, includingfieldName,value,item, andindex.onUpdate: A callback that is triggered whenever an item's form state is updated (e.g., on blur). It receives an object with details about the update. You can return{ preventUpdate: true }to prevent the state change.
List API Methods
The listApi ref provides access to the following methods and properties:
| Name | Description |
|---|---|
items | An array containing the current state of all items in the list. |
addItem() | Programmatically adds a new item to the list. It triggers validation on the "add row" form. |
removeItem(index) | Removes the item at the specified index from the list. |
updateItem(index, item) | Updates the item at the specified index with new data. |
Bulk List API Methods & Types
In addition to single-item operations, the List API provides methods for bulk insert, remove, and update. Here are their types:
insertItems
insertItems: (newItems: T[]) => void;
Inserts an array of items (T[]) into the list.
removeItems
removeItems: (indices: number[]) => void;
Removes items at the specified array of indices (number[]).
updateItems
updateItems: (updates: Array<{ index: number; item: T }>) => void;
Updates items at the given indices with new values. Each update is an object with an index and the new item of type T.
These methods are available on the List API and can be accessed via the useListApi hook or through the imperative handle passed to the List component.
You can also use a render prop children to get access to the listApi:
<List {...props}>
{(listApi) => (
<div>
<button onClick={() => listApi.addItem()}>Add from outside</button>
</div>
)}
</List>