概要
2022/9時点で、特定のAndorid(確認したのは、Android9)において、WebViewをマウントした画面から、ReactNavigationを使った画面遷移を行うとクラッシュする問題がある。
(navigationの遷移アニメーション機能と WebViewのレンダリング機能が競合している?)
本記事は、試行錯誤した結果動いた方法と失敗した方法をまとめた備忘録です。
環境
- ReactNative : v.0.69.3
- react-native-webview : v.11.23.0
- ReactNavigation : v.5.9.8
参考
- https://github.com/react-navigation/react-navigation/issues/9061
- https://github.com/react-native-webview/react-native-webview/issues/575
- https://reactnative-examples.com/example-of-hardwareaccelerated-in-modal-in-react-native/
- https://github.com/react-native-webview/react-native-webview/issues/1161
ベースとなるコード
例として、ボタンをクリックすると、StackNavigatorによってDetailPage
に飛ぶ画面を作成。
画面はWebViewコンポーネントをマウントしていることとする。
const WebViewSample: React.VFC<Props> = (props) => {
const param: string = props.param;
// ボタンタップで別ページに遷移
const goNextPage = useCallback(() => {
navigation.navigate("DetailPage", {param});
}
}, [param]);
// WebViewコンポーネントをマウント
return (
<View>
<MyButton onClick={goNextPage}/>
<WebView
source={hoge}
...
/>
</View>
)
}
結論
ReactNavigationのhookを利用し、画面にフォーカスされた時 or 外れた時に、WebViewコンポーネントをアンマウントすることで回避。
→ フォーカスON/OFFの判定には、ReactNavigationのuseFocusEffectを利用する。
また、遷移先画面から「戻る」場合、再度WebViewコンポーネントをマウントするとクラッシュする。
そこで、「戻る」場合は、一定時間待ち合わせてからマウントすることで回避。待ち合わせには、setTimeout
を利用するため、副作用hookを活用する。(なぜ副作用hookを使うか?などは、他の方々の記事参照のこと。本記事では割愛)
import { useFocusEffect } from "@react-navigation/native";
const WebViewSample: React.VFC<Props> = (props) => {
const param: string = props.param;
const [isShow, setIsShow] = useState<boolean>(false);
useFocusEffect(
useCallback(() => {
//focus後、1秒後にWebViewをマウント
const timeout = setTimeout(() => {
setIsShow(true);
}, 1000);
// focusが外れた際は、WebViewコンポーネントをunmountする
return () => {
clearTimeout(timeout);
setIsShow(false);
};
}, [])
);
// ボタンタップで別ページに遷移
const goNextPage = useCallback(() => {
navigation.navigate("DetailPage", {param});
}
}, [param]);
// WebViewコンポーネントをマウント
return (
<View>
<MyButton onClick={goNextPage}/>
// isShowフラグによってmount or unmount制御
{isShow && <WebView
source={hoge}
...
/>
}
</View>
)
}
試行錯誤したものたち
WebViewのstyleで回避してみる
Webviewのstyleとして、opacity: 0.99, overlay: "hidden"を指定
<WebView style={opacity: 0.99, overlay: "hidden"}>
source={hoge}
...
</WebView>
→ クラッシュは直らず。また画面崩れも発生したので見送り
WebViewのpropsに androidLayerType="software"指定
<WebView>
androidLayerType="software"
source={hoge}
...
</WebView>
→ WebView自体表示されなくなったので見送り
Manifestにて、hardwareacceleratedをオフにする
// AndroidManifest.xml
<application android:hardwareAccelerated="true" ...>
→ WebView自体表示されなくなったので見送り