You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

react-native-webview onShouldStartLoadWithRequest返回false后加载失效

问题描述

在React-Native项目中使用react-native-webview组件时,通过onShouldStartLoadWithRequest回调做URL请求校验,逻辑如下:

  • 当请求URL不等于预设变量myvarUrl时,回调返回true,URL正常加载
  • 当请求URL等于myvarUrl时,弹出提示框,同时onShouldStartLoadWithRequest返回false

实际运行时出现异常:弹出提示框后,如果用户点击「确定」,可以正常导航跳转回应用登录页;但如果用户点击「取消」留在当前页面,WebView就再也无法加载页面内包含的其他URL。核心表现为:对某一个URL请求返回false之后,onShouldStartLoadWithRequest就不再响应加载任何其他URL。

原问题代码

function verifyurl(url) {

    
    if (url == myvarUrl) {
      alerta();
      return false;
    } else {
      setNewUrl(url)
      return true;
    }
  }

  async function alerta() {
    const response = await alert();
    response && logOut();
  }


  //Verifica se existe item na pilha navegação e retira
  function logOut() {
    let canGoBack = navigation.canGoBack();
    canGoBack ? navigation.goBack() : console.log('AGIU MAIS DE UMA VEZ');
  }

  /* ========================> Retorno de Visualização <======================== */

  const Spinner = () => (
    <View style={genericStyles.activityContainer}>
      <ActivityIndicator size="large" color="#f29900" />
    </View>
  );

  return (
    <View style={genericStyles.containerWebView}>
      <StatusBar backgroundColor={genericStyles.colors.BlueBg} barStyle={'light-content'} />
      <WebView
        source={{ uri: propriedade }}
        ref={webViewRef}
        originWhitelist={['*']}
        allowsInlineMediaPlayback
        javaScriptEnabled
        scalesPageToFit
        mediaPlaybackRequiresUserAction={false}
        javaScriptEnabledAndroid
        useWebkit
        startInLoadingState={true}
        cacheEnabled={false}
        renderLoading={Spinner}
        onShouldStartLoadWithRequest={(event) => {
          return verifyurl(event.url)
        }}
      />
    </View>
  )

开发环境信息

react: ^17.0.2 => 17.0.2
react-native: ^0.68.2 => 0.68.2
react-native-webview: 11.22.2
问题原因

这个问题是react-native-webview iOS端(开启useWebkit时)的常见时序问题:

  1. onShouldStartLoadWithRequest是原生端触发的同步回调,必须在同步执行流中立刻返回布尔值,一旦在回调执行过程中触发弹窗这类异步/阻塞UI线程的操作,会打乱WebView原生层的导航状态锁,导致回调被永久挂起,后续所有URL请求都不会再触发该回调。
  2. 原代码在非拦截分支中直接调用setNewUrl触发组件重渲染,会导致WebView的回调引用频繁变化,也会加大状态异常的概率。
修复方案

按照以下逻辑调整代码即可解决:

  1. 不要在onShouldStartLoadWithRequest的同步执行流中直接触发弹窗、state更新这类会打断同步执行的操作,命中拦截规则时先同步返回false,把拦截到的URL存入临时state,等回调执行完成后,再通过监听state变化触发弹窗。
  2. 用户点击取消留在当前页时,主动调用WebView实例的stopLoading()方法重置原生层的加载状态,清空拦截标记。
  3. 不需要触发UI更新的临时变量尽量用useRef存储,避免不必要的组件重渲染影响WebView状态。

修复后核心代码示例

// 新增拦截状态标记、URL存储ref
const [blockedUrl, setBlockedUrl] = useState(null);
const newUrlRef = useRef('');

function verifyurl(url) {
  if (url === myvarUrl) {
    // 同步返回false阻止加载,仅做拦截标记,不触发任何异步/重渲染操作
    setBlockedUrl(url);
    return false;
  }
  // 用ref存URL,不触发重渲染
  newUrlRef.current = url;
  return true;
}

// 监听拦截标记,回调执行完成后再触发弹窗
useEffect(() => {
  if (!blockedUrl) return;
  // 建议使用RN官方Alert组件,不要用自定义阻塞式alert
  Alert.alert(
    '提示',
    '确定要返回登录页吗?',
    [
      {
        text: '取消',
        style: 'cancel',
        onPress: () => {
          setBlockedUrl(null);
          // 重置WebView加载状态,解除导航锁
          webViewRef.current?.stopLoading();
        }
      },
      {
        text: '确定',
        onPress: () => {
          setBlockedUrl(null);
          logOut();
        }
      }
    ]
  )
}, [blockedUrl])

// logOut、Spinner、WebView渲染部分逻辑保持原有即可,注意把原来setNewUrl的相关逻辑替换为读取newUrlRef.current

如果调整后仍偶发锁死问题,可以在取消按钮的回调里额外加一句webViewRef.current?.reload()重载当前页面,即可完全恢复WebView的导航响应能力。


内容的提问来源于stack exchange,提问作者Hérick Raposo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 18:12:13