React: How to change position of footer component depending on route - reactjs

I have a simple footer component. It is used in about and support pages. In my sass, I have set the position to relative.
I want to change this position based on the route. If it's /about then position: relative and it's /support then position: fixed.
Is it possible to achieve this?
function GeneralFooter() {
return (
<div class="container">
<div class="row">
<div class="col-lg-4 col-md-4 col-sm-6 col-xs-6">
<div class="footer-pad">
<ul class="list-unstyled">
<li>
<a
className="nav-link"
href="*"
target="_blank"
rel="noopener noreferrer"
>
Help
</a>
</li>
</ul>
</div>
</div>
<div class="col-lg-4 col-md-4 col-sm-6 col-xs-6">
<div class="footer-pad">
<ul class="list-unstyled">
<li className="nav-item">
<NavLink to="/about" className="nav-link">
<span>About</span>
</NavLink>
</li>
</ul>
</div>
</div>
<div class="col-lg-4 col-md-4 col-sm-6 col-xs-6">
<div class="footer-pad">
<ul class="list-unstyled">
<li className="nav-item">
<NavLink to="/support" className="nav-link">
<span>Support</span>
</NavLink>
</li>
</ul>
</div>
</div>
</div>
</div>
);
}

I'm not sure if you are using any libraries, but without, you can use the following code.
Using the style prop:
function GeneralFooter() {
const location = useLocation();
const pathName = location.pathname;
return (
<div
className="container"
style={{
position: pathName === '/about' ? 'relative' : pathName === '/support' ? 'fixed' : 'inherit'
}}
>
...
Using the className prop
.footer--about {
position: relative;
}
.footer--support {
position: fixed;
}
function GeneralFooter() {
const location = useLocation();
const pathName = location.pathname;
const extraClassName = pathName === '/about' ? 'footer--about' : pathName === '/support' ? 'footer--support' : '';
return (
<div
className={`container ${extraClassName}`}
>
...
With classNames dependency:
function GeneralFooter() {
const location = useLocation();
const rootClassName = classNames('container', {
'footer-about': location.pathname === '/about',
'footer-support': location.pathname === '/support',
});
return (
<div className={rootClassName}>
...

Related

React Dom Outlet Route Confusion. Nest 3 Part Page Into one Route. React Router v6

I'm having a hard time understanding how the routes and outlets work in react. My problem is this.
If I have a route that should load 3 parts.
-Top Menu
-Side Menu
-Center Content
But the center content is contained in the div thats wrapped by the "Side Menu" how do I wrap another element that will be changing inside of a previous component. My goal is to use the first 2 in all of the routes and based on the route the center content will change.
This is what it should look like
<div id="layoutSideNav">
<div id="layoutsidenav_nav"></div>
<div id="layoutSidenav_content"></div>
</div>
But for some reason all I can get is this
<div id="layoutSideNav">
<div id="layoutsidenav_nav"></div>
</div>
<div id="layoutSidenav_content"></div>
My react looks like this.
3 Components
Contains the top nav
contains the side nav
contains body elements that will change.
App()
<Route
path='/admin-dashboard'
element={
<>
<AdminNavigation />
<AdminNavigationLeft />
<AdminDashBoardHome />
</>
}
>
If I add <Outlet/> before the closing div in "layoutSideNav" component. nothing happens. If I remove the outlets from all components the app() looks the same. The top nav and side nave display correclty, but the center content is wonky.
I feel like routes is so much more abstract than I'm used to so maybe I'm just not thinking about it logically. Thank you so much for your help!
I tried using <Outlet/> to adjust how where the third component content displayed.
I've added my full code below for review.
const AdminNavigation = () =>{
const toggleSideMenu = event => {
// 👇️ toggle class on click
document.getElementsByTagName('body')[0].classList.toggle('sb-sidenav-toggled');
};
return (
<Fragment>
<nav className="sb-topnav navbar navbar-expand navbar-dark bg-dark">
<a className="navbar-brand ps-3" href="index.html">Woodmoore Improvement</a>
<button className="btn btn-link btn-sm order-1 order-lg-0 me-4 me-lg-0" onClick={toggleSideMenu} id="sidebarToggle" href="#!"><i className="fas fa-bars"></i></button>
<form className="d-none d-md-inline-block form-inline ms-auto me-0 me-md-3 my-2 my-md-0">
<div className="input-group">
<input className="form-control" type="text" placeholder="Search for..." aria-label="Search for..." aria-describedby="btnNavbarSearch" />
<button className="btn btn-primary" id="btnNavbarSearch" type="button"><i className="fas fa-search"></i></button>
</div>
</form>
<ul className="navbar-nav ms-auto ms-md-0 me-3 me-lg-4">
<li className="nav-item dropdown">
<a className="nav-link dropdown-toggle" id="navbarDropdown" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false"><i className="fas fa-user fa-fw"></i></a>
<ul className="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
<li><a className="dropdown-item" href="#!">Settings</a></li>
<li><hr className="dropdown-divider" /></li>
<li><a className="dropdown-item" href="#!">Logout</a></li>
</ul>
</li>
</ul>
</nav>
</Fragment>
)
}
export default AdminNavigation;
const AdminDashBoardHome = () => {
return(
<Fragment>
<div id='layoutSidenav_content'>
<main>
<div className='container-fluid px-4'>
<h1 className='mt-4'>HOA Member Manager</h1>
<ol className='breadcrumb mb-4'>
<li className='breadcrumb-item active'>Member Map</li>
</ol>
<div className='row'>
<div className='col-lg-12'>
<div className='embed-responsive embed-responsive-16by9'>
<div id='map_canvas' className='embed-responsive-item pb-4' >
</div>
</div>
</div>
</div>
<div className='row'>
</div>
<div className='card mb-4'>
<div className='card-header'>
<i className='fas fa-table me-1'></i>
Member Lookup
</div>
<MemberDataList/>
</div>
</div>
</main>
<footer className='py-4 bg-light mt-auto'>
<div className='container-fluid px-4'>
<div className='d-flex align-items-center justify-content-between small'>
<div className='text-muted'>Copyright © Your Website 2022</div>
<div>
<a href='#'>Privacy Policy</a>
·
<a href='#'>Terms & Conditions</a>
</div>
</div>
</div>
</footer>
</div>
</Fragment>
)
}
export default AdminDashBoardHome;
const AdminNavigationLeft = () => {
document.body.classList.add('sb-nav-fixed');
return(
<Fragment>
<div id="layoutSidenav">
<Outlet/>
<div id="layoutSidenav_nav">
<nav className="sb-sidenav accordion sb-sidenav-dark" id="sidenavAccordion">
<div className="sb-sidenav-menu">
<div className="nav">
<div className="sb-sidenav-menu-heading">Core</div>
<a className="nav-link" href="index.html">
<div className="sb-nav-link-icon"><i className="fas fa-tachometer-alt"></i></div>
Dashboard
</a>
<div className="sb-sidenav-menu-heading">Interface</div>
<a className="nav-link collapsed" href="#" data-bs-toggle="collapse" data-bs-target="#collapseLayouts" aria-expanded="false" aria-controls="collapseLayouts">
<div className="sb-nav-link-icon"><i className="fas fa-columns"></i></div>
Layouts
<div className="sb-sidenav-collapse-arrow"><i className="fas fa-angle-down"></i></div>
</a>
<div className="collapse" id="collapseLayouts" aria-labelledby="headingOne" data-bs-parent="#sidenavAccordion">
<nav className="sb-sidenav-menu-nested nav">
<a className="nav-link" href="layout-static.html">Static Navigation</a>
<a className="nav-link" href="layout-sidenav-light.html">Light Sidenav</a>
</nav>
</div>
<a className="nav-link collapsed" href="#" data-bs-toggle="collapse" data-bs-target="#collapsePages" aria-expanded="false" aria-controls="collapsePages">
<div className="sb-nav-link-icon"><i className="fas fa-book-open"></i></div>
Admin Management
<div className="sb-sidenav-collapse-arrow"><i className="fas fa-angle-down"></i></div>
</a>
<div className="collapse" id="collapsePages" aria-labelledby="headingTwo" data-bs-parent="#sidenavAccordion">
<nav className="sb-sidenav-menu-nested nav accordion" id="sidenavAccordionPages">
<a className="nav-link collapsed" href="#" data-bs-toggle="collapse" data-bs-target="#pagesCollapseAuth" aria-expanded="false" aria-controls="pagesCollapseAuth">
Manage Users
</a>
<a className="nav-link collapsed" href="#" data-bs-toggle="collapse" data-bs-target="#pagesCollapseAuth" aria-expanded="false" aria-controls="pagesCollapseAuth">
Create New
</a>
</nav>
</div>
</div>
</div>
<div className="sb-sidenav-footer">
<div className="small">Logged in as:</div>
Start Bootstrap
</div>
</nav>
</div>
</div>
</Fragment>
)
}
export default AdminNavigationLeft;
If the following is what you intend to be rendered:
<div id="layoutSideNav">
<div id="layoutsidenav_nav"></div>
<div id="layoutSidenav_content"></div>
</div>
Then I suspect you want the routed content to be rendered in the layoutSidenav_content div. Something like the following:
<div id="layoutSideNav">
<div id="layoutsidenav_nav">
... navbar content ...
</div>
<div id="layoutSidenav_content">
... routed content ...
</div>
</div>
In other words, the above snippet describes a layout component, the routed content is the logical place for an Outlet component wrapping nested routes to output their element content into. The route may look like:
const AdminLayout = () => (
<>
<AdminNavigation />
<div id="layoutsidenav">
<div id="layoutSidenav_nav">
<AdminNavigationLeft />
</div>
<div id="layoutsidenav_content">
<Outlet />
</div>
</div>
</>
);
<Route
path='/admin-dashboard'
element={<AdminLayout />}
>
<Route index element={<AdminDashBoardHome />} />
... other admin routes
</Route>
const AdminNavigationLeft = () => {
useEffect(() => {
document.body.classList.add('sb-nav-fixed');
}, []);
return (
<nav className="sb-sidenav accordion sb-sidenav-dark" id="sidenavAccordion">
<div className="sb-sidenav-menu">
...
</div>
</nav>
)
}
const AdminDashBoardHome = () => {
return (
<Fragment>
<main>
...
</main>
<footer className='py-4 bg-light mt-auto'>
...
</footer>
</Fragment>
)
}
I solved my own question.
You need to run the props through the component you're trying to display. The solution should be like this.
App()
<Route path='/admin-dashboard' element={<><AdminNavigation /><AdminNavigationLeft><AdminDashBoardHome/></AdminNavigationLeft></>}>
AdminDashLeft()
const AdminNavigationLeft = (props) => {
{props.children} // This is where the child component should display
}

Unable to use div components with bootstrap classes inside map function in react

I have passed the data via props, the console logs show me the data being passed. here is the code below
import React, { Component } from 'react';
import "../../../node_modules/bootstrap/dist/css/bootstrap.min.css";
import "./TestimonialCard.css";
const Cards = ({cardData}) =>{
console.log(cardData);
return(
<>
<div className="container">
<div className="row">
<div className="col-md-8 col-center m-auto">
<h2>Testimonials</h2>
<div id="myCarousel" className="carousel slide" data-ride="carousel" >
<div className="carousel-inner">
{cardData.map((slide , index )=>{
{console.log(slide)}
<div className="item carousel-item active" key={index} >
<div className="img-box">
<img src={slide.image} alt={slide.alt}/>
</div>
<p className="testimonial">
{slide.testimonial}
</p>
<p className="overview">
<b> {slide.name} </b> ,<span>{slide.designation}</span>
</p>
</div>
})}
</div>
<a className="carousel-control left carousel-control-prev" href="myCarousel" data-slide="prev">
<i className="fa fa-angle-left"></i>
</a>
<a className="carousel-control right carousel-control-next" href="myCarousel" data-slide="next">
<i className="fa fa-angle-right"></i>
</a>
</div>
</div>
</div>
</div>
</>
)
}
export default Cards
Here, I'm not getting an expected UI under my div with carousel-inner class.
You can use it like this:
import React, { Component } from 'react';
import "../../../node_modules/bootstrap/dist/css/bootstrap.min.css";
import "./TestimonialCard.css";
const Cards = ({cardData}) =>{
console.log(cardData);
const cardData = (cardData) => {
cardData.map((slide, index) => {
return (
<div className="item carousel-item active" key={index}>
<div className="img-box">
<img src={slide.image} alt={slide.alt}/>
</div>
<p className="testimonial">
{slide.testimonial}
</p>
<p className="overview">
<b> {slide.name} </b> ,<span>{slide.designation}</span>
</p>
</div>
)
})
}
return(
<>
<div className="container">
<div className="row">
<div className="col-md-8 col-center m-auto">
<h2>Testimonials</h2>
<div id="myCarousel" className="carousel slide" data-ride="carousel" >
<div className="carousel-inner">
{cardData()}
</div>
<a className="carousel-control left carousel-control-prev" href="myCarousel" data-slide="prev">
<i className="fa fa-angle-left"></i>
</a>
<a className="carousel-control right carousel-control-next" href="myCarousel" data-slide="next">
<i className="fa fa-angle-right"></i>
</a>
</div>
</div>
</div>
</div>
</>
)
}
export default Cards
The reason UI was not rendering is because you are using {} instead of () in map inside JSX.
If you want to use map inside render() or the JSX you need to use () instead of {} like this:
{cardData.map((slide , index )=> (
{console.log(slide)}
<div className="item carousel-item active" key={index} >
<div className="img-box">
<img src={slide.image} alt={slide.alt}/>
</div>
<p className="testimonial">
{slide.testimonial}
</p>
<p className="overview">
<b> {slide.name} </b> ,<span>{slide.designation}</span>
</p>
</div>
))}

Next.js onhover Link component gets refreshed

I am having an ecommerce application build in next.js. In this I have top bar contains contact and other link information, below that there is a long and search section. This two items are done in 2 components and both are combined.
Now the problem is, on hover any link in components, the particular page in refreshed (not reloading). What may be the problem. For better understanding I have placed a video blow. Please check.
https://www.dropbox.com/s/tlh4e2os1ok2tpr/screen-recording.webm?dl=0
I have given all link as below,
<Link href="/" as="/">
<a className="mr-4">Help Center</a>
</Link>
Please check my below codes,
Top.js
const Top = () => {
return (
<div>
<div className="topBar">
<div className="container">
<div className="row">
<div className="col-sm-4 col-md-6">
<p className="text-center text-sm-left">
<Link href="/" as="/">
<a>Sell in RangerBee</a>
</Link>
</p>
</div>
<div className="col-sm-8 col-md-6">
<p className="text-center text-sm-right">
<Link href="/" as="/">
<a className="mr-4">Gift Card</a>
</Link>
<Link href="/" as="/">
<a className="mr-4">Help Center</a>
</Link>
<Link href="/" as="/">
<a>Download app</a>
</Link>
</p>
</div>
</div>
</div>
</div>
</div>
}
export default Top;
Header.js
const Header = () => {
return (
<div>
<div className="searchBar">
<div className="container-fluid">
<div className="row align-items-center">
<div className="col-8 col-md-2 col-lg-2">
<p>
<Link href="/" as="/">
<a><img src="/vercel.svg" alt="Example" className="img-fluid" /></a>
</Link>
</p>
</div>
<div className="col-4 col-md-2 col-lg-3 col-xl-2">
<div className="menuCategory">
<p className="main" onClick={() => changeMenuCategory()}><span>Shop By Category</span> <FontAwesomeIcon icon={faChevronDown} className="ml-2 pt-1 fa-down"/> <FontAwesomeIcon icon={faBars} className="ml-2 pt-1 fa-bars"/></p>
<ul className={toggleCategory ? "main-nav" : "main-nav d-none"}>
{menus && menus.data.map((cat, index) => (
<li key={index}>
<Link href={"/products/"+cat.cat_slug} as={"/products/"+cat.cat_slug}><a title={cat.cat_name}>{cat.cat_name} <FontAwesomeIcon icon={faChevronRight} className="ml-2 float-right menu-arrow"/></a></Link>
<ul>
{cat.subcategory.map((subcat, index1) => (
<li key={index1+index}><Link href={"/products/"+cat.cat_slug+"/"+subcat.sub_slug} as={"/products/"+cat.cat_slug+"/"+subcat.sub_slug}><a title={subcat.sub_name}>{subcat.sub_name}</a></Link></li>
))}
</ul>
</li>
))}
</ul>
</div>
</div>
<div className="col-12 col-md-4 col-lg-4 col-xl-5 mt-3 mb-3 mt-md-0 mb-md-0">
<div className="search-box">
<div className="search-inner">
<input type="text" name="q" placeholder="Search for products, brands and much more" onClick={() => setToggleSearch(b => !b)} onFocus={(e) => trendingSearch(e)} onChange={(e) => startSearch(e)} />
<button><FontAwesomeIcon icon={faSearch}/></button>
</div>
<div className={toggleSearch ? "search-result" : "search-result d-none"}>
<ul ref={dropdown}>
{trending !== null && searched === null && trending.data.length > 0 && <li className="trending">Trending Search</li>}
{trending !== null && searched == null && trending.data.length > 0 &&
trending.data.map((trending, index) => (
<li key={index}><Link href={"/products/"+trending.trending_slug} as={"/products/"+trending.trending_slug}><a>{trending.trending_name}</a></Link></li>
))
}
{searched !== null && searched.data.length > 0 &&
searched.data.map((search, index) => (
<li key={index}>
<Link href={search.search_url} as={search.search_url}>
{search.images.length > 0 ?
<a><img src={api.IMAGE_PRODUCTS+"/"+search.images[0].image_name} alt={search.search_name} /> {search.search_name}</a>
:
<a>{search.search_name}</a>
}
</Link>
</li>
))
}
</ul>
</div>
</div>
</div>
<div className="col-12 col-md-4 col-lg-3">
<div className="text-center text-md-right">
<div className="d-inline loginDrop">
<Link href={!isAuthenticated ? "/login" : "/profile"} as={!isAuthenticated ? "/login" : "/profile"}>
<a className="signinBtn mr-5">{!isAuthenticated ? "Sign In" : "My Account"}</a>
</Link>
<div className={!isAuthenticated ? "login-content" : "login-content logout-content"}>
<p> </p>
<div className="login-inner">
<Link href={!isAuthenticated ? "/login" : "/profile"} as={!isAuthenticated ? "/login" : "/profile"}><a><FontAwesomeIcon icon={faUserCircle} className="mr-2"/> Your Profile</a></Link>
<Link href={!isAuthenticated ? "/login" : "/orders"} as={!isAuthenticated ? "/login" : "/orders"}><a><FontAwesomeIcon icon={faBoxOpen} className="mr-2 orderIcon"/> Orders</a></Link>
<Link href={!isAuthenticated ? "/login" : "/wishlist"} as={!isAuthenticated ? "/login" : "/wishlist"}><a><FontAwesomeIcon icon={faHeart} className="mr-2"/> Whishlist</a></Link>
<div className="otherDrop">
{!isAuthenticated ?
<>
<p className="first">Don't have an account?</p>
<p className="register"><Link href="/register" as="/register"><a>Register</a></Link></p>
<p className="login"><Link href="/login" as="/login"><a>Login</a></Link></p>
</>
:
<p className="login"><a href="#" onClick={deauthenticate}>Logout</a></p>
}
</div>
</div>
</div>
</div>
<Link href="/cart" as="/cart">
<a className="cartBtn"><FontAwesomeIcon icon={faShoppingCart} className="mr-xl-1"/> Cart <span>{carts[0] === null ? 0 : carts.length}</span></a>
</Link>
</div>
</div>
</div>
</div>
</div>
</div>
}
export default Header;
Layout.js
import Head from 'next/head';
import * as Main from './Index'
import './styles/Index';
const Layout = ({ children, title }) => (
<div>
<Head>
<title>{ title }</title>
<meta charSet="utf-8" />
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>
<Main.Top/>
<Main.Header/>
<div>
{ children }
</div>
</div>
);
export default Layout;
next.config.js
const withSass = require('#zeit/next-sass')
module.exports = withSass({
cssLoaderOptions: {
url: false
}
})
Is there any problem with my code?
I am using sass for styling. What may be the issue. I have searched a lot and couldn't find any solution. Actually am new to next.js. So I am not able to find a solution.
As said in their docs, next/Link accept the props href and as, where:
href: The page that it should go for, this path will never change at runtime.
as: Something like a decorator, how the link should appears for the user. It can change in runtime.
Tha been said, lets take a look in one of yours Link's:
<Link href={"/products/"+trending.trending_slug} as={"/products/"+trending.trending_slug}><a>{trending.trending_name}</a></Link>
In this case your href is changing dynamic with your trending and it should be a page that exists in your project, like pages/product/trending and if trending is dynamic you can use pages/product/[trending] and then in the as property you should use the dynamic link, like you are already doing, /products/"+trending.trending_slug.
Take a look into their docs about it: https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes
Here is a issue that look's to be the same problem: https://github.com/vercel/next.js/issues/11157

React Issue With Conditional Then Map

I have some react code that checks if an array has any elements. If it does, then it should map over the elements and display them. I am able to do the check but the map throws an error, and I do not know why.
Here is the function:
function MetalOptions() {
const metals_options = GetsOptions(product,"Metal");
if (metals_options.length > 0) {
return(
<React.Fragment>
<h1>yes</h1>
<ul>
{metals_options.map((option,index) =>
<li key={index}>{option}</li>
)}
</ul>
</React.Fragment>
);
}
}
Here is the entire render function:
render() {
const { isLoading, productID, product} = this.state;
function GetsOptions(product,kind) {
const variant_metals = [];
product.variants.map((variant) =>
variant.option_values.map((option) =>
variant_metals.push({type: option.option_type_name,value: option.presentation})
)
);
const filter_variant_metals = variant_metals.filter(item => item.type === kind);
const output = [...new Set(filter_variant_metals.map(s => JSON.stringify(s)))].map(s => JSON.parse(s));
return output;
}
function GetsVariantImages(product) {
const variant_images = [];
product.variants.map((variant) =>
variant.images.map((image) =>
variant_images.push({
original: image.product_url,
thumbnail: image.small_url,
description: image.alt,
originalAlt: image.alt,
thumbnailAlt: image.alt
})
)
);
const output4 = [...new Set(variant_images.map(s => JSON.stringify(s)))].map(s => JSON.parse(s));
return output4;
}
if (isLoading) {
return <div className="sweet-spinner">
<BounceLoader
sizeUnit={"px"}
size={30}
color={"#494847"}
loading={isLoading}
/>
</div>
}
function MetalOptions() {
const metals_options = GetsOptions(product,"Metal");
const metals_options_array = Object.values(metals_options);
if (metals_options.length > 0) {
return(
<React.Fragment>
<h1>yes</h1>
<ul>
{metals_options_array.map((option,index) =>
<li key={index}>{option}</li>
)}
</ul>
</React.Fragment>
);
}
}
const size_options = GetsOptions(product,"Center Diamond Size");
const shape_options = GetsOptions(product,"Center Diamond Shape");
return (
<div>
<div className="container">
<div className="row">
<div className="col-md-7">
{product.master.images.length >= 3?
<section className="main-image-grid">
{product.master.images.slice(0,3).map(image =>
<img key={image.id} src={image.large_url} alt={image.alt}/>
)}
</section>:
<section className="main-image-grid">
{product.master.images.slice(0,1).map(image =>
<img key={image.id} src={image.large_url} alt={image.alt}/>
)}
</section>
}
</div>
<div className="col-md-5 gradient-silver">
<h1 className="text-center">{product.name}</h1>
<p className="text-center">Your Price {product.display_price}</p>
<div className="cta">
<a href={"sms:+19137258268&body=Hello!%20I%20want%20more%20information%20on%20"+product.name}>Text For More Info!</a>
Call To Take A Look
</div>
<h2>Select Your Options</h2>
<MetalOptions />
<p>This comes in various options listed below:</p>
{GetsOptions(product,"Metal").length > 0 &&
<h4>yes</h4>
}
{GetsOptions(product,"Center Diamond Shape").length > 0 &&
<h2>
You have Center Diamond Shape {GetsOptions(product,"Center Diamond Shape").length} Options.
<ul>
</ul>
</h2>
}
{GetsOptions(product,"Center Diamond Size").length > 0 &&
<h2>
You have Center Diamond Size {GetsOptions(product,"Center Diamond Size").length} Options.
</h2>
}
</div>
</div>
<hr className="style-two"/>
<ul className="nav justify-content-center" id="productTab" role="tablist">
<li className="nav-item">
<a className="nav-link active" id="center-tab" data-toggle="tab" href="#center" role="tab" aria-controls="center" aria-selected="false">Center Gemstones</a>
</li>
<li className="nav-item">
<a className="nav-link" id="description-tab" data-toggle="tab" href="#description" role="tab" aria-controls="description" aria-selected="true">Description</a>
</li>
<li className="nav-item">
<a className={"nav-link " + (product.product_properties.length < 0 && 'disabled')} id="properties-tab" data-toggle="tab" href="#properties" role="tab" aria-controls="properties" aria-selected="false">Properties</a>
</li>
</ul>
<div className="tab-content" id="myTabContent">
<div className="tab-pane fade show active" id="center" role="tabpanel" aria-labelledby="center-tab">
<h3 className="h5 text-uppercase text-center">The Center</h3>
<p class="h6 text-center text-muted"><small>Deciding on a center diamond or gemstone is the <span className="text-uppercase font-weight-bold">important decision.</span> The center diamond holds the sentiment and value. If your piece of jewelry is a play, the mounting is a stage, and the center is the actors and script.</small></p>
<p class="h6 text-center text-muted"><small>We have helped so many in Kansas City find beautiful diamonds or gemstones in all qualities and price ranges usually complete with a GIA grading report. We would be honored to help you to your perfect one.</small></p>
<DiamondLinks />
<GemLinks />
</div>
<div className="tab-pane fade" id="description" role="tabpanel" aria-labelledby="description-tab"><p className="text-center">{product.description}</p></div>
<div className="tab-pane fade" id="properties" role="tabpanel" aria-labelledby="properties-tab">
{product.product_properties.map((property,index) =>
<p><span className="font-weight-bold">{property.property_name}:</span> {property.value}</p>
)}
</div>
</div>
</div>
<hr className="style-two"/>
<h2>More Pictures</h2>
<ImageGallery items={GetsVariantImages(product)} lazyLoad={true} showThumbnails={true} thumbnailPosition={'left'}/>
</div>
);
}
}
Any ideas? Thank you for your help!

Show and hide sub menu using React Hooks

I'm trying to redo my menu with React Hooks and here's my latest attempt.
The only issue I have is that when I click sub-menu-click, all the sub menu opens.
How can I make it so that only the child is open when the parent (i.e. Item 1) is clicked?
Thank you.
import React, { useState } from 'react';
export default () => {
const [menuOpen, setMenuOpen] = useState(false);
const [subMenuOpen, setSubMenuOpen] = useState(false);
const toggleClassName = menuOpen ? 'is-open' : '';
const data = useStaticQuery(graphql`
query {
site {
siteMetadata {
title
}
}
}
`);
return (
<Layout>
<Menu>
<div class="menu-left">
<Link to="/">{data.site.siteMetadata.title}</Link>
</div>
<Hamburger
className={toggleClassName}
onClick={() => setMenuOpen(!menuOpen)}
>
<span></span>
<span></span>
<span></span>
</Hamburger>
<div class="menu-right">
<ul className={toggleClassName}>
<li class="menu-item has-children">
<Link to="/">
Item 1
</Link>
<div
class={`sub-menu-click`}
onClick={() => setSubMenuOpen(!subMenuOpen)}
>
<span></span>
</div>
<ul
class={`sub-menu && ${
subMenuOpen ? 'is-open' : ''
}`}
>
<li class="menu-item">Sub-Item 1</li>
<li class="menu-item">Sub-Item 2</li>
<li class="menu-item">Sub-Item 3</li>
</ul>
</li>
<li class="menu-item has-children">
<Link to="/">
Item 2
</Link>
<div
class={`sub-menu-click`}
onClick={() => setSubMenuOpen(!subMenuOpen)}
>
<span
class={`${subMenuOpen ? 'is-open' : ''}`}
></span>
</div>
<ul
class={`sub-menu && ${
subMenuOpen ? 'is-open' : ''
}`}
>
<li class="menu-item">Sub-Item 1</li>
<li class="menu-item">Sub-Item 2</li>
<li class="menu-item">Sub-Item 3</li>
</ul>
</li>
</ul>
</div>
</Menu>
</Layout>
);
};
The simplest solution to this to move the SubMenu to a different component and let it have it's own state.
const SubMenu = () => {
const [subMenuOpen, setSubMenuOpen] = useState(false);
return (
<li class="menu-item has-children">
<Link to="/">
Item 1
</Link>
<div
class={`sub-menu-click`}
onClick={() => setSubMenuOpen(!subMenuOpen)}
>
<span></span>
</div>
<ul
class={`sub-menu && ${
subMenuOpen ? 'is-open' : ''
}`}
>
<li class="menu-item">Sub-Item 1</li>
<li class="menu-item">Sub-Item 2</li>
<li class="menu-item">Sub-Item 3</li>
</ul>
</li>
)
}
and render this component in place for SubMenu in Menu component.
In case you need to control the opening of SubMenu from the Menu component, you have an array of subMenuIds currently open or a single ID according to your use case.

Resources