React - fixed navbar and nav-tab - reactjs

I am using reactstrap package in for my project. So, I have created a HeaderComponent which is a Navbar which is fixed at top fixed=top.
import React from 'react';
import Context from '../provider'
import {
Collapse,
Navbar, NavbarToggler, NavbarBrand, Nav, NavItem, NavLink,
Form, FormGroup, Label, Input, Button,
} from 'reactstrap';
import LoginComponent from './LoginComponent'
import SignupComponent from './SignupComponent'
class HeaderComponent extends React.Component {
render() {
return (
<Context.Consumer>
{context => {
return (
<Navbar color="light" light expand="md" fixed="top">
<NavbarBrand href="/">Reddit</NavbarBrand>
<NavbarToggler onClick={() => context.toggleNavbar()} />
<Collapse isOpen={context.navbarOpen} navbar>
<Nav className="ml-auto" navbar>
<NavItem><LoginComponent /></NavItem>
<NavItem><SignupComponent /></NavItem>
</Nav>
</Collapse>
</Navbar>
)
}}
</Context.Consumer>
)
}
}
export default HeaderComponent;
I also have a TabComponent:
import React, {Component} from 'react'
import Context from '../../provider'
import {Nav, NavItem, NavLink} from 'reactstrap'
import classnames from 'classnames'
class TabComponent extends Component {
render() {
return (
<Context.Consumer>
{context => (
<Nav tabs>
<NavItem>
<NavLink
className={classnames({ active: context.activeTab === '1' })}
onClick={() => { context.toggleTab('1'); }}
>
Home
</NavLink>
</NavItem>
<NavItem>
<NavLink
className={classnames({ active: context.activeTab === '2' })}
onClick={() => { context.toggleTab('2'); }}
>
Popular
</NavLink>
</NavItem>
<NavItem>
<NavLink
className={classnames({ active: context.activeTab === '3' })}
onClick={() => { context.toggleTab('3'); }}
>
All
</NavLink>
</NavItem>
</Nav>
)}
</Context.Consumer>
)
}
}
export default TabComponent;
I am making the HeaderComponent fixed but I don't know how to make the TabComponent fixed below the HeaderComponent.
Based on this answer, I did the following:
TopComponent
import React, {Component} from 'react'
import HeaderComponent from './HeaderComponent'
import TabComponent from './TabComponent'
import {Row, Col, Container} from 'reactstrap'
import './style.css'
export default class TopComponent extends Component {
render() {
return (
<div className='vert-align'>
<Container>
<Row><Col><HeaderComponent /></Col></Row>
<Row><Col><TabComponent /></Col></Row>
</Container>
</div>
)
}
}
style.css
.vert-align {
top:0;
position: fixed;
z-index:100;
}
They are now fixed to the top one below the other but, they are not covering the complete width of screen now!

The easiest in my opinion would be to make a parent component FixedTopComponent for both HeaderComponent and TabComponent, and fix this one to the top fixed=top. then you just have to align the two children components vertically, using either bootstrap rows or {display: flex; flex-direction: column} in your css.
Here's an example:
class FixedTopComponent extends React.Component {
render() {
const vert_align = {
display: 'flex',
flexDirection: 'column'
}
<div id='fixed-top" style={vert_align} fixed='top'>
<HeaderComponent />
<TabComponent />
</div>
}
}
Edit: For your second issue (not sure cause I never used reactstrap), try:
import React, {Component} from 'react'
import HeaderComponent from './HeaderComponent'
import TabComponent from './TabComponent'
import {Row, Col, Container} from 'reactstrap'
import './style.css'
export default class TopComponent extends Component {
render() {
return (
<Container fluid className='vert-align'>
<Row><Col><HeaderComponent /></Col></Row>
<Row><Col><TabComponent /></Col></Row>
</Container>
)
}
}

Related

classname in Nav(bootstrap) not working, button not working

