【ReactNative】react-native-modalで複数モーダルの開閉が同時に行われない問題

react-native-modalはReactNative製アプリにモーダル機能を追加してくれる便利なライブラリです。 しかしながら、複数のモーダルを画面に展開したい場合は少し工夫が必要になります。 今回は、react-native-modalで複数のモーダルの閉じる処理・開く処理を同時に行う方法について解説します。

前提条件

  • react-native@0.61.5
  • react-native-modal@11.5.3

問題となるコード

ABという2種類のモーダルがある画面です。 それぞれ以下の機能を持ちます。

  • AAモーダルを非表示にするボタン
  • BBモーダルを非表示にしてAモーダルを表示するボタン

素直に記載していくと以下のようになるのではないでしょうか? ※下記はTypescriptのコードですが、Javascriptでも大筋は同じだと思います。

import React from 'react';

import {
  View,
  Text,
  FlatList,
  StyleSheet,
  TextStyle,
  TextProps,
  SectionList,
  Image,
  findNodeHandle,
  Alert,
  Button,
} from 'react-native';

import Modal from 'react-native-modal';

export interface Props {}

export interface State {
  dispA: boolean;

  dispB: boolean;
}

export default class ModalTestScreen extends React.Component<Props, State> {
  /**
   * constructor
   * @param props
   */
  constructor(props: Props) {
    super(props);

    this.state = {
      dispA: false,
      dispB: false,
    };
  }

  toggleA = () => {
    this.setState({
      dispA: !this.state.dispA,
    });
  };

  toggleB = () => {
    this.setState({
      dispB: !this.state.dispB,
    });
  };

  toggleAandB = () => {
    this.setState({
      dispB: !this.state.dispB,
    });
  };

  render() {
    const {dispA, dispB} = this.state;

    return (
      <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
        <Button title="Aモーダル表示" onPress={this.toggleA} />
        <Button title="Bモーダル表示" onPress={this.toggleB} />
        <Modal
          isVisible={dispA}
          style={{alignContent: 'center', alignItems: 'center'}}>
          <View
            style={{
              backgroundColor: '#FFF',
              alignContent: 'center',
            }}>
            <Text style={{textAlign: 'center'}}>Aモーダル</Text>
            <Button title="Aモーダル非表示" onPress={this.toggleA} />
          </View>
        </Modal>
        <Modal
          isVisible={dispB}
          style={{alignContent: 'center', alignItems: 'center'}}>
          <View
            style={{
              backgroundColor: '#FFF',

              alignContent: 'center',
            }}>
            <Text style={{textAlign: 'center'}}>Bモーダル</Text>
            <Button
              title="Bモーダル非表示&Aモーダル表示"
              onPress={this.toggleAandB}
            />
          </View>
        </Modal>
      </View>
    );
  }
}

しかし上記のコードだと下記のような挙動になります。

modal-result1

Bモーダルの閉じる処理は成功しましたが、そのままAモーダルを開くことができません。 原因ですが、react-native-modalGitに下記の記述がありました。

I can't show multiple modals one after another

Unfortunately right now react-native doesn't allow multiple modals to be displayed at the same time. This means that, in react-native-modal, if you want to immediately show a new modal after closing one you must first make sure that the modal that your closing has completed its hiding animation by using the onModalHide prop.

意訳ですが、react-native-modalは連続したモーダルの開閉に対応していません。 特定のモーダルが閉じた直後に別なモーダルを開くにはonModalHideプロパティを使用する必要があります。

onModalHideプロパティとは

モーダルが閉じて、画面上の閉じるアニメーションが完了した後に実行されるイベントハンドラです。 モーダルが閉じた際に実行させたい関数を渡します。

修正後のコード

以下が修正後のコードになります。 toggleAandBがなくなり、Bモーダル内のボタン押下時にはtoggleBを、 さらにBモーダルのonModalHideのタイミングでtoggleAを実行することで、 Bモーダルが閉じる→その後にAモーダルを開くを実現しています。

import React from 'react';
import {
  View,
  Text,
  FlatList,
  StyleSheet,
  TextStyle,
  TextProps,
  SectionList,
  Image,
  findNodeHandle,
  Alert,
  Button,
} from 'react-native';

import Modal from 'react-native-modal';

export interface Props {}

export interface State {
  dispA: boolean;
  dispB: boolean;
}

export default class ModalTestScreen extends React.Component<Props, State> {
  /**
   * constructor
   * @param props
   */
  constructor(props: Props) {
    super(props);

    this.state = {
      dispA: false,
      dispB: false,
    };
  }

  toggleA = () => {
    this.setState({
      dispA: !this.state.dispA,
    });
  };

  toggleB = () => {
    this.setState({
      dispB: !this.state.dispB,
    });
  };

  render() {
    const {dispA, dispB} = this.state;

    return (
      <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
        <Button title="Aモーダル表示" onPress={this.toggleA} />
        <Button title="Bモーダル表示" onPress={this.toggleB} />
        <Modal
          isVisible={dispA}
          style={{alignContent: 'center', alignItems: 'center'}}>
          <View
            style={{
              backgroundColor: '#FFF',

              alignContent: 'center',
            }}>
            <Text style={{textAlign: 'center'}}>Aモーダル</Text>
            <Button title="Aモーダル非表示" onPress={this.toggleA} />
          </View>
        </Modal>
        <Modal
          onModalHide={this.toggleA}
          isVisible={dispB}
          style={{alignContent: 'center', alignItems: 'center'}}>
          <View
            style={{
              backgroundColor: '#FFF',

              alignContent: 'center',
            }}>
            <Text style={{textAlign: 'center'}}>Bモーダル</Text>
            <Button
              title="Bモーダル非表示&Aモーダル表示"
              onPress={this.toggleB}
            />
          </View>
        </Modal>
      </View>
    );
  }
}

以下、実行結果です。

modal-result2

まとめ

今回は、react-native-modalを利用して際に、複数モーダルの開閉を連続して行いたい場合の記述方法についてご紹介しました。 機能が複雑なアプリになるにつれ、意図しないところで連続してモーダルを呼び出すような処理の流れになることはしばしば見受けられます(そもそもそういったフローごと見直すのも手だと思います)。 そういった場合に、今回の記事が参考になったら幸いです。

ReactNativeの学習方法

react-nativeの学習には、大元であるreactの知識もさることながら、iOSAndroidといったネイティブの知識も必要になってきます。

いずれかのバックボーンを元々持っている人にとっては取り掛かりやすいかもしれませんが、Javascriptをようやく覚えて「さあスマホアプリを作ろう!」と思い立った方にはなかなか高いハードルです。

そういった場合、書籍や動画といった体系的にまとめられた教材を使用するのが有効です。

ひととおりの基本知識を蓄えてから各種ライブラリやreduxといったフレームワークを利用することになるため、何は無くとも基礎固めをしていきましょう。

以下に管理人が実際に学習した教材を紹介します。

React Native -JavaScriptによるiOS/Androidアプリ開発の実践

特に初心者〜中級者にオススメの書籍です。 ReactNativeの体系的な知識をぎゅっと詰め込んだ一冊となっており、 お題目に沿って進めていくことでTODOアプリを開発することができます。

※ページ量が嵩むため、Kindle版の購入をオススメします。

SNSでシェアする