tab navigation
사용을 위해 npm install @react-navigation/bottom-tabs
를 설치한다import * as React from 'react';
import { Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
function HomeScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home!</Text>
</View>
);
}
function SettingsScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings!</Text>
</View>
);
}
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
스택 네비게이터
를 Customizing
방법과 유사하다. 탭 네비게이터
를 초기화
할 때 설정되는 특성과 옵션에서 화면별로 사용자 정의 할 수있는 특성이 있다.
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused
? 'ios-information-circle'
: 'ios-information-circle-outline';
} else if (route.name === 'Settings') {
iconName = focused ? 'ios-list-box' : 'ios-list';
}
// You can return any component that you like here!
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
tabBarOptions={{
activeTintColor: 'tomato', // 탭 활성
inactiveTintColor: 'gray', // 탭 비활성
}}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
tabBarIcon
tabBarIcon
은 bottom tab navigator
에서 지원되는 옵션이다.options
props
의 화면 component
에서 사용할 수 있지만,이 경우 편의를 위해 아이콘 구성을 중앙 집중화하기 위해 Tab.Navigator
의 screenOptions
props
에 배치하는것이 좋다tabBarIcon
tabBarIcon
은 focused
state, color
, size
params
가 제공되는 기능이다.tabBarIcon
에 전달되는 color
는 focused state
에 따라 활성
또는 비활성
색상입니다.size
는 tab bar
에 필요한 아이콘의 크기이다.tabBarOptions
tabBarOptions
및 activeTintColor
및 inactiveTintColor
가 표시된다.import * as React from 'react';
import { Text, View } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
function IconWithBadge({ name, badgeCount, color, size }) { // 뱃지생성 순서 3)
return (
<View style={{ width: 24, height: 24, margin: 5 }}>
<Ionicons name={name} size={size} color={color} />
{badgeCount > 0 && (
<View
style={{
// On React Native < 0.57 overflow outside of parent will not work on Android, see https://git.io/fhLJ8
position: 'absolute',
right: -6,
top: -3,
backgroundColor: 'red',
borderRadius: 6,
width: 12,
height: 12,
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text style={{ color: 'white', fontSize: 10, fontWeight: 'bold' }}>
{badgeCount}
</Text>
</View>
)}
</View>
);
}
function HomeIconWithBadge(props) {
// You should pass down the badgeCount in some other ways like React Context API, Redux, MobX or event emitters.
return <IconWithBadge {...props} badgeCount={3} />; // 뱃지생성 순서 2)
}
function HomeScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home!</Text>
</View>
);
}
function SettingsScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings!</Text>
</View>
);
}
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
if (route.name === 'Home') {
return (
<HomeIconWithBadge // 뱃지생성 순서 1)
name={
focused
? 'ios-information-circle'
: 'ios-information-circle-outline'
}
size={size}
color={color}
/>
);
} else if (route.name === 'Settings') {
return (
<Ionicons
name={focused ? 'ios-list-box' : 'ios-list'}
size={size}
color={color}
/>
);
}
},
})}
tabBarOptions={{
activeTintColor: 'tomato',
inactiveTintColor: 'gray',
}}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
navigations stacks
이 있다고 생각할 수 있다. 이것이 바로 React Navigation
에서 모델링하는 방법이다import * as React from 'react';
import { Button, Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
function DetailsScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details!</Text>
</View>
);
}
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
function SettingsScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
const HomeStack = createStackNavigator();
function HomeStackScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="Home" component={HomeScreen} />
<HomeStack.Screen name="Details" component={DetailsScreen} />
</HomeStack.Navigator>
);
}
// 각 탭 내에 stack navigation이 존재한다!
const SettingsStack = createStackNavigator();
function SettingsStackScreen() {
return (
<SettingsStack.Navigator>
<SettingsStack.Screen name="Settings" component={SettingsScreen} />
<SettingsStack.Screen name="Details" component={DetailsScreen} />
</SettingsStack.Navigator>
);
}
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Settings" component={SettingsStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
Stack
의 첫 번째 화면 안에 Tab Navigator
를 중첩시키는 방법import * as React from 'react';
import { Button, Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
function DetailsScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details!</Text>
</View>
);
}
function SettingDetailsScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>SettingDetails!!!</Text>
</View>
);
}
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
function SettingsScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('SettingDetails')}
/>
</View>
);
}
function HomeStackScreen() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeStackScreen} />
{/* stack의 첫번째 화면에 tab navigator를 중첩시킨다 */}
<Stack.Screen name="Details" component={DetailsScreen} />
<Stack.Screen name="SettingDetails" component={SettingDetailsScreen} />
{/* 페이지내에서 링크 이동이 되는(탭이 필요없는) 페이지도 여기에 적어줘야함 */}
</Stack.Navigator>
</NavigationContainer>
);
}
위와같이 작성해줬더니 특정 페이지에서는 탭이 노출이 되지않았다! 그런데 문제점을 하나 발견했다..
header title
이 탭에따라 유동적으로 바뀌지 않는 이유와 해결방법검색을 해보니 ..!! react navigation
에 방법이 나와있었다..ㅜㅜㅜ
header title
에 반영이 되지 않아던 이유?App stack
은 직계 자식
만 보기 때문에 child 네비게이터
를 보지 않는다 route.state
속성을 사용하여 tab navigator
의 navigation state
를 기반으로 headerTitle
옵션을 결정할 수 있다고한다. route.state
에서 제목을 얻는 함수를 만든다
function getHeaderTitle(route) {
// tab navigator의 `route.state` state를 사용한다
const routeName = route.state
? route.state.routes[route.state.index].name // 현재 active된 route name을 tab navigator에서 가져온다
: route.params?.screen || 'Home';
switch (routeName) {
case 'Home':
return 'GEAGURI';
case 'Settings':
return 'GEAGURI Setting';
}
}
navigation.setOptions
사용해서 headerTitle
옵션값에 현재 활성화된 route name
을 가져와 넣어준다
이 방법을 사용하면 route prop
에 child 네비게이터의 state
속성이 포함된다 (이 경우 탭 네비게이터가 렌더링하게 된다.). 이 상태에서 현재 활성 route 이름의 값을 가져오고 헤더에 적절한 제목을 설정한다
function HomeStackScreen({ navigation, route }) {
React.useLayoutEffect(() => {
navigation.setOptions({ headerTitle: getHeaderTitle(route) });
}, [navigation, route]);
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
);
}
🤔 여기서 궁금한점!
useLayoutEffect
와 useEffect
의 차이가 무엇일까?(구글링 해봄!!)Tab.Screen
에서 options
prop을 사용하는 방법도 있다. export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeStackScreen}
options={({ route }) => ({ // point!!!!!!
headerTitle: getHeaderTitle(route),
})}
/>
<Stack.Screen name="Details" component={DetailsScreen} />
<Stack.Screen name="SettingDetails" component={SettingDetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
우아 된다..!! 우아우!!!
방법1
을 작성하고보니..
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeStackScreen} />
{/* stack의 첫번째 화면에 tab navigator를 중첩시킨다 */}
<Stack.Screen name="Details" component={DetailsScreen} />
<Stack.Screen name="SettingDetails" component={SettingDetailsScreen} />
{/* 만약 페이지 내에서 이동하는 페이지의 수가 많아진다면.. ??? */}
<Stack.Screen name="SettingDetails" component={SettingDetailsScreen}
<Stack.Screen name="SettingDetails" component={SettingDetailsScreen}
<Stack.Screen name="SettingDetails" component={SettingDetailsScreen}
<Stack.Screen name="SettingDetails" component={SettingDetailsScreen}
{/* ..... */}
</Stack.Navigator>
</NavigationContainer>
);
}
위와같이 페이지내에서 이동되는 페이지 수가 많아질 경우에는 줄이 너무 길어질거 같다... 그래서 다른방법을 찾아보았다.(Hiding tab bar in specific screens
으로 검색해봄)
route.state.index
,navigation.setOptions
, tabBarVisible
를 사용해 탭바 숨기기route.state.index
가 0일때는 첫페이지, 첫페이지는 탭바 필수route.state.index
가 0 이상이면 첫페이지가 아니므로 탭바가 필요없음route.state && route.state.index > 0
? navigation.setOptions({ tabBarVisible: false })
: navigation.setOptions({ tabBarVisible: true });
// App.js
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import HomeStackScreen from './Components/HomeStackScreen';
import SettingsStackScreen from './Components/SettingsStackScreen';
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Settings" component={SettingsStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
// HomeStackScreen.js
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import DetailsScreen from './DetailsScreen';
import HomeScreen from './HomeScreen';
const HomeStack = createStackNavigator();
function HomeStackScreen({ navigation, route }) {
console.log('route.state ? ', route.state && route.state);
console.log('route.state.index ? ', route.state && route.state.index);
route.state && route.state.index > 0
? navigation.setOptions({ tabBarVisible: false })
: navigation.setOptions({ tabBarVisible: true });
return (
<HomeStack.Navigator>
<HomeStack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'GAGURI' }}
/>
<HomeStack.Screen name="Details" component={DetailsScreen} />
</HomeStack.Navigator>
);
}
// 각 탭 내에 stack navigation이 존재한다!
export default HomeStackScreen;
// SettingsStackScreen.js
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import SettingsScreen from './SettingsScreen';
import DetailsScreen from './DetailsScreen';
const SettingsStack = createStackNavigator();
function SettingsStackScreen({ navigation, route }) {
console.log('route.state ???????? ', route.state && route.state);
console.log('route.state.index ? ', route.state && route.state.index);
route.state && route.state.index > 0
? navigation.setOptions({ tabBarVisible: false })
: navigation.setOptions({ tabBarVisible: true });
return (
<SettingsStack.Navigator>
<SettingsStack.Screen name="Settings" component={SettingsScreen} />
<SettingsStack.Screen name="Settings" component={DetailsScreen} />
</SettingsStack.Navigator>
);
}
export default SettingsStackScreen;
route.state
에는 어떤 값들이 들어있을까???route.state.index
에 어떻게해서 index
값이 담길수 있는지 궁금해서 아래와같이 콘솔을 찍어보았다
console.log('route.state ? ', route.state && route.state);
console.log('route.state.index ? ', route.state && route.state.index);
오.. route.state
에 이런값들이 담겨서 route.state.index
에 값이 담길수 있는거구나..
route.state
에 값이 어떻게 변하는지 확인아래의 화면에서 Go to Detail
버튼을 클릭하고,
콘솔창을 확인하면 route.state.index
의 값이 1
이고,
다시 뒤로가기를 클릭하고,
콘솔창을 확인하면 route.state.index
의 값이 0
이 된것을 확인할 수 있다