Come ascoltare le modifiche del percorso nel reagire router v4?


Risposte:


170

Uso withRouterper ottenere l' locationelica. Quando il componente viene aggiornato a causa di una nuova route, controllo se il valore è cambiato:

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

Spero che sia d'aiuto


21
Utilizzare 'this.props.location.pathname' nel reagente router v4.
Ptorsson,

4
@ledfusion Sto facendo lo stesso e utilizzo withRouter, ma ricevo un errore You should not use <Route> or withRouter() outside a <Router>. Non vedo alcun <Router/>componente nel codice sopra. Quindi come funziona?
Maverick,

1
Ciao @maverick. Non sono sicuro di come sia il tuo codice, ma nell'esempio sopra, il <Switch>componente agisce come router di fatto. <Route>Verrà eseguito il rendering solo della prima voce con un percorso corrispondente. Non è necessario alcun <Router/>componente in questo scenario
brickpop,

1
per usare @withRouter devi installare npm install --save-dev transform-decorators-legacy
Sigex

68

Per espandere quanto sopra, dovrai accedere all'oggetto della cronologia. Se stai usando BrowserRouter, puoi importare withRoutere avvolgere il tuo componente con un componente di ordine superiore (HoC) per avere accesso tramite oggetti di scena alle proprietà e alle funzioni dell'oggetto storico.

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

L'unica cosa di cui essere consapevoli è che con Router e la maggior parte degli altri modi per accedervi historysembrano inquinare gli oggetti di scena mentre destrutturano l'oggetto al suo interno.


La risposta mi ha aiutato a capire qualcosa indipendentemente dalla domanda :). Ma ficca withRoutesa withRouter.
Sergey Reutskiy,

Sì, scusa, grazie per averlo sottolineato. Ho modificato il post. Ho messo l'importazione corretta in cima alla domanda e poi l'ho scritto male nell'esempio di codice.
Sam Parmenter,

5
Penso che la versione corrente di withRouter passi historypiuttosto che la variabile listen.
mikebridge,

5
Sarebbe bene modificare il posto per dimostrare di non ascoltare; questo codice contiene una perdita di memoria.
AndrewSouthpaw,

34

Dovresti usare la cronologia v4 v.

Esempio da

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
Le chiamate history.pushState () e history.replaceState () non attivano l'evento popstate, quindi questo da solo non coprirà tutti i cambi di rotta.
Ryan,

1
@Ryan Sembra che history.pushsi inneschi history.listen. Vedi l'esempio sull'utilizzo di un URL di base nella cronologia dei documenti v4 . Poiché in historyrealtà è un wrapper historydell'oggetto nativo di un browser, non si comporta esattamente come quello nativo.
Rockallite,

Sembra una soluzione migliore, poiché spesso è necessario ascoltare i cambiamenti di percorso per la spinta degli eventi, che non è correlato alla reazione degli eventi del ciclo di vita dei componenti.
Daniel Dubovski,

12
Potenziale perdita di memoria! Molto importante che tu lo faccia! "Quando si collega un listener utilizzando history.listen, viene restituita una funzione che può essere utilizzata per rimuovere il listener, che può quindi essere richiamato nella logica di pulizia:const unlisten = history.listen(myListener); unlisten();
Dehan de Croos,

Vai qui per la documentazione sul pacchetto cronologico. github.com/ReactTraining/history/blob/master/docs/…
Jason Kim

26

withRouter, history.listenE useEffect(Reagire Ganci) funziona abbastanza bene insieme:

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)

Il callback dell'ascoltatore si attiverà ogni volta che si cambia una rotta e il ritorno per history.listenè un gestore di spegnimento che funziona bene useEffect.


13

v5.1 introduce l'hook utile 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
Solo una nota, come ho avuto problemi con un errore: Cannot read property 'location' of undefined at useLocation. Devi assicurarti che la chiamata useLocation () non sia nello stesso componente che inserisce il router nell'albero: vedi qui
prima del

11

Con ganci:

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)

Importare e renderizzare come <DebugHistory>componente


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

}

con ganci


Holly Jesys! Come funziona? La tua risposta è fantastica! ma ho messo il punto debugger in usoEffetto ma ogni volta che ho cambiato il nome percorso la posizione è rimasta indefinita ? puoi condividere qualche buon articolo? perché è difficile trovare informazioni chiare
AlexNikonov

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

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

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

  return null;
}

Guarda anche i cambiamenti di hash? route / a # 1 -> route / a # 2
Naren

1

Con reagire Hooks, sto usando 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

In alcuni casi è possibile utilizzare l' renderattributo anziché component, in questo modo:

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

Si noti che se si cambia stato nel onRouteChangemetodo, ciò potrebbe causare l'errore "Profondità di aggiornamento massima superata".


0

Con l' useEffecthook è possibile rilevare le modifiche del percorso senza aggiungere un ascoltatore.

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

Ho appena affrontato questo problema, quindi aggiungerò la mia soluzione come supplemento alle altre risposte fornite.

Il problema qui è che useEffectnon funziona davvero come si vorrebbe, poiché la chiamata viene attivata solo dopo il primo rendering, quindi c'è un ritardo indesiderato.
Se usi un manager statale come Redux, è probabile che otterrai uno sfarfallio sullo schermo a causa dello stato persistente nel negozio.

Quello che vuoi davvero è usare useLayoutEffectpoiché questo viene attivato immediatamente.

Quindi ho scritto una piccola funzione di utilità che ho inserito nella stessa directory del mio router:

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

Che chiamo dall'interno del componente HOC in questo modo:

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

pathè l'elica che viene passata matchnell'oggetto durante l'utilizzo withRouter.

Non sono davvero favorevole all'ascolto / non ascolto manuale della storia. Questo è solo imo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.