admin管理员组文章数量:1252854
I am looking to write a React hook with React 16.8.6 that will let me scroll to a particular HTML element section on click of a navigation item. I have a Navigation
ponent that is a sibling of the sections rendered on the page.
Also when the page scrolls, I would like to update state of the App
with that HTML section.
Navigation Component JSX
<ul class="nav>
<li><a>Section 1</a></li>
<li><a>Section 2</a></li>
</ul>
Sections in Home Page at App Level Component
<section className="section-1">Section 1</section>
<section className="section-2">Section 2</section>
Hooks
const [navItem, setNavItem] = React.useState(null);
const sectionRef = React.useRef(null);
// Scroll To Item
useEffect(() => {
console.log(sectionRef.current);
if (sectionRef.current) {
sectionRef.current.scrollToItem();
}
}, []);
I am looking to write a React hook with React 16.8.6 that will let me scroll to a particular HTML element section on click of a navigation item. I have a Navigation
ponent that is a sibling of the sections rendered on the page.
Also when the page scrolls, I would like to update state of the App
with that HTML section.
Navigation Component JSX
<ul class="nav>
<li><a>Section 1</a></li>
<li><a>Section 2</a></li>
</ul>
Sections in Home Page at App Level Component
<section className="section-1">Section 1</section>
<section className="section-2">Section 2</section>
Hooks
const [navItem, setNavItem] = React.useState(null);
const sectionRef = React.useRef(null);
// Scroll To Item
useEffect(() => {
console.log(sectionRef.current);
if (sectionRef.current) {
sectionRef.current.scrollToItem();
}
}, []);
Share
Improve this question
edited Jun 20, 2020 at 9:12
CommunityBot
11 silver badge
asked Jun 3, 2019 at 2:11
Mark AMark A
2,1354 gold badges21 silver badges28 bronze badges
2
- Is your navigation and sections kept in the same ponent file? – Cat_Enthusiast Commented Jun 3, 2019 at 3:04
- Updated description... The <Nav/> ponent is separate from those sections, but they render on that single page. – Mark A Commented Jun 3, 2019 at 3:30
2 Answers
Reset to default 20If you don't mind using react-router-dom
, then you can track history changes and update the scroll position to an HTML element's id
via a hash
history change. The advantage of this approach is you don't have to utilize state, nor utilize refs, and it can scale across the entire application (regardless of where the elements are located within the application's tree, you can scroll to them).
Working example:
https://fglet.codesandbox.io/ (demo)
https://codesandbox.io/s/fglet (source -- unfortunately, doesn't work within the codesandbox editor)
ponents/ScrollHandler (hook that listens to hash history changes, searches for elements that match the id located within the hash and, if it finds a matching element id, then it'll scroll to the element)
import { useEffect } from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router-dom";
const ScrollHandler = ({ location }) => {
useEffect(() => {
const element = document.getElementById(location.hash));
setTimeout(() => {
window.scrollTo({
behavior: element ? "smooth" : "auto",
top: element ? element.offsetTop : 0
});
}, 100);
}, [location]);
return null;
};
ScrollHandler.propTypes = {
location: PropTypes.shape({
pathname: PropTypes.string,
search: PropTypes.string,
hash: PropTypes.string,
state: PropTypes.any,
key: PropTypes.string
}).isRequired
};
export default withRouter(ScrollHandler);
ponents/Navigation (links to change url hash history location)
import React from "react";
import { Link } from "react-router-dom";
import List from "../List";
const Navigation = () => (
<List>
{[1, 2, 3, 4, 5].map(num => (
<li key={num}>
<Link to={`/#section${num}`}>Section {num}</Link>
</li>
))}
</List>
);
export default Navigation;
ponents/Sections (the Headline
ponent contains the id
that will be matched against)
import React from "react";
import Headline from "../Headline";
const Sections = () =>
[1, 2, 3, 4, 5].map(num => (
<Headline key={num} id={`#section${num}`}>
Section {num}
</Headline>
));
export default Sections;
index.js
import React from "react";
import { render } from "react-dom";
import { BrowserRouter } from "react-router-dom";
import Container from "./ponents/Container";
import Navigation from "./ponents/Navigation";
import Sections from "./ponents/Sections";
import ScrollHandler from "./ponents/ScrollHandler";
import "./styles.css";
const App = () => (
<BrowserRouter>
<Container>
<ScrollHandler />
<Navigation />
<Sections />
</Container>
</BrowserRouter>
);
render(<App />, document.getElementById("root"));
I am using React Router V6. Some things didn't work and were different. For instance, withRouter
was deprecated. React router offered a solution if you need it (link).
My solution for V6:
Create a ponent WithRouter.jsx
:
import { useLocation, useNavigate, useParams } from "react-router-dom";
function withRouter(Component) {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return <Component {...props} router={{ location, navigate, params }} />;
}
return ComponentWithRouterProp;
}
export default withRouter;
Create a ponent ScrollHandler.jsx
import { useEffect } from "react";
import WithRouter from "./WithRouter";
const ScrollHandler = ({ location }) => {
useEffect(() => {
const element = document.getElementById(location.hash.substring(1));
if (element) element.scrollIntoView();
}, [location]);
return null;
};
export default WithRouter(ScrollHandler);
In index.js
I wrapped my <App/>
p with the BrowserRouter as Router
like so:
<Router>
<App />
</Router>
Then in App.js
, add the <ScrollHandler/>
ponent:
<ScrollHandler location={location} />
本文标签: javascriptReact Hooks Scroll to ElementStack Overflow
版权声明:本文标题:javascript - React Hooks Scroll to Element - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1740713453a2279626.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论