TypeScript와 React Navigation

Written on October 4, 2019

리액트 네이티브 프로젝트에 타입스크립트를 적용해 보기로 했는데 처음으로 맞닥뜨린 문제가 React Navigation의 screen props는 어떻게 타입으로 정의하고, 전달할지 였다. 이 문제의 해결법을 가장 기본적인 사용자 인증 플로우로 정리해 보았다.

먼저, AuthLoadingScreen에서 asyncStorage에 토큰이 있는지 확인하고, 있으면 HomeScreen으로, 없으면 SignInScreen으로 라우팅 해주는 SwitchNavigator를 AppContainer로 작성해 보자.

import { createAppContainer, createSwitchNavigator } from "react-navigation";
import SignInScreen from "./components/auth/SignInScreen";
import AuthLoadingScreen from "./components/auth/AuthLoadingScreen";
import HomeScreen from "./components/home/HomeScreen";

const AuthSwitch = createSwitchNavigator(
  {
    AuthLoading: AuthLoadingScreen,
    App: HomeScreen,
    Auth: SignInScreen
  },
  {
    initialRouteName: "AuthLoading"
  }
);
const AppContainer = createAppContainer(AuthSwitch);

export default AppContainer;

AuthLoadingScreen.tsx

App.tsx에서 AppContainer를 렌더링 해주면 AuthLoadingScreen이 화면에 표시될 것이다. 이제 AuthLoadingScreen을 작성해 보려고 하는데, 다양한 방법으로 가능하다.

React Navigation Hooks와 react의 useContext 사용하기

React.useContext(NavigationContext)를 사용해서 navigation props를 받아올 수 있다. 콘솔에 찍어보면, 아래와 같이 screen props가 잘 받아져 온 것을 확인할 수 있다.

navigation

이를 함수형 컴포넌트로 활용 해 보면,

import * as React from "react";
import { SafeAreaView, ActivityIndicator, StyleSheet } from "react-native";
import AsyncStorage from "@react-native-community/async-storage";
import { NavigationContext } from "react-navigation";

const AuthLoadingScreen = () => {
  const navigation = React.useContext(NavigationContext);
  React.useEffect(() => {
    _checkAsyncToken();
  }, []);
  const _checkAsyncToken = async () => {
    const userToken = await AsyncStorage.getItem("userToken");
    navigation.navigate(userToken ? "App" : "Auth");
  };
  return (
    <SafeAreaView style={styles.container}>
      <ActivityIndicator />
    </SafeAreaView>
  );
};

export default AuthLoadingScreen;

React Navigation의 NavigationInjectedProps도 사용할 수 있다.

import * as React from "react";
import { SafeAreaView, ActivityIndicator, StyleSheet } from "react-native";
import AsyncStorage from "@react-native-community/async-storage";
import { NavigationInjectedProps, withNavigation } from "react-navigation";

class AuthLoadingScreen extends React.Component<NavigationInjectedProps> {
  componentDidMount = () => {
    this._checkAsyncToken();
  };
  _checkAsyncToken = async () => {
    const userToken = await AsyncStorage.getItem("userToken");
    this.props.navigation.navigate(userToken ? "App" : "Auth");
  };
  render() {
    return (
      <SafeAreaView style={styles.container}>
        <ActivityIndicator />
      </SafeAreaView>
    );
  }
}

export default withNavigation(AuthLoadingScreen);

Params 타입화 하기

혹은 타입화된 Params 사용을 위해 Navigation 타입을 정의해서 사용하는 방법도 있다. 이때 React Navigation의 NavigationScreenPropNavigationState 는 타입정의에 쓸 수 있다.

import * as React from "react";
import { SafeAreaView, ActivityIndicator, StyleSheet } from "react-native";
import AsyncStorage from "@react-native-community/async-storage";
import { NavigationScreenProp, NavigationState } from "react-navigation";

interface NavigationParams {
  text: string;
}
type Navigation = NavigationScreenProp<NavigationState, NavigationParams>;

interface Props {
  navigation: Navigation;
}
class AuthLoadingScreen extends React.Component<Props> {
  componentDidMount = () => {
    this._checkAsyncToken();
  };
  _checkAsyncToken = async () => {
    const userToken = await AsyncStorage.getItem("userToken");
    this.props.navigation.navigate(userToken ? "App" : "Auth");
  };
  render() {
    return (
      <SafeAreaView style={styles.container}>
        <ActivityIndicator />
      </SafeAreaView>
    );
  }
}

export default AuthLoadingScreen;

👩🏻‍💻 배우는 것을 즐기는 프론트엔드 개발자 입니다
부족한 블로그에 방문해 주셔서 감사합니다 🙇🏻‍♀️

in the process of becoming the best version of myself