React Native - open links in browser

前端 未结 13 659
遇见更好的自我
遇见更好的自我 2020-12-04 11:59

Hi i am using react native\'s webview to display some html, i want that whenever a user clicks a link inside that html, it will open the user\'s browser with that link.

相关标签:
13条回答
  • 2020-12-04 12:23

    It's possible to do this in a cross-platform way using the built-in Linking class and react-native-webview-bridge.

    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>`;
    

    With a WebViewBridge component rendered like so:

    <WebViewBridge
      javaScriptEnabled
      renderLoading={() => <ActivityIndicator animating size="large" />}
      source={{ html: generateHtml() }}
      onBridgeMessage={url => Linking.openURL(url)}
    />
    
    0 讨论(0)
  • 2020-12-04 12:34
    Linking.openURL(url).catch(err => console.error('An error occurred', err));
    

    https://facebook.github.io/react-native/docs/linking.html

    0 讨论(0)
  • 2020-12-04 12:34

    I found a way to do this, it is an iOS-only solution though!

    Here is step-by-step instructions:

    1. Link the 'RCTLinkingIOS' library to your project. I used CocoaPods to so.
    2. In your WebView add "onShouldStartLoadWithRequest". For example

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

    3. Add the this.onShouldStartLoadWithRequest function. Return TRUE or FALSE based on whether or not you want to follow the link in your WebView. This is basically the equivalent of the UIWebViewDelegate implementation you would use when you implement it in native code (Swift or Objective-C).

      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. Make sure to import Linking as well in your javascript source:

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

    That should do the trick.

    For more information see the react-native docs: https://facebook.github.io/react-native/docs/linking.html

    0 讨论(0)
  • 2020-12-04 12:37

    This is my solution. As it is not generic, you can improve the injected javascript according to your requirements

    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}
          />
        )
      }
    }
    
    0 讨论(0)
  • 2020-12-04 12:39

    A big issue with using "stopLoading()" is that on Android it disables further taps on any other links from that source page.

    The WebView component is being split out of core RN and into the community's hands. If you use that version instead (https://github.com/react-native-community/react-native-webview), you can use "onShouldStartLoadWithRequest" prop on both iOS and Android, which makes this a lot more elegant.

    Building off of Damien Varron's really helpful answer, here's an example for how you'd leverage that prop to avoid stopLoading that works cross platform:

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

    And here's how you might do it if your source is HTML as opposed to a URI:

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

    As basbase pointed out, you could also do it this way (I've added the about:blank part). I plan to trial and error more to see which holds up best.

    onShouldStartLoadWithRequest={event => {
        if (!/^[data:text, about:blank]/.test(event.url)) {
            Linking.openURL(event.url)
            return false
        }
        return true
    }}
    
    0 讨论(0)
  • 2020-12-04 12:39

    Solution for those who use expo:

    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 is installed via: expo install expo-web-browser

    0 讨论(0)
提交回复
热议问题