ReactStrap NavLinks are broken when rendered via a component - reactjs

The following code renders my NavLinks properly in my ReactStrap DropdownMenu:
<!-- RENDERS CORRECTLY -->
<Nav className="ml-auto" navbar>
<UncontrolledDropdown nav inNavbar>
<DropdownToggle nav caret> A dropdown menu </DropdownToggle>
<DropdownMenu>
<NavLink className="dropdown-item" to=“/url1”> item 1 </NavLink>
<NavLink className="dropdown-item" to=“/url2”> item 2 </NavLink>
</DropdownMenu>
</UncontrolledDropdown>
</Nav>
But if I move the DropdownMenu into a separate component as follows, returning exactly the same JSX, the css is screwy and the resulting a elements have "to" attributes instead of "href" attributes, so the links don't work.
<!-- BREAKS -->
<Nav className="ml-auto" navbar>
<UncontrolledDropdown nav inNavbar>
<DropdownToggle nav caret> A dropdown menu </DropdownToggle>
<DropdownMenuComponent/>
</UncontrolledDropdown>
</Nav>
...
class DropdownMenuComponent extends Component {
render() {
return (
<DropdownMenu>
<NavLink className="dropdown-item" to=“/url1”> item 1 </NavLink>
<NavLink className="dropdown-item" to=“/url2”> item 2 </NavLink>
</DropdownMenu>
);
}
}
Any ideas how I can fix this? It's disconcerting to use ReactStrap if I can't count on basic nesting of components.

