В ответном маршрутизаторе v4 как ссылка на идентификатор фрагмента?
Учитывая очень простую страницу (предположив, что React и response-router @4 были импортированы):
// Current location: example.com/about
<Link to="/about/#the-team">See the team</Link>
// ... loads of content ... //
<a id="the-team"></a>
Я бы ожидал, что выше, щелкнув "См. команду", прокрутится вниз до якоря команды id. URL правильно обновляется до: example.com/about#the-team
, но он не прокручивается вниз.
Я попробовал альтернативы, такие как <a name="the-team"></a>
, но я считаю, что это больше не спецификация (и она не работает).
В github есть много проблем для response-router @v2, но они полагаются на обратный вызов обновления, присутствующий на BrowserRouter, который больше не присутствует в v4.
Ответы
Ответ 1
<ScrollIntoView>
компонент <ScrollIntoView>
который принимает идентификатор элемента для прокрутки до:
class ScrollIntoView extends React.Component {
componentDidMount() {
this.scroll()
}
componentDidUpdate() {
this.scroll()
}
scroll() {
const { id } = this.props
if (!id) {
return
}
const element = document.querySelector(id)
if (element) {
element.scrollIntoView()
}
}
render() {
return this.props.children
}
}
Вы можете обернуть в него содержимое вашего компонента представления:
const About = (props) => (
<ScrollIntoView id={props.location.hash}>
// ...
</ScrollIntoView>
)
Или вы можете создать упаковщик соответствия:
const MatchWithHash = ({ component:Component, ...props }) => (
<Match {...props} render={(props) => (
<ScrollIntoView id={props.location.hash}>
<Component {...props} />
</ScrollIntoView>
)} />
)
Использование будет:
<MatchWithHash pattern='/about' component={About} />
Полностью продуманное решение может потребовать рассмотрения крайних случаев, но я быстро проверил вышеизложенное, и это, похоже, сработало.
Редактировать:
Этот компонент теперь доступен через npm. GitHub: https://github.com/pshrmn/rrc
npm install --save rrc
import { ScrollIntoView } from 'rrc'
Ответ 2
Команда реактивного маршрутизатора, похоже, активно отслеживает эту проблему (на момент написания v4 даже не была полностью выпущена).
Как временное решение, следующее работает отлично.
РЕДАКТИРОВАТЬ 3 Этот ответ теперь можно смело игнорировать с принятым ответом. Оставленный, поскольку он решает вопрос несколько иначе.
EDIT2 Следующий метод вызывает другие проблемы, в том числе, но не ограничиваясь этим, нажатие секции A, а затем нажатие секции A снова не работает. Также, похоже, не работает с какой-либо анимацией (возникает ощущение, когда анимация начинается, но перезаписывается более поздним изменением состояния)
РЕДАКТИРОВАТЬ Обратите внимание, что следующее соединение с компонентом Miss. Все еще ищете более надежное решение
// App
<Router>
<div>
<Match pattern="*" component={HashWatcher} />
<ul>
<li><Link to="/#section-a">Section A</Link></li>
<li><Link to="/#section-b">Section B</Link></li>
</ul>
<Match pattern="/" component={Home} />
</div>
</Router>
// Home
// Stock standard mark up
<div id="section-a">
Section A content
</div>
<div id="section-b">
Section B content
</div>
Затем компонент HashWatcher будет выглядеть следующим образом. Это временный компонент, который "прослушивает" все изменения маршрута.
import { Component } from 'react';
export default class HashWatcher extends Component {
componentDidMount() {
if(this.props.location.hash !== "") {
this.scrollToId(this.hashToId(this.props.location.hash));
}
}
componentDidUpdate(prevProps) {
// Reset the position to the top on each location change. This can be followed up by the
// following hash check.
// Note, react-router correctly sets the hash and path, even if using HashHistory
if(prevProps.location.pathname !== this.props.location.pathname) {
this.scrollToTop();
}
// Initially checked if hash changed, but wasn't enough, if the user clicked the same hash
// twice - for example, clicking contact us, scroll to top, then contact us again
if(this.props.location.hash !== "") {
this.scrollToId(this.hashToId(this.props.location.hash));
}
}
/**
* Remove the leading # on the hash value
* @param string hash
* @return string
*/
hashToId(hash) {
return hash.substring(1);
}
/**
* Scroll back to the top of the given window
* @return undefined
*/
scrollToTop() {
window.scrollTo(0, 0);
}
/**
* Scroll to a given id on the page
* @param string id The id to scroll to
* @return undefined
*/
scrollToId(id) {
document.getElementById(id).scrollIntoView();
}
/**
* Intentionally return null, as we never want this component actually visible.
* @return {[type]} [description]
*/
render() {
return null;
}
}
Ответ 3
Я создал библиотеку под названиемact -scroll-manager, которая решает эту проблему и другие проблемы, связанные с положением прокрутки в React Router. Он использует эту технику для навигации по хеш-ссылкам в любом месте документа без необходимости оборачивать их по отдельности. Просто оберните ваш компонент Router в компонент ScrollManager:
class App extends React.Component {
constructor() {
super();
this.history = createHistory();
}
render() {
return (
<ScrollManager history={this.history}>
<Router history={this.history}>
...
</Router>
</ScrollManager>
);
}
}
Вы можете ссылаться на любой компонент с помощью свойства id
:
<MyComponent id="mycomp">...</MyComponent>
Просто включите id
в качестве фрагмента в вашу цель ссылки:
<Link to="#mycomp">...</Link>
Библиотека основана на HTML5 и React 16 и поддерживает React Router 4 (и, возможно, более ранние версии).