[처음 배우는 리액트 네이티브] 책을 공부하면서 정리한 내용입니다.
모바일 애플리케이션은 하나의 화면으로 구성되기 보단, 다양한 화면이 상황에 맞게 전환되면서 나타남.
: 내비게이션 기능이 가장 중요한 기능 중 하나인 이유
리액트 내비게이션 라이브러리 사용.
스택 내비게이션, 탭 내비게이션, 드로어 내비게이션 지원.
설치방법 / 대부분 필요한 종속성 설치
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
NavigationContainer 컴포넌트
: 내비게이션의 계층 구조와 상태를 관리하는 컨테이너 역할. 최상위 컴포넌트
Navigator 컴포넌트
: 화면을 관리하는 중간 관리자 역할. 여러 개의 Screen 컴포넌트를 자식 컴포넌트로 가지고 있음.
Screen 컴포넌트
: 화면으로 사용되는 컴포넌트. name(화면이름)과 component(화면으로 사용될 컴포넌트) 속성을 지정.
이때, 화면으로 사용되는 컴포넌트에는 항상 navigation과 route가 props로 전달.
리액트 내비게이션으로 설정할 수 있는 다양한 속성을 수정하는 방법
Navigator 컴포넌트의 속성으로 수정 (자식 컴포넌트로 존재하는 모든 컴포넌트에 적용)
Screen 컴포넌트의 속성으로 수정 (해당 화면에만 적용)
화면에서 사용되는 컴포넌트의 props로 전달되는 navigation을 이용해서 수정 (해당 화면에만 적용)
이때, 작은 범위의 설정일 수록 우선순위가 높음. ex. Screen 컴포넌트 < 화면으로 사용되는 컴포넌트
설치
npm install @react-navigation/stack
일반적으로 가장 많이 사용되는 내비게이션, 현제 화면 위에 다른 화면을 쌓으면서 화면을 이용.
ex. 채팅 애플리케이션에서 채팅방에 입장, 여러 목록 중 특정 항목의 상세화면으로 이동
화면 위에 새로운 화면을 쌓으면서(push) 이동하기 때문에 이전 화면을 유지.
따라서 가장 위에 있는 화면을 들어나면(pop) 이전 화면으로 돌아감.
-src/navigations/Stack.js
import React from 'react';
import { createStactNavigator } from '@react-navigation/stack';
import Home from '../screens/Home';
import List from '../screens/List';
import Item from '../screens/Item';
const Stack = createStackNavigator();
const StackNavigation = () => {
return (
<Stack.Navigator>
<Stack.Screen name="Home" components={Home} />
<Stack.Screen name="List" components={List} />
<Stack.Screen name="Item" components={Item} />
</Stack.Navigator>
);
};
export default StackNavigation;
Screen 컴포넌트의 name은 반드시 서로 다른 값을 가져야 함.
스택 내비게이션에서 첫 번째 화면으로 나오는 화면은 Navigator 컴포넌트의 첫 번째 자식 Screen 컴포넌트.
이외에도 initialRouteName 속성을 이용할 수도 있다.
Screen 컴포넌트의 component로 지정된 컴포넌트는 화면으로 이용되고, navigation이 porps로 전달.
navigate 함수 : 원하는 화면으로 이동하는 데 사용
-src/screens/Home.js
const Home = ({ navigation }) => {
return (
<Container>
<Text>Home</Text>
<Button
title="go to the list screen"
onPress={() => navigation.navigate('List')}
/>
</Container>
);
};
Home 화면에서 List 화면으로 이동.
navigate 함수를 이용하여 원하는 화면의 이름을 전달.
만일, 이동하는 화면이 이전 화면의 상세 화면이라면, 어떤 내용을 렌더링해야 하는지 전달받아야 함.
navigate 함수에서 두 번째 피라미터에 객체를 전달해서 이동하는 화면에 필요한 정보 전달.
-src/screens/List.js
const List = ({ navigation }) => {
const _onPress = item => {
navigation.navigate('Item', { id:item._id, name: item.name });
};
...
};
List 화면에서 목록을 클릭하면 해당 항목의 정보와 함께 Item 화면으로 이동.
전달된 내용은 컴포넌트의 props로 전달되는 route의 params를 통해 확인.
-src/screens/Item.js
const Item = ({ route }) => {
return (
<Container>
<Text>Item</Text>
<Text>ID: {route.params.id}</Text>
<Text>Name: {route.params.name}</Text>
</Container>
);
};
Item 화면에서 전달되는 params를 이용하여 화면에 항목의 id와 name 출력.
-src/screens/Home.js
const Container = styled.View`
background-color: #ffffff;
align-items: center;
`;
Home 화면의 배경색을 흰색으로 설정했을 시,
내비게이션은 화면 전체를 차지하고 있지만 화면으로 사용된 컴포넌트의 영역이 전체를 차지하고 있지 않아 전체가 흰색으로 바뀌지 않을 수 있다.
스타일에 flex: 1을 설정하면 문제를 해결할 수 있다.
하지만 상황에 따라 화면 전체를 차지하지 못하는 경우에는, 리액트 내비게이션의 설정을 수정하여 해결할 수 있다.
-src/navigation/Stack.js
const StackNavigation = () => {
return (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{ cardStyle: { backgroundColor: '0ffffff' } }}
>
...
</Stack.Navigator>
);
};
cardStyle을 사용하면 스택 내비게이션의 화면 배경색 수정 가능.
스택 내비게이션의 헤더는 뒤로 가기 버튼, 타이틀(현재 화면)의 역할.
타이틀 :
-기본적으로 Screen 컴포넌트의 name 속성을 사용.
-headerTitle 사용.
ex. src/navigations/Stack.js
<Stack.Screen
name="List"
component={List}
options={{ headerTitle: 'List Screen' }}
/>
모든 화면에서 같은 타이틀이 나타나도록 수정하기 위해선, Navigator 컴포넌트의 screenOptions 속성에 headertitle을 지정.
스타일 :
headerStyle(해더의 배경색 수정), headerTitleStyle(헤더의 타이틀 컴포넌트의 스타일 수정), headerTitleAlign(헤더의 타이틀 정렬)
-src/navigations/Stacks.js
const StackNavigation = () => {
return (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
cardStyle: { backgroundColor: '#ffffff' },
headerStyle: {
height: 110,
backgroundColor: '#95a5a6',
borderBottomMidth: 5,
borderBottomColor: '#34495e',
},
headerTitleStyle: { color: '#ffffff', fontsize: 24 },
headerTitleAlign: 'center',
}}
>
...
</Stack.Navigator>
);
};
타이틀에 문자가 아닌 다른 것을 렌더링하기 위해선, headerTitle 속성에 컴포넌트를 반환하는 함수를 지정. 이때, 해당 함수의 파라미터로 style(headerTitleStyle로 부터)과 tintColor(headerTintColor로 부터) 등이 포함된 객체가 전달됨.
-src/navigations/Stack.js
import { MaterialCommunityIcons } from '@expo/vector-icons';
...
headerTitle: ({ style }) => (
<MaterialCommunityIcons name="react" style={style} />
);
뒤로가기 버튼 :
ios에서는 이전 화면의 타이틀을 버튼의 타이틀로 보여주고 안드로이드에서는 버튼의 타이틀을 보여주지 않음.
-src/navigations/Stack.js
<Stack Screen
name="List"
component={List}
options={{
headerTitle: 'List Screen'
headerBackTitleVisible: true,
headerBackTitle: 'Prev',
}}
/>
둘 다 보이도록 통일.
headerBackTitle이 없을 시, 이전 화면의 타이틀이 나오고 headerBackTitle에 입력을 하면 해당 값이 나온다. 이때, 빈 문자열을 입력하면 Back이라는 문자열이 나타남.
headerBackTitleStyle을 이용해 버튼의 타이틀의 스타일 지정 가능. (버튼의 타이틀에만 적용) 버튼의 타이틀과 이미지의 색을 동일하게 변경하기 위해선 headerTintColor 사용.
<Stack Screen
name="List"
component={List}
options={{
headerTitle: 'List Screen'
headerBackTitleVisible: true,
headerBackTitle: 'Prev',
headerTitleStyle: { fontSize: 24 },
headerTintColor: '#e74c3c',
}}
/>
뒤로가기 버튼의 이미지를 변경
-src/navigations/Stack.js
import { Platform } from 'react-native';
...
<Stack Screen
name="List"
component={List}
options={{
...
headerBackImage: ({ tintColor }) => {
const style = {
marginRight: 5,
marginLeft: Platform.OS === 'ios' ? 11 : 0,
},
return (
<MaterialCommunityIcons
name="keyboard-backspace"
size={30}
color={tintColor}
style={style}
/>
);
};
}}
/>
만일 뒤로 가기 버튼의 이미지가 아닌, 헤더의 왼쪽/오른쪽 버튼 전체를 변경하고 싶다면 headerLeft/Right에 컴포넌트를 반환하는 함수를 지정.
-src/screens/Item.js
import React, { useLayoutEffect } from 'react';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import styled from 'styled-components/native'
...
const Item = ({ navigation, route )} => {
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}
conPress={() => navigation.popToTop()}
/>
),
});
}, []);
return (...);
};
useLayoutEffect Hook은 useEffect Hook과 사용법이 동일하며 거의 같은 방식으로 동작, 하지만 컴포넌트가 업데이트된 직후 화면이 렌더링되기 전에 실행된다는 차이점이 있음. 따라서 화면을 렌더링하기 전에 변경할 부분이 있거나 수치 등을 측정해야하는 상황에서 사용.
headerLeft 함수의 파라미터 중 onPress는 뒤로 가기 버튼 기능이 전달.
headerRight 함수의 파라미터에는 tintColor만 전달되므로 onPress에 원하는 행동을 정의해야 함. 위의 함수인 popToTop 함수는 현재 쌓여 있는 모든 화면을 내보내고 첫 화면으로 돌아가는 기능.
헤더 감추기 :
headerMode - Navigator 컴포넌트의 속성으로 헤더를 렌더링 하는 방법을 설정하는 속성. float(헤더가 상단에 유지, 하나의 헤더 사용)/screen(각 화면하다 헤더를 가지며 화면 변경과 함께 나타나거나 사라짐)/none(헤더가 렌더링 되지 않음)
headerShown - 화면 옵션, Navigator 컴포넌트의 screenOption에 설정하면 전체 화면의 헤더가 보이지 않도록 설정. ex.-src/navigations/Stack.js
<Stack.Screen
name="Home"
component={Home}
options={{ headerShown: false }}
/>
이때, ios는 노치 디자인 문제로 화면 일부가 가려지는 문제를 해결하기 위해 아래의 코드 추가
-src/screens/Home.js
const Container = styled.SafeAreaView`
background-color: #ffffff;
align-items: centerl
`;
보통 화면 위나 아래에 위치, 탭 버튼을 누르면 버튼과 연결된 화면으로 이동하는 방식
설치방법
npm install @react-navigation/bottom-tabs
-src/navigations/Tab.js
import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Mail, Meet, Setting } from '../screens/TabScreens';
const Tab = createBottomTabNavigator();
const TabNavigation = () => {
return (
<Tab.Navigator>
<Tab.Screen name="Mail" component={Mail} />
<Tab.Screen name="Meet" component={Meet} />
<Tab.Screen name="Settings" component={Settings} />
);
};
export default TabNavigation;
-App.js
import TabNavigation from './navigations/Tab';
const App = () => {
return (
<NavigationContainer>
<TabNavigation />
</NavigationContainer>
);
};
Stack과 같이 initialRouteName 사용 가능.
버튼 아이콘 수정 :
tabBarIcon 사용
-src/navigations/Tab.js
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">
<Tab.Screen
name="Mail"
component={Mail}
options={{
tabBarIcon: props => TabIcon({ ...props, name: 'emil' }),
}}
...
/Tab.Navigator>
);
};
Stack에서와 같이 tabBarIcon에 컴포넌트를 반환하는 함수를 지정하면 버튼의 아이콘이 들어갈 자리에 해당 컴포넌트를 렌더링. 이때, tabBarIcon에 설정된 함수에는 color, size, focused값을 포함한 객체가 파라미터로 전달.
또한, Screen 컴포넌트마다 탭 버튼 아이콘을 지정하지 않고 한곳에서 모든 버튼의 아이콘을 관리하고 싶을 경우 Navigator 컴포넌트의 screenOptions 속성을 사용하여 관리.
-src/navigations/Tab.js
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 }) => ({
tabBarIcon: props => {
let name= '';
if (route.name === 'Mail') name = 'email';
else if (route.name === 'Meet) name = 'video';
else name = 'settings';
return TabIcon({ ...props, name });
},
})}
>
...
screenOptions에 객체를 반환하는 함수를 설정하고 함수로 전달되는 route를 이용.
라벨 수정하기 :
버튼 아이콘 아래에 렌더링되는 라벨은 Screen 컴포넌트의 name값을 기본값을 사용. tabBarLabel을 이용하여 변경. 또한, 라벨의 위치를 바꾸고 싶으면 labelPosition의 값을 조정. (below-icon/beside-icon)
-src/navigations/Tab.js
<Tab.Navigator
initialRouteName="Settings"
tabBarOptions={{ labelPosition: 'beside-icon', showLabel: false }}
>
<Tab.Screen
name="Mail"
component={Mail}
coptions={{
tabBarLabel: 'Inbox',
tabBarIcon: props => TabIcon({ ...props, name: 'email' }),
}}
/>
showLabel을 이용하면 라벨이 렌더링 되지 않음.
탭 바 배경색의 기본값은 흰색.
화면의 배경색 수정
-src/screens/TabScreens.js
const Container = styled.View`
flex: 1;
justify-content: center;
align-items: centerl
background-color: #54b7f9;
`;
const StyledText = styled.Text`
font-size: 30px;
color: #ffffff;
`;
탭 바 스타일 수정
이때, 탭 버튼의 아이콘은 선택되어 활성화된 상태의 색과 선택되지 않아 비활성화된 상태의 색을 각각 activeTintColor와 inactiveTintColor를 이용해 설정 가능.(라벨과 함께)
-src/navigations/Tab.js
const TabNavigation = () => {
return (
<Tab.Navigator
initialRouteName="Settings"
tabBarOptions={{
labelPosition: 'besided-icon',
showLabel: false,
style: {
backgroundColor: '#54b7f9',
borderTopColor: '#ffffff',
borderTopMidth: 2,
},
activeTintColor: '#ffffff',
inactiveTintColor: '#0B92E9;,
}}
>
버튼의 아이콘을 설정하기 위해 barTabIcon에 설정한 함수에는 파라미터로 size, color, focused를 가진 객체가 전달. 이 중 focused는 버튼의 선택된 상태를 나타내는 값인데 버튼의 활성화 상태에 따라 다른 버튼을 렌더링하거나 스타일 변경 가능.
-src/navigations/Tab.js
<Tab.Screen
name="Mail"
component={Mail}
options={{
tabBarLabel: 'Inbox',
tabBarIcon: props =>
TabIcon({
...porps,
name: props.focused ? 'email' : 'email-outline',
}),
}}
/>