From 7cfe4646b8013fb1af5b4e507d226852c8146379 Mon Sep 17 00:00:00 2001 From: junwen-k <40173716+junwen-k@users.noreply.github.com> Date: Sun, 26 Jul 2026 16:42:32 +0800 Subject: [PATCH] fix(engines): guard usePdfiumEngine success path against cancellation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The init effect only checked its `cancelled` flag on the error path, so under React Strict Mode's dev mount/unmount/remount it created two engines and called `setEngine` for both. The second engine identity remounts any consumer keyed on the engine (e.g. ``), abandoning an in-flight document load, and the first engine leaks because the cleanup that ran saw `engineRef.current === null`. Discard the freshly created engine if the effect was already torn down, and factor engine disposal into a `disposeEngine` helper so teardown captures its target locally — a `wasmUrl` change mid-init can no longer destroy the replacement engine from within the old close callback. Co-Authored-By: Claude Opus 4.8 --- .changeset/pdfium-engine-strict-mode-guard.md | 5 +++++ .../src/shared/hooks/use-pdfium-engine.ts | 20 +++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 .changeset/pdfium-engine-strict-mode-guard.md diff --git a/.changeset/pdfium-engine-strict-mode-guard.md b/.changeset/pdfium-engine-strict-mode-guard.md new file mode 100644 index 000000000..55c61d39f --- /dev/null +++ b/.changeset/pdfium-engine-strict-mode-guard.md @@ -0,0 +1,5 @@ +--- +'@embedpdf/engines': patch +--- + +fix(engines): `usePdfiumEngine` now discards the engine it created if the effect was torn down before init resolved, instead of committing it. This stops React Strict Mode's dev remount from creating two engines, remounting engine-keyed consumers (e.g. ``) mid-load, and leaking the first engine. Engine teardown also captures its target locally so a `wasmUrl` change mid-init can no longer destroy the replacement engine. diff --git a/packages/engines/src/shared/hooks/use-pdfium-engine.ts b/packages/engines/src/shared/hooks/use-pdfium-engine.ts index f9365c2cc..b6da16946 100644 --- a/packages/engines/src/shared/hooks/use-pdfium-engine.ts +++ b/packages/engines/src/shared/hooks/use-pdfium-engine.ts @@ -16,6 +16,12 @@ interface UsePdfiumEngineProps { fontFallback?: FontFallbackConfig | null; } +function disposeEngine(engine: PdfEngine | null) { + engine?.closeAllDocuments?.().wait(() => { + engine?.destroy?.(); + }, ignore); +} + export function usePdfiumEngine(config?: UsePdfiumEngineProps) { const { wasmUrl = defaultWasmUrl, @@ -44,6 +50,14 @@ export function usePdfiumEngine(config?: UsePdfiumEngineProps) { encoderPoolSize, fontFallback, }); + + // Effect torn down before we resolved (e.g. Strict Mode's dev + // remount): discard this engine instead of committing it. + if (cancelled) { + disposeEngine(pdfEngine); + return; + } + engineRef.current = pdfEngine; setEngine(pdfEngine); setLoading(false); @@ -57,10 +71,8 @@ export function usePdfiumEngine(config?: UsePdfiumEngineProps) { return () => { cancelled = true; - engineRef.current?.closeAllDocuments?.().wait(() => { - engineRef.current?.destroy?.(); - engineRef.current = null; - }, ignore); + disposeEngine(engineRef.current); + engineRef.current = null; }; }, [wasmUrl, worker, logger, fontFallback]);