proper way to implement reactstrap <Navbar> with submenu items? - reactjs

I have a basic reactstrap implementation in my app. The menu items display as configured in a nice, responsive way. One of the configured top-level menu items is "Reports" and I need to add 2 selectable submenu items for 2 different reports under this top-level menu item.
Does proper responsive design dictate that only top-level menu items should display on the main page for proper responsive handling? Or is there a proper way to configure submenu items for this scenario? I'm not sure if I need to configure a more customized navigation design like this:
https://reactstrap.github.io/components/navs/
Or does the constraint exist because that is simply considered proper design for responsive websites?

I have a couple of responsive websites with several layers of submenus and i works well.
In Reactstrap there is an example of how to do expanding submenus. This is copied from https://reactstrap.github.io/?path=/docs/components-nav--navs#tabs
import React, { useState } from 'react';
import {
Nav,
NavItem,
Dropdown,
DropdownItem,
DropdownToggle,
DropdownMenu,
NavLink,
} from 'reactstrap';
function Example(props) {
const [dropdownOpen, setDropdownOpen] = useState(false);
const toggle = () => setDropdownOpen(!dropdownOpen);
return (
<Nav tabs>
<NavItem>
<NavLink href="#" active>
Link
</NavLink>
</NavItem>
<Dropdown nav isOpen={dropdownOpen} toggle={toggle}>
<DropdownToggle nav caret>
Dropdown
</DropdownToggle>
<DropdownMenu>
<DropdownItem header>Header</DropdownItem>
<DropdownItem disabled>Action</DropdownItem>
<DropdownItem>Another Action</DropdownItem>
<DropdownItem divider />
<DropdownItem>Another Action</DropdownItem>
</DropdownMenu>
</Dropdown>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Another Link</NavLink>
</NavItem>
<NavItem>
<NavLink disabled href="#">
Disabled Link
</NavLink>
</NavItem>
</Nav>
);
}
export default Example;
As you can see it uses useState to hold flags for the openness of the menu item. You can modify this to hold state for whether items are "selected" or whatever, then just draw with that state in mind, for example:
<DropdownItem disabled>Action</DropdownItem>
to
const [actionSelected, setActionSelected] = useState(false);
<DropdownItem disabled={actionSelected} onClick={()=>setActionSelected(true)}>Action</DropdownItem>

Related

React : Error: Too many re-renders. React limits the number of renders to prevent an infinite loop

Im currently trying to import my Navbar into a new project. I created a Navbar using the state hook.
export default function Heading() {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<Navbar color="light" fixed="top" light expand="md">
<NavbarBrand href="/">reactstrap</NavbarBrand>
<NavbarToggler onclick={setIsOpen(!isOpen)} />
<Collapse isOpen={isOpen} navbar>
<Nav className="me-auto" navbar>
<NavItem>
<NavLink href="/components/">Components</NavLink>
</NavItem>
<NavItem>
<NavLink href="https://github.com/reactstrap/reactstrap">
GitHub
</NavLink>
</NavItem>
<UncontrolledDropdown inNavbar nav>
<DropdownToggle caret nav>
Options
</DropdownToggle>
<DropdownMenu end>
<DropdownItem>Option 1</DropdownItem>
<DropdownItem>Option 2</DropdownItem>
<DropdownItem divider />
<DropdownItem>Reset</DropdownItem>
</DropdownMenu>
</UncontrolledDropdown>
</Nav>
<NavbarText>Simple Text</NavbarText>
</Collapse>
</Navbar>
</div>
);
}
I am importing this in my App.tsx like this
import Heading from "./modules/Heading";
export default function App() {
return (
<div className="App">
<Heading/>
</div>
);
}
But im getting this error
Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
Does anyone know how to fix this?
The issue is highly likely with the onclick in your NavbarToggler component.
Usually in React, onclick event handler usually takes in a FUNCTION, not a FUNCTION CALL. Therefore, you should change to
<NavbarToggler onClick={() => setIsOpen(currentState => !currentState)} />
This small change creates an arrow function that will do the same as before. However, the difference is that this is only a function definition, meaning it is not called yet (and is just existing)
It's missing an arrow function in NavbarToggler onClick Event . I guess will solve the problem

reactstrap Collapse is not showing up on mobile

The website I'm working on has an issue where the collapsible menu that's supposed to show up when the screen size is too small is transparent. It still works, but it's not visible, which obviously isn't what I intended. I'm using reactstrap to make the collapsible menu but I haven't discovered a way to make it visible so far in my research.
Can someone please help me solve this issue? The file where this issue happens is here in the render method.
<Navbar expand="sm" className="mb-3 my-navbar">
<Container>
<NavbarBrand tag={RRNavLink} exact to="/"
className="my-navbar-brand">Writer</NavbarBrand>
<NavbarToggler onClick={this.toggle} />
<Collapse isOpen={this.state.isOpen} navbar>
<Nav className="ml-auto" navbar>
{isAuthenticated ? authLinks : guestLinks}
</Nav>
</Collapse>
</Container>
</Navbar>
Since you are not using a theme for your navbar, the CSS for the toggler button is not appearing. As a fix you can try to add a theme to the navbar, e.g., navbar-light and you will see that the hamburger button appears
<Navbar expand="sm" className="mb-3 my-navbar navbar-light">
Alternatively, you can add the CSS for which ever color of the stroke you prefer. Here is the CSS for the navbar-light hamburger button strokes:
.navbar-toggler-icon {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255,255,255, 1)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E");
}

