React Native WebView的ref.injectJavascript在TabNavigator中始终为undefined
我之前也碰到过一模一样的情况,核心问题出在组件渲染/挂载时机和导航监听的绑定时机不匹配,再加上TabNavigator默认的懒加载特性,导致触发focus事件时WebView的ref还没被正确赋值,或者组件已经被卸载重建,ref引用丢失。
为什么之前的写法会失败?
- 最初在空依赖的
useEffect里绑定focus监听:此时组件刚初始化,WebView还没完成挂载,webViewRef.current自然是undefined;而且当Tab切换时,未激活的Tab组件会被卸载,切换回来时组件重新挂载,但监听已经绑定在旧的实例上了。 onLoadEnd里绑定监听:每次WebView加载完成都会绑定一次监听,会造成重复绑定,而且当Tab切换后再回来,WebView可能重新加载,这时候监听里的ref可能已经不是当前实例的引用。useFocusEffect的写法:没有先检查webViewRef.current是否存在,而且依赖了props.navigation,但ref的变化不会触发依赖更新,导致监听里的ref还是旧的引用。
而setTimeout能工作的原因很简单:延迟执行给了WebView足够的挂载时间,此时webViewRef.current已经指向了WebView实例。
正确的解决方案
这里提供两种经过验证的写法,都能解决ref为undefined的问题:
方案1:使用useFocusEffect(推荐,React Navigation官方推荐的焦点监听方式)
import React, { useRef } from 'react'; import { WebView } from 'react-native-webview'; import { useFocusEffect } from '@react-navigation/native'; export default (props) => { const webViewRef = useRef(null); useFocusEffect( React.useCallback(() => { // 关键:先检查ref是否存在,避免报错 if (webViewRef.current) { // 注意injectJavaScript需要返回true,这是WebView的要求 const injectCode = ` console.log("Tab获得焦点,注入JS成功!"); document.body.style.backgroundColor = 'lightgreen'; true; `; webViewRef.current.injectJavaScript(injectCode); } // 可选:返回清理函数,在Tab失焦时执行(比如恢复页面状态) return () => { if (webViewRef.current) { webViewRef.current.injectJavaScript(` document.body.style.backgroundColor = 'white'; true; `); } }; }, []) ); return ( <WebView ref={webViewRef} source={{ uri: 'https://github.com/react-native-community/react-native-webview' }} // 确保启用JavaScript,否则注入代码无效 javaScriptEnabled={true} /> ); };
方案2:使用navigation.addListener配合正确的useEffect依赖
import React, { useRef, useEffect } from 'react'; import { WebView } from 'react-native-webview'; export default (props) => { const webViewRef = useRef(null); useEffect(() => { const unsubscribe = props.navigation.addListener('focus', () => { if (webViewRef.current) { const injectCode = ` console.log("Tab获焦,注入JS"); true; `; webViewRef.current.injectJavaScript(injectCode); } }); // 组件卸载时取消监听,避免内存泄漏 return unsubscribe; }, [props.navigation]); // 依赖navigation,确保每次导航上下文变化时重新绑定监听 return ( <WebView ref={webViewRef} source={{ uri: 'https://github.com/react-native-community/react-native-webview' }} javaScriptEnabled={true} /> ); };
额外注意事项
- 确保WebView开启了
javaScriptEnabled={true},否则注入的代码不会执行。 injectJavaScript方法的代码最后必须返回true,这是react-native-webview的要求,否则可能会出现奇怪的问题。- 如果你的TabNavigator开启了
lazy={false}(默认是true),组件不会被卸载,这时候ref的问题会少一些,但还是建议保留ref存在性检查,避免意外情况。
内容的提问来源于stack exchange,提问作者waffl
相关产品推荐
相关产品推荐

