🪝 Custom Hooks Guide

Index

  1. Available Hooks
  2. Context Hooks
  3. Utility Hooks
  4. Advanced Hooks
  5. Creating Custom Hooks
  6. Common Patterns

Available Hooks

Quick Summary

HookPurposeLocation
useThemeAccess theme (colors, dark/light mode)contexts/ThemeContext
useI18nTranslations and languagecontexts/I18nContext
useAppGlobal app state (user, favorites)contexts/AppContext
useToastShow toast notificationscontexts/ToastContext
useFeatureFlagsFeature flag managementcontexts/FeatureFlagsContext
usePerformancePerformance monitoringcontexts/PerformanceContext
useDebounceDebounce values for searcheshooks/useDebounce
useKeyboardDetect keyboard statehooks/useKeyboard
useRefreshControlEasy pull-to-refreshhooks/useRefreshControl
useNetworkInternet connection statehooks/useNetwork
useFormAdvanced form handlinghooks/useForm
useBiometricsBiometric authenticationhooks/useBiometrics
useAppRatingApp store rating promptshooks/useAppRating
useAppUpdateApp update detectionhooks/useAppUpdate
useCacheCaching with TTLhooks/useCache
useAccessibilityAccessibility featureshooks/useAccessibility
useSecureStorageSecure data storagehooks/useSecureStorage
useClipboardClipboard operationshooks/useClipboard
useShareNative sharinghooks/useShare
useAppStateApp lifecycle eventshooks/useAppState

Context Hooks

useTheme

Access the application's theme system.

import { useTheme } from '@/contexts/ThemeContext';

function MyComponent() {
  const { 
    colors,      // Object with all current theme colors
    isDark,      // boolean - true if dark mode
    themeMode,   // 'light' | 'dark' | 'system'
    setTheme,    // (mode: ThemeMode) => void
    toggleTheme, // () => void - cycles between modes
  } = useTheme();

  return (
    <View style={{ backgroundColor: colors.background }}>
      <Text style={{ color: colors.text }}>
        Current mode: {themeMode}
      </Text>
      <Button 
        title={isDark ? 'Switch to Light' : 'Switch to Dark'}
        onPress={toggleTheme}
      />
    </View>
  );
}

Available colors:

colors = {
  primary, primaryLight, primaryDark,
  secondary, secondaryLight,
  accent, accentLight,
  success, successLight,
  warning, warningLight,
  error, errorLight,
  background, surface, surfaceSecondary,
  text, textSecondary, textTertiary, textInverse,
  border, borderLight,
  overlay,
}

useFeatureFlags

Manage feature flags with conditions and percentage rollouts.

import { useFeatureFlags, useFlag } from '@/contexts/FeatureFlagsContext';

function MyComponent() {
  const { 
    flags,           // All flags with effective values
    isEnabled,       // (key: string) => boolean
    setOverride,     // (key: string, value: boolean | null) => void
    clearOverrides,  // () => void
    setContext,      // (context: UserContext) => void
  } = useFeatureFlags();

  // Simple usage with useFlag helper
  const isDarkModeEnabled = useFlag('dark_mode');
  const areBetaFeaturesEnabled = useFlag('beta_features');

  // Check feature before rendering
  if (!isEnabled('premium_feature')) {
    return <UpgradePrompt />;
  }

  return <PremiumContent />;
}

// Set user context for conditional flags
function App() {
  const { setContext } = useFeatureFlags();

  useEffect(() => {
    setContext({
      userId: 'user-123',
      platform: Platform.OS,
      appVersion: '1.0.0',
    });
  }, []);
}

usePerformance

Monitor app performance with timers and metrics.

import { usePerformance, useScreenPerformance, useTimedOperation } from '@/contexts/PerformanceContext';

// Track screen render time
function MyScreen() {
  useScreenPerformance('MyScreen');
  
  return <View>...</View>;
}

// Time specific operations
function DataLoader() {
  const { measureAsync, startTimer, endTimer } = useTimedOperation();

  const loadData = async () => {
    // Option 1: Measure async function
    const data = await measureAsync('fetchData', async () => {
      return await api.fetchData();
    });

    // Option 2: Manual timing
    startTimer('processData');
    const processed = processData(data);
    endTimer('processData');
  };
}

