[英]React Native webview routing
我實際上正在構建一個 React-Native 應用程序,其中包含一些本機屏幕和一些我使用 web 視圖加載網站的屏幕。 代替經典的網站導航,我有一個原生抽屜,允許我切換頁面。 我的問題是該網站正在使用 react-router,因此它通過僅加載必要的代碼來順利處理瀏覽器上的 url 更改,但是當我在 webview 中更改 url 時,它就像我正在刷新網站一樣重新加載所有內容,導致導航非常緩慢。
我想到的唯一“黑客”是在網站window
變量上公開一個函數以觸發反應路由器“轉到”。
有任何想法嗎 ?
您是否嘗試過UseEffect
和route navigation
的組合?
因此,經過幾天的掙扎,我已經解決了以下解決方案,我承認這有點晦澀,但效果很好(包括后退按鈕、手勢):
在React Web Application 中,我通過全局 Window 對象保持 ReactRouter 歷史對象可用。
import { useHistory } from 'react-router'
// ...
declare global {
interface Window {
ReactRouterHistory: ReturnType<typeof useHistory>
}
}
const AppContextProvider = props => {
const history = useHistory()
// provide history object globally for use in Mobile Application
window.ReactRouterHistory = history
// ...
}
在React Native Mobile Application 中,我將自定義代碼注入到 WebView,它利用歷史對象進行導航,並且應用程序使用消息與此代碼進行通信:
webViewScript.ts
// ...
export default `
const handleMessage = (event) => {
var message = JSON.parse(event.data)
switch (message.type) {
// ...
case '${MessageTypes.NAVIGATE}':
if (message.params.uri && message.params.uri.match('${APP_URL}') && window.ReactRouterHistory) {
const url = message.params.uri.replace('${APP_URL}', '')
window.ReactRouterHistory.push(url)
}
break
}
};
!(() => {
function sendMessage(type, params) {
var message = JSON.stringify({type: type, params: params})
window.ReactNativeWebView.postMessage(message)
}
if (!window.appListenersAdded) {
window.appListenersAdded = true;
window.addEventListener('message', handleMessage)
var originalPushState = window.history.pushState
window.history.pushState = function () {
sendMessage('${MessageTypes.PUSH_STATE}', {state: arguments[0], title: arguments[1], url: arguments[2]})
originalPushState.apply(this, arguments)
}
}
})()
true
`
intercom.ts
(此處沒有路由細節,僅用於通用通信)
import WebView, {WebViewMessageEvent} from 'react-native-webview'
import MessageTypes from './messageTypes'
export const sendMessage = (webview: WebView | null, type: MessageTypes, params: Record<string, unknown> = {}) => {
const src = `
window.postMessage('${JSON.stringify({type, params})}', '*')
true // Might fail silently per documentation
`
if (webview) webview.injectJavaScript(src)
}
export type Message = {
type?: MessageTypes,
params?: Record<string, unknown>
}
export type MessageHandler = (message: Message) => void
export const handleMessage = (handlers: Partial<Record<MessageTypes, MessageHandler>>) => (
(event: WebViewMessageEvent) => {
const message = JSON.parse(event.nativeEvent.data) as Message
const messageType = message.type
if (!messageType) return
const handler = handlers[messageType]
if (handler) handler(message)
}
)
export {default as script} from './webViewScript'
WebViewScreen.tsx
import {handleMessage, Message, script, sendMessage} from '../intercom'
// ...
const WebViewScreen = ({navigation, navigationStack}: WebViewScreenProps) => {
const messageHandlers = {
[MessageTypes.PUSH_STATE]: ({params}: Message) => {
if (!params) return
const {url} = params
const fullUrl = `${APP_URL}${url}`
navigationStack.push(fullUrl)
},
// ...
}
const uri = navigationStack.currentPath
// navigation solution using history object propagated through window object
useEffect(() => {
if (uri) {
sendMessage(webViewRef.current, MessageTypes.NAVIGATE, {uri})
}
}, [uri])
// this is correct! Source is never going to be updated, navigation is done using native history object, see useEffect above
// eslint-disable-next-line react-hooks/exhaustive-deps
const source = useMemo(() => ({uri}), [])
return (
<View
// ...
refreshControl={
<RefreshControl
// ...
/>
}
>
<WebView
source={source}
injectedJavaScript={script}
onMessage={handleMessage(messageHandlers)}
// ...
/>
</View>
)
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.