Handle Stack Navigator's event in current screen with React Navigation

April 12, 2018 0 Comments

Handle Stack Navigator's event in current screen with React Navigation

 

 

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

image.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.

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.

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,

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.

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.


Tag cloud