react-native-pdf-jsi
Version:
🚀 Ultra-fast React Native PDF viewer with JSI (JavaScript Interface) integration for maximum performance. Features lazy loading, smart caching, progressive loading, and zero-bridge overhead operations. Perfect for large PDF files with 30-day persistent c
399 lines (359 loc) • 12.5 kB
JavaScript
/**
* Copyright (c) 2025-present, Punith M (punithm300@gmail.com)
* Enhanced PDF JSI Hook with high-performance operations
* All rights reserved.
*
* React Hook for easy JSI integration in functional components
* Provides automatic JSI availability detection and fallback handling
*/
import { useState, useEffect, useCallback, useRef } from 'react';
import { Platform } from 'react-native';
import PDFJSI from '../PDFJSI';
/**
* Hook for PDF JSI functionality
* @param {Object} options - Configuration options
* @returns {Object} JSI utilities and state
*/
export const usePDFJSI = (options = {}) => {
const {
autoInitialize = true,
enablePerformanceTracking = true,
enableCaching = true,
maxCacheSize = 100
} = options;
// State management
const [isJSIAvailable, setIsJSIAvailable] = useState(false);
const [isInitialized, setIsInitialized] = useState(false);
const [performanceMetrics, setPerformanceMetrics] = useState(null);
const [jsiStats, setJsiStats] = useState(null);
// Refs for tracking
const pdfInstancesRef = useRef(new Map());
const performanceHistoryRef = useRef([]);
/**
* Initialize JSI availability check
*/
const initializeJSI = useCallback(async () => {
try {
// Enable JSI for both Android and iOS
// iOS will get enhanced caching and performance optimizations
if (Platform.OS === 'android') {
// Android gets full JSI support
const isAvailable = await PDFJSI.checkJSIAvailability();
setIsJSIAvailable(isAvailable);
setIsInitialized(true);
return isAvailable;
} else {
// iOS gets enhanced caching support (bridge mode with optimizations)
setIsJSIAvailable(true); // Enable enhanced features for iOS
setIsInitialized(true);
return true;
}
// Get JSI stats for performance tracking (Android only)
if (Platform.OS === 'android' && isJSIAvailable && enablePerformanceTracking) {
const stats = await PDFJSI.getJSIStats();
setJsiStats(stats);
}
} catch (error) {
console.error('📱 usePDFJSI: Error initializing JSI:', error);
setIsJSIAvailable(false);
setIsInitialized(true);
return false;
}
}, [enablePerformanceTracking]);
/**
* Render page with JSI or fallback
*/
const renderPage = useCallback(async (pdfId, pageNumber, scale, base64Data) => {
const startTime = performance.now();
try {
if (isJSIAvailable) {
const result = await PDFJSI.renderPageDirect(pdfId, pageNumber, scale, base64Data);
const endTime = performance.now();
const renderTime = endTime - startTime;
// Track performance
if (enablePerformanceTracking) {
performanceHistoryRef.current.push({
operation: 'renderPage',
duration: renderTime,
timestamp: Date.now(),
pdfId,
pageNumber,
scale,
mode: 'JSI'
});
}
return result;
} else {
// Fallback to bridge mode (would need to implement)
throw new Error('JSI not available and bridge fallback not implemented');
}
} catch (error) {
const endTime = performance.now();
const renderTime = endTime - startTime;
if (enablePerformanceTracking) {
performanceHistoryRef.current.push({
operation: 'renderPage',
duration: renderTime,
timestamp: Date.now(),
pdfId,
pageNumber,
scale,
mode: 'JSI_ERROR',
error: error.message
});
}
throw error;
}
}, [isJSIAvailable, enablePerformanceTracking]);
/**
* Get page metrics
*/
const getPageMetrics = useCallback(async (pdfId, pageNumber) => {
try {
if (isJSIAvailable) {
return await PDFJSI.getPageMetrics(pdfId, pageNumber);
} else {
throw new Error('JSI not available');
}
} catch (error) {
console.error('📱 usePDFJSI: Error getting page metrics:', error);
throw error;
}
}, [isJSIAvailable]);
/**
* Preload pages
*/
const preloadPages = useCallback(async (pdfId, startPage, endPage) => {
try {
if (isJSIAvailable) {
return await PDFJSI.preloadPagesDirect(pdfId, startPage, endPage);
} else {
throw new Error('JSI not available');
}
} catch (error) {
console.error('📱 usePDFJSI: Error preloading pages:', error);
throw error;
}
}, [isJSIAvailable]);
/**
* Search text
*/
const searchText = useCallback(async (pdfId, searchTerm, startPage, endPage) => {
try {
if (isJSIAvailable) {
return await PDFJSI.searchTextDirect(pdfId, searchTerm, startPage, endPage);
} else {
throw new Error('JSI not available');
}
} catch (error) {
console.error('📱 usePDFJSI: Error searching text:', error);
throw error;
}
}, [isJSIAvailable]);
/**
* Get cache metrics
*/
const getCacheMetrics = useCallback(async (pdfId) => {
try {
if (isJSIAvailable) {
return await PDFJSI.getCacheMetrics(pdfId);
} else {
throw new Error('JSI not available');
}
} catch (error) {
console.error('📱 usePDFJSI: Error getting cache metrics:', error);
throw error;
}
}, [isJSIAvailable]);
/**
* Clear cache
*/
const clearCache = useCallback(async (pdfId, cacheType = 'all') => {
try {
if (isJSIAvailable) {
return await PDFJSI.clearCacheDirect(pdfId, cacheType);
} else {
throw new Error('JSI not available');
}
} catch (error) {
console.error('📱 usePDFJSI: Error clearing cache:', error);
throw error;
}
}, [isJSIAvailable]);
/**
* Optimize memory
*/
const optimizeMemory = useCallback(async (pdfId) => {
try {
if (isJSIAvailable) {
return await PDFJSI.optimizeMemory(pdfId);
} else {
throw new Error('JSI not available');
}
} catch (error) {
console.error('📱 usePDFJSI: Error optimizing memory:', error);
throw error;
}
}, [isJSIAvailable]);
/**
* Set render quality
*/
const setRenderQuality = useCallback(async (pdfId, quality) => {
try {
if (isJSIAvailable) {
return await PDFJSI.setRenderQuality(pdfId, quality);
} else {
throw new Error('JSI not available');
}
} catch (error) {
console.error('📱 usePDFJSI: Error setting render quality:', error);
throw error;
}
}, [isJSIAvailable]);
/**
* Update performance metrics
*/
const updatePerformanceMetrics = useCallback(async (pdfId) => {
try {
if (isJSIAvailable) {
const metrics = await PDFJSI.getPerformanceMetrics(pdfId);
setPerformanceMetrics(metrics);
return metrics;
}
} catch (error) {
console.error('📱 usePDFJSI: Error updating performance metrics:', error);
}
return null;
}, [isJSIAvailable]);
/**
* Get performance history
*/
const getPerformanceHistory = useCallback(() => {
return [...performanceHistoryRef.current];
}, []);
/**
* Clear performance history
*/
const clearPerformanceHistory = useCallback(() => {
performanceHistoryRef.current = [];
}, []);
/**
* Create PDF instance with tracking
*/
const createPDFInstance = useCallback((pdfId, options = {}) => {
const instance = {
id: pdfId,
created: Date.now(),
options,
metrics: null,
cacheMetrics: null
};
pdfInstancesRef.current.set(pdfId, instance);
return instance;
}, []);
/**
* Remove PDF instance
*/
const removePDFInstance = useCallback((pdfId) => {
pdfInstancesRef.current.delete(pdfId);
}, []);
/**
* Get all PDF instances
*/
const getPDFInstances = useCallback(() => {
return Array.from(pdfInstancesRef.current.values());
}, []);
/**
* Lazy load pages for large PDF files
*/
const lazyLoadPages = useCallback(async (pdfId, currentPage, preloadRadius = 3, totalPages = null) => {
try {
if (isJSIAvailable) {
return await PDFJSI.lazyLoadPages(pdfId, currentPage, preloadRadius, totalPages);
} else {
throw new Error('JSI not available');
}
} catch (error) {
console.error('📱 usePDFJSI: Error lazy loading pages:', error);
throw error;
}
}, [isJSIAvailable]);
/**
* Progressive loading for large PDF files
*/
const progressiveLoadPages = useCallback(async (pdfId, startPage = 1, batchSize = 5, onProgress = null) => {
try {
if (isJSIAvailable) {
return await PDFJSI.progressiveLoadPages(pdfId, startPage, batchSize, onProgress);
} else {
throw new Error('JSI not available');
}
} catch (error) {
console.error('📱 usePDFJSI: Error progressive loading pages:', error);
throw error;
}
}, [isJSIAvailable]);
/**
* Smart caching for frequently accessed pages
*/
const smartCacheFrequentPages = useCallback(async (pdfId, frequentPages = []) => {
try {
if (isJSIAvailable) {
return await PDFJSI.smartCacheFrequentPages(pdfId, frequentPages);
} else {
throw new Error('JSI not available');
}
} catch (error) {
console.error('📱 usePDFJSI: Error smart caching pages:', error);
throw error;
}
}, [isJSIAvailable]);
// Initialize JSI on mount if autoInitialize is enabled
useEffect(() => {
if (autoInitialize && !isInitialized) {
initializeJSI();
}
}, [autoInitialize, isInitialized, initializeJSI]);
// Cleanup on unmount
useEffect(() => {
return () => {
// Cleanup PDF instances
pdfInstancesRef.current.clear();
// Clear performance history if needed
if (!enablePerformanceTracking) {
performanceHistoryRef.current = [];
}
};
}, [enablePerformanceTracking]);
return {
// State
isJSIAvailable,
isInitialized,
performanceMetrics,
jsiStats,
// Core functions
renderPage,
getPageMetrics,
preloadPages,
searchText,
getCacheMetrics,
clearCache,
optimizeMemory,
setRenderQuality,
// Performance tracking
updatePerformanceMetrics,
getPerformanceHistory,
clearPerformanceHistory,
// Instance management
createPDFInstance,
removePDFInstance,
getPDFInstances,
// Lazy loading and advanced features
lazyLoadPages,
progressiveLoadPages,
smartCacheFrequentPages,
// Utilities
initializeJSI
};
};
export default usePDFJSI;