Handler middleware
Wrap any interaction to observe it, change its input or cancel it, without reimplementing what it does.
Basics
Loading example…
Every interaction the table performs goes through a named handler, and you can wrap any of them.
A handler receives the intent and a next function that runs the default:
type Handler<Ctx> = (ctx: Ctx, next: (override?: Partial<Ctx>) => void | Promise<void>) => void;
Three things you can do with next, which is the whole model:
handlers={{
// Observe, then proceed
onSortToggle: (ctx, next) => { track('sort', ctx.column.id); next(); },
// Change the intent: always pin to the left, whichever side was chosen
onColumnPin: (ctx, next) => next({ position: 'left' }),
// Cancel: a row click navigates instead of selecting
onRowClick: ({ row }) => navigate('/people/' + row.id),
}}
Not calling next cancels the default. That is how a row click becomes navigation without
turning selection off and rebuilding it.
Asking first
A handler may be async, so a confirmation is a normal await:
handlers={{
onPageChange: async (ctx, next) => {
if (!dirty || (await confirm('Discard unsaved changes?'))) next();
},
}}
What can be wrapped
Sorting, search input, column filters, clearing filters, page and page-size changes, row clicks and double-clicks, row selection and select-all, expansion, column resize, move, pin and hide, export, refresh, hotkeys and cell activation. The full list with each context object is on Handlers.
They compose
TableDefaultsProvider handlers wrap the library's defaults, and a handler passed to a table
wraps the provider's. Analytics can live at the provider and a screen-specific rule at the table,
and both run.
Customization
Handlers are for behaviour. Appearance is slots and tokens; content is a cell renderer. Keeping them apart is what lets a handler survive a redesign.
Limitations
- Calling
nexttwice runs the default twice. The middleware does not deduplicate. - A cancelled interaction leaves no trace. If a reader needs to know why their click did nothing, say so; the table will not.
- A handler cannot invent an interaction. It wraps the ones that exist; anything else is your own control calling the instance.
API
- Handlers: every handler, its context and its default.
DataTableProps:handlers,onRowClick,onStateChange,onQueryChange.TableInstance: what the defaults call.