Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
interface
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
LuckySwap
interface
Commits
8c1e41a3
Unverified
Commit
8c1e41a3
authored
Sep 30, 2022
by
lynn
Committed by
GitHub
Sep 30, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: glitchy lazy loading (big jump / unnecessary scroll) (#4776)
* fix glitchy loading * fix initial no tokens state
parent
9859c0b4
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
48 additions
and
26 deletions
+48
-26
TokenTable.tsx
src/components/Tokens/TokenTable/TokenTable.tsx
+3
-4
TopTokens.ts
src/graphql/data/TopTokens.ts
+45
-22
No files found.
src/components/Tokens/TokenTable/TokenTable.tsx
View file @
8c1e41a3
...
@@ -72,8 +72,7 @@ export default function TokenTable() {
...
@@ -72,8 +72,7 @@ export default function TokenTable() {
// TODO: consider moving prefetched call into app.tsx and passing it here, use a preloaded call & updated on interval every 60s
// TODO: consider moving prefetched call into app.tsx and passing it here, use a preloaded call & updated on interval every 60s
const
chainName
=
validateUrlChainParam
(
useParams
<
{
chainName
?:
string
}
>
().
chainName
)
const
chainName
=
validateUrlChainParam
(
useParams
<
{
chainName
?:
string
}
>
().
chainName
)
const
{
loading
,
tokens
,
tokensWithoutPriceHistoryCount
,
hasMore
,
loadMoreTokens
,
maxFetchable
}
=
const
{
error
,
loading
,
tokens
,
hasMore
,
loadMoreTokens
,
maxFetchable
}
=
useTopTokens
(
chainName
)
useTopTokens
(
chainName
)
const
showMoreLoadingRows
=
Boolean
(
loading
&&
hasMore
)
const
showMoreLoadingRows
=
Boolean
(
loading
&&
hasMore
)
const
observer
=
useRef
<
IntersectionObserver
>
()
const
observer
=
useRef
<
IntersectionObserver
>
()
...
@@ -93,9 +92,9 @@ export default function TokenTable() {
...
@@ -93,9 +92,9 @@ export default function TokenTable() {
/* loading and error state */
/* loading and error state */
if
(
loading
&&
(
!
tokens
||
tokens
?.
length
===
0
))
{
if
(
loading
&&
(
!
tokens
||
tokens
?.
length
===
0
))
{
return
<
LoadingTokenTable
rowCount=
{
Math
.
min
(
tokensWithoutPriceHistoryCount
,
PAGE_SIZE
)
}
/>
return
<
LoadingTokenTable
rowCount=
{
PAGE_SIZE
}
/>
}
else
{
}
else
{
if
(
!
tokens
)
{
if
(
error
||
!
tokens
)
{
return
(
return
(
<
NoTokensState
<
NoTokensState
message=
{
message=
{
...
...
src/graphql/data/TopTokens.ts
View file @
8c1e41a3
...
@@ -8,8 +8,8 @@ import {
...
@@ -8,8 +8,8 @@ import {
sortMethodAtom
,
sortMethodAtom
,
}
from
'
components/Tokens/state
'
}
from
'
components/Tokens/state
'
import
{
useAtomValue
}
from
'
jotai/utils
'
import
{
useAtomValue
}
from
'
jotai/utils
'
import
{
useCallback
,
useLayoutEffect
,
useMemo
,
useState
}
from
'
react
'
import
{
useCallback
,
use
Effect
,
use
LayoutEffect
,
useMemo
,
useState
}
from
'
react
'
import
{
fetchQuery
,
use
LazyLoadQuery
,
use
RelayEnvironment
}
from
'
react-relay
'
import
{
fetchQuery
,
useRelayEnvironment
}
from
'
react-relay
'
import
{
import
{
Chain
,
Chain
,
...
@@ -20,10 +20,6 @@ import {
...
@@ -20,10 +20,6 @@ import {
import
type
{
TopTokens100Query
}
from
'
./__generated__/TopTokens100Query.graphql
'
import
type
{
TopTokens100Query
}
from
'
./__generated__/TopTokens100Query.graphql
'
import
{
toHistoryDuration
}
from
'
./util
'
import
{
toHistoryDuration
}
from
'
./util
'
export
function
usePrefetchTopTokens
(
duration
:
HistoryDuration
,
chain
:
Chain
)
{
return
useLazyLoadQuery
<
TopTokens100Query
>
(
topTokens100Query
,
{
duration
,
chain
})
}
const
topTokens100Query
=
graphql
`
const
topTokens100Query
=
graphql
`
query TopTokens100Query($duration: HistoryDuration!, $chain: Chain!) {
query TopTokens100Query($duration: HistoryDuration!, $chain: Chain!) {
topTokens(pageSize: 100, page: 1, chain: $chain) {
topTokens(pageSize: 100, page: 1, chain: $chain) {
...
@@ -166,29 +162,48 @@ const checkIfAllTokensCached = (duration: HistoryDuration, tokens: PrefetchedTop
...
@@ -166,29 +162,48 @@ const checkIfAllTokensCached = (duration: HistoryDuration, tokens: PrefetchedTop
export
type
TopToken
=
NonNullable
<
TopTokens_TokensQuery
[
'
response
'
][
'
tokens
'
]
>
[
number
]
export
type
TopToken
=
NonNullable
<
TopTokens_TokensQuery
[
'
response
'
][
'
tokens
'
]
>
[
number
]
interface
UseTopTokensReturnValue
{
interface
UseTopTokensReturnValue
{
error
:
Error
|
undefined
loading
:
boolean
loading
:
boolean
tokens
:
TopToken
[]
|
undefined
tokens
:
TopToken
[]
|
undefined
tokensWithoutPriceHistoryCount
:
number
hasMore
:
boolean
hasMore
:
boolean
loadMoreTokens
:
()
=>
void
loadMoreTokens
:
()
=>
void
maxFetchable
:
number
maxFetchable
:
number
}
}
export
function
useTopTokens
(
chain
:
Chain
):
UseTopTokensReturnValue
{
export
function
useTopTokens
(
chain
:
Chain
):
UseTopTokensReturnValue
{
const
duration
=
toHistoryDuration
(
useAtomValue
(
filterTimeAtom
))
const
duration
=
toHistoryDuration
(
useAtomValue
(
filterTimeAtom
))
const
[
loading
,
setLoading
]
=
useState
(
true
)
const
[
loadingTokensWithoutPriceHistory
,
setLoadingTokensWithoutPriceHistory
]
=
useState
(
true
)
const
[
loadingTokensWithPriceHistory
,
setLoadingTokensWithPriceHistory
]
=
useState
(
true
)
const
[
tokens
,
setTokens
]
=
useState
<
TopToken
[]
>
()
const
[
tokens
,
setTokens
]
=
useState
<
TopToken
[]
>
()
const
[
page
,
setPage
]
=
useState
(
0
)
const
[
page
,
setPage
]
=
useState
(
0
)
const
prefetchedData
=
usePrefetchTopTokens
(
duration
,
chain
)
const
[
error
,
setError
]
=
useState
<
Error
|
undefined
>
()
const
prefetchedSelectedTokensWithoutPriceHistory
=
useFilteredTokens
(
useSortedTokens
(
prefetchedData
.
topTokens
))
const
[
prefetchedData
,
setPrefetchedData
]
=
useState
<
PrefetchedTopToken
[]
>
([])
const
prefetchedSelectedTokensWithoutPriceHistory
=
useFilteredTokens
(
useSortedTokens
(
prefetchedData
))
const
maxFetchable
=
useMemo
(
const
maxFetchable
=
useMemo
(
()
=>
prefetchedSelectedTokensWithoutPriceHistory
.
length
,
()
=>
prefetchedSelectedTokensWithoutPriceHistory
.
length
,
[
prefetchedSelectedTokensWithoutPriceHistory
]
[
prefetchedSelectedTokensWithoutPriceHistory
]
)
)
const
hasMore
=
!
tokens
||
tokens
.
length
<
prefetchedSelectedTokensWithoutPriceHistory
.
length
const
hasMore
=
!
tokens
||
tokens
.
length
<
prefetchedSelectedTokensWithoutPriceHistory
.
length
const
environment
=
useRelayEnvironment
()
const
environment
=
useRelayEnvironment
()
const
loadTokensWithoutPriceHistory
=
useCallback
(
({
duration
,
chain
}:
{
duration
:
HistoryDuration
;
chain
:
Chain
})
=>
{
fetchQuery
<
TopTokens100Query
>
(
environment
,
topTokens100Query
,
{
duration
,
chain
},
{
fetchPolicy
:
'
store-or-network
'
}
).
subscribe
({
next
:
(
data
)
=>
{
if
(
data
?.
topTokens
)
setPrefetchedData
([...
data
?.
topTokens
])
},
error
:
setError
,
complete
:
()
=>
setLoadingTokensWithoutPriceHistory
(
false
),
})
},
[
environment
]
)
// TopTokens should ideally be fetched with usePaginationFragment. The backend does not current support graphql cursors;
// TopTokens should ideally be fetched with usePaginationFragment. The backend does not current support graphql cursors;
// in the meantime, fetchQuery is used, as other relay hooks do not allow the refreshing and lazy loading we need
// in the meantime, fetchQuery is used, as other relay hooks do not allow the refreshing and lazy loading we need
const
loadTokensWithPriceHistory
=
useCallback
(
const
loadTokensWithPriceHistory
=
useCallback
(
...
@@ -208,25 +223,27 @@ export function useTopTokens(chain: Chain): UseTopTokensReturnValue {
...
@@ -208,25 +223,27 @@ export function useTopTokens(chain: Chain): UseTopTokensReturnValue {
tokensQuery
,
tokensQuery
,
{
contracts
,
duration
},
{
contracts
,
duration
},
{
fetchPolicy
:
'
store-or-network
'
}
{
fetchPolicy
:
'
store-or-network
'
}
)
).
subscribe
({
.
toPromise
()
next
:
(
data
)
=>
{
.
then
((
data
)
=>
{
if
(
data
?.
tokens
)
{
if
(
data
?.
tokens
)
{
const
priceHistoryCacheForCurrentDuration
=
tokensWithPriceHistoryCache
[
duration
]
const
priceHistoryCacheForCurrentDuration
=
tokensWithPriceHistoryCache
[
duration
]
data
.
tokens
.
map
((
token
)
=>
data
.
tokens
.
map
((
token
)
=>
!!
token
?
(
priceHistoryCacheForCurrentDuration
[
`
${
token
.
chain
}${
token
.
address
}
`
]
=
token
)
:
null
!!
token
?
(
priceHistoryCacheForCurrentDuration
[
`
${
token
.
chain
}${
token
.
address
}
`
]
=
token
)
:
null
)
)
appendingTokens
?
setTokens
([...(
tokens
??
[]),
...
data
.
tokens
])
:
setTokens
([...
data
.
tokens
])
appendingTokens
?
setTokens
([...(
tokens
??
[]),
...
data
.
tokens
])
:
setTokens
([...
data
.
tokens
])
setLoading
(
false
)
setLoading
TokensWithPriceHistory
(
false
)
setPage
(
page
+
1
)
setPage
(
page
+
1
)
}
}
})
},
error
:
setError
,
complete
:
()
=>
setLoadingTokensWithPriceHistory
(
false
),
})
},
},
[
duration
,
environment
]
[
duration
,
environment
]
)
)
const
loadMoreTokens
=
useCallback
(()
=>
{
const
loadMoreTokens
=
useCallback
(()
=>
{
setLoading
(
true
)
setLoading
TokensWithPriceHistory
(
true
)
const
contracts
=
prefetchedSelectedTokensWithoutPriceHistory
const
contracts
=
prefetchedSelectedTokensWithoutPriceHistory
.
slice
(
page
*
PAGE_SIZE
,
(
page
+
1
)
*
PAGE_SIZE
)
.
slice
(
page
*
PAGE_SIZE
,
(
page
+
1
)
*
PAGE_SIZE
)
.
map
(
toContractInput
)
.
map
(
toContractInput
)
...
@@ -241,21 +258,27 @@ export function useTopTokens(chain: Chain): UseTopTokensReturnValue {
...
@@ -241,21 +258,27 @@ export function useTopTokens(chain: Chain): UseTopTokensReturnValue {
)
)
if
(
everyTokenInCache
)
{
if
(
everyTokenInCache
)
{
setTokens
(
cachedTokens
)
setTokens
(
cachedTokens
)
setLoading
(
false
)
setLoadingTokensWithPriceHistory
(
false
)
return
}
else
{
}
else
{
setLoading
(
true
)
setLoading
TokensWithPriceHistory
(
true
)
setTokens
([])
setTokens
([])
const
contracts
=
prefetchedSelectedTokensWithoutPriceHistory
.
slice
(
0
,
PAGE_SIZE
).
map
(
toContractInput
)
const
contracts
=
prefetchedSelectedTokensWithoutPriceHistory
.
slice
(
0
,
PAGE_SIZE
).
map
(
toContractInput
)
loadTokensWithPriceHistory
({
contracts
,
appendingTokens
:
false
,
page
:
0
})
loadTokensWithPriceHistory
({
contracts
,
appendingTokens
:
false
,
page
:
0
})
}
}
},
[
loadTokensWithPriceHistory
,
prefetchedSelectedTokensWithoutPriceHistory
,
duration
])
},
[
loadTokensWithPriceHistory
,
prefetchedSelectedTokensWithoutPriceHistory
,
duration
])
// Trigger fetching top 100 tokens without price history on first load, and on
// each change of chain or duration.
useEffect
(()
=>
{
setLoadingTokensWithoutPriceHistory
(
true
)
loadTokensWithoutPriceHistory
({
duration
,
chain
})
},
[
chain
,
duration
,
loadTokensWithoutPriceHistory
])
return
{
return
{
loading
,
error
,
loading
:
loadingTokensWithPriceHistory
||
loadingTokensWithoutPriceHistory
,
tokens
,
tokens
,
hasMore
,
hasMore
,
tokensWithoutPriceHistoryCount
:
prefetchedSelectedTokensWithoutPriceHistory
.
length
,
loadMoreTokens
,
loadMoreTokens
,
maxFetchable
,
maxFetchable
,
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment