I'm working on a site I've build with Gatsby.js, and reactstrap for the ui components. But I have an problem with the collapsed navbar on smaller screens.
I need to collapse the navigation after each click to make it work with gatsby-link, but the collapse is not collapsed after a new page is loaded.
So I added a closeNavbar method to all links via onClick, but for whatever reasons this does not work.
Here is my navigation component:
import React from "react";
import Link from "gatsby-link";
import {
Collapse,
Container,
Navbar,
NavbarToggler,
Nav,
NavItem
} from "reactstrap";
export default class Navigation extends React.Component {
constructor(props) {
super(props);
this.toggleNavbar = this.toggleNavbar.bind(this);
this.closeNavbar = this.closeNavbar.bind(this);
this.state = {
collapsed: true
};
}
toggleNavbar() {
this.setState({
collapsed: !this.state.collapsed
});
}
closeNavbar() {
if (this.state.collapsed == true) {
this.toggleNavbar();
}
}
render() {
return (
<header>
<Navbar className="navigation__navbar" light expand="md">
<Container>
<NavbarToggler onClick={this.toggleNavbar} className="mr-2" />
<Collapse isOpen={!this.state.collapsed} navbar>
<Nav className="mr-auto" navbar>
<NavItem>
<Link className="nav-link navigation__navlinks" to="/">
Home
</Link>
</NavItem>
<NavItem>
<Link
onClick={this.closeNavbar}
className="nav-link navigation__navlinks"
to="/leistungen/"
>
Leistungen
</Link>
</NavItem>
<NavItem>
<Link
onClick={this.closeNavbar}
className="nav-link navigation__navlinks"
to="/kuechen/"
>
Küchen
</Link>
</NavItem>
</Nav>
</Collapse>
</Container>
</Navbar>
</header>
);
}
}
I had an error in the closeNavbar method. I tried to toggle it, only when it was already collapsed. This is the correct code:
closeNavbar() {
if (this.state.collapsed !== true) {
this.toggleNavbar();
}
}
Related
I have used the code below to have navbar with reactstrap but it shows in 2 rows and not inline.
I have installed react strap and imported the CSS in app.js
import React, { Component } from 'react';
import {Collapse,Navbar,NavbarToggler,NavbarBrand,Nav,NavLink,NavItem,Container} from 'reactstrap';
state = {
isOpen: false
}
toggle = () => {
this.setState({
isOpen: !this.state.isOpen
})
}
render(){
return (
<Navbar color="dark" dark expand="sm" className="mb-5">
<Container>
<NavbarBrand href="/">ShoppingList</NavbarBrand>
<NavbarToggler onClick={this.toggle}></NavbarToggler>
<Collapse isOpen={this.state.isOpen} navbar>
<Nav className="ml-auto" navbar>
<NavItem>
<NavLink href="https://www.google.com">google</NavLink>
</NavItem>
</Nav>
</Collapse>
</Container>
</Navbar>
)
}
}
export default Appnavbar;
I'm having trouble with my Actions in Redux with my NextJS app.. I'd imagine it would be relatively similar to implementing Redux in any other React application but I can't for the life of me get my actions to fire off. It has been awhile since I've touched Redux so it might be something really obvious that I'm missing but I've spent several hours trying to troubleshoot it. When my window width gets to 600px I have a breakpoint that shows a hamburger icon and I want it to add the class "open" to my navbar. Here's my repo. https://github.com/nicer00ster/nicer00ster-blog
add an eventListener to handle window resize and then check the innerWidth of window to call your action.
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { toggleMenu } from '../../actions'
import Sidebar from './Sidebar';
import Nicer00ster from '-!svg-react-loader?name=Nicer00ster!../../images/svg/nav/nicer00ster.svg';
import Smartphone from '-!svg-react-loader?name=Smartphone!../../images/svg/nav/smartphone.svg';
import House from '-!svg-react-loader?name=House!../../images/svg/nav/house.svg';
import Telephone from '-!svg-react-loader?name=Telephone!../../images/svg/nav/telephone.svg';
class Navbar exntends React.Component {
constructor() {
super();
this.onWindowResize = this.onWindowResize.bind(this);
}
componentDidMount() {
window.addEventListener('resize', onWindowResize);
}
onWinodwResize() {
if (window.innerWidth < 600 && !this.props.isOpen)
this.props.toggleMenu();
}
render() {
const {isOpen, toggleMenu} = this.props;
return (
<div>
<div className={ isOpen ? "navbar open" : "navbar" }>
<ul className="navbar__links">
<li className="navbar__item">
<Link to="/">
<Nicer00ster className="logo" width={200} height={100}/>
</Link>
<Sidebar />
</li>
</ul>
<ul className="navbar__navigation">
<li className="navbar__item" onClick={ toggleMenu }>
<Link activeClassName="active" to="/">
<a className="navbar__link">
<House className="navbar__item--svg" width={100} height={50} />
<span className="navbar__item--text">Home</span>
</a>
</Link>
</li>
<li className="navbar__item" onClick={ toggleMenu }>
<Link activeClassName="active" to="/blog">
<a className="navbar__link">
<Telephone className="navbar__item--svg" width={100} height={50} />
<span className="navbar__item--text">Blog</span>
</a>
</Link>
</li>
<li className="navbar__item" onClick={ toggleMenu }>
<Link activeClassName="active" to="/work">
<a className="navbar__link">
<Smartphone className="navbar__item--svg" width={100} height={50} />
<span className="navbar__item--text">Work</span>
</a>
</Link>
</li>
<li className="navbar__item" onClick={ toggleMenu }>
<Link activeClassName="active" to="/contact">
<a className="navbar__link">
<Telephone className="navbar__item--svg" width={100} height={50} />
<span className="navbar__item--text">Contact</span>
</a>
</Link>
</li>
</ul>
</div>
<Sidebar isOpen={isOpen} />
</div>
)
}
export default connect(function(state) {
return { isOpen: state.app.open }
}, { toggleMenu })(Navbar);
I recently tried implementing a navlink like so:
import React from 'react';
import { Link, NavLink } from 'react-router-dom';
import '../css/navbar.css';
import logoWhite from '../../common/images';
const Navbar = props => {
return (
<div id="navbar" className="grid">
<Link to="/" className="column logo-container">
<img src={logoWhite} alt="Test logo" />
</Link>
<ul className="column navigation">
<li>
<NavLink to="/" exact className="nav-link" activeClassName="active">Home</NavLink>
</li>
<li>
<NavLink to="/match" className="nav-link" activeClassName="active">Partner Proposals</NavLink>
</li>
</ul>
</div>
);
};
export default Navbar;
Upon running I encountered the issue of the classname not being passed to the navlink when the route changed. Reading on articles and it led me to suspect that the shouldComponentUpdate method was being rendered false. So, I rewrote the Navbar like so:
import React from 'react';
import { Link, NavLink } from 'react-router-dom';
import '../css/navbar.css';
import logoWhite from '../../common/images';
class Navbar extends React.Component {
shouldComponentUpdate() {
console.log('Console log activated');
return true;
}
render() {
return (
<div id="navbar" className="grid">
<Link to="/" className="column logo-container">
<img src={logoWhite} alt="Test image" />
</Link>
<ul className="column navigation">
<li>
<NavLink to="/" exact activeClassName="active" className="nav-link">Home</NavLink>
</li>
<li>
<NavLink to="/match" activeClassName="active" className="nav-link">Partner Proposals</NavLink>
</li>
</ul>
</div>
);
}
}
export default Navbar;
What's strange is that the componentDidUpdate returned the value in the console (suggesting that it returned true), but the NavLink was still not passing the activeClassName
Can anyone suggest a solution to this problem?
Based on the suggestion from #mindaJalaj, I passed a pathname prop (which was rendering and re-rendering whenever there was a update to the route from the parent component), and I implemented the Navbar as follows
import React from 'react';
import PropTypes from 'prop-types';
import { Link, NavLink } from 'react-router-dom';
import { Icon } from 'semantic-ui-react';
import '../css/navbar.css';
import logo from '../../common/images/logo.png';
import navLinks from '../navLinks';
const Navbar = props => {
function renderNavLinks() {
return navLinks.map((navLink, index) => {
const key: string = `nav-link-${navLink.path}-${index}`;
const exact: boolean = navLink.exact || false;
return (
<li key={key}>
<NavLink
to={navLink.path}
exact={exact}
className="nav-link"
isActive={() => navLink.path === props.pathname}
activeClassName="active"
>
<Icon name={navLink.icon} />
<span>{navLink.title}</span>
</NavLink>
</li>
);
});
}
return (
<div id="navbar" className="grid">
<Link to="/" className="column logo-container">
<img src={logo} alt="Test logo" />
</Link>
<ul className="column navigation">{renderNavLinks()}</ul>
</div>
);
};
Navbar.propTypes = {
pathname: PropTypes.string
};
Navbar.defaultProps = {
pathname: '/'
};
export default Navbar;
Is there any way to setState({collapse: true}) for mobile screens only? How can i toggle the this.state.collapse based on current window size?
import React, { Component } from 'react';
import { Route, Redirect } from 'react-router-dom';
import $ from 'jquery';
import { Container, Row, Col, Collapse, Navbar, NavbarToggler, NavbarBrand, Nav, NavItem, NavLink } from 'reactstrap';
import { css } from 'glamor';
import { ToastContainer } from 'react-toastify';
import toast from '../toast';
import { BarLoader } from 'react-spinners';
// ---------------- Custom components
import DashboardNavbar from '../DashboardPage/Dashboard/DashboardNavbar/DashboardNavbar';
import Footer from '../Footer/Footer';
import './VideoPage.css';
class VideoPage extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
collapsed: false
};
this.toggleLoader = this.toggleLoader.bind(this);
this.notifySuccess = this.notifySuccess.bind(this);
this.notifyError = this.notifyError.bind(this);
this.toggleNavbar = this.toggleNavbar.bind(this);
}
notifySuccess(msg) {
toast.success(msg);
}
notifyError(msg) {
toast.error(msg);
}
toggleLoader() {
this.setState({
loading: !this.state.loading
});
}
// isAuthenticated() {
// const token = localStorage.getItem('authToken');
// if (token) {
// return true;
// }
// }
toggleNavbar() {
this.setState({
collapsed: !this.state.collapsed
});
}
render() {
const currentLocationPath = this.props.location.pathname;
const videoPage = currentLocationPath.includes('/video');
return (
<div className="VideoPage d-flex flex-column flex-grow">
<div className="VideoPageMain d-flex flex-grow">
<Container fluid>
<Row>
<DashboardNavbar videoPage={videoPage} />
</Row>
<Row>
<Col xs="12" sm="3">
<div className="sidebarMenu">
<Navbar dark>
<NavbarBrand className="mr-auto">Menu</NavbarBrand>
<NavbarToggler onClick={this.toggleNavbar} className="mr-2 d-sm-none" />
<Collapse isOpen={!this.state.collapsed} navbar>
<Nav navbar>
<NavItem>
<NavLink href="/components/">Components</NavLink>
</NavItem>
<NavItem>
<NavLink href="https://github.com/reactstrap/reactstrap">Github</NavLink>
</NavItem>
</Nav>
</Collapse>
</Navbar>
</div>
</Col>
<Col xs="12" sm="9">.col</Col>
</Row>
</Container>
</div>
<Footer />
</div>
)
}
}
export default VideoPage;
basically i want the list to be hidden on mobile as there is button to toggle it which is hidden from tablet size and onwards.
It looks like there's a library for that: https://github.com/contra/react-responsive
Otherwise, you could add a listener to the resize event of window and fire that listener in the constructor to check the size.
You have 2 options:
1st option
Toggle classNames and let your CSS handles showing/hiding on different viewports
2nd option
use window.innerWidth in your isCollapsed
<Collapse isOpen={!this.state.collapsed && window.innerWidth < 768} navbar>
768 is just as an example
Not too familiar with react router, but I need the functionality of the NavLink to set the active class on the parent li element, and not the a element.
To implement this I just looked at the source code of the NavLink and copied it to a new element. (Example using typescript, but just about the same as js anyway)
import * as React from 'react';
import { Link, withRouter, Route } from 'react-router-dom';
class LiNavLink extends React.Component<any, {}> {
render() {
const {to,exact, strict, activeClassName, className, activeStyle, style, isActive: getIsActive, ...rest } = this.props;
return (
<Route
path={typeof to === 'object' ? to.pathname : to}
exact={exact}
strict={strict}
children={({ location, match }) => {
const isActive = !!(getIsActive ? getIsActive(match, location) : match)
return (
<li
className={isActive ? [activeClassName, className].join(' ') : className}
style={isActive ? { ...style, ...activeStyle } : style}>
<Link
to={to}
{...rest}
/>
</li>
)
}}
/>
);
}
}
export default LiNavLink;
Then the usage:
<ul>
<LiNavLink activeClassName='active' exact={true} strict to="/example"><span>Active</span></LiNavLink>
<LiNavLink activeClassName='active' exact={true} strict to="/example/archived"><span>Archived</span></LiNavLink>
</ul>
I'm using a HashRouter and for some reason which I can't figure out, this does not update when the route changes, only when I hard 'refresh' the page does it update how it should.
I believe it is never updating because the props never change? So it doesn't know to update itself?
How can I get this to update? Or is my problem somewhere else?
In v4 after lots of tries I did.
Here my working code.
import React, { Component } from "react";
import logo from "../../logo.svg";
import { Link, withRouter } from "react-router-dom";
import PropTypes from "prop-types";
class Navbar extends Component {
static propTypes = {
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
};
state = {};
getNavLinkClass = path => {
return this.props.location.pathname === path
? "nav-item active"
: "nav-item";
};
render() {
return (
<nav className="navbar navbar-expand-lg navbar-dark bg-dark">
<Link className="navbar-brand" to="/">
<img
src={logo}
width="30"
height="30"
className="d-inline-block align-top"
alt=""
/>
Utility
</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={this.getNavLinkClass("/")}>
<Link className="nav-link" to="/">
Home
</Link>
</li>
<li className={this.getNavLinkClass("/age-counter")}>
<Link className="nav-link" to="/age-counter">
Age Counter
</Link>
</li>
</ul>
</div>
</nav>
);
}
}
export default withRouter(Navbar);
Demo working Code Sandbox
Check this one
class LiNavLink extends React.Component<NavLinkProps> {
render() {
return (
<Route exact={this.props.exact} path={this.props.to.toString()}>
{
({ match }) =>
<li className={match ? 'active' : undefined}>
<Link to={this.props.to} >
{this.props.children}
</Link>
</li>
}
</Route>
);
}
}
I am just starting with the react, so not sure if this is the best practices, but after going through router v4 docs, I used withRouter props -> location.pathname and compared it to my route.
Here is the Navigation.js:
import React from 'react';
import { withRouter } from 'react-router-dom';
import NavLink from '../General/NavLink';
const activeClass = (path, link) => {
if (path === link) {
return true;
}
return false;
};
const Navigation = props => {
const { location } = props;
return (
<ul className="menu menu--main nano-content">
<NavLink
to="/"
parentClass={
activeClass(location.pathname, '/')
? 'menu__item menu__item--active'
: 'menu__item'
}
linkClass="menu__link effect effect--waves"
>
Dashboard
</NavLink>
<NavLink
to="/users"
parentClass={
activeClass(location.pathname, '/users')
? 'menu__item menu__item--active'
: 'menu__item'
}
linkClass="menu__link effect effect--waves"
>
Users
</NavLink>
<NavLink
to="/projects"
parentClass={
activeClass(location.pathname, '/projects')
? 'menu__item menu__item--active'
: 'menu__item'
}
linkClass="menu__link effect effect--waves"
>
Projects
</NavLink>
<NavLink
href="http://google.com"
parentClass="menu__item"
linkClass="menu__link effect effect--waves"
>
Google
</NavLink>
</ul>
);
};
export default withRouter(Navigation);
From there you have parent and child classes that you can use on child component.
I found that by using CSS you can make the active link expand to fill up it's parent <li> element by setting display:block; in the active class.
For example if our link was:
<li>
<NavLink to="/overview" className=styles.sideLink activeClassName=styles.sideLinkSelected>
Overview
</NavLink>
</li>
then our CSS would be:
&__sideLinkSelected
{
background-color: blue;
display:block;
}