useRef.current.contains不是函数

时间:2019-06-05 08:43:36

标签: reactjs sass material-ui

我在Navbar中有一个用material-ui / core构建的导航菜单。

我使用useRef跟踪切换菜单关闭时单击的按钮的位置。 anchorRef.current.contains(event.target)

我得到'Uncaught TypeError: anchorRef.current.contains is not a function'

我尝试使用'Object.values(anchorRef.current).includes(event.target)',它总是返回false。

-更新-

anchorRef.current.props对象。

withStyles {
    props:{
     aria-haspopup: "true"
     aria-owns: undefined
     children: "계정"
     className: "nav-menu--btn"
     onClic: f onClick()
     get ref: f()
      isReactWarning: true
      arguments: (...)
      caller: (...)
      length: 0
      name: "warnAboutAccessingRef"
     ...
    }, context{...}, refs{...}, ...}

ToggleMenuList

const ToggleMenuList = ({ navAdminList, navAdminItems, classes }) => {
  const [activeId, setActiveId] = useState(null);
  const anchorRef = useRef(null);
  const handleToggle = id => {
    setActiveId(id);
  };
  const handleClose = event => {
    if (anchorRef.current && anchorRef.current.contains(event.target)) {
      return;
    }
    setActiveId(null);
  };

  return (
    <React.Fragment>
      <div className={`nav-menu--admin ${classes.root}`}>
        {navAdminList.map(e => (
          <div key={e.id}>
            <Button
              ref={anchorRef}
              aria-owns={activeId === e.id ? 'menu-list-grow' : undefined}
              aria-haspopup="true"
              onClick={() => handleToggle(e.id)}
            >
              {e.name}
            </Button>
            {activeId === e.id && (
              <ToggleMenuItems
                id={e.id}
                activeId={activeId}
                handleClose={handleClose}
                anchorRef={anchorRef}
                items={navAdminItems[e.id]}
              />
            )}
          </div>
        ))}
      </div>
    </React.Fragment>
  );
};

export default withStyles(styles)(ToggleMenuList);

ToggleMenuItems

 const ToggleMenuItems = ({
      listId,
      activeId,
      handleClose,
      anchorRef,
      items,
    }) => {
      const isOpen = activeId === listId;
      const leftSideMenu = activeId === 3 || activeId === 4 ? 'leftSideMenu' : '';

      return (
        <Popper
          open={isOpen}
          anchorEl={anchorRef.current}
          keepMounted
          transition
          disablePortal
        >
          {({ TransitionProps, placement }) => (
            <Grow
              {...TransitionProps}
              style={{
                transformOrigin:
                  placement === 'bottom' ? 'center top' : 'center bottom',
              }}
              className={`toggle-menu ${leftSideMenu}`}
            >
              <Paper id="menu-list-grow">
                <ClickAwayListener
                  onClickAway={handleClose}
                >
                  <MenuList className="toggle-menu--list">
                    {items.map(e => (
                      <MenuItem
                        key={e.id}
                        className="toggle-menu--item"
                        onClick={handleClose}
                      >
                        <Link
                          to={e.to}
                          className="anchor td-none c-text1 toggle-menu--link"
                        >
                          {e.name}
                        </Link>
                      </MenuItem>
                    ))}
                  </MenuList>
                </ClickAwayListener>
              </Paper>
            </Grow>
          )}
        </Popper>
      );
    };

    export default ToggleMenuItems;
react: ^16.8.6
react-dom: ^16.8.6
react-router-dom: ^4.3.1
@material-ui/core: ^3.1.2

1 个答案:

答案 0 :(得分:0)

我假设您的ToggleMenuItems在单击时设置了全局(document级?)事件侦听器,以在外部单击时折叠菜单。

您有一个同级按钮元素。单击您要保持菜单展开的按钮,对吗?因此,在.contains中使用onClick来检查是否在ToggleMenuItems之外但在特定Button范围内单击了我们。它不起作用的原因:<Button>是基于类的自定义React组件,因此它在ref中返回React组件实例。而且它没有像.contains

这样的特定于DOM的方法

您可以重新设计当前的方法:仅在单击Button的情况下停止冒泡事件。它将停止ToggleMenuItems设置的全局事件处理程序的反应。

const stopPropagation = (event) => event.stopPropagation();

const ToggleMenuList = ({ navAdminList, navAdminItems, classes }) => {
  const [activeId, setActiveId] = useState(null);
  const anchorRef = useRef(null);
  const handleToggle = id => {
    setActiveId(id);
  };
  const handleClose = event => {
    setActiveId(null);
  };

  return (
    <React.Fragment>
      <div className={`nav-menu--admin ${classes.root}`}>
        {navAdminList.map(e => (
          <div key={e.id}>
            <div onClick={stopPropagation}>
              <Button
                aria-owns={activeId === e.id ? 'menu-list-grow' : undefined}
                aria-haspopup="true"
                onClick={() => handleToggle(e.id)}
              >
                {e.name}
              </Button>
            </div>
            {activeId === e.id && (
              <ToggleMenuItems
                id={e.id}
                activeId={activeId}
                handleClose={handleClose}
                anchorRef={anchorRef}
                items={navAdminItems[e.id]}
              />
            )}
          </div>
        ))}
      </div>
    </React.Fragment>
  );
};

export default withStyles(styles)(ToggleMenuList);

我将stopPropagation处理程序放在外面,因为它不依赖于任何内部变量。