React 17传递给子组件的ref为null:MUI Drawer绑定Popover锚点失败
问题原因
你当前的实现存在两个核心问题:
- 你定义的
elRef没有绑定到实际需要作为锚点的DrawerDOM节点上,只是传递给了子组件,自然ref.current始终为null - 函数组件默认不支持直接接收
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
相关产品推荐
相关产品推荐

