React Navigation のモーダルナビゲーターをプログラマブルに閉じたい

React Navigation ではモーダルナビゲーターを使うことが出来る。

これを使えば、スクリーンがモーダルっぽく下から出てくるので、下方向にスワイプすることで閉じることは出来る。

けどこれを行うための API をライブラリが提供してないっぽい。

React Navigation のモーダルナビゲーターを閉じるのむずい

モーダル内で複数スクリーン遷移した後、最後のスクリーンでプログラマブルにモーダル閉じたいって時の方法がわからない。

例えば以下のように、ナビゲーターを定義しているとする。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const MainStack = StackNavigator({
Main: { screen: MainScreen }
})
const ModalStack = StackNavigator({
First: { screen: FirstScreen },
Second: { screen: SecondScreen }
}, {
mode: "screen",
initialRouteName: "First",
});
const Root StackNavigator({
MainStack: { screen: MainStack },
ModalStack: { screen: ModalStack }
}, {
mode: "modal"
})

MainStack から、ModalStack へ遷移すると、モーダルが立ち上げられる。

ModalStack の initialRouteName は”First”なので、FirstScreen が表示される。

ここから、SecondScreen へ遷移したとする。

この SecondScreen にて、モーダルを閉じることが出来ないって話。

探してもライブラリの標準の API にこれを簡単にする方法がないように思う。あったら教えて欲しい。

モーダルをプログラマブルに閉じるための方法

調査してみると、どうやらこの問題に関するイシューを見つけた。

方法としては、モーダル立ち上げの時点で、遷移下画面の key を保持して、this.props.navigation.goBack関数に渡してあげれば良いみたい。

賢い人が、それを簡単に行うための Navigator を HOC として実装してくれてるので、それを紹介する。

DismissableStackNavigator と言う HOC を作る

dismiss と言う関数を呼び出すことで、モーダルを閉じることが出来る DismissableStackNavigator と言う HOC を作る。

これは StackNavigator を拡張したコンポーネントを返す HOC で、StackNavigator 同様に Navigator に登録することができる。

これは、登録してる Screen 内で、this.props.screenProps.dismiss();を呼び出すことで、その Navigator 自体のモーダルを閉じることが出来るもの。

DismissableStackNavigator.js を作成し、以下のように定義する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { StackNavigator } from "react-navigation";
import React, { Component } from 'react';
import { StackNavigator } from 'react-navigation';
export default (routes, options) => {
const StackNav = StackNavigator(routes, options);
return class DismissableStackNav extends Component {
static router = StackNav.router;
render() {
const { state, goBack } = this.props.navigation;
const props = {
...this.props.screenProps,
dismiss: () => goBack(state.key),
};
return (
<StackNav
screenProps={props}
navigation={this.props.navigation}
/>
);
}
}
};

StackNavigator を render する前に、dismissと言う関数を定義して、そいつを一緒にscreenPropsに渡してあげる。

こうすることで、この Navigator に登録した Screen では、this.props.screenProps経由でdismissが呼び出せる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { StackNavigator } from "react-navigation";
import DismissableStackNavigator from "./DismissableStackNavigator"
const MainStack = StackNavigator({
Main: { screen: MainScreen }
})
// ModalStackをDismissableStackNavigatorで作る
const ModalStack = DismissableStackNavigator({
First: { screen: FirstScreen },
Second: { screen: SecondScreen }
}, {
mode: "screen",
initialRouteName: "First",
});
const Root StackNavigator({
MainStack: { screen: MainStack },
ModalStack: { screen: ModalStack }
}, {
mode: "modal"
})

こうすると、FirstScreen でも、SecondScreen でも、this.props.screenProps.dismiss();を呼び出して、MainStack に戻ることが出来る。

もちろん、のちに例えば ThirdScreen などを ModalStack に追加しても、そこでも呼び出せる。便利。

まとめ

そのうち React Navigation の標準 API で提供されそうだけど、今のとこはこれで良いと思う。

参考資料