or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdpermission-management.mdreact-hooks.md
tile.json

react-hooks.mddocs/

React Hooks

React hook integration for declarative permission management with automatic status fetching, lifecycle handling, and memoized callbacks.

Capabilities

usePermissions Hook

React hook for managing permissions declaratively with automatic fetching on mount and lifecycle-aware state management.

/**
 * Get or ask permission for protected functionality within the app.
 * Returns the permission response after fetching or asking it.
 * The hook fetches the permissions when rendered, by default.
 * To ask the user permission, use the askPermission callback or ask option.
 * 
 * @param type - Single permission type or array of permission types
 * @param options - Optional configuration object
 * @returns Tuple of [permission, askPermission, getPermission]
 * @deprecated Package deprecated - use individual Expo modules instead
 */
function usePermissions(
  type: PermissionType | PermissionType[],
  options?: PermissionsOptions
): [PermissionResponse | undefined, () => Promise<void>, () => Promise<void>];

interface PermissionsOptions {
  /** If it should ask the permissions when mounted, defaults to false */
  ask?: boolean;
  /** If it should fetch information about the permissions when mounted, defaults to true */
  get?: boolean;
}

Usage Examples:

import React from 'react';
import { usePermissions, CAMERA, MEDIA_LIBRARY, LOCATION_FOREGROUND, NOTIFICATIONS } from 'expo-permissions';
import { Camera } from 'expo-camera';
import { Button, Text, View } from 'react-native';

// Basic usage with single permission
function CameraScreen() {
  const [permission, askPermission, getPermission] = usePermissions(CAMERA);
  
  if (!permission) {
    return <Text>Loading permission status...</Text>;
  }
  
  if (!permission.granted) {
    return (
      <View>
        <Text>Camera permission not granted</Text>
        <Button title="Grant Camera Permission" onPress={askPermission} />
      </View>
    );
  }
  
  return <Camera style={{ flex: 1 }} />;
}

// Multiple permissions
function MediaScreen() {
  const [permissions, askPermissions] = usePermissions([CAMERA, MEDIA_LIBRARY]);
  
  return (
    <View>
      {permissions?.granted ? (
        <Text>All permissions granted!</Text>
      ) : (
        <Button title="Request Permissions" onPress={askPermissions} />
      )}
    </View>
  );
}

// Auto-ask on mount
function AutoRequestScreen() {
  const [permission] = usePermissions(LOCATION_FOREGROUND, { ask: true });
  
  return (
    <View>
      <Text>Status: {permission?.status || 'Loading...'}</Text>
    </View>
  );
}

// Manual control (no auto-fetch)
function ManualScreen() {
  const [permission, askPermission, getPermission] = usePermissions(
    NOTIFICATIONS, 
    { get: false }
  );
  
  return (
    <View>
      <Button title="Check Permission" onPress={getPermission} />
      <Button title="Request Permission" onPress={askPermission} />
      {permission && <Text>Status: {permission.status}</Text>}
    </View>
  );
}

Hook Behavior

Automatic Fetching

  • Default Behavior: Hook automatically calls getAsync on mount when get: true (default)
  • Disabled Fetching: Set get: false to prevent automatic permission checking
  • Auto-Request: Set ask: true to automatically request permissions on mount

Lifecycle Management

  • Mount/Unmount Tracking: Hook tracks component mount state to prevent state updates on unmounted components
  • Cleanup: Automatically handles cleanup when component unmounts
  • Memoized Callbacks: askPermission and getPermission callbacks are memoized based on permission types

State Management

  • Initial State: Permission state starts as undefined until first fetch completes
  • State Updates: Only updates component state if component is still mounted
  • Type Safety: Full TypeScript support with proper type inference

Return Value Details

The hook returns a tuple with three elements:

type UsePermissionsReturn = [
  PermissionResponse | undefined,  // Current permission state
  () => Promise<void>,             // Ask permission function
  () => Promise<void>              // Get permission function
];

Permission State

  • undefined: Initial state before any permission fetch
  • PermissionResponse: Complete permission information including status, expiration, and individual permission details

Ask Permission Function

  • Behavior: Calls askAsync with the specified permission types
  • Updates State: Automatically updates hook state with new permission response
  • Memoized: Callback reference stable across re-renders (depends only on permission types)

Get Permission Function

  • Behavior: Calls getAsync with the specified permission types
  • Updates State: Automatically updates hook state with current permission response
  • Memoized: Callback reference stable across re-renders (depends only on permission types)

Advanced Usage Patterns

Conditional Rendering Based on Permission State

function ConditionalFeature() {
  const [permission, askPermission] = usePermissions(CAMERA);
  
  // Handle loading state
  if (!permission) {
    return <LoadingSpinner />;
  }
  
  // Handle different permission states
  switch (permission.status) {
    case 'granted':
      return <CameraFeature />;
    case 'denied':
      return (
        <View>
          <Text>Camera access denied</Text>
          {permission.canAskAgain && (
            <Button title="Try Again" onPress={askPermission} />
          )}
        </View>
      );
    case 'undetermined':
      return (
        <View>
          <Text>Camera permission needed</Text>
          <Button title="Grant Permission" onPress={askPermission} />
        </View>
      );
  }
}

Multiple Permission Management

function MultiPermissionFeature() {
  const [permissions, askPermissions] = usePermissions([
    CAMERA,
    MEDIA_LIBRARY,
    LOCATION_FOREGROUND
  ]);
  
  if (!permissions) return <Text>Loading...</Text>;
  
  // Check individual permissions
  const cameraGranted = permissions.permissions.camera?.granted;
  const mediaGranted = permissions.permissions.mediaLibrary?.granted;
  const locationGranted = permissions.permissions.locationForeground?.granted;
  
  return (
    <View>
      <Text>Camera: {cameraGranted ? '✓' : '✗'}</Text>
      <Text>Media: {mediaGranted ? '✓' : '✗'}</Text>
      <Text>Location: {locationGranted ? '✓' : '✗'}</Text>
      
      {!permissions.granted && (
        <Button title="Request All" onPress={askPermissions} />
      )}
    </View>
  );
}

Integration with Effects

function EffectIntegration() {
  const [permission, askPermission] = usePermissions(LOCATION_FOREGROUND);
  
  // React to permission changes
  useEffect(() => {
    if (permission?.granted) {
      // Start location tracking
      startLocationUpdates();
    } else {
      // Stop location tracking
      stopLocationUpdates();
    }
  }, [permission?.granted]);
  
  return (
    <View>
      {permission?.granted ? (
        <LocationMap />
      ) : (
        <Button title="Enable Location" onPress={askPermission} />
      )}
    </View>
  );
}