Search code examples
cssreactjsreact-bootstrapnav

React, Bootstrap Nav


I'm new in React world, and stucked on beginning.

Trying to make Nav menu with react-bootstrap.Everything works fine until Nav.Item is changed. onSelect() should change activeKey, and consequently change style on clicked Nav.Item.

index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import 'bootstrap/dist/css/bootstrap.min.css';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

App.tsx:

import NavMenu from "./components/NavMenu";

const navItems: any = [
    {id: 0, name: "Home", link:"home"},
    {id: 1, name: "First", link:"first"},
    {id: 2, name: "Second", link:"second"}
]


const App = () => {
    return (
        <>
            <NavMenu navItems={navItems}/>
        </>
    );
}

export default App;

NavMenu/idnex.tsx:

import {useState} from "react";
import {Nav} from "react-bootstrap";

const NavMenu = (props: any) => {
    const {navItems} = props;
    const [activeNav, setActiveNav] = useState('first')

    const handleClick = (eventKey: any) => setActiveNav(eventKey);

    console.log(activeNav)

    return (
        <div>
            <Nav
                defaultActiveKey="home"
                className="flex-column"
                activeKey={activeNav}
                variant="pills"
                navbar={true}
                onSelect={handleClick}
            >
            { navItems.map((item: any) => {
                const {id, link, name} = item;

                    return (
                        <Nav.Item key={id}>
                            <Nav.Link href={link}
                                      eventKey={link}>
                                {name}
                            </Nav.Link>
                        </Nav.Item>
                    );
                })}
            </Nav>
        </div>
    );
}

export default NavMenu;

package.json:

{
  "name": "nav-test-case",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-scripts": "4.0.1",
    "react-bootstrap": "1.4.0",
    "web-vitals": "^0.2.4",
    "bootstrap": "4.5.3",
    "typescript": "^4.1.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

Change is visible for one moment, and then it just rollback to default setted Nav.Item. So it's not selected clicked Nav.Item, than the one set by default.

In Console I see change of variable from log

console.log(activeNav)

but whole page refreshes and log is reseted as well

moment before refresh

Please if someone can point me in the right direction to find out what I'm doing wrong


Solution

  • As far as I see it you want to have links working inside the app - but what you did is simply the way you can go to implement links to external pages.

    The way to go is to use react-router-dom.

    1. Add react-router-dom with your package manager.
    2. Wrap your application with the BrowserRouter and define the possible Routes of your page in the Switch that defines all possible pages of your app and the according components.
     <StrictMode>
       <BrowserRouter>
         <Switch>
           <Route exact path="/" component={App} />
           <Route exact path="/home" component={App} />
           <Route exact path="/first" component={App} />
           <Route exact path="/second" component={App} />
         </Switch>
       </BrowserRouter>
     </StrictMode>,
    
    1. Use the Link component from react-router-dom and declare your nav links as those:
    <Nav.Item key={id}>
     <Nav.Link as={Link} to={link} eventKey={link}>
       {name}
     </Nav.Link>
    </Nav.Item>
    

    That should be it. You can see it working here: https://codesandbox.io/s/competent-cherry-8hnu9