반응 라우터는 모든 전환에서 맨 위로 스크롤합니다.


112

다른 페이지로 이동할 때 문제가 있습니다. 그 위치는 이전 페이지와 동일하게 유지됩니다. 따라서 자동으로 맨 위로 스크롤되지 않습니다. 나는 또한 window.scrollTo(0, 0)onChange 라우터 에서 사용하려고했습니다 . 이 문제를 해결하기 위해 scrollBehavior도 사용했지만 작동하지 않았습니다. 이것에 대한 제안이 있습니까?


componentDidMount새 경로의 구성 요소 에서 논리를 수행 할 수 없습니까?
유야

단지 추가 document.body.scrollTop = 0;componentDidMount당신이 이동하는 구성 요소의
존 러델

@Kujira 나는 이미 componentDidMount () 내부에 scrollTo를 추가했지만 작동하지 않았습니다.
아드리안 hartanto

@JohnRuddell 너무 작동하지 않았습니다.
아드리안 hartanto

작업하려면 document.getElementById ( 'root'). scrollTop = 0을 사용해야합니다
Mr. 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.코드 예제를 제공하기 위해 진행하는 모든 옵션이 실제로 속성 설정을 통해 컨트롤에 구워 질 수 있기 때문에 변경 될 수있는 기본 동작에 대한 몇 가지 규칙이 있기 때문에 나를 슬프게합니다. 이것은 슈퍼 해키와 미완성 느낌, imho. 고급 반응 개발자 만 라우팅을 올바르게 수행 할 수 있고 #pitOfSuccess는 없음을 의미합니다.
snowcode

2
oaf-react-router는 work-arounds여기에서 모두 무시한 중요한 접근성 문제를 해결하는 것으로 보입니다 . @danielnixon에 대한 100
snowcode

ScrollToTop이 정의되지 않았습니다. App.js에서 가져 오는 방법은 무엇입니까? './ScrollToTop'에서 ScrollToTop 가져 오기가 작동하지 않습니다.
anhtv13

@ anhtv13, 참조하는 코드를 포함 할 수 있도록 새 질문으로 물어봐야합니다.
mtl

92

하지만 수업은 너무 2018

React Hooks를 사용한 ScrollToTop 구현

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

이것을 단위 테스트하는 적절한 방법은 무엇입니까?
Matt

확인해야합니다 action !== 'POP'. 리스너가 두 번째 인수로 작업을 수락
Atombit

또한 withRouter HOC 대신 useHistory 후크를 사용할 수 있습니다
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);
}

"scrolled = false"와 같은 플래그를 추가 한 다음 업데이트 할 수 있습니다.

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

실제로 맨 위로 스크롤하는 데 사용할 필요가 없었기 때문에 getDomNode없이 답변을 업데이트했습니다. 난 그냥 사용했습니다window.scrollTo(0,0);
루카스 Liesis

3
onUpdate훅에서 더 이상 사용되지 않습니다는 반응 라우터 V4를 - 그냥 지적 싶어
드라고 Rizescu

@DragosRizescu onUpdate후크 없이이 방법을 사용하는 방법에 대한 지침이 있습니까?
Matt Voda 2017

1
@MattVoda 역사 자체의 변경 사항을들을 수 있습니다. 예를 확인하십시오. github.com/ReactTraining/history
Lukas Liesis 2017-06-06

3
@MattVoda 당신이 달성 할 수있는 방법에 대한 아래의 답을 썼다 라우터-V4 반응과
드라고 Rizescu

28

들어 V4 라우터 반응 , 여기에 스크롤 복원을 달성하는 생성 - 반응 - 응용 프로그램입니다 : 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
내가 찾던 바로 그것! 내 경로가 Switch 구성 요소에 래핑되어 있기 때문에 ScrollToTopRoute의 componentDidMount에 scrollTo를 추가해야했습니다 (각 마운트에서 실행하려는 경우에도 if도 제거했습니다)
tocallaghan

ScrollToTopRoute의 렌더링에서 render = {props => (<Component {... props} />)}를 지정할 필요가 없습니다. 경로에서 {... rest}를 사용하기 만하면됩니다.
Finickyflame

이 답변은 솔루션을 달성하고 버그를 수정하는 데 필요한 모든 도구를 제공했습니다. 정말 고맙습니다.
Null isTrue

완전히 작동하지 않습니다. 프로젝트 링크를 따라 가면 기본적으로 Home. 이제 맨 아래로 스크롤 Home하고 이제 'AnotherPage'로 이동하고 스크롤하지 마십시오. 이제로 다시 오면 Home이 페이지가 맨 위로 스크롤됩니다. 그러나 귀하의 답변에 따르면 home페이지는 계속 스크롤되어야합니다.
aditya81070

18

주목할만한 것은 onUpdate={() => window.scrollTo(0, 0)} 방법이 구식 .

다음은 react-router 4+에 대한 간단한 솔루션입니다.

const history = createBrowserHistory()

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

<Router history={history}>

히스토리를 사용하는 경우 정답이어야합니다. 현재 페이지에 대한 링크를 클릭하는 것을 포함하여 모든 최종 결과를 다룹니다. 내가 찾은 유일한 문제는 scrollTo가 실행되지 않았고 setTimeout (window.scrollTo (0, 0), 0); 나는 아직도 이유를 이해하지 못하지만 헤이 호
sidonaldson

2
당신이 추가 할 때 문제가 발생합니다 #?우리의 URL의 끝에. 첫 번째 경우에는 맨 위로 스크롤해야합니다. 두 번째 경우에는 전혀 스크롤하지 않아야합니다
Arseniy-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 속성이있는 브라우저 창의 히스토리 객체 때문이라는 것을 깨달았으며 이것을 수동으로 설정하면 문제가 해결되었습니다.

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 솔루션 중 어느 것도 나를 위해 작동하지 않았으므로 .

내 문제를 해결 한 것은 react-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

Route 구성 요소에 추가 할 수있는 React Hook. 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 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;

이 부분을 설명해 주시겠습니까? const ScrollToTop: React.FC = () => {무슨 ScrollToTop: React.FC뜻 인지
모르겠어요

1
타이프 스크립트 정의
Kepro

2

나는 withScrollToTop. 이 HOC는 두 가지 플래그를받습니다.

  • onComponentWillMount-탐색시 상단 스크롤 여부 ( componentWillMount)
  • onComponentDidUpdate-업데이트시 맨 위로 스크롤할지 여부 ( componentDidUpdate). 이 플래그는 구성 요소가 마운트 해제되지 않았지만 탐색 이벤트가 발생하는 경우 (예 : from /users/1to)에 필요 /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

다른 방법이 있습니다.

대한 반응 라우터 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'));

setImmediate()링크를 클릭하여 경로를 변경하면 스크롤 레벨도 0으로 설정 되지만 브라우저에서 뒤로 버튼을 누르면 브라우저가 뒤로 버튼을 눌렀을 때 스크롤 레벨을 수동으로 이전 레벨로 재설정하므로 작동하지 않습니다. ,를 사용 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}/>

스크롤 복원에 대한 자세한 내용이 도움이 되었기를 바랍니다. vist there docs hare react router dom scroll restore


0

나는 그것이 효과 ReactDOM.findDomNode(this).scrollIntoView()가 있음을 알았다 . 나는 그것을 안에 넣었다 componentDidMount().


1
문서화되지 않은 내부 항목으로 인식없이 변경 될 수 있으며 코드가 작동을 멈출 수 있습니다.
루카스 Liesis

0

경로가 1-4 개인 작은 앱의 경우 경로 대신 #id를 사용하여 최상위 DOM 요소로 리디렉션하여 해킹을 시도 할 수 있습니다. 그러면 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)
    ...
}

props가 변경되지 않고 componentDidUpdate ()가 실행되지 않는 경우 간단한 해결책이 될 수 있습니다.


-11

이것은 해키입니다 (하지만 작동합니다).

window.scrollTo (0,0);

render ();


2
따라서 탐색이 변경 될 때가 아니라 상태가 변경되고 다시 렌더링 될 때마다 위로 스크롤됩니다. 그냥 내 대답 확인
루카스 Liesis
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.