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

如何使用useEffect监听变量变更修改useState且不触发无限循环?

问题根源
  • 无限循环的核心诱因是Id组件每次渲染都会生成全新的idData对象引用:你在组件顶层写了var idData = {...props.id},扩展运算符每次执行都会创建新内存地址的对象,这个对象被作为useEffect的依赖项。
  • 该useEffect内部调用了父组件传入的setId更新父级状态,父级状态更新后会重新渲染Id组件,再次生成新的idData对象,再次触发useEffect执行,由此形成死循环。
  • 额外逻辑问题:你直接修改局部变量idData的data属性的写法是无效的,React不会监听局部变量的变化,你点击加减、刷新按钮时页面展示的ID不会发生任何更新。
修复方案

调整Id组件逻辑,直接使用父组件传入的状态,所有修改走父组件的setId方法即可,删除多余的局部变量和导致死循环的副作用:

import React, { useEffect } from "react";
import ReactTooltip from "react-tooltip";
import { FaQuestionCircle, FaRedoAlt } from 'react-icons/fa';

interface Props {
    setId: React.Dispatch<React.SetStateAction<{data: number, componentRef: React.RefObject<HTMLDivElement>}>>;
    id : {data: number, componentRef:React.RefObject<HTMLDivElement>};
}

const Id : React.FC<Props> = ( props : Props ) => {
    // 直接操作父组件传入的状态,不要创建多余的局部副本
    const generateNewId = () => {
        props.setId(prev => ({
            ...prev,
            data: Math.floor( Math.random()*1000 ) + 1
        }));
    }

    // 组件初始化生成随机ID
    useEffect(()=>{
        generateNewId();
    },[generateNewId])

    const modifyId = (sign : string) => {
        props.setId(prev => ({
            ...prev,
            data: sign === "+" ? prev.data + 1 : prev.data - 1
        }));
    }

    return(
        <div>
            <ReactTooltip id="counter-info">
                <p>点击刷新按钮即可生成随机ID</p>
                <p className="text-bold">什么是ID?</p>
                <p className="text-bold">ID会追加在用户名末尾,确保你的用户名始终唯一</p>
            </ReactTooltip> 
            <h2>你的ID: {props.id.data}
                <span data-tip data-for="counter-info">
                    <FaQuestionCircle/>
                </span>
            </h2>
            <button className="btn rounded-circle btn-dark" onClick={()=>modifyId("-")}>-</button>
            <button className="btn rounded-circle btn-dark mr-3" onClick={()=>modifyId("+")}>+</button>
            <button className="btn" onClick={()=>generateNewId()}><FaRedoAlt/></button>
        </div>
    )
    
}

export default Id;
优化建议

如果这个ID状态只有Id组件自身使用,不需要在父组件App中共享,你也可以直接把状态下移到Id组件内部维护,不需要父组件传id和setId,逻辑会更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 18:57:03