React yönlendirici v4'teki rota değişiklikleri nasıl dinlenir?


Yanıtlar:


170

Prop withRouteralmak için kullanırım location. Bileşen yeni bir rota nedeniyle güncellendiğinde, değerin değişip değişmediğini kontrol ederim:

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

Umarım yardımcı olur


21
React router v4'te 'this.props.location.pathname' kullanın.
ptorsson

4
@ledfusion Ben de aynısını yapıyorum ve kullanıyorum withRouterama hata alıyorum You should not use <Route> or withRouter() outside a <Router>. <Router/>Yukarıdaki kodda herhangi bir bileşen görmüyorum . Peki nasıl çalışıyor?
maverick

1
Merhaba @maverick. Kodunuzun nasıl göründüğünden emin değilim, ancak yukarıdaki örnekte, <Switch>bileşen fiili yönlendirici görevi görüyor. Yalnızca <Route>eşleşen bir yola sahip olan ilk giriş işlenecektir. <Router/>Bu senaryoda herhangi bir bileşene gerek yok
brickpop

1
@withRouter'ı kullanmak için npm install --save-dev transform-decorators-legacy
Sigex

69

Yukarıdakileri genişletmek için geçmiş nesnesine gitmeniz gerekir. Kullanıyorsanız , geçmiş nesnesinin özelliklerine ve işlevlerine props aracılığıyla erişim sağlamak için bileşeninizi daha yüksek dereceli bir bileşenle (HoC)BrowserRouter içe aktarabilir withRouterve sarabilirsiniz .

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

Dikkat edilmesi gereken tek şey, Router ve diğer birçok yolla historynesnenin yapısını bozarken nesneyi kirletmesidir.


Cevap, sorudan bağımsız olarak bir şeyi anlamama yardımcı oldu :). Ama düzeltmek withRoutesiçin withRouter.
Sergey Reutskiy

Evet, özür dilerim, bunu belirttiğiniz için teşekkürler. Gönderiyi düzelttim. Doğru içe aktarmayı sorunun en üstüne koydum ve ardından kod örneğinde yanlış yazdım.
Sam Parmenter

5
Sanırım withRouter'ın şu anki sürümühistory değişkenden çok geçiyor listen.
mikebridge

5
Dinlemediğini göstermek için gönderiyi değiştirmek iyi olur; bu kodda bir bellek sızıntısı var.
AndrewSouthpaw

yalnızca bir kez abone olmalısınız! Şimdi her bileşen yeniden işlemeye abone olacaksınız.
Ievgen Naida

34

History v4 lib kullanmalısınız .

Oradan örnek

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 () ve history.replaceState () çağrıları popstate olayını tetiklemez, bu nedenle bu tek başına tüm rota değişikliklerini kapsamaz.
Ryan

1
@Ryan Bu history.pushtetikleyici gibi görünüyor history.listen. Geçmiş v4 belgelerinde Temel URL kullanma örneğine bakın . Bu aslında bir tarayıcının yerel nesnesinin bir sarmalayıcısı olduğundan, tam olarak yerel olan gibi davranmaz. historyhistory
Rockallite

Bu daha iyi bir çözüm gibi hissettiriyor, çünkü çoğu zaman olay itme için rota değişikliklerini dinlemeniz gerekiyor, bu da bileşen yaşam döngüsü olaylarına tepki vermekle ilgisi yok.
Daniel Dubovski

12
Olası bellek sızıntısı! Bunu yapman çok önemli! "History.listen kullanarak bir dinleyici eklediğinizde, dinleyiciyi kaldırmak için kullanılabilecek bir işlev döndürür ve bu işlev daha sonra temizleme mantığında çağrılabilir:const unlisten = history.listen(myListener); unlisten();
Dehan de Croos

Geçmiş paketiyle ilgili belgeler için buraya gidin. github.com/ReactTraining/history/blob/master/docs/…
Jason Kim

27

withRouter, history.listenve useEffect(React Hooks) birlikte oldukça güzel çalışıyor:

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)

Dinleyici geri araması, bir rota değiştirildiğinde her seferinde tetiklenir ve bunun dönüşü history.listen, iyi bir şekilde oynayan bir kapatma işleyicisidir useEffect.


15

v5.1 kullanışlı kancayı tanıtıyor 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>
}

5
Bir hata ile sorun yaşıyorsanız olduğu gibi sadece bir not: Cannot read property 'location' of undefined at useLocation. UseLocation () çağrısının, yönlendiriciyi ağaca yerleştiren aynı bileşende olmadığından emin olmanız gerekir: buraya bakın
Toddg

12

Kancalı:

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>Bileşen olarak içe aktar ve işle


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

}

kancalı


Holly Jesys! o nasıl çalışır? Cevabınız harika! Buti içinde ayıklayıcı noktayı koymak useEffect ama yol adını yeri değişti her zaman kaldı tanımsız ? güzel bir makale paylaşır mısın? çünkü net bir bilgi bulmak zor
AlexNikonov

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

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

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

  return null;
}

Ayrıca hash değişikliklerini de izliyor mu? rota / a # 1 -> rota / a # 2
Naren

1

Bazı durumlarda şu şekilde renderyerine öznitelik kullanabilirsiniz 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>
    }
}

onRouteChangeYöntemdeki durumu değiştirirseniz , bunun 'Maksimum güncelleme derinliği aşıldı' hatasına neden olabileceğine dikkat edin .


1

React Hooks ile kullanıyorum 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

useEffectKanca ile bir dinleyici eklemeden rota değişikliklerini tespit etmek mümkündür.

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

Bu problemle az önce uğraştım, bu yüzden verilen diğer cevaplara ek olarak çözümümü ekleyeceğim.

Buradaki sorun useEffect, çağrı yalnızca ilk işlemden sonra tetiklendiğinden, istenmeyen bir gecikme olduğundan, gerçekten istediğiniz gibi çalışmamasıdır.
Redux gibi bir durum yöneticisi kullanırsanız, mağazadaki durum nedeniyle ekranda bir titreme yaşama ihtimaliniz vardır.

useLayoutEffectBu hemen tetiklendiği için gerçekten istediğiniz şey kullanmaktır .

Bu yüzden yönlendiricimle aynı dizine koyduğum küçük bir yardımcı program işlevi yazdım:

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

HOC bileşeni içinden şöyle çağırıyorum:

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

pathmatchkullanılırken nesnede geçirilen prop withRouter.

Tarihi elle dinlemekten / dinlemekten gerçekten yana değilim. Bu sadece imo.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.