Source:  Twitter logo

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; }}
      />
    );
  }
}
43 users liked answer #0dislike answer #043
RamRovi profile pic
RamRovi
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.

30 users liked answer #1dislike answer #130
Yozi profile pic
Yozi

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}
    />
  )
}
13 users liked answer #2dislike answer #213
Aaron profile pic
Aaron

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)}
     />
    )

 }
}
9 users liked answer #3dislike answer #39
Peter Moses profile pic
Peter Moses

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;
8 users liked answer #4dislike answer #48
Nisharg Shah profile pic
Nisharg Shah

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)}
    />
  );
}
3 users liked answer #5dislike answer #53
Kazuto_Ute profile pic
Kazuto_Ute

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.

0 users liked answer #6dislike answer #60
Dipankar Dutta profile pic
Dipankar Dutta

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;
0 users liked answer #7dislike answer #70
Ravi profile pic
Ravi

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;
0 users liked answer #8dislike answer #80
Marat profile pic
Marat

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;
0 users liked answer #9dislike answer #90
Rodrigo Pinheiro profile pic
Rodrigo Pinheiro

Copyright © 2022 QueryThreads

All content on Query Threads is licensed under the Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0).