Native reagieren - Links im Browser öffnen

72

Hallo, ich verwende die Webansicht von react native, um etwas HTML anzuzeigen. Ich möchte, dass jedes Mal, wenn ein Benutzer auf einen Link in diesem HTML klickt, der Browser des Benutzers mit diesem Link geöffnet wird.

ist das möglich?

Bearbeiten 1:

Am Ende habe ich dieses Paket verwendet: npmjs.com/package/react-native-communications, das den Browser öffnet. Ich rufe den Browser auf, um auf onNavigationStateChange zu öffnen, wenn sich die URL ändert.

Die Sache ist jetzt, dass das WebView die Anfrage weiterhin verarbeitet, obwohl ich zum Browser gewechselt bin. Wie kann ich die Anfrage stoppen?

Matan Kadosh
quelle
4
Hallo Matan, hast du am Ende eine Lösung gefunden? Ich bin mit dem gleichen Problem fertig, wäre interessiert, wenn es eine Lösung gibt. Ich weiß, dass wir dies in einer UIWebView mit einer Delegatenimplementierung tun können.
Bocaxica

Antworten:

118

Hier ist eine vollständige Arbeitslösung:

import React, { Component } from 'react';
import { WebView, Linking } from 'react-native';

export default class WebViewThatOpensLinksInNavigator extends Component {
  render() {
    const uri = 'http://stackoverflow.com/questions/35531679/react-native-open-links-in-browser';
    return (
      <WebView
        ref={(ref) => { this.webview = ref; }}
        source={{ uri }}
        onNavigationStateChange={(event) => {
          if (event.url !== uri) {
            this.webview.stopLoading();
            Linking.openURL(event.url);
          }
        }}
      />
    );
  }
}

Es verwendet ein einfaches WebView, fängt jede URL-Änderung ab. Wenn diese URL von der ursprünglichen abweicht, stoppt es das Laden, verhindert Seitenänderungen und öffnet sie stattdessen im OS Navigator.

ios simulator

Damusnet
quelle
10
Dies funktioniert für mich, aber ich empfehle, event.navigationType === 'click'die if-Bedingung hinzuzufügen , um nur Klickereignisse im Browser abzufangen ...
Laurenz Glück
1
Das funktioniert bei mir nur, wenn ich anders referenziere - ref="webview"und dann this.refs.webview.stopLoading().
Urska
@Urska Während dies vorerst funktioniert, wurde dieser Weg ausdrücklich abgelehnt. Siehe facebook.github.io/react/docs/… . Außerdem sollten beide Methoden gleich funktionieren, sodass Sie möglicherweise ein anderes Problem haben.
Damusnet
1
@ LaurenzGlück , die wirklich hilfreich wären, aber die Eigenschaft , nicht sehen , navigationTypeauf der Veranstaltung, nur url, loading, canGoForward, canGoBack, target, und title. Es ist ein wenig schlampig, canGoBackfunktioniert aber, wenn Sie alle Links in der WebView verarbeiten möchten und den Benutzer niemals in der WebView navigieren lassen möchten.
Ckeeney
2
Verwenden Sie onShouldStartLoadWithRequest in der Antwort unten. Android 6 ist
fehlerhaft
33

Ein großes Problem bei der Verwendung von "stopLoading ()" ist, dass unter Android das weitere Tippen auf andere Links von dieser Quellseite deaktiviert wird.

Die WebView-Komponente wird aus dem Kern-RN in die Hände der Community aufgeteilt. Wenn Sie stattdessen diese Version verwenden ( https://github.com/react-native-community/react-native-webview ), können Sie die Requisite "onShouldStartLoadWithRequest" sowohl für iOS als auch für Android verwenden, wodurch dies viel eleganter wird.

Aufbauend auf Damien Varrons wirklich hilfreicher Antwort finden Sie hier ein Beispiel dafür, wie Sie diese Requisite nutzen können, um zu vermeiden, dass stopLoading plattformübergreifend funktioniert:

onShouldStartLoadWithRequest={event => {
    if (event.url !== uri) {
        Linking.openURL(event.url)
        return false
    }
    return true
}}

Und so können Sie es tun, wenn Ihre Quelle HTML im Gegensatz zu einer URI ist:

onShouldStartLoadWithRequest={event => {
    if (event.url.slice(0,4) === 'http') {
        Linking.openURL(event.url)
        return false
    }
    return true
}}

Wie basbase betonte, können Sie dies auch auf diese Weise tun (ich habe das about: blank-Teil hinzugefügt). Ich habe vor, mehr zu probieren, um herauszufinden, was am besten hält.

onShouldStartLoadWithRequest={event => {
    if (!/^[data:text, about:blank]/.test(event.url)) {
        Linking.openURL(event.url)
        return false
    }
    return true
}}
timraybould
quelle
toll, das behebt wirklich viele Fehler. B. das Verhindern von Tippen auf die ursprüngliche Webansicht, oder manchmal zeigen die Zielwebsites eine Überlagerung über der ursprünglichen Webansicht + es öffnet sich ein externer Browser.
Luky
/^[data:text, about:blank]/wird so viele Dinge zusammenbringen, weil []es eine Charakterklasse ist. Sie könnten /^(?:data:text|about:blank)/stattdessen gemeint haben . Siehe diese Antwort .
t.888
15

Das ist meine Lösung. Da es nicht generisch ist, können Sie das injizierte Javascript entsprechend Ihren Anforderungen verbessern

import {
  View,
  WebView,
  Linking,
} from 'react-native';

const injectScript = `
  (function () {
    window.onclick = function(e) {
      e.preventDefault();
      window.postMessage(e.target.href);
      e.stopPropagation()
    }
  }());
`;

class MyWebView extends React.Component {

  onMessage({ nativeEvent }) {
    const data = nativeEvent.data;

    if (data !== undefined && data !== null) {
      Linking.openURL(data);
    }
  }

  render() {
    return (
      <WebView
        source={{ html: this.props.html }}
        injectedJavaScript={injectScript}
        onMessage={this.onMessage}
      />
    )
  }
}
César Parra Moreno
quelle
Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined.Wie haben Sie diesen Fehler überwunden? Ich verstehe das immer wieder mit Ihrer Lösung. Auch hier gibt es ein Problem bei der Reaktion auf natives Repo - github.com/facebook/react-native/issues/10865 .
Kshitij
1
Es wirkt wie ein Zauber. Die Bedingung von data !== "undefined"sollte jedoch in ifBlock hinzugefügt werden .
Efkan
Hat perfekt funktioniert! Vielen Dank.
AndyH
Wie benutzt du das? Wenn ich auf einen Link klicke, zeige ich MyWebView?
A.com
Danke, sehr nett. Aber data! == undefined sollte die Zeichenfolge "undefined" sein, nicht der Typ undefined.
Dror Bar
9

Ich habe einen Weg gefunden, dies zu tun, es ist jedoch eine reine iOS-Lösung!

Hier finden Sie eine schrittweise Anleitung:

  1. Verknüpfen Sie die 'RCTLinkingIOS'Bibliothek mit Ihrem Projekt. Ich habe dazu CocoaPods verwendet.
  2. Fügen Sie in Ihrer WebView "onShouldStartLoadWithRequest" hinzu. Zum Beispiel

    <WebView source={{ html: content }} onShouldStartLoadWithRequest={this.onShouldStartLoadWithRequest} />

  3. Fügen Sie die this.onShouldStartLoadWithRequestFunktion hinzu. Geben Sie TRUE oder FALSE zurück, je nachdem, ob Sie dem Link in Ihrer WebView folgen möchten oder nicht. Dies entspricht im Wesentlichen der UIWebViewDelegate-Implementierung, die Sie verwenden würden, wenn Sie sie in nativem Code (Swift oder Objective-C) implementieren.

    onShouldStartLoadWithRequest = (event) => {
         Linking.canOpenURL(event.url).then(supported => {
             if (supported) {
                 Linking.openURL(event.url);
             } else {
                 console.log('Don\'t know how to open URI: ' + event.url);
             }
             return false
         });
    }
    
  4. Stellen Sie sicher, dass Sie auch Linking in Ihre Javascript-Quelle importieren:

    import { AppRegistry, View, WebView, Linking } from 'react-native';

Das sollte den Trick machen.

Weitere Informationen finden Sie in den reaktionsnativen Dokumenten: https://facebook.github.io/react-native/docs/linking.html

Bocaxica
quelle
Dies hat nicht funktioniert, wenn externe Dienste, z. B. Youtube, eingebettet sind. Es wird beim Laden automatisch wiedergegeben und bringt Sie aus der App heraus. Wenn Sie zurückgehen, werden Sie zu Youtube zurückgebracht, was zu einer Schleife führt.
Naoto Ida
Wie bereits oben erwähnt, existiert onShouldStartLoadWithRequest nur auf iOS facebook.github.io/react-native/docs/…
Kazuki
onShouldStartLoadWithRequest arbeitete mit Chrome für mobile Browser unter Verwendung von RN 6.14.6.
Remy
1

Es ist möglich, dies plattformübergreifend mithilfe der integrierten Linking- Klasse und der reaktionsnativen Webview-Bridge zu tun .

const generateLink = (text, url) => {
  if (url) {
    return `<a href='#' onclick='WebViewBridge.send("${url}"); return false;'>${text}</a>`;
  } else {
    return text;
  }
};

const generateHtml = () => `<!DOCTYPE html><html><body>
  ${generateLink('Link text', 'http://example.com')}
</body></html>`;

Mit einer so WebViewBridgegerenderten Komponente:

<WebViewBridge
  javaScriptEnabled
  renderLoading={() => <ActivityIndicator animating size="large" />}
  source={{ html: generateHtml() }}
  onBridgeMessage={url => Linking.openURL(url)}
/>
Eliot
quelle
1

Lösung für diejenigen, die die Messe nutzen:

import * as WebBrowser from 'expo-web-browser';
import { WebView } from 'react-native-webview';

...

render() {

    return (
        <WebView

            source={{
                uri: uri
            }}

            ref={ (ref) => { this.webview = ref; } }

            onNavigationStateChange={ (event) => {
                if (event.url !== uri) {
                    this.webView.stopLoading();
                    WebBrowser.openBrowserAsync(event.url);
                }
            }}
        />
    );
}

WebBrowser wird installiert über: expo install expo-web-browser

sergei
quelle
0

Sie sollten dies lesen: https://facebook.github.io/react-native/docs/linkingios.html

Es ist möglich, überprüfen Sie einfach den Link oben und Sie sollten alle gut sein!

Andere Links:

https://github.com/ivanph/react-native-webintent

https://www.npmjs.com/package/react-native-browser

James111
quelle
Es scheint schön zu sein, aber mein erstes Problem ist, dass ich nicht weiß, wie ich ein Ereignis abfangen soll, bei dem der Benutzer auf einen Link geklickt hat. Gibt es so ein Ereignis? Muss ich das Ereignis onNavigationStateChange verwenden und dort überprüfen?
Matan Kadosh
Nun, ich gehe davon aus, dass Sie <Text> verlinken möchten, um einen bestimmten Link zu öffnen? Was Sie tun können, ist eine <TouchableHighlight onPress={this.function}> <Text>link goes here...</Text></TouchableHighlight> . Diese Funktion wäre eine Funktion, die die oben genannten npm-Pakete verwendet. @MatanKadosh
James111
Nein, du hast mich falsch verstanden. Ich habe eine Webansicht, die HTML anzeigt. Das HTML enthält möglicherweise Links. Ich möchte, dass der Benutzer, wenn er auf einen Link klickt, in den Browser verschoben wird
Matan Kadosh
1
Am Ende habe ich dieses Paket verwendet: npmjs.com/package/react-native-communications, das den Browser öffnet
Matan Kadosh
Kann ich den Link in einem Popup-Fenster in React-Native (React-Native-Modal) öffnen? Wenn der Benutzer auf die URL klickt, wird er zu einem anderen Fenster außerhalb der App umgeleitet. Also muss ich die geöffnete URL im modalen Fenster anzeigen. Irgendein Vorschlag?
27.
0

Wenn stopLoading()in onNavigationStateChangeAndroid nicht funktioniert,

this.webview.stopLoading();
setTimeOut( () => {
  Linking.openURL(event.url)
}, 1000);

Es hat gut funktioniert.

Redara
quelle
0

Ich hatte viele Probleme mit dieser Lösung, als ich die lokale HTML-Datei nur unter iOS verwendete. Wenn ich beispielsweise Instagram in meine Webansicht eingebettet habe, wird es automatisch geöffnet. Um es zu lösen, habe ich hinzugefügt (nur für iOS):

onShouldStartLoadWithRequest={event => {
    const condition = isIos ? event.navigationType == 'click' : true
    if (event.url.slice(0, 4) === 'http' && condition) {
      Linking.openURL(event.url)
      return false
    }
  return true
}}
Porni
quelle
0
  1. Wenn Sie versuchen, eine Weiterleitungs-URL zu öffnen, und Sie eine Fehlermeldung von einer Android-Absicht wie "err_unknown_url_scheme" erhalten.

  2. Überprüfen Sie, was es zu öffnen versucht, da es möglicherweise Android-Verkehr erkannt und versucht hat, in der entsprechenden App zu öffnen.

  3. Wenn Sie möchten, dass es trotzdem in der Webansicht geöffnet wird und Sie eine Absicht von einer Umleitung zurückerhalten, fangen Sie es in onNavigationStateChange ab und schreiben Sie es neu:

onNavigationStateChange={event => {
  if (event.url !== uri) {
      if (event.url.match('intent://')) {
          SetSearchClick(
              event.url.replace('intent://', 'https://'),
          );
         }
   }
}}

Cparello
quelle
0
<WebView
      source={{uri: this.state.src}}
      ref={(webView) => {
        this.webView.ref = webView;
      }}
      onNavigationStateChange={(navState) => {
        this.webView.canGoBack = navState.canGoBack;
        if (!navState.url.includes('yourdomain.com')) {
          Linking.openURL(navState.url);
          return false;
        }
      }}
      onShouldStartLoadWithRequest={(event) => {
        if (!event.url.includes('yourdomain.com')) {
          Linking.openURL(event.url);
          return false;
        }
        return true;
      }}
      style={{flex: 1}}
      startInLoadingState={true}
      renderLoading={() => {
        return (
          <View style={[styles.container, styles.horizontal]}>
            <ActivityIndicator size="large" color="#0000ff" />
          </View>
        );
      }}
    />

Ich hatte Probleme damit. Mein Anwendungsfall bestand darin, externe Links in einem separaten Browser zu öffnen. Diese Lösung hat bei mir funktioniert.

wtfbrb
quelle