clear-virtualizer 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/lib/index.cjs +47 -45
  2. package/lib/index.js +47 -45
  3. package/package.json +1 -1
package/lib/index.cjs CHANGED
@@ -111,6 +111,7 @@ function useVirtualizer({ count, estimateSize, overscan = DEFAULT_OVERSCAN }) {
111
111
  const [scrollOffset, setScrollOffset] = react.useState({ value: 0 });
112
112
  const [forcedRange, setForcedRange] = react.useState(null);
113
113
  const scrollElementRef = react.useRef(null);
114
+ const [scrollElement, setScrollElement] = react.useState(null);
114
115
  const rafRef = react.useRef(null);
115
116
  const observersRef = react.useRef(/* @__PURE__ */ new Map());
116
117
  const containerObserverRef = react.useRef(null);
@@ -188,22 +189,7 @@ function useVirtualizer({ count, estimateSize, overscan = DEFAULT_OVERSCAN }) {
188
189
  },
189
190
  [count, forcedRange, overscan]
190
191
  );
191
- const measureElement = react.useCallback((element, index) => {
192
- if (!element) {
193
- const entry = observersRef.current.get(index);
194
- if (entry) {
195
- entry.observer.disconnect();
196
- observersRef.current.delete(index);
197
- }
198
- return;
199
- }
200
- const existing = observersRef.current.get(index);
201
- if (existing && existing.element === element)
202
- return;
203
- if (existing) {
204
- existing.observer.disconnect();
205
- observersRef.current.delete(index);
206
- }
192
+ const observeItem = react.useCallback((index, element) => {
207
193
  if (typeof ResizeObserver === "undefined")
208
194
  return;
209
195
  const observer = new ResizeObserver(([entry]) => {
@@ -223,6 +209,27 @@ function useVirtualizer({ count, estimateSize, overscan = DEFAULT_OVERSCAN }) {
223
209
  observer.observe(element);
224
210
  observersRef.current.set(index, { observer, element });
225
211
  }, []);
212
+ const measureElement = react.useCallback(
213
+ (element, index) => {
214
+ if (!element) {
215
+ const entry = observersRef.current.get(index);
216
+ if (entry) {
217
+ entry.observer.disconnect();
218
+ observersRef.current.delete(index);
219
+ }
220
+ return;
221
+ }
222
+ const existing = observersRef.current.get(index);
223
+ if (existing && existing.element === element)
224
+ return;
225
+ if (existing) {
226
+ existing.observer.disconnect();
227
+ observersRef.current.delete(index);
228
+ }
229
+ observeItem(index, element);
230
+ },
231
+ [observeItem]
232
+ );
226
233
  const getMeasureRef = react.useCallback(
227
234
  (index) => {
228
235
  let fn = refCacheRef.current.get(index);
@@ -352,42 +359,37 @@ function useVirtualizer({ count, estimateSize, overscan = DEFAULT_OVERSCAN }) {
352
359
  setScrollOffset({ value: el.scrollTop });
353
360
  });
354
361
  }, [releaseSettle, countRef]);
355
- const scrollRef = react.useCallback(
356
- (element) => {
357
- if (scrollElementRef.current)
358
- scrollElementRef.current.removeEventListener("scroll", handleScroll);
359
- containerObserverRef.current?.disconnect();
360
- containerObserverRef.current = null;
361
- scrollElementRef.current = element;
362
- if (element) {
363
- element.addEventListener("scroll", handleScroll, { passive: true });
364
- if (typeof ResizeObserver !== "undefined") {
365
- const observer = new ResizeObserver(() => {
366
- setScrollOffset((prevState) => ({ ...prevState }));
367
- });
368
- observer.observe(element);
369
- containerObserverRef.current = observer;
370
- }
362
+ const scrollRef = react.useCallback((element) => {
363
+ scrollElementRef.current = element;
364
+ setScrollElement(element);
365
+ setScrollOffset(element ? { value: element.scrollTop } : { value: 0 });
366
+ }, []);
367
+ react.useEffect(() => {
368
+ const el = scrollElement;
369
+ const itemObservers = observersRef.current;
370
+ if (el) {
371
+ el.addEventListener("scroll", handleScroll, { passive: true });
372
+ if (typeof ResizeObserver !== "undefined") {
373
+ const observer = new ResizeObserver(() => {
374
+ setScrollOffset((prevState) => ({ ...prevState }));
375
+ });
376
+ observer.observe(el);
377
+ containerObserverRef.current = observer;
371
378
  }
372
- setScrollOffset(element ? { value: element?.scrollTop } : { value: 0 });
373
- },
374
- [handleScroll]
375
- );
376
- react.useEffect(
377
- () => () => {
379
+ }
380
+ for (const [index, { element }] of Array.from(itemObservers))
381
+ observeItem(index, element);
382
+ return () => {
378
383
  if (rafRef.current !== null)
379
384
  cancelAnimationFrame(rafRef.current);
380
385
  if (tickRafRef.current !== null)
381
386
  cancelAnimationFrame(tickRafRef.current);
382
- observersRef.current.forEach((entry) => entry.observer.disconnect());
383
- observersRef.current.clear();
387
+ el?.removeEventListener("scroll", handleScroll);
384
388
  containerObserverRef.current?.disconnect();
385
389
  containerObserverRef.current = null;
386
- scrollElementRef.current?.removeEventListener("scroll", handleScroll);
387
- refCacheRef.current.clear();
388
- },
389
- [handleScroll]
390
- );
390
+ itemObservers.forEach((entry) => entry.observer.disconnect());
391
+ };
392
+ }, [scrollElement, handleScroll, observeItem]);
391
393
  const { virtualItems, scrollHeight } = react.useMemo(
392
394
  () => computeItems(scrollOffset.value),
393
395
  [computeItems, scrollOffset]
package/lib/index.js CHANGED
@@ -109,6 +109,7 @@ function useVirtualizer({ count, estimateSize, overscan = DEFAULT_OVERSCAN }) {
109
109
  const [scrollOffset, setScrollOffset] = useState({ value: 0 });
110
110
  const [forcedRange, setForcedRange] = useState(null);
111
111
  const scrollElementRef = useRef(null);
112
+ const [scrollElement, setScrollElement] = useState(null);
112
113
  const rafRef = useRef(null);
113
114
  const observersRef = useRef(/* @__PURE__ */ new Map());
114
115
  const containerObserverRef = useRef(null);
@@ -186,22 +187,7 @@ function useVirtualizer({ count, estimateSize, overscan = DEFAULT_OVERSCAN }) {
186
187
  },
187
188
  [count, forcedRange, overscan]
188
189
  );
189
- const measureElement = useCallback((element, index) => {
190
- if (!element) {
191
- const entry = observersRef.current.get(index);
192
- if (entry) {
193
- entry.observer.disconnect();
194
- observersRef.current.delete(index);
195
- }
196
- return;
197
- }
198
- const existing = observersRef.current.get(index);
199
- if (existing && existing.element === element)
200
- return;
201
- if (existing) {
202
- existing.observer.disconnect();
203
- observersRef.current.delete(index);
204
- }
190
+ const observeItem = useCallback((index, element) => {
205
191
  if (typeof ResizeObserver === "undefined")
206
192
  return;
207
193
  const observer = new ResizeObserver(([entry]) => {
@@ -221,6 +207,27 @@ function useVirtualizer({ count, estimateSize, overscan = DEFAULT_OVERSCAN }) {
221
207
  observer.observe(element);
222
208
  observersRef.current.set(index, { observer, element });
223
209
  }, []);
210
+ const measureElement = useCallback(
211
+ (element, index) => {
212
+ if (!element) {
213
+ const entry = observersRef.current.get(index);
214
+ if (entry) {
215
+ entry.observer.disconnect();
216
+ observersRef.current.delete(index);
217
+ }
218
+ return;
219
+ }
220
+ const existing = observersRef.current.get(index);
221
+ if (existing && existing.element === element)
222
+ return;
223
+ if (existing) {
224
+ existing.observer.disconnect();
225
+ observersRef.current.delete(index);
226
+ }
227
+ observeItem(index, element);
228
+ },
229
+ [observeItem]
230
+ );
224
231
  const getMeasureRef = useCallback(
225
232
  (index) => {
226
233
  let fn = refCacheRef.current.get(index);
@@ -350,42 +357,37 @@ function useVirtualizer({ count, estimateSize, overscan = DEFAULT_OVERSCAN }) {
350
357
  setScrollOffset({ value: el.scrollTop });
351
358
  });
352
359
  }, [releaseSettle, countRef]);
353
- const scrollRef = useCallback(
354
- (element) => {
355
- if (scrollElementRef.current)
356
- scrollElementRef.current.removeEventListener("scroll", handleScroll);
357
- containerObserverRef.current?.disconnect();
358
- containerObserverRef.current = null;
359
- scrollElementRef.current = element;
360
- if (element) {
361
- element.addEventListener("scroll", handleScroll, { passive: true });
362
- if (typeof ResizeObserver !== "undefined") {
363
- const observer = new ResizeObserver(() => {
364
- setScrollOffset((prevState) => ({ ...prevState }));
365
- });
366
- observer.observe(element);
367
- containerObserverRef.current = observer;
368
- }
360
+ const scrollRef = useCallback((element) => {
361
+ scrollElementRef.current = element;
362
+ setScrollElement(element);
363
+ setScrollOffset(element ? { value: element.scrollTop } : { value: 0 });
364
+ }, []);
365
+ useEffect(() => {
366
+ const el = scrollElement;
367
+ const itemObservers = observersRef.current;
368
+ if (el) {
369
+ el.addEventListener("scroll", handleScroll, { passive: true });
370
+ if (typeof ResizeObserver !== "undefined") {
371
+ const observer = new ResizeObserver(() => {
372
+ setScrollOffset((prevState) => ({ ...prevState }));
373
+ });
374
+ observer.observe(el);
375
+ containerObserverRef.current = observer;
369
376
  }
370
- setScrollOffset(element ? { value: element?.scrollTop } : { value: 0 });
371
- },
372
- [handleScroll]
373
- );
374
- useEffect(
375
- () => () => {
377
+ }
378
+ for (const [index, { element }] of Array.from(itemObservers))
379
+ observeItem(index, element);
380
+ return () => {
376
381
  if (rafRef.current !== null)
377
382
  cancelAnimationFrame(rafRef.current);
378
383
  if (tickRafRef.current !== null)
379
384
  cancelAnimationFrame(tickRafRef.current);
380
- observersRef.current.forEach((entry) => entry.observer.disconnect());
381
- observersRef.current.clear();
385
+ el?.removeEventListener("scroll", handleScroll);
382
386
  containerObserverRef.current?.disconnect();
383
387
  containerObserverRef.current = null;
384
- scrollElementRef.current?.removeEventListener("scroll", handleScroll);
385
- refCacheRef.current.clear();
386
- },
387
- [handleScroll]
388
- );
388
+ itemObservers.forEach((entry) => entry.observer.disconnect());
389
+ };
390
+ }, [scrollElement, handleScroll, observeItem]);
389
391
  const { virtualItems, scrollHeight } = useMemo(
390
392
  () => computeItems(scrollOffset.value),
391
393
  [computeItems, scrollOffset]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clear-virtualizer",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "A lightweight React hook for virtualized lists and tables with dynamic row heights, measured automatically",
5
5
  "type": "module",
6
6
  "main": "./lib/index.cjs",