From 83d562dbdf2eb485b1b832f9b3996d96964a78ee Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Wed, 22 Jul 2026 18:33:08 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F(frontend)=20isolate=20useSiz?= =?UTF-8?q?e=20subscription=20in=20a=20GridLayout=20leaf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the useSize observer into a leaf component of the GridLayout tree. That leaf then propagates size changes back into the GridLayout state through a callback. This prevents every size change from re-rendering the whole GridLayout tree, keeping the re-render local to the leaf that actually observes the size. --- .../features/layout/components/GridLayout.tsx | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/frontend/src/features/layout/components/GridLayout.tsx b/src/frontend/src/features/layout/components/GridLayout.tsx index 02939129..226fc02e 100644 --- a/src/frontend/src/features/layout/components/GridLayout.tsx +++ b/src/frontend/src/features/layout/components/GridLayout.tsx @@ -10,6 +10,35 @@ import { mergeProps } from '@/utils/mergeProps' import { PaginationIndicator } from './PaginationIndicator' import { useGridLayout } from '../hooks/useGridLayout' import { PaginationControl } from './PaginationControl' +import { useEffect, useRef, useState } from 'react' + +interface GridLayoutObserverProps { + gridEl: React.RefObject + trackCount: number + onMaxTilesChange: (maxTiles: number) => void +} + +/** + * Headless component that runs the layout calculation in isolation and + * reports the resulting tile capacity upward. + * + * `useGridLayout` re-renders its host on every layout recalculation + * (e.g. container resizes). Rendering it in a null child means only this + * component churns; the parent `GridLayout` re-renders solely when + * `maxTiles` actually changes, since `setState` bails out on equal values. + */ +const GridLayoutObserver = ({ + gridEl, + trackCount, + onMaxTilesChange, +}: GridLayoutObserverProps) => { + const { layout } = useGridLayout(gridEl, trackCount) + useEffect(() => { + onMaxTilesChange(layout.maxTiles) + }, [onMaxTilesChange, layout.maxTiles]) + + return null +} /** @public */ export interface GridLayoutProps @@ -37,14 +66,14 @@ export interface GridLayoutProps * @public */ export function GridLayout({ tracks, ...props }: GridLayoutProps) { - const gridEl = React.createRef() + const gridEl = useRef(null) + const [maxTiles, setMaxTiles] = useState(1) const elementProps = React.useMemo( () => mergeProps(props, { className: 'lk-grid-layout' }), [props] ) - const { layout } = useGridLayout(gridEl, tracks.length) - const pagination = usePagination(layout.maxTiles, tracks) + const pagination = usePagination(maxTiles, tracks) useSwipe(gridEl, { onLeftSwipe: pagination.nextPage, @@ -57,8 +86,13 @@ export function GridLayout({ tracks, ...props }: GridLayoutProps) { data-lk-pagination={pagination.totalPageCount > 1} {...elementProps} > + {props.children} - {tracks.length > layout.maxTiles && ( + {tracks.length > maxTiles && ( <>