Hooks

Utility hooks for advanced spreadsheet functionality

The spreadsheet provides several utility hooks for common operations beyond the main useSpreadsheetState hook.

useNavigateToSheetRange

Navigate to a specific cell or range in a sheet, optionally with a visual flash effect.

Basic Usage

import { useNavigateToSheetRange } from "@rowsncolumns/spreadsheet";
import { useSpreadsheetState } from "@rowsncolumns/spreadsheet-state";

function MySpreadsheet() {
  const navigateToSheetRange = useNavigateToSheetRange();

  const handleGoToRange = () => {
    navigateToSheetRange?.({
      sheetId: 2,
      startRowIndex: 10,
      endRowIndex: 15,
      startColumnIndex: 5,
      endColumnIndex: 8,
    });
  };

  return (
    <button onClick={handleGoToRange}>
      Go to Sheet 2, Range F11:I16
    </button>
  );
}

Parameters

navigateToSheetRange(
  sheetRange: SheetRange,     // The range to navigate to
  enableFlash?: boolean,       // Show flash effect (default: true)
  flashColor?: Color,          // Flash color (default: "#ffcc00")
  flashDuration?: number       // Flash duration in ms (default: 1500)
)

Features

  • Sheet switching: Automatically switches to the target sheet

  • Cell positioning: Sets active cell to the top-left of the range

  • Scrolling: Scrolls the range into view

  • Visual feedback: Optional flash effect to highlight the range

Example with Custom Flash

const navigateToSheetRange = useNavigateToSheetRange();

// Navigate with red flash for 2 seconds
navigateToSheetRange?.(
  {
    sheetId: 1,
    startRowIndex: 5,
    endRowIndex: 10,
    startColumnIndex: 3,
    endColumnIndex: 6,
  },
  true,      // Enable flash
  "#ff0000", // Red color
  2000       // 2 seconds
);

Use Cases

  • Jump to errors: Navigate to cells with validation errors

  • Go to formulas: Jump to cells referenced in formulas

  • Search results: Navigate to search result cells

  • Links: Implement cell links to other sheet locations

  • Named ranges: Jump to named range locations

useLoadingIndicator

Display a loading indicator during async operations.

Basic Usage

import { useLoadingIndicator, LoadingIndicator } from "@rowsncolumns/spreadsheet";

function MySpreadsheet() {
  const [showLoader, hideLoader] = useLoadingIndicator();

  const handleAsyncOperation = async () => {
    showLoader();
    try {
      await someAsyncOperation();
    } finally {
      hideLoader();
    }
  };

  return (
    <>
      <button onClick={handleAsyncOperation}>
        Load Data
      </button>
      <CanvasGrid />
      <LoadingIndicator />
    </>
  );
}

API

The hook returns a tuple:

const [showLoader, hideLoader] = useLoadingIndicator();
  • showLoader(): Shows the loading indicator

  • hideLoader(): Hides the loading indicator

Example with Multiple Operations

function SpreadsheetWithAsyncOps() {
  const [showLoader, hideLoader] = useLoadingIndicator();

  const loadData = async () => {
    showLoader();
    try {
      const data = await fetchSpreadsheetData();
      setSheetData(data);
    } catch (error) {
      console.error("Failed to load data:", error);
    } finally {
      hideLoader();
    }
  };

  const saveData = async () => {
    showLoader();
    try {
      await saveSpreadsheetData(sheetData);
    } finally {
      hideLoader();
    }
  };

  return (
    <>
      <button onClick={loadData}>Load</button>
      <button onClick={saveData}>Save</button>
      <CanvasGrid />
      <LoadingIndicator />
    </>
  );
}

Component

The LoadingIndicator component must be rendered in your app (typically at the root level):

<SpreadsheetProvider>
  <Toolbar />
  <CanvasGrid />
  <LoadingIndicator />  {/* Required for the hook to work */}
</SpreadsheetProvider>

Use Cases

  • Data loading: Show loader while fetching data from server

  • File import: Display during Excel/CSV import operations

  • Calculations: Show during long-running calculations

  • Export operations: Display while generating exports

useSpreadsheetApi

Access the imperative API for programmatic control of the spreadsheet.

Basic Usage

import { useSpreadsheetApi } from "@rowsncolumns/spreadsheet";

function MyComponent() {
  const api = useSpreadsheetApi();

  const updateCell = () => {
    api?.getActiveSheet()
      ?.getRange({ rowIndex: 1, columnIndex: 1 })
      .setValue("Hello World")
      .setFormat("backgroundColor", "#ffcc00");
  };

  return <button onClick={updateCell}>Update A1</button>;
}

See Imperative Spreadsheet API for complete documentation.

Complete Example

import React, { useState } from "react";
import {
  SpreadsheetProvider,
  CanvasGrid,
  LoadingIndicator,
  useLoadingIndicator,
  useNavigateToSheetRange,
  useSpreadsheetApi,
} from "@rowsncolumns/spreadsheet";
import {
  useSpreadsheetState,
  type SheetData,
} from "@rowsncolumns/spreadsheet-state";

