
4-13강 숙제5 부분입니다. 메인페이지에서 꿀팁찜 누르면 찜한 당연히 리스트가 보여야 합니다.
그런데 이 꿀팁찜이 안보일 때가 너무 많아요. 예를 들어 찜페이지 들어가서 '찜 해제' 버튼을 누르고 메인으로 나왔다가 다시 꿀팁찜에 들어가면 찜 리스트가 하나도 안 보일 때가 많습니다.
수도 없이 많이 시도해 본 결과 서버에서 찜 데이터가 바뀌면 그 다음에는 찜 리스트가 아예 안 보이게 되는 것을 알게 됐습니다.


이렇게 서버에는 찜 데이터가 있는데 앱화면에서는 나오지 않고 있습니다. 계속 여러개 찜을 누르다 보면 어느순간 찜리스트가 나타나기도 합니다. 하지만 그러다 또 해제하거나 추가해서 서버의 데이터가 바뀌면 찜 데이터가 없다고 뜹니다. 며칠째 계속 이러고 있습니다.
찜 추가를 하든 해제를 하든 바로바로 반영되게 하는 방법을 알려주시기 부탁드립니다. 지금은 서버 데이터 한번 바뀌면 바로 데이터가 없다고 뜨는 앱이 되어버렸어요.
MainPage.js
import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
const main = 'https://storage.googleapis.com/sparta-image.appspot.com/lecture/main.png'
import data from '../data.json';
import Card from '../components/Card';
import Loading from '../components/Loading';
import { StatusBar } from 'expo-status-bar';
import * as Location from "expo-location";
import axios from "axios"
import {firebase_db} from "../firebaseConfig"
export default function MainPage({navigation,route}) {
//useState 사용법
//[state,setState] 에서 state는 이 컴포넌트에서 관리될 상태 데이터를 담고 있는 변수
//setState는 state를 변경시킬때 사용해야하는 함수
//모두 다 useState가 선물해줌
//useState()안에 전달되는 값은 state 초기값
const [state,setState] = useState([])
const [cateState,setCateState] = useState([])
//날씨 데이터 상태관리 상태 생성!
const [weather, setWeather] = useState({
temp : 0,
condition : ''
})
//하단의 return 문이 실행되어 화면이 그려진다음 실행되는 useEffect 함수
//내부에서 data.json으로 부터 가져온 데이터를 state 상태에 담고 있음
const [ready,setReady] = useState(true)
useEffect(()=>{
navigation.setOptions({
title:'나만의 꿀팁'
})
//뒤의 1000 숫자는 1초를 뜻함
//1초 뒤에 실행되는 코드들이 담겨 있는 함수
setTimeout(()=>{
firebase_db.ref('/tip').once('value').then((snapshot) => {
console.log("파이어베이스에서 데이터 가져왔습니다!!")
let tip = snapshot.val();
setState(tip)
setCateState(tip)
getLocation()
setReady(false)
});
// getLocation()
// setState(data.tip)
// setCateState(data.tip)
// setReady(false)
},1000)
},[])
const getLocation = async () => {
//수많은 로직중에 에러가 발생하면
//해당 에러를 포착하여 로직을 멈추고,에러를 해결하기 위한 catch 영역 로직이 실행
try {
//자바스크립트 함수의 실행순서를 고정하기 위해 쓰는 async,await
await Location.requestForegroundPermissionsAsync();
const locationData= await Location.getCurrentPositionAsync();
console.log(locationData)
console.log(locationData['coords']['latitude'])
console.log(locationData['coords']['longitude'])
const latitude = locationData['coords']['latitude']
const longitude = locationData['coords']['longitude']
const API_KEY = "cfc258c75e1da2149c33daffd07a911d";
const result = await axios.get(
`http://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric`
);
console.log(result)
const temp = result.data.main.temp;
const condition = result.data.weather[0].main
console.log(temp)
console.log(condition)
//오랜만에 복습해보는 객체 리터럴 방식으로 딕셔너리 구성하기!!
//잘 기억이 안난다면 1주차 강의 6-5를 다시 복습해보세요!
setWeather({
temp,condition
})
} catch (error) {
//혹시나 위치를 못가져올 경우를 대비해서, 안내를 준비합니다
Alert.alert("위치를 찾을 수가 없습니다.", "앱을 껏다 켜볼까요?");
}
}
const category = (cate) => {
if(cate == "전체보기"){
//전체보기면 원래 꿀팁 데이터를 담고 있는 상태값으로 다시 초기화
setCateState(state)
}else{
setCateState(state.filter((d)=>{
return d.category == cate
}))
}
}
//data.json 데이터는 state에 담기므로 상태에서 꺼내옴
// let tip = state.tip;
let todayWeather = 10 + 17;
let todayCondition = "흐림"
//return 구문 밖에서는 슬래시 두개 방식으로 주석
return ready ? <Loading/> : (
<ScrollView style={styles.container}>
<StatusBar style="light" />
{/* <Text style={styles.title}>나만의 꿀팁</Text> */}
<Text style={styles.weather}>오늘의 날!씨: {weather.temp + '°C ' + weather.condition} </Text>
<TouchableOpacity style={styles.AboutPage} onPress={()=>{navigation.navigate('AboutPage')}}>
<Text style={styles.middleButtonText}>소개 페이지</Text>
</TouchableOpacity>
<Image style={styles.mainImage} source={{uri:main}}/>
<ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
<TouchableOpacity style={styles.middleButtonAll} onPress={()=>{category('전체보기')}}><Text style={styles.middleButtonTextAll}>전체보기</Text></TouchableOpacity>
<TouchableOpacity style={styles.middleButton01} onPress={()=>{category('생활')}}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
<TouchableOpacity style={styles.middleButton02} onPress={()=>{category('재테크')}}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
<TouchableOpacity style={styles.middleButton03} onPress={()=>{category('반려견')}}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
<TouchableOpacity style={styles.middleButton04} onPress={()=>{navigation.navigate('LikePage')}}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
</ScrollView>
<View style={styles.cardContainer}>
{/* 하나의 카드 영역을 나타내는 View */}
{
cateState.map((content1,i3)=>{
return (<Card content8={content1} key={i3} navigation2={navigation}/>)
})
}
</View>
</ScrollView>)
}
const styles = StyleSheet.create({
container: {
//앱의 배경 색
backgroundColor: '#fff',
},
title: {
//폰트 사이즈
fontSize: 20,
//폰트 두께
fontWeight: '700',
//위 공간으로 부터 이격
marginTop:50,
//왼쪽 공간으로 부터 이격
marginLeft:20
},
weather:{
alignSelf:"flex-end",
paddingRight:20
},
mainImage: {
//컨텐츠의 넓이 값
width:'90%',
//컨텐츠의 높이 값
height:200,
//컨텐츠의 모서리 구부리기
borderRadius:10,
marginTop:20,
//컨텐츠 자체가 앱에서 어떤 곳에 위치시킬지 결정(정렬기능)
//각 속성의 값들은 공식문서에 고대로~ 나와 있음
alignSelf:"center"
},
AboutPage: {
width:120,
height:50,
padding:15,
backgroundColor:"#fdc453",
borderColor:"deeppink",
borderRadius:15,
marginTop: 10,
margin: 10,
alignSelf:"flex-end",
},
middleContainer:{
marginTop:20,
marginLeft:10,
height:60
},
middleButtonAll: {
width:100,
height:50,
padding:15,
backgroundColor:"#20b2aa",
borderColor:"deeppink",
borderRadius:15,
margin:7
},
middleButton01: {
width:100,
height:50,
padding:15,
backgroundColor:"#fdc453",
borderColor:"deeppink",
borderRadius:15,
margin:7
},
middleButton02: {
width:100,
height:50,
padding:15,
backgroundColor:"#fe8d6f",
borderRadius:15,
margin:7
},
middleButton03: {
width:100,
height:50,
padding:15,
backgroundColor:"#9adbc5",
borderRadius:15,
margin:7
},
middleButton04: {
width:100,
height:50,
padding:15,
backgroundColor:"#f886a8",
borderRadius:15,
margin:7
},
middleButtonText: {
color:"#fff",
fontWeight:"700",
//텍스트의 현재 위치에서의 정렬
textAlign:"center"
},
middleButtonTextAll: {
color:"#fff",
fontWeight:"700",
//텍스트의 현재 위치에서의 정렬
textAlign:"center"
},
cardContainer: {
marginTop:10,
marginLeft:10
},
});
LikePage.js
import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
import Loading from '../components/Loading';
const main = 'https://storage.googleapis.com/sparta-image.appspot.com/lecture/main.png'
import LikeCard from '../components/LikeCard';
import * as Application from 'expo-application';
const isIOS = Platform.OS === 'ios';
import {firebase_db} from "../firebaseConfig"
export default function MainPage({navigation,route}) {
const [tip, setTip] = useState([])
const [ready,setReady] = useState(true)
useEffect(()=>{
//뒤의 1000 숫자는 1초를 뜻함
//1초 뒤에 실행되는 코드들이 담겨 있는 함수
navigation.setOptions({
title:'꿀팁 찜'
})
getLike()
},[])
const getLike = async () => {
let userUniqueId;
if(isIOS){
let iosId = await Application.getIosIdForVendorAsync();
userUniqueId = iosId
}else{
userUniqueId = await Application.androidId
}
console.log(userUniqueId)
firebase_db.ref('/like/'+userUniqueId).once('value').then((snapshot) => {
console.log("파이어베이스에서 데이터 가져왔습니다!!")
let tip = snapshot.val();
if(tip && tip.length >= 0){
setTip(tip)
setReady(false)
}
})
}
return ready ? <Loading/>: (
<ScrollView style={styles.container}>
{
tip.map((content1,i3)=>{
return (<LikeCard content8={content1} key={i3} navigation2={navigation} tip={tip} setTip={setTip}/>)
})
}
</ScrollView>)
}
const styles = StyleSheet.create({
container: {
//앱의 배경 색
backgroundColor: '#fff',
},
});
LikeCard.js
import React from "react";
import { View, Image, Text, StyleSheet, TouchableOpacity, Alert } from "react-native";
import * as Linking from 'expo-linking';
import {firebase_db} from "../firebaseConfig"
const isIOS = Platform.OS === 'ios';
import * as Application from 'expo-application';
//MainPage로 부터 navigation2 속성을 전달받아 Card 컴포넌트 안에서 사용
export default function LikeCard({ content8, navigation2, tip, setTip }) {
const detail = () => {
navigation2.navigate('DetailPage',{idx:content8.idx})
}
const remove = async(cidx) => {
let userUniqueId;
if(isIOS){
let iosId = await Application.getIosIdForVendorAsync();
userUniqueId = iosId
}else{
userUniqueId = await Application.androidId
}
console.log(userUniqueId)
firebase_db.ref('/like/'+userUniqueId+'/'+ cidx).remove().then(function(){
Alert.alert("삭제 완료!");
let result = tip.filter((data,i)=>{
return data.idx !== cidx
})
console.log(result)
setTip(result)
})
}
return (
//카드 자체가 버튼역할로써 누르게되면 상세페이지로 넘어가게끔 TouchableOpacity를 사용
<View>
<TouchableOpacity style={styles.card}>
<Image style={styles.cardImage} source={{ uri: content8.image }} />
<View style={styles.cardText}>
<Text style={styles.cardTitle} numberOfLines={1}>
{content8.title}
</Text>
<Text style={styles.cardDesc} numberOfLines={3}>
{content8.desc}
</Text>
<Text style={styles.cardDate}>{content8.date}</Text>
<View style={styles.buttonGroup}>
<TouchableOpacity style={styles.button}>
<Text style={styles.buttonText} onPress={() => detail()}>
자세히보기
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => remove(content8.idx)}
>
<Text style={styles.buttonText}>찜해제</Text>
</TouchableOpacity>
</View>
</View>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
card: {
flex: 1,
flexDirection: "row",
margin: 10,
borderBottomWidth: 0.5,
borderBottomColor: "#eee",
paddingBottom: 10,
},
cardImage: {
flex: 1,
width: 100,
height: 120,
borderRadius: 10,
},
cardText: {
flex: 2,
flexDirection: "column",
marginLeft: 10,
},
cardTitle: {
fontSize: 20,
fontWeight: "700",
},
cardDesc: {
fontSize: 15,
},
cardDate: {
fontSize: 10,
color: "#A6A6A6",
},
buttonGroup: {
flexDirection:"row",
},
button: {
width: 95,
marginTop: 5,
marginRight: 10,
marginLeft: 10,
padding: 10,
borderWidth: 1,
borderColor: "deeppink",
borderRadius: 7,
},
buttonText:{
color:'deeppink',
textAlign:'center'
}
});
