export default function DetailPage() {
const tip = {
"idx": 9,
"category":"재테크",
"title":"렌탈 서비스 금액 비교해보기",
"image": "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Frental.png?alt=media&token=97a55844-f077-4aeb-8402-e0a27221570b",
"desc":"요즘은 정수기, 공기 청정기, 자동차나 장난감 등 다양한 대여서비스가 활발합니다. 사는 것보다 경제적이라고 생각해 렌탈 서비스를 이용하는 분들이 늘어나고 있는데요. 다만, 이런 렌탈 서비스 이용이 하나둘 늘어나다 보면 그 금액은 겉잡을 수 없이 불어나게 됩니다. 특히, 렌탈 서비스는 빌려주는 물건의 관리비용까지 포함된 것이기에 생각만큼 저렴하지 않습니다. 직접 관리하며 사용할 수 있는 물건이 있는지 살펴보고, 렌탈 서비스 항목에서 제외해보세요. 렌탈 비용과 구매 비용, 관리 비용을 여러모로 비교해보고 고민해보는 것이 좋습니다. ",
"date":"2020.09.09"
}
const popup = () => {
Alert.alert("POPUP!")
}
return (
<ScrollView style={styles.container}>
<Image style={styles.mainImage} source={{uri: tip.image}} resizeMode="cover" />
<View style={styles.textContainer}>
<Text style={styles.title}>{tip.title}</Text>
<Text style={styles.desc}>{tip.desc}</Text>
<TouchableOpacity style={styles.button} onPress={() => popup()}>
<Text style={styles.buttonText}>찜하기</Text>
</TouchableOpacity>
</View>
</ScrollView>
)
}
컴포넌트 (Component) : 정해진 엘리먼트(요소)를 사용하여 만든 화면의 일부분
상태 (State) : 컴포넌트에서 데이터를 유지하고 관리하기 위한 유일한 방법
속성 (Props) : 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 방식
useEffect : 화면에 컴포넌트가 그려지면 처음 실행해야 하는 함수들을 모아두는 곳
MainPage 컴포넌트화
MainPage
// 라이브러리
import Card from '../components/Card';
...
<View style={styles.textContainer}>
{
tip.map((content, i) => {
return (
<Card content={content} key={i} />
)
})
}
</View>
Card component
export default function Card({content}) {
return (
<View style={styles.card}>
<Image style={styles.cardImage} source={{uri:content.image}} />
<View style={styles.cardText}>
<Text style={styles.cardTitle}>{content.title}</Text>
<Text style={styles.cardDesc} numberOfLines={3}>{content.desc}</Text>
<Text style={styles.cardDate}>{content.date}</Text>
</View>
</View>
)
}
MainPage.js에서의 Card.js 컴포넌트
<View style={styles.textContainer}>
{
tip.map((content, i) => {
return (
<Card content={content} key={i} />
)
})
}
</View>
Card.js에서의 속성 값
export default function Card({content})
...
<Image style={styles.cardImage} source={{uri:content.image}} />
...
비구조 할당 방식
// 사용자 화면 (UI)은 컴포넌트 (component)에 어떤 데이터 (state)가 주입되고 변경되냐에 따라 변화
UI = component(state)
useEffect(() => {
// 화면이 그려진 다음 가장 먼저 실행되야 할 코드
} [])
화면이 그려질때 데이터 준비하는 동안 로딩화면으로 대체
const [state, setState] = useState([])
const [ready, setReady] = useState(true)
useEffect(() => {
setTimeout(() => {
setState(data)
setReady(false)
}, 1000)
}, [])
return ready ? <Loading /> : (
...
)
const [state, setState] = useState([])
const [cateState, setCateState] = useState([])
const [ready, setReady] = useState(true)
const category = (cate) => {
if (cate == '전체보기') {
setCateState(state)
} else {
setCateState(state.filter((d) => {
return d.category == cate
}))
}
}
useEffect(() => {
setTimeout(() => {
let tip = data.tip
setState(tip)
setCateState(tip)
setReady(false)
}, 1000)
}, [])
Expo SDK.에서 제공해주는 앱 기능 도구 개발 시 확인할 수 있는 사이트
expo install expo-status-bar
import { StatusBar } from 'expo-status-bar'
...
<StatusBar style="black" /> // 또는
<StatusBar style="light" />
yarn add @react-navigation/native
expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view
yarn add @react-navigation/stack
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import DetailPage from '../pages/DetailPage';
import MainPage from '../pages/MainPage';
const Stack = createStackNavigator()
const StackNavigator = () => {
return (
)
}
export default StackNavigator
<Stack.Navigator screenOptions={{
headerStyle: {
backgroundColor: "white",
height: 100
},
headerTintColor: "black", headerBackTitleVisible: false
}}>
<Stack.Screen name="MainPage" component={MainPage} />
<Stack.Screen name="DetailPage" component={DetailPage} />
</Stack.Navigator>
<Stack.Screen name="MainPage" component={MainPage} />
<Stack.Screen name="DetailPage" component={DetailPage} />
import { NavigationContainer } from '@react-navigation/native';
import StackNavigator from './navigation/StackNavigator';
...
<NavigationContainer>
<StackNavigator />
</NavigationContainer>
스타일 수정
const StackNavigator = () => {
return (
<Stack.Navigator screenOptions={{
headerStyle: {
backgroundColor: "white",
height: 100,
borderBottomColor: "white",
shadowColor: "white"
},
headerTitleAlign: "left",
headerTintColor: "black",
headerBackTitleVisible: false
}}>
<Stack.Screen name="MainPage" component={MainPage} />
<Stack.Screen name="DetailPage" component={DetailPage} />
</Stack.Navigator>
)
}
//해당 페이지의 제목을 설정할 수 있음
navigation.setOptions({
title:'나만의 꿀팁'
})
//Stack.screen에서 name 속성으로 정해준 이름을 지정해주면 해당 페이지로 이동하는 함수
navigation.navigate("DetailPage")
//name 속성을 전달해주고, 두 번째 인자로 딕셔너리 데이터를 전달해주면, Detail 페이지에서
//두번째 인자로 전달된 딕셔너리 데이터를 route 딕셔너리로 로 받을 수 있음
navigation.navigate("DetailPage",{
title: title
})
//전달받은 데이터를 받는 route 딕셔너리
//비구조 할당 방식으로 route에 params 객체 키로 연결되어 전달되는 데이터를 꺼내 사용
//navigate 함수로 전달되는 딕셔너리 데이터는 다음과 같은 모습이기 때문입니다.
/*
{
route : {
params :{
title:title
}
}
}
*/
const { title} = route.params;
MainPage.js
export default function MainPage({navigation, route}) {
...
useEffect(() => {
setTimeout(() => {
navigation.setOptions({
title: '나만의 꿀팁'
})
let tip = data.tip
setState(tip)
setCateState(tip)
setReady(false)
}, 1000)
}, [])
...
<View style={styles.textContainer}>
{
cateState.map((content, i) => {
return (
<Card content={content} key={i} navigation={navigation} />
)
})
}
</View>
...
}
Card.js
export default function Card({content, navigation}) {
...
<TouchableOpacity style={styles.card} onPress={() => {navigation.navigate('DetailPage', content)}}>
...
}
DetailPage.js
import React, { useEffect, useState } from 'react'
...
export default function DetailPage({navigation, route}) {
const [tip, setTip] = useState({
...
})
..
useEffect(() => {
navigation.setOptions({
title: route.params.title,
headerStyle: {
backgroundColor: 'white',
shadowColor: 'white'
},
headerTintColor: 'black'
})
setTip(route.params)
}, [])
...
}
import { Share } from 'react-native'
export default function DetailPage({navigation, route}) {
...
const share = () => {
Share.share({
message: `${tip.title} \n\n ${tip.desc} \n\n ${tip.image}`
})
}
...
}
expo install expo-linking
import * as Linking from 'expo-linking';
...
const link = () => {
Linking.openURL("https://www.notion.so/3-fe352da2985f44998f82c2fa1073d74d")
}
...