# ⚡ react-virtual-aggridautomat > Opinionated, fully-virtualized AG Grid component powered by [react-automat](https://github.com/sebseb7/react-automat) — infinite-scroll data grids as React `PureComponent` with zero hooks, HOCs, or Context Providers. [![AG Grid](https://img.shields.io/badge/AG_Grid-v36-0084c7?style=flat-square)](https://www.ag-grid.com/) [![react-automat](https://img.shields.io/badge/react--automat-v0.1-crimson?style=flat-square)](https://github.com/sebseb7/react-automat) [![React 19](https://img.shields.io/badge/React-19-61dafb?style=flat-square)](https://react.dev/) --- ## What is this? A reusable `` PureComponent that wires AG Grid's [Infinite Row Model](https://www.ag-grid.com/react-data-grid/infinite-scrolling/) 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 `PureComponent` with synchronous constructor reads and fine-grained automat subscriptions ## Install ```bash 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 ```ts // 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 ```tsx // MyView.tsx import { PureComponent } from 'react'; import { AutomatGrid } from './lib'; import { myGridAutomat } from './myGridAutomat'; class MyView extends PureComponent { render() { return ( `€${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: ```tsx 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 (
{this.state.loading ? 'Loading…' : `${this.state.totalRows} rows`}
); } } ``` ## API Reference ### `createGridAutomat(options)` Creates a `react-automat` store to back the grid. | Option | Type | Default | Description | |---|---|---|---| | `fetchRows` | `(params: GridFetchParams) => Promise` | **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` ```ts { startRow: number; endRow: number; sortModel: SortModelItem[]; filterModel: Record; } ``` #### `GridFetchResult` ```ts { 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` | 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 | ### `` Props | Prop | Type | Default | Description | |---|---|---|---| | `automat` | `Automat` | **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 ```bash 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