Files
sheeteasyAI/.cursor/rules/univer-redi-management.mdc
sheetEasy AI Team 2d8e4524b7 redi 이슈 해결
2025-06-26 17:01:28 +09:00

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