在 react-router 中使用锚点

IT技术 javascript reactjs routes react-router anchor
2021-01-16 13:44:21

如何使用 react-router,并让链接导航到特定页面上的特定位置?(例如/home-page#section-three

详情

react-router在我的 React 应用程序中使用。

我有一个站点范围的导航栏,需要链接到页面的特定部分,例如/home-page#section-three.

因此,即使您在说/blog,单击此链接仍将加载主页,第三部分滚动到视图中。这正是标准的<a href="/home-page#section-three>工作方式。

注意:react-router 的创建者没有给出明确的答案。他们说它正在进行中,同时使用其他人的答案。我会尽我最大的努力更新这个问题的进展和可能的解决方案,直到出现一个占主导地位的问题。

研究


如何在 react-router 中使用普通锚链接

这个问题来自 2015 年(所以react时间是 10 年前)。最受好评的答案说使用HistoryLocation而不是HashLocation. 基本上这意味着将位置存储在窗口历史记录中,而不是散列片段中。

坏消息是……即使使用 HistoryLocation(大多数教程和文档在 2016 年都这么说),锚标签仍然不起作用。


https://github.com/ReactTraining/react-router/issues/394

ReactTraining 上的一个线程,关于如何将锚链接与 react-router 一起使用。这不是确定的答案。请注意,因为大多数建议的答案已过时(例如,在 中使用“哈希”props<Link>


6个回答

React Router Hash Link对我有用,并且易于安装和实现:

$ npm install --save react-router-hash-link

在您的 component.js 中将其作为链接导入:

import { HashLink as Link } from 'react-router-hash-link';

而不是使用锚点<a>,使用<Link>

<Link to="home-page#section-three">Section three</Link>

注意:我使用HashRouter而不是Router

此解决方案不适用于以编程方式推送历史记录。
2021-03-13 13:44:21
这是我的首选解决方案
2021-03-16 13:44:21
它也有使用 typescript 的类型: npm install @types/react-router-hash-link
2021-03-16 13:44:21
这终于对我有用了。它不起作用,因为我在组件中插入了 ID,而不是 DOM 元素。
2021-03-22 13:44:21
对我来说很好用。网址看起来像localhost:8080/#!/#services
2021-03-23 13:44:21

这是我找到的一种解决方案(2016 年 10 月)。它是跨浏览器兼容的(在 Internet Explorer、Firefox、Chrome、移动 Safari 和Safari 中测试)。

您可以为onUpdate路由器提供一个属性。任何时候路由更新时都会调用它。该解决方案使用onUpdate属性检查是否存在与哈希匹配的 DOM 元素,然后在路由转换完成后滚动到该元素。

您必须使用 browserHistory 而不是 hashHistory。

答案是哈希链接 #394 中的“Rafrax”

将此代码添加到您定义的位置<Router>

import React from 'react';
import { render } from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';

const routes = (
  // your routes
);

function hashLinkScroll() {
  const { hash } = window.location;
  if (hash !== '') {
    // Push onto callback queue so it runs after the DOM is updated,
    // this is required when navigating from a different page so that
    // the element is rendered on the page before trying to getElementById.
    setTimeout(() => {
      const id = hash.replace('#', '');
      const element = document.getElementById(id);
      if (element) element.scrollIntoView();
    }, 0);
  }
}

render(
  <Router
    history={browserHistory}
    routes={routes}
    onUpdate={hashLinkScroll}
  />,
  document.getElementById('root')
)

如果您感到懒惰并且不想复制该代码,您可以使用 Anchorate,它只是为您定义了该函数。https://github.com/adjohnson916/anchorate

只是想提一下,这个解决方案将不再适用于 v. 4 of react-routeronUpdate方法已被删除。
2021-04-12 13:44:21
刚刚发布了react-routerV4的解决方案,见下文。
2021-04-12 13:44:21

这是一个不需要任何订阅或第三方软件包的简单解决方案。它应该与react-router@3一起使用react-router-dom

工作示例https : //fglet.codesandbox.io/

来源(不幸的是,它目前在编辑器中不起作用):

编辑简单的 React 锚点


#ScrollHandler 钩子示例

import { useEffect } from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router-dom";

const ScrollHandler = ({ location, children }) => {
  useEffect(
    () => {
      const element = document.getElementById(location.hash.replace("#", ""));

      setTimeout(() => {
        window.scrollTo({
          behavior: element ? "smooth" : "auto",
          top: element ? element.offsetTop : 0
        });
      }, 100);
    }, [location]);
  );

  return children;
};

ScrollHandler.propTypes = {
  children: PropTypes.node.isRequired,
  location: PropTypes.shape({
    hash: PropTypes.string,
  }).isRequired
};

export default withRouter(ScrollHandler);

#ScrollHandler 类示例

import { PureComponent } from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router-dom";

class ScrollHandler extends PureComponent {
  componentDidMount = () => this.handleScroll();

  componentDidUpdate = prevProps => {
    const { location: { pathname, hash } } = this.props;
    if (
      pathname !== prevProps.location.pathname ||
      hash !== prevProps.location.hash
    ) {
      this.handleScroll();
    }
  };

  handleScroll = () => {
    const { location: { hash } } = this.props;
    const element = document.getElementById(hash.replace("#", ""));

    setTimeout(() => {
      window.scrollTo({
        behavior: element ? "smooth" : "auto",
        top: element ? element.offsetTop : 0
      });
    }, 100);
  };

  render = () => this.props.children;
};

ScrollHandler.propTypes = {
  children: PropTypes.node.isRequired,
  location: PropTypes.shape({
    hash: PropTypes.string,
    pathname: PropTypes.string,
  })
};

export default withRouter(ScrollHandler);
抱歉不行。某些浏览器(如 Safari)不会立即更新滚动位置。
2021-03-17 13:44:21
在这个简单的例子中,计算element.offsetTop本质上会给你与window.scrollY+相同的结果element.getBoundingClientRect().top但是,如果您将元素嵌套在 a 中table,那么是的,您将希望使用后者而不是前者。例如,嵌套tablejsfiddle.net/pLuvbyx5和未嵌套的元素:jsfiddle.net/8bwj6yz3
2021-03-21 13:44:21
@MattCarlotta 假设我的页面需要超过 100 毫秒来呈现,在这种情况下它会工作吗?如果是,那么请告诉我们一些有关它的信息。你能解决这个stackoverflow.com/questions/64224547/...
2021-03-22 13:44:21
亲爱的,谢谢。为什么element.offsetTop而不是window.scrollY + element.getBoundingClientRect().top后者使其独立于最近的相对父级。
2021-03-31 13:44:21
有什么办法可以避免setTimeOut吗?我们可以在不使用 setTimeOut 的情况下实现相同的功能吗? stackoverflow.com/questions/64224547/...
2021-04-02 13:44:21

此解决方案适用于 react-router v5

import React, { useEffect } from 'react'
import { Route, Switch, useLocation } from 'react-router-dom'

export default function App() {
  const { pathname, hash, key } = useLocation();

  useEffect(() => {
    // if not a hash link, scroll to top
    if (hash === '') {
      window.scrollTo(0, 0);
    }
    // else scroll to id
    else {
      setTimeout(() => {
        const id = hash.replace('#', '');
        const element = document.getElementById(id);
        if (element) {
          element.scrollIntoView();
        }
      }, 0);
    }
  }, [pathname, hash, key]); // do this on route change

  return (
      <Switch>
        <Route exact path="/" component={Home} />
        .
        .
      </Switch>
  )
}

在组件中

<Link to="/#home"> Home </Link>
这很好用。我希望这个解决方案更加突出!
2021-03-13 13:44:21
tnx @JimmyTheCode
2021-03-28 13:44:21
很好的答案。react-router-hash-link对我没有多大作用。我进行了编辑以改进答案:(1)hash作为useEffect(2)的依赖项缺失,如果我们依赖location.key,我们可以保证它仍然会在<Link />点击时滚动到目标用例:假设用户单击<Link />然后滚动离开,<Link />如果我们不依赖key.
2021-04-02 13:44:21
啊,0ms 超时适用于本地路由更改,但从另一个页面来看,它没有足够的时间来呈现目标元素。
2021-04-11 13:44:21

只是避免使用 react-router 进行本地滚动:

document.getElementById('myElementSomewhere').scrollIntoView() 
在我的情况下,我只是想通过模仿与 href 作为#myElementId 的链接相同的方式向下滚动到 div ......这确实是最好和简单的答案,谢谢!
2021-03-16 13:44:21
理想情况下,本地滚动会通过路由器,因为这样您就可以从外部链接到文档的特定部分,但是这个答案仍然非常感谢,因为它告诉我需要在 this.props.history.listen 回调中放入哪些代码.
2021-04-10 13:44:21