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

React Hook控制加载GIF切换:函数调用时状态不更新问题排查

React加载状态不更新的原因与修复方案

问题概述

尝试通过isLoading Hook控制加载GIF的显示/隐藏,点击按钮触发checkFeasibility函数后,加载状态从未变化——isLoading直到函数执行结束才被设为false,UI完全没机会展示加载动画。

核心错误原因

  1. 同步XMLHttpRequest阻塞主线程:你的请求用了request.open(..., false),这是同步请求,会卡住浏览器主线程。React的状态更新依赖主线程空闲时渲染UI,同步请求执行期间,主线程被完全占用,setIsLoading(true)的更新根本来不及触发UI渲染,后续紧接着执行setIsLoading(false),最终状态等于没变化。
  2. React状态更新的异步特性:即使没有同步请求,React的Hook状态更新函数也是异步批量执行的,同步代码里连续调用两次状态更新会被合并,UI同样不会有变化。

修复方案

方案1:将XMLHttpRequest改为异步模式

把同步请求改成异步,在请求的onload回调里处理响应,并在回调末尾设置isLoading为false:

function checkFeasibility(e){
  e.preventDefault()
  setIsLoading(true)
  console.log("Checking Feasibility");
  
  griddata.allowableVoltageDevs = allowableVoltageDevs;
  var request = new XMLHttpRequest();
  // 第三个参数改为true,启用异步模式
  request.open("POST",'http://localhost:5000/checkFeasibility',true);
  request.setRequestHeader('content-type', 'application/json');
  
  // 监听请求完成事件
  request.onload = function() {
    if (request.status >= 200 && request.status < 400) {
      var res = JSON.parse(request.responseText)
      console.log(request.responseText)
      console.log(res)
      
      if(!res.ok){
        plotFeasibility(res);
        setControlLawLabel("GRID IS INFEASIBLE!");
      }
      else{
        plotFeasibility(res);
        console.log(res)
        var controlledGenBusses = griddata.gen.bus
        console.log(controlledGenBusses)
        console.log("feasible")
        var controlLawString = ""
        for(var i =0;i<controlledGenBusses.length;i++){
          if(res.S[controlledGenBusses[i]-1][controlledGenBusses[i]-1]!=0){
            var foo = "q(" + String((controlledGenBusses[i]-1)) + ") = " +String((res.S[controlledGenBusses[i]-1][controlledGenBusses[i]-1]).toFixed(2)) + " p("+String((controlledGenBusses[i]-1))+")" + "\r\n";
            console.log(foo)
            controlLawString = controlLawString + foo
          }
        }
        setControlLawLabel(controlLawString);
        console.log(controlLawLabel)
      }
      
      console.log("Checked Feasiblity")
      // 请求完成后再关闭加载状态
      setIsLoading(false)
    } else {
      // 处理请求错误
      console.error("Request failed")
      setControlLawLabel("REQUEST FAILED!")
      setIsLoading(false)
    }
  };
  
  // 监听请求错误
  request.onerror = function() {
    console.error("Network error")
    setControlLawLabel("NETWORK ERROR!")
    setIsLoading(false)
  };
  
  request.send(JSON.stringify(griddata));
}

方案2:改用现代异步请求API(推荐)

用fetch代替XMLHttpRequest,代码更简洁,天然支持Promise异步流程:

async function checkFeasibility(e){
  e.preventDefault()
  setIsLoading(true)
  console.log("Checking Feasibility");
  
  try {
    griddata.allowableVoltageDevs = allowableVoltageDevs;
    const response = await fetch('http://localhost:5000/checkFeasibility', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(griddata),
    });
    
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    
    const res = await response.json();
    console.log(res)
    
    if(!res.ok){
      plotFeasibility(res);
      setControlLawLabel("GRID IS INFEASIBLE!");
    }
    else{
      plotFeasibility(res);
      console.log(res)
      var controlledGenBusses = griddata.gen.bus
      console.log(controlledGenBusses)
      console.log("feasible")
      var controlLawString = ""
      for(var i =0;i<controlledGenBusses.length;i++){
        if(res.S[controlledGenBusses[i]-1][controlledGenBusses[i]-1]!=0){
          var foo = "q(" + String((controlledGenBusses[i]-1)) + ") = " +String((res.S[controlledGenBusses[i]-1][controlledGenBusses[i]-1]).toFixed(2)) + " p("+String((controlledGenBusses[i]-1))+")" + "\r\n";
          console.log(foo)
          controlLawString = controlLawString + foo
        }
      }
      setControlLawLabel(controlLawString);
      console.log(controlLawLabel)
    }
    
    console.log("Checked Feasiblity")
  } catch (error) {
    console.error('Error:', error);
    setControlLawLabel("REQUEST FAILED!")
  } finally {
    // 无论成功失败都关闭加载状态
    setIsLoading(false)
  }
}

更好的加载状态展示方式

  • 禁用触发按钮:加载时给按钮添加disabled属性,防止用户重复点击:
    <button onClick={checkFeasibility} disabled={isLoading}>
      {isLoading ? "检查中..." : "检查可行性"}
    </button>
    
  • 增强加载提示:在Loading组件里添加文案,提升用户感知:
    const Loading = () => {
      return (
        <div className="loading-overlay">
          <img className='loading' src="loading.gif" alt="loading" />
          <p>正在检查电网可行性,请稍候...</p>
        </div>
      );
    };
    
  • 全屏遮罩:给Loading组件添加半透明全屏背景,避免用户误操作其他内容:
    .loading-overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(0,0,0,0.5);
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      z-index: 9999;
    }
    
  • 错误状态反馈:请求失败时,给提示文案添加错误样式(比如红色文字),让用户明确知道操作失败。

内容的提问来源于stack exchange,提问作者1Pre

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 03:25:03