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

React环形进度条组件动画报错求助:空值读取与可选链赋值错误

问题排查:React环形进度条动画报错分析与修复

问题描述

我编写了一个React环形进度条组件,尝试实现进度动画时出现报错:读取style、background和innerText时提示“Error: cannot read properties of null.”;使用可选链操作符?.时又出现“Error: Invalid left-hand side in assignment expression”错误,希望有人能帮忙排查代码问题。

用户提供的代码:

import React, { Component } from 'react'

class ProgressBar extends Component {


  render() { 

      const progressContainer = document.querySelector('.progress-container');
      const valueContainer = document.querySelector('.progress-value');
      const speed = 400;
      let progressValue = 0;
      let progressEndValue = 70;
      const createProgress = setInterval(() => {
          progressValue++;
        valueContainer?.innerText = `${progressValue} %`
        progressContainer?.style?.background = `conic-gradient(
            rgb(239 68 68) ${progressValue * 3.6}deg,
            black 1deg,
            rgb(241 245 249) 1deg,
        )`
        if (progressValue == progressEndValue) {
            clearInterval(createProgress);
        }
    }) 

    return (
      <div className='progress progress-container w-full h-full rounded-full flex justify-center items-center'>
        <div className="progress progress-value w-3/4 h-3/4 rounded-full bg-slate-100 flex justify-center items-center">
            <h1>0 %</h1>
        </div>
      </div>
    ) 
  }
}


export default ProgressBar;

错误原因分析

  • DOM元素获取时机错误:在render()方法中直接调用document.querySelector时,组件尚未完成渲染挂载,此时页面中还不存在目标DOM元素,返回值为null,后续读取其属性自然报错。
  • 可选链操作符使用违规:可选链?.仅用于读取属性,不能用于赋值操作的左侧,valueContainer?.innerText = ...这种写法不符合JavaScript语法规范,因此触发语法错误。
  • 定时器缺少间隔参数:setInterval未传入第二个间隔时间参数,会导致定时器以最高频率执行,不仅动画异常还会占用大量性能。

修复后的代码

import React, { Component } from 'react'

class ProgressBar extends Component {
  constructor(props) {
    super(props);
    // 创建React引用用于绑定DOM元素
    this.progressContainer = React.createRef();
    this.valueContainer = React.createRef();
    this.timer = null;
  }

  componentDidMount() {
    const speed = 40; // 调整为合适的动画间隔
    let progressValue = 0;
    const progressEndValue = 70;
    
    this.timer = setInterval(() => {
      progressValue++;
      // 先判断DOM元素是否存在,再执行赋值操作
      if (this.valueContainer.current) {
        this.valueContainer.current.innerText = `${progressValue} %`;
      }
      if (this.progressContainer.current) {
        this.progressContainer.current.style.background = `conic-gradient(
          rgb(239 68 68) ${progressValue * 3.6}deg,
          rgb(241 245 249) ${progressValue * 3.6}deg
        )`;
      }
      // 达到目标进度后清除定时器
      if (progressValue === progressEndValue) {
        clearInterval(this.timer);
      }
    }, speed);
  }

  componentWillUnmount() {
    // 组件卸载时清理定时器,避免内存泄漏
    if (this.timer) {
      clearInterval(this.timer);
    }
  }

  render() { 
    return (
      <div 
        ref={this.progressContainer}
        className='progress progress-container w-full h-full rounded-full flex justify-center items-center'
      >
        <div 
          ref={this.valueContainer}
          className="progress progress-value w-3/4 h-3/4 rounded-full bg-slate-100 flex justify-center items-center"
        >
          <h1>0 %</h1>
        </div>
      </div>
    ) 
  }
}

export default ProgressBar;

修复关键点说明

  • 使用React.createRef()创建DOM引用,在componentDidMount生命周期中操作DOM,此时组件已完成挂载,能正确获取到目标元素。
  • 赋值前通过current属性判断元素是否存在,替代错误的可选链赋值写法,避免null报错。
  • 给setInterval添加间隔时间参数,控制动画速度;同时在componentWillUnmount中清理定时器,防止内存泄漏。
  • 优化渐变颜色逻辑,移除多余的黑色过渡,让环形进度的视觉效果更自然。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 10:30:48