web-dev-qa-db-ja.com

React要素にスクロールするフック

ReactフックをReact 16.8.6でフックすると、ナビゲーション項目をクリックすると特定のHTML要素セクションにスクロールできるようになります。ページにレンダリングされるセクションの兄弟であるNavigationコンポーネントがあります。

また、ページがスクロールしたときに、Appの状態をそのHTMLセクションで更新したいと思います。

ナビゲーションコンポーネントJSX

<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();
    }
}, []);
8
Mark A

react-router-domを使用してもかまわない場合は、履歴の変更を追跡し、idの履歴の変更を介して、スクロール位置をHTML要素のhashに更新できます。このアプローチの利点は、状態を利用したり、参照を利用したりする必要がないことであり、アプリケーション全体に拡大縮小できます(要素がアプリケーションのツリー内のどこにあるかに関係なく、それらにスクロールできます)。

動作例

https://fglet.codesandbox.io/ (デモ)

https://codesandbox.io/s/fglet (ソース-残念ながら、コードサンドボックスエディター内では機能しません)


components/ScrollHandler(ハッシュ履歴の変更をリッスンするフック、ハッシュ内にあるIDと一致する要素を検索し、一致する要素IDが見つかった場合、次に要素までスクロールします)

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);

components/Navigation(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;

components/SectionsHeadlineコンポーネントには、照合される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"));
13
Matt Carlotta