i have a problem. My button is not working. Also, no matter what I write in className, the buttons don't move. And even if user.IsAuth = true, its still only auth button in my window
import React, {useContext} from 'react';
import {Context} from "../index";
import Navbar from "react-bootstrap/Navbar";
import Nav from "react-bootstrap/Nav";
import {NavLink} from "react-router-dom";
import {SHOP_ROUTE} from "../utils/consts";
import {Button} from "react-bootstrap";
import {observer} from "mobx-react-lite";
const NavBar = observer(() => {
const {user} = useContext(Context)
console.log(user)
return (
<Navbar bg="dark" variant="dark">
<NavLink style={{color:'white'}} to={SHOP_ROUTE}> ShopUS </NavLink>
{user.IsAuth ?
<Nav className="mr-auto" style={{color:'white'}}>
<Button variant={'outline-light'}>Admin</Button>
<Button variant={'outline-light'} className="ml-2">Exit</Button>
</Nav>
:
<Nav className="mr-auto" style={{color:'white'}}>
<Button variant={'outline-light'} onClick={()=> user.setIsAuth(true)}>Auth</Button>
</Nav>
}
</Navbar>
)
});
export default NavBar;
And UserStore (if someone need it)
import {makeAutoObservable} from 'mobx'
export default class UserStore{
constructor() {
this._isAuth = true
this._user = {}
makeAutoObservable(this)
}
setIsAuth(bool) {
this._isAuth = bool
}
setUser(user) {
this._user = user
}
get isAuth() {
return this._isAuth
}
get user() {
return this._user
}
}

How to close sidebar when clicking link?

I am trying to get my sidebar to close when I click on any of the menu options. I was able to get the sidebar to close/open whenever I click on the burger icon, but not sure if I am supposed to make my sidebar component a class and have its own state. Below are my navigation and sidebar components.
import React from 'react';
import { Link } from 'react-router-dom';
import { ReactComponent as MenuIcon } from '../../assets/menu.svg';
import { ReactComponent as CloseIcon } from '../../assets/x-mark.svg';
import './navigation.styles.scss';
import Sidebar from '../sidebar/sidebar.component';
class Navigation extends React.Component {
constructor(props) {
super(props);
this.state = {
isSidebarHidden: true
};
this.handleSidebar = this.handleSidebar.bind(this);
}
handleSidebar() {
this.setState({ isSidebarHidden: !this.state.isSidebarHidden });
}
render() {
const { isSidebarHidden } = this.state;
return (
<div className='navigation'>
<div className='logo-container'>
<Link className='logo' to='/'>
NAME
</Link>
</div>
<div className='navigation-options'>
<Link className='option' to='/projects'>
PROJECTS
</Link>
<Link className='option' to='contact'>
CONTACT
</Link>
{isSidebarHidden ? (
<MenuIcon className='menu-icon' onClick={this.handleSidebar} />
) : (
<CloseIcon className='menu-icon' onClick={this.handleSidebar} />
)}
</div>
{isSidebarHidden ? null : <Sidebar />}
</div>
);
}
}
export default Navigation;
import React from 'react';
import { Link } from 'react-router-dom';
import './sidebar.styles.scss';
const Sidebar = () => (
<div className='sidebar'>
<Link className='sidebar-option' to='/projects'>
PROJECS
</Link>
<Link className='sidebar-option' to='/contact'>
CONTACT
</Link>
</div>
);
export default Sidebar;
You could create a method to hide the sidebar and pass it to the Sidebar component, so it executes when you click the links.
const Sidebar = ({hideSidebar}) => (
<div className='sidebar'>
<Link onClick={hideSidebar} className='sidebar-option' to='/projects'>
PROJECS
</Link>
<Link onClick={hideSidebar} className='sidebar-option' to='/contact'>
CONTACT
</Link>
</div>
);
Or you could also execute it every time you move to a different path listening to the browser history with react-router.
import { browserHistory } from 'react-router';
browserHistory.listen(handleRouteChange);
I suggest controlling the component with props instead of using if statement inside the parent component.
import React, { useEffect } from 'react';
import { Link } from 'react-router-dom';
import './sidebar.styles.scss';
const Sidebar = ({ visibility, setVisibility }) => {
if (visibility) {
return (
<div className='sidebar'>
<Link className='sidebar-option' to='/projects' onClick={() => setVisibility()}>
PROJECS
</Link>
<Link className='sidebar-option' to='/contact' onClick={() => setVisibility()}>
CONTACT
</Link>
</div>
)
}
return null
};
export default Sidebar;
As you see, I passed setVisibility prop to onClick callback on the sidebar links and checked if visibility is true then return the sidebar contents. So in this step, we just need to pass this.handleSidebar to setVisibility prop and the parent state isSidebarHidden to the visibility prop.
import React from 'react';
import { Link } from 'react-router-dom';
import { ReactComponent as MenuIcon } from '../../assets/menu.svg';
import { ReactComponent as CloseIcon } from '../../assets/x-mark.svg';
import './navigation.styles.scss';
import Sidebar from '../sidebar/sidebar.component';
class Navigation extends React.Component {
constructor(props) {
super(props);
this.state = { isSidebarHidden: true };
this.handleSidebar = this.handleSidebar.bind(this);
}
handleSidebar() {
this.setState({ isSidebarHidden: !this.state.isSidebarHidden });
}
render() {
const { isSidebarHidden } = this.state;
return (
<div className='navigation'>
<div className='logo-container'>
<Link className='logo' to='/'>
NAME
</Link>
</div>
<div className='navigation-options'>
<Link className='option' to='/projects'>
PROJECTS
</Link>
<Link className='option' to='contact'>
CONTACT
</Link>
{isSidebarHidden ? (
<MenuIcon className='menu-icon' onClick={this.handleSidebar} />
) : (
<CloseIcon className='menu-icon' onClick={this.handleSidebar} />
)}
</div>
<Sidebar visibility={isSidebarHidden} setVisibility={this.handleSidebar} />
</div>
);
}
}
export default Navigation;
Then it works.
For the people using bootstrap offcanvas as a sidebar there is a very easy way to do it using only bootstrap and with no JavaScript.
<li data-bs-dismiss="offcanvas">Skills</li>
The above code represent li as one of the item in the sidebar and on upon clicking it takes you to skill section and also closes as it is in dismiss state.

