React Navigationで、特定の画面でボトムタブを非表示にする

## やりたいこと

[React Navigation](https://reactnavigation.org/) で [createBottomTabNavigator](https://reactnavigation.org/docs/en/bottom-tab-navigator.html)を使って画面をタブ表示していて、かつタブ内に [createStackNavigator](https://reactnavigation.org/docs/en/stack-navigator.html)でスタックナビゲータを入れている時。

この**スタックの最初のページ以外はタブバーを非表示にしたい**ということ、あると思います。

イメージでいうと、こんな感じです。

```javascript

import {createStackNavigator, createBottomTabNavigator} from 'react-navigation'

const TabAStack =  createStackNavigator({
  ScreenA1,
  ScreenA2, //タブバー非表示にしたい
  ScreenA3, //タブバー非表示にしたい
});

export default createBottomTabNavigator({
  TabAStack,
  TabB,
  TabC
});
```

今回は、その方法を紹介します。

## 前提
### React Navigationのバージョン
今回試したReact Navigationのバージョンは以下です。

```
"react-navigation": "^2.9.1"
```

## 実現方法

stackNavigatorの` navigationOptions ` で `tabBarVisible` を指定してあげるだけです。

「どの画面に対して」を指定する場合には、少なくとも以下の2通りがあります。

1. スタックのindexで切り分ける場合
2. 画面の名前で切り分ける場合

### スタックのindexで切り分ける場合

`navigation.state.index`でスタックの何番目を表示しているのか分かるので、スタックの一番最初(タブ内に最初に表示される画面)だけタブバーを表示したい場合は以下のようにします。

```javascript
stackNavigator.navigationOptions = ({ navigation }) => ({
  //スタックの一番最初(ScreenA1)だけタブバーを表示する
  tabBarVisible: navigation.state.index === 0,
});
```
全体

```javascript

import {createStackNavigator, createBottomTabNavigator} from 'react-navigation'

const TabAStack =  createStackNavigator({
  ScreenA1,
  ScreenA2, //タブバー非表示にしたい
  ScreenA3, //タブバー非表示にしたい
});

TabAStack.navigationOptions = ({ navigation }) => ({
  //スタックの一番最初だけタブバーを表示する
  tabBarVisible: navigation.state.index === 0,
});

export default createBottomTabNavigator({
  TabAStack,
  TabB,
  TabC
});
```

### 画面の名前で切り分ける場合
```javascript
stackNavigator.navigationOptions = ({ navigation }) => ({
  const { index } = navigation.state;
  //　または、 const index = navigation.state.index;
  const { routeName } = navigation.state.routes[index];
  // または、 const  routeName  = navigation.state.routes[index].routeName;
  //ScreenA1でのみタブバーを表示する
  tabBarVisible = routeName === ScreenA1
});
```

### (余談)navigation.stateの中身
上記の `navigation.state`ですが、
ScreenA1=>ScreenA2=>ScreenA3
と遷移した時の中身は以下のようになっています。

`index`で現在表示している画面のindexが分かり、また `routes`の配列にはスタックの履歴(ScreenA1, ScreenA2, ScreenA3)が入っています。
そのため、`routes[index]`で、現在の画面の情報を取得できます。

```javascript
{
"key":"TabAStack",
"isTransitioning":false,
"index":2, //スタックの何番目か
"routes":[
    {
    "routeName":"ScreenA1", //ScreenA1の画面名
    "key":"id-1536728255349-2"
    },
    {
    "params":{}, //遷移時にパラメータを指定するとその情報がここに入ります。ヘッダーのタイトルとか。
    "routeName":"ScreenA2", //ScreenA2の画面名
    "key":"id-1536728255349-9"
    },
    {
    "routeName":"ScreenA3", //ScreenA3の画面名
    "key":"id-1536728255349-10"
    }
    ],
"routeName":"TabAStack"}
```
ちなみに、一個前の画面(ScreenA2)に戻るとこうなります。

```javascript
{
"key":"TabAStack",
"isTransitioning":false,
"index":1, //スタックの何番目か
"routes":[
    {
    "routeName":"ScreenA1", //ScreenA1の画面名
    "key":"id-1536728255349-2"
    },
    {
    "params":{}, //遷移時にパラメータを指定するとその情報がここに入ります。ヘッダーのタイトルとか。
    "routeName":"ScreenA2", //ScreenA2の画面名
    "key":"id-1536728255349-9"
    },
    ],
"routeName":"TabAStack"}
```
ScreenA3が破棄されたので、 `routes`からその情報が消えました。

###(余談）各画面のnavigationOptionsで指定することはできない
これまでの例では、stackNavigator全体の`navigationOptions`でタブバーの表示/非表示を切り替えていましたが、 以下のように、stackNavigator全体ではなく各画面のnavigationOptionsを指定する方法では上手く行きません。

```javascript
const TabAStack =  createStackNavigator({
  ScreenA1: {
    screen: ScreenA1,
    navigationOptions: { tabBarVisible: true },
  },
    ScreenA2: {
    screen: ScreenA2,
    navigationOptions: { tabBarVisible: false },//タブバー非表示にしたい
  },
    ScreenA3: {
    screen: ScreenA3,
    navigationOptions: { tabBarVisible: false },//タブバー非表示にしたい
  },
});
```
なぜなら、この時のnavigationOptionsはTabAStackにしかかかっておらず、bottomNavigatorの方にはかかっていないためです。

また、以下も上手く行きませんでした。

```javascript
const TabAStack =  createStackNavigator({
  ScreenA1,
  ScreenA2, //タブバー非表示にしたい
  ScreenA3, //タブバー非表示にしたい
},{
navigationOptions : ({ navigation }) => ({
  //スタックの一番最初だけタブバーを表示する
  tabBarVisible: navigation.state.index === 0,
});
});
```

### そもそも、タブ毎にstackNavigatorに突っ込まない方が良いらしい

この記事を書いている途中で気が付いたのですが、
[React Navigationのこの箇所](https://reactnavigation.org/docs/en/navigation-options-resolution.html#a-tab-navigator-contains-a-stack-and-you-want-to-hide-the-tab-bar-on-specific-screens)に、以下のようにありました
> Another option here would be to add another stack navigator as a parent of the tab navigator, and put the details screen there. This is recommended.

タブバーのなかにstackNavigatorをつっこむのではなく、tabNavigatorとそれ以外のタブバー要らない画面をstackNavigatorにつっこむと。
そして、  **This is recommended.** とあります。

```javascript

import {createStackNavigator, createBottomTabNavigator} from 'react-navigation'

const TabNavigator = createBottomTabNavigator({
  ScreenA1,
  TabB,
  TabC
});

export default createStackNavigator({
  TabNavigator,
  ScreenA2, //タブナビゲータの外なのでタブバー表示されない
  ScreenA3, //同上
})
```

確かに、スッキリしますね。

ただ、TabBやTabCにも同じようにタブバー非表示の導線がある場合、この方法だとごっちゃになる気がしました。

```javascript
const TabNavigator = createBottomTabNavigator({
  ScreenA1,
  ScreenB1,
  ScreenC1,
});

export default createStackNavigator({
  TabNavigator,
  ScreenA2, 
  ScreenA3, 
  ScreenB2, 
  ScreenB3,
  ScreenC2, 
  ScreenC3,
})
```

こういう構成のアプリはあまり無いのかもしれないですが、個人的には、それぞれstackNavigatorで囲ってあげる方が、「ScreenA2とScreenA3はScreenA1からしか遷移しない」など体的な導線が分かりやすいのかなぁと思いました。


## 参考URL
以下を参考にしました。
- [React Navigation - createBottomTabNavigator](https://reactnavigation.org/docs/en/bottom-tab-navigator.html)
- [React Navigation - A tab navigator contains a stack and you want to hide the tab bar on specific screens](https://reactnavigation.org/docs/en/navigation-options-resolution.html#a-tab-navigator-contains-a-stack-and-you-want-to-hide-the-tab-bar-on-specific-screens)
- [Hide tabBar from stackNavigator](https://github.com/react-navigation/react-navigation/issues/581)