Overview
When we create our app using React Native + React Navigation, we often want to place buttons like "Save" in Navigation header.
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.
Top comments (0)