@ List
* 5.3 다양한 내비게이터
** 5.3.1 드로어 내비게이터 --> Drawer 라이브러리 에러로 Pass
** 5.3.2 하단 탭 내비게이터
** 5.3.3 머티리얼 상단 탭 내비게이터
** 5.3.4 머티리얼 하단 탭 내비게이터
** 5.3.5 머티리얼 하단 탭 내비게이터 헤더 타이틀 동기화하기
* 5.4 내비게이션 Hooks
** 5.4.1 useNavigation
** 5.4.2 useRoute
** 5.4.3 useFocusEffect
** 5.5 정리
@ Note
1. Drawer 라이브러리 에러
- 라이브러리 설치 시 에러 (해결 완료)
> reference : https://bocoder.tistory.com/66?category=904878
- const Drawer = createDrawerNavigator(); 설정 시 에러 (해결 필요)
> reference : https://github.com/software-mansion/react-native-reanimated/issues/1849
2. 종류별 Tabs Navigator 의 Props
- 하단 탭 내비게이터 : https://reactnavigation.org/docs/bottom-tab-navigator/
- 머티리얼 상단 탭 내비게이터 : https://reactnavigation.org/docs/material-top-tab-navigator/
- 머티리얼 하단 탭 내비게이터 : https://reactnavigation.org/docs/material-bottom-tab-navigator/
- 머티리얼 디자인 관련 : https://callstack.github.io/react-native-p/
3. 내비게이션 Hooks
- useNavigation : Screen으로 사용되고 있지 않은 컴포넌트에서도 navigation 객체 사용 가능
- useRoute : Screen으로 사용되고 있지 않은 컴포넌트에서도 route 객체 사용 가능
- useFocusEffect : 화면에 포커스가 작협을 떄 특정 작업을 할 수 있게 하는 Hook
> useFocusEffect는 반드시 useCallback과 함께 사용해야 함
> 사용하지 않으면 컴포넌트가 리렌더링 될 때마다 useFocusEffect에 등록한 함수가 호출됨
@ Result
@ Git
- https://github.com/eunbok-bocoder/LearnReactNavigation.git
# Source tree
# App.js ( 하단 탭 내비게이터 )
import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import MainScreen from './screens/MainScreen';
import DetailScreen from './screens/DetailScreen';
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Main"
component={MainScreen}
options={{
headerShown: false,
}}
/>
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
# screens/MainScreen.js ( 하단 탭 내비게이터 )
import React from 'react';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import {Text, Button, View} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
const Tab = createBottomTabNavigator();
function HomeScreen({navigation}) {
return (
<View>
<Text>Home</Text>
<Button
title="Detail 1 열기"
onPress={() => {
navigation.push('Detail', {id: 1});
}}
/>
</View>
);
}
function SearchScreen() {
return (
<View>
<Text>Search</Text>
</View>
);
}
function NotificationScreen() {
return (
<View>
<Text>Notification</Text>
</View>
);
}
function MessageScreen() {
return (
<View>
<Text>Message</Text>
</View>
);
}
function MainScreen() {
return (
<Tab.Navigator
initialRouteName="Home"
screenOptions={{
tabBarActiveTintColor: '#fb8c00',
tabBarShowLabel: false,
}}>
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
title: '홈',
tabBarIcon: ({color, size}) => (
<Icon name="home" color={color} size={size} />
),
}}
/>
<Tab.Screen
name="Search"
component={SearchScreen}
options={{
title: '검색',
tabBarIcon: ({color, size}) => (
<Icon name="search" color={color} size={size} />
),
}}
/>
<Tab.Screen
name="Notification"
component={NotificationScreen}
options={{
title: '알림',
tabBarIcon: ({color, size}) => (
<Icon name="notifications" color={color} size={size} />
),
}}
/>
<Tab.Screen
name="Message"
component={MessageScreen}
options={{
title: '메시지',
tabBarIcon: ({color, size}) => (
<Icon name="message" color={color} size={size} />
),
}}
/>
</Tab.Navigator>
);
}
export default MainScreen;
# screens/DetailScreen.js ( 하단 탭 내비게이터 )
import React, {useEffect} from 'react';
import {View, Text, StyleSheet, Button} from 'react-native';
function DetailScreen({route, navigation}) {
useEffect(() => {
navigation.setOptions({
title: `상세 정보 - ${route.params.id}`,
});
}, [navigation, route.params.id]);
return (
<View style={styles.block}>
<Text style={styles.text}>id: {route.params.id}</Text>
<View style={styles.buttons}>
<Button
title="다음"
// 이전 화면과 동일하면 스택을 쌓지 않고 파라미터만 변경함
// onPress={() => navigation.navigate('Detail', {id: route.params.id + 1})}
onPress={() => navigation.push('Detail', {id: route.params.id + 1})}
/>
<Button title="뒤로가기" onPress={() => navigation.pop()} />
<Button title="처음으로" onPress={() => navigation.popToTop()} />
</View>
</View>
);
}
export default DetailScreen;
const styles = StyleSheet.create({
block: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 48,
},
buttons: {
flexDirection: 'row',
},
});
# App.js ( 머티리얼 상단 탭 내비게이터 )
import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import MainScreen from './screens/MainScreen';
import DetailScreen from './screens/DetailScreen';
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Main"
component={MainScreen}
options={{
// headerShown: false, // 하단 내비게이터는 기본 헤더 존재
headerShown: true, // 머티리얼 상단 내비게이터는 기본 헤더가 없음
}}
/>
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
# screens > MainScreen.js ( 머티리얼 상단 탭 내비게이터 )
import React from 'react';
// import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import {createMaterialTopTabNavigator} from '@react-navigation/material-top-tabs';
import {Text, Button, View} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
// 하단 탭 내비게이터
// const Tab = createBottomTabNavigator();
// 머티리얼 상단 탭 내비게이터
const Tab = createMaterialTopTabNavigator();
function HomeScreen({navigation}) {
return (
<View>
<Text>Home</Text>
<Button
title="Detail 1 열기"
onPress={() => {
navigation.push('Detail', {id: 1});
}}
/>
</View>
);
}
function SearchScreen() {
return (
<View>
<Text>Search</Text>
</View>
);
}
function NotificationScreen() {
return (
<View>
<Text>Notification</Text>
</View>
);
}
function MessageScreen() {
return (
<View>
<Text>Message</Text>
</View>
);
}
function MainScreen() {
return (
<Tab.Navigator
initialRouteName="Home"
screenOptions={{
tabBarIndicatorStyle: {
backgroundColor: '#009688',
},
tabBarActiveTintColor: '#009688',
}}>
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
tabBarLabel: '홈',
tabBarIcon: ({color}) => (
<Icon name="home" color={color} size={24} />
),
}}
/>
<Tab.Screen
name="Search"
component={SearchScreen}
options={{
tabBarLabel: '검색',
tabBarIcon: ({color}) => (
<Icon name="search" color={color} size={24} />
),
}}
/>
<Tab.Screen
name="Notification"
component={NotificationScreen}
options={{
tabBarLabel: '알림',
tabBarIcon: ({color}) => (
<Icon name="notifications" color={color} size={24} />
),
}}
/>
<Tab.Screen
name="Message"
component={MessageScreen}
options={{
tabBarLabel: '메시지',
tabBarIcon: ({color}) => (
<Icon name="message" color={color} size={24} />
),
}}
/>
</Tab.Navigator>
);
}
export default MainScreen;
# App.js ( 머티리얼 하단 탭 내비게이터 )
import React from 'react';
import {
getFocusedRouteNameFromRoute,
NavigationContainer,
} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import MainScreen from './screens/MainScreen';
import DetailScreen from './screens/DetailScreen';
const Stack = createNativeStackNavigator();
function getHeaderTitle(route) {
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home';
const nameMap = {
Home: '홈',
Search: '검색',
Notification: '알림',
Message: '메시지',
};
return nameMap[routeName];
}
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Main"
component={MainScreen}
options={({route}) => ({
title: getHeaderTitle(route),
})}
/>
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
# screens > MainScreen.js ( 머티리얼 하단 탭 내비게이터 )
import React from 'react';
// import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
// import {createMaterialTopTabNavigator} from '@react-navigation/material-top-tabs';
import {createMaterialBottomTabNavigator} from '@react-navigation/material-bottom-tabs';
import {Text, Button, View} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
// 하단 탭 내비게이터
// const Tab = createBottomTabNavigator();
// 머티리얼 상단 탭 내비게이터
// const Tab = createMaterialTopTabNavigator();
const Tab = createMaterialBottomTabNavigator();
function HomeScreen({navigation}) {
return (
<View>
<Text>Home</Text>
<Button
title="Detail 1 열기"
onPress={() => {
navigation.push('Detail', {id: 1});
}}
/>
</View>
);
}
function SearchScreen() {
return (
<View>
<Text>Search</Text>
</View>
);
}
function NotificationScreen() {
return (
<View>
<Text>Notification</Text>
</View>
);
}
function MessageScreen() {
return (
<View>
<Text>Message</Text>
</View>
);
}
function MainScreen() {
return (
<Tab.Navigator initialRouteName="Home" labeled={true} shifting={true}>
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
tabBarLabel: '홈',
tabBarIcon: ({color}) => <Icon name="home" color={color} size={24} />,
tabBarColor: 'black',
tabBarBadge: 'new',
}}
/>
<Tab.Screen
name="Search"
component={SearchScreen}
options={{
tabBarLabel: '검색',
tabBarIcon: ({color}) => (
<Icon name="search" color={color} size={24} />
),
tabBarColor: 'gray',
}}
/>
<Tab.Screen
name="Notification"
component={NotificationScreen}
options={{
tabBarLabel: '알림',
tabBarIcon: ({color}) => (
<Icon name="notifications" color={color} size={24} />
),
tabBarColor: 'green',
tabBarBadge: 30,
}}
/>
<Tab.Screen
name="Message"
component={MessageScreen}
options={{
tabBarLabel: '메시지',
tabBarIcon: ({color}) => (
<Icon name="message" color={color} size={24} />
),
tabBarColor: 'blue',
tabBarBadge: true,
}}
/>
</Tab.Navigator>
);
}
export default MainScreen;
# screens > MainScreen.js (내비게이션 Hooks 테스트)
import React, {useEffect, useCallback} from 'react';
// import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
// import {createMaterialTopTabNavigator} from '@react-navigation/material-top-tabs';
import {createMaterialBottomTabNavigator} from '@react-navigation/material-bottom-tabs';
import {Text, Button, View} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import {useNavigation, useFocusEffect} from '@react-navigation/native';
// 하단 탭 내비게이터
// const Tab = createBottomTabNavigator();
// 머티리얼 상단 탭 내비게이터
// const Tab = createMaterialTopTabNavigator();
const Tab = createMaterialBottomTabNavigator();
// case 1)
// function OpenDetailButton({onPress}) {
// return <Button title="Detail 1 열기" onPress={onPress} />;
// }
// case 2)
// function OpenDetailButton({navigation}) {
// return (
// <Button
// title="Detail 1 열기"
// onPress={() => {
// navigation.push('Detail', {id: 1});
// }}
// />
// );
// }
// case 3)
function OpenDetailButton() {
const navigation = useNavigation();
return (
<Button
title="Detail 1 열기"
onPress={() => {
navigation.push('Detail', {id: 1});
}}
/>
);
}
function HomeScreen({navigation}) {
// useEffect(() => {
// console.log('mounted');
// return () => {
// console.log('unmounted');
// };
// }, []);
useFocusEffect(
useCallback(() => {
console.log('이 화면을 보고 있어요.');
return () => {
console.log('다른 화면으로 넘어갔어요.');
};
}, []),
);
return (
<View>
<Text>Home</Text>
{/* <Button
title="Detail 1 열기"
onPress={() => {
navigation.push('Detail', {id: 1});
}}
/> */}
{/* <OpenDetailButton onPress={() => navigation.push('Detail', {id: 1})} /> */}
{/* <OpenDetailButton navigation={navigation} /> */}
<OpenDetailButton />
</View>
);
}
function SearchScreen() {
return (
<View>
<Text>Search</Text>
</View>
);
}
function NotificationScreen() {
return (
<View>
<Text>Notification</Text>
</View>
);
}
function MessageScreen() {
return (
<View>
<Text>Message</Text>
</View>
);
}
function MainScreen() {
return (
<Tab.Navigator initialRouteName="Home" labeled={true} shifting={true}>
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
tabBarLabel: '홈',
tabBarIcon: ({color}) => <Icon name="home" color={color} size={24} />,
tabBarColor: 'black',
tabBarBadge: 'new',
}}
/>
<Tab.Screen
name="Search"
component={SearchScreen}
options={{
tabBarLabel: '검색',
tabBarIcon: ({color}) => (
<Icon name="search" color={color} size={24} />
),
tabBarColor: 'gray',
}}
/>
<Tab.Screen
name="Notification"
component={NotificationScreen}
options={{
tabBarLabel: '알림',
tabBarIcon: ({color}) => (
<Icon name="notifications" color={color} size={24} />
),
tabBarColor: 'green',
tabBarBadge: 30,
}}
/>
<Tab.Screen
name="Message"
component={MessageScreen}
options={{
tabBarLabel: '메시지',
tabBarIcon: ({color}) => (
<Icon name="message" color={color} size={24} />
),
tabBarColor: 'blue',
tabBarBadge: true,
}}
/>
</Tab.Navigator>
);
}
export default MainScreen;
# screens > DetailScreen.js (내비게이션 Hooks 테스트)
import React, {useEffect} from 'react';
import {View, Text, StyleSheet, Button} from 'react-native';
import {useRoute} from '@react-navigation/native';
function IDText() {
const route = useRoute();
return <Text style={styles.text}>id: {route.params.id}</Text>;
}
function DetailScreen({route, navigation}) {
useEffect(() => {
navigation.setOptions({
title: `상세 정보 - ${route.params.id}`,
});
}, [navigation, route.params.id]);
return (
<View style={styles.block}>
{/* <Text style={styles.text}>id: {route.params.id}</Text> */}
<IDText />
<View style={styles.buttons}>
<Button
title="다음"
// 이전 화면과 동일하면 스택을 쌓지 않고 파라미터만 변경함
// onPress={() => navigation.navigate('Detail', {id: route.params.id + 1})}
onPress={() => navigation.push('Detail', {id: route.params.id + 1})}
/>
<Button title="뒤로가기" onPress={() => navigation.pop()} />
<Button title="처음으로" onPress={() => navigation.popToTop()} />
</View>
</View>
);
}
export default DetailScreen;
const styles = StyleSheet.create({
block: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 48,
},
buttons: {
flexDirection: 'row',
},
});
'React Native > React Native_study' 카테고리의 다른 글
[리액트 네이티브를 다루는 기술 #10] 6장 다이어리 앱 만들기1 (p.306 ~ 329) (0) | 2022.01.03 |
---|---|
[리액트 네이티브를 다루는 기술 #9] 6장 다이어리 앱 만들기1 (p.285 ~ 305) (0) | 2021.12.31 |
[리액트 네이티브를 다루는 기술 #7] 5장 리액트 내비게이션으로 여러 화면 관리하기 (p.215 ~ 243) (0) | 2021.12.27 |
[리액트 네이티브를 다루는 기술 #6] 4장 할일 목록 만들기2 (p.179 ~ 214) (0) | 2021.12.24 |
[리액트 네이티브를 다루는 기술 #5] 4장 할일 목록 만들기2 (p.161 ~ 178) (0) | 2021.12.23 |