@@ -13,13 +13,14 @@ import { useSession } from 'next-auth/react';
13
13
import LoginDialog from '@/components/LoginDialog' ;
14
14
import { useRouter } from 'next/navigation' ;
15
15
16
- const LIMIT = 10 ;
16
+ const LIMIT = 10 ; // 一度に取得する投稿数
17
+ const MAX = 100 ; // タイムラインに表示できる最大投稿数
17
18
18
19
const Timeline = ( ) => {
19
20
// 投稿データの配列
20
21
const [ posts , setPosts ] = useState < PostTypes [ ] > ( [ ] ) ;
21
22
// 投稿取得時のオフセットID
22
- const [ offsetId , setOffsetId ] = useState ( '' ) ;
23
+ const offsetIdRef = useRef ( '' ) ;
23
24
// データ取得中かどうかのフラグ
24
25
const [ isLoading , setIsLoading ] = useState ( false ) ;
25
26
// これ以上取得できる投稿があるかのフラグ
@@ -36,6 +37,8 @@ const Timeline = () => {
36
37
37
38
//IntersectionObserverを保持するためのref
38
39
const observer = useRef < IntersectionObserver | null > ( null ) ;
40
+ // 投稿取得の重複実行を防ぐためのref
41
+ const isFetchingRef = useRef ( false ) ;
39
42
40
43
/**
41
44
* 追加の投稿データを取得し,状態を更新する非同期関数のCallback Ref
@@ -44,16 +47,24 @@ const Timeline = () => {
44
47
* @returns {Promise<void> } 投稿データの取得と状態更新が完了するPromise
45
48
*/
46
49
const loadMorePosts = useCallback ( async ( ) => {
50
+ if ( isFetchingRef . current ) return ;
51
+ isFetchingRef . current = true ;
47
52
setIsLoading ( true ) ;
48
53
// 投稿データを取得
49
54
const newPosts = await fetchPosts ( {
50
55
limit : LIMIT ,
51
- iconUrl : session . data ?. user ?. id ,
52
- offsetId : offsetId ,
56
+ iconUrl : session . data ?. user ?. image ?? '' ,
57
+ offsetId : offsetIdRef . current ,
53
58
} ) ;
54
59
if ( newPosts && newPosts . length > 0 ) {
55
- setPosts ( ( prevPosts ) => [ ...prevPosts , ...newPosts ] ) ;
56
- setOffsetId ( ( ) => newPosts [ newPosts . length - 1 ] . id ) ;
60
+ setPosts ( ( prevPosts ) => {
61
+ const updatedPosts = [ ...prevPosts , ...newPosts ] ;
62
+ if ( updatedPosts . length >= MAX ) {
63
+ setHasMore ( false ) ;
64
+ }
65
+ return updatedPosts ;
66
+ } ) ;
67
+ offsetIdRef . current = newPosts [ newPosts . length - 1 ] . id ;
57
68
// 取得した投稿数がLIMIT未満の場合は,これ以上取得できる投稿は無い.
58
69
if ( newPosts . length < LIMIT ) {
59
70
setHasMore ( false ) ;
@@ -63,7 +74,8 @@ const Timeline = () => {
63
74
setHasMore ( false ) ;
64
75
}
65
76
setIsLoading ( false ) ;
66
- } , [ offsetId ] ) ;
77
+ isFetchingRef . current = false ;
78
+ } , [ session . data ?. user ?. image ] ) ;
67
79
68
80
// ターゲットの要素を監視するためのcallback ref
69
81
const targetRef = useCallback (
@@ -94,6 +106,11 @@ const Timeline = () => {
94
106
// eslint-disable-next-line react-hooks/exhaustive-deps
95
107
} , [ ] ) ;
96
108
109
+ // 見かけ上の投稿を削除する関数
110
+ const deletePost = ( postId : string ) => {
111
+ setPosts ( ( prevPosts ) => prevPosts . filter ( ( post ) => post . id !== postId ) ) ;
112
+ } ;
113
+
97
114
return (
98
115
< div className = 'relative min-h-screen' >
99
116
{ /* ヘッダ */ }
@@ -117,10 +134,10 @@ const Timeline = () => {
117
134
< div className = 'pt-12' >
118
135
< div className = 'relative mx-auto max-w-lg' >
119
136
< SideMenu className = 'fixed top-16 hidden -translate-x-full lg:block' />
120
- < PostList posts = { posts } className = 'mx-auto max-w-sm lg:max-w-lg' />
137
+ < PostList posts = { posts } className = 'mx-auto max-w-sm lg:max-w-lg' onDelete = { deletePost } />
121
138
{ isLoading && < p className = 'py-3 text-center' > 投稿を取得中...</ p > }
122
139
< div ref = { targetRef } className = 'h-px' />
123
- { ! hasMore && < p className = 'py-3 text-center' > これ以上投稿はありません </ p > }
140
+ { ! hasMore && < p className = 'py-3 text-center' > これ以上投稿を取得できません。 </ p > }
124
141
</ div >
125
142
</ div >
126
143
@@ -146,7 +163,7 @@ const Timeline = () => {
146
163
) }
147
164
148
165
{ /* ログイン確認ダイアログ表示が有効の場合,ダイアログを表示する */ }
149
- { loginDialogOpen && < LoginDialog isOpen = { loginDialogOpen } setIsOpen = { setLoginDialogOpen } /> }
166
+ < LoginDialog isOpen = { loginDialogOpen } setIsOpen = { setLoginDialogOpen } />
150
167
</ div >
151
168
) ;
152
169
} ;
0 commit comments