Submitting
When the user clicks Submit, Updog hands you the edited dataset grouped by source. You decide what to do with it — INSERT, UPDATE, DELETE, skip — by filtering on per-row flags.
onComplete
Section titled “onComplete”| Type | (result: DataEditorResult<TRow>) => void | Promise<void> |
DataEditorResult
Section titled “DataEditorResult”type DataEditorResult<TRow> = { sources: DataEditorSourceResult<TRow>[]; counts: { new: number; changed: number; deleted: number; invalid: number; };};counts is an aggregate across every source.
DataEditorSourceResult
Section titled “DataEditorSourceResult”type DataEditorSourceResult<TRow> = { sourceId: string; sourceName: string; rows: ResultRow<TRow>[];};One entry per source. Pristine backend rows — unchanged, undeleted, not imported — are omitted. They’re no-ops.
ResultRow
Section titled “ResultRow”type ResultRow<TRow> = { row: TRow; isNew: boolean; isChanged: boolean; isDeleted: boolean; isValid: boolean;};The flags are orthogonal. A single row can be new + changed + deleted at once.
Updog doesn’t pre-group rows because routing depends on your backend — some clients upsert, some reject invalid rows, some keep deletions for audit.
Example
Section titled “Example”onComplete={async (result) => { for (const source of result.sources) { const inserts = source.rows.filter(r => r.isNew && !r.isDeleted && r.isValid); const updates = source.rows.filter(r => !r.isNew && r.isChanged && !r.isDeleted && r.isValid); const deletes = source.rows.filter(r => r.isDeleted && !r.isNew);
await persist(source.sourceId, { inserts, updates, deletes }); }}}If you never tagged sources via loadData, you’ll get one entry with sourceId: "backend".