react-router로 경로 변경 감지


91

검색 기록에 따라 비즈니스 로직을 구현해야합니다.

내가 원하는 것은 다음과 같습니다.

reactRouter.onUrlChange(url => {
   this.history.push(url);
});

URL이 업데이트 될 때 react-router에서 콜백을받는 방법이 있습니까?


어떤 버전의 반응 라우터를 사용하고 있습니까? 이것이 최선의 접근 방식을 결정할 것입니다. 업데이트하시면 답변 드리겠습니다. 즉, withRouter HoC는 구성 요소 위치를 인식하는 데 가장 좋은 방법 일 것입니다. 경로가 변경 될 때마다 새 ({일치, 기록 및 위치})로 구성 요소를 업데이트합니다. 이렇게하면 이벤트를 수동으로 구독 및 구독 취소 할 필요가 없습니다. 즉, 기능적 상태 비 저장 구성 요소 및 클래스 구성 요소와 함께 사용하기 쉽습니다.
카일 리처드슨

답변:


120

history.listen()경로 변경을 감지하려고 할 때 기능 을 사용할 수 있습니다 . 을 사용 react-router v4하고 있다고 생각하면 컴포넌트를 withRouterHOC로 감싸서 history소품에 액세스하십시오 .

history.listen()unlisten함수를 반환합니다 . 당신 unregister은 듣기에서 이것을 사용할 것 입니다.

다음과 같이 경로를 구성 할 수 있습니다.

index.js

ReactDOM.render(
      <BrowserRouter>
            <AppContainer>
                   <Route exact path="/" Component={...} />
                   <Route exact path="/Home" Component={...} />
           </AppContainer>
        </BrowserRouter>,
  document.getElementById('root')
);

그런 다음 AppContainer.js에서

class App extends Component {
  
  componentWillMount() {
    this.unlisten = this.props.history.listen((location, action) => {
      console.log("on route change");
    });
  }
  componentWillUnmount() {
      this.unlisten();
  }
  render() {
     return (
         <div>{this.props.children}</div>
      );
  }
}
export default withRouter(App);

역사 문서에서 :

다음을 사용하여 현재 위치의 변경 사항을 수신 할 수 있습니다 history.listen.

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

위치 개체는 다음을 포함하여 window.location 인터페이스의 하위 집합을 구현합니다.

**location.pathname** - The path of the URL
**location.search** - The URL query string
**location.hash** - The URL hash fragment

위치에는 다음 속성도있을 수 있습니다.

location.state -URL에 상주하지 않는이 위치에 대한 일부 추가 상태 ( createBrowserHistory및 에서 지원됨 createMemoryHistory)

location.key-이 위치를 나타내는 고유 한 문자열 ( createBrowserHistorycreateMemoryHistory )

작업은 PUSH, REPLACE, or POP사용자가 현재 URL에 도달 한 방법 에 따라 다릅니다.

react-router v3를 사용하는 경우 위에서 언급 한대로 패키지 history.listen()에서 사용 history하거나 사용할 수도 있습니다.browserHistory.listen()

다음과 같은 경로를 구성하고 사용할 수 있습니다.

import {browserHistory} from 'react-router';

class App extends React.Component {

    componentDidMount() {
          this.unlisten = browserHistory.listen( location =>  {
                console.log('route changes');
                
           });
      
    }
    componentWillUnmount() {
        this.unlisten();
     
    }
    render() {
        return (
               <Route path="/" onChange={yourHandler} component={AppContainer}>
                   <IndexRoute component={StaticContainer}  />
                   <Route path="/a" component={ContainerA}  />
                   <Route path="/b" component={ContainerB}  />
            </Route>
        )
    }
} 

그는 v3 및 답변의 두 번째 문장을 사용하여이다 "라는 사용중인 고려react-router v4 "
카일 리처드슨

1
@KyleRichardson 나는 당신이 나를 다시 오해했다고 생각한다. 나는 확실히 나의 영어를 공부해야한다. 나는 V4 라우터 반응하고 역사의 객체를 사용하는 당신이 사용하는 경우 다음과 구성 요소를 포장 할 필요가 있다는 것을 의미withRouter
하기 Shubham 카트리

@KyleRichardson 나는 내 완전한 대답을 보았고 v3에서도 그것을 수행하는 방법을 추가했습니다. 한가지 더, 영업 이익은 그가 오늘 V3를 사용하고 댓글을 달았 나는 어제 질문에 answerd 있었다
하기 Shubham 카트리

1
@ShubhamKhatri 예,하지만 대답이 읽는 방식이 잘못되었습니다. 그는 v4를 사용하고 있지 않습니다. 또한 라우팅이 발생할 때마다 새 props로 컴포넌트 history.listen()withRouter이미 업데이트 할 때 왜 사용 합니까? nextProps.location.href === this.props.location.hrefin 의 간단한 비교를 통해 componentWillUpdate변경된 경우 수행해야하는 작업을 수행 할 수 있습니다.
카일 리처드슨

