1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
import React, { useState, useEffect } from 'react';
import './App.css';
import firebase from 'firebase/app';
import 'firebase/firestore';
import dayjs from 'dayjs';
const firebaseConfig = {
apiKey: process.env.REACT_APP_API_KEY,
authDomain: process.env.REACT_APP_AUTH_DOMAIN,
databaseURL: process.env.REACT_APP_DATABASE_URL,
projectId: process.env.REACT_APP_PROJECT_ID,
storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_APP_ID
};
firebase.initializeApp(firebaseConfig);
const db = firebase.firestore();
type BlogItem = {
id: string,
text: string,
created: firebase.firestore.Timestamp,
updated: firebase.firestore.Timestamp
}
type BlogProps = {
items: BlogItem[]
}
type NextButtonProps = {
hasNext: boolean,
onClick: React.MouseEventHandler<HTMLButtonElement>
}
function Blog(props: BlogProps) {
const items = props.items;
return (
<ol>
{items.map((item) => {
const created = dayjs(item.created.toDate());
const updated = dayjs(item.updated.toDate());
return (
<li key={item.id}>
<div>{item.id}</div>
<div className="App-text">{item.text}</div>
<div>
<time dateTime={created.format('YYYY-MM-DDTHH:mm:ss')}>
{created.format('YYYY-MM-DD HH:mm:ss')}
</time>
</div>
<div>
<time dateTime={updated.format('YYYY-MM-DDTHH:mm:ss')}>
{updated.format('YYYY-MM-DD HH:mm:ss')}
</time>
</div>
</li>
);
})}
</ol>
);
}
function NextButton(props: NextButtonProps) {
const hasNext = props.hasNext;
if (hasNext) {
return <button onClick={props.onClick}>次へ</button>;
}
return null;
}
function Main() {
const [items, setItems] = useState<BlogItem[]>([]);
const [lastItem, setLastItem] = useState<firebase.firestore.QueryDocumentSnapshot>();
const [hasNext, setHasNext] = useState(false);
const [loadNext, setLoadNext] = useState(true);
useEffect(() => {
async function getItems() {
let first;
if (hasNext && lastItem && lastItem.data()) {
first = await db.collection("posts").orderBy('created', 'desc').startAfter(lastItem.data().created).limit(5);
} else {
first = await db.collection("posts").orderBy('created', 'desc').limit(5);
}
const snapshot = await first.get();
const last = snapshot.docs[snapshot.docs.length - 1];
const next = await db.collection("posts").orderBy('created', 'desc').startAfter(last.data().created).limit(1);
const nextSnapshot = await next.get();
snapshot.forEach((doc) => {
let item = doc.data();
const blogItem: BlogItem = {
id: doc.id,
text: item.text,
created: item.created,
updated: item.updated
}
items.push(blogItem);
});
setItems(items);
setLastItem(last);
setHasNext(nextSnapshot.docs.length > 0 ? true : false);
}
if (loadNext) {
getItems();
setLoadNext(false);
}
}, [items, lastItem, hasNext, loadNext]);
return (
<main>
<Blog items={items} />
<NextButton hasNext={hasNext} onClick={() => setLoadNext(true)}/>
</main>
);
}
function App() {
return (
<div>
<Main />
</div>
);
}
export default App;
|