I have a simple react-native app with a couple of react-native screens and one of them has a webview, this webview is a page that opens other pages on button click like this ( from main webview on click opens another page (2), from Page 2 opens Page 3:
The issue is android's hardware back button, if I'm on the Page 3 and I press that button, I will be redirected to the previous react-native screen, not Page 2. I tried to fix this by implementing the following code:
import React, {useState, useEffect, useRef, useCallback} from 'react';
import {WebView} from 'react-native-webview';
import {BackHandler, Text} from 'react-native';
const MainWebviewPage= ({navigation}) => {
const webViewRef = useRef();
const [canGoBack, setCanGoBack] = useState(false);
const handleBack = useCallback(() => {
if (canGoBack && webViewRef.current) {
webViewRef.current.goBack();
return true;
}
return false;
}, [canGoBack]);
useEffect(() => {
BackHandler.addEventListener('hardwareBackPress', handleBack);
return () => {
BackHandler.removeEventListener('hardwareBackPress', handleBack);
};
}, [handleBack]);
useEffect(() => {
webViewRef.current.reload();
}, []);
return (
<WebView
scalesPageToFit={false}
ref={(ref) => (webViewRef.current = ref)}
source={{uri:url}
onLoadProgress={(event) => setCanGoBack(event.nativeEvent.canGoBack)}
/>
);
};
export default MainWebviewPage;
This code works partially ( if I'm on the Page 3 and I click android's back button, I'll be redirected to the Page 2) but when I'm on the Main webview page and I click android's back button the screen will reload and either I'm still on this page or it's a blank page, in order to get to the previous react-native screen I need to click back button twice. If I remove the above code and I'm on the Page 3 and I click back button - I'll be redirected to the react-native screen ( not Page 2). I can't figure out why the back button behavior is so different.
In handleBackButtonPress
if there is no webview back page just use the navigation.goBack()
and return true
:
const MainWebviewPage= ({navigation}) => {
const webViewRef = useRef();
const [canGoBack, setCanGoBack] = useState(false);
const handleBack = useCallback(() => {
if (canGoBack && webViewRef.current) {
webViewRef.current.goBack();
return true;
} else {
// Add code to navigate to previous app screen:
navigation.goBack(); //<-- use the navigation module here
/**
* When true is returned the event will not be bubbled up
* & no other back action will execute
*/
return true;
}
/**
* Returning false will let the event to bubble up & let other
event listeners
* or the system's default back action to be executed.
*/
return false;
}, [canGoBack]);
useEffect(() => {
BackHandler.addEventListener('hardwareBackPress', handleBack);
return () => {
BackHandler.removeEventListener('hardwareBackPress', handleBack);
};
}, [handleBack]);
useEffect(() => {
webViewRef.current.reload();
}, []);
return (
<WebView
scalesPageToFit={false}
style={{flex: 1, marginTop: 40, width: 400, height: 600}}
ref={ref => (webViewRef.current = ref)}
source={{uri: 'http://stackoverflow.com'}}
onLoadProgress={event => setCanGoBack(event.nativeEvent.canGoBack)}
/>
);
};
Note: I think using
onNavigationStateChange={(navState) => {
setCanGoBack(navState.canGoBack)}
}
could also be a good option to set the canGoBack
Sources for some comments in the above code and more details here: