fix(ui): avoid offscreen mounts during initial layout
This commit is contained in:
@@ -761,7 +761,7 @@ export default function VirtualFollowList<T>(props: VirtualFollowListProps<T>) {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.renderItem(item(), index)}
|
{() => props.renderItem(item(), index)}
|
||||||
</VirtualItem>
|
</VirtualItem>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { JSX, Accessor, children as resolveChildren, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
import { JSX, Accessor, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
||||||
|
|
||||||
const sizeCache = new Map<string, number>()
|
const sizeCache = new Map<string, number>()
|
||||||
const DEFAULT_MARGIN_PX = 600
|
const DEFAULT_MARGIN_PX = 600
|
||||||
@@ -156,7 +156,7 @@ function subscribeToSharedObserver(
|
|||||||
|
|
||||||
interface VirtualItemProps {
|
interface VirtualItemProps {
|
||||||
cacheKey: string
|
cacheKey: string
|
||||||
children: JSX.Element
|
children: JSX.Element | (() => JSX.Element)
|
||||||
scrollContainer?: Accessor<HTMLElement | undefined | null>
|
scrollContainer?: Accessor<HTMLElement | undefined | null>
|
||||||
threshold?: number
|
threshold?: number
|
||||||
minPlaceholderHeight?: number
|
minPlaceholderHeight?: number
|
||||||
@@ -172,7 +172,7 @@ interface VirtualItemProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function VirtualItem(props: VirtualItemProps) {
|
export default function VirtualItem(props: VirtualItemProps) {
|
||||||
const resolved = resolveChildren(() => props.children)
|
const resolveContent = () => (typeof props.children === "function" ? (props.children as () => JSX.Element)() : props.children)
|
||||||
const cachedHeight = sizeCache.get(props.cacheKey)
|
const cachedHeight = sizeCache.get(props.cacheKey)
|
||||||
// Default to hidden until we can determine visibility.
|
// Default to hidden until we can determine visibility.
|
||||||
// This avoids keeping heavy DOM alive when IntersectionObserver
|
// This avoids keeping heavy DOM alive when IntersectionObserver
|
||||||
@@ -319,20 +319,11 @@ export default function VirtualItem(props: VirtualItemProps) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute an immediate best-effort visibility so switching tabs doesn't
|
// Avoid doing an eager geometry read here.
|
||||||
// depend on the first IntersectionObserver callback.
|
// During large list hydration / initial layout, wrapper rects can be
|
||||||
try {
|
// transiently 0/incorrect and cause many offscreen items to mount.
|
||||||
const rootRect =
|
// Rely on the observer callback (which we harden below) to determine
|
||||||
targetRoot && !(targetRoot instanceof Document)
|
// visibility.
|
||||||
? (targetRoot as Element).getBoundingClientRect()
|
|
||||||
: null
|
|
||||||
const bounds = rootRect ? { top: rootRect.top, bottom: rootRect.bottom } : getViewportRect()
|
|
||||||
setIsIntersecting(
|
|
||||||
shouldRenderByRects({ wrapperRect: wrapperRef.getBoundingClientRect(), rootRect: bounds, margin }),
|
|
||||||
)
|
|
||||||
} catch {
|
|
||||||
// Ignore measurement failures; IntersectionObserver will correct us.
|
|
||||||
}
|
|
||||||
|
|
||||||
const wrapperEl = wrapperRef
|
const wrapperEl = wrapperRef
|
||||||
intersectionCleanup = subscribeToSharedObserver(wrapperEl, targetRoot, margin, (entry) => {
|
intersectionCleanup = subscribeToSharedObserver(wrapperEl, targetRoot, margin, (entry) => {
|
||||||
@@ -441,7 +432,7 @@ export default function VirtualItem(props: VirtualItemProps) {
|
|||||||
const placeholderClass = () => ["virtual-item-placeholder", props.placeholderClass].filter(Boolean).join(" ")
|
const placeholderClass = () => ["virtual-item-placeholder", props.placeholderClass].filter(Boolean).join(" ")
|
||||||
const lazyContent = createMemo<JSX.Element | null>(() => {
|
const lazyContent = createMemo<JSX.Element | null>(() => {
|
||||||
if (shouldHideContent()) return null
|
if (shouldHideContent()) return null
|
||||||
return resolved()
|
return resolveContent()
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user