両サイドからDrawerを出す


## やりたいこと

* 画面の両側からDrawerを出す（普通は左から）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55188/dc6a2649-a53a-5dda-a333-811f2d0fdb54.gif" style="width:300px"/>


## 前提

* React Native利用
* React-Navigation(V4)利用

## 注意点

2019年9月12日現在、expoでReact-Navigationを利用しようとするとインストールにいろいろと注意が必要。
詳しくは[こちら](https://qiita.com/zaburo/items/e4584ff62ffc70d1cc4e)を。

## ファイルの準備

App.jsと同じ階層にscreensというフォルダを作り、その中に、以下のファイルを作成する。

* Home.js → 普通のコンテンツ表示
* Left.js → 左Drawerとするページ（カスタム前提）
* Right.js → 右Drawerとするページ（カスタム前提）

## 各ファイルの準備

### Home.js

ただ、Homeと表示するだけ。

```jsx:Home.js
import React from 'react';
import { View, Text, Button } from 'react-native';

class Home extends React.Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>Home</Text>
            </View>
        );
    }
}

export default Home;
```

### Left.js

ただ、Leftと表示するだけ。基本Homeと同じ。

```jsx:Left.js
import React from 'react';
import { View, Text, Button } from 'react-native';

class Home extends React.Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>Left</Text>
            </View>
        );
    }
}

export default Home;
```

### Right.js

Rightと表示するだけ。

```jsx:Right.js
import React from 'react';
import { View, Text, Button } from 'react-native';

class Right extends React.Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>Right</Text>
            </View>
        );
    }
}

export default Right;
```

## 実装（基本形）

### App.js

ここで出し方を定義する。
ややこしいのは、最後の表示できるコンポーネントは１つだけなので、Drawerの入れ子を作る必要があります。
もっともシンプルな（つもりの）例が下。


```jsx:App.js
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { createAppContainer } from 'react-navigation';
import { createDrawerNavigator, DrawerActions } from 'react-navigation-drawer';
import { createStackNavigator } from 'react-navigation-stack';
import Icon from 'react-native-vector-icons/FontAwesome';

//画面読み込み
import Home from './screens/Home';
import Left from './screens/Left';
import Right from './screens/Right';


//左Drawer
const LeftDrawer = createDrawerNavigator(
    {
        LEFT: Home,
    },
    {
        contentComponent: Left,
        drawerPosition: 'left',
    }
);

//右Drawer
const RightDrawer = createDrawerNavigator(
    {
        stack: LeftDrawer,
    },
    {
        drawerPosition: 'right',
        contentComponent: Right,
    }
);


//AppContainer作成
const AppContainer = createAppContainer(RightDrawer);

export default class App extends React.Component {
    render() {
        return (
            <View style={{ flex: 1 }}>
                <AppContainer />
            </View>
        );
    }
}

```

## 応用１：ヘッダにアイコンを設置し、そこから（も）Drawerを開く

### 完成イメージ

ヘッダつけて、そこからDrawerOpen。コンテンツ中のボタンからもOpen。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55188/d87759cc-e9f0-f5b1-a4e9-e59fa683613e.gif" style="width:300px"/>


### 解説

Drawerをボタンクリック等から開く場合、openDrawer()が使えるのですが、Drawerが複数ある場合うまく機能しません。
そこで、getCustomActionCreatorsを利用して、各DrawerをOpenするためのカスタムメソッドを定義し、それを呼び出す形をとります。

なお、アイコンを表示できるようvector-iconsをインストールしておきます。

```bash
npm install --save react-native-vector-icons
```

実装します。

### App.js

```jsx:App.js
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { createAppContainer } from 'react-navigation';
import { createDrawerNavigator, DrawerActions } from 'react-navigation-drawer';
import { createStackNavigator } from 'react-navigation-stack';
import Icon from 'react-native-vector-icons/FontAwesome';

//画面読み込み
import Home from './screens/Home';
import Left from './screens/Left';
import Right from './screens/Right';

//HomeをStackでラップ（ヘッダを表示するため）
const Stack = createStackNavigator(
    {
        Home: {
            screen: Home,
            navigationOptions: ({ navigation }) => ({
                headerLeft: (<Icon name="bars" size={24} onPress={() => navigation.toggleLeftDrawer()} style={{ paddingLeft: 20 }} />),
                headerRight: (<Icon name="cog" size={24} onPress={() => navigation.toggleRightDrawer()} style={{ paddingRight: 20 }} />),
            })
        }
    }
);

//左Drawer
const LeftDrawer = createDrawerNavigator(
    {
        LEFT: Stack,
    },
    {
        contentComponent: Left,
        drawerPosition: 'left',
        getCustomActionCreators: (route, stateKey) => {
            // console.log("LEFT" + stateKey);
            return {
                toggleLeftDrawer: () => DrawerActions.toggleDrawer({ key: stateKey }),
            };
        },
    }
);

//右Drawer
const RightDrawer = createDrawerNavigator(
    {
        stack: LeftDrawer,
    },
    {
        drawerPosition: 'right',
        contentComponent: Right,
        getCustomActionCreators: (route, stateKey) => {
            // console.log("RIGHT" + stateKey)
            return {
                toggleRightDrawer: () => DrawerActions.toggleDrawer({ key: stateKey }),
            };
        },
    }
);


//AppContainer作成
const AppContainer = createAppContainer(RightDrawer);

export default class App extends React.Component {
    render() {
        return (
            <View style={{ flex: 1 }}>
                <AppContainer />
            </View>
        );
    }
}
```

ヘッダーアイコンだけでなく、Homeに設置したボタンからも呼び出してみます。

```jsx:Home.js
import React from 'react';
import { View, Text, Button } from 'react-native';

class Home extends React.Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>Home</Text>
                <Button
                    title="open left drawer"
                    onPress={() => this.props.navigation.toggleLeftDrawer()}
                />
                <Button
                    title="open right drawer"
                    onPress={() => this.props.navigation.toggleRightDrawer()}
                />
            </View>
        );
    }
}

export default Home;
```

## 応用２：DrawerがHearの下に表示されるようにしてみる（バグ？あり）

応用１では、DrawerがStackNavigatorのヘッダの上に来ます（これはレンダリングの順番に依存）。
場合により、ヘッダはそのまま表示したいときもあるので、その場合のコードを書いてます。

App.jsにおける各種Navigatorの生成・読み込み順序を変更すればいいのですが、下記のコードはバグがあり、なぜかヘッダの左ボタンがエラーとなり、継続調査中です（分かる人いれば教えてください）。

```jsx:App.js
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { createAppContainer } from 'react-navigation';
import { createDrawerNavigator, DrawerActions } from 'react-navigation-drawer';
import { createStackNavigator } from 'react-navigation-stack';
import Icon from 'react-native-vector-icons/FontAwesome';

//画面読み込み
import Home from './screens/Home';
import Left from './screens/Left';
import Right from './screens/Right';


//左Drawer
const LeftDrawer = createDrawerNavigator(
    {
        left: Home,
    },
    {
        contentComponent: Left,
        drawerPosition: 'left',
        getCustomActionCreators: (route, stateKey) => {
            console.log("LEFT" + stateKey);
            return {
                toggleLeftDrawer: () => DrawerActions.toggleDrawer({ key: stateKey }),
            };
        },
    }
);

//右Drawer
const RightDrawer = createDrawerNavigator(
    {
        right: LeftDrawer,
    },
    {
        drawerPosition: 'right',
        contentComponent: Right,
        getCustomActionCreators: (route, stateKey) => {
            console.log("RIGHT" + stateKey)
            return {
                toggleRightDrawer: () => DrawerActions.toggleDrawer({ key: stateKey }),
            };
        },
    }
);


const Stack = createStackNavigator(
    {
        Home: {
            screen: RightDrawer,
            navigationOptions: ({ navigation }) => ({
                headerLeft: (<Icon name="bars" size={24} onPress={() => navigation.toggleLeftDrawer()} style={{ paddingLeft: 20 }} />),
                headerRight: (<Icon name="cog" size={24} onPress={navigation.toggleRightDrawer} style={{ paddingRight: 20 }} />),
            })
        }
    }
);


//AppContainer作成
const AppContainer = createAppContainer(Stack);

export default class App extends React.Component {
    render() {
        return (
            <View style={{ flex: 1 }}>
                <AppContainer />
            </View>
        );
    }
}
```

### バグ？（継続調査中）

なぜか、ヘッダー左のボタン（ここではハンバーガーアイコン）をクリックするとnavigation.toggleLeftDrawer is not a function.エラーがでます。今わかってる現象は、

* navigationの中身をみてもどうやらtoggleLeftDrawerメソッドが追加されていない。
* が、this.props.navigation.toggleLeftDrawer()は実行可能。

読み込みのタイミング次第なのかな？と思いつつ。なぜそうなるか不明。

とりあえず、Drawerが最前で表示されることを仕様にするしかない。。。