# 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 在胶片切换时重新创建组件实例