REACT.JS: как перебрать все кнопки NavBar и удалить их класс и добавить активный класс к нажатой кнопке

Я пытаюсь сделать простой NavBar с React.js. Проблема, с которой я столкнулся, - это зацикливание всех навигационных кнопок и удаление «активного» className, а затем добавление «активного» только к одной нажатой кнопке.

Мне удалось создать состояние, которое переключает "активный" на true на выбранном элементе, который затем в атрибуте className выполняет этот оператор If:

className={this.state.active ? "nav-item nav-link active" : "nav-item nav-link"}

Вот полный код:

import React, { Component } from 'react';

class NavButton extends Component {
    state = {
        active: false
    }

    setActive = () => {
        this.setState({
            active: !this.state.active
        })
    }

    render() {
        return (
            <a 
            className={this.state.active ? "nav-item nav-link active" : "nav-item nav-link"} 
            href={this.props.href}
            onClick={this.setActive}> {this.props.title} 
            </a>
        )
    }
}

class NavBar extends Component {

    buttons = [
        {
            title: "Home",
            key: 0
        },
        {
            title: "Team",
            key: 1
        },
        {
            title: "Discord",
            key: 2
        },
        {
            title: "Gallery",
            key: 3
        },
        {
            title: "Download",
            key: 4
        }
    ]

    render() {
        return (
            <nav className="navbar" id="navbarMain">
                <div></div>
                <div className="navbar-nav flex-row">
                    {this.buttons.map(button => <NavButton title={button.title} key={button.key} />)}
                </div>
                <div></div>
            </nav>
        )
    }
}

export default NavBar

Это работает только для одного элемента (не обращайте внимания на то, что активное состояние переходит в false, когда оно true. Проблема в том, как мне сделать это способом React, чтобы удалить активное имя класса во всех других кнопках?

С простым JS у меня нет проблем с этим, я просто перебираю все элементы с именем className «navbar-item» и устанавливаю их имена классов без «активного», затем добавляю «активный» к нажатому элементу, как в этом пример https://www.w3schools.com/howto/howto_js_tabs.asp

Не могли бы вы помочь мне и сказать, как лучше всего отреагировать на это?

Очень признателен!


person Matt Chowski    schedule 23.06.2019    source источник


Ответы (2)


Обычным шаблоном для этих вариантов использования является сохранение соответствующего состояния в родительском элементе, так что именно родительский элемент (NavBar) отслеживает, какой дочерний элемент (NavButton) является «активным». Затем NavButton может стать компонентом без состояния, который принимает «активный» в качестве опоры.

const NavButton = ({active, title, href, onSetActive}) => {
        return (
            <button 
            className={active ? "nav-item nav-link active" : "nav-item nav-link"} 
            href={href}
            onClick={onSetActive} > 
              {title} 
            </button>
        )
}

class NavBar extends React.Component {
		constructor(props) {
    	super(props);
    	this.state = {
      	activeIndex: 0, // keep the active index in state
      	buttons: [
        {
            title: "Home",
            key: 0
        },
        {
            title: "Team",
            key: 1
        },
        {
            title: "Discord",
            key: 2
        },
        {
            title: "Gallery",
            key: 3
        },
        {
            title: "Download",
            key: 4
        }
    ]
      }
    }
    
    handleChangeActive(newActiveIndex) {
    	this.setState({activeIndex: newActiveIndex});
    }

    render() {
    		const {activeIndex} = this.state;
        return (
            <nav className="navbar" id="navbarMain">
                <div></div>
                <div className="navbar-nav flex-row">
                    {this.state.buttons.map((button, buttonIndex) => 
                    	/* determine which nav button is active depending on the activeIndex state */
                    	<NavButton onSetActive={ () => this.handleChangeActive(buttonIndex)} active={buttonIndex === activeIndex } title={button.title} key={button.key} />)}
                </div>
                <div></div>
            </nav>
        )
    }
}

ReactDOM.render(<NavBar />, document.querySelector("#app"));
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

li {
  margin: 8px 0;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

.done {
  color: rgba(0, 0, 0, 0.3);
  text-decoration: line-through;
}

input {
  margin-right: 5px;
}
.nav-item.nav-link {
  background: grey;
}
.nav-item.nav-link.active {
  background: red;
}
<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>
<body>

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

person jonahe    schedule 23.06.2019
comment
вау, спасибо большое, чувак! Отличное объяснение, и код понятен и отлично работает! Помечен как правильный ответ - person Matt Chowski; 23.06.2019
comment
Извините за беспокойство, хотя я не понимаю, почему другие кнопки отключаются? Почему activeIndex возвращает false для других элементов без щелчка, если мы настроили activeIndex таким, какой будет индекс нажатой кнопки? - person Matt Chowski; 23.06.2019
comment
Когда вы просматриваете список кнопок, каждая из них имеет разные индексы (buttonIndex). Первый будет иметь индекс 0, затем индекс 1 и т. Д. И только один из них будет равен тому, который вы сохранили как activeIndex. Когда вы меняете состояние activeIndex, другой buttonIndex будет соответствовать activeIndex. Надеюсь, это имеет смысл. - person jonahe; 23.06.2019
comment
Функция, которую вы используете в .map (или .forEach), получает текущий элемент в качестве первого аргумента, а текущий индекс - в качестве второго аргумента. (Также передается третий аргумент со всем массивом). Попробуйте вставить ["a","b","c"].forEach(console.log) в консоль браузера. Для каждого элемента будет записано 1: элемент (буква), 2: индекс, 3: массив. Надеюсь, я не утверждаю очевидное. Может я неправильно понял твой вопрос. - person jonahe; 23.06.2019

Я бы перенес ваше состояние и логику в компонент NavBar. Он будет отвечать за сохранение и установку текущей активной кнопки и передачу ее в качестве опоры для всех кнопок.

class NavBar extends Component {
  state = {
    activeButtonIndex: null;
  }

  buttons = [
    {
      title: "Home",
      key: 0
    },
    {
      title: "Team",
      key: 1
    },
  ];

  renderButton = (button, index) => (
    <NavButton 
      {...button} 
      isActive={this.state.activeButtonIndex === index}
      setActive={() => this.setState({ activeButtonIndex: index })}
    />
  );

  render() {
    return (
      <nav className="navbar" id="navbarMain">
        <div className="navbar-nav flex-row">
          {this.buttons.map((button, index) => this.renderButton(button, index)}
        </div>
      </nav>
    );
  }
}

const NavButton = ({ isActive, setActive, href, title }) => (
  <a 
    className={isActive ? "nav-item nav-link active" : "nav-item nav-link"} 
    href={href}
    onClick={setActive}
  >
    {title} 
  </a>
);
person adesurirey    schedule 23.06.2019