Develop/React & React Native

[React Native] linkTo 사용 시, The 'navigation' object hasn't been initialized yet 문제 해결 방법 (react-navigation NavigationContainer 마운팅된 직후를 onReady로 감지하기)

안다희 2023. 4. 8. 01:51
728x90

특정 페이지에 도달 할 수 있는 링크인 딥링크를 이용하고 있다.

 

앱이 완전히 닫힌 상태에서 딥링크로 앱을 열면,

(Android의 경우) Linking.getInitialURL 로 

(iOS의 경우) Linking.addEventListener('url', handleOpenUrl) 로

앱을 열게 한 url 장본인(?)을 알 수 있다.

(이 글은 Android 기준으로 코드를 작성했습니다.)

 

const url = "roubitapp://root-tab/setting"

위 url로 앱을 열면 SettingScreen이라는 화면으로 navigate해야한다고 가정해보자.

코드는 아래와 같이 짤 수 있다. (자세한 딥링크 세팅은 이곳을 참고)

import React, { useEffect, useState } from 'react';
import { Linking } from 'react-native';
import { NavigationContainer, useLinkTo } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

export default () => {
  const linkTo = useLinkTo()

  useEffect(() => {
      Linking.getInitialURL()?.then(url => {
        if (url) {
          const deepLink = getDeepLinkByUrl(url); // ex. '/setting'
          linkTo(deepLink)
        }
      });
  }, [])

  return (
    <NavigationContainer
      linking={{
        prefixes: ['roubitapp://'],
        config: {
         // ...,
         SettingScreen: {
         	path: "setting"
         }
        }
      }}
      >
      <Stack.Navigator>
        <Stack.Screen />
        <Stack.Screen />
        <Stack.Screen />
      </Stack.Navigator>
    </NavigationContainer>
   );
  }

그런데, linkTo를 사용하면 아래와 같은 오류가 난다.

// ERROR  The 'navigation' object hasn't been initialized yet. 
// This might happen if you don't have a navigator mounted, 
// or if the navigator hasn't finished mounting. 
// See https://reactnavigation.org/docs/navigating-without-navigation-prop#handling-initialization for more details.

아직 navigation이 세팅되지 않았는데 사용하려고 해서 생기는 문제다.

 

에러에 친절하게 안내되어있는 링크를 따라가본다. (웬만한 에러는 에러내용 잘 읽으면 해결됨. 떠먹여주는대로 받아먹자!)

navigation이 세팅되었는지를 아는 방법에 대해 안내해준다.

하지만 Linking.getInitialURL()을 실행하는 시점에 navigationRef.isReady()가 여전히 false라면 쓰나마나다.

우리는 navigation이 세팅되는 순간을 알아야 한다. 그래야 그 이후에 linkTo를 사용할 수 있기 때문이다.

 

이 링크를 보자.

NavigationContainer에 onReady라는 prop을 사용하면 된다.

문서에서는 아래와 같이 설명하고 있다.

// Function which is called after the navigation container and all its children finish mounting for the first time.

NavigationContainer와 하위 컴포넌트가 모두 마운팅을 마친 시점에 onReady가 실행된다.

이 점을 이용하여 아래와 같이 코드를 추가했다.

import React, { useEffect, useState } from 'react';
import { Linking } from 'react-native';
import { NavigationContainer, useLinkTo } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator<RootTabStackParams>();

export default () => {
  const [isNavMounted, setIsNavMounted] = useState<boolean>(false);
  const linkTo = useLinkTo()

  useEffect(() => {
    if (isNavMounted) {
      Linking.getInitialURL()?.then(url => {
        if (url) {
          const deepLink = getDeepLinkByUrl(url); // ex. '/setting';
          linkTo(deepLink)
        }
      });
    }
  }, [isNavMounted])

  return (
    <NavigationContainer
   	  onReady={() => {
        setIsNavMounted(true)
      }}
      linking={{
        prefixes: ['roubitapp://'],
        config: {
         // ...,
         SettingScreen: {
         	path: "setting"
         }
        }
      }}
      >
      <Stack.Navigator>
        <Stack.Screen />
        <Stack.Screen />
        <Stack.Screen />
      </Stack.Navigator>
    </NavigationContainer>
   );
  }

이제 isNavMounted를 이용하여 navigation 세팅이 완료되었음을 확신하고 linkTo를 사용할 수 있게 되었다.🔥🔥🔥

 

 

참고링크

https://velog.io/@beanlove97/NavigationContainer

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

https://reactnavigation.org/docs/navigation-container/#onready