From 125602cc535b103f807728729b7df78d5f7428d7 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Wed, 10 Dec 2025 11:39:42 -0300 Subject: [PATCH 1/3] fix: add react/jsx-dev-runtime to external dependencies --- vite.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.config.ts b/vite.config.ts index 7942fee..ce4a033 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -10,7 +10,7 @@ export default defineConfig({ fileName: (format) => (format === 'es' ? 'index.mjs' : 'index.js'), }, rollupOptions: { - external: ['react', 'react-dom', 'react/jsx-runtime', 'superdoc'], + external: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'superdoc'], }, }, plugins: [ From 6619c00f44fa17669b46f36ab59eda5ac53bd117 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Wed, 10 Dec 2025 11:39:48 -0300 Subject: [PATCH 2/3] chore: remove unnecessary dedupe workaround from demo config --- demo/vite.config.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/demo/vite.config.ts b/demo/vite.config.ts index 97f708e..05a5139 100644 --- a/demo/vite.config.ts +++ b/demo/vite.config.ts @@ -1,14 +1,7 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; -import path from 'path'; export default defineConfig({ plugins: [react()], base: '/', - resolve: { - alias: { - 'superdoc/dist/style.css': path.resolve(__dirname, '../node_modules/superdoc/dist/style.css') - }, - dedupe: ['react', 'react-dom', 'react/jsx-runtime'] - } }); \ No newline at end of file From 8447700fb16b9506a1b8d3138cf893c116c3f925 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Wed, 10 Dec 2025 11:40:01 -0300 Subject: [PATCH 3/3] fix: handle React 18 Strict Mode double-invocation in SuperDoc init --- src/index.tsx | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index d013a33..29ab6a6 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -57,7 +57,9 @@ const SuperDocESign = forwardRef< const startTimeRef = useRef(Date.now()); const fieldsRef = useRef(fields); const auditTrailRef = useRef([]); + const onFieldsDiscoveredRef = useRef(onFieldsDiscovered); fieldsRef.current = fields; + onFieldsDiscoveredRef.current = onFieldsDiscovered; useEffect(() => { auditTrailRef.current = auditTrail; @@ -151,7 +153,7 @@ const SuperDocESign = forwardRef< .filter((f: Types.FieldInfo) => f.id); if (discovered.length > 0) { - onFieldsDiscovered?.(discovered); + onFieldsDiscoveredRef.current?.(discovered); const allFields = [ ...(fieldsRef.current.document || []), @@ -168,7 +170,7 @@ const SuperDocESign = forwardRef< ); } }, - [onFieldsDiscovered, updateFieldInDocument], + [updateFieldInDocument], ); const addAuditEvent = ( @@ -188,19 +190,28 @@ const SuperDocESign = forwardRef< return nextTrail; }; - // Initialize SuperDoc - ONLY ONCE per document + // Initialize SuperDoc - uses abort pattern to handle React 18 Strict Mode + // which intentionally double-invokes effects to help identify cleanup issues useEffect(() => { if (!containerRef.current) return; + let aborted = false; + let instance: SuperDoc | null = null; + const initSuperDoc = async () => { const { SuperDoc } = await import("superdoc"); - const instance = new SuperDoc({ + // If cleanup ran while we were importing, abort + if (aborted) return; + + instance = new SuperDoc({ selector: containerRef.current!, document: document.source, documentMode: "viewing", onReady: () => { - if (instance.activeEditor) { + // Guard callback execution if cleanup already ran + if (aborted) return; + if (instance?.activeEditor) { discoverAndApplyFields(instance.activeEditor); } addAuditEvent({ type: "ready" }); @@ -214,12 +225,13 @@ const SuperDocESign = forwardRef< initSuperDoc(); return () => { - if (superdocRef.current) { - if (typeof superdocRef.current.destroy === "function") { - superdocRef.current.destroy(); + aborted = true; + if (instance) { + if (typeof instance.destroy === "function") { + instance.destroy(); } - superdocRef.current = null; } + superdocRef.current = null; }; }, [document.source, document.mode, discoverAndApplyFields]);