Prop to detect if link is active in react - reactjs

I have a NavLink which have child components
<NavLink
className={StyledAppNavItem}
activeClassName={StyledAppNavItemActive}
to={link.path}
>
<Icon size={28} icon={link.icon} color={isActive?'red':'blue'}/>
<StyledAppNavName>{link.name}</StyledAppNavName>
</NavLink>
Like activeClassName is attached when the link is active, I need to detect if this link is active and send a prop to its child component Icon
Somewhat like <Icon color={linkisActive?'red':'blue'}/>

Unfortunately, there is no way to get isActive from NavLink. You need to create your custom NavLink. Something like this:
function MyNavLink() {
return (
<Route path={link.path} children={({ match }) => (
<Link
className={`${StyledAppNavItem} ${match ? StyledAppNavItemActive : ''}`}
to={link.path}
>
<Icon size={28} icon={link.icon} color={match ? 'red' : 'blue'}/>
<StyledAppNavName>{link.name}</StyledAppNavName>
</Link>
)} />
);
}
Or you can just add Route to your current component:
<NavLink
className={StyledAppNavItem}
activeClassName={StyledAppNavItemActive}
to={link.path}
>
<Route path={link.path} children={({ match }) => (
<Icon size={28} icon={link.icon} color={match?'red':'blue'}/>
)} />
<StyledAppNavName>{link.name}</StyledAppNavName>
</NavLink>

Related

NavLink component stays active? [duplicate]

This question already has answers here:
React Router Dom v6 shows active for index as well as other subroutes
(2 answers)
Closed 5 months ago.
A navlink to homepage always stays active. Other 2 navlinks work correctly and only added an active style when chosen. Is there any problem with routes or function that changes the active link?
const activeStyles = {
color: 'green',
};
<ul className="flex gap-8">
<li>
<NavLink
to={"/"}
className="text-xs text-gray-400 hover:text-white"
style={({ isActive }) => (isActive ? activeStyles : undefined)}
>
Main
</NavLink>
</li>
<li>
<NavLink
to={"posts"}
className="text-xs text-gray-400 hover:text-white"
style={({ isActive }) => (isActive ? activeStyles : undefined)}
>
My Posts
</NavLink>
</li>
<li>
<NavLink
to={"new"}
className="text-xs text-gray-400 hover:text-white"
style={({ isActive }) => (isActive ? activeStyles : undefined)}
>
Add post
</NavLink>
</li>
</ul>
For react-router v6, if you want your root route ("/") to be active only at this route, you should add an end prop to <NavLink>:
<NavLink to="/" end>
Main
</NavLink>
For this you have to use exact key work in Navlink like this:
<NavLink to='/' exact={true}>
Main
</NavLink>
because'/' is active for all routes that's why you are getting this.

how to change url without adding another level of path in react router dom 5

i have an api "xyz.com/products/" and i have made an route xyz.com/products/1. This will give me an product with the id 1 . And it will render product page Component with that product details...But now on that particular product page i have an gallery of related product with their own individual "Link" to open in same product page . but when i click on any one of them ....the url changes like this "xyz.com/product/1/23" but i want only to change path like "xyz.com/products/1" --> "xyz.com/products/23"
here is my code snippets
// ///// route and link both are on App Component/////////////////
// this is the route that i have that matches xyz.com/product/1
<Route
path="/product/:id"
render={() => (
<Fragment>
<ProductContext.Provider value={{ productState, productStateDispatch }}>
<Product />
<div className={`overlay ${productState.packageSidebar}`}></div>
</ProductContext.Provider>
</Fragment>
)}
/>;
these are links will go on product page
<Row title="Our best seller">
{homeState.bestSellerProducts.map((product, index) => (
<Link
to={{
pathname: `/product/${product.id}`,
search: `type=${product.type}`,
}}
className="product-link"
key={index}
>
{" "}
<Card {...product} />
</Link>
))}
</Row>
//////////////////////////////
// this is in my product component where i have used one more component called imageGallery inside it. in which we have links////////////
{images.map((imgObj, index) => {
console.log(imgObj);
return (
<div className="item" key={index}>
<Link to={`/${imgObj.id}`} className="product-link">
<div className="img">
<img
height={100}
width={100}
src={imgObj.product_image}
alt="product-img"
/>
</div>
</Link>
</div>
);
})}
// i know i messing in routing i hope will get solution.... i just want that when i click on any link in this imageGallery component .it should be open inside same product page in which i m already present.. thats it
Including exact keyword in the Route component might solve your problem, try out!, demo example:
<Route exact to='/' component={home} />