How to pass values in the <Link to = {}> and get them from another component using [react / typescript]

I would like to see how I can pass the id inside the <Link to = {}> using typescript. Then in the SeriesSection component get the id of the link.
SerieComponent.tsx
import React from 'react';
import { Link } from "react-router-dom";
import Serie from '../../interfaces/Serie/Serie';
type T = {
serie: Serie
};
const SerieComponent: React.FC<T> = ({ serie: { id, title, sinopsis, poster, rating, year, extra } }) => {
let channel = extra.map(x => { return x.channel });
return (
<Link to={}/>
<div className="post-2930 movie type-movie status-publish has-post-thumbnail hentry movie_genre-action">
<div className="movie__poster">
<br />
<a className="masvideos-LoopMovie-link masvideos-loop-movie__link movie__link">
<img width="300" height="450" src={poster} className="movie__poster--image" alt={title} />
<img width="300" height="450" src={poster} className="movie__poster--image" alt={title} sizes="(max-width: 300px) 100vw, 300px" />
</a>
</div>
<div className="movie__body">
<div className="movie__info">
<div className="movie__info--head">
<div className="movie__meta"><span className="movie__meta--release-year">{year}</span><span className="movie__meta--genre">{channel}</span></div>
<a className="masvideos-LoopMovie-link masvideos-loop-movie__link movie__link">
<h3 className="masvideos-loop-movie__title movie__title">{title}</h3>
</a>
</div>
<div className="movie__short-description">
<div>
<p>{sinopsis}</p>
</div>
</div>
</div>
</div>
</div>
</Link>
)
};
export default SerieComponent;
This component is where I want to get the id of the link.
import React from 'react';
const SerieSection: React.FC = () => {
return(
<div>
</div>
)
};
export default SerieSection;
If you notice I already referred to the route
<Route exact path="/serie-section/:id" component={SerieSection}></Route>
import React from 'react';
import { Switch, Route, Redirect, Link } from 'react-router-dom';
import { makeStyles } from '#material-ui/core/styles';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import Button from '#material-ui/core/Button';
import IconButton from '#material-ui/core/IconButton';
import MenuIcon from '#material-ui/icons/Menu';
import Home from '../Home/index';
import About from '../About/index';
import Footer from '../Footer/index';
import SerieSection from '../SerieSection/index';
import useStyles from './styles';
require('../../db/index.ts');
const App: React.FC = () => {
const classes = useStyles();
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<Button component={Link} to="/" color="inherit">Home</Button>
<Button component={Link} to="/about" color="inherit">About</Button>
</Toolbar>
</AppBar>
<div className="App">
<Switch>
<Route exact path="/" component={Home}></Route>
<Route exact path="/about" component={About}></Route>
<Route exact path="/serie-section/:id" component={SerieSection}></Route>
<Redirect to="/" />
</Switch>
<Footer/>
</div>
</div>
)
}
export default App;
<Link to={{
pathName: "/yourpathname",
someProp:{foo:"bar"}
}}>Your Path</Link>
and than in you component that this Link leads to
console.log("test: ", props.location.someProp)
If the second component is class dont forget this keyword
I had to refer to RouteComponentProps
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
interface State {
id: string;
}
interface Props extends RouteComponentProps<State>{
}
const SerieSection: React.FC<Props> = (props) => {
let id = props.match.params.id;
return(
<div>
<h1>{id}</h1>
</div>
)
};
export default SerieSection;

