I have my navigation component here:
import 'navigations/NavMenu.scss';
import React, { Component } from 'react';
import { Collapse, Container, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
import { Link } from 'react-router-dom';
import { useHistory } from 'react-router-dom';
import { Util } from 'helpers/Util';
export class NavMenu extends Component {
static displayName = NavMenu.name;
constructor(props) {
super(props);
this.toggleNavbar = this.toggleNavbar.bind(this);
this.closeNavbar = this.closeNavbar.bind(this);
this.logout = this.logout.bind(this);
this.state = {
collapsed: true
};
}
toggleNavbar() {
this.setState({
collapsed: !this.state.collapsed
});
}
closeNavbar() {
if (!this.state.collapsed)
this.setState({ collapsed: true });
}
async logout() {
const history = useHistory();
let success = await Util.logout();
if (success) {
history.push('/login?msg=' + encodeURI('Success! You have been logged-out.') + '&type=success');
}
this.closeNavbar();
}
render() {
let theme = localStorage.getItem('theme');
let navbarClass = "navbar navbar-expand-sm navbar-toggleable-sm ng-white border-bottom box-shadow mb-3";
let login = Util.isUserLoggedIn() ?
<NavLink onClick={this.logout} tag={Link} className={theme == 'dark' ? 'text-light' : 'text-dark'} to="/login">Logout</NavLink> :
<NavLink onClick={this.closeNavbar} tag={Link} className={theme == 'dark' ? 'text-light' : 'text-dark'} to="/login">Login</NavLink>
return (
<header>
<nav className={theme == 'dark' ? navbarClass + ' navbar-dark' : navbarClass + 'navbar-light'}>
<Container>
<NavbarBrand tag={Link} to="/">NetCoreReact</NavbarBrand>
<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 flex-grow">
<NavItem>
<NavLink onClick={this.closeNavbar} tag={Link} className={theme == 'dark' ? 'text-light' : 'text-dark'} to="/">Home</NavLink>
</NavItem>
<NavItem>
<NavLink onClick={this.closeNavbar} tag={Link} className={theme == 'dark' ? 'text-light' : 'text-dark'} to="/counter">Counter</NavLink>
</NavItem>
<NavItem>
<NavLink onClick={this.closeNavbar} tag={Link} className={theme == 'dark' ? 'text-light' : 'text-dark'} to="/fetch-data">Fetch data</NavLink>
</NavItem>
<NavItem>
{login}
</NavItem>
</ul>
</Collapse>
</Container>
</nav>
</header>
);
}
}
I want to logout() by pressing logout navigation link. But the useHistory() messes me up here. This is the error:
Unhandled Rejection (Error): Invalid hook call. Hooks can only be
called inside of the body of a function component. This could happen
for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app See .... for tips about how to debug and
fix this problem.
When this code is called:
history.push('/login?msg=' + encodeURI('Success! You have been logged-out.') + '&type=success');
How can I safely navigate?
This is not a direct solution however just want to share this approach.
I was facing similar kind of issue in approaching a login and logout. I followed this approach and it works for me. I have used 'react-bootstrap' unlike 'reactstrap' in your code
ReactJS Bootstrap Navbar and Routing not working together
and you can use Redirect from 'react-router-dom'
import { Redirect, Route, Switch, Link } from "react-router-dom"
and use the Redirect in render method of the logout component like I did for Login component (I am redirecting to home based on a state of the component),
<Route path='/login'
exact
render = {()=>(this.state.bLoggedIn ? <Redirect to="/home" /> :
<>
<Login usr={this.getActiveUser}/>
</>
)}
/>
I am trying to create a Login Modal Form for an application. However, I want the popup to appear when I click a link versus a button. In other words, when I click the login link in my navbar, I don't want to be redirected to another page entirely. I just want the modal to pop up.
I'm very new to ReactJS, so I'm not sure how to go about this. Could somebody please help me understand how to get this function to work? I'd really appreciate it.
Additionally, if anyone knows of some great resources on how to implement a proper login form, I would also greatly appreciate that. I found a few on CodePen, but none of them really show a clear and approachable way on how to build this component. At least, for a beginner like me.
Located below is my code. Also, if it helps, I provided the link to the site I am currently using as a reference to build this code.
Resource: https://react-bootstrap.github.io/components/modal/
App.js
import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom'
import Navbar from './components/Navbar/navbar.js';
import Footer from './components/Footer/footer.js';
import Home from './pages/Home/home.js';
import Login from './pages/Login/login.js';
import Languages from './pages/Languages/languages.js';
function App() {
return (
<div className="App">
<BrowserRouter>
<Navbar/>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/login" component={Login}/>
<Route path="/languages" component={Languages}/>
</Switch>
</BrowserRouter>
<Footer />
</div>
);
}
export default App;
Navbar.js
import React from 'react';
import { Link } from 'react-router-dom';
import './navbar.css';
const Navbar = () => {
return (
<nav className="navbar navbar-expand-sm navbar-dark px-sm-5">
<div className="container">
<Link to='/'>
<div className="navbar-brand">
<i class="fas fa-globe fa-2x"></i>
</div>
</Link>
<ul className="navbar-nav align-items-right">
<li className="nav-item ml-5">
<Link to="/login" className="nav-link">
Log In
</Link>
</li>
<li className="nav-item ml-5">
<Link to="/signup" className="nav-link">
Sign Up
</Link>
</li>
</ul>
</div>
</nav>
)
}
export default Navbar;
Login.js
import React, { Component } from 'react';
// import { connect } from 'react-redux';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
import './login.css';
class Login extends Component {
constructor(props, context) {
super(props, context);
this.handleShow = this.handleShow.bind(this);
this.handleClose = this.handleClose.bind(this);
this.state = {
show: false,
};
}
handleClose() {
this.setState({ show: false });
}
handleShow() {
this.setState({ show: true });
}
render() {
return (
<>
<Button variant="primary" onClick={this.handleShow}>
Launch demo modal
</Button>
<Modal show={this.state.show} onHide={this.handleClose}>
<Modal.Header closeButton>
<Modal.Title>Login</Modal.Title>
</Modal.Header>
<Modal.Body>...</Modal.Body>
<Modal.Footer>
<Button variant="danger" onClick={this.handleClose}>
Cancel
</Button>
</Modal.Footer>
</Modal>
</>
);
}
}
export default Login;
Let's refactor your Navbar to be a class-component instead. We're going to need to keep track of state and pass down a binded function to the Login modal.
Additonally, it looks like you won't need a Login page anymore, so let's extract that markup so that its in a component instead. We'll call it LoginModal
Navbar.js
import React from "react"
import { Link } from 'react-router-dom';
import './navbar.css';
import LoginModal from "./components/LoginModal"
class Navbar extends React.Component{
state = {
modalOpen: false
}
handleModalOpen = () => {
this.setState((prevState) => {
return{
modalOpen: !prevState.modalOpen
}
})
}
render(){
return (
<div>
<nav className="navbar navbar-expand-sm navbar-dark px-sm-5">
<div className="container">
<Link to='/'>
<div className="navbar-brand">
<i class="fas fa-globe fa-2x"></i>
</div>
</Link>
<ul className="navbar-nav align-items-right">
<li className="nav-item ml-5">
<a onClick={this.handleModalOpen} className="nav-link">
Log In
</a>
</li>
<li className="nav-item ml-5">
<a onClick={this.handleModalOpen} className="nav-link">
Sign Up
</a>
</li>
</ul>
</div>
</nav>
<LoginModal
modalOpen={this.state.modalOpen}
handleModalOpen={this.handleModalOpen}
/>
</div>
)
}
}
export default Navbar;
Notes about Navbar:
It has a component state that keeps track of the status of the modal.
The modal is placed right at the end of nav jsx.
Replaced the Link components with standard a-tags and gave them an
onClick handler
The onClick handler, handleModalOpen toggles a value in our state
called openModal.
openModal and handleModalOpen gets passed down to the LoginModal
component.
So now let's refactor Login to be LoginModal.
LoginModal
import React from 'react';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
import './login.css';
const LoginModal = (props) => {
return (
<>
<Modal show={props.modalOpen} onHide={props.handleModalOpen}>
<Modal.Header closeButton>
<Modal.Title>Login</Modal.Title>
</Modal.Header>
<Modal.Body>...</Modal.Body>
<Modal.Footer>
<Button variant="danger" onClick={props.handleModalOpen}>
Cancel
</Button>
</Modal.Footer>
</Modal>
</>
);
}
export default LoginModal;
Notes about LoginModal
We were able to remove a lot of the original logic now that
LoginModal is strictly just responsible for consuming props and
displaying content.
We use the prop value, props.modalOpen which is passed down from
Navbar, it gets set to true when the button is clicked inside the
Navbar component. So show={true} will display the modal
Similarly, we use another prop, props.handleModalOpen which toggles
the state in the parent component. When you call that function in the modal, it updates state.modalOpen in the parent to false.
That updated value gets passed back down to LoginModal, setting
props.modalOpen to false, so show={false} thus closing the modal.
Lastly App.js can now just be:
App.js
import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom'
import Navbar from './components/Navbar/navbar.js';
import Footer from './components/Footer/footer.js';
import Home from './pages/Home/home.js';
import Languages from './pages/Languages/languages.js';
function App() {
return (
<div className="App">
<BrowserRouter>
<Navbar/>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/languages" component={Languages}/>
</Switch>
</BrowserRouter>
<Footer />
</div>
);
}
export default App;
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?
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>
I am very new to react and have been using react-storybook to build components to teach myself react UI. I am also trying to learn react-bootstrap which is recommended for the react Facebook boilerplate.
I am getting an error when I try to load a component that uses react-bootstrap
here is the error:
Nav is not defined
ReferenceError: Nav is not defined
at eval (webpack:///./src/stories/index.js?:114:9)
at renderMain (webpack:///./~/#kadira/storybook/dist/client/preview/render.js?:108:17)
at renderPreview (webpack:///./~/#kadira/storybook/dist/client/preview/render.js?:141:12)
at Array.renderUI (webpack:///./~/#kadira/storybook/dist/client/preview/index.js?:89:26)
at Object.dispatch (webpack:///./~/redux/lib/createStore.js?:186:19)
at ConfigApi._renderMain (webpack:///./~/#kadira/storybook/dist/client/preview/config_api.js?:48:24)
at render (webpack:///./~/#kadira/storybook/dist/client/preview/config_api.js?:66:17)
at ConfigApi.configure (webpack:///./~/#kadira/storybook/dist/client/preview/config_api.js?:91:9)
at Object.eval (webpack:///./.storybook/config.js?:9:26)
at eval (webpack:///./.storybook/config.js?:10:30)
Here is my config.js
import { configure } from '#kadira/storybook';
function loadStories() {
require('../src/stories');
}
configure(loadStories, module);
Here is the navbar component called MenuHeader
import React from 'react';
import Navbar from 'react-bootstrap/lib/Navbar';
const Mainbar = ({}) => (
<Navbar inverse collapseOnSelect>
<Navbar.Header>
<Navbar.Brand>
mainBar
</Navbar.Brand>
<Navbar.Toggle />
</Navbar.Header>
<Navbar.Collapse>
<Nav>
<NavItem eventKey={1} href="#">Link1</NavItem>
<NavItem eventKey={2} href="#">Link2</NavItem>
</Nav>
</Navbar.Collapse>
</Navbar>
);
export default Mainbar;
Here is the index.js
import React from 'react';
import { storiesOf, action, linkTo } from '#kadira/storybook';
import Button from './Button';
import Welcome from './Welcome';
import Mainbar from './MenuHeader';
storiesOf('Welcome', module)
.add('to Storybook', () => (
<Welcome showApp={linkTo('Button')}/>
));
storiesOf('Button', module)
.add('with text', () => (
<Button onClick={action('clicked')}>Hello Button</Button>
))
.add('with some emoji', () => (
<Button onClick={action('clicked')}>😀 😎 👍 💯</Button>
));
storiesOf('Mainbar', module)
.add('Test Navbar',() => ( <Navbar inverse collapseOnSelect>
<Navbar.Header>
<Navbar.Brand>
mainBar
</Navbar.Brand>
<Navbar.Toggle />
</Navbar.Header>
<Navbar.Collapse>
<Nav>
<NavItem eventKey={1} href="#">Link1</NavItem>
<NavItem eventKey={2} href="#">Link2</NavItem>
</Nav>
</Navbar.Collapse>
</Navbar>));
Any help would greatly be appreciated
That's because in your MenuHeader Component, Nav and NavItem components are not defined.
You can import it as.
import React from 'react';
import Navbar from 'react-bootstrap/lib/Navbar';
import Nav from 'react-bootstrap/lib/Nav';
import NavItem from 'react-bootstrap/lib/NavItem';
const Mainbar = ({}) => (
Do check the import code as i haven't used react-bootstrap