Data collections
@journeyapps/reactor-lib-data-layer provides observable primitives for remote and long-lived collections. They are independent of Reactor panels and entities, making them useful inside stores and domain models.
Collections own asynchronous list state. Entity definitions explain what the resulting models mean. Presenters decide how those entities appear.
Collection
Collection<T> owns observable items plus loading and failure state:
const todos = new Collection<TodoData>();
await todos.load(async (event) => {
const result = await client.listTodos();
return event.aborted ? [] : result;
});
Concurrent calls share the in-flight promise. Clearing a collection marks its current load event aborted, preventing a late result from replacing newer state.
The abort flag protects collection state from a stale completion. It does not cancel the underlying network request; use an AbortController in the client when transport cancellation matters.
PaginatedCollection
PaginatedCollection<T, R> consumes an async iterator:
const todos = new PaginatedCollection<TodoData, TodoPage>({
loaderIterator: () => client.paginateTodos(),
transformer: (page) => page.items,
hasMore: (page) => Boolean(page.next)
});
await todos.loadInitialData();
await todos.loadMore();
It tracks the last response, accumulated items, loading state, and whether another page exists. loadAll() drains the iterator until completion or cancellation.
The collection can also project itself into a PaginatedSearchResult, keeping search loading and pagination connected.
LifecycleCollection
LifecycleCollection converts serialized records into long-lived models:
- new keys generate models;
- existing keys patch the existing model;
- removed keys dispose their models.
const models = new LifecycleCollection({
collection: todoRecords,
getKeyForSerialized: (todo) => todo.id,
generateModel: (todo) => new TodoModel(todo)
});
This is valuable when models own listeners, nested collections, cached state, or other resources that should survive a refresh.
Every lifecycle model must implement meaningful dispose() behavior. A collection can remove the model from its map, but only the model knows which subscriptions and resources it owns.
Connect collections to entities
A common pattern is:
- a store owns a collection;
- an entity search behavior returns its models;
- descendant providers expose nested collections;
- presenters observe model changes;
- actions mutate through the store and refresh it.
Keep network pagination out of trees and tables. Those widgets should consume collection and entity contracts rather than own transport state.