通知からアプリを開いたときに任意の画面へ遷移する  

# はじめに

タイトルままです。
ReactNativeで通知からアプリを開いたときに、
通知の種類に合わせて任意の画面を表示する方法について紹介します。

# 説明しないこと

- react-navigationの基本的な使い方
- react-native-push-notificationの基本的な使い方

# 使用ライブラリ

- 通知用
    - `@react-native-community/push-notification-ios:^1.8.0`
    - `react-native-push-notification:^7.4.0`
- タブ用
    - `@react-navigation/native:^5.9.4`
    - `react-native-reanimated:^2.2.0`
    - `react-native-gesture-handler:^1.10.3`
    - `react-native-screens:^3.4.0`
    - `react-native-safe-area-context:^3.2.0`
    - `@react-native-community/masked-view:^0.1.11`
    - `@react-navigation/bottom-tabs:^5.11.11`

# コード



```react
import React, {createRef} from 'react';
import {NavigationContainer} from '@react-navigation/native';
import PushNotification from 'react-native-push-notification';

const navigation = createRef().current;

PushNotification.configure({
  onNotification: notification => {
    navigation.navigate(notification.data.screen);
  },
});

PushNotification.popInitialNotification(initialNotification => {
  if (initialNotification) {
    navigation.navigate(initialNotification.data.screen);
  }
});

const scheduleNotification = (screenName) => {
  PushNotification.localNotificationSchedule({
    id: 1,
    title: 'title',
    message: 'message',
    date: new Date(now.setMinutes(now.getMinutes() + 1)), // 1分後
    userInfo: {
      screen: screenName,
    },
    channelId: 'CHANNEL_ID',
  });
}

...

<NavigationContainer ref={navigationRef}>
  <Tab.Navigator initialRouteName="Home">
    <Tab.Screen name="Home" component={HomeScreen} />
    <Tab.Screen name="Tab1" component={Tab1Screen} />
    <Tab.Screen name="Tab2" component={Tab2Screen} />
    <Tab.Screen name="Tab3" component={Tab3Screen} />
    <Tab.Screen name="Tab4" component={Tab4Screen} />
  </Tab.Navigator>
</NavigationContainer>
```

# 解説

```react
const navigationRef = createRef();

...

<NavigationContainer ref={navigationRef}>
  <Tab.Navigator initialRouteName="Home">
    <Tab.Screen name="Home" component={HomeScreen} />
    <Tab.Screen name="Tab1" component={Tab1Screen} />
    ...
  </Tab.Navigator>
</NavigationContainer>
```

通知からアプリを開いたときの処理は、
後述の`PushNotification.configure`で設定するのですが、
[react-native-push-notification](https://github.com/zo0r/react-native-push-notification)のドキュメントでは、
```Must be outside of any component LifeCycle (such as `componentDidMount`).```とあり、
Reactのライフサイクルの影響で重複して設定されることがないよう、
Reactコンポーネント外で実行する必要があります。

そのため、必然的に`react-navigation`の画面遷移はコンポーネント外で行うことになり、
スクリーンに渡ってくる`props`や`useNavigation`を使えません。

そこで、`NavigationContainer`にRefを渡して、navigationにアクセスする方法を取ります。

```react
PushNotification.configure({
  onNotification: notification => {
    navigation.navigate(notification.data.screen);
  },
});
```

`PushNotification.configure`の`onNotification`では、
アプリを開いている状態、もしくはアプリがバックグラウンドで起動している状態で
通知をタップした場合の設定を行うことができます。

コールバック引数の`notification`の`data`には、
通知設定の`userInfo`パラメータに設定したデータが渡ってきます。
遷移先がReactの`state`に依存するような場合には、
通知設定時に遷移先を設定することで、通知の種類に応じた画面を表示することができます。


```javascript
const scheduleNotification = (screenName) => {
  PushNotification.localNotificationSchedule({
    id: 1,
    title: 'title',
    message: 'message',
    date: new Date(now.setMinutes(now.getMinutes() + 1)), // 1分後
    userInfo: {
      screen: screenName,
    },
    channelId: 'CHANNEL_ID',
  });
}
```

アプリが起動している状態（バックグラウンド含む）では、
`PushNotification.configure`で通知をタップしたときの挙動を設定できますが、
アプリが起動していない状態では、
`PushNotification.popInitialNotification`を使用することで、
通知からアプリを開いたときの挙動を設定できます。


```javascript
PushNotification.popInitialNotification(initialNotification => {
  if (initialNotification) {
    navigation.navigate(initialNotification.data.screen);
  }
});
```

`initialNotification`には`PushNotification.configure`の`onNotification`と同じく、
`userInfo`で渡したパラメータが`data`に渡ってきます。



### 参考

https://github.com/zo0r/react-native-push-notification

https://reactnavigation.org/