animation routes with framer motion

Hi i try to animate 2 routes form opacity 1 to 0 on exit. But for some reason on <Link> click the routes will change(ex. '/' => '/chat?name=bob&room=here') but the page content stay the same and it wont render the component of that specific route. Also the animation is working great once i use the "Go back button" from browser.
Anyone have idea why is not working on button click?
App.js
return (
<div>
<Router>
<AnimatePresence exitBeforeEnter>
<Switch location={location} key={location.key}>
<Route path="/" exact component={Login}></Route>
<Route path="/chat" component={ChatRoom}></Route>
</Switch>
</AnimatePresence>
</Router>
</div>
);
Login.js
return (
<motion.div
initial="out"
animate="in"
exit="out"
variants={pageTransition}
className="Login-Container"
>
//same code
<Link
onClick={(event) => (!name || !room ? event.preventDefault() : null)}
to={`/chat?name=${name}&room=${room}`}
>
<button
type="submit"
className="Login-Button"
>
Sign In
</button>
</Link>
</motion.div>
);
ChatRoom.js
return (
<motion.div
initial="out"
animate="in"
exit="out"
variants={pageTransition}
className="ChatRoom-Container"
>
<div className="ChatRoom-NavBar">
<NavLink to="/">
<div
className="ChatRoom-BackButton"
style={{ backgroundImage: `url(${arrow})` }}
></div>
</NavLink>
</div>
</motion.div>
);
};
Your initial and exit prop on motion.div seem to be using the same key i.e "out" try giving the exit prop a different key. Dont forget to adjust your variants as well when you make the change.

React Ternary Operator Prevent Repeating Code with Styled components?

