[React Native] App.js, index.js 내부에서 navigation prop 없이도 navigate 하기 & routeName 받아오기

May·2020년 8월 20일
4

React Native

목록 보기
5/7
post-thumbnail

💫 참고 자료

https://reactnavigation.org/docs/navigating-without-navigation-prop/


나는 왜 이 기능이 필요했는가?

앞서 react-native firebase 로 푸시 알림을 구현했었습니다.
이 게시글4. 푸시 알림 수신 코드 작성을 참고해보시면,
앱이 켜져 있을 때, 즉 foreground 상태일 때에는 푸시 알림이 왔다는 걸
App.js 내부에서 잡아낼
수 있는데요.


// App.js

import React, { useEffect } from 'react';
import { Alert } from 'react-native';
import messaging from '@react-native-firebase/messaging';

function App() {
  useEffect(() => {
    const unsubscribe = messaging().onMessage(async remoteMessage => {
      // 앱이 foreground 인 상태에서 푸시 알림이 오면 이 안쪽을 탑니다.
      // 기존에는 react-native 의 기본 Alert API 를 사용해 알럿을 띄워주도록 했어요.
      Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
    });

    return unsubscribe;
  }, []);
}

위와 같이 코드를 작성하면, foreground 상태에서 푸시 알림을 수신했을 때 아래와 같은 창이 뜹니다.


저는 저 기본 Alert 대신, 커스텀으로 제작한 알럿 창을 띄워주고 싶었습니다.

그런데 우리 프로젝트의 custom alert 는 하나의 screen 으로 제작해 navigate 메서드를 통해 어디서든 띄워줄 수 있는 방식으로 컴포넌트화 해 놓았어요.

따라서 App.js 내부에서 navigate 메서드를 사용해야 하는 상황입니다.


일단 navigation 을 사용하려면 props 내부에 navigation prop 이 있어야 하기에
콘솔에 props 를 찍어보았는데요, 빈 객체가 들어있습니다.
App.js 에는 navigation prop 이 없는 거죠.


그래서 navigation prop 이 없어도 navigate 할 수 있는 방법을 찾아보았습니다.
이 공식 문서의 내용을 참고해주세요.


위 공식 문서에 따르면, RootNavigation.js 라는 파일을 만들어서 navigationRef 를 export 시키고,
navigationRefApp.tsx 내부에서 NavigationContainerref 로 전달하는 방식이었다.


이렇게 적으니 복잡한 것 같지만 한 단계씩 따라하다보면 간단합니다.


1. 일단 RootNavigation.js 파일을 생성합니다.

위치는 알아서! 저는 그냥 App.js 와 같은 위치에 만들었습니다.


// RootNavigation.js

import * as React from 'react';

export const navigationRef = React.createRef();

export function navigate(name, params) {
  navigationRef.current?.navigate(name, params);
}

2. NavigationContainerrefnavigationRef 를 전달합니다.

그런데 우리 프로젝트의 기존 코드에서는 NavigationContainerApp.tsx 가 아닌 RootStackNavigator.tsx 내부에 있었습니다.

RootnavigatorRootStack.Navigator 를 감싸고 있네요.

아래가 기존 코드입니다.


// App.tsx

import RootNavigator from '../RootStackNavigator';

const App = () => {
  return <RootNavigator />
}
// RootStackNavigator.tsx

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

const RootNavigator = props => {
  <NavigationContainer>
    <RootStack.Navigator>
      {...}
    </RootStack.Navigator>
  </NavigationContainer>
}

그래서 NavigationContainerApp.tsx 로 옮겨주고, 거기다 ref를 전달했습니다.


// App.tsx

import { NavigationContainer } from '@react-navigation/native';
import { navigationRef } from './RootNavigation';
import RootNavigator from '../RootStackNavigator';

const App = () => {
  return (
    <NavigationContainer ref={navigationRef}>
      <RootNavigator />
    </NavigationContainer>
  )
}

3. 그럼 이제 App.tsx 내부에서 navigation 이 가능합니다.


// App.tsx

import { NavigationContainer } from '@react-navigation/native';
import { navigationRef } from './RootNavigation';
import RootNavigator from '../RootStackNavigator';

const App = () => {
  useEffect(() => {
    const unsubscribe = messaging().onMessage(async (remoteMessage: any) => {
      // 앱이 foreground 일 경우 push 알림이 오면 이 안쪽을 탑니다.
      // 푸시 알림의 제목과 본문은 remoteMessage.notification 내부에 있으므로
      // 해당 내용을 openPushNotification 함수에 인자로 넣어줍니다.
      const { title, body } = remoteMessage.notification;
      openPushNotification(title, body);
    });

    return unsubscribe;
  }, []);
  
  // custom alert 창을 띄우는 코드입니다.
  // alert 에 띄울 정보를 params 에 담고,
  // navigation prop 과 함께 alert 창을 띄우는 함수에 넘겨줍니다.
  const openPushNotification = (title: string, body: string) => {
    let params = {
      title: title,
      message: body,
      {...}
    };

    // 원래 첫 번째 인자로 navigation prop 을 넘겨주도록 만들어놨는데,
    // 여기선 그 대신 navigationRef.current 를 넘겨주면 됩니다.
    ApplicationManager.showModal(navigationRef.current, params);
  };
  
  return (
    <NavigationContainer ref={navigationRef}>
      <RootNavigator />
    </NavigationContainer>
  )
}

그러면 이제 현재 사용자가 보고 있던 screen 이 어느 screen이든 간에
푸시 알림이 올 경우 해당 screen 에서 알림 내용을 custom alert 으로 띄워주는 코드가 완성되었습니다.



4. 현재 screen 의 routeName 은 어떻게 받아오죠?


console.log(navigationRef.current?.getRootState().routes[0]);

App.tsx이든 index.js 이든 navigation prop 을 받아올 수 없는 곳이라면 어디에서든
위 내용을 콘솔 찍어 보세요.

아래와 같은 내용을 받아볼 수 있습니다.

현재 navigator, 그리고 현재의 route 를 알 수 있죠!!
저는 navigator 정보만 필요했기 때문에

if (navigationRef.current?.getRootState().routes[0].name === 'LoginNavigator')

이런 식으로 사용했습니다.

profile
쉽다는 설명도 저는 어려워요.

3개의 댓글

comment-user-thumbnail
2021년 2월 16일

많은 도움이 되었습니다,,

1개의 답글
comment-user-thumbnail
2022년 12월 12일

잘봤습니다~ 감사합니다!

답글 달기