Reputation: 338
I'm trying to get the onclick handlers in my navbar to scroll different components into view. I have found a way that works, but I'm getting a warning from react, as well as the code is not looking very clean. I'm hoping someone knows how to solve this is in a more correct way, so that the warning goes away and also keeps the code clean.
I'm going to create a scrollTo on each component, so in the Header.js there will be almost similar calls, with different names only.
Here is the structure of the app:
App.js
const App = () => {
return (
<div>
<Header />
<Banner />
<About />
<Technology />
<Contact />
<Portfolio />
</div>
);
};
Technology.js
const Technology = () => {
return (
<section className="technology">
<div className="heading white">
<h2>Technologies</h2>
<p>Some techno</p>
</div>
</section>
)
}
Header.js (Navbar)
const Header = () => {
let technology;
useEffect(() => {
technology = document.querySelector(".technology");
}, []);
return (
<header>
<p className="logo">Portfolio</p>
<div className="toggle"></div>
<ul>
<li>
<p onClick={() => window.scrollTo({ top: 0, behavior: "smooth" })}>
Home
</p>
</li>
<li>
<p>About Me</p>
</li>
<li>
<p onClick={() => technology.scrollIntoView({ behavior: "smooth" })}>
Technologies
</p>
</li>
<li>
<p>Contact</p>
</li>
<li>
<p>Projects</p>
</li>
</ul>
</header>
);
};
Here is the warning:
Assignments to the 'technology' variable from inside React Hook useEffect will be lost after each render. To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property. Otherwise, you can move this variable directly inside useEffect
To solve this I guess I need to pass it from App.js to Header.js, and get it from each Component to App.js? My googling skills have not been sufficient in solving this unfortunately.
I know there is a library for this in react, but I was hoping to solve this in a more "native" way for now.
Upvotes: 2
Views: 2306
Reputation: 9354
You can do this quite flexibly with function refs, which will allow elements to register themselves onto a universal ref
object. Refs are definitely the way to go; you want to avoid accessing the DOM directly whenever possible when using React.
const { useRef } = React;
function App() {
const pageRefs = useRef({});
return (
<div className="app">
<Header pageRefs={pageRefs} />
<About pageRefs={pageRefs} />
<Technology pageRefs={pageRefs} />
<Portfolio pageRefs={pageRefs} />
</div>
);
};
function Header({ pageRefs }) {
function scrollIntoView(type) {
pageRefs.current[type].scrollIntoView({ behavior: "smooth" });
};
return (
<header>
<button onClick={() => scrollIntoView('about')}>
About
</button>
<button onClick={() => scrollIntoView('techno')}>
Technology
</button>
<button onClick={() => scrollIntoView('portfolio')}>
Portfolio
</button>
</header>
);
};
function About({ pageRefs }) {
return (
<section
className="page about"
ref={el => pageRefs.current = { ...pageRefs.current, about: el }}>
About
</section>
);
};
function Technology({ pageRefs }) {
return (
<section
className="page techno"
ref={el => pageRefs.current = { ...pageRefs.current, techno: el }}>
Technology
</section>
);
};
function Portfolio({ pageRefs }) {
return (
<section
className="page portfolio"
ref={el => pageRefs.current = { ...pageRefs.current, portfolio: el }}>
Portfolio
</section>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
* {
padding: 0;
margin: 0;
}
.app {
width: 100%;
}
.page {
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.about {
background-color: crimson;
}
.techno {
background-color: lemonchiffon;
}
.portfolio {
background-color: cornflowerblue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Upvotes: 2
Reputation: 1575
You can use callback for this.
export const scrollIntoView = (selector, option) => (event) => {
const el = document.querySelector(selector);
if (!el) return;
el.scrollIntoView(option);
};
Usage.
import { scrollIntoView } from 'path/to/file';
const scrollIntoViewTechnology = scrollIntoView('.technology', { behavior: "smooth" });
const Component = () => {
return (
<button type='button' onClick={scrollIntoViewTechnology}>Lorem Ipsum</button>
);
};
Upvotes: 0