I have a single page react app which consists of several sections divided into components:
function App() {
return (
<div>
<Langbar />
<Header />
<Nav />
<About />
<Education />
<Experience />
<Projects />
<Contact />
<Footer />
</div>
);
}
The Nav component is a simple navigation bar which uses useState to change styling of a link when clicked:
const Nav = () => {
const [activeNav, setActiveNav] = useState('#');
return (
<nav>
<a href="#" onClick={() => setActiveNav('#')} className={activeNav === '#' ? 'active' : ''}><HiHome /></a>
<a href="#about" onClick={() => setActiveNav('#about')} className={activeNav === '#about' ? 'active' : ''}><HiUser /></a>
<a href="#education" onClick={() => setActiveNav('#education')} className={activeNav === '#education' ? 'active' : ''}><FaGraduationCap /></a>
<a href="#experience" onClick={() => setActiveNav('#experience')} className={activeNav === '#experience' ? 'active' : ''}><HiBriefcase /></a>
<a href="#projects" onClick={() => setActiveNav('#projects')} className={activeNav === '#projects' ? 'active' : ''}><FaLaptopCode /></a>
<a href="#contact" onClick={() => setActiveNav('#contact')} className={activeNav === '#contact' ? 'active' : ''}><HiMailOpen /></a>
</nav>
)
}
I want to change styling of the links not only when I click on them, but also when I scroll the page and relative section (component) enters the viewport.
I have managed to connect react-in-viewport library and detect when component enters the viewport (and log a message to console), but not sure how to pass this information to the Nav component and change its states based on it.
Code of the About component with rect-in-viewport:
require('intersection-observer');
const AboutRender = (props: { inViewport: boolean }) => {
const { inViewport, forwardedRef } = props;
const { t } = useTranslation();
return (
<section id="about" ref={forwardedRef}>
<h2>{t("about.title")}</h2>
<div className="container about__container">
<div className="about__me">
<div className="about__me-image">
<img src={Me} alt="About image" />
</div>
</div>
<div className="about__content">
<h3>{t("about.summary")}</h3>
<div className="about__content__text">
<p>{t("about.summary_1")}</p>
<p>{t("about.summary_2")}</p>
<p>{t("about.summary_3")}</p>
<p>{t("about.summary_4")}</p>
</div>
<h3>{t("about.tech_skills")}</h3>
<div className="about__content__skills">
<article className="skill__card">
<SiHtml5 className="skill__icon"/>
<h5>HTML</h5>
</article>
<article className="skill__card">
<SiCss3 className="skill__icon"/>
<h5>CSS</h5>
</article>
<article className="skill__card">
<SiJavascript className="skill__icon"/>
<h5>JavaScript</h5>
</article>
<article className="skill__card">
<SiReact className="skill__icon"/>
<h5>React, React Native</h5>
</article>
<article className="skill__card">
<SiFigma className="skill__icon"/>
<h5>Figma</h5>
</article>
</div>
</div>
</div>
</section>
)
}
const AboutWithRef = handleViewport(AboutRender /** options: {}, config: {} **/);
const About = (props: {isInViewport: boolean}) => {
const {isInViewport} = props;
return(
<AboutWithRef onEnterViewport={() => this.props.isInViewport=true} onLeaveViewport={() => console.log('leave')} />
)
}
Thanks in advance!
so basically you want to share info between two components,in order to do it, you should do state lifting,that is,state which is common between two component should be hosted by their common container component,in your case its App component,so first inside app component define state to store active link and set it like this
function App() {
const [activeNav, setActiveNav] = useState('#');
return (
<div>
<Langbar />
<Header />
<Nav />
<About />
<Education />
<Experience />
<Projects />
<Contact />
<Footer />
</div>
);}
and the pass it to child component like this
<Nav setActiveNav={setActiveNav} activeNav={activeNav}/>
<AboutRendersetActiveNav={setActiveNav} activeNav={activeNav}/>
now change your Nav component to use setActiveNav and activeNav like this
const Nav = (props) => {
const {activeNav, setActiveNav} = props;
return (
<nav>
<a href="#" onClick={() => setActiveNav('#')} className={activeNav === '#' ? 'active' : ''}><HiHome /></a>
<a href="#about" onClick={() => setActiveNav('#about')} className={activeNav === '#about' ? 'active' : ''}><HiUser /></a>
<a href="#education" onClick={() => setActiveNav('#education')} className={activeNav === '#education' ? 'active' : ''}><FaGraduationCap /></a>
<a href="#experience" onClick={() => setActiveNav('#experience')} className={activeNav === '#experience' ? 'active' : ''}><HiBriefcase /></a>
<a href="#projects" onClick={() => setActiveNav('#projects')} className={activeNav === '#projects' ? 'active' : ''}><FaLaptopCode /></a>
<a href="#contact" onClick={() => setActiveNav('#contact')} className={activeNav === '#contact' ? 'active' : ''}><HiMailOpen /></a>
</nav>
)
}
now your about component should set link like this way
const About = (props) => {
const {setActiveNav}=props
return(
<AboutWithRef onEnterViewport={() => setActiveNav("#active")} />
)
}
since at any given moment you will be in certain view ,you don't need to handle onLeaveViewport event
hope this will help read more here