
리액트 네이티브 애플리케이션의 내비게이션을 쉽게 관리할 수 있게 해준다

Screen 컴포넌트
Navigation 컴포넌트
NavigationContainer 컴포넌트

작은 범위의 설정일수록 우선순위가 높다
리액트 내비게이션 라이브러리 설치
npm install --save @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
npm install @react-navigation/stack
화면 간의 이동과 이전 화면으로의 이동을 스택을 기반으로 해서 내비게이션을 구현한다
새로운 화면을 쌓으면서(push) 이동하므로 이전 화면을 유지하고, 가장 위의 화면을 들어내면(pop) 이전 화면으로 돌아간다
// Home.js
import React from 'react'
import { Button } from 'react-native'
import styled from 'styled-components/native'
const Container = styled.View`
align-items: center;
`
const StyledText = styled.Text`
font-size: 30px;
margin-bottom: 10px;
`
const Home = () => {
return (
<Container>
<StyledText>Home</StyledText>
<Button title="go to the list screen"/>
</Container>
)
}
export default Home
// Item.js
import React from 'react'
import styled from 'styled-components/native'
const Container = styled.View`
flex: 1;
justify-content: center;
align-items: center;
`
const StyledText = styled.Text`
font-size: 30px;
margin-bottom: 10px;
`
const Item = () => {
return (
<Container>
<StyledText>Item</StyledText>
</Container>
)
}
export default Item
// List.js
import React from 'react'
import { Button } from 'react-native'
import styled from 'styled-components/native'
const Container = styled.View`
flex: 1;
justify-content: center;
align-items: center;
`
const StyledText = styled.Text`
font-size: 30px;
margin-bottom: 10px;
`
const items = [
{_id: 1, name: 'React Native'},
{_id: 2, name: 'React Navigation'},
{_id: 3, name: 'Hanbit'},
];
const _onPress = item => {};
const List = () => {
return (
<Container>
<StyledText>List</StyledText>
{items.map(item => (
<Button
key={item._id}
title={item.name}
onPress={() => _onPress(item)}
/>
))}
</Container>
)
}
export default List
// Stack.js
import React from 'react'
import { createStackNavigator } from '@react-navigation/stack'
import Home from '../screens/Home'
import List from '../screens/List'
import Item from '../screens/Item';
const Stack = createStackNavigator();
const StackNavigator = () => {
return (
<Stack.Navigator initialRouteName='Home'> // 첫 번째 화면 지정 가능
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="List" component={List} />
<Stack.Screen name="Item" component={Item} />
</Stack.Navigator>
)
}
export default StackNavigator
// App.js
import React from 'react'
import styled from 'styled-components/native'
import { NavigationContainer } from '@react-navigation/native'
import StackNavigator from './navigations/Stack'
const Container = styled.View`
flex: 1;
background-color: #ffffff;
justify-content: center;
align-items: center;
`
const App = () => {
return (
<NavigationContainer>
<StackNavigator />
</NavigationContainer>
)
}
export default App

navigation의 navigate 함수: 원하는 화면으로 이동
// Home.js
const Home = ({ navigation }) => {
return (
<Container>
<StyledText>Home</StyledText>
<Button
title="go to the list screen"
onPress={() => navigation.navigate('List')}
/>
</Container>
)
}

// List.js
const List = ({ navigation }) => {
// navigate할 때 정보를 같이 넘긴다
const _onPress = item => {
navigation.navigate('Item', {id: item._id, name: item.name});
}
return (
<Container>
<StyledText>List</StyledText>
{items.map(item => (
<Button
key={item._id}
title={item.name}
onPress={() => _onPress(item)}
/>
))}
</Container>
)
}
전달된 내용은 route의 params로 확인 가능
// Item.js
const Item = ({ route }) => {
return (
<Container>
<StyledText>Item</StyledText>
<StyledText>ID: {route.params.id}</StyledText>
<StyledText>NAME: {route.params.name}</StyledText>
</Container>
)
}

return (
<Stack.Navigator
initialRouteName='Home'
screenOptions={{cardStyle: {backgroundColor: '#ffffff'}}}
>
...
</Stack.Navigator>
)

Screen 컴포넌트의 name 속성을 기본값으로 사용하므로 name값 변경하기
headerTitle 이용하기
<Stack.Screen
name="List"
component={List}
options={{headerTitle: 'List Screen'}}
/>

headerStyle: 헤더 속성 수정 (배경색, 높이, 그림자 효과, 하단 경계선 등)headerTitleStyle: 헤더의 타이틀 컴포넌트 스타일 수정headerTitleAlign: left (안드로이드 기본값), center (iOS 기본값)<Stack.Navigator
initialRouteName='Home'
screenOptions={{
cardStyle: {backgroundColor: '#ffffff'},
headerStyle: {
height: 110,
backgroundColor: '#95a5a6',
borderBottomWidth: 5,
borderBottomEndRadius: '#34495e',
},
headerTitleStyle: {color: '#ffffff', fontSize: 24},
headerTitleAlign: 'center',
}}
>

