Как люди обрабатывают восстановление прокрутки с помощью action-router v4?

У меня возникают проблемы с позициями прокрутки на кнопке "Назад" (история всплывающих окон) при использовании "реактивного маршрутизатора". React router v4 не обрабатывает управление прокруткой из коробки, потому что браузеры реализуют некоторое автоматическое поведение прокрутки. Это замечательно, если высота окна браузера слишком сильно изменяется с одного взгляда на другое. Я реализовал компонент ScrollToTop, как описано здесь: https://reacttraining.com/react-router/web/guides/scroll-restoration

Это отлично работает. Когда вы нажимаете ссылку и переходите к другому компоненту, браузер прокручивается вверх (например, обычный веб-сайт, обработанный сервером). Проблема возникает, когда вы возвращаетесь (через кнопку назад браузера) к виду с гораздо более высокой высотой окна. Кажется, что (хром) пытается перейти в положение прокрутки предыдущей страницы, прежде чем реакция отобразит контент (и высоту браузера). Это приводит к тому, что прокрутка будет идти так далеко, насколько это возможно, исходя из высоты представления. Представьте этот сценарий:

View1: Длинный список фильмов (высота окна 3500 пикселей).
(клик по клику)
View2: подробный вид выбранного фильма (высота окна: 1000 пикселей).
(Нажата кнопка "Назад в браузере" )
Назад к просмотру 1, но положение прокрутки не может превышать 1000 пикселей, потому что хром пытается установить позицию, прежде чем реагировать, отобразит длинный список фильмов.

По какой-то причине это только проблема в Chrome. Похоже, Firefox и Safari справляются с этим. Интересно, есть ли у кого-то еще эта проблема, и как вы, ребята, обычно обрабатываете восстановление прокрутки в React.

Примечание: все фильмы импортируются из sampleMovies.js - поэтому я не ожидаю ответа API в моем примере.

Ответ 1

Как вы обрабатываете восстановление прокрутки?

Оказывается, у браузеров есть реализации history.scrollRestoration.

Может быть, вы можете это использовать? Проверьте эти ссылки.

https://developer.mozilla.org/en/docs/Web/API/History#Specifications https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration

Кроме того, я нашел модуль npm, который мог бы легко обрабатывать восстановление прокрутки, но эта библиотека работает только с реагирующим маршрутизатором v3 и ниже

https://www.npmjs.com/package/react-router-restore-scroll https://github.com/ryanflorence/react-router-restore-scroll

Надеюсь, это поможет.

Ответ 2

Обратите внимание, что history.scrollRestoration - это просто способ отключить в браузере автоматические попытки восстановления прокрутки, которые в основном не работают для одностраничных приложений, чтобы они не мешали работе приложения. В дополнение к переключению на ручное восстановление с помощью прокрутки вам понадобится какая-то библиотека, которая обеспечивает интеграцию между API истории браузера, рендерингом React и позицией прокрутки окна и любыми элементами прокручиваемого блока.

После того, как я не смог найти такую библиотеку восстановления прокрутки для React Router 4, я создал одну из них, названную реагировать-прокрутка-менеджер. Он поддерживает прокрутку до вершины при переходе на новое место (также известное как толчок истории) и восстановление прокрутки назад/вперед (также известное как история). Помимо прокрутки окна, оно может прокручивать любой вложенный элемент, который вы переносите в компонент ElementScroller. Он также поддерживает отложенный/асинхронный рендеринг с использованием MutationObserver для просмотра содержимого окна/элемента до указанного пользователем предела времени. Эта поддержка отложенного рендеринга применяется к восстановлению прокрутки, а также к прокрутке к определенному элементу с использованием хэш-ссылки.

npm install react-scroll-manager

import React from 'react';
import { Router } from 'react-router-dom';
import { ScrollManager, WindowScroller, ElementScroller } from 'react-scroll-manager';
import { createBrowserHistory as createHistory } from 'history';

class App extends React.Component {
  constructor() {
    super();
    this.history = createHistory();
  }
  render() {
    return (
      <ScrollManager history={this.history}>
        <Router history={this.history}>
          <WindowScroller>
            <ElementScroller scrollKey="nav">
              <div className="nav">
                ...
              </div>
            </ElementScroller>
            <div className="content">
              ...
            </div>
          </WindowScroller>
        </Router>
      </ScrollManager>
    );
  }
}

Обратите внимание, что требуется браузер HTML5 (10+ для IE) и React 16. HTML5 предоставляет API истории, а библиотека использует современные API Context и Ref из React 16.

Ответ 3

Я использовал localStorage для отслеживания положения прокрутки - не уверен, что это подойдет для всех ситуаций.

В этом примере есть страница компании с набором магазинов, а каждый магазин имеет набор витрин. Мне нужно было отслеживать положение прокрутки в витринах, поэтому я сохранил это в ключе storeScrollTop. Было 6 строк кода для добавления.

company.jsx:

// click on a store link
const handleClickStore = (evt) => {
  window.localStorage.removeItem('storeScrollTop') // <-- reset scroll value
  const storeId = evt.currentTarget.id
  history.push('/store/${storeId}')
}

store.jsx:

// initialize store page
React.useEffect(() => {
  // fetch displays
  getStoreDisplays(storeId).then(objs => setObjs(objs)).then(() => {
    // get the 'store' localstorage scrollvalue and scroll to it
    const scrollTop = Number(window.localStorage.getItem('storeScrollTop') || '0')
    const el = document.getElementsByClassName('contents')[0]
    el.scrollTop = scrollTop
  })
}, [storeId])

// click on a display link    
const handleClickDisplay = (evt) => {
  // save the scroll pos for return visit
  const el = document.getElementsByClassName('contents')[0]
  window.localStorage.setItem('storeScrollTop', String(el.scrollTop))
  // goto the display
  const displayId = evt.currentTarget.id
  history.push('/display/${displayId}')
}