# React Hooks

React hooks for integrating with toast state and managing toast notifications programmatically within React components.

## Capabilities

### useSonner Hook

React hook for accessing the current toast state, useful for building custom toast-aware components or debugging.

```typescript { .api }
/**
 * React hook that provides access to current toast state
 * @returns Object containing array of active toasts
 */
function useSonner(): {
  toasts: ToastT[];
};

interface ToastT {
  id: number | string;
  title?: (() => React.ReactNode) | React.ReactNode;
  type?: "normal" | "action" | "success" | "info" | "warning" | "error" | "loading" | "default";
  icon?: React.ReactNode;
  jsx?: React.ReactNode;
  richColors?: boolean;
  invert?: boolean;
  closeButton?: boolean;
  dismissible?: boolean;
  description?: (() => React.ReactNode) | React.ReactNode;
  duration?: number;
  delete?: boolean;
  action?: Action | React.ReactNode;
  cancel?: Action | React.ReactNode;
  onDismiss?: (toast: ToastT) => void;
  onAutoClose?: (toast: ToastT) => void;
  promise?: Promise<any> | (() => Promise<any>);
  cancelButtonStyle?: React.CSSProperties;
  actionButtonStyle?: React.CSSProperties;
  style?: React.CSSProperties;
  unstyled?: boolean;
  className?: string;
  classNames?: ToastClassnames;
  descriptionClassName?: string;
  position?: Position;
}
```

## Usage Examples

### Basic Toast State Access

```typescript
import React from "react";
import { useSonner, toast } from "sonner";

function ToastStatus() {
  const { toasts } = useSonner();
  
  return (
    <div>
      <p>Active toasts: {toasts.length}</p>
      <button onClick={() => toast("New toast!")}>
        Add Toast
      </button>
      <button onClick={() => toast.dismiss()}>
        Clear All
      </button>
    </div>
  );
}
```

### Toast Type Counter

```typescript
import React from "react";
import { useSonner, toast } from "sonner";

function ToastAnalytics() {
  const { toasts } = useSonner();
  
  const toastCounts = React.useMemo(() => {
    return toasts.reduce((counts, toast) => {
      const type = toast.type || "default";
      counts[type] = (counts[type] || 0) + 1;
      return counts;
    }, {});
  }, [toasts]);
  
  return (
    <div className="toast-analytics">
      <h3>Current Toast Status</h3>
      {Object.entries(toastCounts).map(([type, count]) => (
        <div key={type}>
          {type}: {count}
        </div>
      ))}
      
      <div className="controls">
        <button onClick={() => toast.success("Success!")}>
          Add Success
        </button>
        <button onClick={() => toast.error("Error!")}>
          Add Error
        </button>
        <button onClick={() => toast.loading("Loading...")}>
          Add Loading
        </button>
      </div>
    </div>
  );
}
```

### Loading State Indicator

```typescript
import React from "react";
import { useSonner } from "sonner";

function LoadingIndicator() {
  const { toasts } = useSonner();
  
  const hasLoadingToasts = toasts.some(toast => toast.type === "loading");
  const loadingCount = toasts.filter(toast => toast.type === "loading").length;
  
  if (!hasLoadingToasts) {
    return null;
  }
  
  return (
    <div className="loading-indicator">
      <div className="spinner" />
      <span>
        {loadingCount === 1 
          ? "Processing..." 
          : `${loadingCount} operations in progress...`
        }
      </span>
    </div>
  );
}
```

### Custom Toast Manager

```typescript
import React from "react";
import { useSonner, toast } from "sonner";

function ToastManager() {
  const { toasts } = useSonner();
  
  const dismissToast = (id: string | number) => {
    toast.dismiss(id);
  };
  
  const dismissByType = (type: string) => {
    toasts
      .filter(t => t.type === type)
      .forEach(t => toast.dismiss(t.id));
  };
  
  const dismissOlderThan = (minutes: number) => {
    const cutoff = Date.now() - (minutes * 60 * 1000);
    toasts
      .filter(t => {
        // Approximate age based on ID if it's a timestamp
        const toastTime = typeof t.id === 'number' ? t.id : Date.now();
        return toastTime < cutoff;
      })
      .forEach(t => toast.dismiss(t.id));
  };
  
  return (
    <div className="toast-manager">
      <h3>Toast Manager ({toasts.length} active)</h3>
      
      <div className="toast-list">
        {toasts.map(toast => (
          <div key={toast.id} className="toast-item">
            <span className={`toast-type ${toast.type}`}>
              {toast.type || "default"}
            </span>
            <span className="toast-title">
              {typeof toast.title === "function" ? toast.title() : toast.title}
            </span>
            <button onClick={() => dismissToast(toast.id)}>
              ×
            </button>
          </div>
        ))}
      </div>
      
      <div className="controls">
        <button onClick={() => dismissByType("error")}>
          Clear Errors
        </button>
        <button onClick={() => dismissByType("success")}>
          Clear Success
        </button>
        <button onClick={() => dismissOlderThan(5)}>
          Clear Old (5+ min)
        </button>
        <button onClick={() => toast.dismiss()}>
          Clear All
        </button>
      </div>
    </div>
  );
}
```

### Toast Persistence Monitor

