the navbar with reacte route and darkmode doesnt work together - reactjs

I have created a navbar with light and dark mode, everything working well. Now I want to update it to multi-page with react-router and with a layout. If I gave to the path name to the url is working well. The problem is the url shows me the page but the navbar doesn't navigate to the url and doesn't toggle the dark/light mode.
import React, { useRef, useEffect } from "react";
import "./header.css";
import { Link, useMatch, useResolvedPath } from "react-router-dom"
const nav__links = [
{
to: "home",
display: "Home",
},
{
to: "service",
display: "Service",
},
{
to: "preise",
display: "Preise",
},
{
to: "kontakt",
display: "Kontakt",
},
];
const Header = ({ theme, toggleTheme }) => {
const headerRef = useRef(null);
const menuRef = useRef(null);
const headerFunc = () => {
if (
document.body.scrollTop > 80 ||
document.documentElement.scrollTop > 80
) {
headerRef.current.classList.add("header__shrink");
} else {
headerRef.current.classList.remove("header__shrink");
}
};
useEffect(() => {
window.addEventListener("scroll", headerFunc);
return () => window.removeEventListener("scroll", headerFunc);
}, []);
const handleClick = (e) => {
e.preventDefault();
const targetAttr = e.target.getAttribute("to");
const location = document.querySelector(targetAttr).offsetTop;
window.scrollTo({
left: 0,
top: location - 80,
});
};
const toggleMenu = () => menuRef.current.classList.toggle("menu__active");
function CustomLink({ to, children, ...props }) {
const resolvedPath = useResolvedPath(to)
const isActive = useMatch({ path: resolvedPath.pathname, end: true })
return (
<li className={isActive ? "active" : ""}>
<Link to={to} {...props}>
{children}
</Link>
</li>
);
};
return (
<header className="header" ref={headerRef}>
<div className="container">
<div className="nav__wrapper">
<div className="logo" to="home">
<Link to="home"><h2>Q-Tech</h2></Link>
</div>
{/* ========= navigation ============= */}
<div className="navigation" ref={menuRef} onClick={toggleMenu}>
<ul className="menu">
{nav__links.map((item, index) => (
<li className="menu__item" key={index}>
<CustomLink
to={item.to}
onClick={handleClick}
className="menu__link"
>
{item.display}
</CustomLink>
</li>
))}
</ul>
</div>
{/* ============ light mode ============= */}
<div className="light__mode">
<span onClick={toggleTheme}>
{theme === "light-theme" ? (
<span>
<i class="ri-moon-line"></i>Dark
</span>
) : (
<span>
<i class="ri-sun-line"></i> Light
</span>
)}
</span>
</div>
<span className="mobile__menu" onClick={toggleMenu}>
<i class="ri-menu-line"></i>
</span>
</div>
</div>
</header>
);
};
export default Header;

Why navigation is not working?
Look at the navigation link definition - CustomLink:
<CustomLink
onClick={handleClick} // <--
...
>
You have an onClick handler on the link. Inside this event handler you call e.preventDefault(), which prevents the navigaton behaviour.
Why toggle light/dark theme it not working?
The toggle button here looks fine. So it is probably a problem in the code outside Header component. Try to debug toggleTheme functionality.

Related

How to dynamically fetch api route from component in page folder?

