I am trying to add going back on webview when the android backbutton was pressed and I still couldn't manage to make it work.
This is my code:
<WebView
ref={WEBVIEW_REF}
source={source}
domStorageEnabled={true}
onNavigationStateChange={this.onNavigationStateChange}
/>
componentDidMount() {
BackAndroid.addEventListener('hardwareBackPress', function() {
if(this.state.backButtonEnabled) {
this.refs[WEBVIEW_REF].goBack();
return true;
}
});
};
onNavigationStateChange = (navState) => {
this.setState({
backButtonEnabled: navState.canGoBack,
});
};
With the code above I'm getting the error undefined is not an object this.state.backButtonEnabled (which is set in the state).
Than I just wanted to see if the goBack works so I removed the if statement and than I was getting the error undefined is not an object this.refs[WEBVIEW_REF].
What is the best solution for this?
Wanted to add a full example in case it helps anyone:
import React, { Component } from 'react';
import {
BackHandler,
Platform,
WebView,
} from 'react-native';
class ExampleWebView extends Component {
webView = {
canGoBack: false,
ref: null,
}
onAndroidBackPress = () => {
if (this.webView.canGoBack && this.webView.ref) {
this.webView.ref.goBack();
return true;
}
return false;
}
componentWillMount() {
if (Platform.OS === 'android') {
BackHandler.addEventListener('hardwareBackPress', this.onAndroidBackPress);
}
}
componentWillUnmount() {
if (Platform.OS === 'android') {
BackHandler.removeEventListener('hardwareBackPress');
}
}
render() {
return (
<WebView
source={{ uri: "https://www.google.com" }}
ref={(webView) => { this.webView.ref = webView; }}
onNavigationStateChange={(navState) => { this.webView.canGoBack = navState.canGoBack; }}
/>
);
}
}
class MyComponent extends Component {
state = {};
componentDidMount(){
BackHandler.addEventListener('hardwareBackPress', this.backHandler);
}
componentWillUnmount(){
BackHandler.removeEventListener('hardwareBackPress', this.backHandler);
}
backHandler = () => {
if(this.state.backButtonEnabled) {
this.refs[WEBVIEW_REF].goBack();
return true;
}
}
}
1) Bind your handler 2) Do not forget to removeListener on unmount.
Here's a solution with Typescript and useRef
and useEffect
hooks.
I didn't use canGoBack
, but it seems to work regardless.
import React, { useEffect, useRef } from 'react';
import { BackHandler } from 'react-native';
import WebView from 'react-native-webview';
const WebViewWrapper = (): JSX.Element => {
const webview = useRef<WebView>(null);
const onAndroidBackPress = (): boolean => {
if (webview.current) {
webview.current.goBack();
return true; // prevent default behavior (exit app)
}
return false;
};
useEffect((): (() => void) => {
BackHandler.addEventListener('hardwareBackPress', onAndroidBackPress);
return (): void => {
BackHandler.removeEventListener('hardwareBackPress', onAndroidBackPress);
};
}, []); // Never re-run this effect
return (
<WebView
source={{ uri: 'https://stackoverflow.com' }}
ref={webview}
/>
)
}
This might help someone as the above solutions didn't solve my problem....
import React, { Component } from 'react';
import {
BackHandler,
WebView,
} from 'react-native';
export default class App extends Component {
constructor(props) {
super(props);
this.WEBVIEW_REF = React.createRef();
}
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
}
handleBackButton = ()=>{
this.WEBVIEW_REF.current.goBack();
return true;
}
onNavigationStateChange(navState) {
this.setState({
canGoBack: navState.canGoBack
});
}
render(){
return (
<WebView
source={{ uri: "https://www.cbt.ng" }}
ref={this.WEBVIEW_REF}
onNavigationStateChange={this.onNavigationStateChange.bind(this)}
/>
)
}
}
If you are looking for a functional component
solution.
Note: canGoBack state is not required for performing back operation, it's just to save the current state, you can safely remove it if you want
import React, { useState, useEffect, useRef } from "react"
import { BackHandler, Platform } from "react-native"
import { SafeAreaView } from "react-navigation"
import { WebView } from "react-native-webview"
const Webview = () => {
const webView = useRef(null);
const [canGoBack, setCanGoBack] = useState(false);
useEffect(() => {
if (Platform.OS === 'android') {
BackHandler.addEventListener('hardwareBackPress', HandleBackPressed);
return () => {
BackHandler.removeEventListener('hardwareBackPress', HandleBackPressed);
}
}
}, []); // INITIALIZE ONLY ONCE
const HandleBackPressed = () => {
if (webView.current) {
webView.current.goBack();
return true; // PREVENT DEFAULT BEHAVIOUR (EXITING THE APP)
}
return false;
}
return (
<SafeAreaView>
<WebView
ref={webView}
source={{
uri: "<YOUR_URL>"
}}
onNavigationStateChange={navState => setCanGoBack(navState.canGoBack)}
/>
</SafeAreaView>
)
}
export default Webview;
Unfortunately onNavigationStateChange
does not get triggered for SPA (single page application) sites: https://github.com/react-native-webview/react-native-webview/issues/1510
Use onLoadProgress
instead:
import React, { useRef, useState, useCallback, useEffect } from "react";
import { BackHandler } from "react-native";
import { WebView } from "react-native-webview";
export default function App() {
const webView = useRef();
const [canGoBack, setCanGoBack] = useState(false);
const handleBack = useCallback(() => {
if (canGoBack && webView.current) {
webView.current.goBack();
return true;
}
return false;
}, [canGoBack]);
useEffect(() => {
BackHandler.addEventListener("hardwareBackPress", handleBack);
return () => {
BackHandler.removeEventListener("hardwareBackPress", handleBack);
};
}, [handleBack]);
return (
<WebView
ref={webView}
source={{ uri: "https://my-awesome-spa.com/" }}
onLoadProgress={(event) => setCanGoBack(event.nativeEvent.canGoBack)}
/>
);
}
In case of webview in react native, app exit when pressing the back button of mobile by default. If you want to go the previous page when pressing the back button then you need to implement the "goback" function of react-native webview.
You can see the Step 5 : Handle Mobile Back Button section of this article.
Adding to @Nisharg Shah Answer. For onNavigationStateChange
prop, add below line
onNavigationStateChange={navState => {webView.current.canGoBack = navState.canGoBack}}
and in HandleBackPressed
method check if webView.current.canGoBack
then webView.current.goBack();
else return false
import React, { useState, useEffect, useRef } from "react"
import { BackHandler, Platform } from "react-native"
import { SafeAreaView } from "react-navigation"
import { WebView } from "react-native-webview"
const Webview = () => {
const webView = useRef(null);
const [canGoBack, setCanGoBack] = useState(false);
useEffect(() => {
if (Platform.OS === 'android') {
BackHandler.addEventListener('hardwareBackPress', HandleBackPressed);
return () => {
BackHandler.removeEventListener('hardwareBackPress', HandleBackPressed);
}
}
}, []); // INITIALIZE ONLY ONCE
const HandleBackPressed = () => {
if (webView.current.canGoBack) {
webView.current.goBack();
return true; // PREVENT DEFAULT BEHAVIOUR (EXITING THE APP)
}
return false;
}
return (
<SafeAreaView>
<WebView
ref={webView}
source={{
uri: "<YOUR_URL>"
}}
onNavigationStateChange={navState => {webView.current.canGoBack = navState.canGoBack}} ->> This line is important
/>
</SafeAreaView>
)
}
export default Webview;
All the functional component answers have flaws (like not being able to exit the app with back button when webview can't go back).
This behaves identically to the most upvoted class-based answer:
import React, { useEffect, useRef, useState } from 'react';
import { BackHandler, Platform } from 'react-native';
import { WebView } from 'react-native-webview';
const App = () => {
const webview = useRef<WebView>(null);
const [canGoBack, setCanGoBack] = useState(false);
const onAndroidBackPress = () => {
if (canGoBack && webview.current) {
webview.current.goBack();
return true;
}
return false;
}
useEffect(() => {
if (Platform.OS === 'android') {
BackHandler.addEventListener('hardwareBackPress', onAndroidBackPress);
}
return () => {
BackHandler.removeEventListener('hardwareBackPress', onAndroidBackPress);
}
}, [canGoBack]);
return (
<WebView
source={{uri: 'https://www.google.com'}}
ref={webview}
onNavigationStateChange={(navState) => setCanGoBack(navState.canGoBack)}
/>
);
};
export default App;
Using Functional Component, you have to use the useRef hook for hold the "canGoBack" state. Use onLoadProgress instead onNavigationStateChange, because this method works with SPA`s.
import React, { useEffect, useRef } from 'react';
import { BackHandler, NativeEventSubscription, Platform } from 'react-native';
import { WebView } from 'react-native-webview';
const Screen = () => {
const webViewRef = useRef<WebView>(null);
const canGoBack = useRef<boolean>(false);
const onAndroidBackPress = () => {
if (webViewRef.current && canGoBack.current) {
webViewRef.current?.goBack();
return true;
}
return false;
}
useEffect(() => {
let listener: NativeEventSubscription;
if (Platform.OS === 'android') {
listener = BackHandler.addEventListener(
'hardwareBackPress',
onAndroidBackPress,
);
}
return () => {
if (Platform.OS === 'android') {
listener.remove();
}
};
}, []);
return (
<WebView
ref={webViewRef}
source={{ uri: 'file:///android_asset/page.html' }}
onLoadProgress={({ nativeEvent }) => canGoBack.current = nativeEvent.canGoBack}
/>
);
}
export default Screen;