Mam problem podczas przechodzenia na inną stronę, jej pozycja pozostanie taka sama, jak poprzednia strona. Więc nie przewinie się automatycznie do góry. Próbowałem też użyć window.scrollTo(0, 0)
na onChange
routerze. Kiedyś też scrollBehavior
naprawiałem ten problem, ale to nie zadziałało. Jakieś sugestie na ten temat?
javascript
reactjs
react-router
react-router-redux
adrian hartanto
źródło
źródło
componentDidMount
komponentu nowej trasy?document.body.scrollTop = 0;
wcomponentDidMount
składowej jesteś poruszającego sięOdpowiedzi:
Dokumentacja dla React Router v4 zawiera przykłady kodu do przywracania przewijania. Oto ich pierwszy przykładowy kod, który służy jako rozwiązanie w całej witrynie do „przewijania do góry” po przejściu na stronę:
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)
Następnie wyrenderuj go u góry aplikacji, ale poniżej routera:
const App = () => ( <Router> <ScrollToTop> <App/> </ScrollToTop> </Router> ) // or just render it bare anywhere you want, but just one :) <ScrollToTop/>
^ skopiowane bezpośrednio z dokumentacji
Oczywiście działa to w większości przypadków, ale jest więcej informacji o tym, jak radzić sobie z interfejsami z zakładkami i dlaczego nie zaimplementowano ogólnego rozwiązania.
źródło
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.
mnie zasmuca, ponieważ wszystkie opcje, dla których podają przykłady kodu, mogłyby zostać wprowadzone do kontrolki poprzez ustawienia właściwości, z pewnymi konwencjami dotyczącymi domyślnego zachowania, które można następnie zmienić. To jest super hacky i niedokończone, imho. Oznacza to, że tylko zaawansowani deweloperzy reagują na prawidłowe wyznaczanie tras i nie ma #pitOfSuccesswork-arounds
tutaj zignorowali. +100 dla @danielnixonale zajęcia są takie 2018
Implementacja ScrollToTop z Hookami React
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);
Stosowanie:
<Router> <Fragment> <ScrollToTop /> <Switch> <Route path="/" exact component={Home} /> </Switch> </Fragment> </Router>
ScrollToTop można również zaimplementować jako komponent opakowania:
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);
Stosowanie:
<Router> <ScrollToTop> <Switch> <Route path="/" exact component={Home} /> </Switch> </ScrollToTop> </Router>
źródło
scrollToTop
do<Route>
. Wydaje się, że jest to bardzo często używana funkcja.action !== 'POP'
. Słuchacz akceptuje akcję jakoTa odpowiedź dotyczy starszego kodu. W przypadku routera v4 + sprawdź inne odpowiedzi
<Router onUpdate={() => window.scrollTo(0, 0)} history={createBrowserHistory()}> ... </Router>
Jeśli to nie działa, powinieneś znaleźć przyczynę. Również wewnątrz
componentDidMount
document.body.scrollTop = 0; // or window.scrollTo(0,0);
możesz użyć:
componentDidUpdate() { window.scrollTo(0,0); }
możesz dodać flagę typu „scrolled = false”, a następnie w aktualizacji:
componentDidUpdate() { if(this.scrolled === false){ window.scrollTo(0,0); scrolled = true; } }
źródło
window.scrollTo(0,0);
onUpdate
hook jest przestarzały w react-router v4 - po prostu chcę na to zwrócić uwagęonUpdate
haka?W przypadku routera React-router v4 dostępna jest aplikacja create -reak-app, która umożliwia przywrócenie przewijania: http://router-scroll-top.surge.sh/ .
Aby to osiągnąć, możesz stworzyć dekorację
Route
komponentu i wykorzystać metody cyklu życia: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);
Na ekranie
componentDidUpdate
możemy sprawdzić, kiedy zmienia się ścieżka dostępu do lokalizacji i dopasować ją do właściwości,path
a jeśli te są spełnione, przywrócić przewijanie okna.Fajne w tym podejściu jest to, że możemy mieć trasy, które przywracają przewijanie i trasy, które nie przywracają przewijania.
Oto
App.js
przykład, jak możesz wykorzystać powyższe: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;
Z powyższego kodu warto zauważyć, że tylko podczas przechodzenia do
/about
lub/another-page
przewijania do góry zostanie wykonana akcja. Jednak podczas przechodzenia/
nie nastąpi przywrócenie przewijania.Całą bazę kodu można znaleźć tutaj: https://github.com/rizedr/react-router-scroll-top
źródło
Home
. Teraz przewiń do dołuHome
i przejdź do „AnotherPage” i nie przewijaj. Teraz, jeśli przyjdziesz ponownie doHome
, ta strona zostanie przewinięta do góry. Ale zgodnie z twoją odpowiedziąhome
strona powinna być przewijana.Należy zauważyć, że
onUpdate={() => window.scrollTo(0, 0)}
metoda jest przestarzała.Oto proste rozwiązanie dla routera React 4+.
const history = createBrowserHistory() history.listen(_ => { window.scrollTo(0, 0) }) <Router history={history}>
źródło
#
i?
do końca naszego adresu URL. W pierwszym przypadku powinieneś przewinąć nie na górę, w drugim przypadku nie powinieneś w ogóle przewijaćJeśli używasz React 16.8+, jest to łatwe do obsłużenia z komponentem, który będzie przewijał okno w górę przy każdej nawigacji:
Tutaj jest komponent 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; }
Następnie wyrenderuj go u góry aplikacji, ale poniżej routera.
Tutaj znajduje się plik app.js
import ScrollToTop from "./scrollToTop"; function App() { return ( <Router> <ScrollToTop /> <App /> </Router> ); }
lub w index.js
import ScrollToTop from "./scrollToTop"; ReactDOM.render( <BrowserRouter> <ScrollToTop /> <App /> </BrowserRouter> document.getElementById("root") );
źródło
Hak reakcji, który możesz dodać do komponentu trasy. Używanie
useLayoutEffect
zamiast niestandardowych słuchaczy.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> ); }
Aktualizacja : zaktualizowano do użycia
useLayoutEffect
zamiastuseEffect
, aby zmniejszyć wizualne szarpnięcie. Z grubsza przekłada się to na:useEffect
: renderowanie komponentów -> malowanie na ekranie -> przewijanie do góry (efekt uruchomienia)useLayoutEffect
: renderowanie komponentów -> przewijanie do góry (efekt uruchomienia) -> malowanie do ekranuW zależności od tego, czy ładujesz dane (pomyśl o spinnerach) lub jeśli masz animacje przejścia stron,
useEffect
może działać lepiej dla Ciebie.źródło
Miałem ten sam problem z moją aplikacją: użycie poniższego fragmentu kodu pomogło mi przewinąć na górę strony po kliknięciu następnego przycisku.
<Router onUpdate={() => window.scrollTo(0, 0)} history= {browserHistory}> ... </Router>
Jednak problem nadal występował z powrotem w przeglądarce. Po wielu próbach zdałem sobie sprawę, że dzieje się tak z powodu obiektu historii okna przeglądarki, który ma właściwość scrollRestoration, która była ustawiona na auto. Ustawienie tego na ręczne rozwiązało mój problem.
function scrollToTop() { window.scrollTo(0, 0) if ('scrollRestoration' in history) { history.scrollRestoration = 'manual'; } } <Router onUpdate= {scrollToTop} history={browserHistory}> .... </Router>
źródło
W części poniżej
<Router>
Po prostu dodaj hak do reakcji (na wypadek, gdybyś nie korzystał z klasy React)
React.useEffect(() => { window.scrollTo(0, 0); }, [props.location]);
źródło
Chcę podzielić się moim rozwiązaniem dla tych, którzy używają,
react-router-dom v5
ponieważ żadne z tych rozwiązań v4 nie zadziałało za mnie.To, co rozwiązało mój problem, to zainstalowanie React-router-scroll-top i umieszczenie wrappera w
<App />
ten sposób:const App = () => ( <Router> <ScrollToTop> <App/> </ScrollToTop> </Router> )
i to wszystko! zadziałało!
źródło
Hooki można komponować, a od React Router v5.1 mamy
useHistory()
hook. Na podstawie odpowiedzi @ zurfyx stworzyłem haczyk wielokrotnego użytku dla tej funkcji:// 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]); };
źródło
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;
źródło
const ScrollToTop: React.FC = () => {
Nie rozumiem, co toScrollToTop: React.FC
znaczyMoje rozwiązanie: komponent, którego używam w komponentach moich ekranów (gdzie chcę przewinąć do góry).
import { useLayoutEffect } from 'react'; const ScrollToTop = () => { useLayoutEffect(() => { window.scrollTo(0, 0); }, []); return null; }; export default ScrollToTop;
Pozwala to zachować pozycję przewijania podczas cofania. Używanie useEffect () było dla mnie błędne, gdy wracałem dokument przewijał się do góry, a także miał efekt migotania, gdy trasa została zmieniona w już przewiniętym dokumencie.
źródło
Napisałem komponent wyższego rzędu o nazwie
withScrollToTop
. Ten HOC przyjmuje dwie flagi:onComponentWillMount
- Czy przewijać do góry po nawigacji (componentWillMount
)onComponentDidUpdate
- Czy przewijać do góry po aktualizacji (componentDidUpdate
). Ta flaga jest konieczna w przypadkach, gdy komponent nie jest odmontowany, ale występuje zdarzenie nawigacji, na przykład od/users/1
do/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)); };
Aby z niego skorzystać:
import withScrollToTop from './withScrollToTop'; function MyComponent() { ... } export default withScrollToTop(MyComponent);
źródło
Oto inna metoda.
Dla React-router v4 możesz również powiązać nasłuchiwanie ze zmianą w zdarzeniu historycznym w następujący sposób:
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'));
Poziom przewijania zostanie ustawiony na 0 bez
setImmediate()
również, jeśli trasa zostanie zmieniona przez kliknięcie łącza, ale jeśli użytkownik naciśnie przycisk Wstecz w przeglądarce, nie będzie działać jako przeglądarka zresetować ręcznie poziom przewijania do poprzedniego poziomu po naciśnięciu przycisku Wstecz , więc używającsetImmediate()
powodujemy, że nasza funkcja jest wykonywana po zakończeniu przeglądarki, resetując poziom przewijania, dając nam w ten sposób pożądany efekt.źródło
z React router dom v4 możesz użyć
utwórz składnik scrollToTopComponent, taki jak ten poniżej
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)
lub jeśli używasz kart, użyj czegoś podobnego do poniższego
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}/>
Mam nadzieję, że to pomoże, aby uzyskać więcej informacji na temat przywracania przewijania, odwiedzając tam dokumenty zając reaguje na przywracanie routera dom przewijanie
źródło
Okazało się, że
ReactDOM.findDomNode(this).scrollIntoView()
to działa. Włożyłem go do środkacomponentDidMount()
.źródło
W przypadku mniejszych aplikacji, z trasami 1-4, możesz spróbować zhakować go z przekierowaniem do górnego elementu DOM z #id zamiast tylko trasą. Wtedy nie ma potrzeby zawijania tras w ScrollToTop lub używania metod cyklu życia.
źródło
W swoim
router.js
, po prostu dodaj tę funkcję dorouter
obiektu. To wystarczy.scrollBehavior() { document.getElementById('app').scrollIntoView(); },
Lubię to,
**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" }, ] });
źródło
To było moje podejście oparte na tym, co wszyscy inni zrobili w poprzednich postach. Zastanawiasz się, czy byłoby to dobre podejście w 2020 r., Używając lokalizacji jako zależności zapobiegającej ponownemu renderowaniu?
import React, { useEffect } from 'react'; import { useLocation } from 'react-router-dom'; function ScrollToTop( { children } ) { let location = useLocation(); useEffect( () => { window.scrollTo(0, 0); }, [ location ] ); return children }
źródło
render() { window.scrollTo(0, 0) ... }
Może być prostym rozwiązaniem w przypadku, gdy właściwości nie są zmieniane, a componentDidUpdate () nie uruchamia się.
źródło
To jest hacky (ale działa): po prostu dodaję
window.scrollTo (0,0);
renderować ();
źródło