[React Native] React Navigation + Typescript

โœจ ๊ฐ•์€๋น„ยท2022๋…„ 7์›” 7์ผ
10

React Native

๋ชฉ๋ก ๋ณด๊ธฐ
5/5
post-thumbnail

๐Ÿ‘‰ React Navigation๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์ •๋ฆฌํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค!

NavigationContainer

NavigationContainer๋Š” navigation์„ ์œ„ํ•ด ์ตœ์ƒ์œ„์— ๋ Œ๋ฐ๋ง๋˜์–ด์•ผ ํ•œ๋‹ค.

import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>{/* ... */}</Stack.Navigator>
    </NavigationContainer>
  );
}

Ref

props๋กœ ref๋ฅผ ์ „๋‹ฌํ•˜์—ฌ navigation ๊ด€๋ จ action๋“ค์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';

function App() {
  const navigationRef = useNavigationContainerRef();

  return (
    <View style={{ flex: 1 }}>
      <Button onPress={() => navigationRef.navigate('Home')}>
        Go home
      </Button>
      <NavigationContainer ref={navigationRef}>{/* ... */}</NavigationContainer>
    </View>
  );
}

Methods on the ref

// Example
navigationRef.navigate(name, params);

Stack Navigator

์ƒˆ ํ™”๋ฉด์ด navigation stack ๋งจ ์œ„์— ์œ„์น˜ํ•˜์—ฌ ํ™”๋ฉด ๊ฐ„์˜ ์ „ํ™˜์„ ์ œ๊ณตํ•˜๋Š” navigator์ด๋‹ค.

Example

import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from '@react-navigation/stack';
import { RootStackParamList } from "./src/types";
import HomeScreen from "./src/screens/HomeScreen";
import MovieScreen from "./src/screens/MovieScreen";

const RootStack = createStackNavigator<RootStackParamList>();

export default function App() {
  return (
    <NavigationContainer>
        <RootStack.Navigator initialRouteName="Home">
          <RootStack.Screen
            name="Home"
            component={HomeScreen}
            options={{ headerShown: false }}
          />
          <RootStack.Screen
            name="Movie"
            component={MovieScreen}
            options={{ headerShown: false }}
          />
        </RootStack.Navigator>
      </NavigationContainer>
  );
}
export type RootStackParamList = {
  Home: undefined;
  Movie: {
    id: number;
    title: string;
    overview: string;
    voteCount: number;
  };
};

with Typescript

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ๋จผ์ €, route์˜ ์ด๋ฆ„๊ณผ parameter ํƒ€์ž…์„ ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค.

export type RootStackParamList = {
  [name]: params;
};
  • ์œ„ ์˜ˆ์‹œ์—์„œ๋Š” Home๊ณผ Movie ์ด๋ฆ„์„ ๊ฐ€์ง„ route๊ฐ€ ์žˆ๊ณ , Home route์—๋Š” parameter๊ฐ€ ์—†๊ณ , Movie route์—๋Š” id, title ๋“ฑ์˜ parameter๊ฐ€ ์žˆ๋‹ค.
  • RootStackParamList ํƒ€์ž…์„ createStackNavigator ํ•จ์ˆ˜์—๊ฒŒ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.

StackScreenProps

Stack์˜ Screen์œผ๋กœ ๋ Œ๋”๋ง๋˜๋Š” ์ปดํฌ๋„ŒํŠธ๋Š” ์ž๋™์œผ๋กœ navigation props์™€ route props๋ฅผ ์ฃผ์ž…๋ฐ›๋Š”๋‹ค.

// src/screens/HomeScreen.tsx

import { StackScreenProps } from "@react-navigation/stack";
import { RootStackParamList } from "../types";

export type HomeScreenProps = StackScreenProps<RootStackParamList, "Home">;

export default function HomeScreen({ navigation, route } : HomeScreenProps){
  return (
    //...
  );
}
// src/screens/MovieScreen.tsx

import { StackScreenProps } from "@react-navigation/stack";
import { RootStackParamList } from "../types";

export type MovieScreenProps = StackScreenProps<RootStackParamList, "Movie">;

