useCollection is a hook that wraps @webeach/collection. It creates a stable Collection instance and keeps a reactive snapshot of its items. Any mutation performed through collection methods (e.g. appendItem, patchItem, replaceItem, removeItem, setItems, etc.) automatically updates the returned snapshot array and triggers a component re-render.
The hook supports two forms of usage: with options or with initial items.
// 1) Using options (including initialItems)
function useCollection<
PrimaryKey extends string = 'key',
PrimaryKeyType = CollectionDefaultKeyType,
ItemData extends CollectionBaseItemData<PrimaryKey, PrimaryKeyType> = CollectionBaseItemData<PrimaryKey, PrimaryKeyType>,
>(
options?: CollectionOptions<
CollectionPrimaryKeyWithDefault<PrimaryKey>,
PrimaryKeyType,
ItemData
>,
): UseCollectionReturn<PrimaryKey, PrimaryKeyType, ItemData>;
// 2) Using initial items directly
function useCollection<
PrimaryKey extends string = 'key',
PrimaryKeyType = CollectionDefaultKeyType,
ItemData extends CollectionBaseItemData<PrimaryKey, PrimaryKeyType> = CollectionBaseItemData<PrimaryKey, PrimaryKeyType>,
>(
initialItems?: ReadonlyArray<
CollectionItem<
CollectionPrimaryKeyWithDefault<PrimaryKey>,
PrimaryKeyType,
ItemData
>
>,
): UseCollectionReturn<PrimaryKey, PrimaryKeyType, ItemData>;-
Parameters
options?—Collectionoptions (includinginitialItems, primary key settings, and other@webeach/collectionoptions).initialItems?— an array of initial items if you don’t need other options.
-
Returns:
UseCollectionReturn<PrimaryKey, PrimaryKeyType, ItemData>— a tuple:state—ReadonlyArray<CollectionItem<…>>, the reactive snapshot of the current collection contents.instance—Collection<…>, the stable instance used to perform mutations.
import { useCollection, type CollectionItem } from '@webeach/react-hooks/useCollection';
type TaskData = {
key: string;
title: string;
done?: boolean;
};
export function TodoList() {
const [tasks, tasksCollection] = useCollection<'key', string, TaskData>([
{ key: 't1', title: 'Write docs' },
{ key: 't2', title: 'Review PR', done: true },
]);
const add = () => {
tasksCollection.appendItem({
key: crypto.randomUUID(),
title: 'New task',
});
};
const toggle = (item: CollectionItem<'key', string, TaskData>) => {
tasksCollection.patchItem(item.key, { done: !item.done });
};
const remove = (key: string) => {
usersCollection.removeItem(key);
};
return (
<div>
<button onClick={add}>Add</button>
<ul>
{tasks.map((item) => (
<li key={item.key}>
<label>
<input
type="checkbox"
checked={Boolean(item.done)}
onChange={() => toggle(item)}
/>
{item.title}
</label>
<button onClick={() => remove(item.key)}>×</button>
</li>
))}
</ul>
</div>
);
}import { useCollection } from '@webeach/react-hooks/useCollection';
type UserData = {
id: number;
name: string;
};
export function Users() {
const [users, usersCollection] = useCollection<'id', number, UserData>({
primaryKey: 'id',
initialItems: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
],
});
const rename = (id: number, name: string) => {
usersCollection.patchItem(id, { name });
};
const reloadAll = async () => {
const next = await fetch('/api/users').then((response) => response.json());
usersCollection.setItems(next); // full replacement of contents
};
return (
<>
<button onClick={reloadAll}>Reload</button>
<ul>
{users.map((userItem) => (
<li key={userItem.key}>{userItem.name}</li>
))}
</ul>
</>
);
}-
Stable instance
- The returned
instanceis created once and never changes between renders. Safe to use in effect dependencies or pass down to children.
- The returned
-
Reactive snapshot
stateis an immutable snapshot (ReadonlyArray). Any mutation through collection methods publishes a new snapshot and triggers a re-render.
-
Initialization
- You can provide initial data either as an array (
initialItems) or viaoptions.initialItems.
- You can provide initial data either as an array (
-
Collection methods
- Use the instance methods (
appendItem,prependItem,appendItemAt,insertItemBefore/After,patchItem,replaceItem,removeItem,clear,reset,setItems). They automatically update the snapshot and trigger re-renders.
- Use the instance methods (
-
SSR safe
- The hook does not depend on browser APIs. Safe for SSR/ISR environments.
- Managing a list/collection with frequent insertions, deletions, or updates.
- When you need a stable object for passing between components and subscribing to updates.
- When type safety by primary key and item data structure is important.
- If state changes are rare and a simple
useStatewith an array/object is enough. - If your logic goes beyond basic collection operations (history, transactions) — better build a dedicated state manager.
-
Mutating state directly
- Updating item objects in place won’t update the snapshot. Always use collection methods (
patchItem,replaceItem,setItems, etc.).
- Updating item objects in place won’t update the snapshot. Always use collection methods (
-
Expecting
initialItemsto auto-update- The collection instance is created once. Changing the
initialItemsvariable after mount won’t reset it — useresetorsetItems.
- The collection instance is created once. Changing the
-
Using
statefor mutationsstateis read-only. Always callinstancemethods to update the collection.
-
Unstable dependencies
- Avoid creating new options objects every render unless you want to reset the collection. Use
useMemofor stable options.
- Avoid creating new options objects every render unless you want to reset the collection. Use
Exported types
-
UseCollectionReturn<PrimaryKey, PrimaryKeyType, ItemData>- Tuple
[state, instance], where:state—ReadonlyArray<CollectionItem<…>>, reactive snapshot of current items.instance—Collection<…>, the stable collection instance.
- Tuple
-
Generic parameters
PrimaryKey extends string = 'key'— the field name used as the primary key.PrimaryKeyType = CollectionDefaultKeyType— the type of the primary key value (usuallystring | number).ItemData extends CollectionBaseItemData<PrimaryKey, PrimaryKeyType>— the item data shape.