cuộn react-router lên đầu mỗi lần chuyển đổi


111

Tôi gặp sự cố khi điều hướng đến một trang khác, vị trí của nó sẽ vẫn như trang trước đó. Vì vậy, nó sẽ không tự động cuộn lên đầu trang. Tôi cũng đã thử sử dụng window.scrollTo(0, 0)trên bộ định tuyến onChange. Tôi cũng đã sử dụng scrollBehavior để khắc phục sự cố này nhưng nó không hoạt động. Bất kỳ đề nghị về điều này?


Bạn có thể không thực hiện logic trong componentDidMountthành phần của tuyến đường mới?
Yuya

chỉ cần thêm document.body.scrollTop = 0;vào componentDidMountthành phần bạn đang chuyển đến
John Ruddell

@Kujira Tôi đã thêm scrollTo bên trong componentDidMount () nhưng nó không hoạt động.
adrian hartanto

@JohnRuddell Điều đó cũng không hoạt động.
adrian hartanto

Tôi phải sử dụng document.getElementById ('root'). ScrollTop = 0 để làm việc
Mr. 14

Câu trả lời:


120

Tài liệu cho React Router v4 chứa các mẫu mã để khôi phục cuộn. Đây là mẫu mã đầu tiên của họ, đóng vai trò như một giải pháp trên toàn trang web để "cuộn lên đầu" khi một trang được điều hướng đến:

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)

Sau đó, hiển thị nó ở đầu ứng dụng của bạn, nhưng bên dưới Bộ định tuyến:

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

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

^ được sao chép trực tiếp từ tài liệu

Rõ ràng là điều này phù hợp với hầu hết các trường hợp, nhưng còn nhiều hơn về cách xử lý các giao diện theo thẻ và tại sao một giải pháp chung chưa được triển khai.


3
Bạn cũng nên đặt lại tiêu điểm bàn phím cùng lúc với việc cuộn lên trên cùng. Tôi đã viết một điều cần giải quyết: github.com/oaf-project/oaf-react-router
danielnixon

3
trích xuất này từ các tài liệu 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.làm tôi buồn, bởi vì tất cả các tùy chọn mà họ sử dụng để cung cấp các ví dụ mã, thực sự có thể được đưa vào điều khiển thông qua cài đặt thuộc tính, với một số quy ước xung quanh hành vi mặc định sau đó có thể được thay đổi. Điều này cảm thấy siêu khó và chưa hoàn thành, imho. Phương tiện duy nhất tiên tiến phản ứng devs có thể làm việc định tuyến đúng cách và không #pitOfSuccess
snowcode

2
oaf-react-router dường như giải quyết các vấn đề quan trọng về khả năng truy cập mà tất cả những người work-aroundsở đây đã bỏ qua. +100 cho @danielnixon
snowcode

ScrollToTop không được định nghĩa. Làm cách nào để nhập nó vào App.js? nhập ScrollToTop từ './ScrollToTop' không hoạt động.
anhtv13 22/07

@ anhtv13, bạn nên hỏi đó là câu hỏi mới để bạn đưa mã đang tham khảo vào.
mtl

93

nhưng các lớp học là như vậy 2018

Triển khai ScrollToTop với 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);

Sử dụng:

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

ScrollToTop cũng có thể được triển khai như một thành phần trình bao bọc:

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

Sử dụng:

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

2
Giải pháp tốt nhất mà tôi đã thấy cho đến nay trên internet. Tôi chỉ hơi bối rối tại sao dự án react-router không thêm thuộc tính scrollToTopvào <Route>. Nó dường như là tính năng được sử dụng rất thường xuyên.
stanleyxu2005

Làm tốt lắm anh bạn. Đã giúp đỡ rất nhiều.
VaibS

Cách thích hợp để kiểm tra đơn vị này là gì?
Matt

30

Câu trả lời này dành cho mã kế thừa, cho bộ định tuyến v4 + kiểm tra các câu trả lời khác

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

Nếu nó không hoạt động, bạn nên tìm lý do. Ngoài ra bên trongcomponentDidMount

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

bạn đã có thể sử dụng:

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

bạn có thể thêm một số cờ như "scrolled = false" và sau đó trong bản cập nhật:

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

Tôi đã cập nhật câu trả lời w / o getDomNode bởi vì trên thực tế, tôi chưa bao giờ phải sử dụng nó để cuộn lên đầu trang. Tôi vừa mới sử dụngwindow.scrollTo(0,0);
Lukas Liesis

3
onUpdatemóc bị phản đối trong phản ứng-router v4 - chỉ muốn chỉ ra rằng
Dragos Rizescu

@DragosRizescu Bất kỳ hướng dẫn nào về cách sử dụng phương pháp này mà không cần onUpdatehook?
Matt Voda

1
@MattVoda Bạn có thể lắng nghe những thay đổi trên lịch sử của chính nó, kiểm tra các ví dụ: github.com/ReactTraining/history
Lukas Liesis

3
@MattVoda đã viết một câu trả lời dưới đây về cách bạn có thể đạt được điều đó với phản ứng-router-v4
Dragos Rizescu

28

Đối với react-router v4 , đây là ứng dụng tạo-phản ứng giúp khôi phục cuộn: http://router-scroll-top.surge.sh/ .

Để đạt được điều này, bạn có thể tạo trang trí Routethành phần và tận dụng các phương pháp vòng đời:

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

Trên đó, componentDidUpdatechúng tôi có thể kiểm tra khi tên đường dẫn vị trí thay đổi và khớp với nó pathvà nếu những người đó hài lòng, hãy khôi phục cuộn cửa sổ.

Điều thú vị về cách tiếp cận này là chúng ta có thể có các tuyến khôi phục cuộn và các tuyến không khôi phục cuộn.

Đây là một App.jsví dụ về cách bạn có thể sử dụng ở trên:

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;

Từ đoạn mã trên, điều thú vị cần chỉ ra là chỉ khi điều hướng đến /abouthoặc /another-pagehành động cuộn đến đầu trang mới được định dạng trước. Tuy nhiên, khi tiếp tục /không có khôi phục cuộn sẽ xảy ra.

Toàn bộ cơ sở mã có thể được tìm thấy tại đây: https://github.com/rizedr/react-router-scroll-top


1
Chỉ cần những gì tôi đang tìm kiếm! Tôi phải thêm scrollTo trong componentDidMount của ScrollToTopRoute, như tôi đã có tuyến đường của tôi được bao bọc trong một thành phần chuyển mạch (Tôi cũng loại bỏ nếu như tôi muốn nó nổ súng vào từng gắn)
tocallaghan

Trong kết xuất của ScrollToTopRoute, bạn không cần chỉ định kết xuất = {props => (<Thành phần {... props} />)}. Chỉ cần sử dụng {... rest} trong tuyến sẽ hoạt động.
Finickyflame

Câu trả lời này đã cung cấp cho tôi tất cả các công cụ để đạt được giải pháp và sửa lỗi của mình. Cảm ơn bạn rất nhiều.
Null isTrue

Nó không hoạt động hoàn toàn. Nếu bạn theo liên kết dự án thì nó sẽ được mặc định mở Home. Bây giờ cuộn xuống cuối Homevà bây giờ đi tới 'Trang khác' và không cuộn. Bây giờ nếu bạn quay lại Home, trang này sẽ được cuộn lên trên cùng. Nhưng theo câu trả lời của bạn, hometrang này nên được cuộn lại.
aditya81070

18

Điều đáng chú ý là onUpdate={() => window.scrollTo(0, 0)}phương pháp này đã lỗi thời.

Đây là một giải pháp đơn giản cho react-router 4+.

const history = createBrowserHistory()

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

<Router history={history}>

Đây sẽ là câu trả lời chính xác nếu lịch sử được sử dụng. Nó bao gồm tất cả các trang web cuối cùng bao gồm cả việc nhấp vào liên kết đến trang bạn hiện đang truy cập. Vấn đề duy nhất tôi tìm thấy là scrollTo không kích hoạt và cần được bao bọc trong setTimeout (window.scrollTo (0, 0), 0); Tôi vẫn không hiểu tại sao nhưng này ho
sidonaldson

2
Bạn sẽ gặp sự cố khi thêm #?vào cuối url của chúng tôi. Trong trường hợp đầu tiên bạn nên di chuyển không lên đỉnh trong trường hợp thứ hai bạn không nên di chuyển ở tất cả
Arseniy-II

1
Thật không may, điều này không hoạt động với BrowserRouter.
VaibS

12

Nếu bạn đang chạy React 16.8+, điều này rất dễ xử lý với một thành phần sẽ cuộn cửa sổ lên trên mọi điều hướng:
Đây là thành phần 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;
}

Sau đó, hiển thị nó ở đầu ứng dụng của bạn, nhưng bên dưới Bộ định tuyến
Đây là trong app.js

import ScrollToTop from "./scrollToTop";

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

hoặc trong index.js

import ScrollToTop from "./scrollToTop";

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

7

Tôi đã gặp vấn đề tương tự với ứng dụng của mình. Việc sử dụng đoạn mã dưới đây đã giúp tôi cuộn lên đầu trang khi nhấp vào nút tiếp theo.

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

Tuy nhiên, sự cố vẫn tiếp diễn trên trình duyệt trở lại. Sau nhiều lần thử nghiệm, tôi nhận ra rằng điều này là do đối tượng lịch sử của cửa sổ trình duyệt, có thuộc tính scrollRestoration được đặt thành tự động. Đặt điều này thành thủ công đã giải quyết được vấn đề của tôi.

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

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

6

Trong một thành phần bên dưới <Router>

Chỉ cần thêm một React Hook (trong trường hợp bạn không sử dụng một lớp React)

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

4

Tôi muốn chia sẻ giải pháp của mình cho những người đang sử dụng react-router-dom v5vì không có giải pháp v4 nào phù hợp với tôi.

Điều đã giải quyết được vấn đề của tôi là cài đặt react-router-scroll-top và đặt trình bao bọc <App />như sau:

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

và đó là nó! nó đã làm việc!


4

Hook có thể kết hợp và kể từ React Router v5.1, chúng tôi có một useHistory()hook. Vì vậy, dựa trên câu trả lời của @ zurfyx, tôi đã tạo một hook có thể sử dụng lại cho chức năng này:

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

Một React Hook bạn có thể thêm vào thành phần Route của mình. Sử dụng useLayoutEffectthay vì trình nghe tùy chỉnh.

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

Cập nhật : Đã cập nhật để sử dụng useLayoutEffectthay vì useEffectít hình ảnh hơn. Đại khái điều này được dịch thành:

  • useEffect: kết xuất các thành phần -> vẽ lên màn hình -> cuộn lên trên cùng (chạy hiệu ứng)
  • useLayoutEffect: kết xuất các thành phần -> cuộn lên trên cùng (chạy hiệu ứng) -> vẽ lên màn hình

Tùy thuộc vào việc bạn đang tải dữ liệu (hãy nghĩ đến trình quay) hoặc nếu bạn có hoạt ảnh chuyển trang, useEffectcó thể hoạt động tốt hơn cho bạn.


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;

bạn có thể vui lòng giải thích phần này không? const ScrollToTop: React.FC = () => {Tôi không hiểu ScrollToTop: React.FCnghĩa là gì
beeftosino

1
định nghĩa chữ viết
Kepro

2

Tôi đã viết một Thành phần thứ tự cao hơn được gọi withScrollToTop. HOC này có hai cờ:

  • onComponentWillMount- Có cuộn lên đầu khi điều hướng ( componentWillMount)
  • onComponentDidUpdate- Có cuộn lên đầu khi cập nhật ( componentDidUpdate). Cờ này là cần thiết trong các trường hợp thành phần không được ngắt kết nối nhưng xảy ra sự kiện điều hướng, ví dụ: từ /users/1đến /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));
};

Để dùng nó:

import withScrollToTop from './withScrollToTop';

function MyComponent() { ... }

export default withScrollToTop(MyComponent);

2

Giải pháp của tôi: một thành phần mà tôi đang sử dụng trong các thành phần màn hình của mình (nơi tôi muốn cuộn lên trên cùng).

import { useLayoutEffect } from 'react';

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

    return null;
};

export default ScrollToTop;

Điều này giữ nguyên vị trí cuộn khi quay lại. Với tôi, việc sử dụng useEffect () là một lỗi, khi quay lại tài liệu sẽ cuộn lên đầu và cũng có hiệu ứng nhấp nháy khi tuyến đường được thay đổi trong tài liệu đã được cuộn.


1

Đây là một phương pháp khác.

Đối với react-router v4, bạn cũng có thể ràng buộc người nghe thay đổi trong sự kiện lịch sử, theo cách sau:

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

Mức độ cuộn sẽ không được đặt thành 0 setImmediate()nếu tuyến đường được thay đổi bằng cách nhấp vào một liên kết nhưng nếu người dùng nhấn nút quay lại trên trình duyệt thì nó sẽ không hoạt động vì trình duyệt đặt lại mức độ cuộn theo cách thủ công về mức trước đó khi nhấn nút quay lại , do đó, bằng cách sử dụng, setImmediate()chúng tôi làm cho chức năng của chúng tôi được thực thi sau khi trình duyệt hoàn tất việc đặt lại mức cuộn, do đó mang lại cho chúng tôi hiệu quả mong muốn.


1

với bộ định tuyến React dom v4 bạn có thể sử dụng

tạo một thành phần scrollToTopComponent giống như bên dưới

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)

hoặc nếu bạn đang sử dụng các tab, hãy sử dụng một cái gì đó giống như bên dưới

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

hy vọng điều này sẽ giúp ích cho nhiều hơn về khôi phục cuộn vist there docs hare react router dom scroll restore


0

Tôi thấy rằng nó ReactDOM.findDomNode(this).scrollIntoView()đang hoạt động. Tôi đã đặt nó bên trong componentDidMount().


1
đó là những thứ nội bộ không có giấy tờ có thể thay đổi việc nhận thấy và mã của bạn sẽ ngừng hoạt động.
Lukas Liesis

0

Đối với các ứng dụng nhỏ hơn, với 1-4 tuyến, bạn có thể thử hack nó bằng cách chuyển hướng đến phần tử DOM trên cùng với #id thay vì chỉ một tuyến. Sau đó, không cần phải bọc Routes trong ScrollToTop hoặc sử dụng các phương thức vòng đời.


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

Có thể là một giải pháp đơn giản trong trường hợp các đạo cụ không được thay đổi và componentDidUpdate () không kích hoạt.


0

Trong của bạn router.js, chỉ cần thêm chức năng này trong routerđối tượng. Điều này sẽ thực hiện công việc.

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

Như thế này,

**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" },
    ]
});

-10

Đây là hacky (nhưng hoạt động): Tôi chỉ thêm

window.scrollTo (0,0);

để kết xuất ();


2
vì vậy nó sẽ cuộn lên mỗi khi trạng thái thay đổi và kết xuất lại xảy ra, không phải khi điều hướng thay đổi. Chỉ cần kiểm tra câu trả lời của tôi
Lukas Liesis
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.