Je cherche à écrire un crochet React avec React 16.8.6 qui me permettra de faire défiler jusqu'à une section d'élément HTML particulière en cliquant sur un élément de navigation. I avoir un composant Navigation
qui est un frère des sections rendues sur la page.
Aussi, lorsque la page défile, je voudrais mettre à jour l'état du App
avec cette section 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.log(sectionRef.current);
if (sectionRef.current) {
sectionRef.current.scrollToItem();
}
}, []);
Si cela ne vous dérange pas d'utiliser react-router-dom
, vous pouvez alors suivre les modifications de l'historique et mettre à jour la position de défilement vers id
d'un élément HTML via une modification de l'historique hash
. L'avantage de cette approche est que vous n'avez pas besoin d'utiliser l'état, ni d'utiliser les références, et elle peut évoluer sur l'ensemble de l'application (indépendamment de l'emplacement des éléments dans l'arborescence de l'application, vous pouvez les faire défiler).
Exemple de travail :
https://fglet.codesandbox.io/ (démo)
https://codesandbox.io/s/fglet (source - malheureusement, ne fonctionne pas dans l'éditeur de codesandbox)
components/ScrollHandler (hook qui écoute les changements d'historique de hachage, recherche les éléments qui correspondent à l'ID situé dans le hachage et, s'il trouve un identifiant d'élément correspondant , il défilera jusqu'à l'élément)
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);
composants/Navigation (liens pour changer l'emplacement de l'historique de hachage d'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;
composants/Sections (le composant Headline
contient le id
qui sera comparé)
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"));