How to refresh pages in Expo Webview by pulling down?

Viewed 5430

I've made Webview app with Expo, React-Native watching youtube.

At first I made basic app like this

import * as React from 'react';
import { WebView } from 'react-native-webview';

export default class App extends React.Component {
  render() {
    return <WebView 
    source={{ uri: 'https://www.google.com' }} 
    allowsFullscreenVideo={true} 
    allowsBackForwardNavigationGestures={true}
    style={{ marginTop: 20 }} />;
  }
}

And in Android apk, "goBack" button didn't work. If I touched it, app just closed. But I wanted to go to back page.

So I changed like this:

import * as React from 'react';
import {Component} from 'react';
import {View,BackHandler,Platform} from 'react-native';
import {WebView} from 'react-native-webview';

export default class App extends Component {
  constructor(props) {
    super(props);
  }
  webView = {
    canGoBack: false,
    ref: null,
  }

  onAndroidBackPress = () => {
    if (this.webView.canGoBack && this.webView.ref) {
      this.webView.ref.goBack();
      return true;
    }
    return false;
  }

  UNSAFE_componentWillMount() {
    if (Platform.OS === 'android') {
      BackHandler.addEventListener('hardwareBackPress', this.onAndroidBackPress);
    }
  }

  UNSAFE_componentWillUnmount() {
    if (Platform.OS === 'android') {
      BackHandler.removeEventListener('hardwareBackPress');
    }
  }

  render() {
    return (
      <View style={{flex:1}}>
        <WebView
          ref={(webView) => { this.webView.ref = webView; }}
          onNavigationStateChange={(navState) => { this.webView.canGoBack = navState.canGoBack; }}
          automaticallyAdjustContentInsets={false}
          source={{ uri: 'https://www.google.com' }}
          allowsFullscreenVideo={true}
          javaScriptEnabled={true}
          domStorageEnabled={true}
          startInLoadingState={true}
          style={{marginTop: 25}}
        />
    </View>
    )
  }
}

Finally back button worked. If I touched it, I moved to back page.

But I realised this Webview app cannot refresh the page So I searched for 3 days. And this is the function what I expected. In this link, you can see rounded arrow when you pull down the page. https://reactnative.dev/docs/refreshcontrol?redirected

So I mixed the codes, but it doesn't work

import * as React from 'react';
import {Component} from 'react';
import {WebView} from 'react-native-webview';
import {
  ScrollView,View,BackHandler,Platform,
  RefreshControl,
  StyleSheet,
  Text,
  SafeAreaView,
} from 'react-native';
import Constants from 'expo-constants';


function wait(timeout) {
  return new Promise(resolve => {
    setTimeout(resolve, timeout);
  });
}

export default class App extends Component {
    
    
  const [refreshing, setRefreshing] = React.useState(false);

  const onRefresh = React.useCallback(() => {
    setRefreshing(true);
    wait(2000).then(() => setRefreshing(false));
  }, [refreshing]);

  constructor(props) {
    super(props);
  }
  webView = {
    canGoBack: false,
    ref: null,
  }

  onAndroidBackPress = () => {
    if (this.webView.canGoBack && this.webView.ref) {
      this.webView.ref.goBack();
      return true;
    }
    return false;
  }

  UNSAFE_componentWillMount() {
    if (Platform.OS === 'android') {
      BackHandler.addEventListener('hardwareBackPress', this.onAndroidBackPress);
    }
  }

  UNSAFE_componentWillUnmount() {
    if (Platform.OS === 'android') {
      BackHandler.removeEventListener('hardwareBackPress');
    }
  }

  render() {
    return (
    <SafeAreaView style={styles.container}>
      <ScrollView
        contentContainerStyle={styles.scrollView}
        refreshControl={
          <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
        }
      >
        <WebView
          ref={(webView) => { this.webView.ref = webView; }}
          onNavigationStateChange={(navState) => { this.webView.canGoBack = navState.canGoBack; }}
          automaticallyAdjustContentInsets={false}
          source={{ uri: 'https://www.google.com' }} 
          allowsFullscreenVideo={true}
          javaScriptEnabled={true}
          domStorageEnabled={true}
          startInLoadingState={true}
          style={{marginTop: 25}}
        />
      </ScrollView>
    </SafeAreaView>
    )
  }
}

This is error message..

