feat(island): ✨ allow lazy load blog card list with intersection observer
This commit is contained in:
parent
3dd4f2cc21
commit
8b716a382f
|
@ -2,8 +2,8 @@ import { BlogCard, BlogProps } from ':components/BlogBlocks.tsx'
|
||||||
import Suspense from ':islands/Suspens.tsx'
|
import Suspense from ':islands/Suspens.tsx'
|
||||||
import { requestApiStream } from ':src/utils.ts'
|
import { requestApiStream } from ':src/utils.ts'
|
||||||
import { Signal, useSignal } from '@preact/signals'
|
import { Signal, useSignal } from '@preact/signals'
|
||||||
import type { JSX } from 'preact'
|
import type { JSX, Ref } from 'preact'
|
||||||
import { useEffect } from 'preact/hooks'
|
import { useEffect, useRef } from 'preact/hooks'
|
||||||
|
|
||||||
function fillList(
|
function fillList(
|
||||||
list: Signal<JSX.Element[]>,
|
list: Signal<JSX.Element[]>,
|
||||||
|
@ -27,13 +27,31 @@ function fillList(
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function BlogCardList(
|
export default function BlogCardList(
|
||||||
{ limit, usePlaceholder }: { usePlaceholder?: boolean; limit?: number },
|
{ limit, usePlaceholder, useObserver }: {
|
||||||
|
usePlaceholder?: boolean
|
||||||
|
limit?: number
|
||||||
|
useObserver?: boolean
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
const list = useSignal<JSX.Element[]>([])
|
const list: Signal<JSX.Element[]> = useSignal<JSX.Element[]>([])
|
||||||
const ac = new AbortController()
|
const ac = new AbortController()
|
||||||
|
const ref = useRef(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (ref.current && useObserver) {
|
||||||
|
const observer = new IntersectionObserver(([entry]) => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
fillList(list, { limit, ac })
|
fillList(list, { limit, ac })
|
||||||
|
observer.disconnect()
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
rootMargin: '300px',
|
||||||
|
})
|
||||||
|
//@ts-expect-error Need to investigate why it's work
|
||||||
|
observer.observe(ref.current.base)
|
||||||
|
} else {
|
||||||
|
fillList(list, { limit, ac })
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (limit && usePlaceholder) {
|
if (limit && usePlaceholder) {
|
||||||
|
@ -41,7 +59,7 @@ export default function BlogCardList(
|
||||||
.from({ length: limit })
|
.from({ length: limit })
|
||||||
.map((_, index) => (
|
.map((_, index) => (
|
||||||
<Suspense
|
<Suspense
|
||||||
loader={<Placeholder />}
|
loader={<Placeholder ref={index === 0 ? ref : undefined} />}
|
||||||
fallback={Fallback}
|
fallback={Fallback}
|
||||||
signal={ac.signal}
|
signal={ac.signal}
|
||||||
>
|
>
|
||||||
|
@ -54,9 +72,12 @@ export default function BlogCardList(
|
||||||
return <>{list}</>
|
return <>{list}</>
|
||||||
}
|
}
|
||||||
|
|
||||||
function Placeholder() {
|
function Placeholder({ ref }: { ref?: Ref<HTMLDivElement> | undefined }) {
|
||||||
return (
|
return (
|
||||||
<div class='components__blog_block components__blog_block--card components__blog_block--placeholder'>
|
<div
|
||||||
|
class='components__blog_block components__blog_block--card components__blog_block--placeholder'
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
<h3>Chargement ...</h3>
|
<h3>Chargement ...</h3>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue