Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React-Native: Go back on android hardware back button pressed

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?

like image 908
NinetyHH Avatar asked Sep 12 '16 08:09

NinetyHH


People also ask

How would you handle the physical back button presses in Android React Native?

To handle the Android Back Button Press in the React Native we have to register the hardwareBackPress event listener with a callback function, which will be called after pressing the Back Button.

Which callback of modal is fired when hardware back button is pressed React Native?

The addEventListener takes in a callback function as its second parameter. The callback function is triggered when the event we are listening to occurs (hardware back button press).


5 Answers

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; }}
      />
    );
  }
}
like image 179
RamRovi Avatar answered Oct 21 '22 05:10

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.

like image 40
Yozi Avatar answered Oct 21 '22 03:10

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}
    />
  )
}
like image 31
Aaron Avatar answered Oct 21 '22 05:10

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

 }
}
like image 35
Peter Moses Avatar answered Oct 21 '22 04:10

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;
like image 8
Nisharg Shah Avatar answered Oct 21 '22 03:10

Nisharg Shah