How to detect when a page is in loading state in webview react native

1.5k Views Asked by At

I am building an application with React Native and I am trying to implement payment system with paystack. I actually would like to use webview to display the pagstack payment gateway.

Everything is working well until I tested the cancel button which wasn't working. I had to use injectjavascript method to listen to click event and pragmatically navigate the user to the declined screen. The problem now that this code uses javascript setTimeout. This is because paystack firsts mount a loading state. So, setTimeOut was used to delay the script for 3 seconds. I don't like this. What if the loading takes way longer than that? I have tried window.load and some other recommendations but it is not working. The script runs immediately the paystack loading state is mounted. At this point, the document elements such as the span is yet to load. Here is the code:

    const handleCancleScript = `

  setTimeout(function() {
    const buttons = document.getElementsByClassName("text")

    for(let button of buttons){
    
       const resp = button.textContent
       if(resp == "Cancel Payment"){
        button.addEventListener("click", function() {  
            var response = {event:'cancelled'};
            window.ReactNativeWebView.postMessage(JSON.stringify(response))
            });
        
       }
       
    }
  
}, 3000);
  true; 
`

The webview:

 <WebView 
    javaScriptEnabled={true}
    source={{ uri: paymentData }}
    style={{ marginTop: 50 }}
    onNavigationStateChange={ handleNav }
    cacheEnabled={false}
    cacheMode={'LOAD_NO_CACHE'}
    javaScriptEnabledAndroid={true}
    ref={webView}


    onMessage={(e) => {
    messageReceived(e?.nativeEvent?.data);
   }}

  />

Is there really a way I can prevent this script from running until the paystack loading state has completed? I realized that paystack is using loading state when I inspected the html elements on browser.

1

There are 1 best solutions below

5
Boopy On

Which recommendations did you use? Did you try the props from React Native WebView? There are some interesting props like onLoad, onLoadEnd... React Native WebView API Reference . Maybe it could works in your case.

export function App() {
  const [isLoading, setIsLoading] = React.useState(false)
  let uri = "https://reactnative.dev/docs/environment-setup"

  function showSpinner() {
    setIsLoading(true)
  }

  function hideSpinner() {
    setIsLoading(false)
  }

  function onLoadStart() {
    showSpinner()
  }

  function onLoad() {
    hideSpinner()
  }

  function onLoadEnd() {
    hideSpinner()
  }

  return (
    <View style={styles.container}>
      {isLoading && (
        <View style={styles.loadingContainer}>
          <ActivityIndicator size="large" color="#0000ff" />
        </View>
      )}

      <WebView
        style={styles.webView}
        source={{ uri }}
        onLoadStart={onLoadStart}
        onLoad={onLoad}
        onLoadEnd={onLoadEnd}
      />
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  loadingContainer: {
    ...StyleSheet.absoluteFillObject,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "white",
    zIndex: 1
  },
  webView: { flex: 1 }
})

If it doesn't work, maybe instead of using setTimeout with injectedJavaScript props, you could do that:

export const sleep = milliseconds => {
  return new Promise(resolve => setTimeout(resolve, milliseconds))
}

export function App() {
  const [isLoading, setIsLoading] = React.useState(false)
  let uri = "https://reactnative.dev/docs/environment-setup"

  const loadWebView = async () => {
    await sleep(3000)
    setIsLoading(false)
  }

  React.useEffect(() => {
    setIsLoading(true)
    loadWebView()
  }, [])

  return (
    <View style={styles.container}>
      {isLoading && (
        <View style={styles.loadingContainer}>
          <ActivityIndicator size="large" color="#0000ff" />
        </View>
      )}

      <WebView style={styles.webView} source={{ uri }} />
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  loadingContainer: {
    ...StyleSheet.absoluteFillObject,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "white",
    zIndex: 1
  },
  webView: { flex: 1 }
})

This is the result:

enter image description here