gulir react-router ke atas pada setiap transisi

120

Saya mengalami masalah saat menavigasi ke halaman lain, posisinya akan tetap seperti halaman sebelumnya. Jadi itu tidak akan bergulir ke atas secara otomatis. Saya juga mencoba menggunakan window.scrollTo(0, 0)di onChangerouter. Saya juga pernah scrollBehaviormemperbaiki masalah ini tetapi tidak berhasil. Ada saran tentang ini?

adrian hartanto
sumber
Bisakah Anda tidak melakukan logika di componentDidMountkomponen rute baru?
Yuya
hanya menambahkan document.body.scrollTop = 0;dalam componentDidMountkomponen Anda pindah ke
John Ruddell
@ Kujira Saya sudah menambahkan scrollTo di dalam componentDidMount () tetapi tidak berhasil.
adrian hartanto
@JohnRuddell Itu tidak berhasil juga.
adrian hartanto
Saya harus menggunakan document.getElementById ('root'). ScrollTop = 0 agar dapat bekerja
Tn. 14

Jawaban:

123

Dokumentasi untuk React Router v4 berisi contoh kode untuk pemulihan gulir. Berikut adalah contoh kode pertama mereka, yang berfungsi sebagai solusi seluruh situs untuk "gulir ke atas" saat halaman dinavigasi ke:

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)

Kemudian render di bagian atas aplikasi Anda, tetapi di bawah Router:

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

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

^ disalin langsung dari dokumentasi

Jelas ini berfungsi untuk banyak kasus, tetapi ada lebih banyak tentang cara menangani antarmuka tab dan mengapa solusi umum belum diterapkan.

mtl
sumber
3
Anda juga harus menyetel ulang fokus keyboard pada saat yang sama dengan menggulir ke atas. Saya menulis sesuatu untuk mengurusnya: github.com/oaf-project/oaf-react-router
danielnixon
3
Ekstrak dari dokumen ini 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.membuat saya sedih, karena semua opsi yang mereka berikan untuk contoh kode, sebenarnya dapat dimasukkan ke dalam kontrol melalui pengaturan properti, dengan beberapa konvensi seputar perilaku default yang kemudian dapat diubah. Ini terasa super hacky dan belum selesai, imho. Berarti hanya pengembang react lanjutan yang dapat melakukan perutean dengan benar dan tidak ada #pitOfSuccess
snowcode
2
oaf-react-router tampaknya mengatasi masalah aksesibilitas penting yang work-aroundstelah diabaikan oleh semua pihak di sini. +100 untuk @danielnixon
kode salju
ScrollToTop tidak ditentukan. Bagaimana cara mengimpornya di App.js? impor ScrollToTop dari './ScrollToTop' tidak berfungsi.
anhtv13
@ anhtv13, Anda harus menanyakannya sebagai pertanyaan baru sehingga Anda dapat menyertakan kode yang Anda rujuk.
mtl
100

tetapi kelasnya begitu 2018

Implementasi ScrollToTop dengan 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);

Pemakaian:

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

ScrollToTop juga dapat diimplementasikan sebagai komponen pembungkus:

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);

Pemakaian:

<Router>
  <ScrollToTop>
    <Switch>
        <Route path="/" exact component={Home} />
    </Switch>
  </ScrollToTop>
</Router>
zurfyx
sumber
2
Solusi terbaik yang saya lihat sejauh ini di internet. Saya hanya sedikit bingung mengapa proyek react-router tidak menambahkan properti scrollToTopke <Route>. Sepertinya fitur ini sangat sering digunakan.
stanleyxu2005
Kerja bagus bung. Banyak membantu.
VaibS
Apa cara yang tepat untuk menguji unit ini?
Matt
Anda harus memeriksa action !== 'POP'. Pendengar menerima tindakan sebagai
argumen ke-
juga, Anda bisa menggunakan hook useHistory alih-alih withRouter HOC
Atombit
29

Jawaban ini untuk kode lama, untuk router v4 + periksa jawaban lain

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

Jika tidak berhasil, Anda harus menemukan alasannya. Juga di dalamcomponentDidMount

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

Anda bisa menggunakan:

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

Anda dapat menambahkan beberapa bendera seperti "scrolled = false" dan kemudian memperbarui:

componentDidUpdate() {
  if(this.scrolled === false){
    window.scrollTo(0,0);
    scrolled = true;
  }
}
Lukas Liesis
sumber
Saya telah memperbarui jawaban tanpa getDomNode sama sekali karena sebenarnya saya tidak pernah menggunakannya untuk menggulir ke atas. Saya baru saja menggunakanwindow.scrollTo(0,0);
Lukas Liesis
3
onUpdatehook tidak digunakan lagi di react-router v4 - hanya ingin menunjukkannya
Dragos Rizescu
@DragosRizescu Ada panduan untuk menggunakan metode ini tanpa onUpdatekail?
Matt Voda
1
@MattVoda Anda dapat mendengarkan perubahan pada sejarah itu sendiri, periksa contoh: github.com/ReactTraining/history
Lukas Liesis
3
@MattVoda menulis jawaban di bawah ini tentang bagaimana Anda dapat mencapainya dengan react-router-v4
Dragos Rizescu
28

