
react native 프로젝트에서 하단 탭 네비게이터와 스택 네비게이터를 동시에 사용하는 방법입니다.
react native 앱을 개발하면서 bottom-tabs-navigator와 stack-navigator가 둘 다 필요했는데,
각 tab에 있는 stack간 이동이 어려워서 tab navigator는 직접 제작하고 각 tab에 stack-navigator를 넣어주었습니다.
즉, bottom-tabs-navigator 라이브러리는 사용하지 않았습니다.
먼저 이동할 페이지들을 stack navigator에 포함시켜 줍니다.
// DiaryNav.tsx
import { createNativeStackNavigator } from '@react-navigation/native-stack';
// 각 페이지 타입 지정
type DiaryStackParam = {
DiaryList: undefined;
EditDiary: undefined;
WriteDiary: undefined;
DiaryDetail: undefined;
};
// stack 네비게이터 생성
const DiaryStack = createNativeStackNavigator<DiaryStackParam>();
export const DiaryNav = () => {
return (
<DiaryStack.Navigator
initialRouteName="DiaryList">
// 해당 stack에서 이동할 페이지들
<DiaryStack.Screen name="DiaryList" component={DiaryList} />
<DiaryStack.Screen name="EditDiary" component={EditDiary} />
<DiaryStack.Screen name="WriteDiary" component={WriteDiary} />
<DiaryStack.Screen name="DiaryDetail" component={DiaryDetail} />
</DiaryStack.Navigator>
);
};
// NavigationContext.tsx
// 활성화 된 탭의 상태를 전역으로 관리하는 context
import React, { createContext, useContext, useState } from 'react';
export type TabName = 'Calendar' | 'Diary' | 'Mypage';
interface NavigationContextType {
currentTab: TabName;
setCurrentTab: (tabName: TabName) => void;
}
const NavigationContext = createContext<NavigationContextType | undefined>(undefined);
export const NavigationProvider = ({ children }: { children: React.ReactNode }) => {
const [currentTab, setCurrentTab] = useState<TabName>('Calendar');
return <NavigationContext.Provider value={{ currentTab, setCurrentTab }}>{children}</NavigationContext.Provider>;
};
export const useNavigationState = () => {
const context = useContext(NavigationContext);
if (!context) {
throw new Error('useNavigation must be used within a NavigationProvider');
}
return context;
};
// BottomTabNav.tsx
import { useNavigation } from '@react-navigation/native';
const BottomNav = () => {
const navigation: any = useNavigation();
// 활성화된 탭의 색 변화
// context에서 따로 상태 관리
const { currentTab, setCurrentTab } = useNavigationState();
const getIconColor = (tabKey: string) => (currentTab === tabKey ? 'pink' : 'black');
// 각 탭의 이름과 stack navigator, icon을 정의
const tabs = [
{ key: 'Calendar', nav: 'CalendarNav', label: <Icon name="calendar" size={27} color={getIconColor('Calendar')} /> },
{ key: 'Diary', nav: 'DiaryNav', label: <Icon name="book" size={27} color={getIconColor('Diary')} /> },
{ key: 'Mypage', nav: 'UserNav', label: <Icon name="user" size={27} color={getIconColor('Mypage')} /> },
];
return (
<View>
{tabs.map(tab => (
<TouchableOpacity
key={tab.key}
onPress={() => {
setCurrentTab(tab.key as TabName);
navigation.reset({
index: 0,
routes: [
{
name: tab.nav,
state: {
routes: [{ name: tab.key }],
},
},
],
});
}}>
{tab.label}
</TouchableOpacity>
))}
</View>
);
};
style은 제외한 코드입니다.
navigation.reset을 사용해 각 탭을 눌렀을 때, stack이 쌓이지 않고 초기화 되면서 이동하도록 했습니다.
이렇게 하면 각 탭을 누르면 각 stack의 초기 페이지로 이동하게 되면서 페이지 간 이동이 원활히 됩니다.