Как я могу отключить активный класс для каждого дочернего компонента, установив его активным для дочернего элемента, на который щелкнули

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

До сих пор я пробовал использовать хуки в родительском классе, где я отключил активную опору для всех дочерних элементов, используя setActive('');, а затем установил класс текущей цели для связывания - активный с помощью e.currentTarget.className === 'link--active' ? e.currentTarget.className = '' : e.currentTarget.className = 'link--active';. К сожалению, все, что он делает в данный момент, - это добавляет класс или удаляет класс для дочернего элемента, по которому щелкнули мышью.

Родитель:

  const [active, setActive] = useState('');

  const navigate = (e) => {
    setActive('');
    e.currentTarget.className === 'link--active' ? e.currentTarget.className = '' : e.currentTarget.className = 'link--active';
  };

и в заявлении возврата:

{menuItems.map((item, index) => (
  <li key={index} >
    <NavLink target={item} onClick={(e) => navigate(e)} active={active} />
  </li>
))}

Дети:

<a href="#"
   onClick={props.onClick} 
   className={props.active}>
   {props.target}
</a>

Редактировать:

После использования решения от Ори Дрори активный класс был установлен на нажатой ссылке NavLink и удален из остальных. Поскольку я хотел, чтобы onClick был функцией навигации, все, что я изменил, - это установка onClick в родительском элементе для навигации и вызова функции навигации setActive, используя id в качестве параметра и снова вызывая setActive в функции навигации с идентификатором в качестве параметра. Теперь классы выглядят так:

Родитель:

const [active, setActive] = useState(null);

const navigate = (id) => {
  setActive(id);
};

return (
    {menuItems.map((item) => (
      <li key={item.id} >
        <NavLink 
          {...item}
          isActive={active === item.id}
          onClick={navigate} />
      </li>
    ))}
)

Ребенок:

const NavLink = ({id, target, isActive, onClick}) => {
  return (
      <a href="#"
        onClick={useCallback(() => onClick(id), [id, onClick])} 
        className={isActive ? 'active' : ''}>
        {target}
      </a>
  );
}

person Jayce    schedule 02.05.2019    source источник


Ответы (3)


Передайте setActive в NavLinks. При щелчке по NavLink он устанавливает id через setActive. Каждый элемент также получает свойство isActive, которое равно true, если состояние active совпадает с id.

const { useCallback, useState } = React

const NavLink = ({ id, target, isActive, onClick }) => (
  <a href="#"
     onClick={useCallback(() => onClick(id), [id])} 
     className={`navLink ${isActive ? 'active' : ''}` }>
     {target}
  </a>
)

const Parent = ({ menuItems }) => {
  const [active, setActive] = useState(null);

  return (
    <ul>
      {menuItems.map((item) => (
        <li key={item.id} >
          <NavLink 
            {...item} 
            onClick={setActive} 
            isActive={active === item.id} />
        </li>
      ))}
    </ul>
  )
}

const items = [{ id: 0, target: 'Ready' }, { id: 1, target: 'Player' }, { id: 2, target: 'One' }]

ReactDOM.render(
  <Parent menuItems={items} />,
  demo
)
.navLink {
  color: blue;
  text-decoration: none;
}

.active {
  color: red;
}
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="demo"></div>

person Ori Drori    schedule 02.05.2019
comment
Спасибо за ответ, в настоящее время он действительно работает, однако следующая проблема, с которой я столкнулся, заключается в том, что теперь каждая навигационная ссылка вызывает setActive вместо навигации, в которой также вызывается setActive. Можно ли вызвать навигацию, которая, в свою очередь, вызовет setActive? - person Jayce; 03.05.2019
comment
Ничего, я нашел решение, выложу в редакцию - person Jayce; 03.05.2019

(1) Assign an id to each of child components
(2) Add inactive class to all child components
(3) Remove inactive class from selected component and add active class to it.
Это рабочее решение вашего вопроса. Я надеюсь, что это помогает.

class App extends React.Component {
  state = {
    childComponents: [
      { id: "ironman", component: <IronMan /> },
      { id: "captainamerica", component: <CaptainAmerica /> },
      { id: "thor", component: <Thor /> },
      { id: "loki", component: <Loki /> },
      { id: "spiderman", component: <Spiderman /> }
    ],
    currComponentId: ""
  };

  clickHandler = idComponent => {
    // get access to all classes
    if (this.state.currComponentId !== "")
      document
        .getElementById(this.state.currComponentId)
        .classList.remove("active");
    let element = document.getElementsByClassName("child-components");
    for (let index = 0; index < element.length; index++) {
      element[index].classList.add("inactive");
    }
    document.getElementById(idComponent).classList.remove("inactive");
    document.getElementById(idComponent).classList.add("active");
    this.setState({ currComponentId: idComponent });
  };
  render() {
    return (
      <div className="parent">
        <ul>
          {this.state.childComponents.map(element => {
            return (
              <li>
                <button
                  id={element.id}
                  className="child-components"
                  onClick={() => this.clickHandler(element.id)}
                >
                  {element.id}
                </button>
                {this.state.currComponentId === element.id ? (
                  <span> Active component</span>
                ) : null}
              </li>
            );
          })}
        </ul>
        <div>
          {this.state.childComponents.map(element => {
            if (element.id === this.state.currComponentId)
              return <div>{element.component}</div>;
          })}
        </div>
      </div>
    );
  }
}


const IronMan = () => <div>This is IronMan Component</div>;
const CaptainAmerica = () => <div>This is CaptainAmerica Component</div>;
const Thor = () => <div>This is Thor Component</div>;
const Loki = () => <div>This is Loki Component</div>;
const Spiderman = () => <div>This is Spiderman Component</div>;

ReactDOM.render(<App/>, document.getElementById('root'));
.active {
  border: solid 1px red;
  background-color: black;
  color: #fff;
}
.inactive {
  color: #000;
  background-color: #fff;
}
.parent{
 border: solid 1px #322f31;
}
<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>
<div id='root' />

person Avinash Mahlawat    schedule 02.05.2019

Я думаю, что у меня будет запись состояния в родительском элементе, например whichIsActive, и дать ему свойство set активной ссылки с функцией onclick, например, индекс.

const navigate = (index) => {
this.setState{(whichIsActive: index)}
 };

Затем в вашем className вы можете сделать что-то вроде className=${this.state.whichIsActive === index && 'active'} (не забывая `around). Я не тестировал, но думаю, должно работать.

person Alicia    schedule 02.05.2019