react-native-dropdownalertをreact-native-router-fluxと一緒に使う
2019-02-15

react-native-dropdownalertをreact-native-router-fluxと一緒に使う

react-nativeでエラーメッセージなどを表示したいので、何かライブラリなどを選定していたら、react-native-dropdownalertが割と良さそうなので、react-native-router-fluxと一緒に使ってみました。

コードはGitHubに置いてあります。Snack Expoで実機でもすぐ試せます。

react-native-dropdownalertの基本的な使い方

基本的な使い方はDropDownAlertを表示したViewの一番下にDropdownAlertを配置して、このrefを使ってメッセージを表示させる感じです。
初見、ちょっと癖があって導入に一手間必要そうだなと思いました。

// ...
import DropdownAlert from 'react-native-dropdownalert';

export const One = () => (
  <View style={styles.container}>
    <Text>One</Text>
    <Button
      title="Press to show the Alert"
      onPress={() => this.dropdown.alertWithType('error', 'Error', 'Alert shows inside ;(')}
    />
    // コンポーネントツリーの一番最後に置くこと!
    <DropdownAlert ref={ref => this.dropdown = ref} />
  </View>
);

// ...

結果は。。。

NavBarの内側に表示されてしまいました。
どうやらRoute設定しているコンポーネントの外側に置く必要があるみたいです。

DropdownAlertをRoute設定の外側に置くことは簡単ですが、どうやって他のコンポーネントから操作しようかと頭を悩ませていたところ、こんなIssueがあって助かりました。

DropDownHolderというDropdownAlertのrefの参照を保存するクラスを作成して、これを他のコンポーネントから使う方式です。

シンプル。まさに目から鱗ですね。実際のコードではAlertHelperとしました。

// AlertHelper.js
export class AlertHelper {
  static dropDown;

  static setDropDown(dropDown) {
    this.dropDown = dropDown;
  }

  static show(type, title, message) {
    if (this.dropDown) {
      this.dropDown.alertWithType(type, title, message);
    }
  }
}

App.jsなどのRoute設定の外側にDropdownAlertを置いて、AlertHelpersetDropDownでrefを保存しておきます。

// App.js

// ...

import { AlertHelper } from './pages/AlertHelper';

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

他のコンポーネントではAlertHelpershowを呼び出すことでメッセージを表示することができます。

// Three.js

// ...

import { AlertHelper } from './AlertHelper';

export const Three = () => (
  <View style={styles.container}>
    <Text>Three</Text>
    <Button
      title="Press to show the Info"
      onPress={() => AlertHelper.show('info', 'Info', 'Looks good!!')}
    />
  </View>
);

結果は正しく出ましたね。

AndroidのStatusBarと重なる場合の対処方法

正しく出たと思ったのですが、Androidの実機で試したところ、メッセージがStatusBarと重なってしまっていました。

あー、やだやだ。クロスプラットフォーム。react-nativeの闇を垣間見た気がします。

色々やり方調べていたらDropdownAlertのstyleにStatusBarのheightを追加して回避する方法が良さそうなので試してみました。

// App.js

// ...

export default class App extends React.Component {
  render() {
    return (
      <View style={{ flex: 1 }}>
        <AppRoute />
        <DropdownAlert
+         defaultContainer={{ padding: 8, paddingTop: StatusBar.currentHeight, flexDirection: 'row' }}
          ref={ref => AlertHelper.setDropDown(ref)}
        />
      </View>
    );
  }
}

これで実機でも正しく表示することができました。

おまけ

GitHubにはDropdownAlertを閉じた時にonCloseのコールバックを受け取るコードも書かれています。興味あれば覗いてみてください。

最近、react-nativeを「RN」に省略したい人の気持ちがわかってきた気がします。
自分は頑張って「react-native」と書き続けたいと思います。