My Boundary As Much As I Experienced

RN) 리액트 네비게이션 - 스크린 이동 총정리 (navigation객체와 훅, useNavigate) 본문

FrontEnd/React Native

RN) 리액트 네비게이션 - 스크린 이동 총정리 (navigation객체와 훅, useNavigate)

Bumang 2024. 8. 17. 18:08

navigation객체와 useNavigation훅

리액트 네비게이션에서 다른 페이지로 네비게이션을 하려할 때 navigation을 이용해야된다.

그런데 navigation을 생성할 수 있는 방법은 2개가 있다.

navigation prop와 useNavigation훅을 이용하는 방법이다.

 

1. 페이지 컴포넌트에서 prop으로 제공되는 navigation객체 꺼내기

navigation 객체는 일반적으로 스크린 컴포넌트의 props로 전달된다.

'페이지 컴포넌트'라면 언제 어느 곳에서든 꺼내서 사용할 수 있다.

(페이지 컴포넌트의 기준이 뭐냐고? Stack.Screen에 제공한 컴포넌트는 페이지 컴포넌트로 분류된다.)

function HomeScreen({ navigation }) {
  return (
    <View>
      <Button
        title="Go to Details"
        onPress={() => navigation.navigate('Details')}
      />
    </View>
  );
}

 

그러나 타입스크립트를 적용한 프로젝트에서 쓰고 싶으면 조금 귀찮다.

해당 네비게이션의 '모든 페이지들과 모든 패러미터의 타입'을 StackNavigationProp라는

wrapper로 감싼 타입을 만들고 navigation에 지정해줘야 되기 때문이다.

타입스크립트를 적용하면 아래와 같다.

import React from 'react';
import { Button, View, Text } from 'react-native';
import { StackNavigationProp } from '@react-navigation/stack';
import { RootStackParamList } from './types'; // 스택 네비게이션에 대한 타입 정의

// RootStackParamList는 해당 네비게이션 스택에 있는 모든 스크린의 이름과 파라미터 타입을 정의한 타입
type HomeScreenNavigationProp = StackNavigationProp<RootStackParamList, 'Home'>;

interface Props {
  navigation: HomeScreenNavigationProp;
  someTextProp: string;
};

function HomeScreen({ navigation, someTextProp }: Props) {
  return (
    <View>
      <Button
        title="Go to Details"
        onPress={() => navigation.navigate('Details')}
      />
      <Text>{someTextProp}</Text>
    </View>
  );
}

 

 

2. useNavigation훅으로 navigation 생성하기

페이지 컴포넌트가 아닌 곳에서 navigating을 발생시키려면 어떻게 해야되나?

예를 들어 특정 페이지로 이동시키는 버튼을 만들고 내부에 navigating 로직을 만들어야 된다고 칠 때,

navigation객체를 꺼내는 방법은 쓸 수 없다. (페이지 컴포넌트가 아니기 때문에.)

 

이럴 때 useNavigation을 통해 네비게이션을 생성하여 사용할 수 있다.

import { useNavigation } from '@react-navigation/native';

function HomeScreen() {
  const navigation = useNavigation();

  return (
    <View>
      <Button
        title="Go to Details"
        onPress={() => navigation.navigate('Details')}
      />
    </View>
  );
}

 

이 역시 타입스크립트를 적용시키려면 매우 귀찮아진다.

해당 navigation의 모든 페이지와 패러미터를 포함한 타입을

NavigationProp이란 타입으로 wrapping한다음에 제공해야 에러가 안 뜨기 때문이다.

 

import React from 'react';
import { Button, View } from 'react-native';
import { useNavigation, NavigationProp } from '@react-navigation/native';
import { RootStackParamList } from './types';

function HomeScreen() {
  const navigation = useNavigation<NavigationProp<RootStackParamList>>();

  return (
    <View>
      <Button
        title="Go to Details"
        onPress={() => navigation.navigate('Details')}
      />
    </View>
  );
}

 

만약 그런데 해당 Stack이 아닌 다른 Stack의 페이지로 이동할 때는 어떻게 해야되나?

현재 '구매플로우 관련 Stack'에 있는데, 갑자기 '이벤트 응모 관련 Stack'에 보내야할 때

구매플로우의 ParamList만 제공하면 타입스크립트는 가차없이 에러를 일으킨다...

 

(이때 유의해야할게 타입스크립트만 타입을 못찾겠다고 찡찡대는 것 뿐이지

다른 스택으로의 이동 자체가 불가능한건 아니다.

스택은 독립적으로 운용되며, 다른 스택으로 이동한다고 기존 스택이 상태가 비워지거나 사라지지 않는다.)

 

그러니 그냥 앗싸리 모든 스택의 타입을 총망라한 전체타입을 만들어서 사용하면 속편하다. 

 

총체적인 네비게이션 구조 타입을 만들어서 어느 페이지로든 이동을 용이하게 하기

그래서 나는 총체적인 네비게이션 구조 타입을 만들어서 이걸 navigation할 때 사용한다.

CompositeNavigationProp을 꺼내 2개의 타입을 묶을 수 있다.

 

2개 이상의 복수의 타입을 묶을 수 있으면 되게 편하련만.. 리액트 네비게이션은 그런 타입을 지원하지 않는다.

그래서 Stack들이 여러개면 2개씩 계속 묶어서 총체적인 타입을 만들어야 한다.

(글쓰면서 생각한건데 복수의 타입을 묶을 수 있는 타입을 내가 만들까?)

import { CompositeNavigationProp } from "@react-navigation/native";

export type TotalNavigationProp = CompositeNavigationProp<
  NativeStackNavigationProp<MainStackParamList, "BottomTab">,
  BottomTabNavigationProp<BottomTabParamList, "Home">
>;

 

하여튼 이걸 사용하면 Navigating할때 마다 '이 스택이 무슨 스택이었더라' 고민할 일이 사라진다.

 

navigation prop에 사용할 때:

import { TotalNavigationProp } from "../../../App";

interface LoginProps {
  navigation: TotalNavigationProp; // navigation prop에 지정 가능
}

const LoginPage = ({ navigation }: LoginProps) => {
  const [id, setId] = useState("");
  const [password, setPassword] = useState("");

 

useNavigation에 사용할 때:

  const navigation = useNavigation<TotalNavigationProp>();

 

끝내며..

ReactNavigation은 리액트네이티브 프로젝트의 국룰 라우트 라이브러리이다.

리액트 프로젝트에서 ReactRouterDOM이 차지하는 위치와 비슷하달까..

 

그럼에도 ReactRouterDOM이나 Next.js의 폴더구조 Router에 비하면 학습곡선이 매우 높은 것 같다.

아무래도 웹보다 모바일이 한 화면에서 컨트롤할 수 있는 정보량이 적다보니

페이지가 많아지게 되고, 페이지가 운용되는 구조도 웹과 다르다보니 낯선 개념들이 많아서 그런거 같다.

이런 부분만 잘 파악한다면 어렵지 않게 쓸 수 있을 것이다.