// Get performance report
function DebugScreen() {
  const { generateReport, clearMetrics } = usePerformance();

  const showReport = () => {
    const report = generateReport();
    console.log('Average screen render:', report.summary.averageScreenRenderTime);
    console.log('Slowest screen:', report.summary.slowestScreen);
  };
}

Utility Hooks

All utility hooks are available from @/hooks:

import { 
  useDebounce, 
  useDebouncedCallback,
  useKeyboard, 
  useKeyboardDismiss,
  useRefreshControl, 
  useNetwork, 
  useIsOnline,
  useForm, 
  validators,
  useBiometrics,
  useAppRating,
  useAppUpdate,
  useCache,
  useAccessibility,
} from '@/hooks';

useAppRating

Smart app store rating prompts based on user engagement.

import { useAppRating } from '@/hooks';

function MyComponent() {
  const {
    canShowPrompt,       // Whether conditions are met to show prompt
    hasRated,            // User has already rated
    trackPositiveAction, // Track engagement (completes task, etc.)
    promptIfReady,       // Show prompt if conditions met
    requestReview,       // Force show review prompt
    markAsRated,         // Mark user as having rated
  } = useAppRating({
    minLaunches: 3,              // Minimum app launches
    minDaysSinceFirstLaunch: 3,  // Days since install
    minDaysBetweenPrompts: 30,   // Days between prompts
    minPositiveActions: 5,       // Positive actions required
  });

  // Track positive user actions
  const completeTask = () => {
    // ... task logic
    trackPositiveAction();
  };

  // Check and show rating prompt at appropriate time
  useEffect(() => {
    if (canShowPrompt) {
      promptIfReady();
    }
  }, [canShowPrompt]);
}

useAppUpdate

Detect and prompt for app updates.

import { useAppUpdate } from '@/hooks';

function App() {
  const {
    currentVersion,      // Current app version
    latestVersion,       // Latest available version
    isUpdateAvailable,   // Update is available
    isForceUpdate,       // Update is mandatory
    isChecking,          // Currently checking
    checkForUpdate,      // Manual check
    checkIfNeeded,       // Check if interval passed
    promptForUpdate,     // Show update dialog
    openStore,           // Open app store
  } = useAppUpdate({
    checkInterval: 24 * 60 * 60 * 1000, // 24 hours
    versionCheckUrl: 'https://api.example.com/version',
    forceUpdateVersions: ['0.9.0'], // Versions that must update
  });

  useEffect(() => {
    checkIfNeeded();
  }, []);

  useEffect(() => {
    if (isUpdateAvailable) {
      promptForUpdate({
        title: 'Update Available',
        message: `Version ${latestVersion} is available!`,
        onUpdate: () => console.log('User chose to update'),
      });
    }
  }, [isUpdateAvailable]);
}

useCache

Caching layer with TTL support.

import { useCache, useCacheManager } from '@/hooks';

// Cache a specific data fetch
function UserProfile({ userId }) {
  const { data, isLoading, refresh, invalidate } = useCache(
    `user-${userId}`,
    () => fetchUser(userId),
    {
      ttl: 5 * 60 * 1000, // 5 minutes
      persist: true,      // Save to AsyncStorage
    }
  );

  return (
    <View>
      {isLoading ? <Spinner /> : <Text>{data?.name}</Text>}
      <Button title="Refresh" onPress={refresh} />
    </View>
  );
}

// Global cache management
function Settings() {
  const cache = useCacheManager();

  const clearAllCache = async () => {
    await cache.clear();
  };

  const clearUserCache = async () => {
    await cache.invalidatePattern('user-.*');
  };

  const getCacheStats = () => {
    const stats = cache.getStats();
    console.log('Cached items:', stats.size);
  };
}

useAccessibility

Handle accessibility features and preferences.

import { useAccessibility, useResponsiveFontSize } from '@/hooks';