i have a reusable contact form that works perfectly when used in the index.js file.
However when i use it from a component in the page folder i am having a 404 not found error message because it uses this route 3000/ourServices/conciergerie/api/contact/ instead of 3000/api/contact.
How do i ensure the it will always fetch the correct route? please see how i fetch the api below :
async function handleSubmit() {
const data = {
firstName,
email,
phone,
message,
};
const res = await fetch("api/contact", {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
data: data,
token: "test",
}),
});
alert("Message sent! Thank you\nWe will be in touch with you soon!");
}
pages/ourServices/conciergerie
import Image from "next/image";
import { AiOutlinePlus, AiOutlineMinus } from "react-icons/ai";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { Contact } from "../../components/contact/Contact";
import es from "../../locales/es-ES/conciergerie.json";
import en from "../../locales/en-US/conciergerie.json";
import Icon1 from "/public/1.svg";
import Icon2 from "/public/2.svg";
import Icon3 from "/public/3.svg";
const Conciergerie = () => {
let { locale } = useRouter();
let t = locale === "es-ES" ? es : en;
// const { t } = useTranslation(locale, "conciergerie");
let myIcons = [Icon1, Icon2, Icon3];
const scrollToConciergerie = () => {
window.scrollTo({
top: 300,
behavior: "smooth",
});
};
const myLoader = ({ src, width, quality }) => {
return `${src}?w=${width}&q=${quality || 75}`;
};
const [showform, setshowform] = useState(false);
useEffect(() => {
window.addEventListener("load", scrollToConciergerie);
return () => {
window.removeEventListener("load", scrollToConciergerie);
};
});
const showContactForm = () => {
return <Contact />;
};
const contentData = t.conciergerieData;
return (
<div className="section" onLoad={scrollToConciergerie}>
<div className="container">
<div className="text-center">
<h1 className=" my-4 text-capitalize" id="conciergerie">
{t.conciergerieHeader}
</h1>
</div>
<h3 className="text-capitalize concierge-subheading mt-3">
{t.conciergerieTitle}
</h3>
<p className="lead concierge-subheading-text">{t.conciergerieText}</p>
</div>
<div className="container">
<div className="row text-center mt-5">
{contentData?.map((item, index) => {
return (
<div className="col-md-4" key={index}>
<span className="fa-stack fa-4x">
<Image
layout="responsive"
src={myIcons[index]}
alt="icons"
className="svg-inline--fa fa-solid fa-stack-1x fa-inverse img-fluid"
aria-hidden="true"
focusable="false"
data-prefix="fas"
data-icon="house"
role="img"
objectFit="cover"
height={300}
width={300}
//loader={myLoader}
/>
</span>
<h4 className="my-3 text-hogar2 text-uppercase">
{item.title}
</h4>
<ul>
{item.text.map((text) => {
return (
<li key={text.id} className="list-unstyled">
<p className="m-0 text-muted text-list">
{text.content}
</p>
</li>
);
})}
</ul>
{item.id === "algomas" &&
(!showform ? (
<AiOutlinePlus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
) : (
<AiOutlineMinus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
))}
{item.id === "else" &&
(!showform ? (
<AiOutlinePlus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
) : (
<AiOutlineMinus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
))}
</div>
);
})}
</div>
{showform && showContactForm()}
</div>
</div>
);
};
export default Conciergerie;
can someone help me please?
The reason this problem is happening has to do with absolute and relative paths.
fetch("api/contact")
Is a relative path. The fetch function figures out the path of the current file, ie 3000/ourServices/conciergerie, and adds api/contact to it
On the other hand, if you add a "/" before the path :
fetch("/api/contact")
Fetch figures out the root path of the project, then adds the path you added, ie :
3000/api/contact
TL;DR: Change fetch("api/contact") to fetch("/api/contact").

<button> that changes "navigation titles" with "content field" / passing data from child to parent [duplicate]

