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

点击IconButton触发状态更新后出现无限循环问题求助

问题描述

使用MUI框架实现图标按钮触发菜单弹窗,复制官方示例代码测试,但点击IconButton时UI冻结,同时Firebase的onAuthStateChanged函数无限循环执行。

相关代码如下:

组件JSX代码

<div>
  <IconButton onClick={handleOpenMenu} 
    aria-controls={open ? 'basic-menu' : undefined}
    aria-haspopup="true"
    aria-expanded={open ? 'true' : undefined}
  >
    <MoreVertIcon />
  </IconButton>

  <Menu
    id="demo-positioned-menu"
    aria-labelledby="demo-positioned-button"
    anchorEl={anchorEl}
    open={open}
    onClose={handleMenuClose}
    anchorOrigin={{
      vertical: 'top',
      horizontal: 'left',
    }}
    transformOrigin={{
      vertical: 'top',
      horizontal: 'left',
    }}
  >
    <MenuItem onClick={handleMenuClose}>Profile</MenuItem>
    <MenuItem onClick={handleMenuClose}>My account</MenuItem>
    <MenuItem onClick={handleMenuClose}>Logout</MenuItem>
  </Menu>
</div>

事件处理函数

const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const handleOpenMenu = (event) => {
  setAnchorEl(event.currentTarget);
};
const handleMenuClose = () => {
  setAnchorEl(null);
};

Firebase认证监听代码

var unsubscribe = onAuthStateChanged(auth, (user) => {
  if (user) {
    const uid = user.uid;
    console.log(uid)
    setAuthenticated(true)
  } else {
    setAuthenticated(false)
  }
});

问题原因与解决方法

核心原因

onAuthStateChanged直接写在组件函数内部,会在每次组件渲染时重新创建订阅:

  1. 初始渲染时订阅认证状态变化,触发setAuthenticated更新状态,导致组件重渲染;
  2. 重渲染时又会再次执行onAuthStateChanged创建新订阅,再次触发setAuthenticated,形成无限循环;
  3. 点击IconButton时调用setAnchorEl触发组件重渲染,进一步加剧循环,最终导致UI冻结。

解决步骤

用React的useEffect钩子包裹认证监听逻辑,确保只在组件挂载时订阅一次,并且在组件卸载时清理订阅:

import { useEffect } from 'react';

// ...其他组件代码

useEffect(() => {
  const unsubscribe = onAuthStateChanged(auth, (user) => {
    if (user) {
      const uid = user.uid;
      console.log(uid);
      setAuthenticated(true);
    } else {
      setAuthenticated(false);
    }
  });

  // 组件卸载时取消订阅,避免内存泄漏
  return unsubscribe;
}, []); // 空依赖数组表示仅在组件挂载/卸载时执行

额外优化点

当前代码中IconButton的aria-controls属性值是basic-menu,但Menu的id是demo-positioned-menu,两者不匹配,可能导致无障碍访问问题,建议修正为一致:

<IconButton onClick={handleOpenMenu} 
  aria-controls={open ? 'demo-positioned-menu' : undefined}
  aria-haspopup="true"
  aria-expanded={open ? 'true' : undefined}
>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 23:10:51