
React Navigation
stack-navigator
expo-guides
네비게이션 & 추가 라이브러리 설치 코드
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
컴포넌트에 페이지 기능을 부여하고, 컴포넌트 -> 컴포넌트 (페이지 간) 이동을 가능하게 함, 페이지를 쌓는 것
책갈피 기능을 하는 스택 네비게이터에 등록시킴
페이지는 Stack.Screen 이라 부르고
책갈피는 Stack.Navigator 라 부른다.

//라이브러리 임포트
import { createStackNavigator } from '@react-navigation/stack';
//페이지로 만든 컴포넌트들을 불러옵니다
import DetailPage from '../pages/DetailPage';
//스택 네비게이션 라이브러리가 제공해주는 여러 기능이 담겨있는 객체를 사용합니다
//그래서 이렇게 항상 상단에 선언하고 시작하는게 규칙입니다!
const Stack = createStackNavigator();
//리액트의 모~든 파일은 컴포넌트라 생각하고
//페이지 기능을 해주는 모든 기능이 담겨 있는 컴포넌트를 만든다 생각하세요!
const StackNavigator = () =>{
return (
/// 페이지 기능이 들어갈 곳
//컴포넌트들을 페이지처럼 여기게끔 해주는 기능을 하는 네비게이터 태그를 선언합니다.
//위에서 선언한 const Stack = createStackNavigator(); Stack 변수에 들어있는 태그를 꺼내 사용합니다.
//Stack.Navigator 태그 내부엔 페이지(화면)를 스타일링 할 수 있는 다양한 옵션들이 담겨 있습니다.
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: "black",
borderBottomColor: "black",
shadowColor: "black",
height:100
},
headerTintColor: "#f6f6f6",
headerBackTitleVisible: false
}}
>
{/* 컴포넌트를 페이지로 만들어주는 엘리먼트에 끼워 넣습니다. 이 자체로 이제 페이지 기능을 합니다*/}
<Stack.Screen name="MainPage" component={MainPage}/>
<Stack.Screen name="DetailPage" component={DetailPage}/>
</Stack.Navigator>
)
}
export default StackNavigator;
App.js
import React from 'react';
//이제 모든 페이지 컴포넌트들이 끼워져있는 책갈피를 메인에 둘예정이므로
//컴포넌트를 더이상 불러오지 않아도 됩니다.
// import MainPage from './pages/MainPage';
// import DetailPage from './pages/DetailPage';
import { StatusBar } from 'expo-status-bar';
//메인에 세팅할 네비게이션 도구들을 가져옵니다.
import {NavigationContainer} from '@react-navigation/native';
import StackNavigator from './navigation/StackNavigator'
export default function App() {
console.disableYellowBox = true;
return (
<NavigationContainer>
<StatusBar style="auto" />
<StackNavigator/>
</NavigationContainer>);
}
이하 두 딕셔너리 기능은...
//navigation 객체가 가지고 있는 두 함수(setOptions와 navigate)
//해당 페이지의 제목을 설정할 수 있음
navigation.setOptions({
title:'My list'
})
//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 같은) 곳에 useEffect 추가
useEffect(()=>{
setTimeout(()=>{
//헤더의 타이틀 변경
navigation.setOptions({
title:'My List'
})
//꿀팁 데이터로 모두 초기화 준비
let tip = data.tip;
setState(tip)
setCateState(tip)
setReady(false)
},1000)
...
return (<Card content={content} key={i} navigation={navigation}/>)
...
MainPage.js 에서 Card.js 에 전달한 navigation 속성을 전달받아 Card 컴포넌트 안에서 사용
export default function Card({content,navigation}){
return(
//카드 자체가 버튼역할로써 누르게되면 상세페이지로 넘어가게끔 TouchableOpacity를 사용
<TouchableOpacity style={styles.card} onPress={()=>{navigation.navigate('DetailPage')}}>
...
)}
이동시킬 때 사용한 함수에 두번째 인자로 딕셔너리를 넘겨주면, 이동한 페이지에서 넘겨준 데이터를 받을 수 있음
navigation.navigate("Detail",{
title: title
})
navigation.navigate("Detail", content)
export default function DetailPage({navigation,route}) {
//초기 컴포넌트의 상태값을 설정
//state, setState 뿐 아니라 이름을 마음대로 지정할 수 있음!
const [tip, setTip] = useState({
"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"
})
useEffect(()=>{
console.log(route)
//Card.js에서 navigation.navigate 함수를 쓸때 두번째 인자로 content를 넘겨줬죠?
//content는 딕셔너리 그 자체였으므로 route.params에 고대~로 남겨옵니다.
//즉, route.params 는 content죠!
navigation.setOptions({
//setOptions로 페이지 타이틀도 지정 가능하고
title:route.params.title,
//StackNavigator에서 작성했던 옵션을 다시 수정할 수도 있습니다.
headerStyle: {
backgroundColor: '#000',
shadowColor: "#000",
},
headerTintColor: "#fff",
})
setTip(route.params)
},[])
return ()}
DetailPage 에서 상태값을 초기에 설정한 이유
💡 DetailPage 초반에 우린 이렇게 상태값을 설정해놨었습니다. 그 이유가 뭘까요?
심지어 이 상태값을 초기에 설정안하면 오류가 발생합니다. tip엔 아무것도 없다며...
//초기 컴포넌트의 상태값을 설정
const [tip, setTip] = useState({
"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"
})
💡 그 이유는 컴포넌트가 화면에 그려지는 순서에 있습니다.
1. DetailPage 컴포넌트가 useState에 들어 있는 데이터를 가지고 화면에 그려짐(return 함수실행)
2. 화면에 다 그려진후, useEffect 함수 실행
3. useEffect에서 상태값 변경 이벤트가 실행되면 변경된 데이터 가지고 다시 return 실행
4. 변경된 데이터를 가지고 화면에 DetailPage가 다시 그려짐.
💡 곰곰히 생각해보면 결국,
리액트 네이티브에서 화면이 변경되는 시점은 컴포넌트의 상태값이 변경될 때이니,
어떠한 데이터를 보여주는 컴포넌트라면
초기값을 의미없는 값이더라도 넣고 시작한다! 라고 생각하시면 편함.
또는 Loading.js를 이용해 데이터가 준비가 되면 로딩 화면 -> 본 화면을 보여준다던가!