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.
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
オプションで使用しているもの)
画面遷移
Provider
で navStore
ストアを登録してあるので子孫のコンポーネントでもどこでも
@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;
感想
今のところはこれで問題なくナビゲーションできている。シンプルなので、しばらくこれで使ってみようと思う。問題が起きたら考える。