Error: /workspace/Human_Impact/App.js: Unexpected token (23:8)

  21 |
  22 |
> 23 |   const [refreshing, setRefreshing] = React.useState(false);
     |         ^
  24 |
  25 |   const onRefresh = React.useCallback(() => {
  26 |     setRefreshing(true);
Failed building JavaScript bundle.
  • Conclusion : I need these functions
  1. If I touch Android hardware back button once, go to back page, twice in very short time, end app. (Android)
  2. If I pull down, then refresh the web page. (iOS / Android both)
3 Answers

Firstly, you can add Styles to ScrollView and WebView:

container: {
  position : 'relative',
},
ScrollStyle: {
 backgroundColor : 'white',
 position: 'relative',
}

Then, render your ScrollView and WebView:

<View style={{flex: 1}}>
    <ScrollView
        style = {styles.ScrollStyle}
        contentContainerStyle={{flex: 1}}
        refreshControl={
            <RefreshControl
              refreshing={false}
              onRefresh={this.yourFunctionReload} // exl in function : this.yourWebview.reload();
            />
        }>
        <Header />  
        <WebView
          style={styles.container}
            .....
         />
    </ScrollView>
</View>

Note: don't forget to import View, ScrollView, RefreshControl and WebView

A shot demo which is working fine for pull to refresh going through the answers. Thanks for the help everyone.

import {
  ScrollView,View,
  RefreshControl,
  StyleSheet,
} from 'react-native';
import { WebView } from 'react-native-webview';

function wait(timeout) {
  return new Promise(resolve => {
    setTimeout(resolve, timeout);
  });
}

export default function App() {

  const [refreshing, setRefreshing] = React.useState(false);
  const webViewRef = useRef()
  
  const onRefresh = React.useCallback(() => {
    setRefreshing(true);
    webViewRef.current.reload();
    wait(2000).then(() => setRefreshing(false));
  }, [refreshing]);

  return (
    <View style={{flex: 1}}>
    <ScrollView
        style = {styles.ScrollStyle}
        contentContainerStyle={{flex: 1}}
        refreshControl={
            <RefreshControl
              refreshing={false}
              onRefresh={onRefresh} // exl in function : this.yourWebview.reload();
            />
        }>
        <WebView
          ref = {webViewRef}
          automaticallyAdjustContentInsets={false}
          source={{ uri: 'https://www.google.com' }} 
          allowsFullscreenVideo={true}
          javaScriptEnabled={true}
          domStorageEnabled={true}
          startInLoadingState={true}
          style={styles.container}
        />
    </ScrollView>
</View>
  );
}

const styles = StyleSheet.create({
  container: {
    position : 'relative',
  },
  ScrollStyle: {
   backgroundColor : 'white',
   position: 'relative',
  }
});

as I said before, i tried to implement it this way, however I can only scroll down but when I need to scroll back it gives refrash, however I didn't get good results because the RefreshControl was always enabled, so I made the following adjustments along with the previously mentioned solutions:

const [refreshing, setRefreshing] = useState(false);
const [refresherEnabled, setEnableRefresher] = useState(true);


  //Code to get scroll position
  const handleScroll = (event) =>  {
    console.log(Number(event.nativeEvent.contentOffset.y))
    const yOffset = Number(event.nativeEvent.contentOffset.y)
    if (yOffset === 0){
      console.log('top of the page')
      setEnableRefresher(true)
    }else{
      setEnableRefresher(false)
    }
  }
    
return (
    <SafeAreaView style={genericStyles.containerWebView}>
      <StatusBar backgroundColor={genericStyles.colors.BlueBg} barStyle={'light-content'} />
      <ScrollView style={genericStyles.scrollView} 
      contentContainerStyle={{flex: 1}}
      refreshControl={
        <RefreshControl 
          refreshing={refreshing}
          enabled={refresherEnabled}
          onRefresh={()=>{webViewRef.current.reload()}}
        />
      }>
        <WebView
          source={{ uri: your url }}
          ref={webViewRef}
          originWhitelist={['*']}
          allowsInlineMediaPlayback
          javaScriptEnabled
          scalesPageToFit
          mediaPlaybackRequiresUserAction={false}
          javaScriptEnabledAndroid
          useWebkit
          startInLoadingState={true}
          cacheEnabled
          onScroll={handleScroll}
        />
      </ScrollView>
    </SafeAreaView>
  )
}
Related