Skip to main content

Command Palette

Search for a command to run...

Tab Navigator에서 params에 따라 렌더링하기

Updated
3 min read

React Navigation은 react native에서 화면이동(routing) 시 사용되는 라이브러리로, 페이지가 쌓이는 구조(stack 구조)를 제공한다. Stack 뿐만 아니라 Tab(하단탭), Drawer(옆에서 열리는 전체메뉴) 등의 화면 이동 방식(네비게이터)도 가지고 있다. 네비게이터들은 서로 중첩이 가능하다. 우리 프로젝트에서는 Stack과 Tab 네비게이터를 사용했고, Stack 내 하나의 화면에 Tab을 중첩하여 사용했다.

📘 기본사용방법

  1. Stack 네비게이터를 생성 - 리액트 네비게이션은 <NavigationContainer> 내에서 실행할 수 있다.
    Stack.Screen은 화면의 이름과 렌더링되는 컴포넌트를 지정한다.
  • 네비게이터 타입의 Stack.Screen의 name이 될 수 있다. 키의 타입undefined면 화면 이동 시 params가 필요없는 화면이고, 객체로 들어간다면 해당 객체를 화면 이동시 params로 넘겨준다.
    navigation.navigate('Detail', {idx: item.index}
export type RootStackParamList = {
  Tab: NavigatorScreenParams<HomeTabParamList>;
  Shop: undefined;
  Detail: { idx: number };
}

const Stack = createNativeStackNavigator<RootStackParamList>(); // 스택 네비게이터 생성

const App = () => (
  <NavigationContainer>  
    <Stack.Navigator>
      <Stack.Screen name='Tab' component={TabNavigator} />
      <Stack.Screen name='Shop' component={Shop} />
      <Stack.Screen name='Detail' component={Detail} />
    </Stack.Navigator>
  </NavigationContainer>
)
  1. 탭 네비게이터를 생성 - 탭 네비게이터는 스택의 화면 중 하나로 들어가고 있다. 하단 탭의 컴포넌트를 tabBar로 설정할 수 있고 props는 BottomTabBarProps 타입이 전달되어 해당 탭의 이름과 파람스를 알 수 있다.
type HomeTabParamList = {
  Home: {tab: 'first' | 'second'} | undefined;
  Mypage: undefined;
  Menu: undefined;
}

// BottomTabBarProps의 속성을 사용해 탭 클릭 시 화면 이동
const TabBar = ({state, navigation}) => (
  // ... 
  const onPress = () => {
    navigation.navigate(state.route.name, state.route.params);
  }
 // ...
)

const Tab = createBottomTabNavigator<HomeTabParamList>();  // 탭 네비게이터 생성

const TabNavigator = () => (
  <Tab.Navigator tabBar={TabBar}>
    <Tab.Screen name='Home' component={Home} />
    <Tab.Screen name='Mypage' component={Mypage} />
    <Tab.Screen name='Menu' component={Menu} />
  </Tab.Navigator>
)
  • 탭 네비게이터 간의 이동은 화면이 전환되기만 할 뿐 렌더링되지 않는다. 반면에 스택 네비게이터는 화면 이동 시 새로 렌더링된다. 스택 네비게이터는 화면이 쌓이는 구조이기때문에 이전화면으로 이동이 가능하며, 새로운 화면으로 이동할 때는 새로운 스크린이 push 되는 방식이다.

⚠️ 문제

  • 그래서 탭 간의 이동을 할때, 특히나 파람스에 따라 다르게 렌더링되는 화면의 이동이 잘 되지 않았다. 기존 스택 네비게이션에서 사용하는 방식을 사용하면 해당화면으로 이동은 하지만, 파람스에 따라 tab을 보여주진 않았다.
  • 그리고 특이하게 route.params?.tab가 변경되면 탭의 상태(select)를 변경하도록 했는데 setSelect가 두번 동작하면서 기존 tab으로 돌아가는 현상이 일어났다. first → second → first 이런식으로..
// Menu 화면에서 Home 화면으로 이동
// type CombinedParamList = RootStackParamList & HomeTabParamList;
navigation.navigate('Home', { tab: 'second' } as CombinedParamList['Home']);

// 위 네비게이션이 동작하면 Home 으로 이동하면서 params 변경에 따라 select가 변경되어 렌더링 되는 것을 예상했다
// Home.tsx
  const [select, setSelect] = useState<THomeTab>(route.params?.tab ?? 'first');

  useEffect(() => {
    console.log('2️⃣ 셀렉트 변경됨', select);
  }, [select]);

  useEffect(() => {
    if (route?.params?.tab) {
      setSelect(route?.params?.tab);
      console.log('1️⃣ 탭 변경됨', route?.params?.tab);
    }
  }, [route.params?.tab]);

위의 코드는 아래처럼 찍힌다

LOG 1️⃣ 탭 변경됨 second     # route.params?.tab이 second로 변경
LOG 2️⃣ 셀렉트 변경됨 second  # setSelect가 작동하여 select가 second로 변경
LOG 2️⃣ 셀렉트 변경됨 first   # ????

💡 navigation.dispatch(CommonActions.reset(...))

탭 네비게이션 이동시 리렌더링이 되지 않는것은 알겠으나 왜 setSelect가 두번 실행되어 기존 상태로 돌아가는지는 알아내지 못했다. 대신 홈의 각각의 탭으로 이동할 때는 네비게이션 기록을 초기화하여 새롭게 렌더링하는 navigation.dispatch(CommonActions.reset(...)) 이 방식을 사용했다. 기존의 화면 상태나 파라미터가 남지 않고 완전 초기화되어 렌더링하는 방식이다.

navigation.dispatch(
  CommonActions.reset({
  routes: [{ name: item.page, params: item.params as CombinedParamList['Home'] }],
  }),
);

react-navigation 라이브러리 사용 방법을 정리하면서 네비게이션이 어떤 구조로 이루어져있고, 동작하는지, 그리고 각각의 네비게이터의 특성에 따라 다르게 실행하는 방법도 익힐 수 있었다. 탭 네비게이터에서 새로 렌더링 하지않고 파람스에 따라 화면을 렌더링 시키는 다른 방법이 있는지도 궁금하다. 작업 중 일어난 버그를 완전히 해결하진 못했지만 사용하다보면 또 새롭게 알아가겠지 싶다.

More from this blog

npm run ios 에러 해결기

이전부터 리액트 네이티브 프로젝트에서 ios 시뮬레이터를 실행시키면 절대 한번에 실행되는 법이 없었다. 그래서 그때도 어떻게 에러를 해결했는지 기록해놨었는데, 그걸 참고해도 실행이 안되더라는😂 그래서 또 다시 작성한다. npm run ios로 스트레스 받지 않는 그날을 위해 1. pod install 깃에서 프로젝트를 받고 npm run ios를 하기 전 pod install(프로젝트에 필요한 라이브러리 설치)을 했다. 하지만 언제나 여기부터...

Feb 23, 20254 min read

react-hook-form을 알아보자

React-hook-form에 대해서는 input 입력 시 렌더링을 최소화하는 라이브러리 정도로만 알고 있었다. 현 회사에 입사해서 계속 리액트 훅 폼을 사용하고 있는데, 기계적으로 붙여넣기(..)만 하다가 제대로 공부해야겠다는 생각이 들어 작성하는 리액트 훅 폼 정리 글이다. 1. 제어 컴포넌트 vs 비제어 컴포넌트 제어/비제어 컴포넌트는 리액트가 실시간으로 state를 제어할 수 있는지 없는지에 따라 나눠진다. 제어 컴포넌트를 사용하게 되면...

Feb 4, 20255 min read

axios 응답 인터셉터로 리턴 값 변경 시 타입에러 발생

🐞 문제상황 axios에서 interceptor를 사용해서 데이터를 바로 리턴해주도록 설정했다. 그런데 axios에서 리턴 받은 데이터 객체의 속성을 바로 사용하려고 하니 타입에러가 발생했다. 그리고 인터셉터를 적용하지 않은 것처럼 data.data.속성명을 작성했더니 타입에러가 뜨지 않았다. // utils/api.ts const api = axios.create({ baseURL: 'http://baseurl', timeout: 1...

Jan 20, 20253 min read

개발 블로그

6 posts

Tab Navigator에서 params에 따라 렌더링하기