So I'm using styled components and found the only way to trigger my function is with a ternary operator that wraps my entire navbar component. The problem is I have to recopy the entire component code into the : statement which basically means I'm writing my nav component code twice just to get the ternary to work properly.
Is there a way to not repeat it and write the ternary with the least amount of code?
So here's my code issue
return (
<>
// If scroll is true, I want to add this active prop onto my <Nav>
{scroll ? (
<Nav active click={click}>
<NavbarContainer>
<NavLogo to="/" onClick={closeMobileMenu}>
<NavIcon />
EXPLOR
</NavLogo>
<MobileIcon onClick={handleClick}>
{click ? <FaTimes /> : <FaBars />}
</MobileIcon>
<NavMenu onClick={handleClick} click={click}>
<NavItem>
<NavLinks to="/">Home</NavLinks>
</NavItem>
<NavItem>
<NavLinks to="/images">Images</NavLinks>
</NavItem>
<NavItem>
<NavLinks to="/products">Products</NavLinks>
</NavItem>
</Nav>
:
// If the scroll is false, I remove the active prop from my nav
(
<Nav click={click}>
<NavbarContainer>
<NavLogo to="/" onClick={closeMobileMenu}>
<NavIcon />
EXPLOR
</NavLogo>
<MobileIcon onClick={handleClick}>
{click ? <FaTimes /> : <FaBars />}
</MobileIcon>
<NavMenu onClick={handleClick} click={click}>
<NavItem>
<NavLinks to="/">Home</NavLinks>
</NavItem>
<NavItem>
<NavLinks to="/images">Images</NavLinks>
</NavItem>
<NavItem>
<NavLinks to="/products">Products</NavLinks>
</NavItem>
</Nav>
}
</>
The whole point is so I can trigger this active prop from my styled component
background: ${({ active }) => (active ? "#141414" : "transparent")};
But as you can see I had to copy my entire component code twice because the <Nav> that has the active prop wraps around my entire code.
Is there an easier way to toggle the active prop with styled components without having to recopy my entire navbar in the : or statement?
Assuming active is a booelan you can smply set active={scroll } like so
<Nav active={scroll} click={click}>
.....
</Nav>

How to reset useState everytime I change my page?

I'm working on a Hamburger Navbar
here is my page look like:
the issues is whenever I clicked to another route but the navbar still appear on right side. I want to make it whenever I go to another route it will disappear.
Here is my code:
const App = (props) => {
const [ menuOpen, setMenuOpen ] = useState(false);
<HamburgerNav>
<HamburgerNavContainer>
<LogoLinkStyled to="/">
<LogoNav src="https://thebeuter.com/wp-content/uploads/2020/04/logo-black.png" />
</LogoLinkStyled>
<HamburgerUtilities>
<HamburgerUlityItem>
<Icon className="fal fa-search fa-rotate-90" onClick={openModalHandler} />
</HamburgerUlityItem>
<HamburgerUlityItem>
<Link to="/cart" style={{ color: 'black', textDecoration: 'none' }}>
<Icon className="fal fa-shopping-bag" />
<CartNumb>({props.basketProps.basketNumbers})</CartNumb>
</Link>
</HamburgerUlityItem>
<HamburgerUlityItem>
<HamburgerLine onClick={() => setMenuOpen(!menuOpen)} />
</HamburgerUlityItem>
</HamburgerUtilities>
</HamburgerNavContainer>
</HamburgerNav>
How can I fix this problem? Really appreciate it.!!!
Github project: https://github.com/nathannewyen/the-beuter
update Router code:
Here is my Router for all routes in navbar
<Router>
<ContactForm path="/contact" />
<SizeChart path="/size-chart" />
<ShippingAndReturn path="/shipping-return" />
<PrivacyAndPolicy path="/privacy-policy" />
<AboutUs path="/about-us" />
<ShopAllProducts path="/" />
<NewArrival path="/shop/new-arrival" />
<Tops path="/product-category/top" />
<Bottoms path="/product-category/bottom" />
<Product path="/product/:title_url" />
<SearchInfo path="/search/:title" searchTerm={searchTerm} title="Profile" />
<Cart path="/cart" />
<Checkout path="/checkout" />
</Router>
You need to add onClick to the "route" component that is not closing it and do something like this:
onClick={() => { setMenuOpen(prevState => {return !prevState}) }}
If for example you want it to close when you click on "T-Shirts", then "T-Shirts" must also have that onClick.
If you already have onClicks on these components with another function, you can just add multiple functions inside the anonymous function like this:
onClick={() => {
YourOtherFunction();
setMenuOpen(prevState => {return !prevState});
}
If your components are not in the App.js you need to somehow pass the onClick down too them.
Since its a state you wont be able to pass down the setMenuOpen itself, you need a wrapper function. So first create the wrapper:
const setMenuOpenWrapper = () => {
setMenuOpen(prevState => return { !prevState });
}
Then pass it down to the childrens like:
and then inside your ContactForm on the link to the contact form add the onClick:
...onClick={() => { closeMenuFunction(); }}
Ok I just checked your code, you need to pass down the function to the Sidenav component.
So in your App.js first create the wrapper function as I explained above
After that again in App.js on line 316 where you have <SideNav menuOpen={menuOpen} /> change it to <SideNav menuOpen={menuOpen} closeMenuFunction={setMenuOpenWrapper}/>
Then in your Sidenav.jsx on all of your menu items add an onclick:
onClick={props.closeMenuFunction}
And then it should work.

Resources