React Native 웹뷰 안드로이드 Pull to Refresh 활성화

개요

React Native 로 개발한 앱에 WebView 를 넣었다. iOS 는 별 다른 로직 없이 웹뷰에서 Pull To Refresh(PTR)이 일반적인 웹 브라우저에서의 동작과 동일하게 작동한다.

그러나 안드로이드에서는 이 기능이 정상적으로 작동하지 않았고, 해결을 위해 웹뷰를 로드할 때 PTR 이벤트를 등록해야 한다는 것을 알게 되었다.

React Native

/src/WebviewContainer.tsx


const isIos = Platform.OS === 'ios';
const isAndroid = Platform.OS === 'android';

const WebViewContainer = () => {
  const webViewRef = useRef<WebView>(null);
  const [refreshing, setRefreshing] = useState(false);
  const [enablePTR, setEnablePTR] = useState(true);

  const onRefresh = useCallback(async () => {
    setRefreshing(true);
    setTimeout(() => {
      setRefreshing(false);
      webViewRef.current?.reload();
    }, 2000);
  }, []);

  /** 웹뷰에서 데이터를 받을 때 필요한 함수 */
  const handleMessage = (e: WebViewMessageEvent) => {
    const data = JSON.parse(e.nativeEvent.data);
    switch (data?.type) {
      // 안드로이드 웹뷰에서 스크롤링할 때 마다 refreshControl 동작을 제어하는 핸들러
      case 'ENABLE_PTR':
        if (isIos) {
          break;
        } else if (data.value?.scrollTop === 0 && !enablePTR) {
          setEnablePTR(true);
        } else if (data.value?.scrollTop > 10 && enablePTR) {
          setEnablePTR(false);
        }
        break;
    }
  };

  // 스크롤링 할 때 마다 새로고침을 막고 원활한 Pull to Refresh 기능을 위해 웹뷰가 로딩되기 전에 이 함수를 가장 먼저 실행
  // 스크롤링이 발생할 때마다 웹에서 앱으로 ENABLE_PTR data 를 보낸다.
  const runFirst = `window.onscroll = function() {
    window.ReactNativeWebView.postMessage(
      JSON.stringify({
        type: 'ENABLE_PTR',
        data: {
          value:
            {
              scrollTop: 
                document.documentElement.scrollTop || document.body.scrollTop,
              reqId: Date.now(),
          }
        }
      }),     
    )
  }
  `;

  const runBeforeFirst = `
      window.isNativeApp = true;
      true;
  `;

  return (
    <ScrollView
      refreshControl={
        <RefreshControl
          refreshing={refreshing}
          onRefresh={onRefresh}
          progressViewOffset={refreshing ? 40 : 0}
          enabled={isIos ? true : enablePTR}
        />
      }
      scrollEventThrottle={16}
      scrollEnabled={isIos ? true : enablePTR}
    >
      <WebView
        ref={webViewRef}
        source=
        onMessage={handleMessage}
        originWhitelist={['*']}
        injectedJavaScript={runFirst}
        injectedJavaScriptBeforeContentLoaded={runBeforeFirst}
      />
    </ScrollView>
  );
};

모바일 앱에서 웹뷰를 로드될 때 runFirst 를 실행하여 웹뷰의 스크롤링 이벤트를 감지한다. 스크롤링 할 때마다 ENABLE_PTR 이라는 이벤트를 웹뷰에서 앱으로 보내고, 앱에서는 handleMessage 에 존재하는 ENABLE_PTR 로직을 실행한다.

웹뷰에서 ENABLE_PTR 이벤트를 앱으로 보낼 때 현재 페이지의 스크롤 데이터를 보낸다. 이를 실행하는 로직은 스크롤 값에 따라 enablePTR 이라는 상태를 변경한다.

이후 앱의 웹뷰를 감싸고 있는 ScrollView 가 이 상태 값에 따라 페이지 새로고침을 실행하게 된다. ScrollViewrefreshControl 값에 새로고침 활성화 여부를 enablePTR 에 따라 활성화 하고, PTR 조건에 합당하면 onRefresh 함수를 실행한다.

위와 같은 로직으로 모바일 디바이스 환경에 구애받지 않고 동일한 Pull To Refresh 를 구현할 수 있다.

2022년 12월 30일에 수정됨
YUNSU BAE

YUNSU BAE

주니어 웹 개발자 배윤수 입니다!

예술의 영역을 동경하고 있어요. 🧑‍🎨