【问题标题】:Display menu on hover reactjs在悬停reactjs上显示菜单
【发布时间】:2020-04-21 19:12:37
【问题描述】:

在一个 react 应用程序中,我使用 reactstrap css 框架来制作下拉菜单以列出下拉菜单。

Example.Js

      <Dropdown
        className="d-inline-block"
        onMouseOver={this.onMouseEnter}
        onMouseLeave={this.onMouseLeave}
        isOpen={this.state.dropdownOpen}
        toggle={this.toggle}
      >
        <DropdownToggle caret>Dropdown1</DropdownToggle>
        <DropdownMenu>
          <DropdownItem header>Submenu 1</DropdownItem>
          <DropdownItem>Submenu 1.1</DropdownItem>
        </DropdownMenu>
        &nbsp;&nbsp;&nbsp;
        <DropdownToggle caret>Dropdown2</DropdownToggle>
        <DropdownMenu>
          <DropdownItem header>Submenu 2</DropdownItem>
          <DropdownItem>Submenu 2.1</DropdownItem>
          <DropdownItem>Submenu 2.2</DropdownItem>
        </DropdownMenu>
        &nbsp;&nbsp;&nbsp;
        <br /><br />
        <DropdownToggle caret>Dropdown3</DropdownToggle>
        <DropdownMenu>
          <DropdownItem header>Submenu 3</DropdownItem>
          <DropdownItem>Submenu 3.1</DropdownItem>
          <DropdownItem>Submenu 3.2</DropdownItem>
          <DropdownItem>Submenu 3.3</DropdownItem>
        </DropdownMenu>
      </Dropdown>

这里我做了setState 来设置dropDownOpenonMouseOveronMouseLeave 等事件中的状态。

问题悬停在单个下拉菜单上,每个下拉菜单都会打开。

Click here for Working Demo

请帮我制作悬停下拉菜单,以便一次只列出悬停的菜单,而不是全部

注意:在我的实际应用程序中,这些下拉菜单将是动态的,所以我无法制作任何硬编码状态,例如dropDown1dropDown2dropDown3 ... 等。

它可能有任何n 数量的下拉菜单。所以请给我解决方案以考虑动态菜单。

【问题讨论】:

  • 所有下拉切换和菜单都在一个下拉列表中,由单个状态变量控制。我认为每个人都应该在自己的Dropdown 组件中。
  • 对于这三个下拉菜单,您使用相同的状态
  • @DrewReese,我是 reactjs 的新手,所以你能通过修改上面给出的例子来帮助我给出正确的解决方案吗?请随时编辑我的示例并为这个问题提供良好的解决方案。我的方法可能是错误的,所以请纠正我..
  • @DILEEPTHOMAS,我是 reactjs 的新手,所以你能通过修改上面给出的例子来帮助我给出正确的解决方案吗?
  • @TestUser 当然我会用新的 stackblitz 更新

标签: javascript css reactjs twitter-bootstrap reactstrap


【解决方案1】:

基本上每个下拉菜单都需要在自己的Dropdown 复合组件中,并具有自己的状态和处理程序。我分叉并更新了你的演示,希望能给你正确的想法。

<div>
    <Dropdown
      className="d-inline-block"
      onMouseOver={this.onMouseEnter}
      onMouseLeave={this.onMouseLeave}
      isOpen={this.state.dropdownOpen1}
      toggle={this.toggle1}
    >
      <DropdownToggle caret>Dropdown1</DropdownToggle>
      <DropdownMenu>
        <DropdownItem header>Submenu 1</DropdownItem>
        <DropdownItem>Submenu 1.1</DropdownItem>
      </DropdownMenu>
      &nbsp;&nbsp;&nbsp;
    </Dropdown>
    <Dropdown
      className="d-inline-block"
      // onMouseOver={this.onMouseEnter}
      // onMouseLeave={this.onMouseLeave}
      isOpen={this.state.dropdownOpen2}
      toggle={this.toggle2}
    >

      <DropdownToggle caret>Dropdown2</DropdownToggle>
      <DropdownMenu>
        <DropdownItem header>Submenu 2</DropdownItem>
        <DropdownItem>Submenu 2.1</DropdownItem>
        <DropdownItem>Submenu 2.2</DropdownItem>
      </DropdownMenu>
      &nbsp;&nbsp;&nbsp;
      <br /><br />

    </Dropdown>
    <Dropdown
      className="d-inline-block"
      // onMouseOver={this.onMouseEnter}
      // onMouseLeave={this.onMouseLeave}
      isOpen={this.state.dropdownOpen3}
      toggle={this.toggle3}
    >

      <DropdownToggle caret>Dropdown3</DropdownToggle>
      <DropdownMenu>
        <DropdownItem header>Submenu 3</DropdownItem>
        <DropdownItem>Submenu 3.1</DropdownItem>
        <DropdownItem>Submenu 3.2</DropdownItem>
        <DropdownItem>Submenu 3.3</DropdownItem>
      </DropdownMenu>
    </Dropdown>
  </div>