headerTitle에 컴포넌트를 반환하는 함수를 사용하면 타이틀 컴포넌트 변경 가능
style 매개변수에는 headerTitleStyle에서 지정한 스타일을 포함한다
import { MaterialCommunityIcons } from '@expo/vector-icons';
headerTitleStyle: {color: '#ffffff', fontSize: 24},
headerTitleAlign: 'center',
headerTitle: ({style}) => (
<MaterialCommunityIcons name="react" style={style} />
)

안드로이드: 뒤로 가기 버튼에 타이틀 x
iOS: 뒤로 가기 버튼에 타이틀 o
options={{
headerTitle: 'List Screen',
headerBackTitleVisible: true, // 뒤로 가기 버튼 제목 유무
headerBackTitle: 'Prev', // 뒤로 가기 버튼 값
}}
우선순위: headerTitleStyle, headerBackTitleStyle > headerTintClor
headerTitleStyle: 버튼뿐만 아니라 헤더 타이틀에도 적용
options={{
headerTitle: 'List Screen',
headerBackTitleVisible: true,
headerBackTitle: 'Prev',
headerTitleStyle: {fontSize: 24},
headerTintColor: '#e74c3c',
}}

headerRight와 headerLeft에 원하는 버튼 컴포넌트를 렌더링 가능
useLayoutEffect Hook은 useEffect Hook과 거의 비슷하지만 업데이트된 직후 화면이 렌더링되기 전에 실행된다
popToTop : 쌓인 stack을 치우고 맨 처음 스택으로 이동
useLayoutEffect(() => {
navigation.setOptions({
headerBackTitleVisible: false,
headerTintColor: '#ffffff',
headerLeft: ({onPress, tintColor}) => {
return (
<MaterialCommunityIcons
name="keyboard-backspace"
size={30}
style={{marginLeft: 11}}
color={tintColor}
onPress={onPress}
/>
)
},
headerRight: ({tintColor}) => (
<MaterialCommunityIcons
name="home-variant"
size={30}
style={{marginRight: 11}}
color={tintColor}
onPress={() => {navigation.popToTop()}}
/>
)
})
}, []);

headerMode : Navigator 컴포넌트 속성
headerShown : 화면 옵션, Navigator 컴포넌트의 screenOptions에 설정하면 전체 헤더 보이지 않는다
<Stack.Screen
name="Home"
component={Home}
options={{headerShown: false}}
/>

npm install @react-navigation/bottom-tabs
import React from 'react'
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
import { Mail, Meet, Settings } from '../screens/TabScreens'
import { MaterialCommunityIcons } from '@expo/vector-icons';
const TabIcon = ({name, size, color}) => {
return <MaterialCommunityIcons name={name} size={size} color={color} />
}
const Tab = createBottomTabNavigator();
const TabNavigation = () => {
return (
<Tab.Navigator
initialRouteName='Settings'
screenOptions={({route}) => ({
tabIcon: props => {
let name = '';
if (route.name === 'Mail') name='email';
else if (route.name === 'Meet') name='video';
else name='settings';
return TabIcon({...props, name});
}
})}
>
<Tab.Screen
name="Mail"
component={Mail}
options={{
tabBarIcon: props => TabIcon({...props, name: 'email'})
}}
/>
<Tab.Screen
name="Meet"
component={Meet}
options={{
tabBarIcon: props => TabIcon({...props, name: 'video'})
}}
/>
<Tab.Screen
name="Settings"
component={Settings}
options={{
tabBarIcon: props => TabIcon({...props, name: 'settings'})
}}
/>
</Tab.Navigator>
)
}
export default TabNavigation

<Tab.Screen
name="Mail"
component={Mail}
options={{
tabBarLabel: 'Inbox', // 라벨 변경
tabBarIcon: props => TabIcon({...props, name: 'email'})
}}
/>

tabBarOptions은 React Navigation v6 이상에서 더 이상 작동 x , screenOptions의 tabBar 속성으로 대체되었다
<Tab.Navigator
initialRouteName='Settings'
screenOptions={({route}) => ({
tabBarLabelPosition: 'beside-icon',
tabIcon: props => {
let name = '';
if (route.name === 'Mail') name='email';
else if (route.name === 'Meet') name='video';
else name='settings';
return TabIcon({...props, name});
}
})}
>

<Tab.Navigator
initialRouteName='Settings'
screenOptions={() => ({
tabBarLabelPosition: 'beside-icon',
tabBarShowLabel: false, // 라벨 숨기기
})}
>

<Tab.Navigator
initialRouteName='Settings'
screenOptions={() => ({
tabBarLabelPosition: 'beside-icon',
tabBarStyle: {
backgroundColor: '#54b7f9',
borderTopColor: '#ffffff',
borderTopWidth: 2,
},
activeTintColor: '#ffffff',
inactiveTintColot: '#0B92E9'
})}
>

버튼 활성화 상태에 따라 다른 아이콘 렌더링
<Tab.Screen
name="Mail"
component={Mail}
options={{
tabBarLabel: 'Inbox',
tabBarIcon: props =>
TabIcon({
...props,
name: props.focused?'email':'email-outline'
})
}}
/>

참고
처음 배우는 리액트 네이티브