I'm learning navigation in React Native, and can't figure out way how to conditionally use one Back Button. Wanted behaviour would be like this - if user navigate between screens, he can press Go Back button, but if one of the screens contains WebView and he already visited few pages there, user would go back through this webview pages first, then Screens. Hope that makes sense. Something like that: Screen A <- Screen B <- Screen C with WebView <- Webview Page 1 <- Webview Page 2
AppScreen
import React from 'react';
import {Pressable, StyleSheet, Text} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import HomeScreen from './src/screens/HomeScreen';
import WebViewScreen from './src/screens/WebViewScreen';
import ParamsScreen from './src/screens/ParamsScreen';
const Stack = createNativeStackNavigator();
function App(): React.JSX.Element {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={
({navigation}) => {}
}>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{title: 'Customized Header Home'}}
/>
<Stack.Screen
name="WebView Screen"
component={WebViewScreen}
options={({navigation, route}) => ({
title: 'Webview',
headerLeft: () => (
<Pressable onPress={() => navigation.goBack()}>
<Text style={{color: 'blue'}}>Go Back within Screens</Text>
</Pressable>
),
})}
/>
<Stack.Screen name="Params Screen" component={ParamsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({});
export default App;
WebViewScreen
import {StyleSheet, Text, View, ActivityIndicator, Button} from 'react-native';
import React, {useRef, useState} from 'react';
import {WebView} from 'react-native-webview';
const WebViewScreen = ({navigation, route}) => {
const webViewRef = useRef<WebView>(null);
return (
<>
<View style={{flex: 0, alignItems: 'center', justifyContent: 'center'}}>
<Text>WebView Screen</Text>
<Button
title="Go Back within WebView"
onPress={() => webViewRef.current?.goBack()}
/>
</View>
<WebView
source={{uri: 'https://google.com'}}
startInLoadingState
ref={webViewRef}
renderLoading={() => (
<View style={{flex: 1, alignItems: 'center'}}>
<ActivityIndicator size="large" />
</View>
)}
allowsBackForwardNavigationGestures
onNavigationStateChange={navState => {}}
/>
</>
);
};
export default WebViewScreen;
const styles = StyleSheet.create({});
At the moment I've managed to have two separate working buttons, but it would be much better to have one back button in the header. I was playing with Screen options and onNavigationStateChange params, and but kind of got lost there.
I guess I would need some kind of function like this:
const goBack = () => {
if (webViewRef.current) {
webViewRef.current.goBack();
} else {
navigation.goBack();
}
};
but not sure how to integrate it with Stack.Navigator
You can achieve the desired behavior by using a combination of headerLeft
and onNavigationStateChange
in your WebViewScreen
. Here's a modified version of your WebViewScreen
that should help you implement the combined back functionality:
import { StyleSheet, Text, View, ActivityIndicator, Button } from 'react-native';
import React, { useRef } from 'react';
import { WebView } from 'react-native-webview';
const WebViewScreen = ({ navigation, route }) => {
const webViewRef = useRef();
const handleNavigationStateChange = (navState) => {
// Check if WebView can go back
const canGoBack = navState.canGoBack;
// Update the header left button based on WebView navigation
navigation.setOptions({
headerLeft: () => (
<View style={{ marginLeft: 10 }}>
<Button
onPress={() => {
if (canGoBack) {
// If WebView can go back, go back in WebView
webViewRef.current.goBack();
} else {
// If WebView cannot go back, go back in the navigation stack
navigation.goBack();
}
}}
title="Go Back"
color="blue"
/>
</View>
),
});
};
return (
<>
<View style={{ flex: 0, alignItems: 'center', justifyContent: 'center' }}>
<Text>WebView Screen</Text>
</View>
<WebView
source={{ uri: 'https://google.com' }}
startInLoadingState
ref={webViewRef}
renderLoading={() => (
<View style={{ flex: 1, alignItems: 'center' }}>
<ActivityIndicator size="large" />
</View>
)}
allowsBackForwardNavigationGestures
onNavigationStateChange={handleNavigationStateChange}
/>
</>
);
};
export default WebViewScreen;
const styles = StyleSheet.create({});
In this code, the handleNavigationStateChange
function checks if the WebView can go back (canGoBack
). It then updates the headerLeft
button accordingly. If the WebView can go back, the button will trigger webViewRef.current.goBack()
, otherwise, it will trigger navigation.goBack()
to go back in the navigation stack.
This way, you have a single "Go Back" button in the header that handles both WebView and screen navigation.