disabling navlink react router - reactjs

I am using react router in and I want to disable the to attribute in a certain state. I passed empty string, but that doesn't disable the link instead it takes to the base route of the page. I even tried to pass null but that breaks the code. Is it even possible to do so?
<NavLink className="nav-link" to={this.state.role == 4 ? "/pages" : ""}></NavLink>

You could try disabling the button with a custom click handler.
handleClick = (e) => {
const { linkDisabled } = this.state
if(linkDisabled) e.preventDefault()
}
render() {
return (
<NavLink
onClick={this.handleClick}
className="nav-link"
to="/pages"
>
...
</NavLink>
)
}
You might want to add some css for when the button is disabled
Alternatively you could just not show the button at all
{
this.state.linkDisabled ?
null :
<NavLink className="nav-link" to="/pages"></NavLink>
}

I used the same as Stretch0 , but i just change to functional component
only my confirmation is disabled
my styled links is the same of NavLink :
NavBar --> index.js
export default function NavBar() {
const handleClick = (e) => {
e.preventDefault()
}
return (
<Body>
<Header>
<Nav>
<StyledLink exact activeClassName="current" to="/">
<MenuLinks>CART</MenuLinks>
</StyledLink>
<StyledLink activeClassName="current" to="/payment">
<MenuLinks>PAYMENT</MenuLinks>
</StyledLink>
<StyledLink activeClassName="current" onClick={handleClick} to="/confirmation">
<MenuLinks>CONFIRMATION</MenuLinks>
</StyledLink>
</Nav>
</Header>
</Body>
)
}
NavBar ---> Styles.js
export const StyledLink = styled(NavLink)`
text-decoration: none;
color: #d6d6d6;
display: flex;
cursor: pointer;
${(props) => props.disabled && `
cursor: default;`}
&.${(props) => props.activeClassName} {
color: #fe8d3b;
}
&:focus,
&:hover,
&:visited,
&:link,
&:active {
text-decoration: none;
}
`

{
this.state.role !== 4 ?
:
<NavLink className="nav-link" to="/pages"></NavLink>
}

Another option would be to create your custom link wrapper component and to render the NavLink or not conditionally. In the following example, the property active determines if the link will be rendered or simply the text of the link.
function HeaderLink(props) {
if(props.active) {
return <NavLink {...props}>{props.children}</NavLink>
}
return <div className='link-disabled'>{props.children}</div>
}
Usage with e.g. state dependency within a navigation element:
<ul className='main-navigation'>
<li><HeaderLink to='/'>Personal</HeaderLink></li>
<li><HeaderLink to='/contact' active={state.personalDataComplete}>Contact</HeaderLink></li>
<li><HeaderLink to='/signup' active={state.contactDataComplete}>Sigup</HeaderLink></li>
</ul>

Related

Scrolling the page up in React

In my component, i wanna scroll the page up when the user click on Link Component witch will change the url and therefore what user sees.
import { Link } from "react-router-dom";
const RelatedMovieItem = props => {
const movie = props.movie;
return (
<div className="card" style={{width: "18rem",marginTop: "10px", padding: 0, marginLeft: "10px"}}>
<img src={movie.imageUrl} className="card-img-top" alt={movie.name} />
<div className="card-body">
<h5 className="card-title">{movie.name}</h5>
<p className="card-text">{movie.description}</p>
<Link to={`/movies/${movie.id}`} className="btn btn-primary">Go to details</Link> //once this clicked i want to scroll the page up
</div>
</div>
);
};
export default RelatedMovieItem;
How to do so?
if every route changes you want to scroll top you can do it using <ScrollToTop> passing children down the tree like that.
<BrowserRouter>
<ScrollToTop>
<Switch>
// your route
</Switch>
</ScrollToTop>
</BrowserRouter>;
And create component ScrollToTop.js
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function ScrollToTop({children}) {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return <>{children}</>;
}
You can make use of window.scrollTo(0,0) by passing it to the onClick attribute.
This way your page will scroll to the top left whenever you click on <Link />.
<Link
to={`/movies/${movie.id}`}
className="btn btn-primary"
onClick={() => window.scrollTo(0, 0)}
>
Go to details
</Link>
you can use a reference and attach it to the component you want to scroll to.
Then onclick you can call ref.current.scrollIntoView()

Encapsulate a NavLink component and style it with Styled Component

