Я хочу написать хук React с React 16.8.6, который позволит мне прокручивать до определенного раздела элемента HTML при щелчке элемента навигации. У меня есть компонент Navigation, который является братом разделов, отображаемых на странице.
Также, когда страница прокручивается, я хотел бы обновить состояние App с помощью этого раздела HTML.
<ul class = "nav>
<li><a>Section 1</a></li>
<li><a>Section 2</a></li>
</ul>
<section className = "section-1">Section 1</section>
<section className = "section-2">Section 2</section>
const [navItem, setNavItem] = React.useState(null);
const sectionRef = React.useRef(null);
// Scroll To Item
useEffect(() => {
console.info(sectionRef.current);
if (sectionRef.current) {
sectionRef.current.scrollToItem();
}
}, []);
Обновленное описание... Компонент <Nav/> отделен от этих разделов, но они отображаются на этой единственной странице.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Если вы не возражаете против использования react-router-dom, вы можете отслеживать изменения истории и обновлять положение прокрутки до id элемента HTML с помощью hash изменения истории. Преимущество этого подхода в том, что вам не нужно использовать ни состояние, ни ссылки, и он может масштабироваться по всему приложению (независимо от того, где расположены элементы в дереве приложения, вы можете прокручивать их).
Рабочий пример:
https://fglet.codesandbox.io/ (демо)
https://codesandbox.io/s/fglet (источник -- к сожалению, не работает в редакторе codeandbox)
компоненты/ScrollHandler (хук, который прослушивает изменения истории хеша, ищет элементы, которые соответствуют идентификатору, расположенному в хэше, и, если он находит соответствующий идентификатор элемента, то он прокручивается до элемента)
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);
компоненты/навигация (ссылки для изменения местоположения истории хэшей URL)
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;
компоненты/разделы (компонент Headline содержит id, с которым будет выполняться сопоставление)
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 "./components/Container";
import Navigation from "./components/Navigation";
import Sections from "./components/Sections";
import ScrollHandler from "./components/ScrollHandler";
import "./styles.css";
const App = () => (
<BrowserRouter>
<Container>
<ScrollHandler />
<Navigation />
<Sections />
</Container>
</BrowserRouter>
);
render(<App />, document.getElementById("root"));
Ваша навигация и разделы хранятся в одном файле компонента?