Skip to content

Data Fetching & Caching

Selection filters, value dialogs, notes and history/audit storage have different domain contracts. Their typed adapters use the same Query engine while retaining existing callbacks. They are opt-in: local synchronous providers and local formula calculation remain unchanged. There is no additional adapter package to install.

ProviderQueryOptions is exported from @revolist/revogrid-pro. It describes one-shot reads: source, resource, staleTime, gcTime, retry, retryDelay, networkMode, meta and onStatus. Native callback types and values follow configuration types. Default freshness is 0; private contexts use the shared read defaults. Pass false to factories accepting it for uncached reads. Polling, enabled, focus and reconnect policies require a mounted observer and are deliberately absent from this one-shot type.

ValueSelectionDialogOpenOptions<T>.query uses the full LoadQueryOptions<ValueSelectionDialogPage<T>> | false type: native polling, focus/reconnect, enabled, consistency, reconciliation and status apply while the dialog is open. Omitting it or passing false retains the original direct loadValues({search, signal}) behavior. Search debounce and draft selection stay in the dialog. Search replacement, every dialog close path and grid destruction release the current observer and stop polling. Polling errors can recover on the next read without discarding the draft.

A shared source must include tenant and authorization identity. Set resource to include an endpoint, column and relevant filter/report context hidden in callback closures. Keys retain request fields such as search text, note targets, exact row batches and cursor/limit. Shared scopes coalesce equivalent in-flight requests; distinct semantic keys remain separate. Results must be structured-cloneable and are detached before consumers can edit them. Live renderers, DOM nodes and callbacks must not be used as query keys. Normalize Date, Map, Set and custom instances into primitive key fields.

Owner / public factoryExisting callback and resultIntegration and lifetime
Pro createFilterQueryProvider(getItems, query?)Existing GetItemsFn(prop, {search?,signal?}?) returns SelectionItem[].Pass returned getItems to selection.getItems. Filter search debounce and cancellation remain unchanged. dispose() releases this wrapper; accepted() fences its scope after a known external change.
Pro value dialog open({ ..., query })Existing loadValues({search,signal}) returns {values,totalCount}.Mounted only while open. Include column/filter identity in query.resource. Existing key/label/template/apply/clear callbacks are unchanged.
Pro createGridNotesQueryAdapter(adapter, query?)Existing GridNoteStorageAdapter methods and results, including mutationId and expectedVersion.One wrapper per notes owner; share query.source between owners. Notes batches/projections, permissions, conflict handling, retry policy and version history stay in Notes. Owner destroy() releases reads; a later init() can reactivate it.
Pro createGridNoteMentionsQueryProvider(mentions, query?)Existing search(query, signal?) returns users.Pass to gridNotes.mentions; host owns dispose() when removing this integration. UI debounce remains unchanged.
Pro createHistoryQueryAdapter(storage, query?)Existing optional load(), save(state), clear().Only present methods are exposed. Source IDs, provider ordering, timestamps, local-edit guards and undo/redo stay in History. Host disposes the wrapper on config removal/grid teardown.
Pro createAuditHistoryQueryAdapter(storage, query?)Existing optional load(), save(records), append(record,records), clear().Audit IDs, append order and restoration remain audit-owned. Host disposes the wrapper on config removal/grid teardown.

All wrappers except the mentions helper expose accepted() to invalidate their shared read scope after a confirmed external write. Successful storage/note writes fence reads automatically. Thrown writes do not. Cache/status notification failures cannot turn a confirmed write into a failed write or cause a second submission. The original backend callbacks still own durability and authorization; these legacy results do not become command receipts.

import {
createDataQueryContext,
createFilterQueryProvider,
} from '@revolist/revogrid-pro';
import type { ProviderQueryOptions } from '@revolist/revogrid-pro';
const context = createDataQueryContext();
const source = context.source({
sourceId: 'workspace-api', schemaId: '1',
tenantId: tenant.id, authorizationScope: session.permissionRevision,
});
const query: ProviderQueryOptions = {
source, resource: 'orders/country-values', staleTime: 30_000, retry: 2,
};
const values = createFilterQueryProvider(existingGetItems, query);
grid.filter = {
...existingFilterConfig,
selection: { ...existingFilterConfig.selection, getItems: values.getItems },
};
valueDialog.open({
...existingDialogOptions,
query: {
source, resource: 'orders/country-dialog', refetchInterval: 10_000,
refetchIntervalInBackground: true, retry: false,
},
});
// On integration removal, after closing the dialog/removing grids:
values.dispose();
context.dispose();

Synchronous structuredFilterAggregates.get/getPrepared supply already prepared aggregate data. They remain synchronous and are not reinterpreted as network callbacks. Application code may fetch data first, then provide those models.

import {
createGridNotesQueryAdapter,
createGridNoteMentionsQueryProvider,
createHistoryQueryAdapter,
createAuditHistoryQueryAdapter,
} from '@revolist/revogrid-pro';
const notes = createGridNotesQueryAdapter(existingNoteAdapter, {
source, resource: 'orders/notes', staleTime: 30_000,
});
const mentions = createGridNoteMentionsQueryProvider(existingMentions, {
source, resource: 'workspace/users', staleTime: 30_000,
});
grid.gridNotes = { ...existingNotesConfig, adapter: notes, mentions };
const history = createHistoryQueryAdapter(existingHistoryStorage, {
source, resource: 'orders/undo-history',
});
grid.history = { storage: 'custom', sourceId: 'orders', storageAdapter: history };
const audit = createAuditHistoryQueryAdapter(existingAuditStorage, {
source, resource: 'orders/audit-history',
});
grid.auditHistory = { storage: 'custom', storageAdapter: audit };

Note summary batching and loaded-row state remain in GridNoteStore. The optional second loadSummaries(input, context?) argument {fresh?: boolean} marks explicit forced refreshes; refreshNotes(rowKeys) sets it through the store, and the adapter bypasses cached summary freshness. Ordinary existing adapters may ignore this extra argument; the existing input object is unchanged. After a confirmed foreign note change, call await notes.accepted() to fence all note read targets, then await notesPlugin.refreshNotes(rowKeys) to update its projection. A Query invalidation alone does not replace a note store’s projection. History and audit loads remain one-shot; their existing controllers decide whether a result may hydrate. Wrapped reads return Promises, even if a supplied storage adapter is synchronous. Leave local memory/browser adapters unwrapped when synchronous loading is required. These wrappers add no write retries; Notes’ existing retry and idempotency policy remains with Notes.

Removing an integration does not remove durable data. Dispose application-owned history, audit and mention wrappers after detaching their consumers; Notes owns the wrapper supplied to its config. Dispose the shared context only after all owners have stopped using it. History/audit callbacks have no transport signal parameter: disposal rejects pending subscribers and discards late results, but cannot physically abort an opaque backend operation that ignores cancellation.