RN ↔ React(JS) Webview 통신

March 20, 2022

앱을 못 만드니까 WebView라도 만들자 : )

TL;DR

  • postMessage onMessage를 활용해서 통신하자
    • RN → Web : RN에서 Web JS 실행 방법
      1. injectedJavaScript : WebView Loading js코드 실행
      2. injectJavaScript : RN에서 WebView로 JS코드 실행시킬때
    • Send/Receive Message (Only String type!!!)
      1. Send (Web ←→ RN)
        • postMessage : method를 활용해서 Message를 보낼 수 있다
      2. Receive
        • onMessage (RN)
        • window.addEventListener('message') (Web)
  • <Iframe> ,window.open 통신하는 방법이랑 유사하다!!!

1. React(JS) ↔ Native

기존 Webview(v0.60부터 Deprecated) 대신 react-native-webview 를 사용

이쁘게 위치 시키기 위해 <SafeAreaView/> 를 사용하자

yarn add react-native-webview
pod install //ios

WebView에 js를 주입(실행) 시켜주려면

스크립트 끝에 true;?!! 오류 발생하는것을 방지(공식 문서)

  • injectedJavaScript Props활용하면, WebView를 실행하기전에 스크립트를 실행!

    injectedJavaScript

// RN
const App = () => {
    const runFirst = `
      window.isNativeApp = true;
      true;
    `

    return (
        <SafeAreaView style={{ flex: 1 }}>
            <WebView
                source={{
                    uri: 'http://localhost:3000/',
                }}
                injectedJavaScript={runFirst}
            />
        </SafeAreaView>
    )
}

// Web
const Web = () => {
    return <div>{window.isNativeApp && 'Hello WebView'}</div>
}
  • injectJavaScript Method를 활용하면 실행중인 WebView를 확인 할 수 있다.

    injectJavaScript

    afterinjectJavaScript

// RN
const App = () => {
    const runJS = `
      document.body.style.backgroundColor = 'red';
      true;
    `

    const handleClick = () => {
        ref.current.injectJavaScript(runJS)
    }

    return (
        <SafeAreaView style={{ flex: 1 }}>
            <Button title="injectJavaScript" onPress={handleClick} />
            <WebView
                source={{
                    uri: 'http://localhost:3000/',
                }}
            />
        </SafeAreaView>
    )
}

// Web
const Web = () => {
    return <div></div>
}

Web에서 보낸 메세지를 받아보자

onmessage.png

webviewpostmessage

  1. onMessage Props를 활용해서 Messge를 수신

    const App = () => {
        const handleMessage = (event) => {
            const {
                nativeEvent: { data },
            } = event
            Alert.alert(data)
        }
    
        return (
            <SafeAreaView style={{ flex: 1 }}>
                <WebView
                    source={{
                        uri: 'http://localhost:3000/',
                    }}
                    onMessage={handleMessage}
                />
            </SafeAreaView>
        )
    }
  2. WebView가 호출되면서, window 네임스페이스에 WebView객체가 추가(window.ReactNativeWebView)

    • postMessage Method를 활용해서 Message를 보낸다
    • 중요!! 보낼 수 있는 Message의 타입은 string 뿐이다
    const Web = () => {
      const [input, setInput] = useState('');
    
    	const handleChange = ({ target: {value} } ) => {
        setInput(value)
      }
    
      const handleSubmit = () => {
        if(window.ReactNativeWebView) {
          window.ReactNativeWebView.postMessage(JSON.stringify(input))
        }
      }
      return (
        <div>
          <h1>Input Message</h1>
          <input value={input} onChange={handleChange}/>
          <button onClick={handleSubmit}>submit</button>
        </div>
      )

RN에서 보낸 메세지를 받아보자

rnpostmessage.png

windowmessage

  1. postMessage를 통해 WebView로 발신

    const App = () => {
        const ref = useRef()
        const handleClick = () => {
            ref.current.postMessage('This Message from RN')
        }
    
        return (
            <SafeAreaView style={{ flex: 1 }}>
                <Button title="sendMessageToWebView" onPress={handleClick} />
                <WebView
                    ref={ref}
                    source={{
                        uri: 'http://localhost:3000/',
                    }}
                />
            </SafeAreaView>
        )
    }
  2. window.addEventListener('message')를 활용해서 Message를 수신

    const Web = () => {
        const [message, setMessage] = useState('')
    
        useEffect(() => {
            window.addEventListener('message', ({ data }) => {
                setMessage(data)
            })
        }, [])
    
        return (
            <div>
                <h3>Receive Message : {message}</h3>
            </div>
        )
    }

2. Iframe / window open 통신방법

Iframe

// 발신
$iframe.contentWindow.postMessage(value, 'http://localhost:3000')

// 수신
window.addEventListener('message', (e) => {
    if (e.origin !== 'http://localhost:3000') return
    console.log(e.data)
})

popup(window.open)

// 발신
const openPopup = window.open(
    'http://localhost:3000/child',
    'popupWindowName',
    'popup=1'
)
openPopup.postMessage(value, 'http://localhost:3000')

// 수신
window.addEventListener('message', (e) => {
    if (e.origin !== 'http://localhost:3000') return
    console.log(e.data)
})

참고


© 2023, Customized by Joon