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

React Native中axios.post首次调用不发送 第二次触发才生效问题

问题根因

两个核心问题导致这个异常:

  • 副作用设计不合理:用户点击开关触发的是主动操作类请求,这类逻辑不应该放在useEffect里通过监听状态变化触发。这种写法天然容易出现闭包捕获旧值、执行时序和操作不同步的问题,也很难排查请求触发的来源。
  • React 18+ 开发环境严格模式的双调用机制:React 18之后开发环境默认开启的严格模式,会对首次挂载的组件额外执行一次「挂载→执行effect→清理effect→卸载→重新挂载→执行effect」的校验流程,用来检测未正确清理的副作用。你的代码中useEffect没有做请求取消的清理逻辑,加上sendState每次渲染都重新生成、捕获的状态存在时序差,就会出现奇数次操作的请求被打断积压、偶数次操作时积压请求被批量发出的现象。
修复方案

优先推荐直接在开关切换的事件回调中发起请求,从根源上避开useEffect带来的闭包、时序、严格模式兼容问题,逻辑也更直观:

const GroundFloorLamps = props => {
  const [lampsState, setLampsState] = useState({
    l1: 'checked',
    l2: 'unchecked',
    l3: 'unchecked',
    l4: 'unchecked',
    l5: 'unchecked',
  });

  // 改造sendState接收最新状态作为入参,避免闭包捕获旧值
  const sendState = async (currentState) => {
    const newState = EncodeLampState(currentState);
    try {
      const res = await axios.post(GlobalData.azureFunctions.broadcastUrl, {
        state: newState,
      });
      Log(res.status);
    } catch (err) {
      Log('Error: \n' + err.message);
    }
  };

  const onToggleSwitch = lampNo => {
    // 先计算出最新的全量状态
    const newLampsState = {
      ...lampsState,
      [lampNo]: lampsState[lampNo] === 'checked' ? 'unchecked' : 'checked',
    };
    // 更新本地UI状态
    setLampsState(newLampsState);
    // 直接用最新状态发起请求,不需要依赖useEffect监听
    sendState(newLampsState);
  };

  // 如果需要页面首次加载时同步一次初始灯状态,可以保留仅执行一次的effect
  useEffect(() => {
    sendState(lampsState);
  }, []);

额外优化建议

  • 快速点击防重:如果用户短时间内多次点击开关,会触发多次并发请求,可能导致设备状态和服务端不一致,可以加一个请求中的loading态,请求返回前屏蔽开关点击事件。
  • 如果因为业务逻辑特殊,必须保留useEffect监听状态触发请求的写法,需要补全请求清理逻辑和依赖项,避免内存泄漏和闭包问题,参考实现:
import { useState, useEffect, useCallback } from 'react';

// 用useCallback包裹请求函数,固定引用
const sendState = useCallback(async (currentState, signal) => {
  const newState = EncodeLampState(currentState);
  try {
    // 传入signal支持请求取消
    const res = await axios.post(GlobalData.azureFunctions.broadcastUrl, {
      state: newState,
    }, { signal });
    Log(res.status);
  } catch (err) {
    // 忽略主动取消的请求抛出的错误
    if (err.name !== 'CanceledError') {
      Log('Error: \n' + err.message);
    }
  }
}, []);

useEffect(() => {
  const abortController = new AbortController();
  sendState(lampsState, abortController.signal);
  // 依赖变更或组件卸载时,取消上一次未完成的请求
  return () => abortController.abort();
}, [lampsState, sendState]);

注意:React严格模式的双调用逻辑仅在开发环境生效,生产环境不会触发,但用useEffect监听状态触发用户主动操作类请求的写法依然存在时序不可控、闭包隐患,不推荐使用。

内容的提问来源于stack exchange,提问作者Umeshan Weerasekara

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 06:24:24