Skip to content

Commit 47823a2

Browse files
junwen-kclaude
andcommitted
fix(engines): guard usePdfiumEngine success path against cancellation
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. `<EmbedPDF>`), abandoning an in-flight document load, and the first engine leaks because the cleanup that ran saw `engineRef.current === null`. Guard the success path: if the effect was already torn down, destroy the freshly created engine and return before touching state. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 07ed5fe commit 47823a2

2 files changed

Lines changed: 19 additions & 0 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@embedpdf/engines': patch
3+
---
4+
5+
fix(engines): guard `usePdfiumEngine`'s success path against cancellation. The init effect only checked its `cancelled` flag on the error path, so under React Strict Mode's dev double-mount it created two engines and called `setEngine` for both — remounting engine-keyed consumers (e.g. `<EmbedPDF>`) mid-load and leaking the first engine. It now destroys the engine and bails if the effect was already torn down.

packages/engines/src/shared/hooks/use-pdfium-engine.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ export function usePdfiumEngine(config?: UsePdfiumEngineProps) {
4444
encoderPoolSize,
4545
fontFallback,
4646
});
47+
48+
// The effect can be torn down before this resolves — notably under
49+
// React Strict Mode, which mounts/unmounts/remounts in dev. Without
50+
// this guard the success path runs after cleanup: it calls setEngine
51+
// with a second engine identity (remounting engine-keyed consumers)
52+
// and leaks this engine, because the cleanup that already ran saw
53+
// engineRef.current === null.
54+
if (cancelled) {
55+
pdfEngine.closeAllDocuments?.().wait(() => {
56+
pdfEngine.destroy?.();
57+
}, ignore);
58+
return;
59+
}
60+
4761
engineRef.current = pdfEngine;
4862
setEngine(pdfEngine);
4963
setLoading(false);

0 commit comments

Comments
 (0)