I am trying to create a menu with hyperlinks from the react-rounter-dom library component NavLink
and style it with styled component
I Created a component called Link which is where NavLink is so you don't repeat this same line of code multiple times, then pass this component to styled component to inherit its properties so you can apply styles to it.
but the styles are not being applied to my component
NavLink
import { NavLink as NavLinkReactRouterDom } from "react-router-dom";
const Link = function ({ to, children, ...props }) {
return (
<>
<NavLinkReactRouterDom
{...props}
className={({ isActive }) =>
// console.log(isActive)
isActive ? "is-active" : undefined
}
to={to}
>
{children}
</NavLinkReactRouterDom>
</>
);
};
export default Link;
sidebarStyled.js (css)
import styled from "styled-components";
import Link from "./NavLink";
// NavLink
export const Prueba = styled(Link)`
color: white;
font-size: 50px;
text-decoration: none;
display: flex;
justify-content: flex-start;
align-items: stretch;
flex-direction: row;
&.is-active {
color: green;
}
`
Sidebar
const Sidebar = function () {
return (
<SidebarContainer>
<LogoContainer>
<img src={Logo} alt="Logo" />
</LogoContainer>
<h1>Sidebe Here</h1>
<Divider />
<Menu>
<NavList>
<NavItem>
<Prueba to="/">
<LinkIcon icon="typcn:home-outline" />
Inicio
</Prueba>
</NavItem>
</NavList>
</Menu>
</SidebarContainer>
);
};
export default Sidebar;
Issue
The className prop of the styled component, Prueba isn't passed through to the component it's attempting to style, the NavLinkReactRouterDom component. Or rather, it is passed implicitly when the props are spread into it, but NavLinkReactRouterDom is overriding and setting it's own className prop.
const Link = function ({ to, children, ...props }) {
return (
<>
<NavLinkReactRouterDom
{...props} // <-- styled-component className pass here
className={({ isActive }) => // <-- overridden here!
// console.log(isActive)
isActive ? "is-active" : undefined
}
to={to}
>
{children}
</NavLinkReactRouterDom>
</>
);
};
Solution
The solution is to merge the styled-component's className prop with the active classname used for the NavLinkReactRouterDom component.
Example:
const Link = function ({ to, children, className, ...props }) {
return (
<NavLinkReactRouterDom
{...props}
className={({ isActive }) =>
[className, isActive ? "is-active" : null].filter(Boolean).join(" ")
}
to={to}
>
{children}
</NavLinkReactRouterDom>
);
};
you may need to use Prueba, instead of Link. since you inherit the Link component, applied custom CSS and store it in the variable named Prueba.
hence import it in the sidebar.js file and use it there
refer: https://codesandbox.io/s/objective-smoke-1bflsh?file=/src/App.js
add
import 'Prueba' from sidebarStyled.js'
change
....
<Prueba to="/">
<LinkIcon icon="typcn:home-outline" />
Inicio
</Prueba>
....

React Logout Redirect From Nav Component

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}/>
</>
)}
/>

menuLinks.map is not a function

I am trying to call a request with graphiQL but something is wrong with it. Is anybody can help me? I am getting this error:
TypeError: menuLinks.map is not a function
It is working on gatsby/react
import React from "react"
import { graphql, StaticQuery, Link } from "gatsby"
import { Menu } from "antd"
import headerStyles from "./headerStyles.module.scss"
class Header extends React.Component {
render() {
const menuLinks = props.data.site.siteMetadata
return (
<StaticQuery
query={graphql`
query SiteTitleQuery {
site {
siteMetadata {
menuLinks {
name
link
}
}
}
}
`}
render={data => (
<div data={data} className={headerStyles.menu}>
<Link to="/">
<img
style={{ width: "30px", float: "left" }}
/>
</Link>
<Menu mode="horizontal" breakpoint="lg" collapsedWidth="0">
{menuLinks.map(link => (
<Menu.Item key={link.name}>
<Link to={link.link}>{link.name}</Link>
</Menu.Item>
))}
</Menu>
</div>
)}
/>
)
}
}
export default Header
const menuLinks = this.props.data.site.siteMetadata
instead of
const menuLinks = props.data.site.siteMetadata
How to access props in react you can check this props
In Class based Component
this.props.data
In function based component
props.data
You should use this.props.data.site.siteMetadata instead of props.data.site.siteMetadata because you are using Class component.
And things can go wrong in these cases. You have to check whether data, site, and siteMetadata existed either in the componentDidMount or render. Most likely, in the first render your props.data.site.siteMetadata might not exist.
Change your mapping to this code.
{
this.props.data
&& this.props.data.site
&& this.props.site.siteMetadata
&& this.props.site.siteMetadata.length > 0
? this.props.site.siteMetadata.map(each => <MenuItem />)
: <Loading />
}

Can I add active class when url starts a string in react Navlink?

How do I add an active class in react navlink when url starts with a "/teacher/create/" string?
<NavLink to={"/teacher/create/"} exact activeClassName={this.props.location.pathname.startsWith("/teacher/create/") && "activeLink"}>{title}</NavLink>
and add css:
.activeLink {
color: red;
}
<NavLink to={"/teacher/create/2"} exact activeClassName={this.props.location.pathname === "/teacher/create/2" && "activeLink"}>{title}</NavLink>
and add css:
.activeLink {
color: red;
}
react-router-dom: "^4.1.2"
I don't know how the other solutions above work but in my case I had no access to this.props.location. You can do this by trying something like that:
export const LooseNavLink = props => (
<NavLink {...props} isActive={(match, location) => location.pathname.startsWith(props.to.pathname)}/>
)

Resources