Untuk react-router v4 , berikut adalah aplikasi create-react yang mencapai pemulihan gulir: http://router-scroll-top.surge.sh/ .

Untuk mencapai ini, Anda dapat membuat menghias Routekomponen dan memanfaatkan metode siklus hidup:

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);

Di bagian componentDidUpdatekita dapat memeriksa kapan nama path lokasi berubah dan mencocokkannya dengan pathprop dan, jika mereka puas, mengembalikan window scroll.

Yang keren dari pendekatan ini, adalah kita dapat memiliki rute yang memulihkan gulir dan rute yang tidak mengembalikan gulir.

Berikut adalah App.jscontoh bagaimana Anda dapat menggunakan yang di atas:

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;

Dari kode di atas, yang menarik untuk ditunjukkan adalah bahwa hanya ketika menavigasi ke /aboutatau /another-pagetindakan gulir ke atas yang akan dilakukan. Namun ketika /tidak ada pemulihan gulir yang akan terjadi.

Seluruh basis kode dapat ditemukan di sini: https://github.com/rizedr/react-router-scroll-top

Dragos Rizescu
sumber
1
Hanya apa yang saya cari! Saya harus menambahkan scrollTo di componentDidMount dari ScrollToTopRoute, karena rute saya dibungkus dalam komponen Switch (saya juga menghapus jika seperti yang saya inginkan untuk diaktifkan pada setiap mount)
tocallaghan
Dalam render ScrollToTopRoute, Anda tidak perlu menentukan render = {props => (<Component {... props} />)}. Cukup menggunakan {... rest} di rute akan berhasil.
Finickyflame
Jawaban ini memberi saya semua alat untuk mencapai solusi dan memperbaiki bug saya. Terima kasih banyak.
Null isTrue
Itu tidak bekerja sepenuhnya. Jika Anda mengikuti tautan proyek maka itu akan menjadi default buka Home. Sekarang gulir ke bagian bawah Homedan sekarang pergi ke 'AnotherPage' dan jangan gulir. Sekarang jika Anda kembali ke Homehalaman ini, halaman ini akan digulir ke atas. Tetapi menurut jawaban Anda, homehalaman harus tetap di-scroll.
aditya81070
18

Perlu dicatat bahwa onUpdate={() => window.scrollTo(0, 0)}metode ini sudah usang.

Berikut adalah solusi sederhana untuk react-router 4+.

const history = createBrowserHistory()

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

<Router history={history}>
azurebean
sumber
Ini harus menjadi jawaban yang benar jika sejarah digunakan. Ini mencakup semua kejadian termasuk mengklik link ke halaman Anda saat ini. Satu-satunya masalah yang saya temukan adalah scrollTo tidak menyala dan perlu dibungkus dalam setTimeout (window.scrollTo (0, 0), 0); Saya masih tidak mengerti mengapa tapi hei ho
sidonalds
3
Anda akan mengalami masalah saat menambahkan #dan ?ke akhir url kami. Dalam kasus pertama Anda harus menggulir tidak ke atas dalam kasus kedua Anda tidak boleh menggulir sama sekali
Arseniy-II
1
Sayangnya, ini tidak berfungsi dengan BrowserRouter.
VaibS
14

Jika Anda menjalankan React 16.8+, ini mudah ditangani dengan komponen yang akan menggulir jendela ke atas pada setiap navigasi:
Ini di komponen 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;
}

Kemudian render di bagian atas aplikasi Anda, tetapi di bawah Router, di
sini ada di app.js

import ScrollToTop from "./scrollToTop";

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

atau di index.js

import ScrollToTop from "./scrollToTop";

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

React Hook yang dapat Anda tambahkan ke komponen Route Anda. Menggunakan useLayoutEffectalih-alih pendengar khusus.

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>
  );
}

Pembaruan : Diperbarui untuk digunakan useLayoutEffectsebagai pengganti useEffect, untuk mengurangi visual jank. Secara kasar ini diterjemahkan menjadi:

  • useEffect: membuat komponen -> cat ke layar -> gulir ke atas (efek run)
  • useLayoutEffect: membuat komponen -> gulir ke atas (efek run) -> cat ke layar

Bergantung pada apakah Anda memuat data (pikirkan pemintal) atau jika Anda memiliki animasi transisi halaman, useEffectmungkin bekerja lebih baik untuk Anda.

Gabe M
sumber
7

Saya memiliki masalah yang sama dengan aplikasi saya, menggunakan potongan kode di bawah ini membantu saya menggulir ke bagian atas halaman dengan mengklik tombol berikutnya.

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

Namun, masalah masih berlanjut di browser belakang. Setelah banyak percobaan, saya menyadari bahwa ini karena objek riwayat jendela browser, yang memiliki properti scrollRestoration yang disetel ke auto.Menyetel ini ke manual memecahkan masalah saya.

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

<Router onUpdate= {scrollToTop} history={browserHistory}>
....
</Router>
Jhalaa Chinoy
sumber
7

Dalam komponen di bawah ini <Router>

Cukup tambahkan React Hook (jika Anda tidak menggunakan kelas React)

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

Saya ingin membagikan solusi saya untuk mereka yang menggunakan react-router-dom v5karena tidak ada solusi v4 yang berhasil untuk saya.

Apa yang memecahkan masalah saya adalah menginstal react-router-scroll-top dan meletakkan pembungkusnya <App />seperti ini:

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

dan hanya itu! itu berhasil!

Luis Febro
sumber
4

Hooks dapat disusun, dan sejak React Router v5.1 kami memiliki useHistory()hook. Jadi berdasarkan jawaban @ zurfyx, saya telah membuat pengait yang dapat digunakan kembali untuk fungsi ini:

// 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]);
};
Kris Dover
sumber
3

Solusi saya: komponen yang saya gunakan di komponen layar saya (di mana saya ingin gulir ke atas).

import { useLayoutEffect } from 'react';

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

    return null;
};

export default ScrollToTop;

Ini mempertahankan posisi gulir saat kembali. Menggunakan useEffect () adalah buggy bagi saya, ketika kembali dokumen akan bergulir ke atas dan juga memiliki efek berkedip ketika rute diubah dalam dokumen yang sudah digulir.

Sergiu
sumber
3

React hooks 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;
Kepro
sumber
bisakah Anda menjelaskan bagian ini? const ScrollToTop: React.FC = () => {Saya tidak mengerti apa ScrollToTop: React.FCartinya
beeftosino
1
definisi naskah
Kepro
2

Saya menulis Komponen Tingkat Tinggi bernama withScrollToTop. HOC ini mengambil dua tanda:

  • onComponentWillMount- Apakah akan menggulir ke atas saat navigasi ( componentWillMount)
  • onComponentDidUpdate- Apakah akan menggulir ke atas saat update ( componentDidUpdate). Bendera ini diperlukan jika komponen tidak dilepas tetapi peristiwa navigasi terjadi, misalnya, dari /users/1ke /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));
};

Untuk menggunakannya:

import withScrollToTop from './withScrollToTop';

function MyComponent() { ... }

export default withScrollToTop(MyComponent);
Yangshun Tay
sumber
1

Ini metode lain.

Untuk react-router v4 Anda juga dapat mengikat pendengar untuk mengubah peristiwa sejarah, dengan cara berikut:

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'));

Level gulir akan diatur ke 0 tanpa setImmediate()juga jika rute diubah dengan mengklik tautan tetapi jika pengguna menekan tombol kembali pada browser maka itu tidak akan berfungsi karena browser mengatur ulang level gulir secara manual ke level sebelumnya ketika tombol kembali ditekan , jadi dengan menggunakan setImmediate()kami menyebabkan fungsi kami dijalankan setelah browser selesai mengatur ulang level gulir sehingga memberi kami efek yang diinginkan.

Zeus
sumber
1

dengan router React dom v4 Anda dapat menggunakan

buat komponen scrollToTopComponent seperti di bawah ini

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)

atau jika Anda menggunakan tab, gunakan sesuatu seperti di bawah ini

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}/>

semoga ini membantu untuk lebih lanjut tentang restorasi gulir, ada docs hare react router dom scroll restoration

pengguna2907964
sumber
0

Saya menemukan itu ReactDOM.findDomNode(this).scrollIntoView()berhasil. Saya menempatkannya di dalam componentDidMount().

adrian hartanto
sumber
1
itu hal-hal internal yang tidak terdokumentasi yang mungkin berubah tanpa memperhatikan dan kode Anda akan berhenti bekerja.
Lukas Liesis
0

Untuk aplikasi yang lebih kecil, dengan 1-4 rute, Anda dapat mencoba meretasnya dengan pengalihan ke elemen DOM teratas dengan #id, bukan hanya sebuah rute. Maka tidak perlu menggabungkan Rute di ScrollToTop atau menggunakan metode siklus hidup.

yakato
sumber
0

Di Anda router.js, tambahkan saja fungsi ini di routerobjek. Ini akan berhasil.

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

Seperti ini,

**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" },
    ]
});
Debu Shinobi
sumber
-1
render() {
    window.scrollTo(0, 0)
    ...
}

Bisa menjadi solusi sederhana jika props tidak diubah dan componentDidUpdate () tidak aktif.

Ingvar Lond
sumber
-11

Ini hacky (tapi berhasil): Saya hanya menambahkan

window.scrollTo (0,0);

untuk merender ();

JJ
sumber
2
jadi itu akan menggulir ke atas setiap kali perubahan status dan render ulang terjadi, bukan ketika navigasi berubah. Periksa saja jawaban saya
Lukas Liesis