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

React组件更新状态触发Too many re-renders无限重渲染报错排查

问题根因

无限重渲染的核心触发逻辑是:你在组件的渲染执行阶段(return块内)直接调用了countDone函数,而这个函数内部执行了setProgress状态更新操作,形成了死循环:

  1. 组件首次渲染 → 执行return中的countDone → 调用setProgress修改状态
  2. 状态变更触发组件重新渲染 → 再次执行return中的countDone → 再次调用setProgress
  3. 流程无限重复,直接触发React的渲染次数上限报错。

除此之外你的代码里还有几个会导致运行失败的语法/逻辑问题:

  • 拼写错误:调用countDone时传入的参数名是groupOfTakss,和定义的状态名groupOfTasks不一致,会直接抛出引用错误
  • 语法错误:JS中布尔值为小写true,不是大写开头的True;groupOfTasks.map的回调函数缺少闭合括号
  • 语法错误:JSX中元素的类名属性要写className,不能写原生HTML的class,否则会抛出React属性警告
  • 逻辑错误:完成数/总任务数得到的是0-1区间的小数,直接作为百分比传入进度条的话,100%完成只会显示1%,需要乘100转成0-100的百分比值;任务列表为空时直接做除法会得到NaN,导致进度条显示异常
  • 用法问题:map是用来遍历生成新数组的,单纯做计数遍历用filter/reduce/forEach更合适
修复方案

不要在渲染阶段直接执行带状态更新的逻辑,进度属于依赖任务列表的派生值,推荐两种实现方式:

方式1:用useMemo直接计算派生值(最简洁)

不需要单独维护progress状态,直接根据groupOfTasks计算进度即可,省去额外的状态维护成本:
首先修正Progress组件的类名写法:

const Progress = ({done}) => {
  return (
    <div className="progress">
      <div className="progress-done" 
        style={{
          opacity: 1,
          width: `${done}%`
        }}>
        {done}%
      </div>
    </div>
  )
}

修正后的App组件代码:

import { useState, useMemo, useEffect } from 'react'

const App = () => {
  const [groupOfTasks, setGroupOfTasks] = useState([])

  // 此处省略从数据库拉取任务列表的逻辑,建议放在useEffect中执行

  // 仅当groupOfTasks变化时才重新计算进度
  const progress = useMemo(() => {
    const total = groupOfTasks.length
    if (total === 0) return 0 // 空列表默认进度为0,避免NaN
    const completedCount = groupOfTasks.filter(task => task.completed).length
    return Math.round(completedCount / total * 100) // 转换为0-100的百分比值
  }, [groupOfTasks])

  return (
    <div>
      <Progress done={progress}/>
    </div>
  )
}

方式2:在任务列表更新时同步计算进度

如果你确实需要单独维护progress状态,把计算逻辑移到groupOfTasks变更的监听钩子中执行,不要放在渲染阶段:

import { useState, useEffect } from 'react'

const App = () => {
  const [progress, setProgress] = useState(0)
  const [groupOfTasks, setGroupOfTasks] = useState([])

  // 组件挂载时从数据库拉取任务列表
  useEffect(() => {
    const fetchTasks = async () => {
      // 接口请求逻辑,拿到数据后调用setGroupOfTasks更新
    }
    fetchTasks()
  }, [])

  // 仅当任务列表变化时,才重新计算进度并更新状态
  useEffect(() => {
    const total = groupOfTasks.length
    if (total === 0) {
      setProgress(0)
      return
    }
    const completedCount = groupOfTasks.filter(task => task.completed).length
    setProgress(Math.round(completedCount / total * 100))
  }, [groupOfTasks])

  return (
    <div>
      <Progress done={progress}/>
    </div>
  )
}
无限重渲染问题通用排查思路
  • 先检查组件渲染阶段(函数组件顶层、return块内)有没有直接调用状态更新函数(setXxx),这类调用会在每次渲染时触发新的渲染,直接导致死循环
  • 再检查useEffect的依赖数组是否配置正确:如果漏写依赖、或者依赖项是每次渲染都会生成新引用的对象/函数,会导致effect无限触发,连带触发状态更新形成循环
  • 最后检查传给子组件的回调、props是否每次渲染都是新引用,触发子组件重复更新,若子组件更新又回调父组件修改状态,也会形成渲染循环

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 20:27:19