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

React 17传递给子组件的ref为null:MUI Drawer绑定Popover锚点失败

问题原因

你当前的实现存在两个核心问题:

  1. 你定义的elRef没有绑定到实际需要作为锚点的Drawer DOM节点上,只是传递给了子组件,自然ref.current始终为null
  2. 函数组件默认不支持直接接收ref属性传递的引用,需要用React.forwardRef包裹才能透传,如果你不打算用forwardRef,也可以把ref作为普通props传递更简单

解决方案

方案1:用普通props传递ref(无需forwardRef,更简便)

修改DrawerContainer.tsx,将ref绑定到Drawer组件上,再作为普通props传给子组件:

// DrawerContainer.tsx
import React, {useRef, useState} from 'react';
import Drawer from '@mui/material/Drawer';
import List from '@mui/material/List';
import NotificationsMenu from './NotificationsMenu';

export const DrawerContainer = () => {
  const [open, setOpen] = useState(false);
  // 把ref绑定到Drawer组件,获取Drawer的DOM节点
  const drawerRef = useRef<HTMLDivElement>(null);

  return (
    <Drawer 
      variant="permanent" 
      open={open}
      ref={drawerRef}
    >
      <List>
        {/* 直接把drawerRef当普通props传给子组件 */}
        <NotificationsMenu drawerAnchorRef={drawerRef}/>
      </List>
    </Drawer>
  );
}

修改NotificationsMenu.tsx直接使用props传入的ref:

// NotificationsMenu.tsx
import React, {useState} from 'react';
import ListItem from '@mui/material/ListItem';
import Popover from '@mui/material/Popover';

const NotificationsMenu = (props) => {
  const [anchorEl, setAnchorEl] = useState<HTMLDivElement | null>(null);

  const handleClick = () => {
    setAnchorEl(props.drawerAnchorRef.current);
  };

  return (
    <>
      <ListItem disablePadding onClick={handleClick}>
        通知入口
      </ListItem>

      <Popover
        anchorEl={anchorEl}
        open={Boolean(anchorEl)}
        onClose={() => setAnchorEl(null)}
        // 可根据需求调整对齐位置,示例为Drawer右侧居中对齐
        anchorOrigin={{
          vertical: 'center',
          horizontal: 'right',
        }}
        transformOrigin={{
          vertical: 'center',
          horizontal: 'left',
        }}
      > 
        通知内容
      </Popover>
    </>
  );
}

export default NotificationsMenu;

方案2:用forwardRef透传ref

如果你坚持要通过ref属性传递引用,就用forwardRef包裹子组件:

// NotificationsMenu.tsx
import React, {useState, forwardRef} from 'react';
import ListItem from '@mui/material/ListItem';
import Popover from '@mui/material/Popover';

// 用forwardRef包裹组件即可接收外部传入的ref
const NotificationsMenu = forwardRef((props, ref) => {
  const [anchorEl, setAnchorEl] = useState(null);

  const handleClick = () => {
    setAnchorEl(ref.current);
  };

  return (
    <>
      <ListItem disablePadding onClick={handleClick}>
        通知入口
      </ListItem>
      <Popover
        anchorEl={anchorEl}
        open={Boolean(anchorEl)}
        onClose={() => setAnchorEl(null)}
      > 
        通知内容
      </Popover>
    </>
  );
});

export default NotificationsMenu;

父组件的Drawer绑定ref后直接传给子组件的ref属性即可。

两种方案都可以实现Popover锚定Drawer,会自动适配Drawer打开/关闭的宽度变化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 23:48:03