簡體   English   中英

React Hooks 滾動到元素

[英]React Hooks Scroll to Element

我正在尋找使用 React 16.8.6 編寫一個 React 鈎子,它可以讓我在單擊導航項時滾動到特定的 HTML 元素部分。 我有一個Navigation組件,它是頁面上呈現的部分的兄弟。

此外,當頁面滾動時,我想用 HTML 部分更新App的 state。

導航組件 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();
    }
}, []);

如果您不介意使用react-router-dom ,那么您可以跟蹤歷史更改並通過hash歷史更改將滾動位置更新為 HTML 元素的id 這種方法的優點是您不必使用狀態,也不必使用引用,並且它可以在整個應用程序中擴展(無論元素位於應用程序樹中的哪個位置,您都可以滾動到它們)。

工作示例

https://fglet.codesandbox.io/ (演示)

https://codesandbox.io/s/fglet (來源——不幸的是,在codesandbox編輯器中不起作用)


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

組件/導航(更改 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;

索引.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"));

我正在使用 React 路由器 V6。 有些事情沒有奏效,而且有所不同。 例如, withRouter已被棄用。 如果您需要,React 路由器提供了一個解決方案( 鏈接)。

我的 V6 解決方案:

創建一個組件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;

創建一個組件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);

index.js中,我將我的<App/>組件與BrowserRouter as Router包裝起來,如下所示:

<Router>
   <App />
</Router>

然后在App.js中,添加<ScrollHandler/>組件:

<ScrollHandler location={location} />

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM