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
相关产品推荐
相关产品推荐

