Search code examples
reactjsreact-bootstrapreact-bootstrap-nav

NavDropdown Tab not set to active when Dropdown Item link is active (react-bootstrap)


I'm still quite new to Typescript and react and unfortunately don't understand what I'm doing wrong. I have a tab style nav with a NavDropdown. Unfortunately the dropdown is the only tab that does not get the active class when one of the dropdown items is selected. I have already seen that some people had this problem and could solve it somehow but I can't find a way to do it in my case. Can anyone help me?

Currently my nav looks like this:

  <Nav variant="tabs">
    <Nav.Item>
      <Nav.Link
        onClick={(e: React.MouseEvent<Element, MouseEvent>) => setTab(e, "today")}
        href="#today"
      >
        today
      </Nav.Link>
    </Nav.Item>

    <NavDropdown
      title={activeTab === "month" ? dateFormat(dateStart!, "MMMM yyyy") : "month"}
      id="month"
    >

      {months.map((m) => (
        <NavDropdown.Item onClick={() => setMonth(m)} key={dateFormat(m, "MMMM yyyy")}>
          {dateFormat(m, "MMMM yyyy")}
        </NavDropdown.Item>
      ))}
    </NavDropdown>

Solution

  • I've modified the following parts to fix the tab navigation and month selection:

    • Added defaultActiveKey={activeTab} to the Nav component to activate the tab on load
    • Nav.Link added eventKey="today" to have the key for the today tab set up properly
    • On NavDropdown.Item added active={m === month} where m is the iterator of the map (e.g. Jan 2021, Feb 2021 etc) and month is the selected month. This is needed so the selection in the drop-down is working.
    • In the onClick of the drop-down items added setMonth & setTab to update the state.
    • On NavDropdown added active={activeTab === "month"} so the tab navigation is correctly updating.

    I've also used moment.js to generate the month array and to format the months. Generation of the array is from this SO answer.

    You can have a look at the code below or in this Codesandbox.

    /*import moment from "moment";
    import { useState } from "react";
    import Nav from "react-bootstrap/Nav";
    import NavDropdown from "react-bootstrap/NavDropdown";
    import "./styles.css";
    import "bootstrap/dist/css/bootstrap.min.css";*/
    
    const {useState} = React;
    const {Nav, NavDropdown} = ReactBootstrap;
    const MONTH_FORMAT_STR = "MMM yyyy";
    const months = Array.apply(0, Array(12)).map(function (_, i) {
      return moment().month(i).format(MONTH_FORMAT_STR);
    });
    
    const curMonth = moment().format(MONTH_FORMAT_STR);
    
    function App() {
      const [month, setMonth] = useState(curMonth);
      const [activeTab, setTab] = useState(`month`);
      const [dateStart, setStart] = useState(""); // not implemented yet
    
      return (
        <Nav variant="tabs" defaultActiveKey={activeTab}>
          <Nav.Item>
            <Nav.Link
              eventKey="today"
              onClick={() => {
                setTab("today");
                console.log("set month", curMonth);
                setMonth(curMonth);
              }}
              href="#today"
            >
              today
            </Nav.Link>
          </Nav.Item>
          <Nav.Item>
            <NavDropdown
              id="month"
              active={activeTab === "month"}
              title={moment().month(month).format("MMMM YYYY")}
            >
              {months.map((m) => (
                <NavDropdown.Item
                  title={m}
                  eventKey={m}
                  active={m === month}
                  onClick={() => {
                    setMonth(m);
                    setTab("month");
                  }}
                  key={m}
                >
                  {m}
                </NavDropdown.Item>
              ))}
            </NavDropdown>
          </Nav.Item>
          {activeTab} {month}
        </Nav>
      );
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    <script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" integrity="sha512-qTXRIMyZIFb8iQcfjXWCO8+M5Tbc38Qi5WzdPOYZHIlZpzBHG3L3by84BBBOiRGiEb7KKtAOAs5qYdUiZiQNNQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-bootstrap/1.5.2/react-bootstrap.min.js" integrity="sha512-Mw0NWa5M4d73pjqO5CY7Olq0yjPg80GsKCRHpy114idMaX8v+scJmRvhWbXTct8inkKGJNzBvMyWG4ok79zigQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css"
      integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l"
      crossorigin="anonymous"
    />
    
      
    <div id="root"/>