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

Next.js 12项目函数重复渲染过多问题求助

问题求助:React组件重复渲染+子元素内调用函数错误

项目环境

使用Next.js 12,package.json配置如下:

{
  "name": "next12firebase",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "eslint": "8.28.0",
    "eslint-config-next": "13.0.5",
    "firebase": "^9.14.0",
    "next": "13.0.5",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-firebase-hooks": "^5.1.1",
    "react-icons": "^4.7.1"
  },
  "devDependencies": {
    "autoprefixer": "^10.4.13",
    "postcss": "^8.4.19",
    "tailwindcss": "^3.2.4"
  }
}

需求

通过react-firebase-hooks的useCollectionData获取数据,当loading变为false后,执行数据处理逻辑,将结果存入datamap状态,后续用该状态遍历渲染内容。

当前代码问题

当前在JSX渲染逻辑中直接处理数据并调用setDatamap,导致两个核心错误:

  • 每次渲染都会触发状态更新,引发无限重复渲染
  • 在React子元素内调用状态更新函数,违反React渲染规则

当前临时代码:

export default function portfolio() {
  // ...省略其他代码
  const [docs, loading, error] = useCollectionData(q);

  return (
    // ...省略其他渲染内容
    {!loading &&
      docs.map((doc) => {
        console.log(doc)
        let tic = doc.stockTicker
        let data = {
          shares: doc.shares,
          price: doc.price,
          type: doc.type
        }
        
        let map = datamap;
        console.log(datamap)
        if(Array.from(datamap.keys).includes(tic) === false) {
          setDatamap(datamap.set(tic, [data]));
          console.log(tic + " " + data)
        } else {
          setDatamap((map.get(tic)).push(data))
        }
        console.log(datamap)
      })
    }
    // ...省略其他渲染内容
  )
}

解决方案

将数据处理逻辑移至useEffect钩子中,仅在loading结束且数据更新时执行,同时保证状态更新的不可变性:

export default function Portfolio() {
  const [docs, loading, error] = useCollectionData(q);
  const [datamap, setDatamap] = useState(new Map());

  // 监听loading和docs变化,处理数据
  useEffect(() => {
    if (!loading && docs?.length) {
      // 创建新Map,避免直接修改原状态
      const updatedMap = new Map(datamap);
      
      docs.forEach(doc => {
        const stockTicker = doc.stockTicker;
        const itemData = {
          shares: doc.shares,
          price: doc.price,
          type: doc.type
        };

        if (updatedMap.has(stockTicker)) {
          // 用扩展运算符创建新数组,保证引用更新
          const existingItems = updatedMap.get(stockTicker);
          updatedMap.set(stockTicker, [...existingItems, itemData]);
        } else {
          updatedMap.set(stockTicker, [itemData]);
        }
      });

      setDatamap(updatedMap);
    }
  }, [loading, docs]); // 依赖项:loading或docs变化时触发

  // 渲染逻辑
  return (
    <div>
      {loading ? (
        <div>加载中...</div>
      ) : (
        Array.from(datamap.entries()).map(([ticker, items]) => (
          <div key={ticker}>
            <h3>{ticker}</h3>
            {items.map((item, index) => (
              <div key={index}>
                股份:{item.shares} | 价格:{item.price} | 类型:{item.type}
              </div>
            ))}
          </div>
        ))
      )}
    </div>
  );
}

关键说明

  • 避免渲染时修改状态:数据处理放在useEffect中,仅在依赖项变化时执行,杜绝无限重渲染
  • 状态不可变性:更新Map和数组时创建新实例,React通过引用变化识别状态更新,确保组件正确重渲染
  • 清晰的渲染逻辑:渲染阶段仅使用处理好的datamap,不包含任何状态修改逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 07:25:24