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

React-Native异步请求JSON解析偶发undefined错误排查

问题根因

你遇到的undefined报错不是异步逻辑提前执行导致的,是代码存在3个确定性逻辑错误,偶发是因为React状态更新的时序不固定:

  • 错误1:读取了闭包里的旧状态值:你在then回调中调用setSessionDetails之后,立刻读取sessionDetails做判断、取sessionDetails.session_notes,但React状态更新是异步批处理的,此时sessionDetails还是初始的undefined,根本没拿到刚请求返回的值
  • 错误2:取值路径错误:你已经取了sessionData.session_details[0]作为详情对象,后面格式化日期时却写sessionData.session_details.session_date,session_details是数组,直接读属性必然返回undefined
  • 错误3:缺少边界兜底:没处理fetch请求失败、接口返回字段缺失、session_details为空数组的情况,一旦接口返回异常直接抛错
  • 额外问题:你贴的接口返回JSON示例本身语法错误,{"session_details [位置缺少冒号,如果后端实际返回该格式,JSON解析阶段就会直接报错,需要先确认接口返回格式合法

修复方案(Promise.then 版本)

所有派生数据直接基于接口返回的原始值计算,不要依赖未更新完成的React状态,同时补全所有兜底逻辑:

export function SessionDetails( { route }  ) {
    const { sessionUid } = route.params;

    // 所有状态给明确初始值,避免留undefined导致渲染报错
    const [loadingSessionData, setLoadingSessionData] = useState(true);
    const [sessionDetails, setSessionDetails] = useState(null);
    const [sessionClothes, setSessionClothes] = useState([]);
    const [sessionGear, setSessionGear] = useState([]);
    const [sessionTasks, setSessionTasks] = useState([]);
    const [formattedDate, setFormattedDate] = useState('');
    const [currentNote, setCurrentNote] = useState('');
    const [saveNotesVisible, setSaveNotesVisible] = useState(false);
    const [loadError, setLoadError] = useState(null);

    function getSessionDetails(sessionUid){
      const url = `${apiUrl}sessions/detail.php?session_uid=${sessionUid}`;
      return fetch(url, {
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json'
        }
      }).then((response) => {
        // fetch默认仅网络错误会reject,需手动判断HTTP状态码
        if (!response.ok) throw new Error(`请求失败,状态码:${response.status}`);
        return response.json();
      })
    };

    useEffect(() => {
      setLoadingSessionData(true);
      setSaveNotesVisible(false);
      setLoadError(null);

      getSessionDetails(sessionUid)
        .then((sessionData) => {
          // 可选链兜底空数据
          const detail = sessionData?.session_details?.[0] || null;
          const clothes = sessionData?.clothes_items || [];
          const gear = sessionData?.gear_items || [];
          const tasks = sessionData?.task_items || [];
          
          // 直接用原始返回值计算派生数据,不读取state
          const dateStr = detail?.session_date 
            ? moment(detail.session_date).format("dddd, MMMM Do, YYYY.")
            : 'There is a problem';
          const note = detail?.session_notes || '';

          // 批量更新所有状态
          setSessionDetails(detail);
          setSessionClothes(clothes);
          setSessionGear(gear);
          setSessionTasks(tasks);
          setFormattedDate(dateStr);
          setCurrentNote(note);
        })
        .catch(err => {
          setLoadError(err.message);
        })
        .finally(() => {
          setLoadingSessionData(false);
        })

    }, [sessionUid]); // 依赖数组加入sessionUid,避免参数变化不触发重新请求

    // 渲染层兜底
    if (loadingSessionData) return <Text>加载中...</Text>;
    if (loadError) return <Text>加载失败:{loadError}</Text>;
    if (!sessionDetails) return <Text>数据不存在</Text>;

    // 下方写正常页面渲染逻辑
    return <></>
}

修复方案(async/await 版本)

逻辑和上述版本完全一致,仅替换useEffect内的异步写法即可:

useEffect(() => {
  const fetchData = async () => {
    setLoadingSessionData(true);
    setSaveNotesVisible(false);
    setLoadError(null);
    try {
      const sessionData = await getSessionDetails(sessionUid);
      const detail = sessionData?.session_details?.[0] || null;
      const clothes = sessionData?.clothes_items || [];
      const gear = sessionData?.gear_items || [];
      const tasks = sessionData?.task_items || [];
          
      const dateStr = detail?.session_date 
        ? moment(detail.session_date).format("dddd, MMMM Do, YYYY.")
        : 'There is a problem';
      const note = detail?.session_notes || '';

      setSessionDetails(detail);
      setSessionClothes(clothes);
      setSessionGear(gear);
      setSessionTasks(tasks);
      setFormattedDate(dateStr);
      setCurrentNote(note);
    } catch (err) {
      setLoadError(err.message);
    } finally {
      setLoadingSessionData(false);
    }
  }
  fetchData();
}, [sessionUid]);

额外注意点
  • 单请求场景不需要套Promise.all,该方法仅适合并行等待多个异步任务的场景
  • 可选链操作符?.是处理嵌套对象取值的最简便方式,路径上任意一级为null/undefined时会直接返回undefined,不会抛错
  • 不要只写请求成功的回调,必须加catch/finally处理异常场景,保证loading状态能正确关闭
  • 数组类型的状态初始值设为空数组[],避免渲染时调用map方法直接报错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 20:21:28