fix: reliably init grid width on page refresh using useLayoutEffect

useResizeObserver reads ref.current at render time; on page refresh with
fast SWR cache, no re-render occurs after mount so ref.current remains null
in the effect, observation never starts, and containerWidth stays 0 forever.

Add a useLayoutEffect that measures offsetWidth synchronously before paint
as a seed value (effectiveWidth = containerWidth || initialWidth). Once
ResizeObserver fires normally, containerWidth takes over. The Responsive
grid is gated on effectiveWidth > 0 so it always renders correctly on both
first load and refresh.

https://claude.ai/code/session_01H1sqbcFmtwwsdNTJcJHJWd
This commit is contained in:
Claude 2026-03-15 11:32:13 +00:00
parent d39590604f
commit 5e40dbbcd2
No known key found for this signature in database

View File

@ -332,6 +332,20 @@ export default function DraggableGridLayout({
const [{ width: containerWidth, height: containerHeight }] = const [{ width: containerWidth, height: containerHeight }] =
useResizeObserver(gridContainerRef); useResizeObserver(gridContainerRef);
// useResizeObserver reads ref.current at render time, so it may miss the
// initial mount when ref.current is null (e.g. on page refresh with cached
// SWR data). Measure the container synchronously in useLayoutEffect as a
// reliable seed value; containerWidth from ResizeObserver takes over once
// it fires.
const [initialWidth, setInitialWidth] = useState(0);
useLayoutEffect(() => {
if (gridContainerRef.current) {
setInitialWidth(gridContainerRef.current.offsetWidth);
}
}, []);
const effectiveWidth = containerWidth || initialWidth;
const scrollBarWidth = useMemo(() => { const scrollBarWidth = useMemo(() => {
if (containerWidth && containerHeight && containerRef.current) { if (containerWidth && containerHeight && containerRef.current) {
return ( return (
@ -342,8 +356,8 @@ export default function DraggableGridLayout({
}, [containerRef, containerHeight, containerWidth]); }, [containerRef, containerHeight, containerWidth]);
const availableWidth = useMemo( const availableWidth = useMemo(
() => (scrollBarWidth ? containerWidth + scrollBarWidth : containerWidth), () => (scrollBarWidth ? effectiveWidth + scrollBarWidth : effectiveWidth),
[containerWidth, scrollBarWidth], [effectiveWidth, scrollBarWidth],
); );
const hasScrollbar = useMemo(() => { const hasScrollbar = useMemo(() => {
@ -710,7 +724,7 @@ export default function DraggableGridLayout({
currentGroups={groups} currentGroups={groups}
activeGroup={group} activeGroup={group}
/> />
{containerWidth > 0 && <Responsive {effectiveWidth > 0 && <Responsive
className="grid-layout" className="grid-layout"
width={availableWidth} width={availableWidth}
layouts={{ layouts={{