feat(hooks): introduce useUnmountCleanupRef for effect cleanup abstraction
Add useUnmountCleanupRef custom hook to centralize and simplify unmount cleanup logic in document page components. Refactor EPUB, HTML, and PDF page components to utilize this hook, replacing repetitive ref and effect patterns for invoking cleanup functions on unmount. This reduces code duplication and improves maintainability.
This commit is contained in:
parent
139a227aa4
commit
3db193e012
4 changed files with 25 additions and 30 deletions
|
|
@ -19,6 +19,7 @@ import { resolveDocumentId } from '@/lib/client/dexie';
|
|||
import { RateLimitBanner } from '@/components/auth/RateLimitBanner';
|
||||
import { useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
|
||||
import { useFeatureFlag } from '@/contexts/RuntimeConfigContext';
|
||||
import { useUnmountCleanupRef } from '@/hooks/useUnmountCleanupRef';
|
||||
import { useEpubDocument } from './useEpubDocument';
|
||||
|
||||
export default function EPUBPage() {
|
||||
|
|
@ -46,7 +47,6 @@ export default function EPUBPage() {
|
|||
const inFlightDocIdRef = useRef<string | null>(null);
|
||||
const loadedDocIdRef = useRef<string | null>(null);
|
||||
const didInitPadPctRef = useRef(false);
|
||||
const clearCurrDocRef = useRef(clearCurrDoc);
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
|
|
@ -103,15 +103,7 @@ export default function EPUBPage() {
|
|||
loadDocument();
|
||||
}, [loadDocument, isLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
clearCurrDocRef.current = clearCurrDoc;
|
||||
}, [clearCurrDoc]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
clearCurrDocRef.current();
|
||||
};
|
||||
}, []);
|
||||
useUnmountCleanupRef(clearCurrDoc);
|
||||
|
||||
// Compute available height = viewport - (header height + tts bar height)
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { RateLimitBanner } from '@/components/auth/RateLimitBanner';
|
|||
import { AudiobookExportModal } from '@/components/AudiobookExportModal';
|
||||
import { useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
|
||||
import { useFeatureFlag } from '@/contexts/RuntimeConfigContext';
|
||||
import { useUnmountCleanupRef } from '@/hooks/useUnmountCleanupRef';
|
||||
import type { TTSAudiobookChapter } from '@/types/tts';
|
||||
import type { AudiobookGenerationSettings } from '@/types/client';
|
||||
import { useHtmlDocument } from './useHtmlDocument';
|
||||
|
|
@ -46,7 +47,6 @@ export default function HTMLPage() {
|
|||
const [maxPadPx, setMaxPadPx] = useState<number>(0);
|
||||
const inFlightDocIdRef = useRef<string | null>(null);
|
||||
const loadedDocIdRef = useRef<string | null>(null);
|
||||
const clearCurrDocRef = useRef(clearCurrDoc);
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
|
|
@ -103,15 +103,7 @@ export default function HTMLPage() {
|
|||
loadDocument();
|
||||
}, [loadDocument, isLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
clearCurrDocRef.current = clearCurrDoc;
|
||||
}, [clearCurrDoc]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
clearCurrDocRef.current();
|
||||
};
|
||||
}, []);
|
||||
useUnmountCleanupRef(clearCurrDoc);
|
||||
|
||||
// Compute available height = viewport - (header height + tts bar height)
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {
|
|||
FORCE_REPARSE_CONFIRM_TITLE,
|
||||
isForceReparseDisabled,
|
||||
} from '@/lib/client/pdf/force-reparse';
|
||||
import { useUnmountCleanupRef } from '@/hooks/useUnmountCleanupRef';
|
||||
import { usePdfDocument } from './usePdfDocument';
|
||||
|
||||
// Dynamic import for client-side rendering only
|
||||
|
|
@ -72,7 +73,6 @@ export default function PDFViewerPage() {
|
|||
const [containerHeight, setContainerHeight] = useState<string>('auto');
|
||||
const inFlightDocIdRef = useRef<string | null>(null);
|
||||
const loadedDocIdRef = useRef<string | null>(null);
|
||||
const clearCurrDocRef = useRef(clearCurrDoc);
|
||||
const [isNavigatingBack, setIsNavigatingBack] = useState(false);
|
||||
const parseUiState: NonNullable<typeof parseStatus> = parseStatus ?? 'pending';
|
||||
const hasResolvedParseStatus = parseStatus !== null;
|
||||
|
|
@ -153,15 +153,7 @@ export default function PDFViewerPage() {
|
|||
loadDocument();
|
||||
}, [loadDocument]);
|
||||
|
||||
useEffect(() => {
|
||||
clearCurrDocRef.current = clearCurrDoc;
|
||||
}, [clearCurrDoc]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
clearCurrDocRef.current();
|
||||
};
|
||||
}, []);
|
||||
useUnmountCleanupRef(clearCurrDoc);
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading) return;
|
||||
|
|
|
|||
19
src/hooks/useUnmountCleanupRef.ts
Normal file
19
src/hooks/useUnmountCleanupRef.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { useEffect, useRef } from 'react';
|
||||
|
||||
/**
|
||||
* Runs cleanup only on unmount, while always invoking the latest cleanup function.
|
||||
*/
|
||||
export function useUnmountCleanupRef(cleanup: () => void) {
|
||||
const cleanupRef = useRef(cleanup);
|
||||
|
||||
useEffect(() => {
|
||||
cleanupRef.current = cleanup;
|
||||
}, [cleanup]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
cleanupRef.current();
|
||||
};
|
||||
}, []);
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue