Lazy loading/Infinite scrolling

Load data based on the visible viewport of the grid

CanvasGrid invokes onViewPortChange callback whenever user scrolls or pans around the spreadsheet.

This can be used to load data for the next set of visible rows.

Do note that lazy loading can affect calculations if cell dependents are out of the viewport and not loaded initially.

We recommend lazy loading only if calculations are moved to the server side.

import { CanvasGrid } from "@rowsncolumns/spreadsheet"

const App = () => {
  const [ sheetData, onChangeSheetData ] = useState<SheetData<T>>({})
  <CanvasGrid
    onViewPortChange={(viewport: ViewPortProps) => {
      const {
        columnStartIndex, 
        columnStopIndex,
        rowStartIndex,
        rowStopIndex,
        visibleColumnStartIndex,
        visibleColumnStopIndex,
        visibleRowStartIndex,
        visibleRowStopIndex      
      } = viewport
      
      // Throttle fetch request
      // Move it out of the render loop, this is just an example
      throttle(
        fetch(`/rows?start=${rowStartIndex}&end=${rowStopIndex}`)
          .then(rowData => {
            // Append new row data
            onChangeSheetData(prev => {
              const newRowData = prev[sheetId]
                  .splice(rowStartIndex, rowStopIndex - rowStartIndex, ...rowData)
              return {
                ...prev,
                [sheetId]: newRowData
              }
            })
          })
      , 300)
    }}
  />
}

Hooks for Lazy loading

@rowsncolumns/spreadsheet-state exports the useAsyncDatasource hook to easily load an asynchronous paged data source

Usage

import { useSpreadsheetState, useAsyncDataSource } from '@rowsncolumnse/spreadsheet-state'
import { CircularLoader } from '@rowsncolumns/ui'

const App = () => {
  const locale = 'en-US'
  const [sheets, onChangeSheets] = useState<Sheet[]>(mockSheets);
  const [sheetData, onChangeSheetData] = useState<SheetData<CellData>>({});
  const { enqueueCalculation, activeSheetId } = useSpreadsheetState({
    sheets,
    sheetData,
    onChangeSheetData,
    onChangeSheets
  })
  const { onViewPortChange, isLoading } = useAsyncDatasource<CellData>({
    sheetId: activeSheetId,
    locale,
    onChangeSheetData,
    enqueueCalculation,
    getRowData: useCallback(
      async (sheetId, [rowStartIndex, rowStopIndex]) => {
        await new Promise((res) => setTimeout(res, 1000));
        return [
          null,
          {
            values: [
              null,
              {
                userEnteredValue: {
                  formulaValue: "=SUM(E11,4)",
                },
              },
            ],
          },
        ];
      },
      []
    ),
  });
  
  return (
    <>
      <CanvasGrid
        onViewPortChange={onViewPortChange}
      />
      {isLoading ? (
        <CircularLoader className="absolute left-1/2 bottom-1/2 z-10 bg-rnc-background/60 border-solid border-px border-rnc-border p-5 rounded-md" />
      ) : null}
  </>
  )
}

Last updated