```typescript
import React from "react";
import { useSonner } from "sonner";

function ToastPersistenceMonitor() {
  const { toasts } = useSonner();
  const [persistentToasts, setPersistentToasts] = React.useState([]);
  
  React.useEffect(() => {
    // Track toasts that have been around for a long time
    const persistent = toasts.filter(toast => {
      // Check if duration is Infinity or very long
      return toast.duration === Infinity || 
             (toast.duration && toast.duration > 30000) ||
             toast.type === "loading";
    });
    
    setPersistentToasts(persistent);
  }, [toasts]);
  
  if (persistentToasts.length === 0) {
    return null;
  }
  
  return (
    <div className="persistence-monitor">
      <h4>⚠️ Persistent Toasts ({persistentToasts.length})</h4>
      <p>These toasts won't auto-dismiss:</p>
      <ul>
        {persistentToasts.map(toast => (
          <li key={toast.id}>
            {toast.type}: {typeof toast.title === "function" ? toast.title() : toast.title}
          </li>
        ))}
      </ul>
    </div>
  );
}
```

### Performance Monitor

```typescript
import React from "react";
import { useSonner } from "sonner";

function ToastPerformanceMonitor() {
  const { toasts } = useSonner();
  const [maxToasts, setMaxToasts] = React.useState(0);
  const [toastHistory, setToastHistory] = React.useState([]);
  
  React.useEffect(() => {
    setMaxToasts(Math.max(maxToasts, toasts.length));
    
    // Keep a history of toast count changes
    setToastHistory(prev => [
      ...prev.slice(-20), // Keep last 20 entries
      { timestamp: Date.now(), count: toasts.length }
    ]);
  }, [toasts.length]);
  
  const averageToasts = React.useMemo(() => {
    if (toastHistory.length === 0) return 0;
    const sum = toastHistory.reduce((acc, entry) => acc + entry.count, 0);
    return (sum / toastHistory.length).toFixed(1);
  }, [toastHistory]);
  
  return (
    <div className="performance-monitor">
      <h4>Toast Performance</h4>
      <div className="metrics">
        <div>Current: {toasts.length}</div>
        <div>Peak: {maxToasts}</div>
        <div>Average: {averageToasts}</div>
      </div>
      
      {toasts.length > 5 && (
        <div className="warning">
          ⚠️ High toast count may impact performance
        </div>
      )}
    </div>
  );
}
```

### Conditional Rendering Based on Toasts

```typescript
import React from "react";
import { useSonner } from "sonner";

function AppWithToastAwareness() {
  const { toasts } = useSonner();
  
  const hasErrors = toasts.some(t => t.type === "error");
  const hasLoading = toasts.some(t => t.type === "loading");
  const hasSuccessToasts = toasts.some(t => t.type === "success");
  
  return (
    <div className={`app ${hasErrors ? "has-errors" : ""}`}>
      <header>
        <h1>My App</h1>
        {hasLoading && <div className="loading-indicator">Processing...</div>}
      </header>
      
      <main>
        {hasErrors && (
          <div className="error-state">
            <p>Some operations failed. Check notifications for details.</p>
          </div>
        )}
        
        {hasSuccessToasts && (
          <div className="success-state">
            <p>✅ Recent successful operations</p>
          </div>
        )}
        
        <YourAppContent />
      </main>
      
      {toasts.length > 0 && (
        <div className="toast-summary">
          {toasts.length} active notification{toasts.length !== 1 ? "s" : ""}
        </div>
      )}
    </div>
  );
}
```

## Advanced Patterns

### Custom Toast Provider

```typescript
import React from "react";
import { useSonner, toast } from "sonner";

const ToastContext = React.createContext({
  toasts: [],
  addToast: (message, options) => {},
  clearToasts: () => {},
  hasToasts: false,
});

export function ToastProvider({ children }) {
  const { toasts } = useSonner();
  
  const contextValue = React.useMemo(() => ({
    toasts,
    addToast: (message, options) => toast(message, options),
    clearToasts: () => toast.dismiss(),
    hasToasts: toasts.length > 0,
  }), [toasts]);
  
  return (
    <ToastContext.Provider value={contextValue}>
      {children}
    </ToastContext.Provider>
  );
}

export function useToastContext() {
  return React.useContext(ToastContext);
}
```

### Toast State Synchronization

```typescript
import React from "react";
import { useSonner } from "sonner";

function useToastSync() {
  const { toasts } = useSonner();
  
  // Sync toast state to localStorage
  React.useEffect(() => {
    const toastData = toasts.map(t => ({
      id: t.id,
      type: t.type,
      title: typeof t.title === "string" ? t.title : "[React Element]",
      timestamp: Date.now()
    }));
    
    localStorage.setItem("recent-toasts", JSON.stringify(toastData));
  }, [toasts]);
  
  // Analytics tracking
  React.useEffect(() => {
    if (typeof window !== "undefined" && window.gtag) {
      toasts.forEach(toast => {
        if (toast.type === "error") {
          window.gtag("event", "toast_error", {
            error_type: toast.type,
            error_message: toast.title
          });
        }
      });
    }
  }, [toasts]);
}
```

## Common Use Cases

1. **Debug Panel**: Display current toast state during development
2. **Analytics**: Track toast usage patterns and error rates
3. **Performance Monitoring**: Watch for excessive toast creation
4. **Conditional UI**: Show/hide elements based on toast presence
5. **State Synchronization**: Sync toast state with external systems
6. **Custom Management**: Build advanced toast management interfaces

The `useSonner` hook provides real-time access to the current toast state, enabling these advanced patterns while maintaining performance through React's built-in optimization mechanisms.