繁体   English   中英

使用锚点 react-router

[英]Use anchors with react-router

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

详情

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

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

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

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

研究


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

这个问题是从 2015 年开始的(所以 10 年前的反应时间)。 投票最多的答案是使用HistoryLocation而不是HashLocation 基本上,这意味着将位置存储在 window 历史记录中,而不是存储在 hash 片段中。

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


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

ReactTraining 上关于如何使用 react-router 的锚链接的线程。这不是确定的答案。 要小心,因为大多数建议的答案都已过时(例如,在<Link>中使用“hash”道具)


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

这是我找到的一种解决方案(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

这是一个不需要任何订阅或第 3 方软件包的简单解决方案。 应该与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);

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

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

export default function App() {
  const { pathname, hash } = 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]); // do this on route change

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

在组件中

<Link to="/#home"> Home </Link>

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

document.getElementById('myElementSomewhere').scrollIntoView() 

https://stackoverflow.com/a/40280486/515585的问题是有时具有 id 的元素仍然被渲染或加载,如果该部分取决于某些异步操作。 以下函数将尝试通过 id 查找元素并导航到它并每 100 毫秒重试一次,直到达到最多 50 次重试:

scrollToLocation = () => {
  const { hash } = window.location;
  if (hash !== '') {
    let retries = 0;
    const id = hash.replace('#', '');
    const scroll = () => {
      retries += 0;
      if (retries > 50) return;
      const element = document.getElementById(id);
      if (element) {
        setTimeout(() => element.scrollIntoView(), 0);
      } else {
        setTimeout(scroll, 100);
      }
    };
    scroll();
  }
}

我将 Don P 的解决方案(见上文)改编为react-router 4(2019 年 1 月),因为<Router>上不再有onUpdate道具。

import React from 'react';
import * as ReactDOM from 'react-dom';
import { Router, Route } from 'react-router';
import { createBrowserHistory } from 'history';

const browserHistory = createBrowserHistory();

browserHistory.listen(location => {
    const { hash } = 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
        );
    }
});

ReactDOM.render(
  <Router history={browserHistory}>
      // insert your routes here...
  />,
  document.getElementById('root')
)
<Link to='/homepage#faq-1'>Question 1</Link>
useEffect(() => {
    const hash = props.history.location.hash
    if (hash && document.getElementById(hash.substr(1))) {
        // Check if there is a hash and if an element with that id exists
        document.getElementById(hash.substr(1)).scrollIntoView({behavior: "smooth"})
    }
}, [props.history.location.hash]) // Fires when component mounts and every time hash changes

另一种选择:react-scrollchor https://www.npmjs.com/package/react-scrollchor

react-scrollchor:一个 React 组件,用于滚动到具有平滑动画的 #hash 链接。 Scrollchor 是 Scroll 和 Anchor 的混合体

注意:它不使用反应路由器

对于简单的页内导航,您可以添加这样的内容,尽管它不处理初始化页面 -

// handle back/fwd buttons
function hashHandler() {
  const id = window.location.hash.slice(1) // remove leading '#'
  const el = document.getElementById(id)
  if (el) {
    el.scrollIntoView()
  }
}
window.addEventListener('hashchange', hashHandler, false)

创建一个 scrollHandle 组件

    import { useEffect } from "react";
    import { useLocation } from "react-router-dom";

    export const ScrollHandler = ({ children}) => {

        const { pathname, hash } = useLocation()

        const handleScroll = () => {

            const element = document.getElementById(hash.replace("#", ""));

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

        useEffect(() => {
            handleScroll()
        }, [pathname, hash])

        return children
    }

将 ScrollHandler 组件直接导入到您的app.js文件中,或者您可以使用withScrollHandler创建一个高阶组件并将您的应用程序导出为withScrollHandler(App)

并在链接<Link to='/page#section'>Section</Link><Link to='#section'>Section</Link>

并在您的部分组件中添加id="section"

我知道它很旧但是在我最新的 react-router-dom@6.4.4 中,这个简单的属性reloadDocument正在工作:

div>
 <Link to="#result" reloadDocument>GO TO ⬇  (Navigate to Same Page) </Link>
</div>
<div id='result'>CLICK 'GO TO' ABOVE TO REACH HERE</div>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM