react-native-router-fluxのonRightをカスタマイズする(part2)
2019-08-27

react-native-router-fluxのonRightをカスタマイズする(part2)

react-native-router-fluxの小ネタです。

前回の話はこちら。

前回はアプリのNavBarの右側にあるボタン(以下、RightButton)をクリックした時に、次の画面にpropsを渡したいようなユースケースを想定していましたが、今回はRightButtonをpropsの条件で出し分けしたいと思います。

次のような手順で実現できそうです。

  1. PageComponent内部でRightButtonを出す
  2. RightButtonをタップした時に、Component内部のタップハンドラ(private function)を実行する
  3. 外部からのpropsの値でRightButtonを出し分けする

コードは全てTypeScriptです。

PageComponent内部でRightButtonを出す

RightButtonの追加方法はいくつかあるのですが、今回はPageComponentにstaticなnavigationOptionsプロパティを追加して、この中でRightButton用のComponentを定義します。

この方法というものがいくつかあって、それぞれ期待する動きをしないので辛いです。

// ...
import { NavigationScreenProps } from 'react-navigation';

class Page extends React.Component<State, Props> {
  static navigationOptions = ({ navigation }: NavigationScreenProps) => {
    return {
      // ここにRightButtonのComponentを渡す
      headerRight: <Button title="+1" />
    };
  }

  render() {
    // ...
  }
}

navigationOptionsで渡されている関数のnavigationreact-navigationNavigationScreenPropsの型を使います。これは、react-native-router-fluxreact-navigationをベースとして拡張しているためです。

RightButtonをタップした時に、Component内部のハンドラを実行する

RightButtonをタップした時に、Component内部のハンドラを実行するにはButtonComponentのonPressを使えば可能です。しかし、staticプロパティ内部からPageComponent内のprivate functionを呼び出すにはひと工夫必要でした。

staticプロパティ内部からPageComponentのfunctionを実行するには、propsのnavigationを通じてfunctionの参照を渡すことで可能となります。
componentDidMountの中でpropsのnavigation.setParamを使うことで参照を渡すことができます。

// ...

import { NavigationScreenProp, NavigationScreenProps } from 'react-navigation';

export interface Props {
  navigation: NavigationScreenProp<any, any>;
}

class Page extends React.Component<State, Props> {
  static navigationOptions = ({ navigation }: NavigationScreenProps) => {
    return {
      headerRight: <Button title="+1" />
    };
  }

  componentDidMount() {
    // ここでハンドラの参照をセットする
    this.props.navigation.setParams({ onRight: this.onRight });
  }

  // ...
}

渡されたハンドラの参照を次のようにnavigationOptionsで使うことができます。

// ...

class Page extends React.Component<State, Props> {
  static navigationOptions = ({ navigation }: NavigationScreenProps) => {
    return {
-      headerRight: <Button title="+1" />
+      headerRight: <Button title="+1" onPress={navigation.getParam('onRight')} />
    };
  }

// ...

これでRightButtonをタップした時にonRightが呼び出されるようになります。

外部からのpropsの値でRightButtonを出し分けする

外部からのpropsも上の方法と同様にnavigation.setParamgetParamを使って実現します。

今回はpropsでhasButtonが渡されるとします。componentDidMountの中で再びnavigation.setParamを使います。

// ...

  componentDidMount() {
    // ここでハンドラの参照をセットする
    this.props.navigation.setParams({ onRight: this.onRight });
+   this.props.navigation.setParams({ hasButton: this.props.hasButton });
  }

// ...

この値をnavigationOptionsの中で使います。

// ...

class Page extends React.Component<State, Props> {
  static navigationOptions = ({ navigation }: NavigationScreenProps) => {
    return {
-     headerRight: <Button title="+1" onPress={navigation.getParam('onRight')} />
+     headerRight: navigation.getParam('hasButton') ? 
+       <Button title="+1" onPress={navigation.getParam('onRight')} /> :
+       undefined
    };
  }

// ...

これでprops経由でRightButtonを出し分けすることができるようになりました。

。。。大変。

以上