Toggling Images for Active Links with React.js

I am currently trying to learn React.js for a school project and am having trouble with toggling images in combination with active links (I have searched high and low for a tutorial with no avail). My site is going to look similar to the old time Kingdom Hearts 1 menu theme; the Navbar looks like the following image.
As shown in the above image, the home page is hard-coded to be "active." What I need help with is making each link appear in this "active" state when selected/active. When a user clicks on one of the NavLinks, the image should go from grey to black/orange. When a link is not selected (or inactive), the image should go from black/orange back to grey. Active/selected links should also have black text while inactive links have grey. So far, all I have found out is how to toggle a singular class but not multiple. Overall, I really don't know how to go about this at all. How do I toggle one NavLink active and change its image while also changing all other NavLinks to inactive, changing their images as well? Any help would be much appreciated. I have the following code for my Navbar so far:
export class NavMenu extends Component {
static displayName = NavMenu.name;
constructor(props) {
super(props);
this.toggleNavbar = this.toggleNavbar.bind(this);
this.state = {
collapsed: true
};
}
toggleNavbar() {
this.setState({
collapsed: !this.state.collapsed
});
}
render() {
return (
<Navbar
fixed="bottom"
className="navbar-expand-sm navbar-toggleable-sm ng-white border-top mb-3"
light
>
<NavbarToggler onClick={this.toggleNavbar} className="mr-2" />
<Collapse
className="d-sm-inline-flex flex-sm-row-reverse"
isOpen={!this.state.collapsed}
navbar
>
<ul className="navbar-nav mr-auto">
<NavItem>
<NavLink
tag={Link}
id="navHome"
className="text-dark active"
to="/"
>
<img
src="/Images/NLUnactiveImg.png"
alt="Unactive Link Image"
height="45"
width="45"
hidden
/>
<img
src="/Images/NLActiveImg.png"
alt="Active Link Image"
height="45"
width="45"
/>
Home
</NavLink>
</NavItem>
<NavItem>
<NavLink
tag={Link}
d="navResume"
className="text-dark inactive"
to="/resume"
>
<img
src="/Images/NLUnactiveImg.png"
alt="Unactive Link Image"
height="45"
width="45"
/>
<img
src="/Images/NLActiveImg.png"
alt="Active Link Image"
height="45"
width="45"
hidden
/>
Resume
</NavLink>
</NavItem>
<NavItem>
<NavLink
tag={Link}
d="navContact"
className="text-dark inactive"
to="/contact"
>
<img
src="/Images/NLUnactiveImg.png"
alt="Unactive Link Image"
height="45"
width="45"
/>
<img
src="/Images/NLActiveImg.png"
alt="Active Link Image"
height="45"
width="45"
hidden
/>
Contact
</NavLink>
</NavItem>
<NavItem>
<NavLink
tag={Link}
d="navFetch"
className="text-dark inactive"
to="/fetch-data"
>
<img
src="/Images/NLUnactiveImg.png"
alt="Unactive Link Image"
height="45"
width="45"
/>
<img
src="/Images/NLActiveImg.png"
alt="Active Link Image"
height="45"
width="45"
hidden
/>
Fetch data
</NavLink>
</NavItem>
</ul>
</Collapse>
<div>
{' '}
<PlaySound child={this.state.child} />{' '}
</div>
</Navbar>
);
}
}
First of all remove you css (images) into a seperate css file. Make a class and put your css into that. Something like this
<NavLink tag={Link} d="navResume" className="text-dark inactive resumeImage" to="/resume">
Resume
</NavLink>;
in your css file
.resumeImage {
//declare your image styles here
}
Repeat above for all your Navlinks.
Now keep those styles which you only want to apply to the active link in a seperate class and apply that class to all the Navlinks like below
<NavLink
tag={Link}
d="navResume"
className="text-dark inactive resumeImage"
activeClassName="activestyleclass"
to="/resume"
>
Resume
</NavLink>;
You can check the current URL and change the image based on the url since active class should be depend on the url too.
You can use react hook useLocation to get current URL.
import { useLocation } from 'react-router-dom';
const Component= () => {
const [pathname, setPathname] = useState('/');
const location = useLocation();
useEffect(() => {
setPathname(location.pathname);
}, [location]);
return (
<nav className="navbar">
<NavLink to="/" className="navbar-item">
{pathname === '/' ? (
<img src="/images/navbar/home_active.png" alt="Home" />
) : (
<img src="/images/navbar/home.png" alt="Home" />
)}
</NavLink>
.....

Nav not collapsing when a NavItem is selected on mobile devices

I'm building a navbar that is expected to be responsive when it switches to mobile devices. In other words, collapse when mobile, then when the navitems are selected / clicked, it should route to the path and collapse all navitems to a toggle-style bar. However, below a snapshot of what I got and it's associated code:
CODE:
render() {
return (
!this.state.isAuthenticating &&
<div className="App">
<Navbar fluid collapseOnSelect>
<Navbar.Header>
<Navbar.Brand>
<LinkContainer to="/">
<a>
<img src={logo} alt="logo" height="70" width="75"/>
<p>hybriData</p>
</a>
</LinkContainer>
</Navbar.Brand>
<Navbar.Toggle />
</Navbar.Header>
<Navbar.Collapse>
<Nav pullRight>
{this.state.isAuthenticated
? <Fragment>
<LinkContainer to="/settings">
<NavItem>Settings</NavItem>
</LinkContainer>
<NavItem eventKey={1} onClick={this.handleLogout}>Logout</NavItem>
</Fragment>
: <Fragment>
<LinkContainer to="/About Us">
<NavItem eventKey={2}>About Us</NavItem>
</LinkContainer>
<LinkContainer to="/Contact">
<NavItem eventKey={3}>Contact</NavItem>
</LinkContainer>
<LinkContainer to="/works">
<NavItem eventKey={5}>Works</NavItem>
</LinkContainer>
<LinkContainer to="/signin">
<NavItem eventKey={6}>SignIn</NavItem>
</LinkContainer>
</Fragment>
}
</Nav>
</Navbar.Collapse>
</Navbar>
</div>
);
}
I also integrated some of this solution react-bootstrap how to collapse menu when item is selected using the expanded and onSelect props of the navbar and nav's activeKey and onSelect props to achieve desired functionality but to no fruition. How can I implement desired functionality of having all navitem's collapsed when any path(s) is navigated to , clicked / selected? Thank you
Here is a simplified working example. It's about the order of items. If you look at the bootstrap CSS, you see some styles like
.nav > li {
position: relative;
display: block;
}}
The > symbol is a selector that selects all immediate descendants or children (first 'wrap layer' only, not applied to second 'wrap layer'), namely if you have
<ul class="nav">
<div>
<li>something</li>
<li>something</li>
</div>
</ul>
the styles are not applied, while
<ul class="nav">
<li>something</li>
<li>something</li>
</ul>
works. In your case, try replacing the <Fragment> with <Nav> and remove the outer <Nav>.
Another part you may want to modify is the LinkContainer wrapping the NavItem part. Usually we have <li> (NavItem) wrapping <a> (Link) but not the other way round.
Although react-bootstrap makes the code easier to read, it abstracts the details and may be harder to get something working. If you have difficulty with react-bootstrap, I suggest you to stick with pure bootstrap elements. The codes are slightly longer but the appearance is the same. You can follow w3schools to use bootstrap styles.

Remove Active Class from LinkContainer

In my react app I have buttons for logging in and out. Logging out isn't a big deal because it's never made active. When using LinkContainer (which is great) it will add an active class of active when that route is selected. This is great for everything except buttons. When it adds the `active class to my button it adds a padded background color behind the button. I don't want that. Heres the code and image now:
And here's the code:
import { Glyphicon, Nav, NavItem, Button } from 'react-bootstrap';
import { LinkContainer } from 'react-router-bootstrap';
<Nav>
<LinkContainer to={'/'} exact>
<NavItem>
<Glyphicon glyph='home' /> Home
</NavItem>
</LinkContainer>
<LinkContainer to={'/documents'}>
<NavItem>
<Glyphicon glyph='education' /> User Documents
</NavItem>
</LinkContainer>
<LinkContainer to={'/login'}>
<NavItem>
<Button bsStyle="primary" block>
<span className='glyphicon glyphicon-log-in'></span>Login
</Button>
</NavItem>
</LinkContainer>
</Nav>
I want it to set that active class on everything other than my buttons.
Using react-bootstrap and react-router-bootstrap
After looking through the code on react-router-bootstrap and a bunch of different articles and posts I figured it out.
I had to change:
<LinkContainer to={'/login'}>
<NavItem>
<Button bsStyle="primary" block>
<span className='glyphicon glyphicon-log-in'></span>Login
</Button>
</NavItem>
</LinkContainer>
To:
<LinkContainer to={'/login'} activeClassName="">
<NavItem>
<Button bsStyle="primary" block>
<span className='glyphicon glyphicon-log-in'></span>Login
</Button>
</NavItem>
</LinkContainer>
Making the activeClassName="" worked as it would make sure there was no active class being set.
Extremely helpful for buttons. I hope this saves people time. Here's what it looked like after for comparison:

Resources