반응 라우터 v4에서 경로 변경을 청취하는 방법은 무엇입니까?


답변:


170

나는 소품 withRouter을 얻기 위해 사용 합니다 location. 새로운 경로로 인해 구성 요소가 업데이트되면 값이 변경되었는지 확인합니다.

@withRouter
class App extends React.Component {

  static propTypes = {
    location: React.PropTypes.object.isRequired
  }

  // ...

  componentDidUpdate(prevProps) {
    if (this.props.location !== prevProps.location) {
      this.onRouteChanged();
    }
  }

  onRouteChanged() {
    console.log("ROUTE CHANGED");
  }

  // ...
  render(){
    return <Switch>
        <Route path="/" exact component={HomePage} />
        <Route path="/checkout" component={CheckoutPage} />
        <Route path="/success" component={SuccessPage} />
        // ...
        <Route component={NotFound} />
      </Switch>
  }
}

그것이 도움이되기를 바랍니다.


21
반응 라우터 v4에서 'this.props.location.pathname'을 사용하십시오.
ptorsson

4
@ledfusion 나는 똑같이하고 사용 withRouter하고 있지만 오류가 발생 You should not use <Route> or withRouter() outside a <Router>합니다. <Router/>위 코드에 구성 요소 가 없습니다 . 어떻게 작동합니까?
maverick

1
안녕하세요 @maverick. 코드의 모양이 확실하지 않지만 위의 예에서 <Switch>구성 요소가 사실상의 라우터 역할을합니다. <Route>일치하는 경로를 가진 첫 번째 항목 만 렌더링됩니다. <Router/>이 시나리오에서는 구성 요소 가 필요 없습니다
brickpop

1
@withRouter를 사용하려면 npm install --save-dev transform-decorators-legacy를 설치해야합니다.
Sigex

68

위를 확장하려면 기록 개체를 가져와야합니다. 을 사용 하는 경우 히스토리 오브젝트의 특성 및 기능에 대한 소품을 통해 액세스 할 수 있도록 상위 컴포넌트 (HoC) 로 컴포넌트를 BrowserRouter가져 withRouter오고 랩핑 할 수 있습니다 .

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

const myComponent = ({ history }) => {

    history.listen((location, action) => {
        // location is an object like window.location
        console.log(action, location.pathname, location.state)
    });

    return <div>...</div>;
};

export default withRouter(myComponent);

유일하게 알아 두어야 할 것은 라우터와 함께 접근 할 수있는 대부분의 다른 방법으로 history소품을 구조화 할 때 소품을 오염시키는 것처럼 보입니다.


대답은 질문에 관계없이 무언가를 이해하는 데 도움이되었습니다 :). 그러나 수정 withRouteswithRouter.
Sergey Reutskiy

네, 죄송합니다. 지적 해 주셔서 감사합니다. 게시물을 수정했습니다. 질문의 맨 위에 올바른 가져 오기를 넣은 다음 코드 예제에서 잘못 입력하십시오.
Sam Parmenter

5
나는 생각 withRouter의 현재 버전은 통과 history하지 않고 변수보다 listen.
mikebridge

5
비공개를 보여주기 위해 게시물을 수정하는 것이 좋습니다. 이 코드에는 메모리 누수가 있습니다.
AndrewSouthpaw 2016 년

34

히스토리 v4 lib 를 사용해야 합니다.

거기 에서 예

history.listen((location, action) => {
  console.log(`The current URL is ${location.pathname}${location.search}${location.hash}`)
  console.log(`The last navigation action was ${action}`)
})

1
history.pushState () 및 history.replaceState () 호출은 popstate 이벤트를 트리거하지 않으므로 이것만으로는 모든 경로 변경을 다루지 않습니다.
Ryan

1
@Ryan 그것은 history.push트리거 하는 것 같습니다 history.listen. history v4 docs 에서 기본 URL 사용 예를 참조하십시오 . 실제로는 브라우저 의 기본 개체 래퍼 이기 때문에 기본 개체와 정확히 동일하게 동작하지 않습니다. historyhistory
Rockallite

이는 구성 요소 수명주기 이벤트 반응과 관련이없는 이벤트 푸시에 대한 변경 사항을 수신해야하는 경우가 종종 있기 때문에 더 나은 솔루션 인 것 같습니다.
Daniel Dubovski

12
잠재적 인 메모리 누출! 이 작업을 수행하는 것이 매우 중요합니다! "history.listen을 사용하여 리스너를 연결하면 리스너를 제거하는 데 사용할 수있는 함수가 반환되고,이 함수는 정리 논리에서 호출 될 수 있습니다.const unlisten = history.listen(myListener); unlisten();
Dehan de Croos

히스토리 패키지에 대한 문서를 보려면 여기로 이동하십시오. github.com/ReactTraining/history/blob/master/docs/…
Jason Kim

26

withRouter, history.listenuseEffect(React Hooks)는 매우 잘 작동합니다.

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

const Component = ({ history }) => {
    useEffect(() => history.listen(() => {
        // do something on route change
        // for my example, close a drawer
    }), [])

    //...
}

export default withRouter(Component)

리스너 콜백은 경로가 변경 될 때마다 발생하며에 대한 반환 값 history.listen은으로 잘 작동하는 종료 처리기입니다 useEffect.


13

v5.1은 유용한 후크를 소개합니다 useLocation

https://reacttraining.com/blog/react-router-v5-1/#uselocation

import { Switch, useLocation } from 'react-router-dom'

function usePageViews() {
  let location = useLocation()

  useEffect(
    () => {
      ga.send(['pageview', location.pathname])
    },
    [location]
  )
}

function App() {
  usePageViews()
  return <Switch>{/* your routes here */}</Switch>
}

4
오류가 발생했을 때 참고 사항 : Cannot read property 'location' of undefined at useLocation. useLocation () 호출이 라우터를 트리에 배치하는 동일한 구성 요소에 있지 않은지 확인해야합니다. 여기 참조
toddg

11

후크 포함 :

import { useEffect } from 'react'
import { withRouter } from 'react-router-dom'
import { history as historyShape } from 'react-router-prop-types'

const DebugHistory = ({ history }) => {
  useEffect(() => {
    console.log('> Router', history.action, history.location])
  }, [history.location.key])

  return null
}

DebugHistory.propTypes = { history: historyShape }

export default withRouter(DebugHistory)

<DebugHistory>컴포넌트 로 가져 오기 및 렌더링


11
import React, { useEffect } from 'react';
import { useLocation } from 'react-router';

function MyApp() {

  const location = useLocation();

  useEffect(() => {
      console.log('route has been changed');
      ...your code
  },[location.pathname]);

}

후크


홀리 제 시스! 어떻게 작동합니까? 당신의 대답은 멋지다! buti는 디버거 포인트를 useEffect넣었 지만 경로 이름을 변경할 때마다 위치가 정의되지 않은 채로 있습니까? 좋은 기사를 공유 할 수 있습니까?
알기 쉬운

7
import { useHistory } from 'react-router-dom';

const Scroll = () => {
  const history = useHistory();

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

  return null;
}

해시 변경도 감시합니까? route / a # 1-> route / a # 2
Naren

1

반응 후크를 사용하여 useEffect

  const history = useHistory()
  const queryString = require('query-string')
  const parsed = queryString.parse(location.search)
  const [search, setSearch] = useState(parsed.search ? parsed.search : '')

  useEffect(() => {
    const parsedSearch = parsed.search ? parsed.search : ''
    if (parsedSearch !== search) {
      // do some action! The route Changed!
    }
  }, [location.search])

0

어떤 경우에는 다음과 같이 render속성 대신 속성을 사용할 수 있습니다 component.

class App extends React.Component {

    constructor (props) {
        super(props);
    }

    onRouteChange (pageId) {
        console.log(pageId);
    }

    render () {
        return  <Switch>
                    <Route path="/" exact render={(props) => { 
                        this.onRouteChange('home');
                        return <HomePage {...props} />;
                    }} />
                    <Route path="/checkout" exact render={(props) => { 
                        this.onRouteChange('checkout');
                        return <CheckoutPage {...props} />;
                    }} />
                </Switch>
    }
}

onRouteChange방법에서 상태를 변경 하면 '최대 업데이트 깊이 초과'오류가 발생할 수 있습니다.


0

useEffect후크를 사용하면 리스너를 추가하지 않고도 경로 변경을 감지 할 수 있습니다.

import React, { useEffect }           from 'react';
import { Switch, Route, withRouter }  from 'react-router-dom';
import Main                           from './Main';
import Blog                           from './Blog';


const App  = ({history}) => {

    useEffect( () => {

        // When route changes, history.location.pathname changes as well
        // And the code will execute after this line

    }, [history.location.pathname]);

    return (<Switch>
              <Route exact path = '/'     component = {Main}/>
              <Route exact path = '/blog' component = {Blog}/>
            </Switch>);

}

export default withRouter(App);

0

방금이 문제를 해결 했으므로 주어진 다른 답변에 대한 보충으로 솔루션을 추가 할 것입니다.

여기서 문제 useEffect는 원할 때 실제로 작동하지 않는다는 것입니다. 첫 번째 렌더링 후에 만 ​​호출이 트리거되므로 원하지 않는 지연이 있습니다.
redux와 같은 상태 관리자를 사용하는 경우 상점의 느린 상태로 인해 화면에서 깜박임이 발생할 가능성이 있습니다.

실제로 원하는 것은 useLayoutEffect이것이 즉시 트리거되기 때문에 사용하는 것 입니다.

그래서 라우터와 같은 디렉토리에 작은 유틸리티 기능을 작성했습니다.

export const callApis = (fn, path) => {
    useLayoutEffect(() => {
      fn();
    }, [path]);
};

구성 요소 HOC 내에서 다음과 같이 호출합니다.

callApis(() => getTopicById({topicId}), path);

pathmatch사용할 때 객체에 전달되는 소품입니다 withRouter.

나는 역사에서 수동으로 듣기 / 듣기를 좋아하지 않습니다. 그것은 단지 imo입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.