function MyComponent() {
  const {
    isScreenReaderEnabled,  // VoiceOver/TalkBack active
    isReduceMotionEnabled,  // System reduce motion
    shouldReduceMotion,     // Combined system + user pref
    shouldUseLargeText,     // Large text preference
    shouldUseHighContrast,  // High contrast preference
    preferences,            // User accessibility preferences
    updatePreferences,      // Update user preferences
    announceForAccessibility, // Screen reader announcement
    getScaledFontSize,      // Get scaled font size
    getAnimationDuration,   // Get animation duration (0 if reduced)
  } = useAccessibility();

  // Responsive font sizes
  const { getResponsiveSize } = useResponsiveFontSize();

  const handleAction = () => {
    announceForAccessibility('Action completed successfully');
  };

  return (
    <View>
      <Text style={{ fontSize: getScaledFontSize(16) }}>
        Accessibility-aware text
      </Text>
      
      <Animated.View
        style={{
          transform: [{
            translateX: shouldReduceMotion ? 0 : animatedValue
          }]
        }}
      />

      <Switch
        value={preferences.largeText}
        onValueChange={(value) => updatePreferences({ largeText: value })}
        accessibilityLabel="Enable large text"
      />
    </View>
  );
}

useBiometrics

Biometric authentication (Face ID, Touch ID, Fingerprint).

import { useBiometrics, useBiometricGuard } from '@/hooks';

function SecuritySettings() {
  const {
    isAvailable,      // Biometrics available on device
    isEnabled,        // User has enabled biometrics
    biometricLabel,   // "Face ID", "Touch ID", etc.
    authenticate,     // Trigger authentication
    enable,           // Enable biometrics
    disable,          // Disable biometrics
  } = useBiometrics();

  return (
    <View>
      {isAvailable && (
        <SettingsRow
          title={`Use ${biometricLabel}`}
          value={isEnabled}
          onToggle={isEnabled ? disable : enable}
        />
      )}
    </View>
  );
}

// Guard sensitive screens
function SensitiveScreen() {
  const { requiresAuth, requireAuth, isLoading } = useBiometricGuard();

  useEffect(() => {
    if (requiresAuth) {
      requireAuth('Authenticate to view sensitive data');
    }
  }, [requiresAuth]);

  if (isLoading || requiresAuth) {
    return <AuthPrompt />;
  }

  return <SensitiveContent />;
}

useNetwork

Monitor network connectivity.

import { useNetwork, useIsOnline } from '@/hooks';

// Detailed information
function NetworkStatus() {
  const { isConnected, isInternetReachable, type, isLoading } = useNetwork();

  if (isLoading) return <Text>Checking connection...</Text>;

  return (
    <View>
      <Text>Connected: {isConnected ? 'Yes' : 'No'}</Text>
      <Text>Internet: {isInternetReachable ? 'Yes' : 'No'}</Text>
      <Text>Type: {type}</Text>
    </View>
  );
}

// Simplified
function SimpleCheck() {
  const isOnline = useIsOnline();
  
  return isOnline ? <App /> : <OfflineScreen />;
}

useForm

Advanced form handling with validation.

import { useForm, validators } from '@/hooks';

function RegisterForm() {
  const {
    values,
    errors,
    touched,
    isValid,
    isSubmitting,
    isDirty,
    setValue,
    handleSubmit,
    reset,
    getFieldProps,
  } = useForm({
    initialValues: { 
      email: '', 
      password: '', 
      confirmPassword: '' 
    },
    validationRules: {
      email: [
        validators.required('Email required'),
        validators.email('Invalid email'),
      ],
      password: [
        validators.required('Password required'),
        validators.minLength(8, 'Minimum 8 characters'),
        validators.strongPassword(),
      ],
      confirmPassword: [
        validators.required('Confirm your password'),
        validators.match('password', 'Passwords do not match'),
      ],
    },
    onSubmit: async (values) => {
      await registerUser(values.email, values.password);
    },
  });

  return (
    <View>
      <Input
        {...getFieldProps('email')}
        placeholder="Email"
        error={touched.email ? errors.email : undefined}
      />
      
      <Input
        {...getFieldProps('password')}
        placeholder="Password"
        secureTextEntry
        error={touched.password ? errors.password : undefined}
      />
      
      <Button
        title="Register"
        onPress={handleSubmit}
        loading={isSubmitting}
        disabled={!isValid}
      />
    </View>
  );
}

