Skip to content

Commit 93a4550

Browse files
committed
Fix issue with react-pdf-viewer unmount and make sure that examples are not being analyzed/transpiled by next.js
1 parent 9e60a41 commit 93a4550

2 files changed

Lines changed: 38 additions & 9 deletions

File tree

viewers/react/src/index.tsx

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,29 +78,45 @@ export const PDFViewer = forwardRef(function PDFViewer(
7878
useEffect(() => {
7979
if (!containerRef.current) return;
8080

81+
// Track if this effect instance is still active (for Strict Mode)
82+
let isActive = true;
83+
84+
// IMPORTANT: Capture the container reference in a closure
85+
// This ensures we can clean up even after React unmounts the component
86+
const containerElement = containerRef.current;
87+
8188
// Initialize the viewer with the config prop
8289
const viewer = EmbedPDF.init({
8390
type: 'container',
84-
target: containerRef.current,
91+
target: containerElement,
8592
...config,
8693
});
8794

8895
if (viewer) {
8996
viewerRef.current = viewer;
9097
onInit?.(viewer);
9198

92-
// Call onReady when registry is available
99+
// Call onReady when registry is available, but only if still active
93100
if (onReady) {
94-
viewer.registry.then(onReady);
101+
viewer.registry.then((registry) => {
102+
// Only call onReady if this effect instance is still active
103+
// This prevents stale callbacks in React Strict Mode
104+
if (isActive) {
105+
onReady(registry);
106+
}
107+
});
95108
}
96109
}
97110

98111
return () => {
99-
// Cleanup: remove the viewer element
100-
if (viewerRef.current && containerRef.current) {
101-
containerRef.current.innerHTML = '';
102-
viewerRef.current = null;
103-
}
112+
// Mark this effect instance as inactive
113+
isActive = false;
114+
115+
// Cleanup: remove the viewer element using captured reference
116+
// We use the captured containerElement instead of containerRef.current
117+
// because React may have already unmounted and cleared the ref
118+
containerElement.innerHTML = '';
119+
viewerRef.current = null;
104120
};
105121
// eslint-disable-next-line react-hooks/exhaustive-deps
106122
}, []);

website/next.config.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,16 +75,27 @@ const withNextra = nextra({
7575
},
7676
})
7777

78+
// Pre-built example packages that should NOT be analyzed/transpiled by Next.js.
79+
// These packages use Vue/Svelte and have their own build process.
80+
// Next.js has issues with `export *` re-exports across package boundaries,
81+
// which causes sporadic "Attempted import error" failures.
82+
const EXTERNAL_EXAMPLE_PACKAGES = [
83+
'@embedpdf/example-vue-tailwind',
84+
'@embedpdf/example-svelte-tailwind',
85+
]
86+
7887
// Export a function that handles phase-specific logic and merges with Nextra
7988
export default async (phase: string) => {
8089
// Build config with env
8190
const nextConfig: NextConfig = {
8291
env: {
8392
NEXT_PUBLIC_SNIPPET_VERSION: SNIPPET_VERSION,
8493
},
94+
// Mark Vue/Svelte example packages as external for server-side bundling
95+
serverExternalPackages: EXTERNAL_EXAMPLE_PACKAGES,
8596
}
8697

87-
// Add transpilePackages in development
98+
// Add transpilePackages in development (but exclude the pre-built examples)
8899
if (phase === 'phase-development-server') {
89100
const fs = await import('node:fs')
90101
const allFiles = globSync('../packages/*/package.json')
@@ -99,6 +110,8 @@ export default async (phase: string) => {
99110
}
100111
})
101112
.filter((pkg: string) => pkg?.startsWith('@embedpdf'))
113+
// Explicitly exclude the example packages from transpilation
114+
.filter((pkg: string) => !EXTERNAL_EXAMPLE_PACKAGES.includes(pkg))
102115

103116
nextConfig.transpilePackages = packageNames
104117
}

0 commit comments

Comments
 (0)