useDynamicList
A hook that helps you manage dynamic list and generate unique key for each item.
Overview
A hook that helps you manage dynamic list and generate unique key for each item.
Installation
Open inpnpm dlx shadcn@latest add https://shadcn-ahooks.vercel.app/r/useDynamicList.jsonnpx shadcn@latest add https://shadcn-ahooks.vercel.app/r/useDynamicList.jsonyarn shadcn@latest add https://shadcn-ahooks.vercel.app/r/useDynamicList.jsonbun shadcn@latest add https://shadcn-ahooks.vercel.app/r/useDynamicList.jsonA hook that helps you manage dynamic list and generate unique key for each item.
Examples
Basic usage
import useDynamicList from '@/src/hooks/ahooks/useDynamicList';
import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons';
import { Button, Input, Space } from 'antd';
import React from 'react';
const Example = () => {
const { list, remove, batchRemove, getKey, insert, replace } = useDynamicList(['David', 'Jack']);
const listIndexes = list.map((item, index) => index);
const Row = (index: number, item: any) => (
<div key={getKey(index)} style={{ marginBottom: 16 }}>
<Input
style={{ width: 300 }}
placeholder="Please enter name"
onChange={(e) => replace(index, e.target.value)}
value={item}
/>
{list.length > 1 && (
<MinusCircleOutlined
style={{ marginLeft: 8 }}
onClick={() => {
remove(index);
}}
/>
)}
<PlusCircleOutlined
style={{ marginLeft: 8 }}
onClick={() => {
insert(index + 1, '');
}}
/>
</div>
);
return (
<>
{list.map((ele, index) => Row(index, ele))}
<Space style={{ marginBottom: 16 }}>
<Button
danger
disabled={list.length <= 1}
onClick={() => batchRemove(listIndexes.filter((index) => index % 2 === 0))}
>
Remove odd items
</Button>
<Button
danger
disabled={list.length <= 1}
onClick={() => batchRemove(listIndexes.filter((index) => index % 2 !== 0))}
>
Remove even items
</Button>
</Space>
<div>{JSON.stringify([list])}</div>
</>
);
};
export default Example;Using with antd Form
import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons';
import useDynamicList from '@/src/hooks/ahooks/useDynamicList';
import { Button, Form, Input } from 'antd';
import React, { useEffect, useState } from 'react';
const DynamicInputs = ({
value = [],
onChange,
}: {
value?: string[];
onChange?: (value: string[]) => void;
}) => {
const { list, remove, getKey, insert, replace, resetList } = useDynamicList(value);
useEffect(() => {
// If value change manual, reset list
if (value !== list) {
resetList(value);
}
}, [value]);
useEffect(() => {
onChange?.(list);
}, [list]);
const Row = (index: number, item: any) => (
<div key={getKey(index)} style={{ marginBottom: 16 }}>
<Input
style={{ width: 300 }}
placeholder="Please enter name"
onChange={(e) => replace(index, e.target.value)}
value={item}
/>
{list.length > 1 && (
<MinusCircleOutlined
style={{ marginLeft: 8 }}
onClick={() => {
remove(index);
}}
/>
)}
<PlusCircleOutlined
style={{ marginLeft: 8 }}
onClick={() => {
insert(index + 1, '');
}}
/>
</div>
);
return <>{list.map((ele, index) => Row(index, ele))}</>;
};
export default () => {
const [form] = Form.useForm();
const [result, setResult] = useState('');
return (
<>
<Form form={form}>
<Form.Item name="names" initialValue={['David', 'Jack']}>
<DynamicInputs />
</Form.Item>
</Form>
<Button
type="primary"
onClick={() =>
form
.validateFields()
.then((val) => {
setResult(JSON.stringify(val.names));
})
.catch(() => {})
}
>
Submit
</Button>
<Button style={{ marginLeft: 16 }} onClick={() => form.resetFields()}>
Reset
</Button>
<p>{result}</p>
</>
);
};Another way of writing used in antd Form
import React, { useState } from 'react';
import { Form, Button, Input } from 'antd';
import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons';
import useDynamicList from '@/src/hooks/ahooks/useDynamicList';
export default () => {
const { list, remove, getKey, insert, resetList, sortList } = useDynamicList(['David', 'Jack']);
const [form] = Form.useForm();
const [result, setResult] = useState('');
const Row = (index: number, item: any) => (
<div style={{ display: 'flex' }} key={getKey(index)}>
<div>
<Form.Item
rules={[{ required: true, message: 'required' }]}
name={['names', getKey(index)]}
initialValue={item}
>
<Input style={{ width: 300 }} placeholder="Please enter your name" />
</Form.Item>
</div>
<div style={{ marginTop: 4 }}>
{list.length > 1 && (
<MinusCircleOutlined
style={{ marginLeft: 8 }}
onClick={() => {
remove(index);
}}
/>
)}
<PlusCircleOutlined
style={{ marginLeft: 8 }}
onClick={() => {
insert(index + 1, '');
}}
/>
</div>
</div>
);
return (
<>
<Form form={form}>{list.map((ele, index) => Row(index, ele))}</Form>
<Button
type="primary"
onClick={() =>
form
.validateFields()
.then((val) => {
const sortedResult = sortList(val.names);
setResult(JSON.stringify(sortedResult, null, 2));
})
.catch(() => {})
}
>
Submit
</Button>
<Button style={{ marginLeft: 16 }} onClick={() => resetList(['David', 'Jack'])}>
Reset
</Button>
<div>{result}</div>
</>
);
};Draggable dynamic table
import { DragOutlined } from '@ant-design/icons';
import { Button, Form, Input, Table } from 'antd';
import React, { useState } from 'react';
import ReactDragListView from 'react-drag-listview';
import useDynamicList from '@/src/hooks/ahooks/useDynamicList';
interface Item {
name?: string;
age?: string;
memo?: string;
}
export default () => {
const { list, remove, getKey, move, push, sortList } = useDynamicList<Item>([
{ name: 'my bro', age: '23', memo: "he's my bro" },
{ name: 'my sis', age: '21', memo: "she's my sis" },
{},
]);
const [form] = Form.useForm();
const [result, setResult] = useState('');
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (text: string, row: Item, index: number) => (
<>
<DragOutlined style={{ cursor: 'move', marginRight: 8 }} />
<Form.Item name={['params', getKey(index), 'name']} initialValue={text} noStyle>
<Input style={{ width: 120, marginRight: 16 }} placeholder="name" />
</Form.Item>
</>
),
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
render: (text: string, row: Item, index: number) => (
<Form.Item name={['params', getKey(index), 'age']} initialValue={text} noStyle>
<Input style={{ width: 120, marginRight: 16 }} placeholder="age" />
</Form.Item>
),
},
{
key: 'memo',
title: 'Memo',
dataIndex: 'memo',
render: (text: string, row: Item, index: number) => (
<>
<Form.Item name={['params', getKey(index), 'memo']} initialValue={text} noStyle>
<Input style={{ width: 300, marginRight: 16 }} placeholder="please input the memo" />
</Form.Item>
<Button.Group>
<Button danger onClick={() => remove(index)}>
Delete
</Button>
</Button.Group>
</>
),
},
];
return (
<div>
<Form form={form}>
<ReactDragListView
onDragEnd={(oldIndex: number, newIndex: number) => move(oldIndex, newIndex)}
handleSelector={'span[aria-label="drag"]'}
>
<Table
columns={columns}
dataSource={list}
rowKey={(r: Item, index: number) => getKey(index).toString()}
pagination={false}
style={{ overflow: 'auto' }}
/>
</ReactDragListView>
</Form>
<Button
style={{ marginTop: 8 }}
block
type="dashed"
onClick={() => push({ name: 'new row', age: '25' })}
>
+ Add row
</Button>
<Button
type="primary"
style={{ marginTop: 16 }}
onClick={() => {
form
.validateFields()
.then((val) => {
console.log(val, val.params);
const sortedResult = sortList(val.params);
setResult(JSON.stringify(sortedResult, null, 2));
})
.catch(() => {});
}}
>
Submit
</Button>
<div style={{ whiteSpace: 'pre' }}>{result && `content: ${result}`}</div>
</div>
);
};API
const result: Result = useDynamicList(initialValue: T[]);Result
| Property | Description | Type | Remarks |
|---|---|---|---|
| list | Current list | T[] | - |
| resetList | Reset list current data | (list: T[]) => void | - |
| insert | Add item at specific position | (index: number, item: T) => void | - |
| merge | Merge items into specific position | (index: number, items: T[]) => void | - |
| replace | Replace item at specific position | (index: number, item: T) => void | - |
| remove | Delete specific item | (index: number) => void | - |
| move | Move item from old index to new index | (oldIndex: number, newIndex: number) => void | - |
| getKey | Get the uuid of specific item | (index: number) => number | - |
| getIndex | Retrieve index from uuid | (key: number) => number | - |
| sortList | Sort the form data(using with antd form) | (list: T[]) => T[] | seeAnother way of writing used in antd Form |
| push | Push new item at the end of list | (item: T) => void | - |
| pop | Remove the last item from the list | () => void | - |
| unshift | Add new item at the front of the list | (item: T) => void | - |
| shift | Remove the first item from the list | () => void | - |
Params
| Property | Description | Type | Default |
|---|---|---|---|
| initialValue | Initial value of the list | T[] | [] |