如何在 reactjs 中添加下拉列表?

How to add dropdown in reactjs?

我正在制作一个简单的 React 应用程序,其中要求我需要使用 pure reactjs 制作下拉菜单。下拉菜单也需要响应。

预计会发生以下 mouse/click 事件。

工作代码段

function App() {

  React.useEffect(() => {

    const has_submenu = document.querySelector(".has-submenu");
    const submenu = document.querySelector(".submenu");
    const submenu_height = submenu && submenu.childElementCount * 34;

    if (has_submenu && submenu && submenu_height) {
      has_submenu.addEventListener("mouseover", function () {
        submenu.style.height = submenu_height + "px";
      });

      has_submenu.addEventListener("mouseout", function () {
        submenu.style.height = "0px";
      });

      submenu.addEventListener("mouseover", function () {
        submenu.style.height = submenu_height + "px";
      });

      submenu.addEventListener("mouseout", function () {
        submenu.style.height = "0px";
      });

    }

  }, []);

  return (
    <nav>
      <ul>
        <li className="menu-item  has-submenu inline-flex"> Account </li>
           <ul className="submenu">
              <li className="submenu-item submenu1">
                Profile
               </li>
              <li className="submenu-item submenu1">
                Change Password
               </li>
           </ul>
       </ul>
    </nav>
  );
}

ReactDOM.render(<App />, document.querySelector('#app'));
.submenu {
  background: #fff;
  position: absolute;
  list-style-type: none;
  padding: 0;
  height: 0px;
  overflow: hidden;
  color: #000;
  cursor: pointer;
  transition: height 0.33333s ease-in;
}
.submenu-item {
  padding: 2px 16px;
  list-style-position: outside;
  transition: background 0.33333s;
}
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>

如果你看一下上面的代码,那么你可以看到在鼠标悬停时 account,子菜单列在下面。

要求:

尽管它有效,但我觉得我已经使用反应之外的范围实现了它,因为我正在使用 dom methods 并设置 submenu 部分的高度。

是否有任何纯粹的反应方式来实现这一目标?

如果可以,请帮助我重构上面的代码片段,以实现桌面视图(悬停时)和移动视图(单击时)的菜单下拉菜单。

预期输出:

桌面:(悬停时)

 Dashboard      Account               Logout
                | -- Profile -- |
                | -- Change Password -- |

手机:(点击)

  Dashboard

  Account 

    | -- Profile -- |
    | -- Change Password -- |

 Logout

React 允许您在组件中控制 state。有很多方法可以解决这个问题,但我将在下面举一个小例子。

您将想要对事件做出反应并更改 state。每次 state 更新时,您的组件将被重新渲染。

const MAGIC_NUMBER = 34;

const MyApp = () => {

  const subMenuRef = React.createRef();
  const [ isMenuOpen, setMenuOpen ] = React.useState(false);
  const [ menuHeight, setMenuHeight ] = React.useState(0);

  const openMenu = () => setMenuOpen(true);
  const closeMenu = () => setMenuOpen(false);

  React.useEffect(() => {
    if (!subMenuRef.current) { return; }
    setMenuHeight(subMenuRef.current.childElementCount * MAGIC_NUMBER);
  }, [subMenuRef.current]);

  return (
    <nav>
      <ul>
        <li
          className="menu-item inline-flex" 
          onMouseOver={openMenu}
          onMouseOut={closeMenu}
        >
         Account
        </li>
        <ul
          className="submenu"
          ref={subMenuRef}
          style={{height: isMenuOpen ? menuHeight : 0}}
          onMouseOver={openMenu}
          onMouseOut={closeMenu}
        >
          <li className="submenu-item submenu1">
           Profile
          </li>
          <li className="submenu-item submenu1">
           Change Password
          </li>
        </ul>
      </ul>
    </nav>
  );
};

ReactDOM.render(<MyApp />, document.querySelector('#app'));
.submenu {
  background: #fff;
  position: absolute;
  list-style-type: none;
  padding: 0;
  height: 0px;
  overflow: hidden;
  color: #000;
  cursor: pointer;
  transition: height 0.33333s ease-in;
  border: 1px solid #777;
}
.submenu-item {
  padding: 2px 16px;
  list-style-position: outside;
  transition: background 0.33333s;
}
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>

上面发生了很多事情,所以我会在这里分解一下..

  • 我们直接向我们的元素添加了事件侦听器。
    • React 允许我们直接附加这些事件监听器而无需访问 DOM
  • 我们添加了一些状态来跟踪菜单的 open/close 状态
    • 在我们的事件处理程序中,我们只需翻转状态,一切正常。
    • 我们向元素添加条件逻辑来处理状态变化。 (改变菜单时的高度opens/closes)
  • 我们使用 ref 直接访问 DOM 以获取子元素计数。 Documentation
    • 如果您完全处于反应状态,这些子项可能是您正在迭代的列表,您可以简单地获取它的长度。
    • 通常您不需要访问底层 DOM 因为大多数事情将在 React 本身中处理。

PS:尽可能避免使用幻数。在本例中,您的值为 34。这代表什么?它为什么在这里?如果您必须使用精确的高度,请改为使用 remlineHeight

进行 css 计算