Handle Stack Navigator's event in current screen with React Navigation
Tags: javascript, reactnative
Published: 2018-04-11 15:29:13

---
title: Handle Stack Navigator's event in current screen with React Navigation
published: true
description: 
tags: JavaScript, ReactNative
---

# Overview

When we create our app using React Native + React Navigation, we often want to place buttons like "Save" in Navigation header.

![image.png](https://qiita-image-store.s3.amazonaws.com/0/103885/09a71c59-9726-cf05-1491-d62f9e4da27a.png)

In this situation, we may want to handle the press event in Screen Component, not in Navigation Action.
This is because React Navigation should focus on handling navigation so events and logic on the screen should be handled by the screen Component.

The official document says define NavigationActions and set Route parameters is a good way, but if we are in this way it obviously cause spaghetti code.

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

We capsule data in the screen component, we would like to put the logic in it.

Some people think the same thing:

https://github.com/react-navigation/react-navigation/issues/145

> 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?

# Solution

In the above issue, I found the following code works well.

```js
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()} />,
    }
  }

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

  saveDetails() {
    alert('saved')
  }

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

If we use Flow, we can annotate the type `NavigationNavigator`.

```js
import { NavigationNavigator } from 'react-navigation'

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

# Description

In the official docs, we should define `NavigationOptions` when creating the instance of `StackNavigator`,

```js
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')} />,
      }
    },
  },
```

In fact, we can also define  `static navigationOptions: NavigationNavigator => void` in screen Component.

```js
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()} />,
    }
  }
}
```

`navigationOptions` is a static function, so we should pass the action to navigation state.

The demerit is we should write almost useless code just to define navigation action.

# Conclusion

If you are suffering from React Navigation's navigation header, you should try to create your own header.
Its navigation header is easy to use lightly, but as your app grows it is hard to customize header.

In my project, React Navigation was always my concern.
Do not rely highly on React Navigation, and you can do well with your own logic.