155 lines
4.7 KiB
Plaintext
155 lines
4.7 KiB
Plaintext
---
|
|
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<any> {
|
|
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
|