简体   繁体   English

将活动类动态添加到 REACT JS 中的多级侧边栏菜单

[英]Dynamically adding active class to multi level sidebar menu in REACT JS

I'm currently creating a multilevel sidebar menu in REACT and I want to dynamically add active class to parent <li> when the current route is active.我目前正在 REACT 中创建一个多级侧边栏菜单,我想在当前路由处于活动状态时将活动类动态添加到父<li>

The code below is how I do it for a single menu.下面的代码是我如何为单个菜单执行此操作。

<Route
  path={this.props.to}
  exact={this.props.activeOnlyWhenExact}
  children={({ match }) => (
    <li className={match ? "active" : ""}>
      <Link to={this.props.to} activeClassName="active">
        <span className="sidebar-mini-icon">
          <i className={this.props.icon}></i></span>
          {this.props.label}
      </Link>
    </li>
  )}
/>

Now I wonder how I do it with multilevel menu.现在我想知道如何使用多级菜单来做到这一点。 My menu looks like this;我的菜单是这样的;

在此处输入图片说明

As you can see, only the menu has the active class.如您所见,只有菜单具有活动类。 Here's my code for the multi level menu:这是我的多级菜单代码:

<Route
  path={this.props.to}
  exact={this.props.activeOnlyWhenExact}
  children={({ match }) => (
  <li>
    <a data-toggle="collapse" href="#Collapse">
      <span className="sidebar-mini-icon">
        <i className={this.props.icon}></i>
      </span>
      <span className="sidebar-normal">
        {this.props.name}
        <b className="caret"></b>
      </span>
    </a>
    <div class="collapse" id="Collapse">
       <ul className="nav">
         {this.props.children}
       </ul>
    </div>
  </li>
  )}
/>

I'm using React Router.我正在使用 React 路由器。 Any help will be appreciated.任何帮助将不胜感激。 Thank you.谢谢你。

You can use React router's nav link您可以使用React 路由器的导航链接

<NavLink to="/faq" activeClassName="selected">
  FAQs
</NavLink>

The solution would depend on your route configuration.解决方案将取决于您的路由配置。 If you are using non-exact routes, the solution with NavLink would be the most applicable.如果您使用的是非精确路线,则使用NavLink的解决方案将是最适用的。

Example:例子:

routes.jsx路由.jsx

<Route path="/parent/child-a">
  <ChildA />
</Route>
<Route path="/parent/child-b">
  <ChildB />
</Route>
<Route path="/parent">
  <Parent />
</Route>

Using the above structure, /parent/child-a would match two routes ( /parent and /parent/child-a ) and the activeClassName prop on the NavLink would be applied on both HTML element.使用上述结构, /parent/child-a将匹配两个路由( /parent/parent/child-a ),并且activeClassName上的NavLink将应用于两个 HTML 元素。

The "challenge" with the above structure is that you are going to render <Parent /> in all three cases.上述结构的“挑战”是您将在所有三种情况下渲染<Parent />

This could be solved by using nested/child routes.这可以通过使用嵌套/子路由来解决。

routes.jsx路由.jsx

<Route path="/parent">
  <NestedRoutes />
</Route>

nestedRoutes.jsx嵌套路由.jsx

<Route path="/parent/child-a" exact>
  <ChildA />
</Route>
<Route path="/parent/child-b" exact>
  <ChildB />
</Route>

With the updated structure above, /parent/child-a would match the top-level route (and as a result add the activeClassName to your HTML element) and also match the child route and add the activeClassName there too.随着更新的结构之上, /parent/child-a将匹配的顶级路线(并因此在添加activeClassName到你的HTML元素),符合孩子的路线,并添加activeClassName也有。

Note that if you need /parent to also work as a route, you need to update your nested routes as follows:请注意,如果您需要/parent也用作路由,则需要按如下方式更新嵌套路由:

nestedRoutes.jsx嵌套路由.jsx

<Route path="/parent" exact>
  <Parent />
</Route>

From my experience, if you find yourself handling these cases manually through JavaScript or useLocation you might want to revisit your route configuration.根据我的经验,如果您发现自己通过 JavaScript 或useLocation手动处理这些情况,您可能需要重新访问您的路由配置。 react-router is an excellent library in its simplicity and will probably handle your case out of the box. react-router是一个非常简单的库,它可能会立即处理您的情况。

My first instinct would be to add className={match ? "active" : ""} 我的第一个直觉是添加className={match ? "active" : ""} className={match ? "active" : ""} to the parent li , but since you're asking about it, I'm assuming you already tried that. className={match ? "active" : ""}到父级li ,但是由于您正在询问,我假设您已经尝试过了。

You can dig around in the children's props to find out if any of them have the match prop (or any other prop that would indicate they're active). 您可以在孩子们的道具中四处挖掘,以了解他们是否有match道具(或其他任何表明他们活跃的道具)。 The following function can find a match prop from the children. 以下功能可以从孩子那里找到match道具。

