# Hooks

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

```tsx
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

```typescript
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

```tsx
const navigateToSheetRange = useNavigateToSheetRange();

// Navigate with red flash for 2 seconds
navigateToSheetRange?.(
  {
    sheetId: 1,
    startRowIndex: 5,
    endRowIndex: 10,
    startColumnIndex: 3,
    endColumnIndex: 6,
  },
  {
    enableFlash: true,      // Enable flash
    flashColor: "#ff0000", // Red color
    flashDuration: 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

```tsx
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:

```typescript
const [showLoader, hideLoader] = useLoadingIndicator();
```

* `showLoader()`: Shows the loading indicator
* `hideLoader()`: Hides the loading indicator

### Example with Multiple Operations

```tsx
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):

```tsx
<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

```tsx
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](/getting-started/imperative-spreadsheet-api.md) for complete documentation.

## Complete Example

```tsx
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:

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

### useLoadingIndicator

Requires `LoadingIndicator` component to be rendered:

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

### useSpreadsheetApi

Requires `SpreadsheetProvider` and active spreadsheet state:

```tsx
<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:

```typescript
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`:

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

### LoadingIndicator not showing

Verify the component is rendered:

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

### useSpreadsheetApi returns null

The API is only available after the spreadsheet is mounted:

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.rowsncolumns.app/configuration/api/hooks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
