React Native / React Navigation を Mobx と使う

React Native で個人アプリを開発していて、ナビゲーションどうしようかなと。
デファクトになりつつあるという噂の React Navigation を使ってみた。
使ってみたが、 Mobx と同時に使うにはちょっと工夫が必要だったのでメモしておきます。

React Navigation

Github
https://github.com/react-community/react-navigation
公式ページ
https://reactnavigation.org/

React Mobx

https://github.com/mobxjs/mobx-react

問題点

React Native で登録されている画面は navigation prop を受け取れるのだけど、その子孫のコンポーネントには渡ってこない。バケツリレーで受け渡すのは微妙なので、そこで Mobx の出番となる。

それはそうなんだけど、どうやって navigation prop を Mobx で管理するのかしばらく悩んだ。

アイデア

トップレベルのナビゲーターは Navigation Containers と呼ばれ、このコンポーネントから navigation prop が受け渡される。なので、このトップレベルのナビゲーターを参照できれば良いと思う。

The built in navigators can automatically behave like top-level navigators when the navigation prop is missing. This functionality provides a transparent navigation container, which is where the top-level navigation prop comes from.

https://reactnavigation.org/docs/navigators/navigation-prop

Main.js

jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const MainNavigator = TabNavigator({
  ScreenA:  { screen: ComponentA },
  ScreenB:  { screen: ComponentB },
  ScreenC:  { screen: ComponentC },
});

@observer
class Main extends Component {

  stores: {
    navStore: ObservableNavStore,
  };

  render() {
    return (
      <Provider {...stores} >
        <MainNavigator ref={(nav) => { stores.navStore.setNavigator(nav); }} />
      </Provider>
    );
  }
}

export default Main;

Navigation Container を参照

ref オプションで Navigation Container が参照できるので、それを Mobx の observable なプロパティにセットしている。

ただ、デフォルトの observable では新しいオブジェクトを生成してしまうので、ただの参照にするように ref. モディファイヤーを使う。詳しくは下記のドキュメントで。

observable.ref: Disables automatic observable conversion, just creates an observable reference instead.

modifiers | MobX

ObservableNavStore.js

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// @flow

import { NavigationNavigator, NavigationActions } from 'react-navigation';
import { action, observable } from 'mobx';

class ObservableNavStore {

  @observable.ref navigation: NavigationActions = null;

  @action setNavigator(navigator: NavigationNavigator): void {
    // navigator is NavigationContainer
    // Store `navigation` in navStore for future use in child components.

    // eslint-disable-next-line no-underscore-dangle
    this.navigation = navigator._navigation;
  }

}

export const navStore = new ObservableNavStore();
export default ObservableNavStore;

NavigationNavigator / NavigationActions の import
ObservableNavStore の export は 型チェックのためなので、flow を使っていなければ不要です。

やっていることは navigation という observable なプロパティと setNavigator という関数を定義しているだけです。(MainNavigator の ref オプションで使用しているもの)

画面遷移

ProvidernavStore ストアを登録してあるので子孫のコンポーネントでもどこでも
@inject を使用して navigation prop をピックアップできます。
これで navigate メソッドが使えるので、あとは普通の React Navigation の使い方と同じ。

jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@inject('navStore') @observer
class ScreenB extends Component {

  static defaultProps = {
    navStore: null,
  };

  navigate(id: number) {
    this.props.navStore.navigation.navigate('ScreenC', { id });
  }

  render() {
    const id = 123;
    <View>
      <Button onPress={() => { this.navigate(id); }} title="See More" />
    </View>
  }
}

export default ScreenB;

感想

今のところはこれで問題なくナビゲーションできている。シンプルなので、しばらくこれで使ってみようと思う。問題が起きたら考える。

何かありましたらコメントをどうぞ

comments powered by Disqus

人気の記事

950 Points チリ出身のギタリストが弾くドラゴンボールZがむちゃくちゃかっこいい…
774 Points Wordpress + Heroku + PostgreSQL + Amazon S3 = ¥0 / 無料でサイト運営
700 Points Rubyのチートシート 変数 / クラス / モジュール
524 Points Rubyのチートシート / アクティブサポート
451 Points 紙のデザイナーがウェブ開発できるようになるまでに必要なこと
435 Points Rails / Google Analyticsのデータを使って分析や管理画面のためのグラフをつくる
323 Points RailsとHerokuでノーティフィケーションをプッシュする / PusherとTurbolinksの兼ね合い
222 Points Rails / RSpec テスト書いたことない メンドクサイ(n´Д`)という時のチートシート
193 Points Rails / Ajaxを使って画面遷移しない一時保存機能をつける
193 Points Protractorでスクレイピングしてみた