Listen for Changes
Want to know when a user types in a cell, a formula recalculates, or a row gets inserted above your data? Add a listener to a range. In React, a hook does the subscribing and cleanup for you.
Using SDK
To listen for changes at a range use addListener. The listener is handed an ICellRange.Event.
import { Workbook, type ICellRange, type IListener } from '@sheetxl/sdk';
const workbook = new Workbook();
const removeListener: IListener.Remove = workbook.getRange('Sheet1!A1:E5000').addListener((event: ICellRange.Event): void => {
// The listened range, where it is now. Rows inserted above it move it; read from here, not from
// the range addListener was called on.
const range = event.getSource();
switch (event.getTrigger()) {
case 'data':
// The cells that changed within the range. event.getCommit() is null for a recalculation.
console.log('changed:', event.getRanges()!.getAddress(), event.isUndo() ? '(undo)' : '');
break;
case 'address':
// Moved by an insert, delete or sheet rename; #REF! if its cells were deleted.
console.log('now at:', range.getAddress(), 'was:', event.getPreviousAddress());
break;
case 'restore':
// Restored to a revision from before the listener was added; the range is #N/A there.
break;
}
});
// call removeListener() to remove the listener when finished
ICellRange.ListenerOptions
choose which events the listener hears (ignoreDataChanges, ignoreAddressChanges), whether it takes
part in the change or observes it afterwards (transactional), and how the range follows rows and
columns inserted or deleted around it (anchor). A listener on the rows a view shows is anchored
Absolute, so that it keeps naming the rows on screen:
import { IAnchored } from '@sheetxl/sdk';
sheet.getRange('A1:Z40').addListener(repaint, { anchor: IAnchored.Type.Absolute });
Using React
Hooks are available for React to make it easy to listen for changes. These are:
useModelSync example
import { type IWorksheet } from '@sheetxl/sdk';
import { useModelSync } from '@sheetxl/react';
/* Causes a re-render when style changes */
useModelSync<IWorksheet.IListeners>(sheet, 'onStyleChange');
useModelListener example
import { type IWorksheet } from '@sheetxl/sdk';
import { useModelListener } from '@sheetxl/react';
/* Updates state when there are changes to sheet */
useModelListener<IWorksheet, IWorksheet.IListeners>(sheet, {
// See IWorksheet.IListeners for all events
onBeforeSave(source: IWorksheet): Promise<void> | void {
// throw an exception to prevent save.
},
onSave(source: IWorksheet, json: IWorksheet.JSON): void {
// do something with the json
},
}, {
// Optional: skip the immediate callback that otherwise fires when the
// hook mounts or the model changes
ignoreModelChange: true
});
useRangeListener example
import { type ICellRange, type IWorksheet } from '@sheetxl/sdk';
import { useRangeListener } from '@sheetxl/react';
/* Listens to A1:C3 of the sheet while mounted; the options are ICellRange.ListenerOptions */
useRangeListener(sheet, 'A1:C3', (event: ICellRange.Event) => {
if (event.getTrigger() === 'data') console.log('changed:', event.getRanges()!.getAddress());
}, { transactional: false });