Files
react-virtual-aggridautomat/README.md
2026-10-05 22:29:22 +02:00

8.6 KiB

⚡ react-virtual-aggridautomat

Opinionated, fully-virtualized AG Grid component powered by react-automat — infinite-scroll data grids as React PureComponent with zero hooks, HOCs, or Context Providers.

AG Grid react-automat React 19


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 PureComponent with 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