Динамическое добавление активного класса в многоуровневое меню боковой панели в REACT JS

В настоящее время я создаю многоуровневое меню боковой панели в REACT, и я хочу динамически добавлять активный класс в родительский <li>, когда активен текущий маршрут.

В приведенном ниже коде показано, как я это делаю для одного меню.

<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>
  )}
/>

Теперь интересно, как у меня это получается с многоуровневым меню. Мое меню выглядит так;

Динамическое добавление активного класса в многоуровневое меню боковой панели в REACT JS

Как видите, активный класс есть только в меню. Вот мой код для многоуровневого меню:

<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>
  )}
/>

Я использую React Router. Любая помощь будет оценена по достоинству. Спасибо.

Я тоже ищу аналогичный ответ. Пожалуйста, помогите кому-нибудь.

umanga shrestha 14.09.2021 10:25

@Sonson Ixon, ответили на ваш вопрос?

Benjamin 22.09.2021 18:43
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
10
2
1 068
6

Ответы 6

Моим первым побуждением было бы добавить className = {match ? "active" : ""} к родительскому li, но, поскольку вы спрашиваете об этом, я предполагаю, что вы уже пробовали это.

Вы можете покопаться в детском реквизите, чтобы узнать, есть ли у кого-нибудь из них реквизит match (или любой другой реквизит, который указывал бы, что они активны). Следующая функция может найти опору 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)
}

Затем вы можете использовать его как className = {childMatch(this.props.children) ? "active" : ""}.

Если объект, который вы ищете, находится глубже в дереве детей, вы, конечно, можете копнуть глубже. Вы можете найти внуков как child.props.children.

Это не сработало. Но спасибо за ответ. Оценил.

Sonson Ixon 09.01.2019 17:58

Вы можете использовать Навигационная ссылка маршрутизатора React

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

это было бы для одного меню, мы хотим добавить класс в родительское меню

umanga shrestha 14.09.2021 10:30
stackoverflow.com/questions/69160078/…
umanga shrestha 14.09.2021 10:31

Нет, это не имеет зависимости. Если вы находитесь на пути к, он добавит activeClassName. У него нет родительской и дочерней зависимости.

moshfiqrony 14.09.2021 10:32

Опция 1

Вы можете использовать ловушку useLocation.

С помощью ловушки useMemo вы можете запускать функцию всякий раз, когда изменяется местоположение. Таким образом вы добавляете класс к активному родительскому элементу при каждом изменении местоположения.

Но вам нужно убедиться, что вызов useLocation не находится в том же компоненте, который помещает маршрутизатор в DOM.

Итак, в корневом индексном файле или каком-либо другом компоненте вы можете иметь что-то вроде этого

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 (...);
}

Вариант 2

Это основано на предположении, что сгруппированные маршруты имеют идентичный начальный маршрут, такой как /a/b/c и a/b/e.

Пункт многоуровневого меню должен выглядеть так.

Ярлык группы должен иметь собственный Route, он не должен быть точным и должен иметь путь, который делает его родительским каталогом для фактических ссылок меню.

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

И тогда фактические элементы ссылок в меню должны быть точными и иметь путь, который делает его подкаталогом к первому ключу.

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

Вам не нужно использовать здесь селекторы useEffect или Dom, выясните, должны ли быть активными группа и ссылка (максимум useMemo, который является синхронным), и извлеките из них правильные классы (передавая как опору, если необходимо) в JSX из тот же или более высокий уровень в дереве. Доступ к DOM предназначен для редких случаев использования, и запись в DOM в дереве реакции всегда неверна.

Dominic 16.09.2021 15:36

Решение будет зависеть от конфигурации вашего маршрута. Если вы используете неточные маршруты, решение с NavLink будет наиболее подходящим.

Пример:

routes.jsx

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

Используя приведенную выше структуру, /parent/child-a будет соответствовать двум маршрутам (/parent и /parent/child-a), а опора activeClassName на NavLink будет применяться к обоим элементам HTML.

«Проблема» с приведенной выше структурой заключается в том, что вы собираетесь рендерить <Parent /> во всех трех случаях.

Это можно решить, используя вложенные / дочерние маршруты.

routes.jsx

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

nestedRoutes.jsx

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

С обновленной структурой, приведенной выше, /parent/child-a будет соответствовать маршруту верхнего уровня (и в результате добавить activeClassName к вашему элементу HTML), а также сопоставить дочерний маршрут и добавить туда activeClassName.

Обратите внимание, что если вам нужно, чтобы /parent также работал в качестве маршрута, вам необходимо обновить вложенные маршруты следующим образом:

nestedRoutes.jsx

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

По моему опыту, если вы обнаружите, что обрабатываете эти случаи вручную с помощью JavaScript или useLocation, вам может потребоваться пересмотреть конфигурацию маршрута. react-router - отличная библиотека благодаря своей простоте и, вероятно, справится с вашим случаем сразу после установки.

Это большой вопрос!

Если я правильно понимаю, есть желание динамически добавить активный класс к элементу li, если текущий маршрут совпадает.

Для этого давайте создадим новый компонент под названием NavListItem. NavListItem проверит, соответствует ли текущий маршрут его опоре to. Если это так, он отобразит activeClassName. Это мало чем отличается от компонента NavLink response-router-doms. За исключением того, что мы будем отображать li вместо ссылки.

NavListItem.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>;
};

И вот использование вашего примера кода.

<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>

По вашему вопросу. Я бы посоветовал использовать навигационную ссылку маршрутизатора React

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

Другие вопросы по теме