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

React:按不同时机多次渲染组件及解决渲染异常问题

问题描述

我从用户录入的数据库中获取到一个对象数组:

const play = data.plays;

数组结构示例:

// [{time: "30", host: "John", booths: ["1","2","3"]},
//  {time: "60", host: "Maria", booths: ["4","5","6"]},
//  ...]

启动应用时出现大量警告与错误,比如:

Uncaught TypeError: Cannot read properties of undefined (reading 'length')

修改useEffect的依赖数组后页面能显示,但只会渲染数组的最后一个对象。我尝试用useState管理time、host、box状态,也没得到预期效果。以下是我的Booth组件代码:

function Booth(boxId, label) {

let time, host, box, allPlay

    const play = data.plays;
        console.log(play)
    
    if (play == null) {
        time = 0
        host = ''
        box = []
    }  
    
    useEffect(() => {
        
        allPlay = play.map((play) => {
            time = play.time
            host = play.host
            box = play.booths
            
            console.log(time,host,box)          
    
        let timer = parseInt(time)        
       
       const currentRender = setInterval (() => {
    
                for (let i = 0; i < box.length; i++) {
                    for (let k = 0; k < name.length; k++) {
                    
                        if (box[i] === name[k].value) {
    
                            const activeBox = document.getElementById(`${box[i]}`)
                                document.getElementById(`time`+`${box[i]}`).innerText = `Time : ` + `${timer}`
                                document.getElementById(`host`+`${box[i]}`).innerText = `Host : ` + `${host}`
                        
                            if (timer > 15) {
                                activeBox.classList.add("red")
                            } else if (timer > 5){
                                activeBox.classList.add("yellow")
                            } else if (timer > 0){
                                activeBox.classList.add("green")
                            } else if (timer === 0){
                                host = ''
                                time = 0
                                activeBox.className = "white"
                            }
                        }
                    }
            }
    
            if (timer > 0) {
                timer--;
            }
            
       }, 1000);   
       return () => clearInterval(currentRender);   
       
    })
        
    },[allPlay])   // react-hooks/exhaustive-deps     
                // but if i change the value of the array, it will re-render the component
    
    return (
        <div className={`box col-1`}>
            <h4>{label}</h4>
            <p className="white" id={`time`+`${boxId}`}>Time:</p>
            <p className="white" id={`host`+`${boxId}`}>Host:</p>
        </div>
    )

}

export default Booth;

页面中有多个该组件,希望它们能根据用户输入和时间改变状态,并按不同时机完成渲染。


解决方案

1. 修复组件参数传递错误

React组件接收的是props对象,不是单独的参数,需改为解构写法:

function Booth({ boxId, label }) {
  // ... 后续代码
}

这样才能正确获取boxId和label属性。

2. 用React状态管理替代DOM直接操作

直接通过getElementById操作DOM违背React状态驱动理念,容易引发渲染冲突。每个Booth组件应独立管理自身状态:

  • 用useState存储倒计时、主持人信息、样式类名
  • 无需全局遍历所有booth,组件只关注自身状态变化

3. 重构useEffect逻辑与依赖

原代码中useEffect依赖allPlay为内部赋值变量,会导致依赖错误;循环创建定时器会覆盖之前的变量,最终仅保留最后一个对象的定时器。重构后的组件示例:

import { useState, useEffect } from 'react';

function Booth({ boxId, label, initialTime, initialHost }) {
  // 组件内部状态
  const [timer, setTimer] = useState(parseInt(initialTime) || 0);
  const [host, setHost] = useState(initialHost || '');
  const [boxClass, setBoxClass] = useState('white');

  // 倒计时逻辑
  useEffect(() => {
    if (timer <= 0) return;

    const interval = setInterval(() => {
      setTimer(prev => {
        const newTimer = prev - 1;
        // 根据时间更新样式
        if (newTimer > 15) {
          setBoxClass('red');
        } else if (newTimer > 5) {
          setBoxClass('yellow');
        } else if (newTimer > 0) {
          setBoxClass('green');
        } else {
          setBoxClass('white');
          setHost('');
        }
        return newTimer;
      });
    }, 1000);

    return () => clearInterval(interval);
  }, [timer]);

  // 外部数据更新时重置状态(适配用户输入修改)
  useEffect(() => {
    setTimer(parseInt(initialTime) || 0);
    setHost(initialHost || '');
    setBoxClass('white');
  }, [initialTime, initialHost]);

  return (
    <div className={`box col-1 ${boxClass}`}>
      <h4>{label}</h4>
      <p className="white">Time: {timer > 0 ? timer : ''}</p>
      <p className="white">Host: {host}</p>
    </div>
  );
}

export default Booth;

4. 父组件正确传递props

在父组件中遍历play数组,为每个booth分配对应初始值:

// 父组件示例
function BoothContainer() {
  const [plays, setPlays] = useState(data.plays || []);

  // 此处可添加处理用户输入更新plays的逻辑

  // 处理空数据状态
  if (!plays || plays.length === 0) {
    return <div>No booth data available</div>;
  }

  return (
    <div className="row">
      {plays.map((playItem, idx) => {
        return playItem.booths.map(boxId => (
          <Booth
            key={`booth-${boxId}-${idx}`}
            boxId={boxId}
            label={`Booth ${boxId}`}
            initialTime={playItem.time}
            initialHost={playItem.host}
          />
        ));
      })}
    </div>
  );
}

关键问题总结

  • 组件参数未正确解构,导致boxId等属性无法获取
  • 直接操作DOM违背React状态驱动原则,引发渲染冲突
  • useEffect依赖错误+循环创建定时器,导致仅最后一个对象生效
  • 未为每个组件独立管理状态,多个组件共享变量导致覆盖

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 18:45:38