react-bootstrap, как свернуть меню, когда элемент выбран

Как сделать так, чтобы меню сворачивалось после выбора пункта?

Я не знаю, как заставить его работать на скрипке, но что бы я сделал? https://jsfiddle.net/vjeux/kb3gN/

import React from 'react';
import {Navbar, Nav, NavItem, NavDropdown, DropdownButton, MenuItem, CollapsibleNav} from 'react-bootstrap';

export default class App extends React.Component {

    constructor(props) {
      super(props);
      this.onSelect = this.onSelect.bind(this);
      this.toggleNav = this.toggleNav.bind(this);
      // this.state = {navExpanded: false};
    }

    onSelect(e){
        console.log('OnSelect')
        // console.log(this.state.navExpanded);
        // this.setState({navExpanded: false});
    }

    toggleNav(){console.log('toggle...')};

    // <Navbar inverse fixedTop toggleNavKey={0} navExpanded={this.state.navExpanded} onToggle={() => this.toggleNav()}>
    // <Navbar inverse fixedTop toggleNavKey={0} navExpanded={this.state.navExpanded} >

    render() {
        return (

          <Navbar inverse fixedTop toggleNavKey={0} >
            <Nav right eventKey={0} onSelect={this.onSelect} > {/* This is the eventKey referenced */}
              <NavItem eventKey={1} href="#">Link</NavItem>
              <NavItem eventKey={2} href="#">Link</NavItem>
            </Nav>
          </Navbar>     

      )

    }

    componentDidMount() {

    }
}

React.render(<App />, document.getElementById('example'));

person Chris G.    schedule 08.09.2015    source источник
comment
Не могли бы вы решить эту проблему?   -  person Johannes Reuter    schedule 08.09.2015
comment
Похоже, это ошибка. Я предполагаю, что вы также являетесь автором github.com/react-bootstrap/react- bootstrap / issues / 1301. Мы принимаем запросы на вытягивание, если вы готовы с этим справиться.   -  person Matt Smith    schedule 09.09.2015
comment
Отлично звучит, я в. В какой ветке?   -  person Chris G.    schedule 09.09.2015


Ответы (11)


я нашел решение по этой ссылке https://github.com/react-bootstrap/react-bootstrap/issues/1301

я помещу образец кода ссылки выше здесь

const Menu = React.createClass ({
  getInitialState () {
    return {
      navExpanded: false
    }
  },

  setNavExpanded(expanded) {
    this.setState({ navExpanded: expanded });
  },

  closeNav() {
    this.setState({ navExpanded: false });
  },

  render() {
    return (
      <div>
        <Navbar onToggle={this.setNavExpanded}
                expanded={this.state.navExpanded}>
          <Navbar.Header>
            <Navbar.Brand>
              <Link to="some url">Main</Link>
            </Navbar.Brand>
            <Navbar.Toggle />
          </Navbar.Header>
          <Navbar.Collapse>
            <Nav onSelect={this.closeNav}>
              { this.renderMenuItem() }
            </Nav>
          </Navbar.Collapse>
        </Navbar>
      </div>
    )
  }
person Alongkorn Chetasumon    schedule 25.06.2017

Для тех, кто приезжает сюда в 2020 году и использует Хуки, возможно, вы используете react-router, и в результате вместо Nav.Link, которые являются компонентом по умолчанию для панели навигации, вы используете Link из react-router.

А что вы нашли? В результате мобильное меню не работает должным образом и не закрывается после нажатия на ссылку, и, похоже, ничего не работает.

Вот мое простое решение (с помощью крючков) этой проблемы:

Сначала настраиваем перехватчик:

const [expanded, setExpanded] = useState(false);

Во-вторых в Navbar мы добавляем эту опору:

<Navbar expanded={expanded}>

Теперь у нас есть контроль над видимостью меню, при первой загрузке оно будет скрыто.

В-третьих, мы добавляем onClick событие в обработчик переключения, которое изменяет статус видимости меню:

<Navbar.Toggle onClick={() => setExpanded(expanded ? false : "expanded")} />

В-четвертых, мы добавляем опору onClick={() => setExpanded(false)} ко всем нашим Link компонентам из реактивного маршрутизатора внутри нашей панели навигации.

Выгода! Я клянусь, что после более чем 1 часа блуждания в Интернете это самое простое и чистое решение, которое я нашел.

person Josep Vidal    schedule 23.10.2019
comment
Это отличное решение. Я добавил таймаут, чтобы немного улучшить UX: onClick={() => setTimeout(() => {setExpanded(false)}, 150)} - person Martin; 26.02.2020
comment
Идеальное решение - person Hassan Raza; 14.07.2020
comment
классно! вы получите 10/10 - person Darren; 03.08.2020
comment
Это определенно работает для обычного переключателя меню, но не совсем работает для MDBHamburgerToggler с анимацией. Меню открывается и закрывается правильно во всех случаях, но когда вы нажимаете на ссылку, значок гамбургера не переключается, поэтому у вас есть правильная расширенная настройка, но неправильный значок. Их документы, похоже, не решают эту проблему. Это третий пример здесь: mdbootstrap.com/docs/react/navigation/hamburger -menu / - person Alex; 20.09.2020
comment
Если кто-то работает с Typescript: onClick = {(e) = ›setExpanded (! Expanded)} - person user1912383; 13.10.2020
comment
Великолепно, спасибо вам большое! Для последнего шага я добавил onClick={() => setExpanded(false)} только в ‹Nav›, и он по-прежнему работал хорошо. Этот метод работает только в том случае, если у вас нет вложенных меню, иначе щелчок по вложенному меню свернет всю панель навигации. В этом случае лучше добавить callbak в каждый ‹Link›. - person Edgar Manukyan; 25.12.2020
comment
Отлично работает, как и ожидалось. Спасибо. - person invinciblemuffi; 03.01.2021
comment
ПРЕДУПРЕЖДЕНИЕ. В этом примере значение expanded не будет отражать истинную видимость навигационной панели, когда окно расширяется после первоначального переключения. - person s2t2; 07.01.2021
comment
setExpanded(expanded ? false : "expanded") можно упростить до setExpanded(!expanded). - person Mike; 03.03.2021

Немного связано с проблемой, может быть полезно для кого-то Это то, что я сделал, чтобы закрыть панель навигации при нажатии вне меню

class Menu extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isNavExpanded: false
    };
  
    this.setIsNavExpanded = (isNavExpanded) => {
      this.setState({ isNavExpanded: isNavExpanded });
    }
  
    this.handleClick = (e) => {
      if (this.node.contains(e.target)) {
        // if clicked inside menu do something
      } else {
        // If clicked outside menu, close the navbar.
        this.setState({ isNavExpanded: false });
      }
    }
  }
  componentDidMount() {
    document.addEventListener('click', this.handleClick, false);      
  }

  componentWillUnmount() {
    document.removeEventListener('click', this.handleClick, false);
  }

  render() {
    return (
      <div ref={node => this.node = node}
        <Navbar collapseOnSelect
           onToggle={this.setIsNavExpanded}
           expanded={this.state.isNavExpanded}
           >
          <Navbar.Collapse>
            // Nav Items
          </Navbar.Collapse>
        </Navbar>
      </div>
    )
  }

