Skip to content

Commit 89185da

Browse files
authored
Merge pull request #13298 from ethereum/performance/home-stats-intersection-obs
Performance: lazy load stats using intersection obs
2 parents 8d39bf2 + 04719a5 commit 89185da

File tree

2 files changed

+89
-24
lines changed

2 files changed

+89
-24
lines changed

src/components/LazyLoadComponent.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React, { Suspense, useEffect, useRef, useState } from "react"
2+
3+
interface LazyLoadComponentProps<T extends React.ElementType> {
4+
component: T
5+
fallback: React.ReactNode
6+
componentProps: React.ComponentProps<T>
7+
intersectionOptions?: IntersectionObserverInit
8+
}
9+
10+
const LazyLoadComponent = <T extends React.ElementType>({
11+
component: Component,
12+
fallback,
13+
componentProps,
14+
intersectionOptions = {},
15+
}: LazyLoadComponentProps<T>) => {
16+
const [isVisible, setIsVisible] = useState(false)
17+
const ref = useRef<HTMLDivElement>(null)
18+
19+
useEffect(() => {
20+
const observer = new IntersectionObserver(([entry]) => {
21+
// Update the state when observer callback fires
22+
if (entry.isIntersecting) {
23+
setIsVisible(true)
24+
observer.disconnect()
25+
}
26+
}, intersectionOptions)
27+
28+
if (ref.current) {
29+
observer.observe(ref.current)
30+
}
31+
32+
// Clean up the observer on component unmount
33+
return () => {
34+
if (ref.current) {
35+
observer.disconnect()
36+
}
37+
}
38+
}, [])
39+
40+
return (
41+
<div ref={ref}>
42+
{isVisible ? (
43+
<Suspense fallback={fallback}>
44+
<Component {...componentProps} />
45+
</Suspense>
46+
) : (
47+
fallback // Show fallback until the component is visible
48+
)}
49+
</div>
50+
)
51+
}
52+
53+
export default LazyLoadComponent

src/pages/index.tsx

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ import CodeModal from "@/components/CodeModal"
2929
import CommunityEvents from "@/components/CommunityEvents"
3030
import HomeHero from "@/components/Hero/HomeHero"
3131
import { Image } from "@/components/Image"
32+
import LazyLoadComponent from "@/components/LazyLoadComponent"
3233
import MainArticle from "@/components/MainArticle"
3334
import PageMetadata from "@/components/PageMetadata"
34-
import StatsBoxGrid from "@/components/StatsBoxGrid"
3535
import TitleCardList from "@/components/TitleCardList"
3636
import Translation from "@/components/Translation"
3737

@@ -70,6 +70,29 @@ import merge from "@/public/images/upgrades/merge.png"
7070
import robotfixed from "@/public/images/wallet-cropped.png"
7171
import ethereum from "@/public/images/what-is-ethereum.png"
7272

73+
// lazy loaded components
74+
const Codeblock = lazy(() =>
75+
Promise.all([
76+
import("@/components/Codeblock"),
77+
// Add a delay to prevent the skeleton from flashing
78+
new Promise((resolve) => setTimeout(resolve, 1000)),
79+
]).then(([module]) => module)
80+
)
81+
const StatsBoxGrid = lazy(() => import("@/components/StatsBoxGrid"))
82+
83+
const Skeleton = () => (
84+
<Stack px={6} pt="2.75rem" h="50vh">
85+
<SkeletonText
86+
mt="4"
87+
noOfLines={6}
88+
spacing={4}
89+
skeletonHeight="1.4rem"
90+
startColor="body.medium"
91+
opacity={0.2}
92+
/>
93+
</Stack>
94+
)
95+
7396
const SectionHeading = (props: HeadingProps) => (
7497
<Heading
7598
lineHeight={1.4}
@@ -225,27 +248,6 @@ export const getStaticProps = (async ({ locale }) => {
225248
}
226249
}) satisfies GetStaticProps<Props>
227250

228-
const CodeblockSkeleton = () => (
229-
<Stack px={6} pt="2.75rem" h="50vh">
230-
<SkeletonText
231-
mt="4"
232-
noOfLines={6}
233-
spacing={4}
234-
skeletonHeight="1.4rem"
235-
startColor="body.medium"
236-
opacity={0.2}
237-
/>
238-
</Stack>
239-
)
240-
241-
const Codeblock = lazy(() =>
242-
Promise.all([
243-
import("@/components/Codeblock"),
244-
// Add a delay to prevent the skeleton from flashing
245-
new Promise((resolve) => setTimeout(resolve, 1000)),
246-
]).then(([module]) => module)
247-
)
248-
249251
const HomePage = ({
250252
communityEvents,
251253
metricResults,
@@ -561,7 +563,7 @@ const HomePage = ({
561563
setIsOpen={setModalOpen}
562564
title={codeExamples[activeCode].title}
563565
>
564-
<Suspense fallback={<CodeblockSkeleton />}>
566+
<Suspense fallback={<Skeleton />}>
565567
<Codeblock
566568
codeLanguage={codeExamples[activeCode].codeLanguage}
567569
allowCollapse={false}
@@ -584,7 +586,17 @@ const HomePage = ({
584586
<Translation id="page-index:page-index-network-stats-subtitle" />
585587
</SectionDecription>
586588
</ContentBox>
587-
<StatsBoxGrid data={metricResults} />
589+
590+
<LazyLoadComponent
591+
component={StatsBoxGrid}
592+
fallback={<Skeleton />}
593+
componentProps={{ data: metricResults }}
594+
intersectionOptions={{
595+
root: null,
596+
rootMargin: "500px",
597+
threshold: 0,
598+
}}
599+
/>
588600
</GrayContainer>
589601
<Divider mb={16} mt={16} w="10%" height="0.25rem" bgColor="homeDivider" />
590602
<CommunityEvents events={communityEvents} />

0 commit comments

Comments
 (0)