--- description: globs: alwaysApply: false --- ## REDI Duplicate Identifier Prevention in Univer CE ### **Problem Analysis** - **Root Cause**: REDI dependency injection system retains global service identifiers in memory even after Univer instance disposal - **Trigger**: Component remounting causes duplicate service registration attempts - **Symptoms**: "Identifier [service-name] already exists" console errors ### **Core Prevention Patterns** #### **1. Global Instance Management** ```typescript // ✅ DO: Use singleton pattern with proper state tracking const UniverseManager = { async createInstance(container: HTMLElement): Promise { const state = getGlobalState(); // Check existing instance with complete validation if (state.instance && state.univerAPI && !state.isInitializing && !state.isDisposing) { return { univer: state.instance, univerAPI: state.univerAPI }; } // Wait for ongoing operations if (state.isInitializing && state.initializationPromise) { return state.initializationPromise; } } }; // ❌ DON'T: Create new instances without checking global state const univer = createUniver({ /* config */ }); // Direct creation ``` #### **2. Component Mount Lifecycle** ```typescript // ✅ DO: Smart initialization with state checks useEffect(() => { const existingUniver = UniverseManager.getInstance(); const state = getGlobalState(); // Reuse existing instance if available and stable if (existingUniver && state.univerAPI && !UniverseManager.isInitializing() && !UniverseManager.isDisposing()) { setIsInitialized(true); return; } // Wait for ongoing operations if (UniverseManager.isInitializing() || UniverseManager.isDisposing()) { const waitTimer = setInterval(() => { if (!UniverseManager.isInitializing() && !UniverseManager.isDisposing()) { clearInterval(waitTimer); // Check and initialize as needed } }, 100); return () => clearInterval(waitTimer); } }, []); // Empty dependency array to prevent re-execution // ❌ DON'T: Include dependencies that cause re-initialization useEffect(() => { initializeUniver(); }, [initializeUniver]); // This causes re-execution on every render ``` #### **3. Complete Cleanup Strategy** ```typescript // ✅ DO: Implement thorough REDI state cleanup forceReset(): void { // Standard disposal if (state.instance) { state.instance.dispose(); } // REDI global state cleanup attempt if (typeof window !== "undefined") { const globalKeys = Object.keys(window).filter(key => key.includes('redi') || key.includes('REDI') || key.includes('univerjs') || key.includes('univer') ); globalKeys.forEach(key => { try { delete (window as any)[key]; } catch (e) { console.warn(`Global key ${key} cleanup failed:`, e); } }); } // Reset all state flags Object.assign(state, { instance: null, univerAPI: null, isInitializing: false, isDisposing: false, initializationPromise: null, lastContainerId: null, }); } ``` ### **4. State Validation Patterns** ```typescript // ✅ DO: Multi-level state validation const isInstanceReady = (state: GlobalUniverState): boolean => { return !!( state.instance && state.univerAPI && !state.isInitializing && !state.isDisposing && state.lastContainerId ); }; // ❌ DON'T: Simple existence check if (state.instance) { /* insufficient validation */ } ``` ### **5. Debug Tools Integration** ```typescript // ✅ DO: Provide comprehensive debugging tools window.__UNIVER_DEBUG__ = { getGlobalUniver: () => UniverseManager.getInstance(), getGlobalState: () => getGlobalState(), forceReset: () => UniverseManager.forceReset(), completeCleanup: () => UniverseManager.completeCleanup(), }; ``` ### **Error Resolution Steps** 1. **Immediate**: Call `window.__UNIVER_DEBUG__.completeCleanup()` in console 2. **Prevention**: Use empty dependency arrays in useEffect for initialization 3. **Validation**: Always check both `instance` and `univerAPI` existence 4. **Cleanup**: Implement thorough REDI global state cleanup in disposal methods ### **Common Anti-Patterns to Avoid** - ❌ Re-initializing on every component render - ❌ Not waiting for ongoing initialization/disposal operations - ❌ Incomplete state validation before reuse - ❌ Missing REDI global state cleanup - ❌ Using complex dependency arrays in initialization useEffect ### **Best Practices** - ✅ Implement singleton pattern with proper state management - ✅ Use polling-based waiting for state transitions - ✅ Provide debug tools for runtime state inspection - ✅ Separate initialization concerns from component lifecycle - ✅ Implement both standard and emergency cleanup methods