retry autofit calculation to prevent zero value on initial render and memoization

This commit is contained in:
jeffvli
2026-01-22 02:53:25 -08:00
parent 3a5d701195
commit b45594515e
@@ -1,4 +1,4 @@
import { useEffect } from 'react'; import { useLayoutEffect } from 'react';
interface UseContainerWidthTrackingProps { interface UseContainerWidthTrackingProps {
autoFitColumns: boolean; autoFitColumns: boolean;
@@ -18,14 +18,58 @@ export const useContainerWidthTracking = ({
setCenterContainerWidth, setCenterContainerWidth,
setTotalContainerWidth, setTotalContainerWidth,
}: UseContainerWidthTrackingProps) => { }: UseContainerWidthTrackingProps) => {
const createWidthUpdater = (
el: HTMLDivElement,
setWidth: (width: number) => void,
opts?: { maxRafRetries?: number },
) => {
const maxRafRetries = opts?.maxRafRetries ?? 10;
let rafId: null | number = null;
const cancel = () => {
if (rafId !== null) cancelAnimationFrame(rafId);
rafId = null;
};
const updateWidth = () => {
const measured = el.clientWidth || 0;
if (measured > 0) {
cancel();
setWidth(measured);
return;
}
// Some layouts can report 0 on first paint
// Retry a few frames to catch the first non-zero measurement
cancel();
let attempts = 0;
const retry = () => {
const next = el.clientWidth || 0;
if (next > 0) {
rafId = null;
setWidth(next);
return;
}
attempts++;
if (attempts < maxRafRetries) {
rafId = requestAnimationFrame(retry);
} else {
rafId = null;
setWidth(0);
}
};
rafId = requestAnimationFrame(retry);
};
return { cancel, updateWidth };
};
// Track center container width (for column distribution) // Track center container width (for column distribution)
useEffect(() => { useLayoutEffect(() => {
const el = rowRef.current; const el = rowRef.current;
if (!el) return; if (!el) return;
const updateWidth = () => { const { cancel, updateWidth } = createWidthUpdater(el, setCenterContainerWidth);
setCenterContainerWidth(el.clientWidth || 0);
};
updateWidth(); updateWidth();
@@ -45,18 +89,17 @@ export const useContainerWidthTracking = ({
if (debounceTimeout) { if (debounceTimeout) {
clearTimeout(debounceTimeout); clearTimeout(debounceTimeout);
} }
cancel();
resizeObserver.disconnect(); resizeObserver.disconnect();
}; };
}, [rowRef, setCenterContainerWidth]); }, [rowRef, setCenterContainerWidth]);
// Track total container width for autoFitColumns // Track total container width for autoFitColumns
useEffect(() => { useLayoutEffect(() => {
const el = containerRef.current; const el = containerRef.current;
if (!el || !autoFitColumns) return; if (!el || !autoFitColumns) return;
const updateWidth = () => { const { cancel, updateWidth } = createWidthUpdater(el, setTotalContainerWidth);
setTotalContainerWidth(el.clientWidth || 0);
};
updateWidth(); updateWidth();
@@ -76,6 +119,7 @@ export const useContainerWidthTracking = ({
if (debounceTimeout) { if (debounceTimeout) {
clearTimeout(debounceTimeout); clearTimeout(debounceTimeout);
} }
cancel();
resizeObserver.disconnect(); resizeObserver.disconnect();
}; };
}, [autoFitColumns, containerRef, setTotalContainerWidth]); }, [autoFitColumns, containerRef, setTotalContainerWidth]);