MCPcopy Index your code
hub / github.com/codeaashu/claude-code / useVirtualScroll

Function useVirtualScroll

src/hooks/useVirtualScroll.ts:142–721  ·  view source on GitHub ↗
(
  scrollRef: RefObject<ScrollBoxHandle | null>,
  itemKeys: readonly string[],
  /**
   * Terminal column count. On change, cached heights are stale (text
   * rewraps) — SCALED by oldCols/newCols rather than cleared. Clearing
   * made the pessimistic coverage back-walk mount ~190 items (every
   * uncached item → PESSIMISTIC_HEIGHT=1 → walk 190 to reach
   * viewport+2×overscan). Each fresh mount runs marked.lexer + syntax
   * highlighting ≈ 3ms; ~600ms React reconcile on first resize with a
   * long conversation. Scaling keeps heightCache populated → back-walk
   * uses real-ish heights → mount range stays tight. Scaled estimates
   * are overwritten by real Yoga heights on next useLayoutEffect.
   *
   * Scaled heights are close enough that the black-screen-on-widen bug
   * (inflated pre-resize offsets overshoot post-resize scrollTop → end
   * loop stops short of tail) doesn't trigger: ratio<1 on widen scales
   * heights DOWN, keeping offsets roughly aligned with post-resize Yoga.
   */
  columns: number,
)

Source from the content-addressed store, hash-verified

140 * the last N items regardless of what scrollTop claims.
141 */
142export function useVirtualScroll(
143 scrollRef: RefObject<ScrollBoxHandle | null>,
144 itemKeys: readonly string[],
145 /**
146 * Terminal column count. On change, cached heights are stale (text
147 * rewraps) — SCALED by oldCols/newCols rather than cleared. Clearing
148 * made the pessimistic coverage back-walk mount ~190 items (every
149 * uncached item → PESSIMISTIC_HEIGHT=1 → walk 190 to reach
150 * viewport+2×overscan). Each fresh mount runs marked.lexer + syntax
151 * highlighting ≈ 3ms; ~600ms React reconcile on first resize with a
152 * long conversation. Scaling keeps heightCache populated → back-walk
153 * uses real-ish heights → mount range stays tight. Scaled estimates
154 * are overwritten by real Yoga heights on next useLayoutEffect.
155 *
156 * Scaled heights are close enough that the black-screen-on-widen bug
157 * (inflated pre-resize offsets overshoot post-resize scrollTop → end
158 * loop stops short of tail) doesn't trigger: ratio<1 on widen scales
159 * heights DOWN, keeping offsets roughly aligned with post-resize Yoga.
160 */
161 columns: number,
162): VirtualScrollResult {
163 const heightCache = useRef(new Map<string, number>())
164 // Bump whenever heightCache mutates so offsets rebuild on next read. Ref
165 // (not state) — checked during render phase, zero extra commits.
166 const offsetVersionRef = useRef(0)
167 // scrollTop at last commit, for detecting fast-scroll mode (slide cap gate).
168 const lastScrollTopRef = useRef(0)
169 const offsetsRef = useRef<{ arr: Float64Array; version: number; n: number }>({
170 arr: new Float64Array(0),
171 version: -1,
172 n: -1,
173 })
174 const itemRefs = useRef(new Map<string, DOMElement>())
175 const refCache = useRef(new Map<string, (el: DOMElement | null) => void>())
176 // Inline ref-compare: must run before offsets is computed below. The
177 // skip-flag guards useLayoutEffect from re-populating heightCache with
178 // PRE-resize Yoga heights (useLayoutEffect reads Yoga from the frame
179 // BEFORE this render's calculateLayout — the one that had the old width).
180 // Next render's useLayoutEffect reads post-resize Yoga → correct.
181 const prevColumns = useRef(columns)
182 const skipMeasurementRef = useRef(false)
183 // Freeze the mount range for the resize-settling cycle. Already-mounted
184 // items have warm useMemo (marked.lexer, highlighting); recomputing range
185 // from scaled/pessimistic estimates causes mount/unmount churn (~3ms per
186 // fresh mount = ~150ms visible as a second flash). The pre-resize range is
187 // as good as any — items visible at old width are what the user wants at
188 // new width. Frozen for 2 renders: render #1 has skipMeasurement (Yoga
189 // still pre-resize), render #2's useLayoutEffect reads post-resize Yoga
190 // into heightCache. Render #3 has accurate heights → normal recompute.
191 const prevRangeRef = useRef<readonly [number, number] | null>(null)
192 const freezeRendersRef = useRef(0)
193 if (prevColumns.current !== columns) {
194 const ratio = prevColumns.current / columns
195 prevColumns.current = columns
196 for (const [k, h] of heightCache.current) {
197 heightCache.current.set(k, Math.max(1, Math.round(h * ratio)))
198 }
199 offsetVersionRef.current++

Callers 1

VirtualMessageListFunction · 0.85

Calls 9

maxMethod · 0.80
keysMethod · 0.80
deleteMethod · 0.65
getMethod · 0.65
setMethod · 0.45
hasMethod · 0.45
getComputedWidthMethod · 0.45
getComputedTopMethod · 0.45
getComputedHeightMethod · 0.45

Tested by

no test coverage detected