10

I installed the react-navigation package in react-native

I have implemented tab navigation and one of them is implemented in webview format.

My problem is that if I press the back physical button on Android, I go from the app itself to the previous tab, not back from the webview.

I've already applied the back button for the webview on the internet, but I have not done that.

I tried to display the onNavigationStateChange log when debugging, but it was not updated when url was moved after it was loaded at first startup. Here is the code I implemented:

import React from "react";
import {BackHandler} from "react-native";
import {WebView} from "react-native-webview";

class SermonScreen extends React.Component {
    constructor(props) {
        super(props);
    }
    static navigationOptions = {
        header: null
    };

    componentDidMount() {
        BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
    }

    componentWillUnmount() {
        BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
    }

    _onNavigationStateChange(navState) {
        console.log(navState);
        this.setState({
            canGoBack: navState.canGoBack
        });
    }

    handleBackButton = () => {
        console.log(this.state);
        if (this.state.canGoBack === true) {
            this.webView.goBack();
            return true;
        } else {
            return false;
        }
    };

    render() {
        return (
            <WebView
                source={{uri: 'https://m.youtube.com/channel/UCw3kP3qCCF7ZpLUNzm_Q9Xw/videos' }}
                ref={(webView) => this.webView = webView}
                onNavigationStateChange={this._onNavigationStateChange.bind(this)}
            />
        );
    }
}

export default SermonScreen;
gran33
  • 12,421
  • 9
  • 48
  • 76
Jay-flow
  • 167
  • 1
  • 14

3 Answers3

6

Following the official webview documnentation you could try to do this: https://github.com/react-native-community/react-native-webview/blob/master/docs/Guide.md#intercepting-hash-url-changes

In general you were almost there, however the way the YT navigation works made it impossible to be caught via the onNavigationStateChange, that's why we inject a JS code that intercepts these hash changes and posts a message to the parent component, we then catch it inside the onMessage handler and set the state variable properly. Copying the injectedJavaScript and onMessage properties to your example should solve your problem.

I prepared a component for you that seems to do what is needed:

 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow
 */

import React, { Fragment } from "react";
import {
  SafeAreaView,
  StyleSheet,
  ScrollView,
  View,
  Text,
  BackHandler,
  StatusBar
} from "react-native";
import { WebView } from "react-native-webview";
import {
  Header,
  LearnMoreLinks,
  Colors,
  DebugInstructions,
  ReloadInstructions
} from "react-native/Libraries/NewAppScreen";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.startingUrl =
      "https://m.youtube.com/channel/UCw3kP3qCCF7ZpLUNzm_Q9Xw/videos";
    this.handleBackButton = this.handleBackButton.bind(this);
  }

  componentDidMount() {
    BackHandler.addEventListener("hardwareBackPress", this.handleBackButton);
  }

  componentWillUnmount() {
    BackHandler.removeEventListener("hardwareBackPress", this.handleBackButton);
  }

  handleBackButton = () => {
    console.log(this.state);
    const { canGoBack } = this.state;
    if (canGoBack) {
      this.webView.goBack();
      return true;
    } else {
      return false;
    }
  };

  render() {
    return (
      <Fragment>
        <WebView
          source={{ uri: this.startingUrl }}
          style={{ marginTop: 20 }}
          ref={webView => (this.webView = webView)}
          injectedJavaScript={`
          (function() {
            function wrap(fn) {
              return function wrapper() {
                var res = fn.apply(this, arguments);
                window.ReactNativeWebView.postMessage('navigationStateChange');
                return res;
              }
            }

            history.pushState = wrap(history.pushState);
            history.replaceState = wrap(history.replaceState);
            window.addEventListener('popstate', function() {
              window.ReactNativeWebView.postMessage('navigationStateChange');
            });
          })();

          true;
        `}
          onMessage={({ nativeEvent: state }) => {
            if (state.data === "navigationStateChange") {
              // Navigation state updated, can check state.canGoBack, etc.
              this.setState({
                canGoBack: state.canGoBack
              });
            }
          }}
        />
      </Fragment>
    );
  }
}

export default App;

Lukasz
  • 1,807
  • 8
  • 12
  • 1
    I can't really explain how useful this is. Is there any chance of adding this to the React Native Webview project itself so that the navigationStateChange works as expected? – lovubuntu Jun 10 '20 at 06:57
0

The response above was perfect. I set the state true for canGoBack though; I was getting a null error, so:

constructor(props) {
    super(props);
    this.startingUrl = "https://app.vethorcardpag.com.br/GIF/login/0/";
    this.state = {
        canGoBack : true
    }
    this.handleBackButton = this.handleBackButton.bind(this);
}
BSMP
  • 4,596
  • 8
  • 33
  • 44
0

Here is a simple solution using the magic of React's State.

Hope this helps.

import React, { useRef, useState } from 'react'

export default function Component () {

    // This is used to save the reference of your webview, so you can control it
    const webViewRef = useRef(null);

    // This state saves whether your WebView can go back
    const [webViewcanGoBack, setWebViewcanGoBack] = useState(false);

    const goBack = () => {

        // Getting the webview reference
        const webView = webViewRef.current
        
        if (webViewcanGoBack)
            // Do stuff here if your webview can go back 
        else
            // Do stuff here if your webview can't go back 
    }


    return (        
        <WebView
            source={{ uri: `Your URL` }}
            ref={webViewRef}
            javaScriptEnabled={true}
            onLoadProgress={({ nativeEvent }) => {
                // This function is called everytime your web view loads a page
                // and here we change the state of can go back
                setWebViewcanGoBack(nativeEvent.canGoBack)
            }}
        />
    )
}

Original answer https://stackoverflow.com/a/74500469/7823800

Nazim.A
  • 101
  • 1
  • 4