프로젝트
오늘 한 일 1
- 관심목록 기능 및 페이지 완성
- onSnapshot 메서드를 이용하고 상위컴포넌트에서 useState로 데이터를 저장해주었는데, 메인 페이지를 벗어나는 순간 코드들이 죽어 업데이트 된 데이터가 넘어오지 않는 현상이 발생하였다.
- getDocs 메서드를 이용해서 컴포넌트마다 데이터를 불러오고 useState에 불러온 데이터를 저장해주니 해결되었다.
- 내 아이디가 관심목록에 추가한 것만 목록에 나타나야해서 데이터베이스 키-값에
userId: authService.currentUser.uid
를 추가해주었다.
- if문을 사용해서 내가 눌렀던 것이 아니라면 데이터베이스에 추가해주었다.
- 하트 버튼을 누르면 바로 렌더링 되지 않는 현상이 발생하였다.
- 원래는 useState로 if문을 이용해서 isLike의 true : false 확인했다.
- 변수에 저장해서 if문을 이용해서 확인하니 잘 작동하였다.
import {
View,
Text,
TouchableOpacity,
Image,
SafeAreaView,
ScrollView,
useColorScheme,
} from 'react-native';
import { useNavigation } from '@react-navigation/native';
import styled from '@emotion/native';
import {
onSnapshot,
collection,
addDoc,
setDoc,
query,
orderBy,
getDocs,
getDoc,
doc,
updateDoc,
deleteDoc,
} from 'firebase/firestore';
import { dbService, authService } from '../firebase';
import { useCallback, useEffect, useState } from 'react';
import { useFocusEffect } from '@react-navigation/native';
import { v4 as uuidv4 } from 'uuid';
import DropShadow from 'react-native-drop-shadow';
import { DARK_COLOR } from '../colors';
export default function MainCard({ item }) {
const { navigate } = useNavigation();
const [items, setItems] = useState([]);
const isDark = useColorScheme() === 'dark';
const q = query(collection(dbService, 'isLike'));
const addIsLike = async (data) => {
const selectedItem = items.find(
(item) =>
item.desertionNo === data.desertionNo &&
item.userId === authService?.currentUser?.uid
);
if (!selectedItem && !!authService.currentUser) {
const id = uuidv4();
await setDoc(doc(dbService, 'isLike', id), {
...item,
id,
isLike: false,
userId: authService?.currentUser?.uid,
});
getData();
}
};
const getData = async () => {
const querySnapshot = await getDocs(q);
const itemArray = [];
querySnapshot.forEach((doc) => {
itemArray.push(doc.data());
});
setItems(itemArray);
};
useEffect(() => {
getData();
}, []);
return (
<TouchableOpacity
onPress={() => {
addIsLike(item);
navigate('Detail', {
params: { data: item },
});
}}
>
<DropShadow
style={{
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 5,
},
shadowOpacity: 0.29,
shadowRadius: 4.65,
}}
>
<SingleCard style={{ backgroundColor: isDark ? '#212123' : 'white' }}>
<AnimalCardPicture>
<AnimalPic source={{ url: `${item.popfile}` }} />
</AnimalCardPicture>
<AnimalCardType>
<TextC style={{ color: isDark ? DARK_COLOR : 'black' }}>성별</TextC>
<TextC style={{ color: isDark ? DARK_COLOR : 'black' }}>품종</TextC>
<TextC style={{ color: isDark ? DARK_COLOR : 'black' }}>나이</TextC>
<TextC style={{ color: isDark ? DARK_COLOR : 'black' }}>지역</TextC>
<TextC style={{ color: isDark ? DARK_COLOR : 'black' }}>
등록일
</TextC>
</AnimalCardType>
<AnimalCardDescription>
<AnimalCardGender style={{ color: isDark ? 'white' : 'black' }}>
{item.sexCd === 'M' ? '남' : 'W' ? '여' : '중성'}
</AnimalCardGender>
<AnimalCardKind style={{ color: isDark ? 'white' : 'black' }}>
{item.kindCd}
</AnimalCardKind>
<AnimalCardAge style={{ color: isDark ? 'white' : 'black' }}>
{item.age}
</AnimalCardAge>
<AnimalCardLocation style={{ color: isDark ? 'white' : 'black' }}>
{item.orgNm.slice(0, 2)}
{}
</AnimalCardLocation>
<AnimalCardDate style={{ color: isDark ? 'white' : 'black' }}>
{item.happenDt}
</AnimalCardDate>
</AnimalCardDescription>
</SingleCard>
</DropShadow>
</TouchableOpacity>
);
}
const AnimalPic = styled.Image`
width: 120px;
height: 120px;
border-radius: 70%;
`;
const TextC = styled.Text`
font-size: 15px;
font-weight: bold;
margin-top: 8px;
`;
const SingleCard = styled.View`
margin-left: 2.5%;
flex-direction: row;
align-items: center;
width: 95%;
height: 170px;
margin-top: 15px;
border-radius: 10px;
background-color: #fff;
`;
const AnimalCardPicture = styled.View`
margin-left: 25px;
width: 120px;
height: 120px;
border-radius: 70%;
background-color: #b3b3b3;
`;
const AnimalCardType = styled.View`
margin-left: 30px;
font-size: 25px;
font-weight: bold;
`;
const AnimalCardDescription = styled.View`
margin-left: 20px;
position: relative;
`;
const AnimalCardKind = styled.Text`
margin-top: 9px;
position: relative;
`;
const AnimalCardGender = styled.Text`
margin-top: 9px;
position: relative;
`;
const AnimalCardAge = styled.Text`
margin-top: 8px;
position: relative;
`;
const AnimalCardLocation = styled.Text`
margin-top: 8px;
position: relative;
`;
const AnimalCardDate = styled.Text`
margin-top: 6px;
position: relative;
`;
import styled from '@emotion/native';
import { useState, useEffect, useCallback } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
useColorScheme,
Alert,
} from 'react-native';
import DropShadow from 'react-native-drop-shadow';
import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../utils';
import { AntDesign } from '@expo/vector-icons';
import Item from './Item';
import {
onSnapshot,
collection,
addDoc,
setDoc,
query,
orderBy,
getDocs,
getDoc,
doc,
updateDoc,
deleteDoc,
} from 'firebase/firestore';
import { dbService, authService } from '../../firebase';
import { useFocusEffect } from '@react-navigation/native';
export default function Details({ data }) {
const [items, setItems] = useState([]);
const isDark = useColorScheme() === 'dark';
const checkLike = items?.find(
(item) =>
item?.desertionNo === data?.desertionNo &&
item.userId === authService?.currentUser?.uid
)?.isLike;
const q = query(collection(dbService, 'isLike'));
const getData = async () => {
const querySnapshot = await getDocs(q);
const itemArray = [];
querySnapshot.forEach((doc) => {
itemArray.push(doc.data());
});
setItems(itemArray);
};
useFocusEffect(
useCallback(() => {
getData();
return () => {
getData();
};
}, [])
);
const isLikeChangeHandler = async (desertionNo) => {
const choiceItem = items.find(
(item) =>
item.desertionNo === desertionNo &&
item.userId === authService?.currentUser?.uid
);
const commentRef = doc(dbService, 'isLike', choiceItem.id);
await updateDoc(commentRef, {
isLike: !choiceItem.isLike,
});
getData();
};
return (
<>
<ScrollWrap style={{ backgroundColor: isDark ? '#1B1D21' : 'white' }}>
<DetailPictureBox>
<DetailImage
source={{
url: `${data.popfile}`,
}}
style={StyleSheet.absoluteFill}
/>
<HeartWrapper
onPress={() => {
isLikeChangeHandler(data.desertionNo);
checkLike
? Alert.alert('관심목록에서 제거되었습니다.')
: Alert.alert('관심목록에 추가되었습니다.');
}}
>
{!authService.currentUser ? null : checkLike ? (
<AntDesign name='heart' size={24} color='red' />
) : (
<AntDesign name='hearto' size={24} color='red' />
)}
</HeartWrapper>
</DetailPictureBox>
<DropShadow
style={{
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 5,
},
shadowOpacity: 0.29,
shadowRadius: 4.65,
}}
>
<Item data={data} />
</DropShadow>
</ScrollWrap>
</>
);
}
const ScrollWrap = styled.View`
padding: 5%;
`;
const DetailImage = styled.Image`
height: ${SCREEN_HEIGHT / 3 + 'px'};
/* width: ${SCREEN_WIDTH}; */
width: 100%;
border-radius: 10%;
position: relative;
`;
const DetailPictureBox = styled.View`
width: ${SCREEN_WIDTH};
height: ${SCREEN_HEIGHT / 3 + 'px'};
border-radius: 10%;
margin-bottom: 5%;
background-color: #b3b3b3;
`;
const HeartWrapper = styled.TouchableOpacity`
position: absolute;
margin-top: 10px;
margin-left: 10px;
`;
오늘 한 일 2
- useNavigation 뒤로가기 수정
- 원래 navigation 폴더에서 뒤로가기를 한 번에 해주었는데 뒤로가기를 누르면 필터페이지로 가는 현상이 발생하였다.
- 뒤로가기가 필요한 각각의 컴포넌트에서 useFocusEffect를 적용해서 navigation의 setOptions를 이용해주었더니 잘 작동하였다.
useFocusEffect(
useCallback(() => {
setOptions({
headerLeft: () => (
<TouchableOpacity onPress={() => goBack()}>
<AntDesign
name='left'
size={24}
/>
</TouchableOpacity>
),
});
}, [])
);
오늘 한 일 3
- Github로 팀원들과 코드 합치기
- 지금까지 한 작업들을 모두 합쳐서 잘 작동하는지 해보았다.
- 깃허브에 코드가 잘못 pull & push 되어서 수정하는데 시간이 걸렸다.
- 잘못된 코드를 찾기 너무 어려워서 새벽까지 계속하였고 결국에는 찾아서 수정하니 잘 되었다.
프로젝트 후 느낀점
- 다른 어려운 것도 많았지만 이번 프로젝트는 git이 말썽을 많이 부려 마음 고생을 심하게 했다.
- git 공부를 다시 해야겠다.