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

React报错:渲染Duck组件时更新HotReload组件问题求助

问题排查:React鸭子生成组件的状态更新警告及功能修复

错误信息

Warning: Cannot update a component (HotReload) while rendering a different component (Duck). 如需定位Duck内错误的setState()调用,请按照官方文档中描述的堆栈跟踪操作
at Duck (webpack-internal:///(app-pages-browser)/./src/app/componentsTESTING/Duck.js:31:9)
at div
at SummonDuckWrapper (webpack-internal:///(app-pages-browser)/./src/app/componentsTESTING/SummonDuckWrapper.js:17:78)
at main
at Home
at StaticGenerationSearchParamsBailoutProvider

现有代码

SummonDuckWrapper.js

export const SummonDuckWrapper = () => {
    const [ducks, setDucks] = useState([])

    const addDuck = (duck, picture) => {
        setDucks([...ducks, {location: duck, duckPic: picture}])
    }
    
    React.useEffect(() => {
        console.log(ducks)
    });

  return (
    <div>
      <SummonDuckButton addDuck={addDuck}/>
      {ducks.map((duck, duckPic, index) => (
        <Duck location={duck} picture={duckPic} key={index}/>
      ))}
    </div>
  )
}

export default SummonDuckWrapper

SummonDuckButton.js

export const SummonDuckButton = ({ addDuck, duckImage }) => {
    const [left, setLeft] = useState(0);
    const [top, setTop] = useState(0);

    useEffect(() => {
        setLeft((Math.floor(Math.random() * screen.width)))
        setTop(Math.floor(Math.random() * screen.height))
    }, [])

    const location = {
        left: left + 'px',
        top: top + 'px'
    }

    const summonDuck = () => {
        setLeft(Math.floor(Math.random() * screen.width));
        setTop(Math.floor(Math.random() * screen.height));
        const randomImage = Math.floor(Math.random() * 3);
        
        addDuck(location, randomImage)
    }

    return (
        <button onClick={summonDuck} className='I removed this so it'll be easier to read'>
            Summon Duck
        </button>
    )
}

export default SummonDuckButton

Duck.js(问题组件)

const images = [
    //these are SVGs that I imported
    <DuckFamily />,
    <DuckOne />,
    <DuckTwo />
];

const Duck = () => {
    const thePicture = images[{ picture }]
    return (
        <div style={location}>
            <img src={thePicture} />
        </div>
    )
}

export default Duck

问题分析

  1. Duck组件未接收props:直接使用location和picture会导致未定义错误,React渲染时的异常触发了HotReload组件的更新警告。
  2. map参数传递错误:ducks.map((duck, duckPic, index))中第二个参数是索引值,不是duckPic,导致传递给Duck的picture属性错误。
  3. SummonDuckButton状态冗余且异步问题:按钮不需要保存位置状态,且setLeft/setTop是异步的,调用addDuck时传递的是旧的location值。
  4. Duck组件图片渲染错误:images[{ picture }]语法错误,且SVG组件不能用<img>标签渲染,需直接使用组件。
  5. 旧实现的随机值不稳定:之前的RandomDuck在每次渲染时重新生成随机值,导致已有鸭子变化,根源是未将随机数据固化到ducks数组中。

修复步骤

  1. 修正SummonDuckWrapper的map逻辑:正确解构ducks数组中的每个对象,传递正确的location和picture属性。
  2. 简化SummonDuckButton:移除left/top状态,在summonDuck函数中直接生成随机位置和图片,同步传递给addDuck。
  3. 修复Duck组件:接收props参数,正确获取location和picture,直接渲染SVG组件。
  4. 固化随机数据:在添加鸭子时生成所有随机数据并存入ducks数组,确保每个鸭子的数据固定不变。

完整修复代码

SummonDuckWrapper.js

import { useState, useEffect } from 'react';
import SummonDuckButton from './SummonDuckButton';
import Duck from './Duck';

export const SummonDuckWrapper = () => {
    const [ducks, setDucks] = useState([]);

    const addDuck = (location, pictureIndex) => {
        // 函数式更新确保基于最新数组状态添加鸭子
        setDucks(prevDucks => [...prevDucks, { location, pictureIndex }]);
    };
    
    useEffect(() => {
        console.log(ducks);
    }, [ducks]); // 仅当ducks变化时打印日志

    return (
        <div style={{ position: 'relative', minHeight: '100vh' }}>
            <SummonDuckButton addDuck={addDuck} />
            {ducks.map((duck, index) => (
                <Duck 
                    location={duck.location} 
                    pictureIndex={duck.pictureIndex} 
                    key={index} 
                />
            ))}
        </div>
    );
};

export default SummonDuckWrapper;

SummonDuckButton.js

export const SummonDuckButton = ({ addDuck }) => {
    const summonDuck = () => {
        // 直接生成随机位置,无需存入组件状态
        const left = `${Math.floor(Math.random() * window.innerWidth)}px`;
        const top = `${Math.floor(Math.random() * window.innerHeight)}px`;
        const randomImageIndex = Math.floor(Math.random() * 3);
        
        // 传递最新生成的位置和图片索引
        addDuck({ left, top }, randomImageIndex);
    };

    return (
        <button onClick={summonDuck}>
            Summon Duck
        </button>
    );
};

export default SummonDuckButton;

Duck.js

import DuckFamily from './path-to-duck-family'; // 替换为你的SVG实际路径
import DuckOne from './path-to-duck-one';
import DuckTwo from './path-to-duck-two';

const images = [
    <DuckFamily />,
    <DuckOne />,
    <DuckTwo />
];

// 接收props参数
const Duck = ({ location, pictureIndex }) => {
    const selectedPicture = images[pictureIndex];
    
    return (
        <div 
            style={{ 
                ...location, 
                position: 'absolute', // 绝对定位确保位置生效
                zIndex: 10 
            }}
        >
            {/* 直接渲染SVG组件,无需img标签 */}
            {selectedPicture}
        </div>
    );
};

export default Duck;

额外说明

  • 给SummonDuckWrapper添加position: relative,确保鸭子的绝对定位基于该容器;给Duck容器添加position: absolute,让left/top样式生效。
  • 使用setDucks(prevDucks => [...prevDucks, ...])的函数式更新,避免因状态异步更新导致的鸭子数据丢失。
  • 移除了SummonDuckButton中不必要的useEffect和状态,简化逻辑的同时解决了异步状态导致的旧值传递问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 23:20:11