8.6 KiB
⚡ react-virtual-aggridautomat
Opinionated, fully-virtualized AG Grid component powered by react-automat — infinite-scroll data grids as React
PureComponentwith zero hooks, HOCs, or Context Providers.
What is this?
A reusable <AutomatGrid /> PureComponent that wires AG Grid's Infinite Row Model to a react-automat store. Your async data fetcher is the only thing you provide — the library handles:
- Virtual row loading — only visible rows exist in the DOM
- Block caching — configurable cache size, debounced scroll loading
- Server-side sort & filter — sort/filter changes flow through the automat and trigger datasource resets
- Reactive state — loading indicators, error state, row counts, and revision tracking all live in the automat
- Zero hooks — designed for
PureComponentwith synchronous constructor reads and fine-grained automat subscriptions
Install
npm install ag-grid-community ag-grid-react react-automat
This library is not yet published to npm. Clone or copy
src/lib/into your project.
Quick Start
1. Create a grid automat
// myGridAutomat.ts
import { createGridAutomat } from './lib';
export const myGridAutomat = createGridAutomat({
fetchRows: async ({ startRow, endRow, sortModel, filterModel }) => {
const res = await fetch(`/api/items?start=${startRow}&end=${endRow}`);
const { items, total } = await res.json();
return { rows: items, lastRow: total };
},
name: 'myGrid', // Visible in window.__AUTOMATS__
blockSize: 100, // Rows per block (passed to AG Grid's cacheBlockSize)
});
2. Render the grid
// MyView.tsx
import { PureComponent } from 'react';
import { AutomatGrid } from './lib';
import { myGridAutomat } from './myGridAutomat';
class MyView extends PureComponent {
render() {
return (
<AutomatGrid
automat={myGridAutomat}
columnDefs={[
{ field: 'id', maxWidth: 80 },
{ field: 'name', flex: 2, filter: 'agTextColumnFilter' },
{ field: 'price', valueFormatter: p => `€${p.value?.toFixed(2)}` },
]}
rowIdField="id"
height="600px"
/>
);
}
}
3. React to grid state
The automat is a standard react-automat store — subscribe to it from any PureComponent:
class StatusBar extends PureComponent {
constructor(props) {
super(props);
this.state = {
loading: myGridAutomat.state.loading,
totalRows: myGridAutomat.state.totalRows,
};
}
componentDidMount() {
this.unsub = myGridAutomat.subscribe(this, (s) => ({
loading: s.loading,
totalRows: s.totalRows,
}));
}
componentWillUnmount() {
this.unsub();
}
render() {
return (
<div>
{this.state.loading ? 'Loading…' : `${this.state.totalRows} rows`}
<button onClick={() => myGridAutomat.actions.reload()}>↻ Reload</button>
</div>
);
}
}
API Reference
createGridAutomat(options)
Creates a react-automat store to back the grid.
| Option | Type | Default | Description |
|---|---|---|---|
fetchRows |
(params: GridFetchParams) => Promise<GridFetchResult> |
required | Async function called by AG Grid's datasource for each block |
name |
string |
null |
Automat registry name (visible in window.__AUTOMATS__) |
blockSize |
number |
100 |
Rows per cache block |
GridFetchParams
{
startRow: number;
endRow: number;
sortModel: SortModelItem[];
filterModel: Record<string, unknown>;
}
GridFetchResult<TRow>
{
rows: TRow[];
lastRow?: number; // Total count. undefined/-1 = unknown (infinite scroll continues)
}
Automat State
| Field | Type | Description |
|---|---|---|
ready |
boolean |
true after first block loads |
loading |
boolean |
true while any block is in-flight |
totalRows |
number |
Known row count (-1 if unknown) |
sortModel |
SortModelItem[] |
Current sort state |
filterModel |
Record<string, unknown> |
Current filter state |
revision |
number |
Increments on reload/sort/filter change |
error |
string | null |
Last fetch error message |
Automat Actions
| Action | Description |
|---|---|
reload() |
Bumps revision, clears cache, forces full refetch |
setSort(sortModel) |
Updates sort model + reloads |
setFilter(filterModel) |
Updates filter model + reloads |
<AutomatGrid /> Props
| Prop | Type | Default | Description |
|---|---|---|---|
automat |
Automat<GridAutomatState, GridAutomatActions> |
required | The grid automat store |
columnDefs |
ColDef[] |
required | AG Grid column definitions |
defaultColDef |
ColDef |
{ flex: 1, minWidth: 100, sortable: true, resizable: true } |
Default column config |
rowIdField |
string |
'id' |
Row data field used for stable row IDs |
cacheBlockSize |
number |
automat's blockSize |
Override cache block size |
rowBuffer |
number |
0 |
Extra rows rendered outside viewport |
infiniteInitialRowCount |
number |
200 |
Scrollbar size hint |
maxConcurrentDatasourceRequests |
number |
2 |
Parallel block fetches |
maxBlocksInCache |
number |
10 |
Max cached blocks before eviction |
blockLoadDebounceMillis |
number |
100 |
Scroll debounce |
height |
string | number |
'100%' |
Container height |
width |
string | number |
'100%' |
Container width |
rowHeight |
number |
AG Grid default (42) | Row height in pixels |
theme |
string |
'ag-theme-alpine ag-theme-alpine-dark' |
Container theme class |
modules |
Module[] |
InfiniteRowModel + Text/Number/Date/Custom filters | Additional AG Grid modules |
onGridApiReady |
(api: GridApi) => void |
— | Callback when AG Grid API is available |
className |
string |
— | Container CSS class |
style |
CSSProperties |
— | Container inline styles |
Data Flow
┌─────────────────────────────────────────────────────────┐
│ AutomatGrid (PureComponent) │
│ │
│ constructor: reads automat.state synchronously │
│ componentDidMount: subscribes with selector │
│ componentDidUpdate: if revision changed → reset ds │
│ │
│ ┌──────────────┐ ┌────────────────────────┐ │
│ │ AG Grid │────▶│ IDatasource.getRows() │ │
│ │ (infinite │ │ calls automat._fetcher │ │
│ │ row model) │◀────│ → successCallback() │ │
│ └──────────────┘ └────────────────────────┘ │
│ │ │ │
│ onSortChanged ──────▶ automat.actions.setSort() │
│ onFilterChanged ────▶ automat.actions.setFilter() │
│ │ │ │
│ └──── revision bump ─────┘ │
└─────────────────────────────────────────────────────────┘
Running the Demo
npm install
npm run dev
The demo generates 10,000 fake product rows and serves them through a simulated async fetcher with artificial latency. Open http://localhost:5173 to see infinite scroll in action.
Project Structure
src/
├── lib/ # Reusable library
│ ├── createGridAutomat.ts # Automat factory
│ ├── AutomatGrid.tsx # PureComponent grid wrapper
│ └── index.ts # Barrel exports
└── demo/ # Demo app
├── demoAutomat.ts # 10k fake rows + fetcher
└── DemoGrid.tsx # Demo consumer component
License
0BSD