export default function MovieScreen({ navigation, route } : MovieScreenProps){
  const { id, title, overview, voteCount } = route.params;
  return (
    //...
  );
}
  • route์—๋Š” ์•ž์„œ RootStackParamList์—์„œ ์ •์˜ํ•œ params ๊ฐ์ฒด๊ฐ€ ์žˆ๋‹ค.
  • navigation์€ navigation action์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค.
navigation.goBack();
navigation.navigate("Home");
navigation.navigate("Movie", {
  id: movie.id,
  title: movie.original_title,
  overview: movie.overview,
  voteCount: movie.vote_count,
}); // MovieScreen์œผ๋กœ ์ „ํ™˜ํ•  ๋•Œ ์•ž์„œ ์ •์˜ํ•œ ํƒ€์ž…์— ๋งž๋Š” parameter ๊ฐ์ฒด๋ฅผ ๋ณด๋‚ด์•ผ ํ•œ๋‹ค!!

Hook

  • Screen ๋ ˆ๋ฒจ์˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์•„๋‹Œ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ navigation์™€ route๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ useNavigation๊ณผ useRoute๋ฅผ ์ด์šฉํ•˜๋ฉด ๋œ๋‹ค.
  • props๋กœ ์ „๋‹ฌํ•˜์ง€ ์•Š์•„๋„ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ํŽธ๋ฆฌํ•˜๋‹ค.
  • ๋งŒ์•ฝ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด Screen ์ปดํฌ๋„ŒํŠธ์˜ props ํƒ€์ž…์ด ํ•„์š”ํ•˜๋‹ค.
const route = useRoute<MovieScreenProps["route"]>();
const navigation = useNavigation<MovieScreenProps["navigation"]();

์ด ๋ฐ–์—๋„ useScrollToTop, useIsFocused ๋“ฑ ๋‹ค์–‘ํ•˜๊ณ  ์œ ์šฉํ•œ hook๋“ค์„ ์ œ๊ณตํ•œ๋‹ค.


BUG

๊ณต์‹๋ฌธ์„œ๋Œ€๋กœ Stack Navigator๋ฅผ ์ œ๊ณตํ–ˆ๋Š”๋ฐ๋„ ์•„๋ž˜์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

โš ๏ธ 'RootStack.Navigator' cannot be used as a JSX component

Stack overflow์—์„œ ๋ฐฉ๋ฒ•์„ ์ฐพ์•˜๋Š”๋ฐ @types/react์˜ ๋ฒ„์ „ ๋ฌธ์ œ์˜€๋‹ค. ๋ฒ„์ „์„ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์˜€๋‹ค.

- "@types/react": "~17.0.21",
+ "@types/react": "18.0.0",

๐Ÿ‘‰ React Navigation์€ Stack Navigator๋ฟ๋งŒ ์•„๋‹ˆ๋ผ Bottom Tabs Navigator ๋“ฑ ์ด 6๊ฐœ์˜ Navigator๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

๐Ÿ‘€ ์ •๋ฆฌํ•œ ๋‚ด์šฉ ์ด์™ธ์—๋„ ๊ณต์‹๋ฌธ์„œ์—๋Š” Nesting Navigators, Navigator Lifecycle ๋“ฑ ๋‹ค์–‘ํ•œ ๋‚ด์šฉ๋“ค์„ ์นœ์ ˆํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๊ณ  ์žˆ๋‹ค. Typescript๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋“ค์„ ์œ„ํ•ด Type checking with TypeScript์— ๋Œ€ํ•œ ๋‚ด์šฉ๋„ ํ•จ๊ป˜ ์„ค๋ช…ํ•˜๊ณ  ์žˆ๋‹ค. ์‚ฌ์šฉ์ž์—๊ฒŒ ๋‹ค์–‘ํ•œ ํ™”๋ฉด์„ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด์„œ๋Š” navigation์€ ๊ฑฐ์˜ ํ•„์ˆ˜ ์š”์†Œ์ด๋‹ค. ๊ทธ๋ž˜์„œ navigation ๊ด€๋ จ api๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š” React Native ์•ฑ์„ ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐ ์žˆ์–ด์„œ๋Š” ๋งค์šฐ ์œ ์šฉํ•˜๊ณ  ๊ณ ๋งˆ์šด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ๐Ÿ’–

0๊ฐœ์˜ ๋Œ“๊ธ€