You have to wrap the links in the <DropdownItem> component and then they'll render correctly.
I put together a working sandbox here
import React from "react";
import ReactDOM from "react-dom";
import {
Nav,
UncontrolledDropdown,
DropdownToggle,
DropdownMenu,
DropdownItem,
NavLink
} from "reactstrap";
import "bootstrap-css-only";
const DropdownComponent = () => (
<DropdownMenu>
<DropdownItem>
<NavLink className="dropdown-item" to="/url1">
item 1
</NavLink>
</DropdownItem>
<DropdownItem>
<NavLink className="dropdown-item" to="/url2">
item 2
</NavLink>
</DropdownItem>
</DropdownMenu>
);
const App = () => {
return (
<Nav className="ml-auto" navbar>
<UncontrolledDropdown nav inNavbar>
<DropdownToggle nav caret>
A dropdown menu
</DropdownToggle>
<DropdownComponent />
</UncontrolledDropdown>
</Nav>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

I've used ReactStrap before, and from my experience, you often have to use Bootstrap classes in place of components if you start getting wonky styling attributes.
Also, in your second example when the code breaks, your Nav component isn't going to render to the DOM since it's not being returned inside of the render() life cycle method. You're going to need to place it inside the existing DropdownMenuComponent render method or create a new component for the Nav and import it into the parent component in order for it to render properly.
Hope this helps!

Related

react router on how to collapse the navbar on mobile when selecting the menu links

Any idea on how to collapse the navbar on mobile when selecting the menu link pages.
here is the codesandbox link, feel free to fork it.
https://codesandbox.io/s/it6lj
Thanks!
I ran into this issue using Reactstrap 9.0.0-2 (based on Bootstrap 5.1.0 css library). I'm not sure if it was a problem with earlier (more stable) releases. Already had an idea in mind of how to get the menu to close on NavLink select, but wanted to see how other people solved the problem for some new ideas and experiences.
I tried the first accepted answer, adding the data-attributes and data-target
on the NavLinks, and pointing at a css #id attribute on the NavbarToggler. This didn't work for me, unfortunately.
Instead, I solved the issue by setting up a click handler for the <NavLink /> tags, independent of the handler that toggles the menu. Unlike the other handler that opens and closes the menu, the NavLink handler only closes it by directly setting the menu's state object to false.
The logic goes: the menu has to be open in order to click on the <NavLink />, so this NavLink handler does not need to open the menu. And once you've made your choice about where you'd like to go, and have clicked on a Navlink tag, you'd probably like the menu to close and get out of your way, rather than having to close it yourself manually.
Simplified example:
import React, { useState } from 'react';
import { Collapse, Nav, NavBar, NarbarBrand, NavbarToggler, NavItem } from 'reactstrap';
import { NavLink } from 'react-router-dom';
const Header = (props) => {
const [menuOpen, setMenuOpen] = useState(false); // initially closed
const toggleMenu = () => { // this handler is "regular"
setMenuOpen(!menuOpen); // open and close...
};
const closeMenu = () => { // ... and this one only
setMenuOpen(false); // closes it ...
};
return (
<NavBar expand="md">
<Collapse isOpen={menuOpen} navbar>
<Nav navbar>
<NavLink
to="/blog/:article"
className="navlink text-light shadow-lg"
onClick={closeMenu}
>
{blogArticle.title}
</NavLink>
// . . . and so on . . .
Sometimes the simplest solutions are also the best...
Add data-toggle="collapse" and data-target="#navbarCollapse" to each NavLink to also toggle closed the menu.
<ul className="navbar-nav ml-auto">
<li className="nav-item">
<NavLink
data-toggle="collapse"
data-target="#navbarCollapse"
className="nav-link"
activeClassName="active"
to="/"
exact
>
Home
</NavLink>
</li>
<li className="nav-item">
<NavLink
data-toggle="collapse"
data-target="#navbarCollapse"
className="nav-link"
activeClassName="active"
to="/about"
>
About
</NavLink>
</li>
you can manage the menu visibility using a state:
import { useState } from "react";
import { NavLink } from "react-router-dom";
const Navbar = () => {
const [show, setShow] = useState(false);
const handleNavClick = () => {
setShow(false);
};
return (
<nav className="navbar navbar-expand-md navbar-dark bg-dark">
<a className="navbar-brand" href="/">
Fixed navbar
</a>
<button
className="navbar-toggler"
type="button"
onClick={() => setShow(!show)}
>
<span className="navbar-toggler-icon" />
</button>
<div
className={`collapse navbar-collapse ${show ? "show" : ""}`}
id="navbarCollapse"
>
<ul className="navbar-nav ml-auto">
<li className="nav-item">
<NavLink
onClick={handleNavClick}
className="nav-link"
activeClassName="active"
to="/"
exact
>
Home
</NavLink>
</li>
<li className="nav-item">
<NavLink
onClick={handleNavClick}
className="nav-link"
activeClassName="active"
to="/about"
>
About
</NavLink>
</li>
</ul>
</div>
</nav>
);
};
export default Navbar;
You can use useRef hook for this and handle the class attribute of the div.
const collapseRef = useRef(null);
const hideBars = () => {
collapseRef.current.setAttribute("class", "navbar-collapse collapse");
};
<div
className="collapse navbar-collapse"
id="navbarCollapse"
ref={collapseRef}
>
and then in your NavLink
<NavLink
className="nav-link"
activeClassName="active"
to="/about"
onClick={hideBars}
>
About
</NavLink>
Check this sandbox
if some still have this issue in bootsrap 5 just add these props to the li tag like this:
<li className="nav-item" data-bs-toggle='collapse' data-bs-target='.navbar-collapse.show'>
<Link className="nav-link" to="/">
<img src={homepage_icon} className='nav-item-icon' />
<h6>Page d'accueil</h6>
</Link>
and you end up by collapsing navbar

react-scroll | How to scroll to a specific targeted component when clicking on Navbar Link

I am making a single scroller page using React and want to navigate to a specific section of the page. In HTML we use an href and anchor tag to achieve this interaction.
I found a React library called react-scroll but I do not know how to link each component in different folders from the a link in the NavBar component. My NavBar has multiple links for scrolling to a section/ component. Any help would really be appreciated!
import React, { Component } from "react";
import { Link, animateScroll as scroll } from "react-scroll";
class Navbar extends Component {
render() {
return (
<nav className="navbar navbar-expand-lg navbar-dark">
<Link className="navbar-brand" to="/">
CMD <span>Custom Movie Database</span>
</Link>
<button
className="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarNav"
aria-controls="navbarNav"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span className="navbar-toggler-icon" />
</button>
<div className="collapse navbar-collapse" id="navbarNav">
<ul className="navbar-nav">
<li className="nav-item ">
<Link
className="nav-link"
to="/"
spy={true}
smooth={true}
offset={-70}
duration={500}
>
Home
</Link>
</li>
<li className="nav-item">
<Link
className="nav-link"
to="/"
spy={true}
smooth={true}
offset={-70}
duration={500}
>
Search
</Link>
</li>
<li className="nav-item">
<Link
className="nav-link"
to="/"
spy={true}
smooth={true}
offset={-70}
duration={500}
>
Category
</Link>
</li>
<li className="nav-item">
<Link
className="nav-link"
to="/"
spy={true}
smooth={true}
offset={-70}
duration={500}
>
Popular
</Link>
</li>
<li className="nav-item">
<Link
className="nav-link"
to="/"
spy={true}
smooth={true}
offset={-70}
duration={500}
>
Trailer
</Link>
</li>
<li className="nav-item">
<Link
className="nav-link"
to="/"
spy={true}
smooth={true}
offset={-70}
duration={500}
>
Article
</Link>
</li>
<li className="nav-item">
<Link
className="nav-link"
to="/"
spy={true}
smooth={true}
offset={-70}
duration={500}
>
Contact
</Link>
</li>
</ul>
</div>
</nav>
);
}
}
export default Navbar;
This is Home Component where all component is added
class Home extends React.Component {
render() {
return (
<React.Fragment>
<Navbar />
<Showcase />
<FormWrapper />
<CategoryList />
<MovieGrid />
<MovieTrailer />
<ArticleGrid />
<Footer />
</React.Fragment>
);
}
}
react-scroll is a great library - let me try and explain how I understand it.
Wherever I need a Link that scrolls to a certain element, I import that link, define it, and render it:
import React, { Component } from 'react'
import Scroll from 'react-scroll'
const ScrollLink = Scroll.ScrollLink
class Navbar extends Component {
render() {
return (
<nav>
<ScrollLink
to="example-destination"
spy={true}
smooth={true}
duration={500}
className='some-class'
activeClass='some-active-class'
>
Link Text Goes Here
</ScrollLink>
</nav>
)
}
export default Navbar;
In a separate file, we define the destination that the `Link` will scroll to. The `Element` import from react-scroll makes this pretty easy!
import React, { Component } from 'react'
import { Element } from 'react-scroll'
export default function () {
return (
<React.Fragment>
<Element id='example-destination' name='example-destination'>
// wrap your content in the Element from react-scroll
</Element>
</React.Fragment>
)
}
Making sense? Let me know if I can expand on this further!
Other Alternative:
Put an id in component that you want to go:
<div id='some-id'>
</div>
After that, call from any place:
const anchor = document.querySelector('#some-id')
anchor.scrollIntoView({ behavior: 'smooth', block: 'center' })
Just building upon Diego Baranowski's solution. Made a onClick event listener for an element by clicking which your view is being scrolled down to the anchor element. Works just fine. I am a newbie so hopefully it does not defy React concept.
<div
style={{"cursor":"pointer"}}
onClick={() => {
const anchor = document.querySelector('#reviews-link')
anchor.scrollIntoView({ behavior: 'smooth', block: 'center' })
}}
>
<Rating
rating={rating}
numReviews={numReviews}
></Rating>
</div>
A more React-friendly solution would be to get a reference to the element by using a "ref" (with useRef if it is a function component), instead of trying to access the DOM directly with document.querySelector.

The best way to create a drop-down menu with React components?

I have tried to make a drop-down menu without libraries but I do not find much information about it, they know some good library to create the menu, or in what way I could create it directly with the component states
Use reactstrap but I can not find a way to just click on it with the tabulator and enter I can access the link at http: // localhost: 3000 / landing
this is the code of my navigation bar
import React from 'react';
import { Fade, Flip, Rotate, Zoom, Bounce, Stepper } from 'react-reveal';
import Headroom from 'react-headrooms';
import { Accounts } from 'meteor/accounts-base';
import {Button } from 'reactstrap';
import { ButtonDropdown, DropdownToggle, DropdownMenu, DropdownItem, NavLink, Link, NavItem } from 'reactstrap';
export default class NavbarBoots extends React.Component {
constructor(){
super();
this.toogle = this.toogle.bind(this);
this.state={dropdownMenu:false}
}
toogle() {
this.setState({dropdownMenu:!this.state.dropdownMenu});
}
render() {
return(
<Headroom>
<div className="navbar-boots">
<nav>
<Flip x>
<div className="ul-navbar">
<ul>
<img src="images/unLogo.png" size="mini"
style={{width:'50',height:'50'}} />
<li><a className="titulo-boots"id="titulo"><span>T</span>itulo</a></li>
<ButtonDropdown isOpen={this.state.dropdownOpen} toggle={this.toggle}>
<DropdownToggle caret>
Portafolio
</DropdownToggle>
<DropdownMenu className='dropdown-menu'>
<DropdownItem tag={Link} to="/landing" classname='dropdown-item'>ACERCA DE MI</DropdownItem>
<DropdownItem href="#" classname='dropdown-item'><a>PROYECTOS</a></DropdownItem>
<DropdownItem href="http://localhost:3000/landing" classname='dropdown-item' active>LINKS</DropdownItem>
<DropdownItem classname='dropdown-item' > LINKS</DropdownItem>
</DropdownMenu>
</ButtonDropdown>
<button id="btn"className="btn"onClick={() => Accounts.logout()}>Logout</button>
</ul>
</div>
</Flip>
</nav>
</div>
</Headroom>
); // return
};
}

How can I add a link within a DropdownItem with reactstrap?

How can I add a link within a DropdownItem with reactstrap?
I would like to add a link within a dropdown menu, but how can I add it because in the reactstrap documentation I could not find anything related.
import React from 'react';
import { Fade, Flip, Rotate, Zoom, Bounce, Stepper } from 'react-reveal';
import Headroom from 'react-headrooms';
import { Accounts } from 'meteor/accounts-base';
import {Button } from 'reactstrap';
import { ButtonDropdown, DropdownToggle, DropdownMenu, DropdownItem, NavLink, Link, NavItem } from 'reactstrap';
export default class NavbarBoots extends React.Component {
constructor(){
super();
this.toogle = this.toogle.bind(this);
this.state={dropdownMenu:false}
}
toogle() {
this.setState({dropdownMenu:!this.state.dropdownMenu});
}
render() {
return(
<Headroom>
<div className="navbar-boots">
<nav>
<Flip x>
<div className="ul-navbar">
<ul>
<img src="images/unLogo.png" size="mini"
style={{width:'50',height:'50'}} />
<li><a className="titulo-boots"id="titulo"><span>T</span>itulo</a></li>
<ButtonDropdown isOpen={this.state.dropdownOpen} toggle={this.toggle}>
<DropdownToggle caret>
Portafolio
</DropdownToggle>
<DropdownMenu className='dropdown-menu'>
<DropdownItem tag={Link} to="/landing" classname='dropdown-item'>ACERCA DE MI</DropdownItem>
<DropdownItem href="#" classname='dropdown-item'><a>PROYECTOS</a></DropdownItem>
<DropdownItem href="http://localhost:3000/vitae" classname='dropdown-item' active>LINKS</DropdownItem>
</DropdownMenu>
</ButtonDropdown>
<button id="btn"className="btn"onClick={() => Accounts.logout()}>Logout</button>
</ul>
</div>
</Flip>
</nav>
</div>
</Headroom>
); // return
};
}
it is displayed in this way but I can not add a link
Incase anyone else is looking for this, here's the proper straightforward solution.
<DropdownItem tag={Link} to="/me">text here</DropdownItem>
Or if it is meant to be a standard link then,
<DropdownItem tag={a} href="/me">text here</DropdownItem>
Source
if you use react-bootstrap instead of reactstrap an come across same issue you need to:
import { Link } from 'react-router-dom';
<Dropdown.Item as={Link} to="/me">text here</Dropdown.Item>
2020 Updated
Looking over these answers suggest Link should come from reactstrap, yet that doesn't export a Link component.
Link should come from react-router-dom.
import React from "react";
import { Link } from "react-router-dom";
import {
ButtonDropdown,
DropdownToggle,
DropdownMenu,
DropdownItem
} from "reactstrap";
// ...
<ButtonDropdown isOpen={dropdownOpen} toggle={toggle}>
<DropdownToggle caret>Actions</DropdownToggle>
<DropdownMenu>
<DropdownItem tag={Link} to={`/action`}>Action</DropdownItem>
</DropdownMenu>
</ButtonDropdown>
Make sure you have react-router-bootstrap installed. LinkContainer is the component that will make the link clickable. It must be placed outside of DropdownItem for it to work in Firefox. Also, adding className="collapse" to Collapse component will hide the menu initially in Firefox.
npm install react-router-bootstrap --save
Pre-requisites:
npm install --save bootstrap#4.0.0
npm install --save reactstrap#next
npm install --save jquery#1.9.1
npm install --save react-transition-group
npm install --save react-popper
import { LinkContainer } from 'react-router-bootstrap';
import { Button, ButtonGroup, NavDropdown, Collapse, Navbar,
NavbarToggler, NavbarBrand, Nav, NavItem, NavLink,
Dropdown, DropdownMenu, DropdownToggle, DropdownItem, UncontrolledDropdown } from 'reactstrap';
class MyComponent extends Component{
constructor(props) {
super(props);
this.toggleNavbar = this.toggleNavbar.bind(this);
this.state = {
isOpen: false
};
}
toggleNavbar() {
this.setState({
isOpen: !this.state.isOpen
});
}
render(){
return (
<div>
<Navbar color="faded" light expand="md">
<NavbarBrand href="/">
<img src={logo} alt="Logo" />
<h2 className="header-title">My Site</h2>
</NavbarBrand>
<NavbarToggler onClick={this.toggleNavbar} />
<Collapse isOpen={this.state.isOpen} navbar className="collapse">
<Nav className="ml-auto" navbar pullRight>
<NavItem><LinkContainer to="/admin"><NavLink>Home</NavLink></LinkContainer></NavItem>
<UncontrolledDropdown nav inNavbar>
<DropdownToggle nav caret>
Link 1
</DropdownToggle>
<DropdownMenu >
<LinkContainer to="/sub-link1">
<DropdownItem>Sub Link 1</DropdownItem>
</LinkContainer>
</DropdownMenu>
</UncontrolledDropdown>
<LinkContainer to="/logout">
<NavItem><NavLink>Logout</NavLink></NavItem>
</LinkContainer>
</Nav>
</Collapse>
</Navbar>
</div>
)
}
}
export default MyComponent;
<DropdownMenu>
<DropdownItem tag="a" href="/yourpage">YourLink</DropdownItem>
<DropdownMenu>
source: https://reactstrap.github.io/components/dropdowns/
One more option if you ise react router:
import { Link } from 'react-router-dom';
<DropdownMenu className="dropdown__menu">
<Link to={`somewhere`}><DropdownItem>Edit</DropdownItem></Link>
</DropdownMenu>
Had this same issue. Tried originally using withRouter and adding an onClick property which called history.push(newRoute), but just learned of a simpler way:
const DropdownItemLink = props => {
return <DropdownItem tag={Link} {...props}>{props.title}</DropdownItem>;
};
return (
<div className="ActionsDropdown">
<Dropdown isOpen={this.state.dropdownOpen} toggle={this.toggle}>
<DropdownToggle>Actions</DropdownToggle>
<DropdownMenu>
{[
DropdownItemLink({
title: 'title1',
to: 'path1',
}),
DropdownItemLink({
title: 'title2',
to: 'path2',
}),
...
]}
</DropdownMenu>
</Dropdown>
</div>
);
Need to import Link from 'react-router-dom' library and obviously all the dropdown components from 'reactstrap' library. And also need to properly manage this.state.dropdownOpen and this.toggle according to reactstrap documentation.
Can you add anchor tag to DropdownItem like this?
<DropdownItem classname='dropdown-item' > <a href="http://localhost:3000/vitae" target="_blank"> LINKS</DropdownItem>
I was using react-router Link for few months inside DropdownItem until i realized it didnt worked in firefox !.. It worked fine in chrome.. looks like the right way is to use the onClick prop ...
<DropdownItem id={e.id} key={e.id} onClick={this.changeValue}>{e.name}</DropdownItem>
The reactstrap documentation is poor.
Examine the src for supported props and render logic
This will render as <a>
You use that syntax in your example so not sure why it doesn't work as DropdownItem hasn't been changed since before you posted.
<DropdownItem href="/link">A link</DropdownItem>
In my case, I have a nested DropDownMenu inside another DropDownMenu.
Add toggle={false} to DropDownMenuItem and override CSS events solved my problem
JSX:
<DropdownItem
toggle={false}
className='dropdown-item-inactive'>
<UnitsFormat
disabled={props.isLoading}
unitsFormat={props.unitsFormat}
onChange={props.onUnitFormatChanged} />
</DropdownItem>
CSS:
.dropdown-item-inactive:active {
color: inherit!important;
background-color: #ffffff!important;
}
You are using reactstrap. so this is the best option. in this option, you can set react-router link tag.
<Button tag={Link} color="primary" to="{{url}}">know more</Button>

How to Remove Active Background in React-Bootstrap

How can I remove the active Nav Item background feature in react-bootstrap when selecting Nav Item? It acting abnormally when I use it with 'react-redux' and 'react-router-bootstrap'?
For instance, when I reload the home page, the active background only stays on one of the Nav Items even if I select other Nav Items. Below is the image of the navitem (Dashboard) item is selected and the code for the navbar! Any recommendations would be greatly appreciate!
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {LinkContainer, IndexLinkContainer} from 'react-router-bootstrap';
import {
Nav,
NavItem,
Navbar
} from 'react-bootstrap';
class NavBar extends Component {
render() {
const {authenticated} = this.props;
return (
<Navbar>
<Navbar.Header>
<Navbar.Brand>
Timesheet App
</Navbar.Brand>
<Navbar.Toggle/>
</Navbar.Header>
<Navbar.Collapse>
<Nav>
<IndexLinkContainer to="/">
<NavItem className="nav-link" eventKey={1}>Dashboard</NavItem>
</IndexLinkContainer>
<LinkContainer to="/timesheet/new">
<NavItem className="nav-link" eventKey={2}>Submit Time</NavItem>
</LinkContainer>
<LinkContainer to="/user/Andriy">
<NavItem className="nav-link" eventKey={3}>Andriy Time</NavItem>
</LinkContainer>
{authenticated &&
<LinkContainer to="/signout">
<NavItem className="nav-link" eventKey={4}>Sign Out</NavItem>
</LinkContainer>}
{!authenticated &&
<LinkContainer to="/signin">
<NavItem className="nav-link" eventKey={5}>Sign In</NavItem>
</LinkContainer>}
{!authenticated &&
<LinkContainer to="/signup">
<NavItem className="nav-link">Sign Up</NavItem>
</LinkContainer>}
</Nav>
</Navbar.Collapse>
</Navbar>
);
}
}
NavBar.propTypes = {
authenticated: PropTypes.bool
};
function mapStateToProps(state) {
return {
authenticated: state.auth.authenticated
};
}
export default connect(mapStateToProps)(NavBar);
If you want the active background gone, this can just be a CSS-only fix.
Just override the CSS for the active state background/border/etc to be the same as the non-active state.
Hope this helps.
You should create a class as a variable and use classNames to changed it based on whatever logic you decide to use.
You can then define something in CSS to deal with remove the active background.
For example (taken from the docs):
var btnClass = 'btn';
if (this.state.isPressed) btnClass += ' btn-pressed';
else if (this.state.isHovered) btnClass += ' btn-over';
return <button className={btnClass}>{this.props.label}</button>;
You can use different states to dictate what classes should be added and removed. This also helps with DRY principles as it will prevent you having to repeat yourself across all of the Navbar.
If you set activeKey="", then none of the links are active, and it's only the active key which gets a background.
<Nav activeKey="">
...
</Nav>

Resources