React Native 页面重渲染后已加载的接口文本消失问题排查
React Native 个人中心页重渲染后文本消失问题排查
核心原因定位
结合现象和现有代码的共性问题,问题触发点集中在3个维度:
- 空值覆盖无防护:接口取值链路为
data.data.data.result,当重渲染触发页面重新聚焦、再次执行请求时,如果接口返回200响应但结构异常(比如鉴权态丢失返回空成功体、结构未匹配同步),会直接将userInfo赋值为undefined,触发所有绑定该状态的文本消失。由于是200响应,不会进入catch分支,控制台无任何报错,容易被忽略。 - 并发请求时序混乱:未做请求取消逻辑,重渲染时可能多次触发
getData,不同请求的响应返回顺序不确定,后返回的空响应会直接覆盖首次拿到的有效用户数据。同时focus监听若因依赖问题被重复绑定,会进一步放大并发请求的概率。 - 布局计算异常:原代码中
TouchableHighlight直接嵌套无样式的Fragment,在部分RN版本中,重渲染时组件布局计算可能出现异常,导致内部文本被折叠隐藏。
分步排查与修复
- 增加取值校验:在
setUserInfo前判断接口返回的result是否为合法对象,只有结构匹配时才更新状态,避免空值直接覆盖原有有效数据。 - 增加请求取消机制:页面失焦、组件卸载、发起新请求前,主动abort未完成的axios请求,杜绝并发响应乱序覆盖状态的问题。
- 调整JSX结构:将
TouchableHighlight下的Fragment替换为带默认样式的View,修复重渲染时的布局计算异常。 - 校验focus监听的绑定次数:在focus回调内加日志,确认一次页面聚焦是否会触发多次
getData执行,排查重复绑定监听的问题。
修复参考代码
import { useState, useEffect, useRef } from 'react'; import axios from 'axios'; const MyPage = ({ navigation }) => { const [userInfo, setUserInfo] = useState(); const abortControllerRef = useRef(null); const getData = async () => { // 取消上一次未完成的请求 if (abortControllerRef.current) { abortControllerRef.current.abort(); } abortControllerRef.current = new AbortController(); try { const res = await axios.get("http://everyweardev-env.eba-azpdvh2m.ap-northeast-2.elasticbeanstalk.com/api/v1/user", { signal: abortControllerRef.current.signal }); const targetResult = res?.data?.data?.result; // 仅当返回合法对象时更新状态,避免空值覆盖 if (targetResult && typeof targetResult === 'object') { setUserInfo(targetResult); } } catch (err) { // 主动取消的请求不输出错误日志 if (err.code !== 'ERR_CANCELED') { console.log('用户信息获取失败', err); } } }; useEffect(() => { const focusListener = navigation.addListener("focus", () => { getData(); }); // 组件卸载时清理监听、取消未完成请求 return () => { focusListener(); if (abortControllerRef.current) { abortControllerRef.current.abort(); } }; }, [navigation]); return ( <View style={styles.container}> <View style={styles.profileContainer}> <Image source={require("../../images/profile.png")} /> <View style={styles.profileName}> <TouchableHighlight> {/* 替换无样式Fragment为可计算布局的View */} <View> <Text style={{ fontWeight: "bold", marginBottom: hp("1%"), fontSize: wp("7%"), }} > {userInfo?._name ? `${userInfo._name} 님` : ''} </Text> <Text style={{ fontSize: wp("5%"), }} > {userInfo?._mail || ''} </Text> </View> </TouchableHighlight> </View> </View> </View> ) }
内容的提问来源于stack exchange,提问作者byungju
相关产品推荐
相关产品推荐

