https://github.com/form-atoms/list-atom
A listAtom extension for the Jotai form-atoms.
https://github.com/form-atoms/list-atom
fieldarray form-atoms jotai-form react-form
Last synced: 8 months ago
JSON representation
A listAtom extension for the Jotai form-atoms.
- Host: GitHub
- URL: https://github.com/form-atoms/list-atom
- Owner: form-atoms
- License: mit
- Created: 2024-03-04T14:51:50.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2024-09-13T09:31:48.000Z (over 1 year ago)
- Last Synced: 2025-07-12T13:05:50.333Z (8 months ago)
- Topics: fieldarray, form-atoms, jotai-form, react-form
- Language: TypeScript
- Homepage: https://form-atoms.github.io/list-atom/
- Size: 1.67 MB
- Stars: 4
- Watchers: 1
- Forks: 1
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.md
Awesome Lists containing this project
README
```sh
npm install jotai-effect @form-atoms/list-atom
```
### Quick start
```tsx
import { fromAtom, useForm, fieldAtom, InputField } from "form-atoms";
import { listAtom, List } from "@form-atoms/list-atom";
const environmentVariables = listAtom({
name: "environment",
value: [{ key: "GITHUB_SECRET", value: "" }],
fields: ({ key, value }) => ({
key: fieldAtom({ value: key }),
value: fieldAtom({ value }),
}),
});
const form = formAtom({ environmentVariables });
export const Form = () => {
const { submit } = useForm(form);
return (
{({ fields }) => (
<>
(
<>
Variable Key
>
)}
/>
(
<>
Variable Value
>
)}
/>
>
)}
);
};
```
## Table of contents
| Atoms | Description |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`listAtom()`](#listatom) | An atom that represents a list of form fields in a form. It manages state for the list, including the name, value, errors, dirty, validation and empty state. |
| Hooks | Description |
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| [`useListActions()`](#uselistactions) | A hook that returns a `add`, `remove` & `move` actions, that can be used to interact with the list atom state. |
| [`useList()`](#uselist) | A hook that returns the list `items` ready to be rendred together with the list actions. |
| Components | Description |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| [``](#list) | A component that render the individual items of listAtom with render props to render `AddButton`, `RemoveButton` and/or `Empty` slate. |
## List atoms
### listAtom()
An atom that represents a **list of form fields** in a form. It manages state for the list, including the name, value, errors, dirty, validation and empty state.
#### Arguments
| Name | Type | Required? | Description |
| ------ | -------------------------------------------------- | --------- | ------------------------------------------------- |
| config | [`ListAtomConfig`](#ListAtomConfig) | Yes | The initial state and configuration of the field. |
#### `ListAtomConfig`
```ts
export type ListAtomConfig = {
/**
* Optionally provide a name for the field that will be added
* prefixed to inner fields
* E.g. list name "contacts" and field name "email"
* will have scoped name for the 4th item "contacts[3].email"
*/
name?: string;
/**
* The initial array of values of the list
*/
value: Value[];
/**
* A function to initialize the fields for each of the initial values.
*/
fields: (value: Value) => Fields;
/**
* Error message the listAtom will have, when its items have nested errors.
* It will be one of errors returned by the `useFieldErrors()` hook.
*/
invalidItemError?: string;
/**
* A function that validates the value of the field any time
* one of its atoms changes. It must either return an array of
* string error messages or undefined. If it returns undefined,
* the validation is "skipped" and the current errors in state
* are retained.
*/
validate?: (state: {
/**
* A Jotai getter that can read other atoms
*/
get: Getter;
/**
* The current value of the field
*/
value: Value;
/**
* The dirty state of the field
*/
dirty: boolean;
/**
* The touched state of the field
*/
touched: boolean;
/**
* The event that caused the validation. Either:
*
* - `"change"` - The value of the field has changed
* - `"touch"` - The field has been touched
* - `"blur"` - The field has been blurred
* - `"submit"` - The form has been submitted
* - `"user"` - A user/developer has triggered the validation
*/
event: ValidateOn;
}) => void | string[] | Promise;
};
```
#### Returns
An extended `FieldAtom`:
```ts
export type ListAtom = ExtendFieldAtom<
Value[],
{
/**
* An atom indicating whether the list is empty.
*/
empty: Atom;
/**
* A splitAtom() instance from jotai/utils.
* It handles adding, removing and moving of items in the list.
* @internal
*/
_splitList: WritableAtom<
PrimitiveAtom>[],
[SplitAtomAction>],
void
>;
/**
* An atom holding the list of forms of each item.
* @internal
*/
_formList: WritableAtom<
ListItemForm[],
[typeof RESET | SetStateAction[]>],
void
>;
/**
* An atom holding the fields of the internal formAtom of each item.
* @internal
*/
_formFields: Atom;
buildItem(): ListItemForm;
}
>;
```
#### [⇗ Back to top](#table-of-contents)
## Hooks
### useListActions()
A hook that returns a `add`, `remove` & `move` actions, that can be used to interact with the list atom state.
#### Arguments
| Name | Type | Required? | Description |
| -------- | -------------------------------------------- | --------- | ----------------------------------------------------------------------------------- |
| listAtom | `ListAtom` | Yes | The atom that stores the list's state |
| options | `UseAtomOptions` | No | Options that are forwarded to the `useAtom`, `useAtomValue`, and `useSetAtom` hooks |
#### Returns
```ts
export type UseListActions = {
/**
* Removes the item from the list.
*
* @param item - An item from the listAtom's splitList array.
*/
remove: (item: ListItem) => void;
/**
* Appends a new item to the list by default, when no 'before' position is used.
* Optionally the item can be initialized, with the 'fields' argument.
*
* @param before - An item from the listAtom's splitList array.
* @param fields - A custom initialized fieldAtoms matching the Fields shape of the list.
*/
add: (
before?: ListItem | undefined,
fields?: Fields | undefined,
) => void;
/**
* Moves the item to the end of the list, or where specified when the 'before' is defined.
*
* @param item - A splitList item to be moved.
* @param before - A splitList item before which to place the moved item.
*/
move: (item: ListItem, before?: ListItem | undefined) => void;
};
```
#### [⇗ Back to top](#table-of-contents)
### useList()
A hook that returns the list `items` ready to be rendred together with the list actions.
#### Arguments
| Name | Type | Required? | Description |
| -------- | -------------------------------------------- | --------- | ----------------------------------------------------------------------------------- |
| listAtom | `ListAtom` | Yes | The atom that stores the list's state |
| options | `UseAtomOptions` | No | Options that are forwarded to the `useAtom`, `useAtomValue`, and `useSetAtom` hooks |
#### Returns
```ts
export type UseList = UseListActions & {
/**
* Resolved value from the list.empty atom.
*/
isEmpty: boolean;
items: {
/**
* The item from the internal splitList.
*/
item: ListItem;
/**
* Stable React key prop derived from atom id.
*/
key: string;
/**
* The form fields of the current item.
*/
fields: Fields;
/**
* A function to remove the current item from the list.
*/
remove: () => void;
/**
* A helper function to move the item to the previous index in the list.
*/
moveUp: () => void;
/**
* A helper function to move the item to the next index in the list.
*/
moveDown: () => void;
};
};
```
#### [⇗ Back to top](#table-of-contents)
## Components
### <List>
#### Props
| Name | Type | Required? | Description |
| ------------ | ----------------------------------------------------- | --------- | ------------------------------------------------------------------------- |
| atom | `ListAtom` | Yes | A list atom |
| children | `(props: ListItemProps) => JSX.Element` | Yes | A render prop |
| initialValue | `Value[]` | No | A value to (re)initialize the listAtom |
| RemoveButton | `FunctionComponent<{remove: () => void}>` | no | A render prop receiving `remove` function for each individual list item |
| AddButton | `FunctionComponent<{add: (fields?: Fields) => void}>` | No | A render prop to render a button adding new items to the end of the list |
| Empty | `FunctionComponent` | No | A render prop to render a blank slate when there are no items in the list |
| store | `AtomStore` | No | [A Jotai store](https://jotai.org/docs/api/core#createstore) |
#### Render Props
Your `children` render prop will receive the following props:
```ts
type ListItemProps = {
/**
* The fields of current item, as returned from the builder function.
*/
fields: Fields;
/**
* The item from the internal splitList.
*/
item: ListItem;
/**
* The index of the current item.
*/
index: number;
/**
* Total count of items in the list.
*/
count: number;
/**
* Append a new item to the end of the list.
* When called with current item, it will be prepend with a new item.
*/
add: (before?: ListItem) => void;
/**
* Removes the current item.
*/
remove: () => void;
/**
* Moves the current item one slot up in the list.
* When called for the first item, the action is no-op.
*/
moveUp: () => void;
/**
* Moves the current item one slot down in the list.
* When called for the last item, the item moves to the start of the list.
*/
moveDown: () => void;
/**
* A component with an onClick handler bound to remove the current item from the list.
*/
RemoveButton: FunctionComponent;
};
```
#### [⇗ Back to top](#table-of-contents)
## Let's see what's in this listAtom

#### [⇗ Back to top](#table-of-contents)
## LICENSE
MIT