LoginSignup
24
16

More than 3 years have passed since last update.

React Navigation で、StackNavigator のイベントを現在の Screen 上でハンドルする

Last updated at Posted at 2018-01-11

概要

React Native + React Navigation で作っている時、React Navigation側で描画される Header に「保存」などのボタンを設置すると思う。

この時、このボタンが押された時の挙動を(React Navigation側ではなく) コンポーネント側から操作したい。
なぜかというと、現在の Screen で描画されている内容は、その Screen でハンドリングすべきであって、 React Navigation にその責務を追わせるべきではない。

公式では NavigationActions を定義して、 Routeのパラメーターに渡すようなことが書いてあるが、実際にこれをやると、アプリが成長した時にスパゲッティになることは確実だろう。

せっかくコンポーネントに閉じ込めているので、「保存」ボタンを押した時の挙動はそのコンポーネントに閉じ込めたい。

同じことが議論されていた。GitHubは神。

Often times if you are editing or creating something, one of the header buttons will be 'Save'. I don't see how this is feasible with this library since there is no access to the state or props of the current screen component. The only (ugly) solution would be to keep the data to be saved outside of the component. Anyone have any suggestions how this pattern can be achieved with this library?

何かを保存したり作成する時、ヘッダーの右側のアクションは「保存」となるはずだ。が、 React Navigation には、現在の Screen を描画している Component にアクセスできないので、これを実現する良い方法が無いっぽい。唯一の(キモい)方法は、データをコンポーネントの外で取得して保存することである。が、これはやりたくない。こういう場合、 React Navigation を使って実現する方法は無いか?

解決策

これこれ。

import React from 'react'
import {
  View,
  Button
} from 'react-native'

class MyScreen extends React.Component {
  static navigationOptions = ({ navigation }) => {
    const { state } = navigation
    return {
      headerTitle: 'New Task',
      headerRight: <Button title="Save" onPress={() => state.params.handleSave()} />,
    }
  }

  saveDetails() {
    alert('saved')
  }


  componentDidMount() {
    this.props.navigation.setParams({ handleSave: () => this.saveDetails() })
  }

  render() {
    return (
      <View />
    )
  }
}

ちなみにFlowを使っている場合は

import { NavigationNavigator } from 'react-navigation'

  static navigationOptions = ({ navigation }: NavigationNavigator) => {
    return {
      headerTitle: navigation.state.params.intern.title
    }
  }

解説

NavigationOptions は、公式ドキュメントでは下記のように StackNavigator のインスタンス作成時に定義するように書かれているが、、、

import { StackNavigator } from 'react-navigation'
import HomeScreen from './components/HomeScreen'
import NewScreen from './components/NewScreen'

const navigator = StackNavigator({
  Home: {
    screen: HomeScreen,
    navigationOptions: ({ navigation }) => {
      const { navigate } = navigation
      return {
        headerTitle: 'Home',
        headerRight: <Button title="New" onPress={() => navigate('NewScreen')} />,
      }
    },
  },

実は Screen Component の static navigationOptions : Function でも定義できる。

import React from 'react'
import { Button } from 'react-native'

class HomeScreen extends React.Component {
  static navigationOptions = ({ navigation }) => {
    const { state } = navigation
    return {
      headerTitle: 'New Task',
      headerRight: <Button title="Save" onPress={() => state.params.handleSave()} />,
    }
  }
}

自動で読み取ってくれるみたい。

よく分かっていないこと

navigation に関数をバインドする必要あるのかなあ

  componentDidMount() {
    this.props.navigation.setParams({ handleSave: () => this.saveDetails() })
  }

これでもいける気が。

      headerRight: <Button title="Save" onPress={() => this.saveDetails()} />,

と思ったらいけなかった。 navigation にバインドしてないといけないみたい。

考えてみたら静的メソッドだから当然だな

24
16
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
16