TypeError: undefined is not an object (evaluating 'this.props.location') [ReactJS + react-router-dom V4]

I'm developing a ReactJS app. For my routing setup, I use react-router-dom V4.
My problem is as follows: I'm trying to access "location.pathname" in a file. Nonetheless, I get the error:
TypeError: undefined is not an object (evaluating
'this.props.location')
Sample of side-menu_user-types.js:
import React from 'react';
import { Menu, Icon } from 'antd';
import { NavLink, withRouter } from 'react-router-dom';
const SubMenu = Menu.SubMenu;
//sidebar for each user type
const config = {
1: {
desc: [
<Menu theme="dark" mode="inline" defaultSelectedKeys={[this.props.location.pathname]}>
<Menu.Item key="/home">
<NavLink to="/home">
<Icon type="home"/>
<span>home</span>
</NavLink>
</Menu.Item>
</Menu>
],
},
2: {
desc: [
<Menu theme="dark" mode="inline" defaultSelectedKeys={[this.props.location.pathname]}>
<Menu.Item key="/home">
<NavLink to="/home">
<Icon type="home"/>
<span>home</span>
</NavLink>
</Menu.Item>
<Menu.Item key="/nav1">
<NavLink to="/nav1">
<Icon type="star-o"/>
<span>nav 1</span>
</NavLink>
</Menu.Item>
</Menu>
],
}
};
export default withRouter(config);
Sample of side-menu.js:
import React from 'react';
import { Layout } from 'antd';
import { connect } from 'react-redux';
const { Sider } = Layout;
class SideMenu extends React.Component {
render() {
return (
<Sider
className="sider"
trigger={null}
collapsible
collapsed={this.props.collapsed}
>
<div className="logo"/>
{this.props.sidebar}
</Sider>
);
}
}
const mapStatetoProps = state => ({
sidebar: state.user.sidebarLoggedIn,
});
export default connect(mapStatetoProps)(SideMenu);
PS: I retrieve the sidebar ("this.props.sidebar") in side-menu.js from side-menu_user-types.js, according to the user type (1 or 2), in an action as follows:
const sidebar = config[1].desc[0] => user type #1;
const sidebar = config[2].desc[0] => user type #2.
The const sidebar is then dispatched to my side-menu.js.
You cannot reference this.props.location from config directly as there's no this context. (No props context as well for that matter).
What you could do is pass the user type from redux store to the SideMenu props. Then use this to decide which kind of navbar you want to use.
It could be something like this.
import React from 'react';
import { Layout } from 'antd';
import { connect } from 'react-redux';
import { Menu, Icon } from 'antd';
import { NavLink, withRouter } from 'react-router-dom';
const { Sider } = Layout;
class SideMenu extends React.Component {
renderSideBar(props) {
if(props.userType === '1') {
return (
<Menu theme="dark" mode="inline" defaultSelectedKeys={[props.location.pathname]}>
<Menu.Item key="/home">
<NavLink to="/home">
<Icon type="home"/>
<span>home</span>
</NavLink>
</Menu.Item>
</Menu>
);
}
else if(props.userType === '2') {
return (
<Menu theme="dark" mode="inline" defaultSelectedKeys={[props.location.pathname]}>
<Menu.Item key="/home">
<NavLink to="/home">
<Icon type="home"/>
<span>home</span>
</NavLink>
</Menu.Item>
<Menu.Item key="/nav1">
<NavLink to="/nav1">
<Icon type="star-o"/>
<span>nav 1</span>
</NavLink>
</Menu.Item>
</Menu>
);
}
}
render() {
return (
<Sider
className="sider"
trigger={null}
collapsible
collapsed={this.props.collapsed}
>
<div className="logo"/>
{this.renderSideBar(this.props)}
</Sider>
);
}
}
const mapStatetoProps = state => ({
userType: //get user type from state
});
export default withRouter(connect(mapStatetoProps)(SideMenu));
It only makes sense to store the userType in redux-store rather than storing the whole render layout.

React setState with media queries

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

Resources