In <Nav/> component, "click event" on chevron <button>, triggers nextTitle(length) function in useNextTitle.js custom hook. This function sets the value of val, which is being returned by useNextTitle.js. How to pass that new val to App.js ?
Bigger picture: I want to change the display between <Dod /> and <Analogia /> (in App.js), after "click event" in <Nav /> (figured out val would be helpful for that, as a parameter in Conditional Statement).
Functionality I`m trying to achieve is visualized on the website I done with vanilla Java Script : link (the blue navigation, changes "main pages" with "titles" when chevron clicked)
App.js
import Nav from "./components/Nav";
import Dod from "./components/Dod";
import Analogia from "./components/Analogia";
function App() {
return (
<div className="App">
<Nav />
<Dod />
<Analogia />
</div>
);
}
export default App
Nav.js
import useNextTitle from './useNextTitle';
import './Nav.css';
const Nav = () => {
const navData = [
{id: 0, text: "DOD"},
{id: 1, text: "analogia"}
]
const length = navData.length;
const { val, nextTitle } = useNextTitle();
return (
<nav>
<div>
{/* titles */}
<ul>
<li key="li1">
{navData.map((title, index) => {
return (
<div
className={index === val ? "active" : "display-none"} key={title.id}>
{title.text}
</div>
)
})}
</li>
</ul>
{/* chevron button */}
<div>
<button onClick={() => nextTitle(length)}>
<span className="material-icons">
chevron_right
</span>
</button>
</div>
</div>
</nav>
)
}
export default Nav
useNextTitle.js
import { useState } from 'react';
const useNextTitle = () => {
const [val, setVal] = useState(0);
const nextTitle = (length) => {
setVal(val === length -1 ? 0 : val + 1 )
console.log("hook vav = " + val)
}
return { val, nextTitle }
}
export default useNextTitle;
Move the useNextTitle hook/state up to App and pass val and nextTitle down to Nav to toggle/update the state. Use val to conditionally render Dod and Analogia.
Example:
function App() {
const { val, nextTitle } = useNextTitle();
return (
<div className="App">
<Nav {...{ val, nextTitle }} />
{val === 0 && <Dod />}
{val === 1 && <Analogia />}
</div>
);
}
...
const Nav = ({ val, nextTitle }) => {
const navData = [
{ id: 0, text: "DOD" },
{ id: 1, text: "analogia" }
];
const length = navData.length;
return (
<nav>
<div>
{/* titles */}
<ul>
<li key="li1">
{navData.map((title, index) => {
return (
<div
className={index === val ? "active" : "display-none"}
key={title.id}
>
{title.text}
</div>
);
})}
</li>
</ul>
{/* chevron button */}
<div>
<button onClick={() => nextTitle(length)}>
<span className="material-icons">chevron_right</span>
</button>
</div>
</div>
</nav>
);
};

Passing value from child to parent, after click event in child (which triggers function)

In <Nav/> component, "click event" on chevron <button>, triggers nextTitle(length) function in useNextTitle.js custom hook. This function sets the value of val, which is being returned by useNextTitle.js. How to pass that new val to App.js ?
Bigger picture: I want to change the display between <Dod /> and <Analogia /> (in App.js), after "click event" in <Nav /> (figured out val would be helpful for that, as a parameter in Conditional Statement).
Functionality I`m trying to achieve is visualized on the website I done with vanilla Java Script : link (the blue navigation, changes "main pages" with "titles" when chevron clicked)
App.js
import Nav from "./components/Nav";
import Dod from "./components/Dod";
import Analogia from "./components/Analogia";
function App() {
return (
<div className="App">
<Nav />
<Dod />
<Analogia />
</div>
);
}
export default App
Nav.js
import useNextTitle from './useNextTitle';
import './Nav.css';
const Nav = () => {
const navData = [
{id: 0, text: "DOD"},
{id: 1, text: "analogia"}
]
const length = navData.length;
const { val, nextTitle } = useNextTitle();
return (
<nav>
<div>
{/* titles */}
<ul>
<li key="li1">
{navData.map((title, index) => {
return (
<div
className={index === val ? "active" : "display-none"} key={title.id}>
{title.text}
</div>
)
})}
</li>
</ul>
{/* chevron button */}
<div>
<button onClick={() => nextTitle(length)}>
<span className="material-icons">
chevron_right
</span>
</button>
</div>
</div>
</nav>
)
}
export default Nav
useNextTitle.js
import { useState } from 'react';
const useNextTitle = () => {
const [val, setVal] = useState(0);
const nextTitle = (length) => {
setVal(val === length -1 ? 0 : val + 1 )
console.log("hook vav = " + val)
}
return { val, nextTitle }
}
export default useNextTitle;
Move the useNextTitle hook/state up to App and pass val and nextTitle down to Nav to toggle/update the state. Use val to conditionally render Dod and Analogia.
Example:
function App() {
const { val, nextTitle } = useNextTitle();
return (
<div className="App">
<Nav {...{ val, nextTitle }} />
{val === 0 && <Dod />}
{val === 1 && <Analogia />}
</div>
);
}
...
const Nav = ({ val, nextTitle }) => {
const navData = [
{ id: 0, text: "DOD" },
{ id: 1, text: "analogia" }
];
const length = navData.length;
return (
<nav>
<div>
{/* titles */}
<ul>
<li key="li1">
{navData.map((title, index) => {
return (
<div
className={index === val ? "active" : "display-none"}
key={title.id}
>
{title.text}
</div>
);
})}
</li>
</ul>
{/* chevron button */}
<div>
<button onClick={() => nextTitle(length)}>
<span className="material-icons">chevron_right</span>
</button>
</div>
</div>
</nav>
);
};

React.js - Disable background scroll when a modal is showing