person madhur    schedule 02.04.2019

<Navbar collapseOnSelect ... > должен работать, но работает нестабильно :(

person KARPOLAN    schedule 29.05.2019

Мне нравится сообщение от @Josep Vidal, потому что у меня была такая же проблема, однако у меня есть небольшое изменение в его ответе. Хотя его подход действительно лучший на данный момент, я считаю, что функция была бы чище, если бы вместо

<Navbar.Toggle onClick={() => setExpanded(expanded ? false : "expanded")} />

мы сделали

<Navbar.Toggle onClick={() => setExpanded((prevExpanded) => (prevExpanded = !prevExpanded)) />

Кроме того, нет необходимости помещать onClick={() => setExpanded(false)} во все наши Link компоненты, мы можем просто поместить его один раз в родительский Nav

person Filip Filipovic    schedule 28.04.2020

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

Итак ... мы позволяем функции Bootstrap по умолчанию обрабатывать эту часть, и нам просто нужно добавить функциональность, чтобы свернуть меню, используя ту же функциональность, но только она должна срабатывать только при раскрытии меню (в противном случае у нас возникает нежелательное конфликтное поведение. )

Нам нужна расширенная константа, чтобы определить, есть ли статус сворачиваемого / расширяемого меню, чтобы предотвратить его расширение, когда сайт просматривается на настольном устройстве, а полное меню уже отображается.

const [expanded, setExpanded] = useState("")

Это единственная константа, которую мы устанавливаем с событием щелчка на кнопке переключения навигационной панели. Он либо развернут, либо не развернут (мы справляемся с этим), а свернутый или не свернутый (это обрабатывает Bootstrap).

<button
    className={"navbar-toggler collapsed" + expanded}
    type="button"
    data-bs-toggle="collapse"
    data-bs-target="#navbar"
    aria-controls="navbar"
    aria-expanded="false"
    aria-label="Toggle navigation"
    onClick={() => setExpanded(expanded ? "" : " expanded")}
>

Теперь у нас есть состояние расширенного только тогда, когда меню действительно расширено, и поскольку кнопка переключения навигационной панели отображается только на мобильных устройствах (в зависимости от вашей конфигурации), на настольном устройстве не будет конфликтов.

Теперь нам нужно только использовать функцию Bootstrap по умолчанию для переключения меню, поэтому для каждого элемента, который вы хотите вести себя как переключатель, вы должны добавить эту функциональность. Это будет обрабатывать все поведение Bootstrap, поэтому нам не нужны дополнительные перехватчики.

<li className="nav-item" data-bs-toggle={expanded && "collapse"} data-bs-target="#navbar" aria-controls="navbar" onClick={() => setExpanded("")}>

Это приводит к срабатыванию функции переключения только при раскрытии меню, то есть только на мобильных устройствах. Никаких конфликтов.

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

Жду комментариев и улучшений.

person user3004118    schedule 29.01.2021

В зависимости от приведенных выше ответов и некоторых передовых практик в Интернете я придумал отличный код и поделился им здесь. Bellow - это просто компонент Navbar, который можно легко выбрать и использовать, поскольку он является адаптивным медиа, установленным на ширину 500 пикселей.

import './navbar.css'
import { useState, useEffect } from 'react'
import { Route, BrowserRouter as Router, Switch, Link} from 'react-router-dom'
import Home from './Pages/Home'
import About from './Pages/About'
import Contact from './Pages/Contact'
import Product from './Pages/Product'

const Navbar = () => {
    const [screenWidth, setScreenWidth] = useState(window.innerWidth)
    const [toggleMenu, setToggleMenu] = useState(false)

    function toggle(){
        setToggleMenu( preValue => (!preValue))
    }
     
    useEffect(() => {

        const changeWidth = () => {
            setScreenWidth(window.innerWidth);
          }
          window.addEventListener('resize', changeWidth)

        return () => {
            window.removeEventListener('resize', changeWidth)
        }

    },[])

    return (
        <Router>
            <nav>
                {// better to use Link component instead of href as Link will keep entire operation at client side and not reeload the page
                }
            {
                (toggleMenu || screenWidth >= 500 ) && (
                <ul className='list'>
                <li className='items'><Link onClick={() => setToggleMenu(false)} to='/'>Home</Link></li>
                <li className='items'><Link onClick={() => setToggleMenu(false)} to='/about'>About Us</Link></li>
                <li className='items'><Link onClick={() => setToggleMenu(false)} to='/product'>Product</Link></li>
                <li className='items'><Link onClick={() => setToggleMenu(false)} to='/contact'>Contact Us</Link></li>
            </ul>
                )

            }
            <button className='btn'
            onClick={toggle}>Btn</button>
        </nav>

        <Switch>
            <Route path='/contact'><Contact /></Route>
            <Route path='/product'><Product /></Route>
            <Route path='/about'><About /></Route>
            <Route path='/'><Home /></Route>
        </Switch>
        </Router>
    )
}

export default Navbar
person pr0mpT_07    schedule 28.04.2021

У меня работает решение Йозефа. Я реализовал все четыре пункта. Но у меня есть другой макияж:

            <Navbar expanded={expanded} bg="light" expand="lg" className="p-3">
            <Navbar.Brand href="/"><img src={CarLogo} alt="Tim-Tim auto" title="Tim-Tim auto" /></Navbar.Brand>
            <Navbar.Toggle onClick={() => setExpanded(expanded ? false : "expanded")} aria-controls="basic-navbar-nav" />
                <Navbar.Collapse id="basic-navbar-nav" className="justify-content-end">
                    <Nav> 
                        <Nav.Item><NavLink onClick={() => setExpanded(false)} exact to='/'>Home</NavLink></Nav.Item>
                        <Nav.Item><NavLink onClick={() => setExpanded(false)} to='/Cars'>Cars</NavLink></Nav.Item>
                        <Nav.Item><NavLink onClick={() => setExpanded(false)} to='/about'>About</NavLink></Nav.Item>
                        <Nav.Item><NavLink onClick={() => setExpanded(false)} to='/news'>News</NavLink></Nav.Item>
                        <Nav.Item><NavLink onClick={() => setExpanded(false)} to='/contacts'>Contacts</NavLink></Nav.Item>
                    </Nav>
                </Navbar.Collapse>
            </Navbar>
person Vadim Bocharov    schedule 12.11.2019

Используя крючки на шаге 3,

<Navbar.Toggle aria-controls="basic-navbar-nav" onClick={() => setExpanded(!expanded)} />

у меня работает, и шаг 4 я не писал.

person Athena Chen    schedule 15.12.2020

Для тех, кто использует перехватчики реакции, используйте @Alongkorn Chetasumon выше:

  const wrapperRef = useRef(null);
  const [navExpanded, setNavExpanded] = useState();
  const closeNav =()=> {
    setNavExpanded(false);
  }
  useEffect(() => {
    /**
     * Alert if clicked on outside of element
     */
    function handleClickOutside(event) {
      if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
        //alert("You clicked outside of me!");
        closeNav();
      }
    }

    // Bind the event listener
    document.addEventListener("mousedown", handleClickOutside);
    return () => {
        // Unbind the event listener on clean up
        document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [wrapperRef]);
person Tony    schedule 23.03.2021

person    schedule
comment
Этим я смог решить проблему с развалом. - person user3396478; 02.03.2021