Available validators:

validators.required(message?)       // Required field
validators.email(message?)          // Valid email
validators.minLength(min, message?) // Minimum length
validators.maxLength(max, message?) // Maximum length
validators.pattern(regex, message)  // Regex pattern
validators.match(field, message)    // Match another field
validators.phone(message?)          // Valid phone number
validators.url(message?)            // Valid URL
validators.numeric(message?)        // Numeric value
validators.min(min, message?)       // Minimum number
validators.max(max, message?)       // Maximum number
validators.date(message?)           // Valid date
validators.strongPassword(message?) // Strong password
validators.creditCard(message?)     // Valid credit card
validators.custom(fn, message)      // Custom validation

Advanced Hooks

useDebounce

Debounce values (ideal for searches).

import { useDebounce, useDebouncedCallback } from '@/hooks';

// Debounce value
function SearchComponent() {
  const [search, setSearch] = useState('');
  const debouncedSearch = useDebounce(search, 300);

  useEffect(() => {
    if (debouncedSearch) {
      performSearch(debouncedSearch);
    }
  }, [debouncedSearch]);

  return (
    <Input value={search} onChangeText={setSearch} placeholder="Search..." />
  );
}

// Debounce callback
function AutoSave() {
  const debouncedSave = useDebouncedCallback((text: string) => {
    saveToServer(text);
  }, 1000);

  return <Input onChangeText={debouncedSave} />;
}

Creating Custom Hooks

Base Template

// hooks/useMyHook.ts
import { useState, useEffect, useCallback, useMemo } from 'react';

interface UseMyHookOptions {
  initialValue?: string;
  onSuccess?: () => void;
}

interface UseMyHookReturn {
  value: string;
  isLoading: boolean;
  setValue: (value: string) => void;
  reset: () => void;
}

export function useMyHook(options: UseMyHookOptions = {}): UseMyHookReturn {
  const { initialValue = '', onSuccess } = options;
  
  const [value, setValue] = useState(initialValue);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    // Initialization logic
  }, []);

  const handleChange = useCallback((newValue: string) => {
    setValue(newValue);
    onSuccess?.();
  }, [onSuccess]);

  const reset = useCallback(() => {
    setValue(initialValue);
  }, [initialValue]);

  return {
    value,
    isLoading,
    setValue: handleChange,
    reset,
  };
}

Hook Rules

  1. Name with "use" - Always start with use (useSearch, useAuth, etc.)
  2. Don't call conditionally - Hooks must be called in the same order
  3. Only in components or hooks - Don't use in regular functions
  4. Memoize callbacks - Use useCallback for functions passed as props
  5. Memoize expensive calculations - Use useMemo for heavy operations

Common Patterns

Combine Multiple Hooks

function useAppData() {
  const { user } = useApp();
  const { colors, isDark } = useTheme();
  const { t } = useI18n();
  const { showToast } = useToast();

  return { user, colors, isDark, t, showToast };
}

function MyScreen() {
  const { user, colors, t, showToast } = useAppData();
}

Hook with React Query

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

export function useProducts() {
  const queryClient = useQueryClient();

  const productsQuery = useQuery({
    queryKey: ['products'],
    queryFn: fetchProducts,
  });

  const createMutation = useMutation({
    mutationFn: createProduct,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['products'] });
    },
  });

  return {
    products: productsQuery.data ?? [],
    isLoading: productsQuery.isLoading,
    error: productsQuery.error,
    createProduct: createMutation.mutateAsync,
    isCreating: createMutation.isPending,
  };
}

Hooks Checklist

  • Descriptive name with use prefix
  • TypeScript types defined
  • useCallback for functions
  • useMemo for expensive calculations
  • Cleanup handling in useEffect
  • Usage documentation
  • Implementation examples