.md

Real-time updates

React to data changes with list.onResultChange() and push changes into dcupl with data.upsert().

Key dcupl functions

Function Purpose
list.onResultChange() Run a callback when the list's result changes (returns unsubscribe function)
dcupl.data.upsert() Add new items or update existing ones (staged)
dcupl.data.remove() Remove data items (staged)
dcupl.update() Apply staged data changes; affected lists emit result_updated

Example data

The snippets up to the Complete Flow use this Product model:

Setup
import { Dcupl } from '@dcupl/core';

const dcupl = new Dcupl();
dcupl.models.set({
  key: 'Product',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'stock', type: 'int' },
  ],
});
dcupl.data.set(
  [
    { key: 'prod-1', name: 'Widget', stock: 50 },
    { key: 'prod-2', name: 'Gadget', stock: 100 },
    { key: 'prod-3', name: 'Gizmo', stock: 5 },
  ],
  { model: 'Product' },
);
await dcupl.init();

Listening to list events

Subscribe to result changes on a list with list.onResultChange(). It fires once per dcupl.update() that can have changed the list (also for an update with nothing staged), and when the list's query changes — never for reads such as items():

const productList = dcupl.lists.create({ modelKey: 'Product' });

const unsubscribe = productList.onResultChange(() => {
  // Refresh your UI here
  console.log('Products:', productList.catalog.query.items());
});

// Later: cleanup
unsubscribe();

list.on() receives every event of the list, including reads; filter on event.type === 'result_updated' if you need it. See the Events reference.

A list created with a query option keeps the keys that query selected: after dcupl.update() it serves their current values and removed items drop out, but a new match doesn't join it and an item that stops matching stays. Call list.update() to re-run the query once, or give the list autoUpdate: true (or set performance.autoUpdateLists.enabled) to re-run it on each dcupl.update(); the Auto-Update Pattern shows how to pass it in TypeScript.

Updating data with data.upsert()

Push new or updated items into dcupl. upsert() inserts unknown keys and updates existing items; every attribute in the payload replaces the stored value, attributes you leave out keep theirs. Don't use data.set() for this: it replaces all data of the model.

// Add a new item or update an existing one
dcupl.data.upsert([{ key: 'prod-4', name: 'Doohickey', stock: 20 }], { model: 'Product' });

// Apply changes
await dcupl.update();

Update multiple items at once:

dcupl.data.upsert(
  [
    { key: 'prod-1', stock: 45 },
    { key: 'prod-2', stock: 120 },
    { key: 'prod-3', stock: 0 },
  ],
  { model: 'Product' },
);

await dcupl.update();

Removing data

Remove items by key:

dcupl.data.remove([{ key: 'prod-3' }], { model: 'Product' });

await dcupl.update();

Complete flow

Combine event listening with data updates:

import { Dcupl } from '@dcupl/core';

const dcupl = new Dcupl();
dcupl.models.set({
  key: 'Order',
  properties: [{ key: 'status', type: 'string' }],
});
dcupl.data.set([], { model: 'Order' }); // Start empty
await dcupl.init();

const orderList = dcupl.lists.create({ modelKey: 'Order' });

// 1. Listen for changes
orderList.onResultChange(() => {
  const orders = orderList.catalog.query.items();
  console.log('Orders updated:', orders.length);
});

// 2. Add data (from your data source)
async function handleNewOrder(order: { key: string; status: string }) {
  dcupl.data.upsert([order], { model: 'Order' });
  await dcupl.update(); // orderList emits result_updated
}

// 3. Update existing item
async function updateOrderStatus(orderId: string, status: string) {
  dcupl.data.update([{ key: orderId, status }], { model: 'Order' });
  await dcupl.update();
}

// 4. Remove item
async function deleteOrder(orderId: string) {
  dcupl.data.remove([{ key: orderId }], { model: 'Order' });
  await dcupl.update();
}

data.update() only changes items that already exist; an unknown key is ignored. Use upsert() when the item may be new.

Batching updates

For multiple changes, call dcupl.update() once at the end:

// Queue multiple changes
dcupl.data.upsert([{ key: 'prod-1', stock: 10 }], { model: 'Product' });
dcupl.data.upsert([{ key: 'prod-2', stock: 20 }], { model: 'Product' });
dcupl.data.remove([{ key: 'prod-3' }], { model: 'Product' });

// A single update: each affected list emits result_updated once
await dcupl.update();