React-navigationで、screen trackingしてて、上位のルーターを跨げなくなったときの対処法

## はじめに
こんにちは、React, ReactNativeでWebとアプリの開発を行っております、株式会社Lyactの山田と申します。

普段は音声小説配信サービスWritoneというアプリの開発を行っております。

今回は、アプリの分析を行うために導入したscreentrackingでかなり手こずったところがあったので、
それについて書きます。

色々わからないことも多いため、ご指摘・ご指導いただけるとありがたいです。

## どんな問題？
これは、[公式ドキュメントのScreen trackingの方法](https://reactnavigation.org/docs/en/screen-tracking.html)
上記を見ていただくとわかるのですが、

react nativeで、ScreenTrackingを行う場合は、

``` navigatorA.js
import { GoogleAnalyticsTracker } from 'react-native-google-analytics-bridge';

const tracker = new GoogleAnalyticsTracker(GA_TRACKING_ID);

// gets the current screen from navigation state
function getActiveRouteName(navigationState) {
  if (!navigationState) {
    return null;
  }
  const route = navigationState.routes[navigationState.index];
  // dive into nested navigators
  if (route.routes) {
    return getActiveRouteName(route);
  }
  return route.routeName;
}

const NavigatorA = createStackNavigator(NavigatorAConfigs);

//　export default NavigatorAではなく、　() => <NavigatorA />という形になる
export default () => (
  <NavigatorA
    onNavigationStateChange={(prevState, currentState) => {
      const currentScreen = getActiveRouteName(currentState);
      const prevScreen = getActiveRouteName(prevState);

      if (prevScreen !== currentScreen) {
        tracker.trackScreenView(currentScreen);
      }
    }}
  />
);

```
こんな感じで、普通のコンポーネントをexportするのではなく、
それを関数化(?)して、export defaultする形になります。

しかし、自分が作っているアプリの構成は
このNavigatorAの上に
RouteNavigatorがいて、
同階層にNavigatorB、NavigatorCがいるという構成でした。
<img width="751" alt="スクリーンショット 2019-01-24 10.06.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/183240/72199fff-ac7a-9cab-3b8f-665efa73b948.png">

すると、Navigator間の遷移（例えば、PageA1から、PageB1に移るなど)が、できなくなってしまったのです！！

## 解決策
結論としては、上位ルーターののnavigationをcallbackでいつでも呼び出せるようにしておけば、解決します。

まず、下のように、Initialというコンポーネントを作ります
<img width="718" alt="スクリーンショット 2019-01-24 10.18.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/183240/e02aa082-46cc-8af2-b983-6ff7eff81d6d.png">


``` Initial.js

・・・
import NavigateListener from './lib/NavigationListener'
・・・

export default class Initial extends Component {
  componentDidMount = async () => {
    const { navigation } = this.props

    // とりあえず、ここのnavigationをcallbackにいれておく。
    NavigateListener.setTopRouting((navigator) => navigation.navigate(navigator))

　　　　　　　// 入れ終わったらNavigatorAに遷移しとく
    navigation.navigate('navigatorA')
  }

  render() {
    return (
      <View></View>
    )
  }
}
```
ここのポイントは、NavigationListenerのsetChangeActionに、callbackを送っていることです。

このNavigationListenerは、

``` NavigationListener.js
class NavigateListener {

　　　 // 関数をセットしておく
  setTopRouting = (callback) => {
    this.topRouting = callback
  }

　　　　// これであらかじめセットしていた関数を発火
  topRouting = (navigato) => {
    this.topRouting(navigator)
  }
}

export default new NavigateListener()

```
というシンプルな作りになっていて、
callbackでsetした関数を、NavigateListener.topRouting('{navigator名}')
で呼び出せるようになっています！

これで、例えば、pageA1から、pageB1に遷移したい場合は、

```NavigatorA.js
・・・
import NavigationListener from './lib/NavigationListener'
・・・

export default class PageA extends React.Component{
・・・
render(){
 return(
  ・・・
   <div onPress={()=> NavigationListener.topRouting('NavigatorB')>NavigatorBへ</div>
　　　　・・・
 )
}
・・・
}

```

こんな感じにすればいけます。

以上です。
ちなみに、このcallbackを使って他のコンポーネントで呼び出すという処理は、
とても便利ですが、多用すると複雑になってしまうので、ほどほどに使うと良いかもです

ちなみに、なぜ () => <NavigatorA　/>の形にしたら
上位Routerを跨げなくなったのかは、わかっておりません笑

教えていただけると幸いです。