const childMatch = children => {
  if (!children) { return false }
  if (Array.isArray(children)) {
    return children.reduce(
      (acc, child) => acc || Boolean(child.props.match),
      false
    )
  }
  return Boolean(children.props.match)
}

You could then use it like className={childMatch(this.props.children) ? "active" : ""} 然后,您可以像className={childMatch(this.props.children) ? "active" : ""} className={childMatch(this.props.children) ? "active" : ""} . className={childMatch(this.props.children) ? "active" : ""}

If the prop you're looking for is deeper down the children tree, you can of course dig deeper. 如果您要寻找的道具在儿童树的更深处,那么您当然可以更深入地挖掘。 You can find grandchildren as child.props.children . 您可以将孙子作为child.props.children找到。

Option 1选项1

You can use the useLocation hook.您可以使用useLocation钩子。

With the useMemo hook, you can run a function whenever, the location changes.使用useMemo挂钩,您可以在位置发生变化时运行函数。 That way you add the class to the active parent on every location change.这样您就可以在每次位置更改时将类添加到活动的父类中。

But you'll need to make sure that the useLocation call is not in the same component that puts the router into the DOM.但是您需要确保useLocation调用不在将路由器放入 DOM 的同一组件中。

So in the root index file, or some other component, you can have something like this所以在根索引文件或其他一些组件中,你可以有这样的东西

import { useLocation, useEffect } from "react-router-dom";

const Item = () => {
  const location = useLocation();

  // Select the nav item with the active class, and add the extra class to its parent
  useMemo(() => document.querySelector(activeClassName)?.parentElement?.classList.add(activeParentClassName), [location])
  
  return (...);
}

Option 2选项 2

This is based on the assumption, that the routes that are grouped have an identical starting route like /a/b/c and a/b/e这是基于这样的假设,即分组的路线具有相同的起始路线,如/a/b/ca/b/e

A multi-level menu item should be render like this.多级菜单项应该像这样呈现。

The label of the group should have its own Route , it shouldn't be exact and should have a path that makes it a parent-directory to the actual menu links组的标签应该有自己的Route ,它不应该是精确的,并且应该有一个路径,使其成为实际菜单链接的父目录

<li>
  <Route
    path="/a"
    children={({ match }) => (
      <a data-toggle="collapse" href="#Collapse" className={match ? 'active' : ''}>
          ...
      </a>
    )}
  />

And then the actual link items in the menu, should be exact and have a path that makes it a sub-directory to the first key然后菜单中的实际链接项应该是准确的,并且有一个路径,使其成为第一个键的子目录

<ul>
  <Route
    exact
    path="/a/b"
    children={({ match }) => (
      <li className={match ? "active" : ""}>
        <Link to={this.props.to} activeClassName="active">
          {/* ... */}
        </Link>
      </li>
    )}
  />
  {/* other routes */}
</ul>

This is a great question!这是一个很好的问题!

If I understand correctly, the desire is to dynamically add an active class to an li element if the current route matches.如果我理解正确,如果当前路由匹配,则希望将活动类动态添加到li元素。

To do that, lets create a new componet called NavListItem.为此,让我们创建一个名为 NavListItem 的新组件。 NavListItem will check to see if the current route matches its to prop. NavListItem 将检查当前路由是否与其to prop 匹配。 If it does, it will render the activeClassName.如果是,它将呈现 activeClassName。 This is not too unlike the react-router-doms NavLink component.这与 react-router- NavLink组件并无太大不同。 Except that we will render an li instead of a link.除了我们将呈现li而不是链接。

NavListItem.js导航列表项.js

import React from 'react';
import { useLocation, matchPath } from 'react-router-dom';

export const NavListItem = ({
  path,
  exact,
  sensitive,
  strict,
  activeClassName = 'active',
  className,
  children,
  ...otherProps
}) => {
  const location = useLocation();

  const match = matchPath(location.pathName, {
    path: path,
    exact: exact,
    sensitive: sensitive,
    strict: strict,
  });

  const isActive = Boolean(match);

  const className = isActive ? [activeClassName, className].join(' ') : className;

  const props = {
    className: className,
    ...otherProps,
  };

  return <li {...props}>{children}</li>;
};

And here is the usuage with your example code.这是您的示例代码的用法。

<NavListItem path={this.props.to} exact={this.props.activeOnlyWhenExact}>
  <a data-toggle="collapse" href="#Collapse">
    <span className="sidebar-mini-icon">
      <i className={this.props.icon}></i>
    </span>
    <span className="sidebar-normal">
      {this.props.name}
      <b className="caret"></b>
    </span>
  </a>
  <div class="collapse" id="Collapse">
    <ul className="nav">{this.props.children}</ul>
  </div>
</NavListItem>

As per your question.根据你的问题。 I would suggest to use React router's nav link我建议使用 React 路由器的导航链接

<NavLink to="/<route-name>" activeClassName=<classname>>
  Title
</NavLink>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM