на кожному переході прокрутіть до маршрутизатора response-router


112

У мене виникає проблема під час переходу на іншу сторінку, її позиція залишатиметься такою, як раніше. Тож він не прокручуватиметься автоматично доверху. Я також намагався використовувати window.scrollTo(0, 0)на маршрутизаторі onChange. Я також використовував scrollBehavior, щоб вирішити цю проблему, але вона не спрацювала. Будь-яка пропозиція щодо цього?


Чи не могли б ви ввести логіку в componentDidMountкомпоненті нового маршруту?
Юя

просто додайте document.body.scrollTop = 0;в componentDidMountкомпоненти ви рухаєтеся до
Джон Раделл

@Kujira я вже додав scrollTo всередину componentDidMount (), але це не спрацювало.
Адріан Хартанто

@JohnRuddell Це теж не працювало.
Адріан Хартанто

Мені потрібно використовувати document.getElementById ('root'). ScrollTop = 0 для того, щоб працювати
п. 14

Відповіді:


119

Документація для React Router v4 містить зразки коду для відновлення прокрутки. Ось їх перший зразок коду, який служить рішенням для всього сайту для "прокрутки до початку", коли сторінка переходить до:

class ScrollToTop extends Component {
  componentDidUpdate(prevProps) {
    if (this.props.location !== prevProps.location) {
      window.scrollTo(0, 0)
    }
  }

  render() {
    return this.props.children
  }
}

export default withRouter(ScrollToTop)

Потім виведіть його вгорі програми, але нижче маршрутизатора:

const App = () => (
  <Router>
    <ScrollToTop>
      <App/>
    </ScrollToTop>
  </Router>
)

// or just render it bare anywhere you want, but just one :)
<ScrollToTop/>

^ скопійовано безпосередньо з документації

Очевидно, що це працює для більшості випадків, але є більше відомостей про те, як поводитися з інтерфейсами із вкладками та чому загальне рішення не було впроваджено.


3
Вам також слід скинути фокус клавіатури одночасно з прокруткою вгору. Я написав щось, щоб подбати про це: github.com/oaf-project/oaf-react-router
danielnixon

3
цей витяг з документів Because browsers are starting to handle the “default case” and apps have varying scrolling needs (like this website!), we don’t ship with default scroll management. This guide should help you implement whatever scrolling needs you have.мене засмучує, тому що всі параметри, для яких вони надають приклади коду, насправді можуть бути включені в елемент керування за допомогою налаштувань властивостей, з деякими умовами щодо поведінки за замовчуванням, які потім можна змінити. Це відчуває себе супер хакі і незавершеним, імхо. Означає, що лише розробники з просунутою реакцією можуть виконувати маршрутизацію належним чином, і немає #pitOfSuccess
snowcode

2
Схоже, маршрутизатор oaf-response-вирішує важливі проблеми доступності, які всі work-aroundsтут ігнорували. +100 для @danielnixon
сніговий код

ScrollToTop не визначено. Як імпортувати його в App.js? імпорт ScrollToTop з './ScrollToTop' не працює.
anhtv13

@ anhtv13, ви повинні поставити це як нове запитання, щоб ви могли включити код, на який ви посилаєтесь.
mtl

92

але заняття так 2018 рік

Реалізація ScrollToTop за допомогою React Hooks

ScrollToTop.js

import { useEffect } from 'react';
import { withRouter } from 'react-router-dom';

function ScrollToTop({ history }) {
  useEffect(() => {
    const unlisten = history.listen(() => {
      window.scrollTo(0, 0);
    });
    return () => {
      unlisten();
    }
  }, []);

  return (null);
}

export default withRouter(ScrollToTop);

Використання:

<Router>
  <Fragment>
    <ScrollToTop />
    <Switch>
        <Route path="/" exact component={Home} />
    </Switch>
  </Fragment>
</Router>

ScrollToTop також може бути реалізований як компонент обгортки:

ScrollToTop.js

import React, { useEffect, Fragment } from 'react';
import { withRouter } from 'react-router-dom';

function ScrollToTop({ history, children }) {
  useEffect(() => {
    const unlisten = history.listen(() => {
      window.scrollTo(0, 0);
    });
    return () => {
      unlisten();
    }
  }, []);

  return <Fragment>{children}</Fragment>;
}

export default withRouter(ScrollToTop);

Використання:

<Router>
  <ScrollToTop>
    <Switch>
        <Route path="/" exact component={Home} />
    </Switch>
  </ScrollToTop>
</Router>

2
Найкраще рішення, яке я бачив поки що в Інтернеті. Я просто трохи заплутався , чому реагувати маршрутизатори проекту не додає властивості scrollToTopдо <Route>. Здається, це дуже часто використовувана функція.
stanleyxu2005

Гарна робота чувак. Дуже допомогли.
VaibS

Який був би відповідний спосіб перевірити це?
Метт

вам слід перевірити action !== 'POP'. Слухач прийняв дію як
другий

також ви можете просто використовувати useHistory гачок замість withRouter HOC
Atombit

29

Ця відповідь стосується застарілого коду, для маршрутизатора v4 + перевірте інші відповіді

<Router onUpdate={() => window.scrollTo(0, 0)} history={createBrowserHistory()}>
  ...
</Router>

Якщо це не працює, вам слід знайти причину. Також всерединіcomponentDidMount

document.body.scrollTop = 0;
// or
window.scrollTo(0,0);

Ви можете використовувати:

componentDidUpdate() {
  window.scrollTo(0,0);
}

Ви можете додати прапор типу "scroll = false", а потім оновити:

componentDidUpdate() {
  if(this.scrolled === false){
    window.scrollTo(0,0);
    scrolled = true;
  }
}

Я взагалі оновив відповідь без getDomNode, бо насправді мені ніколи не доводилося використовувати її для прокрутки до початку. Я щойно використовуюwindow.scrollTo(0,0);
Lukas Liesis

3
onUpdateхук застарілий у
реагуючому

@DragosRizescu Будь-які вказівки щодо використання цього методу без onUpdateгачка?
Matt Voda

1
@MattVoda Ви можете прослухати зміни в самій історії, перевірити приклади: github.com/ReactTraining/history
Lukas Liesis

3
@MattVoda написав відповідь нижче про те, як цього можна досягти за допомогою
response

28

Для react-router v4 ось додаток create-react, що забезпечує відновлення прокрутки: http://router-scroll-top.surge.sh/ .

Для цього ви можете створити декор Routeкомпонента та використати методи життєвого циклу:

import React, { Component } from 'react';
import { Route, withRouter } from 'react-router-dom';

class ScrollToTopRoute extends Component {
  componentDidUpdate(prevProps) {
    if (this.props.path === this.props.location.pathname && this.props.location.pathname !== prevProps.location.pathname) {
      window.scrollTo(0, 0)
    }
  }

  render() {
    const { component: Component, ...rest } = this.props;

    return <Route {...rest} render={props => (<Component {...props} />)} />;
  }
}

export default withRouter(ScrollToTopRoute);

На цьому componentDidUpdateми можемо перевірити, коли змінюється ім’я шляху до місця розташування, зіставити його з pathреквізитом і, якщо це задовольняє, відновити прокрутку вікна.

Що круто в цьому підході, так це те, що ми можемо мати маршрути, що відновлюють прокрутку, та маршрути, які не відновлюють прокрутку.

Ось App.jsприклад того, як ви можете використовувати вищезазначене:

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import Lorem from 'react-lorem-component';
import ScrollToTopRoute from './ScrollToTopRoute';
import './App.css';

const Home = () => (
  <div className="App-page">
    <h2>Home</h2>
    <Lorem count={12} seed={12} />
  </div>
);

const About = () => (
  <div className="App-page">
    <h2>About</h2>
    <Lorem count={30} seed={4} />
  </div>
);

const AnotherPage = () => (
  <div className="App-page">
    <h2>This is just Another Page</h2>
    <Lorem count={12} seed={45} />
  </div>
);

class App extends Component {
  render() {
    return (
      <Router>
        <div className="App">
          <div className="App-header">
            <ul className="App-nav">
              <li><Link to="/">Home</Link></li>
              <li><Link to="/about">About</Link></li>
              <li><Link to="/another-page">Another Page</Link></li>
            </ul>
          </div>
          <Route exact path="/" component={Home} />
          <ScrollToTopRoute path="/about" component={About} />
          <ScrollToTopRoute path="/another-page" component={AnotherPage} />
        </div>
      </Router>
    );
  }
}

export default App;

З коду, наведеного вище, цікаво зазначити, що лише під час переходу до /aboutабо /another-pageпрокрутки до верхньої дії буде виконано. Однак при продовженні /відновлення прокрутки не відбудеться.

Повну кодову базу можна знайти тут: https://github.com/rizedr/react-router-scroll-top


1
Тільки те, що я шукав! Мені довелося додати scrollTo до componentDidMount ScrollToTopRoute, оскільки мої маршрути загорнуті в компонент Switch (я також видалив if, як хотів, щоб він
запускався

У візуалізації вашого ScrollToTopRoute не потрібно вказувати render = {props => (<Компонент {... реквізит / /))}. Просто використання {... rest} у маршруті буде працювати.
Finickyflame

Ця відповідь надала мені всі інструменти для досягнення рішення та виправлення помилки. Дуже дякую.
Null isTrue

Це працює не повністю. Якщо ви перейдете за посиланням на проект, то за замовчуванням відкрийте Home. Тепер прокрутіть Homeуниз сторінки, а тепер перейдіть до «Іншої сторінки» і не прокручуйте. Тепер, якщо ви повернетесь знову Home, ця сторінка буде прокручена до початку. Але відповідно до вашої відповіді, homeсторінку слід продовжувати прокручувати.
aditya81070

18

Примітно, що onUpdate={() => window.scrollTo(0, 0)}метод застарів.

Ось просте рішення для реакційного маршрутизатора 4+.

const history = createBrowserHistory()

history.listen(_ => {
    window.scrollTo(0, 0)  
})

<Router history={history}>

Це повинна бути правильна відповідь, якщо використовується історія. Він охоплює всі можливі варіанти, включаючи клацання на посилання на сторінку, на якій ви зараз перебуваєте. Єдиною проблемою, яку я знайшов, є scrollTo не спрацював, і її потрібно було обернути в setTimeout (window.scrollTo (0, 0), 0); Я до сих пір не розумію, чому, але привіт
sidonaldson

2
У вас будуть проблеми, коли ви додасте #та ?до кінця нашої URL-адреси. У першому випадку вам слід прокрутити ні до початку, у другому випадку взагалі не слід прокручувати
Арсеній-II

1
На жаль, це не працює з BrowserRouter.
VaibS

12

Якщо ви використовуєте React 16.8+, це просто обробляти з компонентом, який буде прокручувати вікно вгору на кожній навігації:
Ось компонент scrollToTop.js

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

export default function ScrollToTop() {
  const { pathname } = useLocation();

  useEffect(() => {
    window.scrollTo(0, 0);
  }, [pathname]);

  return null;
}

Потім виведіть його у верхній частині програми, але нижче маршрутизатора.
Тут знаходиться в app.js

import ScrollToTop from "./scrollToTop";

function App() {
  return (
    <Router>
      <ScrollToTop />
      <App />
    </Router>
  );
}

або в index.js

import ScrollToTop from "./scrollToTop";

ReactDOM.render(
    <BrowserRouter>
        <ScrollToTop />
        <App />
    </BrowserRouter>
    document.getElementById("root")
);

7

У мене була та сама проблема з моєю програмою. Використання наведеного нижче фрагмента коду допомогло мені прокрутити угорі сторінки, натиснувши наступну кнопку.

<Router onUpdate={() => window.scrollTo(0, 0)} history= {browserHistory}>
...
</Router>

Однак проблема все ще зберігалася у браузері. Після багатьох випробувань зрозумів, що це пов’язано з об’єктом історії вікна браузера, який має властивість scrollRestoration, для якого встановлено значення auto.

function scrollToTop() {
    window.scrollTo(0, 0)
    if ('scrollRestoration' in history) {
        history.scrollRestoration = 'manual';
    }
}

<Router onUpdate= {scrollToTop} history={browserHistory}>
....
</Router>

6

У компоненті нижче <Router>

Просто додайте React Hook (якщо ви не використовуєте клас React)

  React.useEffect(() => {
    window.scrollTo(0, 0);
  }, [props.location]);

4

Я хочу поділитися своїм рішенням для тих, хто використовує, react-router-dom v5оскільки жодне з цих рішень v4 не допомогло мені.

Що вирішило мою проблему, так це встановити response-router-scroll-top і помістити обгортку приблизно <App />так:

const App = () => (
  <Router>
    <ScrollToTop>
      <App/>
    </ScrollToTop>
  </Router>
)

і це все! це спрацювало!


4

Хуки можна компонувати, і з React Router v5.1 у нас є useHistory()гачок. Отже, виходячи з відповіді @ zurfyx, я створив багаторазовий гачок для цієї функціональності:

// useScrollTop.ts
import { useHistory } from 'react-router-dom';
import { useEffect } from 'react';

/*
 * Registers a history listener on mount which
 * scrolls to the top of the page on route change
 */
export const useScrollTop = () => {
    const history = useHistory();
    useEffect(() => {
        const unlisten = history.listen(() => {
            window.scrollTo(0, 0);
        });
        return unlisten;
    }, [history]);
};

4

Гак React, який ви можете додати до свого компоненту Route. Використання useLayoutEffectзамість користувацьких прослуховувачів.

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

export default function Routes() {
  const location = useLocation();
  // Scroll to top if path changes
  useLayoutEffect(() => {
    window.scrollTo(0, 0);
  }, [location.pathname]);

  return (
      <Switch>
        <Route exact path="/">

        </Route>
      </Switch>
  );
}

Оновлення : оновлено для використання useLayoutEffectзамість того useEffect, щоб зменшити візуальний збиток. Приблизно це означає:

  • useEffect: візуалізація компонентів -> фарба на екран -> прокрутка вгору (ефект запуску)
  • useLayoutEffect: візуалізація компонентів -> прокрутка вгору (ефект запуску) -> фарба на екран

Залежно від того, чи завантажуєте ви дані (думайте про вертушки), чи у вас є анімація переходу сторінки, це useEffectможе працювати краще для вас.


3

Моє рішення: компонент, який я використовую в компонентах своїх екранів (там, де я хочу прокрутити до вершини).

import { useLayoutEffect } from 'react';

const ScrollToTop = () => {
    useLayoutEffect(() => {
        window.scrollTo(0, 0);
    }, []);

    return null;
};

export default ScrollToTop;

Це зберігає положення прокрутки при поверненні назад. Використання useEffect () було для мене помилковим, коли при поверненні документ прокручувався вгору, а також мав ефект моргання, коли маршрут було змінено у вже прокрученому документі.


3

Гачки React 2020 :)

import React, { useLayoutEffect } from 'react';
import { useLocation } from 'react-router-dom';

const ScrollToTop: React.FC = () => {
  const { pathname } = useLocation();
  useLayoutEffect(() => {
    window.scrollTo(0, 0);
  }, [pathname]);

  return null;
};

export default ScrollToTop;

Ви можете пояснити, будь ласка, цю частину? const ScrollToTop: React.FC = () => {Я не розумію, що ScrollToTop: React.FCозначає
beeftosino

1
визначення машинопису
Kepro

2

Я написав компонент вищого порядку, який називається withScrollToTop. Цей ГОС бере два прапори:

  • onComponentWillMount- Незалежно від того , щоб перейти до початку навігації по ( componentWillMount)
  • onComponentDidUpdate- Чи слід прокручувати верх до оновлення ( componentDidUpdate). Цей прапор необхідний у випадках, коли компонент не демонтується, але відбувається навігаційна подія, наприклад, від /users/1до /users/2.

// @flow
import type { Location } from 'react-router-dom';
import type { ComponentType } from 'react';

import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';

type Props = {
  location: Location,
};

type Options = {
  onComponentWillMount?: boolean,
  onComponentDidUpdate?: boolean,
};

const defaultOptions: Options = {
  onComponentWillMount: true,
  onComponentDidUpdate: true,
};

function scrollToTop() {
  window.scrollTo(0, 0);
}

const withScrollToTop = (WrappedComponent: ComponentType, options: Options = defaultOptions) => {
  return class withScrollToTopComponent extends Component<Props> {
    props: Props;

    componentWillMount() {
      if (options.onComponentWillMount) {
        scrollToTop();
      }
    }

    componentDidUpdate(prevProps: Props) {
      if (options.onComponentDidUpdate &&
        this.props.location.pathname !== prevProps.location.pathname) {
        scrollToTop();
      }
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
};

export default (WrappedComponent: ComponentType, options?: Options) => {
  return withRouter(withScrollToTop(WrappedComponent, options));
};

Щоб використовувати його:

import withScrollToTop from './withScrollToTop';

function MyComponent() { ... }

export default withScrollToTop(MyComponent);

1

Ось ще один метод.

Для response-router v4 ви також можете прив'язати слухача до зміни в історії подій, таким чином:

let firstMount = true;
const App = (props) => {
    if (typeof window != 'undefined') { //incase you have server-side rendering too             
        firstMount && props.history.listen((location, action) => {
            setImmediate(() => window.scrollTo(0, 0)); // ive explained why i used setImmediate below
        });
        firstMount = false;
    }

    return (
        <div>
            <MyHeader/>            
            <Switch>                            
                <Route path='/' exact={true} component={IndexPage} />
                <Route path='/other' component={OtherPage} />
                // ...
             </Switch>                        
            <MyFooter/>
        </div>
    );
}

//mounting app:
render((<BrowserRouter><Route component={App} /></BrowserRouter>), document.getElementById('root'));

Рівень прокрутки буде встановлено на 0 без setImmediate()занадто, якщо маршрут буде змінено, натиснувши на посилання, але якщо користувач натисне кнопку назад у браузері, він не працюватиме, оскільки браузер скидає рівень прокрутки вручну до попереднього рівня, коли натискається кнопка назад. , отже, за допомогою setImmediate()ми призводимо до того, що наша функція буде виконана після того, як браузер закінчить скидання рівня прокрутки, що дає нам бажаний ефект.


1

з React router dom v4 ви можете використовувати

створити компонент scrollToTopComponent, як показано нижче

class ScrollToTop extends Component {
    componentDidUpdate(prevProps) {
      if (this.props.location !== prevProps.location) {
        window.scrollTo(0, 0)
      }
    }

    render() {
      return this.props.children
    }
}

export default withRouter(ScrollToTop)

або якщо ви використовуєте вкладки, використовуйте щось на зразок наведеного нижче

class ScrollToTopOnMount extends Component {
    componentDidMount() {
      window.scrollTo(0, 0)
    }

    render() {
      return null
    }
}

class LongContent extends Component {
    render() {
      <div>
         <ScrollToTopOnMount/>
         <h1>Here is my long content page</h1>
      </div>
    }
}

// somewhere else
<Route path="/long-content" component={LongContent}/>

сподіваємось, це допомагає отримати більше відновлення прокрутки прокрутки, там документи зможуть реагувати на відновлення маршрутизатора


0

Я виявив, що ReactDOM.findDomNode(this).scrollIntoView()це працює. Я помістив його всередину componentDidMount().


1
це незадокументовані внутрішні речі, які можуть змінитись без помітності, і ваш код перестане працювати.
Lukas Liesis

0

Для менших додатків, що мають 1-4 маршрути, ви можете спробувати його зламати з перенаправленням до верхнього елемента DOM з #id, а не просто маршрутом. Тоді немає необхідності загортати маршрути в ScrollToTop або використовувати методи життєвого циклу.


0

У своєму router.jsпросто додайте цю функцію в routerоб’єкт. Це зробить роботу.

scrollBehavior() {
        document.getElementById('app').scrollIntoView();
    },

Подобається це,

**Routes.js**

import vue from 'blah!'
import Router from 'blah!'

let router = new Router({
    mode: 'history',
    base: process.env.BASE_URL,
    scrollBehavior() {
        document.getElementById('app').scrollIntoView();
    },
    routes: [
            { url: "Solar System" },
            { url: "Milky Way" },
            { url: "Galaxy" },
    ]
});

-1
render() {
    window.scrollTo(0, 0)
    ...
}

Це може бути простим рішенням у випадку, якщо реквізит не змінено, а componentDidUpdate () не спрацьовує.


-11

Це хакі (але працює): я просто додаю

window.scrollTo (0,0);

render ();


2
тому він буде прокручуватися вгору кожного разу, коли стан змінюється і відбувається рендерінг, а не коли змінюється навігація. Просто перевірте мою відповідь
Лукас Ліїс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.