I'd say that the only to solve your problem would be to measure the size of content with useLayoutEffect
, and then to 'split' said content into pages.
(This is because I'm assuming you don't know the size of the each item from the list - if you did it would be simpler!).
I'd start with a useComponentSize
hook that would look something like so:
// Adapted from: https://stackoverflow.com/a/57272554/6595024
const useComponentSize = () => {
const informParentOfHeightChange = useContext<(h: number) => void>(
UpdateParentAboutMyHeight
);
const targetRef = useRef();
useLayoutEffect(() => {
if (targetRef.current) {
informParentOfHeightChange(targetRef.current.offsetHeight);
}
return () => informParentOfHeightChange(0);
}, [informParentOfHeightChange]);
return targetRef;
};
const UpdateParentAboutMyHeight = /* Left as an exercise to the reader */
Note: That my implementation is rather 'basic'; you'd probably want to useTransition or some other type of debounce effect so your UI wouldn't slow down to a crawl
Using this hook, the child would be able to inform the parent whenever it's height changed.
Here's an example:
const Comp = () => {
const targetRef = useComponentSize();
return <div ref={targetRef}>{/* etc */}</div>;
};
So then 'all' you'd need to do is create a parent component that keeps track of the height of all of it's Comp
children.
Here's some (inefficient) pseudo code to help you get an idea:
const UpdateParentAboutMyHeightProvider = /* Left as an exercise to the reader */
const HEIGHT_PER_PAGE = 1000;
const PageLayout = ({ items_to_render }: { items_to_render: Comp[] }) => {
const [items, setItems] = useState<{ height: number }[]>([]);
let curHeight = 0;
let curPage = 0;
const pages = items_to_render.reduce((pageList, item, index) => {
const itemHeight = items[index].height;
if (curHeight + itemHeight >= HEIGHT_PER_PAGE) {
// Create a new page!
pageList.push([item]);
curHeight = itemHeight;
curPage += 1;
return pageList;
}
// Add to existing page!
curHeight += itemHeight;
pageList[curPage].push(item);
return pageList;
}, [] as Array<Array<Comp>>);
return (
<UpdateParentAboutMyHeightProvider value={setItems}>
{pages.map((page, number) => (
<Page number={number + 1} key={number}>{page.map((item) => item)}</Page>
))}
</UpdateParentAboutMyHeightProvider>
);
};
I'll repeat that the implementation I wrote here is very basic and inefficient; the logic for when a page would be bigger than the maximum size is also probably insufficient[1]
Hopefully it'll be helpful in pointing you in the right direction.
[1] I kept it simple for readability and because I don't know your particular use case