무한 스크롤
앱 속도 증진
사용자 이탈 감소
Cloud Firebase에서 특정 개수 게시글 출력
MainPage.jsx
const [next, setNext] = useState(0);
const readyData = async () => {
const data = await getData(setNext);
setData(data);
};
firebaseFunctions.js
export async function getData(setNext) {
try {
let data = [];
const db = firebase.firestore();
const first = db.collection("diary").orderBy("date", "desc").limit(5);
const snapshot = await first.get();
snapshot.docs.map((doc) => {
data.push(doc.data());
});
let last;
if (snapshot.docs.length !== 0) {
last = snapshot.docs[snapshot.docs.length - 1];
}
setNext(last.data().date);
return data;
} catch (err) {
return false;
}
}
MainPage.jsx 구조 변경
<Container>
<Header />
<Content>
<CardComponent />
</Content>
</Container>
⬇︎
<Container>
<Header/>
<FlatList
header={()=>{return (<Content></Content>)}}
renderItem={()=> { return (<CardComponent/>)}}
>
</Container>
FlatList는 큰 ScrollView 기능의 도화지
ListHeaderComponent={() => {
return (
)
}}
<View style={{ marginTop: -20 }}>
{data.map((content, i) => {
return <CardComponent content={content} key={i} navigation={navigation} />;
})}
</View>
⬇︎
renderItem={(data) => {
return (
<CardComponent navigation={navigation} content={data.item} />
);
}}
numColumns={1}
keyExtractor={(item) => item.data.toString()
onEndReachedThreshold={0}
onEndReached={async () => {
}}
{data.length == 0 ? (
<ActivityIndicator size="large" />
) : (
<FlatList ...
firebaseFunctions.js
export async function getNextData(nextDate, setNext) {
try {
let data = [];
const db = firebase.firestore();
const next = db
.collection("diary")
.orderBy("date", "desc")
.startAfter(nextDate)
.limit(5);
const snapshot = await next.get();
snapshot.docs.map((doc) => {
doc.data();
data.push(doc.data());
});
let last;
if (snapshot.docs.length !== 0) {
last = snapshot.docs[snapshot.docs.length - 1];
setNext(last.data().date);
return data;
} else {
return 0;
}
} catch (err) {
return false;
}
}
MainPage.jsx
onEndReached={async () => {
let nextData = await getNextData(next, setNext);
if (nextData == 0) {
Alert.alert('마지막 글입니다.');
} else {
let newData = [...data, ...nextData];
await setData(newData);
}
}}
const next = db
.collection("diary")
.orderBy("date", "desc")
.startAfter(nextDate)
.limit(5);
let newData = [...data, ...nextData];
기존 게시글이 담겨있는 상태 값 data와 새로운 게시글이 담겨있는 nextData 변수를 합칠 때 사용하는 문법
1번은 댓글 양 증가 시 게시글 당 가져와야 하는 데이터 증가
DetailPage.jsx
const [commentInput, setCommentInput] = useState("");
const commentFunc = async () => {
let date = new Date();
let getTime = date.getTime();
const currentUser = firebase.auth().currentUser;
let comment = {
date: getTime,
comment: commentInput,
did: content.date + "D",
uid: currentUser.uid,
};
};
return (
...
<Item style={{ marginTop: 100 }}>
<Input
placeholder="Comment"
value={commentInput}
onChangeText={(text) => {
setCommentInput(text);
}}
/>
<Icon
active
name="paper-plane"
onPress={() => {
commentFunc();
}}
/>
</Item>
...
)
DetailPage -> DB(comment)
firebaseFunctions.js
export async function addComment(comment) {
try {
const db = firebase.firestore();
let userRef = await db.collection("users").doc(comment.uid);
let data = await userRef.get().then((doc) => {
return doc.data();
});
comment.author = data.nickName;
await db
.collection("comment")
.doc(comment.data + "D")
.set(comment);
return true;
} catch (err) {
Alert.alert("댓글 작성 오류 -> ", err.message);
return false;
}
}
firebaseFunctions.js
export async function getComment(did) {
const db = firebase.firestore();
let data = [];
let snapshot = await db.collection("comment").where("did", "==", did).get();
if (snapshot.empty) {
return 0;
} else {
snapshot.forEach((doc) => {
data.push(doc.data());
});
return data;
}
}
// comment 컬렉션에 저장된 댓글 데이터 중 did 값이 DetailPage로부터 넘어온 값과 일치하는 데이터를 골라 조회하는 조건문
let snapshot = await db.collection("comment").where("did", "==", did).get();
DetailPage.jsx
const [comment, setComment] = useState([]);
useEffect(() => {
navigation.setOptions({
title: content.title,
headerStyle: {
backgroundColor: "white",
shadowColor: "white",
},
headerTintColor: "black",
headerShown: "true",
headerBackTitleVisible: false,
});
commentLoad(content.date);
}, []);
const commentLoad = async (did) => {
let c = await getComment(did + "D");
if (c == 0) {
} else {
setComment(c);
}
};
return (
...
<List>
{comment.map((c, i) => {
return <CommentComponent key={i} comment={c} />;
})}
</List>
...
)
DetailPage.jsx
const commentFunc = async () => {
let date = new Date();
let getTime = date.getTime();
const currentUser = firebase.auth().currentUser;
let newComment = {
date: getTime,
comment: commentInput,
did: content.date + "D",
uid: currentUser.uid,
};
let result = await addComment(newComment);
if (result) {
Alert.alert("댓글 저장");
await setComment([...comment, newComment]);
}
};
배열 + 배열을 위해 사용했던 스프레드 연산자를 통해 배열에 새 객체 추가
기존의 상태값은 보존하면서 새로운 값 추가 (데이터 불변성)
diary -> doc -> field
-> likes -> doc -> field
MainPage.jsx
useEffect(() => {
navigation.addListener("beforeRemove", (e) => {
e.preventDefault();
});
readyData();
}, []);
const readyData = async () => {
const data = await getData(setNext);
setData(data);
};
⬇︎
useEffect(() => {
navigation.addListener("beforeRemove", (e) => {
e.preventDefault();
});
getData(setNext, setData);
}, []);
firebaseFunctions.js
export async function doLike(uid, did) {
try {
const db = firebase.firestore();
const date = new Date();
const getTime = date.getTime();
await db.collection("diary").doc(did).collection("likes").doc(uid).set({
date: getTime,
});
return true;
} catch (error) {
return false;
}
}
문서는 같은 값 중복 체크 기능을 제공하여 같은 게시글을 여러 번 좋아요 눌러도 딱 한 번만 저장
CardComponent.jsx
const likeFunc = () => {
const currentUser = firebase.auth().currentUser;
const uid = currentUser.uid;
const did = content.date + "D";
let result = doLike(uid, did);
if (result) {
console.log("likes");
}
};
uid
did
firebaseFunctions
export async function getData(setNext, setData) {
try {
let data = [];
const db = firebase.firestore();
const first = db.collection("diary").orderBy("date", "desc").limit(5);
const snapshot = await first.get();
const currentUser = firebase.auth().currentUser;
let last;
if (snapshot.docs.length !== 0) {
last = snapshot.docs[snapshot.docs.length - 1];
}
setNext(last.data().date);
let count = 0;
let limit = snapshot.docs.length;
snapshot.docs.map(async (doc) => {
let d = doc.data();
const like = await db
.collection("diary")
.doc(d.date + "D")
.collection("likes")
.doc(currentUser.uid)
.get();
if (like.data() == undefined) {
d.like = false;
} else {
d.like = true;
}
count += 1;
data.push(d);
if (count == limit) {
setData(data);
}
});
return data;
} catch (err) {
return false;
}
}
CardComponent.jsx
<Icon
name={content.like == true ? "heart" : "heart-outline"}
style={content.like == true ? styles.pink : styles.grey}
onPress={() => {
likeFunc();
}}
/>
CardComponent.jsx
const [like, setLike] = useState(false);
useEffect(() => {
if (content.like == true) {
setLike(true);
} else {
setLike(false);
}
}, []);
const likeFunc = () => {
const currentUser = firebase.auth().currentUser;
const uid = currentUser.uid;
const did = content.date + "D";
let result = doLike(uid, did);
if (result) {
if (like == true) {
setLike(false);
} else {
setLike(true);
}
}
};
firebaseFunctions.jsx
export async function doLike(uid, did, like) {
try {
const db = firebase.firestore();
const date = new Date();
const getTime = date.getTime();
if (like == true) {
await db
.collection("diary")
.doc(did)
.collection("likes")
.doc(uid)
.delete();
} else {
await db.collection("diary").doc(did).collection("likes").doc(uid).set({
date: getTime,
});
}
return true;
} catch (error) {
return false;
}
}
"android": {
"package": "",
"versionCode": 1,
"config": {
"googleMobileAdsAppId": ""
},
"permissions": ["CAMERA", "READ_EXTERNAL_STORAGE"]
},
expo build:android
// ios
expo build:ios
> apk 파일 선택
> expo 사이트에서 다운