I am trying to disable the background body scroll when a modal pop up window is open. I created the Modal using React portal. The only way I can think of doing this is by giving the body an overflow-y property when the Modal is open, but am unsure how to execute this..
my Modal.js looks like this
export default function Modal({open, onClose, image, title, description}){
if (!open) return null
return ReactDom.createPortal(
<>
<div id="overlay" />
<div id="modal" >
<button title={title} onClick={onClose}>close</button>
<h3>{title}</h3>
<img src={image} className="modal-img"/>
{description}
</div>
</>,
document.getElementById('portal')
)
}
and the component where I am 'rendering' the model
const ProjectCardUI = (props)=>{
const[isOpen, setIsOpen] = useState(false)
function openModal() {
setIsOpen(true)
// can I set the styling for '.body' or 'html' here?
}
return(
<div className="card text-center container-fluid d-flex">
<a className="modal-click" onClick ={openModal}>this is a link</a>
<Modal
open={isOpen}
onClose={() => setIsOpen(false)}
title={props.cardName}
image={props.imgsrc}
description={props.cardDescription}>
</Modal>
</div>
)
};
any help would be great.
You can directly manipulate the body style via the document object.
For example:
import React from "react";
import ReactDOM from "react-dom";
export default function Test() {
const setHidden = () => {
console.log(document.body.style.overflow);
if (document.body.style.overflow !== "hidden") {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "scroll";
}
};
return (
<div>
<button onClick={setHidden}>Click me! </button>
<h1>1</h1>
<h1>2</h1>
<h1>3</h1>
<h1>4</h1>
<h1>5</h1>
<h1>6</h1>
<h1>7</h1>
<h1>8</h1>
<h1>9</h1>
<h1>10</h1>
<h1>11</h1>
<h1>12</h1>
<h1>13</h1>
<h1>14</h1>
<h1>15</h1>
<h1>16</h1>
</div>
);
}
ReactDOM.render(<Test />, document.getElementById("container"));
Maybe something like this:
const ProjectCardUI = (props) => {
const [isOpen, setIsOpen] = useState(false);
function openModal() {
setIsOpen(true);
// can I set the styling for '.body' or 'html' here?
}
useEffect(() => {
const body = document.querySelector('body');
body.style.overflow = isOpen ? 'hidden' : 'auto';
}, [isOpen])
return (
<div className="card text-center container-fluid d-flex">
<a className="modal-click" onClick={openModal}>
this is a link
</a>
<Modal
open={isOpen}
onClose={() => setIsOpen(false)}
title={props.cardName}
image={props.imgsrc}
description={props.cardDescription}
></Modal>
</div>
);
};

Hiding an element after a while in Next.js

I have a header component as a function component. I want show a popup when logo text is clicked. After for a time it should close automatically. I use hooks for state of popup. But set state function doesn't work in setTimeout function. How can fix this?
import Link from 'next/link'
import style from './header.module.css'
const Header = () => {
const [popupOpen, setPopupOpen] = React.useState(false)
return (
<header className={style.header}>
<nav className={style.nav}>
<div
className={style.popupContainer}
onClick={() => {
setPopupOpen(!popupOpen)
console.log(popupOpen)
setTimeout(() => {
console.log(popupOpen)
setPopupOpen(!popupOpen)
console.log(popupOpen)
}, 1000)
}}
>
<span className={style.logo}>Logo</span>
<span
className={`${style.popupText} ${
popupOpen ? style.show : style.hide
}`}
>
Popup Text
</span>
</div>
<ul className={style.ul}>
<li>
<Link href='/'>
<a>.home</a>
</Link>
</li>
<li>
<Link href='/contact'>
<a>.contact</a>
</Link>
</li>
</ul>
</nav>
</header>
)
}
export default Header
Console log:
Let me suggests, this is the same question as:
React - useState - why setTimeout function does not have latest state value?
const _onClick = () => {
setPopupOpen(!popupOpen);
setTimeout(() => {
setPopupOpen(popupOpen => !popupOpen)
}, 2000);
};
Its happening because setPopupOpen is asynchronous. So by the time setPopupOpen(!popupOpen) is called it has same value as onClick first setPopupOpen(!popupOpen) so eventually when it called both setPopup doing same state update i.e both updating as false. Better way is to usesetPopupOpen callback function to update the value. I added this code.
import { useState } from "react";
import Link from "next/link";
import style from "./style.module.css";
const Header = () => {
const [popupOpen, setPopupOpen] = useState(false);
const toggle = () => {
setPopupOpen((prev) => !prev);
};
const onClick = () => {
setPopupOpen(!popupOpen);
setTimeout(() => {
toggle();
}, 1000);
};
return (
<header className={style.header}>
<nav className={style.nav}>
<div className={style.popupContainer} onClick={onClick}>
<span className={style.logo}>Logo</span>
{popupOpen && (
<span
className={`${style.popupText} ${
popupOpen ? style.show : style.hide
}`}
>
Popup Text
</span>
)}
</div>
<ul className={style.ul}>
<li>
<Link href="/">
<a>.home</a>
</Link>
</li>
<li>
<Link href="/contact">
<a>.contact</a>
</Link>
</li>
</ul>
</nav>
</header>
);
};
export default function IndexPage() {
return (
<div>
<Header />
</div>
);
}
Here is the demo: https://codesandbox.io/s/pedantic-haibt-iqecz?file=/pages/index.js:0-1212

Resources