# React Key 缺失导致 Viewport 图像残留的因果关系分析
## 问题:为什么没有 key 会导致图像残留?
### React 组件重用机制
```typescript
// Film.tsx 当前代码(有问题)
const generateCell = (imageId, indexOfCell) => {
return ();
};
```
### 因果关系链
#### 1. 没有 key → React 重用组件实例
- **没有 key 时**:React 认为相同位置的相同类型组件是同一个组件
- **切换胶片时**:
- 胶片1的第一个格子:``
- 切换到胶片2的第一个格子:``
- React 认为这是**同一个组件**,只需要更新 props
#### 2. 组件重用 → 内部状态不重置
```typescript
// ViewportContainer 内部状态在组件重用时不会重置
const [hasImage, setHasImage] = useState(false);
const [currentImageId, setCurrentImageId] = useState(null);
const [stackViewport, setStackViewport] = useState(null);
const viewportRef = useRef(null);
// 🚨 问题:切换胶片时这些状态保持原值!
```
#### 3. 状态不重置 → Viewport 实例保持原有状态
```typescript
// useEffect 只在组件首次挂载时执行
useEffect(() => {
// 创建渲染引擎和 viewport...
setStackViewport(viewport);
}, []); // 空依赖数组意味着只执行一次
// 🚨 关键问题:viewport 实例仍然绑定着之前的图像数据
```
#### 4. Viewport 状态保持 → 图像残留显示
- Cornerstone 的 viewport 实例内部缓存了图像数据
- 即使 props 中的 `imageId` 改变,viewport 可能仍显示缓存的图像
- `useEffect([imageId, stackViewport])` 虽然会触发,但 `stackViewport` 实例本身没变
## 具体场景演示
### 场景:胶片1 → 胶片2 切换
```
1. 用户在胶片1拖拽图像A到第一个格子
- ViewportContainer 创建 viewport-1
- viewport-1 显示图像A
- hasImage = true, currentImageId = "imageA"
2. 用户切换到胶片2(第一个格子为空)
- React 重用 ViewportContainer 组件实例
- props 更新:imageId 从 "imageA" 变为 null
- 但是 viewport-1 实例仍然存在,仍然显示图像A
- 虽然触发 useEffect,但清空逻辑可能执行不彻底
3. 结果:胶片2显示了胶片1的图像
```
## 解决方案:为什么 key 能解决问题?
### 有 key 的情况
```typescript
// 修复后的代码
const generateCell = (imageId, indexOfCell) => {
const uniqueKey = `${currentFilm.id}-${indexOfCell}`;
return (
);
};
```
### 修复后的因果关系
```
1. 切换胶片时,key 从 "1-0" 变为 "2-0"
2. React 识别这是不同的组件,卸载旧组件,创建新组件
3. 旧组件卸载 → 触发 cleanup 函数 → 清理 viewport
4. 新组件挂载 → 重新初始化所有状态 → 创建新的 viewport
5. 结果:完全隔离,没有状态残留
```
## 总结
**核心问题**:React 组件重用 + Cornerstone Viewport 状态持久化
**根本原因**:没有 key 导致 React 错误地重用了应该重新创建的组件
**解决方案**:通过 key 强制 React 在胶片切换时重新创建组件实例