Skip to content

Commit 8aeec88

Browse files
committed
perf directory
1 parent d70d656 commit 8aeec88

File tree

8 files changed

+64
-33
lines changed

8 files changed

+64
-33
lines changed

src/components/CCIP/Cards/NetworkCard.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
background: var(--white);
77
border: 1px solid var(--gray-200);
88
border-radius: var(--space-1x);
9+
/* Optimize rendering performance */
10+
contain: layout style paint;
11+
will-change: background-color;
912
}
1013

1114
.network-card__container:hover {
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { memo } from "react"
12
import "./NetworkCard.css"
23

34
interface NetworkCardProps {
@@ -7,10 +8,10 @@ interface NetworkCardProps {
78
logo: string
89
}
910

10-
function NetworkCard({ name, totalLanes, totalTokens, logo }: NetworkCardProps) {
11+
const NetworkCard = memo(function NetworkCard({ name, totalLanes, totalTokens, logo }: NetworkCardProps) {
1112
return (
1213
<div className="network-card__container">
13-
<img src={logo} alt="" />
14+
<img src={logo} alt="" loading="lazy" />
1415
<div>
1516
<h3>{name}</h3>
1617
<p>
@@ -19,6 +20,6 @@ function NetworkCard({ name, totalLanes, totalTokens, logo }: NetworkCardProps)
1920
</div>
2021
</div>
2122
)
22-
}
23+
})
2324

2425
export default NetworkCard

src/components/CCIP/Cards/TokenCard.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
border-radius: var(--space-1x);
1515
justify-content: center;
1616
cursor: pointer;
17+
/* Optimize rendering performance */
18+
contain: layout style paint;
19+
will-change: background-color;
1720
}
1821

1922
.token-card__container:hover {

src/components/CCIP/Cards/TokenCard.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { memo } from "react"
12
import { fallbackTokenIconUrl } from "~/features/utils/index.ts"
23
import "./TokenCard.css"
34

@@ -8,15 +9,15 @@ interface TokenCardProps {
89
onClick?: () => void
910
}
1011

11-
function TokenCard({ id, logo, link, onClick }: TokenCardProps) {
12+
const TokenCard = memo(function TokenCard({ id, logo, link, onClick }: TokenCardProps) {
1213
if (link) {
1314
return (
1415
<a href={link}>
1516
<div className="token-card__container">
1617
{/* We cannot use the normal Image/onError syntax as a fallback as the element is server rendered
1718
and the onerror does not seem to work correctly. Using Picture will also not work. */}
1819
<object data={logo} type="image/png">
19-
<img src={fallbackTokenIconUrl} alt="" />
20+
<img src={fallbackTokenIconUrl} alt="" loading="lazy" />
2021
</object>
2122
<h3>{id}</h3>
2223
</div>
@@ -28,7 +29,7 @@ function TokenCard({ id, logo, link, onClick }: TokenCardProps) {
2829
return (
2930
<div className="token-card__container" onClick={onClick} role="button">
3031
<object data={logo} type="image/png">
31-
<img src={fallbackTokenIconUrl} alt="" />
32+
<img src={fallbackTokenIconUrl} alt="" loading="lazy" />
3233
</object>
3334
<h3>{id}</h3>
3435
</div>
@@ -38,11 +39,11 @@ function TokenCard({ id, logo, link, onClick }: TokenCardProps) {
3839
return (
3940
<div className="token-card__container">
4041
<object data={logo} type="image/png">
41-
<img src={fallbackTokenIconUrl} alt="" />
42+
<img src={fallbackTokenIconUrl} alt="" loading="lazy" />
4243
</object>
4344
<h3>{id}</h3>
4445
</div>
4546
)
46-
}
47+
})
4748

4849
export default TokenCard

src/components/CCIP/Drawer/Drawer.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
background-color: #1a2332e5;
99
backdrop-filter: blur(var(--space-1x)) opacity(0);
1010
transition: backdrop-filter 0.5s ease-in-out;
11+
will-change: opacity, visibility;
1112
}
1213

1314
.drawer__open.drawer {
@@ -27,6 +28,7 @@
2728
background-color: white;
2829
transform: translateX(100%);
2930
transition: transform 0.5s ease-in-out;
31+
will-change: transform;
3032
}
3133

3234
.drawer__content {

src/components/CCIP/Drawer/Drawer.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,14 @@ function Drawer() {
4444
const handleClose = () => {
4545
setIsOpened(false)
4646

47-
// wait for animation to finish
48-
setTimeout(() => {
47+
// Use transitionend event instead of setTimeout for better performance
48+
const handleTransitionEnd = () => {
4949
drawerContentStore.set(null)
50-
}, 500)
50+
drawerRef.current?.removeEventListener("transitionend", handleTransitionEnd)
51+
}
52+
53+
// Listen for the transition to complete
54+
drawerRef.current?.addEventListener("transitionend", handleTransitionEnd)
5155
}
5256

5357
return (

src/components/CCIP/Landing/ccip-landing.astro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,14 @@ const directoryStructuredData = generateDirectoryStructuredData(environment, net
7171
<div class="ccip-heading">
7272
<h2>Networks {environment} <span>({networks.length})</span></h2>
7373
</div>
74-
<NetworkGrid networks={networks} environment={environment} client:load />
74+
<NetworkGrid networks={networks} environment={environment} client:idle />
7575
</div>
7676
<div>
7777
<div class="ccip-heading">
7878
<h2>Tokens <span>({allTokens.length})</span></h2>
7979
<a class="button secondary" href="/ccip/tutorials/evm/token-manager#verifying-your-token">Add my token</a>
8080
</div>
81-
<TokenGrid tokens={allTokens} environment={environment} client:load />
81+
<TokenGrid tokens={allTokens} environment={environment} client:idle />
8282
</div>
8383
</section>
8484
</CcipDirectoryLayout>

src/components/CCIP/Search/Search.tsx

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useEffect, useRef } from "react"
1+
import { useState, useEffect, useRef, useMemo } from "react"
22
import "./Search.css"
33
import { clsx } from "~/lib/clsx/clsx.ts"
44
import { useClickOutside } from "~/hooks/useClickOutside.tsx"
@@ -43,34 +43,49 @@ interface SearchProps {
4343

4444
function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
4545
const [search, setSearch] = useState("")
46+
const [debouncedSearch, setDebouncedSearch] = useState("")
4647
const [openSearchMenu, setOpenSearchMenu] = useState(false)
4748
const [isActive, setIsActive] = useState(false)
48-
const [networksResults, setNetworksResults] = useState<SearchProps["chains"]>([])
49-
const [tokensResults, setTokensResults] = useState<SearchProps["tokens"]>([])
50-
const [lanesResults, setLanesResults] = useState<SearchProps["lanes"]>([])
5149
const searchRef = useRef<HTMLDivElement>(null)
5250

51+
// Debounce search input
5352
useEffect(() => {
54-
if (search) {
55-
const networks = chains.filter((chain) => chain.name.toLowerCase().includes(search.toLowerCase()))
56-
const tokensList = tokens.filter((token) => token.id.toLowerCase().includes(search.toLowerCase()))
57-
const lanesList = lanes.filter(
58-
(lane) =>
59-
(lane.sourceNetwork.name.toLowerCase().includes(search.toLowerCase()) ||
60-
lane.destinationNetwork.name.toLowerCase().includes(search.toLowerCase())) &&
61-
(lane.lane.supportedTokens ? Object.keys(lane.lane.supportedTokens).length : 0) > 0
62-
)
63-
setNetworksResults(networks)
64-
setTokensResults(tokensList)
65-
setLanesResults(lanesList)
53+
const timer = setTimeout(() => {
54+
setDebouncedSearch(search)
55+
}, 300)
56+
57+
return () => clearTimeout(timer)
58+
}, [search])
59+
60+
// Memoize filtered results to prevent unnecessary recalculations
61+
const networksResults = useMemo(() => {
62+
if (!debouncedSearch) return []
63+
return chains.filter((chain) => chain.name.toLowerCase().includes(debouncedSearch.toLowerCase()))
64+
}, [debouncedSearch, chains])
65+
66+
const tokensResults = useMemo(() => {
67+
if (!debouncedSearch) return []
68+
return tokens.filter((token) => token.id.toLowerCase().includes(debouncedSearch.toLowerCase()))
69+
}, [debouncedSearch, tokens])
70+
71+
const lanesResults = useMemo(() => {
72+
if (!debouncedSearch) return []
73+
return lanes.filter(
74+
(lane) =>
75+
(lane.sourceNetwork.name.toLowerCase().includes(debouncedSearch.toLowerCase()) ||
76+
lane.destinationNetwork.name.toLowerCase().includes(debouncedSearch.toLowerCase())) &&
77+
(lane.lane.supportedTokens ? Object.keys(lane.lane.supportedTokens).length : 0) > 0
78+
)
79+
}, [debouncedSearch, lanes])
80+
81+
// Handle menu visibility
82+
useEffect(() => {
83+
if (debouncedSearch) {
6684
setOpenSearchMenu(true)
6785
} else {
68-
setNetworksResults([])
69-
setTokensResults([])
70-
setLanesResults([])
7186
setOpenSearchMenu(false)
7287
}
73-
}, [search, chains, tokens])
88+
}, [debouncedSearch])
7489

7590
useClickOutside(searchRef, () => setOpenSearchMenu(false))
7691

@@ -126,6 +141,7 @@ function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
126141
<img
127142
src={network.logo}
128143
alt=""
144+
loading="lazy"
129145
onError={({ currentTarget }) => {
130146
currentTarget.onerror = null // prevents looping
131147
currentTarget.src = fallbackTokenIconUrl
@@ -154,6 +170,7 @@ function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
154170
<img
155171
src={token.logo}
156172
alt=""
173+
loading="lazy"
157174
onError={({ currentTarget }) => {
158175
currentTarget.onerror = null // prevents looping
159176
currentTarget.src = fallbackTokenIconUrl

0 commit comments

Comments
 (0)