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

React Native热重载时触发productsCtg.map未定义TypeError报错

React Native商品列表页map调用报错问题

问题场景

开发React Native商品列表页面时触发TypeError: undefined is not a function (near '...productsCtg.map...')报错,现有实现逻辑:

  • 渲染逻辑:判断productsCtg数组长度大于0时,调用map遍历渲染ProductList组件,否则渲染「未找到商品」占位提示
  • 数据逻辑:使用useState初始化productsCtg默认值为空数组[],通过useFocusEffect+useCallback配合axios拉取后端接口数据,请求成功后调用setProductsCtg存入返回商品,useFocusEffect清理函数中已注释setProductsCtg([])的重置逻辑
  • 异常表现:应用首次启动时商品展示正常,开发环境下不修改代码直接保存文件触发热重载时必现上述报错,再次保存后商品又恢复正常展示
  • 调试现象:组件return前打印productsCtg,前5次输出为空数组,后续输出重复的商品数组(单个数组包含6个商品对象),map回调内打印也会输出重复的商品数据日志

根因分析

  1. 渲染判断存在逻辑漏洞
    现有判断逻辑productsCtg.length > 0默认productsCtg永远是数组类型,一旦某次状态更新将其赋值为undefined/null/其他非数组值,读取length属性、调用map方法都会直接抛出类型错误。
  2. 热重载副作用重复执行
    React Native开发环境的Fast Refresh热更新机制会尝试保留组件状态,但如果useFocusEffect依赖声明不全、未做请求取消处理,热更新时旧的副作用闭包不会被销毁,会和新挂载组件的请求逻辑同时执行,多次触发setProductsCtg就会出现重复打印商品数组的现象,极端时序下状态会被异常值覆盖触发报错。
  3. 接口赋值缺少类型校验
    多数同类报错都和接口取值不规范有关:如果请求回调中没有精准取到接口返回的数组字段(比如误将整个axios响应对象、接口返回的非数组字段存入state),首次加载时如果时序刚好匹配不会触发问题,热重载时序变化后就会直接触发类型错误。

修复方案

1. 加固渲染层类型校验

从渲染层兜底,永远先判断值为数组再调用数组方法,彻底杜绝非数组值调用map的问题:

// 替换原有渲染判断逻辑
{Array.isArray(productsCtg) && productsCtg.length > 0 ? (
  productsCtg.map(product => (
    <ProductList key={product.id || Math.random()} product={product} />
  ))
) : (
  <Text>未找到商品</Text>
)}

2. 补全副作用清理与依赖

不要直接注释清理函数,必须在清理逻辑中取消未完成的请求,避免闭包残留导致的重复更新;同时接口赋值前必须做类型校验,确保存入state的永远是数组:

import { useState, useCallback } from 'react';
import { useFocusEffect } from '@react-navigation/native';
import axios from 'axios';

// 组件内状态定义
const [productsCtg, setProductsCtg] = useState([]);

useFocusEffect(
  useCallback(() => {
    // 生成请求取消令牌
    const cancelSource = axios.CancelToken.source();

    const loadCategoryProducts = async () => {
      try {
        const res = await axios.get('替换为你的商品接口地址', {
          cancelToken: cancelSource.token
        });
        // *重点*:精准取接口返回的数组字段,赋值前校验类型
        // 注意根据自己的接口返回结构调整取值路径,不要直接传res
        const productList = res.data?.data?.list || [];
        setProductsCtg(Array.isArray(productList) ? productList : []);
      } catch (err) {
        if (!axios.isCancel(err)) {
          console.error('商品列表加载失败', err);
          setProductsCtg([]);
        }
      }
    };

    loadCategoryProducts();

    // 清理逻辑:组件卸载/热更新/失焦时取消未完成请求
    return () => {
      cancelSource.cancel();
      // 如果不需要页面失焦保留列表数据,可打开下方重置逻辑
      // setProductsCtg([]);
    };
  }, []) // 注意:如果回调内引用了外部变量/方法,必须加入依赖数组,不要留空依赖
);

3. 开发环境热重载兼容

如果上述修改后仍偶发热重载报错,可以给商品列表页在开发环境下加动态key,触发热更新时强制重新挂载组件,避免旧状态缓存干扰:

// 路由配置示例
<Stack.Screen
  name="ProductCategory"
  component={ProductListPage}
  key={__DEV__ ? `product-list-${Date.now()}` : 'product-list-page'}
/>

内容的提问来源于stack exchange,提问作者stephen-mhuli

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 04:21:12