From afc983577f706abe412dfa5f76365840a95f130e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=20=ED=95=9C?= Date: Sun, 19 Jul 2026 15:04:05 +0900 Subject: [PATCH] =?UTF-8?q?=ED=8F=B4=EB=8D=94/=EB=B7=B0=20=EC=A0=84?= =?UTF-8?q?=ED=99=98=20=EC=8B=9C=20=EC=8D=B8=EB=84=A4=EC=9D=BC=20=EB=AF=B8?= =?UTF-8?q?=EA=B0=B1=EC=8B=A0=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 원인: JustifiedGrid 셀 본문에서 snapshot/id를 한 번만 캡처(const)했다. 는 인덱스 값이 같으면(뷰 전환 시 가시범위 [0..N] 동일) DOM 행을 재사용하므로, 재사용된 셀이 이전 폴더의 id·썸네일을 그대로 유지했다. (레이아웃 리사이즈 버그와 동일 계열) - id()/showImg()/isVideo()/version()을 snapshot() 기반 반응형 접근자로 변경 - 클릭/컨텍스트/드래그 핸들러도 호출 시점의 snapshot()을 읽도록 - img에 onLoad 추가 — 재사용된 img가 이전 onError로 display:none에 걸려 새 썸네일이 안 보이던 문제 방지 검증: 3개 하위폴더 픽스처에서 폴더 클릭 시 가시 썸네일 id가 folderA(1-40)/folderB(41-80)/folderC(83-140)로 즉시 전환 확인. Co-Authored-By: Claude Fable 5 --- src/features/grid/JustifiedGrid.tsx | 45 +++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/features/grid/JustifiedGrid.tsx b/src/features/grid/JustifiedGrid.tsx index c58e566..059c4e3 100644 --- a/src/features/grid/JustifiedGrid.tsx +++ b/src/features/grid/JustifiedGrid.tsx @@ -370,47 +370,68 @@ export const JustifiedGrid: Component = () => { {(i) => { - const snap = snapshot()!; - const id = snap.id(i); - // 위치/크기는 반응형으로 — layout() 변경(리사이즈/줌) 시 For가 DOM을 - // 재사용해도 style이 갱신되도록 한다. + // 모든 파생값은 반응형으로 — For가 같은 인덱스 값에 대해 DOM을 재사용해도 + // 뷰(폴더) 전환 시 스냅샷이 바뀌면 id/썸네일/선택상태가 즉시 갱신되도록 한다. const box = () => { const l = layout(); return l ? [l.boxes[i * 4], l.boxes[i * 4 + 1], l.boxes[i * 4 + 2], l.boxes[i * 4 + 3]] : [0, 0, 0, 0]; }; - const version = () => thumbVersions[id] ?? 0; - const showImg = () => snap.hasThumb(i) || version() > 0; + const id = () => { + const s = snapshot(); + return s ? s.id(i) : -1; + }; + const version = () => thumbVersions[id()] ?? 0; + const showImg = () => { + const s = snapshot(); + return !!s && (s.hasThumb(i) || version() > 0); + }; + const isVideo = () => { + const s = snapshot(); + return !!s && s.kind(i) === 1; + }; return (
onCellClick(e, snap, i)} + onClick={(e) => { + const s = snapshot(); + if (s) onCellClick(e, s, i); + }} onDblClick={() => openLightbox(i)} - onContextMenu={(e) => onCellContextMenu(e, snap, i)} + onContextMenu={(e) => { + const s = snapshot(); + if (s) onCellContextMenu(e, s, i); + }} draggable={true} - onDragStart={(e) => onCellDragStart(e, snap, i)} + onDragStart={(e) => { + const s = snapshot(); + if (s) onCellDragStart(e, s, i); + }} > { + (e.currentTarget as HTMLImageElement).style.display = ""; + }} onError={(e) => { (e.currentTarget as HTMLImageElement).style.display = "none"; }} /> - + 영상