https://stackblitz.com/edit/reactstrap-v6-2dnzex?file=Example.js

【讨论】:

  • 是否没有最佳的做法?在您的示例中,您已重复切换 3 次以使其工作,但有没有办法使其处于单一状态?我严格遵循 DRY(Don't Repeat Yourself) 方法,因此在审查时,此代码将被忽略..
  • 而且每个菜单都应该在悬停时显示。为什么你修改了第二和第三个下拉菜单?一切都应该悬停并单独处理..
  • 您可以尝试制作自己的 Dropdown 包装版本,该版本将状态和处理程序内部化,然后组成您的菜单。这是一个快速而肮脏的展示如何拆分菜单项,以免它们被单个变量锁定/驱动。
  • 如果下拉菜单以n 数字作为动态菜单出现,这里的解决方案是什么??
  • n menus 不是您最初问题的一部分,伙计。您可以使用一组切换和悬停状态值并使用索引进行访问。只需为您添加的每个新菜单推送新的处理程序。
【解决方案2】:
 import React from "react";
import {
  Dropdown,
  DropdownToggle,
  DropdownMenu,
  DropdownItem
} from "reactstrap";

export default class Example extends React.Component {
  constructor(props) {
    super(props);

    this.toggle = this.toggle.bind(this);
    this.onMouseEnter = this.onMouseEnter.bind(this);
    this.onMouseLeave = this.onMouseLeave.bind(this);
    this.state = {
      dropdownOpen1: false,
      dropdownOpen2: false,
      dropdownOpen3: false
    };
  }

  toggle(id) {
   this.setState({[id]:!this.state[`${id}`]})
  }

  onMouseEnter(id) {
    this.setState({ [id]: true });
  }

  onMouseLeave(id) {
    this.setState({ [id]: false });

  }

  render() {
    return (
      <div>
        <Dropdown
          className="d-inline-block"
          onMouseOver={()=>this.onMouseEnter("dropdownOpen1")}
          onMouseLeave={()=>this.onMouseLeave("dropdownOpen1")}
          isOpen={this.state.dropdownOpen1}
          toggle={()=>this.toggle("dropdownOpen1")}
        >
          <DropdownToggle caret>Dropdown1</DropdownToggle>
          <DropdownMenu>
            <DropdownItem header>Submenu 1</DropdownItem>
            <DropdownItem>Submenu 1.1</DropdownItem>
          </DropdownMenu>
        </Dropdown>
        <Dropdown
          className="d-inline-block"
           onMouseOver={()=>this.onMouseEnter("dropdownOpen2")}
          onMouseLeave={()=>this.onMouseLeave("dropdownOpen2")}
          isOpen={this.state.dropdownOpen2}
          toggle={()=>this.toggle("dropdownOpen2")}
        >
          &nbsp;&nbsp;&nbsp;
          <DropdownToggle caret>Dropdown2</DropdownToggle>
          <DropdownMenu>
            <DropdownItem header>Submenu 2</DropdownItem>
            <DropdownItem>Submenu 2.1</DropdownItem>
            <DropdownItem>Submenu 2.2</DropdownItem>
          </DropdownMenu>
        </Dropdown>
        <Dropdown
          className="d-inline-block"
        onMouseOver={()=>this.onMouseEnter("dropdownOpen3")}
          onMouseLeave={()=>this.onMouseLeave("dropdownOpen3")}
          isOpen={this.state.dropdownOpen3}
          toggle={()=>this.toggle("dropdownOpen3")}
        >
          &nbsp;&nbsp;&nbsp;
          <br />
          <br />
          <DropdownToggle caret>Dropdown3</DropdownToggle>
          <DropdownMenu>
            <DropdownItem header>Submenu 3</DropdownItem>
            <DropdownItem>Submenu 3.1</DropdownItem>
            <DropdownItem>Submenu 3.2</DropdownItem>
            <DropdownItem>Submenu 3.3</DropdownItem>
          </DropdownMenu>
        </Dropdown> 
      </div>
    );
  }
}

试一试!!!告诉我,基本上你需要为每个下拉菜单传递不同的参数,这样你的方法就可以区分打开或关闭什么。

【讨论】:

  • 您可以根据您从源命中获得的内容使您的 DropDown 动态化,然后在渲染中使用地图使一切动态化!
【解决方案3】:

嗨 @TestUser 和 Drew Reese 我已经使用箭头函数减少了一些行:https://stackblitz.com/edit/reactstrap-dropdown?file=Example.js 如果您需要这个用于 n 下拉菜单,那么您可以创建单个下拉菜单并使用道具您可以为此创建多个下拉菜单(您可以使用地图重复此)并且可以在多个地方使用它。这就是反应的美妙之处。

【讨论】:

    【解决方案4】:

    所以基本上你对所有三个下拉菜单都使用了相同的状态变量。

    要解决这个问题,您需要维护三个不同的状态变量,因为您想要动态方式,您可以遵循以下方法

    对于可重复使用的方法,您可以将下拉菜单作为一个单独的组件。

    如果你愿意,你可以添加更多的逻辑,这是解决问题的简单方法

    App.js

    import React, { useState, useEffect } from "react";
    import ReactDOM from "react-dom";
    import { Container } from "reactstrap";
    import CustomDropDown from "./CustomDropdown";
    import "bootstrap/dist/css/bootstrap.min.css";
    
    import "./styles.css";
    
    const dropdownConfig = [
      {
        customKey: 1,
        options: [
          { title: "Submenu 1", header: true },
          { title: "Submenu 1.1", header: false }
        ],
        name: "dropdownOpen1"
      },
      {
        customKey: 2,
        options: [
          { title: "Submenu 2", header: true },
          { title: "Submenu 2.1", header: false }
        ],
        name: "dropdownOpen2"
      },
      {
        customKey: 3,
        options: [
          { title: "Submenu 3", header: true },
          { title: "Submenu 3.1", header: false }
        ],
        name: "dropdownOpen3"
      }
    ];
    
    function App() {
      const [keysForDropdown, setKeysForDropdown] = useState({});
    
      useEffect(() => {
        const keys = dropdownConfig.map(dropdown => dropdown.name);
        const object = keys.reduce((acc, curr) => {
          acc[curr] = false;
          return acc;
        }, {});
        setKeysForDropdown({ ...object });
      }, []);
    
      const _handleToggle = e => {
        setKeysForDropdown({
          ...keysForDropdown,
          [e.target.name]: !keysForDropdown[e.target.name]
        });
      };
    
      const _handleMouseEnter = e => {
        setKeysForDropdown({
          ...keysForDropdown,
          [e.target.name]: !keysForDropdown[e.target.name]
        });
      };
    
      const _handleMouseLeave = e => {
        setKeysForDropdown({
          ...keysForDropdown,
          [e.target.name]: !keysForDropdown[e.target.name]
        });
      };
    
      return (
        <div className="App">
          <Container>
            {keysForDropdown &&
              dropdownConfig.map(dropdown => (
                <CustomDropDown
                  {...dropdown}
                  key={dropdown.customKey}
                  stateKeys={keysForDropdown}
                  handleToggle={_handleToggle}
                  handleMouseEnter={_handleMouseEnter}
                  handleMouseLeave={_handleMouseLeave}
                />
              ))}
          </Container>
        </div>
      );
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    

    CustomDropdown.js

    import React from "react";
    import {
      Dropdown,
      DropdownToggle,
      DropdownMenu,
      DropdownItem
    } from "reactstrap";
    
    const CustomDropDown = props => {
      const {
        handleMouseEnter,
        handleMouseLeave,
        handleToggle,
        options,
        name,
        stateKeys
      } = props;
      return (
        <div className="dropdown-container">
          <Dropdown
            className="d-inline-block"
            onMouseOver={handleMouseEnter}
            onMouseLeave={handleMouseLeave}
            isOpen={stateKeys[name]}
            toggle={handleToggle}
          >
            <DropdownToggle name={name} caret>
              Dropdown1
            </DropdownToggle>
            <DropdownMenu>
              {options.length &&
                options.map(({ header, title }) => (
                  <DropdownItem header={header}>{title}</DropdownItem>
                ))}
            </DropdownMenu>
          </Dropdown>
        </div>
      );
    };
    
    export default CustomDropDown;
    

    工作codesandbox

    【讨论】:

    • 此解决方案运行良好,但存在一个错误,即当鼠标悬停时,切换打开的菜单会关闭。您应该为每个菜单保留两个状态,isOpenisHovered,并将组件上的 isOpen 属性设置为它们的并集。像isOpen={stateKeys[name].isOpen || stateKeys[name].isHovered} 这样的东西。这样一个“状态”就不会覆盖另一个。
    • 我有点困惑@DrewReese,你能分叉并更新它
    • @DILEEPTHOMAS,看看这个链接supply.com,这就是我的下拉菜单应该是这样的真正的应用程序下拉菜单看起来像。仅考虑此链接中的水平下拉菜单..
    【解决方案5】:

    这是我显示菜单或下拉菜单的子菜单的解决方案。 如果您对此有任何疑问,请随时提出任何问题。

    import React from 'react';
    import { Box, jsx } from 'theme-ui';
    import { Link } from 'gatsby';
    import { H1 } from '../../components/ThemeHeader';
    import { Image } from '../../components';
    
    const CardWithCTALinks = (props) => {
      const { cardWithCTALinksImage, ctaLinks, heading, bgColor } = props;
      const [onCTAListHover, setOnCTAListHover] = React.useState({ status: '', indexAt: -1 });
    
      const updateCTAListHover = (newOnCTAListHover, idx) => {
        if (newOnCTAListHover !== onCTAListHover) setOnCTAListHover({ state: newOnCTAListHover, indexAt: idx });
      };
      const renderImageSection = (src, alt) => {
        return <Image src={src} alt={alt} />;
      };
      const renderSubLinks = (subLinks, idx) => {
        return (
          <Box>
            {idx === onCTAListHover.indexAt &&
              subLinks.map((link) => (
                <Link key={Math.random().toString(36).substring(7)} to="/">
                  {link.text}
                </Link>
              ))}
          </Box>
        );
      };
      const renderLinksSection = (linksList, headingText) => {
        return (
          <Box>
            {headingText && <H1>{headingText}</H1>}
            {linksList && (
              <Box>
                {linksList.map((link, index) => (
                  <h1 onMouseEnter={() => updateCTAListHover(true, index)} onMouseLeave={() => updateCTAListHover(false, index)}>
                    {link.node?.title}
                    {link.node?.navItems.length > 0 && <>{onCTAListHover && renderSubLinks(link.node?.navItems, index)}</>}
                  </h1>
                ))}
              </Box>
            )}
          </Box>
        );
      };
    
      return (
        <Box style={{ backgroundColor: bgColor }}>
          {cardWithCTALinksImage && <Box>{renderImageSection(cardWithCTALinksImage?.asset._ref, 'alt')}</Box>}
          {ctaLinks && heading && <Box>{renderLinksSection(ctaLinks.edges, heading)}</Box>}
        </Box>
      );
    };
    
    export default CardWithCTALinks;
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-15
      • 1970-01-01
      • 2015-10-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多