1
@Aris, 당신은 그것을 시도하는 변화셨어요
하기 Shubham 카트리

38

React Router 5.1 업데이트.

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

const App = () => {
  const location = useLocation();

  React.useEffect(() => {
    console.log('Location changed');
  }, [location]);

  return (
    <Switch>
      {/* Routes go here */}
    </Switch>
  );
};

14

history객체를 전역 적으로 듣고 싶다면 직접 만들어서 Router. 그런 다음 해당 listen()방법으로 들을 수 있습니다 .

// Use Router from react-router, not BrowserRouter.
import { Router } from 'react-router';

// Create history object.
import createHistory from 'history/createBrowserHistory';
const history = createHistory();

// Listen to history changes.
// You can unlisten by calling the constant (`unlisten()`).
const unlisten = history.listen((location, action) => {
  console.log(action, location.pathname, location.state);
});

// Pass history to Router.
<Router history={history}>
   ...
</Router>

히스토리 객체를 모듈로 생성하는 것이 더 좋으므로 필요할 수있는 모든 곳에서 쉽게 가져올 수 있습니다 (예 : import history from './history';


11

react-router v6

다가오는 v6 에서는 useLocationuseEffect후크 를 결합하여 수행 할 수 있습니다.

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

const MyComponent = () => {
  const location = useLocation()

  React.useEffect(() => {
    // runs on location, i.e. route, change
    console.log('handle route change here', location)
  }, [location])
  ...
}

편리한 재사용을 위해 사용자 지정 useLocationChange후크 에서이 작업을 수행 할 수 있습니다.

// runs action(location) on location, i.e. route, change
const useLocationChange = (action) => {
  const location = useLocation()
  React.useEffect(() => { action(location) }, [location])
}

const MyComponent1 = () => {
  useLocationChange((location) => { 
    console.log('handle route change here', location) 
  })
  ...
}

const MyComponent2 = () => {
  useLocationChange((location) => { 
    console.log('and also here', location) 
  })
  ...
}

변경시 이전 경로도 확인해야하는 경우 usePrevious후크 와 결합 할 수 있습니다.

const usePrevious(value) {
  const ref = React.useRef()
  React.useEffect(() => { ref.current = value })

  return ref.current
}

const useLocationChange = (action) => {
  const location = useLocation()
  const prevLocation = usePrevious(location)
  React.useEffect(() => { 
    action(location, prevLocation) 
  }, [location])
}

const MyComponent1 = () => {
  useLocationChange((location, prevLocation) => { 
    console.log('changed from', prevLocation, 'to', location) 
  })
  ...
}

위의 모든 것이 마운트되는 첫 번째 클라이언트 경로와 후속 변경에서 발생한다는 점에 유의하는 것이 중요 합니다. 이것이 문제라면 후자의 예를 사용 prevLocation하고 어떤 일을하기 전에 a 가 존재 하는지 확인하십시오 .


질문이 있습니다. 여러 구성 요소가 렌더링되고 모두 useLocation을보고있는 경우 모든 useEffect가 트리거됩니다. 이 위치가 표시 될 특정 구성 요소에 대해 올바른지 어떻게 확인합니까?
Kex

1
Hey @Kex- location여기서 명확히하기 위해 브라우저 위치가 있으므로 모든 구성 요소에서 동일하며 그 의미에서 항상 정확합니다. 다른 구성 요소에서 후크를 사용하면 위치가 변경 될 때 모두 동일한 값을 받게됩니다. 나는 그들이 그 정보로하는 일이 다를 것이라고 생각하지만 항상 일관 적입니다.
davnicwil

말이 되네요. 위치 변경이 작업을 수행하는 것과 관련이 있는지 구성 요소가 어떻게 알 수 있는지 궁금합니다. 예를 들어 구성 요소가 대시 보드 / 목록을 수신하지만 해당 위치에 연결되어 있는지 여부를 어떻게 알 수 있습니까?
Kex

if (location.pathName === "dashboard / list") {..... actions}과 같은 작업을 수행하지 않는 한. 그래도 구성 요소에 대한 매우 우아한 하드 코딩 경로는 아닙니다.
Kex

8

이것은 오래된 질문이며 경로 변경을 추진하기 위해 경로 변경을 경청해야하는 비즈니스 요구를 잘 이해하지 못합니다. 로터리처럼 보입니다.

그러나 원하는 것은 'page_path'Google 분석 / 글로벌 사이트 태그 / 이와 유사한 것에 대한 반응 라우터 경로 변경 을 업데이트하는 것이기 때문에 여기에서 끝났다면 이제 사용할 수 있는 후크가 있습니다. 나는 받아 들인 대답을 기반으로 작성했습니다.

useTracking.js

import { useEffect } from 'react'
import { useHistory } from 'react-router-dom'

export const useTracking = (trackingId) => {
  const { listen } = useHistory()

  useEffect(() => {
    const unlisten = listen((location) => {
      // if you pasted the google snippet on your index.html
      // you've declared this function in the global
      if (!window.gtag) return

      window.gtag('config', trackingId, { page_path: location.pathname })
    })

    // remember, hooks that add listeners
    // should have cleanup to remove them
    return unlisten
  }, [trackingId, listen])
}

이 후크 는 앱에서 한 번 , 상단 근처에 있지만 여전히 라우터 내부에 사용해야 합니다. 나는 App.js다음과 같이 보입니다.

App.js

import * as React from 'react'
import { BrowserRouter, Route, Switch } from 'react-router-dom'

import Home from './Home/Home'
import About from './About/About'
// this is the file above
import { useTracking } from './useTracking'

export const App = () => {
  useTracking('UA-USE-YOURS-HERE')

  return (
    <Switch>
      <Route path="/about">
        <About />
      </Route>
      <Route path="/">
        <Home />
      </Route>
    </Switch>
  )
}

// I find it handy to have a named export of the App
// and then the default export which wraps it with
// all the providers I need.
// Mostly for testing purposes, but in this case,
// it allows us to use the hook above,
// since you may only use it when inside a Router
export default () => (
  <BrowserRouter>
    <App />
  </BrowserRouter>
)

1

React 단일 페이지 앱에서 새 화면으로 이동 한 후 ChromeVox 스크린 리더를 "화면"상단에 집중하려고 할 때이 질문을 받았습니다. 기본적으로 새 서버 렌더링 웹 페이지에 대한 링크를 따라이 페이지가로드되면 어떤 일이 발생할지 모방하려고합니다.

이 솔루션은 리스너가 필요하지 않습니다, 그것은 사용 withRouter()componentDidUpdate()수명주기 방법은 새로운 URL 경로를 탐색 할 때 원하는 요소에 ChromeVox에 초점을 클릭을 트리거 할 수 있습니다.


이행

모든 앱 화면을 포함하는 react-router 스위치 태그를 감싸는 "Screen"구성 요소를 만들었습니다.

<Screen>
  <Switch>
    ... add <Route> for each screen here...
  </Switch>
</Screen>

Screen.tsx 구성 요소

참고 : 이 구성 요소는 React + TypeScript를 사용합니다.

import React from 'react'
import { RouteComponentProps, withRouter } from 'react-router'

class Screen extends React.Component<RouteComponentProps> {
  public screen = React.createRef<HTMLDivElement>()
  public componentDidUpdate = (prevProps: RouteComponentProps) => {
    if (this.props.location.pathname !== prevProps.location.pathname) {
      // Hack: setTimeout delays click until end of current
      // event loop to ensure new screen has mounted.
      window.setTimeout(() => {
        this.screen.current!.click()
      }, 0)
    }
  }
  public render() {
    return <div ref={this.screen}>{this.props.children}</div>
  }
}

export default withRouter(Screen)

focus()대신을 ( 를) 사용해 click()보았지만 클릭하면 ChromeVox가 현재 읽고있는 내용을 읽지 않고 시작하도록 지시 한 위치에서 다시 시작합니다.

고급 참고 : 이 솔루션에서는 <nav>Screen 구성 요소 내부에서 <main>콘텐츠 이후에 렌더링되는 탐색 이 mainusing css 위에 시각적으로 배치 order: -1;됩니다. 따라서 의사 코드에서 :

<Screen style={{ display: 'flex' }}>
  <main>
  <nav style={{ order: -1 }}>
<Screen>

이 솔루션에 대한 생각, 의견 또는 팁이 있으면 의견을 추가하십시오.


1

React Router V5

pathName을 문자열 ( '/'또는 'users')로 원하는 경우 다음을 사용할 수 있습니다.

  // React Hooks: React Router DOM
  let history = useHistory();
  const location = useLocation();
  const pathName = location.pathname;

0
import React from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Sidebar from './Sidebar';
import Chat from './Chat';

<Router>
    <Sidebar />
        <Switch>
            <Route path="/rooms/:roomId" component={Chat}>
            </Route>
        </Switch>
</Router>

import { useHistory } from 'react-router-dom';
function SidebarChat(props) {
    **const history = useHistory();**
    var openChat = function (id) {
        **//To navigate**
        history.push("/rooms/" + id);
    }
}

**//To Detect the navigation change or param change**
import { useParams } from 'react-router-dom';
function Chat(props) {
    var { roomId } = useParams();
    var roomId = props.match.params.roomId;

    useEffect(() => {
       //Detect the paramter change
    }, [roomId])

    useEffect(() => {
       //Detect the location/url change
    }, [location])
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.