Skip to content

Commit dfeed6c

Browse files
scroll to latest y position after page reload
1 parent 764c084 commit dfeed6c

File tree

7 files changed

+129
-19
lines changed

7 files changed

+129
-19
lines changed

src/app/page.tsx

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,59 @@
11
'use client'
22

3-
import { useRef } from 'react'
3+
import { useRef, useState } from 'react'
44
import Feed from '@/components/feed/Feed'
55
import Footer from '@/components/footer/Footer'
66
import Hero from '@/components/hero/Hero'
77
import Navigation from '@/components/navigation/Navigation'
88
import Quote from '@/components/quote/Quote'
99
import Users from '@/components/users/Users'
10+
import { ContextContentLoaded } from '@/context/ContextContentLoaded'
1011
import { ContextQuote } from '@/context/ContextQuote'
1112
import { ContextTopTenPosts } from '@/context/ContextTopTenPosts'
1213
import { ContextTopUsers } from '@/context/ContextTopUsers'
1314
import { useFocusTrap } from '@/hooks/useFocusTrap'
15+
import { useGotoWindowScrollY } from '@/hooks/useGotoWindowScrollY'
1416
import { useScrollRestoration } from '@/hooks/useScrollRestoration'
17+
import { useSetWindowScrollY } from '@/hooks/useSetWindowScrollY'
1518

1619
export default function Home() {
20+
const [contentLoaded, setContentLoaded] = useState<{
21+
posts: boolean
22+
quote: boolean
23+
users: boolean
24+
}>({
25+
posts: false,
26+
quote: false,
27+
users: false,
28+
})
29+
1730
const quoteRef = useRef<HTMLQuoteElement>(null)
1831
const topTenPostsRef = useRef<HTMLDivElement | null>(null)
1932
const topUsersRef = useRef<HTMLDivElement | null>(null)
2033

2134
useFocusTrap()
35+
useGotoWindowScrollY(contentLoaded)
2236
useScrollRestoration()
37+
useSetWindowScrollY()
2338

2439
return (
2540
<div className="min-h-svh flex flex-col items-center justify-start bg-gradient-to-b from-stone-700 to-stone-800">
26-
<ContextQuote.Provider value={quoteRef}>
27-
<ContextTopTenPosts.Provider value={topTenPostsRef}>
28-
<ContextTopUsers.Provider value={topUsersRef}>
29-
<Navigation />
30-
<Hero />
31-
<Users />
32-
<Quote />
33-
<Feed />
34-
<Footer />
35-
</ContextTopUsers.Provider>
36-
</ContextTopTenPosts.Provider>
37-
</ContextQuote.Provider>
41+
<ContextContentLoaded.Provider
42+
value={[contentLoaded, setContentLoaded]}
43+
>
44+
<ContextQuote.Provider value={quoteRef}>
45+
<ContextTopTenPosts.Provider value={topTenPostsRef}>
46+
<ContextTopUsers.Provider value={topUsersRef}>
47+
<Navigation />
48+
<Hero />
49+
<Users />
50+
<Quote />
51+
<Feed />
52+
<Footer />
53+
</ContextTopUsers.Provider>
54+
</ContextTopTenPosts.Provider>
55+
</ContextQuote.Provider>
56+
</ContextContentLoaded.Provider>
3857
</div>
3958
)
4059
}

src/components/feed/Feed.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,20 @@ import {
2121
} from '@/components/ui/carousel'
2222
import { Skeleton } from '@/components/ui/skeleton'
2323
import fetchFeedItems from '@/api/fetchFeedItems'
24+
import { ContextContentLoaded } from '@/context/ContextContentLoaded'
2425
import { ContextTopTenPosts } from '@/context/ContextTopTenPosts'
2526
import { FeedItemsType } from '@/types/types'
2627
import { getItemFromSessionStorage } from '@/utils/getItemFromSessionStorage'
2728

2829
const Feed = () => {
30+
const contextContentLoaded = useContext(ContextContentLoaded)
31+
if (!contextContentLoaded) {
32+
throw new Error(
33+
'Feed must be used within a ContextContentLoaded.Provider'
34+
)
35+
} // eslint-disable-next-line @typescript-eslint/no-unused-vars
36+
const [_contentLoaded, setContentLoaded] = contextContentLoaded
37+
2938
const contextTopTenPosts = useContext(ContextTopTenPosts)
3039
if (!contextTopTenPosts) {
3140
throw new Error(
@@ -45,8 +54,9 @@ const Feed = () => {
4554
}
4655
setIsLoading(true)
4756
await fetchFeedItems(nextPage, PAGE_SIZE, setFeedItems)
57+
setContentLoaded((prev) => ({ ...prev, posts: true }))
4858
setIsLoading(false)
49-
}, [isLoading])
59+
}, [isLoading, setContentLoaded])
5060

5161
useEffect(() => {
5262
const parsedStorageData = getItemFromSessionStorage()
@@ -55,8 +65,10 @@ const Feed = () => {
5565

5666
if (!parsedStorageData?.feedItems?.length) {
5767
loadMoreItems()
68+
} else {
69+
setContentLoaded((prev) => ({ ...prev, posts: true }))
5870
}
59-
}, [loadMoreItems])
71+
}, [loadMoreItems, setContentLoaded])
6072