function SpreadsheetWithHooks() {
  const [sheets, setSheets] = useState([
    { sheetId: 1, rowCount: 100, columnCount: 26, title: "Sheet 1" },
    { sheetId: 2, rowCount: 100, columnCount: 26, title: "Sheet 2" },
  ]);
  const [sheetData, setSheetData] = useState<SheetData>({});

  return (
    <SpreadsheetProvider>
      <SpreadsheetControls
        sheets={sheets}
        sheetData={sheetData}
        onChangeSheets={setSheets}
        onChangeSheetData={setSheetData}
      />
    </SpreadsheetProvider>
  );
}

function SpreadsheetControls({ sheets, sheetData, onChangeSheets, onChangeSheetData }) {
  const [showLoader, hideLoader] = useLoadingIndicator();
  const navigateToSheetRange = useNavigateToSheetRange();
  const api = useSpreadsheetApi();

  const {
    activeCell,
    activeSheetId,
    selections,
    getCellData,
    onChangeActiveCell,
    onChangeSelections,
    onChangeActiveSheet,
  } = useSpreadsheetState({
    sheets,
    sheetData,
    onChangeSheets,
    onChangeSheetData,
  });

  const findAndNavigate = async () => {
    showLoader();
    try {
      // Simulate search operation
      await new Promise(resolve => setTimeout(resolve, 1000));
      
      // Navigate to found cell
      navigateToSheetRange?.({
        sheetId: 2,
        startRowIndex: 15,
        endRowIndex: 15,
        startColumnIndex: 5,
        endColumnIndex: 5,
      });
    } finally {
      hideLoader();
    }
  };

  const updateCellProgrammatically = () => {
    api?.getActiveSheet()
      ?.getRange({ rowIndex: 1, columnIndex: 1 })
      .setValue("Updated via API");
  };

  return (
    <>
      <div className="toolbar">
        <button onClick={findAndNavigate}>
          Find and Navigate
        </button>
        <button onClick={updateCellProgrammatically}>
          Update A1
        </button>
      </div>

      <CanvasGrid
        sheetId={activeSheetId}
        activeCell={activeCell}
        selections={selections}
        getCellData={getCellData}
        onChangeActiveCell={onChangeActiveCell}
        onChangeSelections={onChangeSelections}
        onChangeActiveSheet={onChangeActiveSheet}
      />

      <LoadingIndicator />
    </>
  );
}

export default SpreadsheetWithHooks;

Hook Dependencies

useNavigateToSheetRange

Requires SpreadsheetProvider context:

<SpreadsheetProvider>
  {/* Component using useNavigateToSheetRange */}
</SpreadsheetProvider>

useLoadingIndicator

Requires LoadingIndicator component to be rendered:

<SpreadsheetProvider>
  <YourComponents />
  <LoadingIndicator />  {/* Required */}
</SpreadsheetProvider>

useSpreadsheetApi

Requires SpreadsheetProvider and active spreadsheet state:

<SpreadsheetProvider>
  {/* Component using useSpreadsheetApi */}
</SpreadsheetProvider>

Best Practices

useNavigateToSheetRange

  1. User feedback: Always provide visual feedback when navigating (use flash effect)

  2. Bounds checking: Ensure the range exists before navigating

  3. Sheet existence: Verify the target sheet exists

useLoadingIndicator

  1. Always hide: Use try-finally to ensure hideLoader is called

  2. Error handling: Show errors to users if operations fail

  3. Timeout: Consider adding timeouts for long operations

  4. Multiple operations: Use separate show/hide pairs for different operations

useSpreadsheetApi

  1. Null checking: Always use optional chaining (api?.)

  2. State management: Prefer declarative state over imperative API when possible

  3. Batch operations: Use batch methods for multiple changes

TypeScript Support

All hooks are fully typed:

import type { SheetRange, Color } from "@rowsncolumns/spreadsheet";

const navigateToSheetRange = useNavigateToSheetRange();
// Type: (range: SheetRange, flash?: boolean, color?: Color, duration?: number) => void

const [showLoader, hideLoader] = useLoadingIndicator();
// Type: [() => void, () => void]

const api = useSpreadsheetApi();
// Type: SpreadsheetAPI | null

Troubleshooting

useNavigateToSheetRange not working

Ensure you're inside SpreadsheetProvider:

<SpreadsheetProvider>
  <YourComponent />  {/* Hook works here */}
</SpreadsheetProvider>

LoadingIndicator not showing

Verify the component is rendered:

<LoadingIndicator />  {/* Must be present */}

useSpreadsheetApi returns null

The API is only available after the spreadsheet is mounted:

useEffect(() => {
  if (api) {
    // API is ready
  }
}, [api]);

Last updated

Was this helpful?