如何使用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
相关产品推荐
相关产品推荐