6173
useEffect(() => {
6274
const handleScroll = () => {

src/components/quote/Quote.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
import { useCallback, useContext, useEffect, useState } from 'react'
2+
import { ContextContentLoaded } from '@/context/ContextContentLoaded'
23
import { ContextQuote } from '@/context/ContextQuote'
34
import { QuoteType } from '@/types/types'
45
import fetchQuote from '@/api/fetchQuote'
56
import { Skeleton } from '@/components/ui/skeleton'
67
import { getItemFromSessionStorage } from '@/utils/getItemFromSessionStorage'
78

89
const Quote = () => {
10+
const contextContentLoaded = useContext(ContextContentLoaded)
11+
if (!contextContentLoaded) {
12+
throw new Error(
13+
'Quote must be used within a ContextContentLoaded.Provider'
14+
)
15+
} // eslint-disable-next-line @typescript-eslint/no-unused-vars
16+
const [_contentLoaded, setContentLoaded] = contextContentLoaded
17+
918
const contextQuote = useContext(ContextQuote)
1019
if (!contextQuote) {
1120
throw new Error('Users must be used within a ContextQuote.Provider')
@@ -21,17 +30,20 @@ const Quote = () => {
2130
}
2231
setIsLoading(true)
2332
await fetchQuote(setQuote)
33+
setContentLoaded((prev) => ({ ...prev, quote: true }))
2434
setIsLoading(false)
25-
}, [isLoading])
35+
}, [isLoading, setContentLoaded])
2636

2737
useEffect(() => {
2838
const parsedStorageData = getItemFromSessionStorage()
2939
setQuote(parsedStorageData?.quote || null)
3040

3141
if (!parsedStorageData?.quote) {
3242
loadQuote()
43+
} else {
44+
setContentLoaded((prev) => ({ ...prev, quote: true }))
3345
}
34-
}, [loadQuote])
46+
}, [loadQuote, setContentLoaded])
3547

3648
return (
3749
<section className="w-full max-w-7xl" ref={quoteRef}>

src/components/users/Users.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,21 @@ import {
1515
} from '@/components/ui/card'
1616
import { Skeleton } from '@/components/ui/skeleton'
1717
import fetchUsers from '@/api/fetchUsers'
18+
import { ContextContentLoaded } from '@/context/ContextContentLoaded'
1819
import { ContextTopUsers } from '@/context/ContextTopUsers'
1920
import { UsersType } from '@/types/types'
2021
import { getItemFromSessionStorage } from '@/utils/getItemFromSessionStorage'
2122
import { useScreenWidth } from '@/hooks/useScreenWidth'
2223

2324
const Users = () => {
25+
const contextContentLoaded = useContext(ContextContentLoaded)
26+
if (!contextContentLoaded) {
27+
throw new Error(
28+
'Users must be used within a ContextContentLoaded.Provider'
29+
)
30+
} // eslint-disable-next-line @typescript-eslint/no-unused-vars
31+
const [_contentLoaded, setContentLoaded] = contextContentLoaded
32+
2433
const contextTopUsersRef = useContext(ContextTopUsers)
2534
if (!contextTopUsersRef) {
2635
throw new Error('Users must be used within a ContextTopUsers.Provider')
@@ -38,17 +47,20 @@ const Users = () => {
3847
}
3948
setIsLoading(true)
4049
await fetchUsers(setUsers)
50+
setContentLoaded((prev) => ({ ...prev, users: true }))
4151
setIsLoading(false)
42-
}, [isLoading])
52+
}, [isLoading, setContentLoaded])
4353

4454
useEffect(() => {
4555
const parsedStorageData = getItemFromSessionStorage()
4656
setUsers(parsedStorageData?.users || [])
4757

4858
if (!parsedStorageData?.users?.length) {
4959
loadUsers()
60+
} else {
61+
setContentLoaded((prev) => ({ ...prev, users: true }))
5062
}
51-
}, [loadUsers])
63+
}, [loadUsers, setContentLoaded])
5264

5365
return (
5466
<section
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { createContext, Dispatch, SetStateAction } from 'react'
2+
3+
export const ContextContentLoaded = createContext<
4+
| [
5+
{
6+
posts: boolean
7+
quote: boolean
8+
users: boolean
9+
},
10+
Dispatch<
11+
SetStateAction<{
12+
posts: boolean
13+
quote: boolean
14+
users: boolean
15+
}>
16+
>
17+
]
18+
| undefined
19+
>(undefined)

src/hooks/useGotoWindowScrollY.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { useEffect } from 'react'
2+
import { getItemFromSessionStorage } from '@/utils/getItemFromSessionStorage'
3+
4+
export const useGotoWindowScrollY = (contentLoaded: {
5+
posts: boolean
6+
quote: boolean
7+
users: boolean
8+
}) => {
9+
useEffect(() => {
10+
if (contentLoaded.posts && contentLoaded.quote && contentLoaded.users) {
11+
const parsedStorageData = getItemFromSessionStorage()
12+
if (parsedStorageData?.scrollY) {
13+
window.scrollTo({
14+
top: parsedStorageData.scrollY,
15+
behavior: 'smooth',
16+
})
17+
}
18+
}
19+
}, [contentLoaded])
20+
}

src/hooks/useSetWindowScrollY.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use client'
2+
3+
import { useEffect } from 'react'
4+
import { setItemInSessionStorage } from '@/utils/setItemInSessionStorage'
5+
6+
export const useSetWindowScrollY = () => {
7+
useEffect(() => {
8+
const onScroll = () => {
9+
setItemInSessionStorage('scrollY', window.scrollY)
10+
}
11+
12+
window.addEventListener('scroll', onScroll)
13+
14+
return () => window.removeEventListener('scroll', onScroll)
15+
}, [])
16+
}

0 commit comments

Comments
 (0)