Tech Blog - mixross

react-navigationのHeaderでsetState

このエントリーをはてなブックマークに追加
LINE

環境

react-navigation(4.0.10)

概要

ルーティングライブラリにreact-navigationを使用し、ヘッダーの表示もreact-navigationの機能で行っていたところ、ヘッダーにボタンを設置してタップ時にsetStateをすることが簡単にできずに困った。


簡単のために1画面だけのアプリだとすると

  • index.js (エントリーポイント)
  • app/router.js (ルーティング)
  • app/screens/home-screen.js (ホーム画面)

という3つのファイルに分割していて各ソースはこのようになっている。


index.js
import {AppRegistry} from 'react-native'
import App from './app/router'
import {name as appName} from './app.json'

AppRegistry.registerComponent(appName, () => App)
router.js
import React, { Component } from 'react'
import { createAppContainer } from 'react-navigation'
import { createStackNavigator } from 'react-navigation-stack'
import HomeScreen from './screens/home-screen'

const AppNavigator = createStackNavigator(
  {
    home: {
      screen: HomeScreen,
    },
  },
  {
    initialRouteName: 'home'
  }
)

const Router = createAppContainer(AppNavigator)

class App extends Component {
  render() {
    return (
      <Router />
    )
  }
}

export default App
home-screen.js
import React, { Component } from 'react'
import Button from './components/button'

class HomeScreen extends Component {

  static navigationOptions = ({navigation}) => {
    return {
      title: 'Home',
      headerRight:(
        <Button
          onPress={() => console.log('button is clicked')}        />
      ),
    }
  }

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

今回はButtonのonPressでsetStateを呼びたかったわけだが、setStateに限らずHomeScreenのインスタンス関数を呼ぶことが出来ない。
navigationOptionsがstaticなため、this.xxx という呼び出し自体が不可能というわけ。

解決策

navigationオブジェクトにパラメータを持たせることができるのでsetState関数のラッパー関数を登録してあげることで解決した。

home-screen.js
import React, { Component } from 'react'
import Button from './components/button'

class HomeScreen extends Component {

  static navigationOptions = ({navigation}) => {
    return {
      title: 'Home',
      headerRight:(
        <Button
          onPress={() => navigation.state.params.setState({hoge: 'hoge'})}        />
      ),
    }
  }

  componentDidMount() {    this.props.navigation.setParams({      setState: (state = {}, callback = () => {}) => this.setState(state, callback),    })  }
  render() {
    return (
      <View><Text>Sample</Text></View>
    )
  }
}

今回は単純にsetStateだけを呼びたかったからこうしたが、
よくよく考えるとボタンタップ時の処理を丸っと1つの関数で定義してこれを呼び出すだけのほうが分かりやすかったかも。
setState以外にもHomeScreenの関数を呼びたいこともあるだろうし。

動作確認していないけどこんな感じで。。

    (省略)
          onPress={() => navigation.state.params.setState({hoge: 'hoge'})}
    (省略)
  componentDidMount() {
    this.props.navigation.setParams({onPressButton: () => this.onPressButton()})
  }
    (省略)
  onPressButton() {
    this.setState({hoge: 'hoge'})
  }
RSS