2
votes

I'm having trouble detecting route changes with React Router v5. The desire is, when the route changes I want output a console.log. The desire is, when the route changes I want output that route in a console.log in the App component (as shown in the code below) so I can then set a variable in App.js based on the route.

Package versions:

"dependencies": {
   "react": "^16.12.0",
   "react-router-dom": "^5.1.2",
}

This is the App.js code where my router is currently located:

import React from "react";
import { BrowserRouter as Router, Route, Switch, NavLink } from "react-router-dom"
import "./App.css";
import AdminDashboard from "./pages/AdminDashboard"
import UserList from "./pages/UserList"

function App() {

    const routeChange = () => {
        console.log("foo!")
        console.log(window.location.pathname)
    }

    return (
        <>
            <Router onChange={routeChange}>
                <NavLink exact={true} className="nav-link" to="/AdminDashboard">Admin Dashboard</NavLink>
                <NavLink exact={true} className="nav-link" to="/UserList">User List</NavLink>
                <Switch>
                    <Route path="/AdminDashboard" component={AdminDashboard} exact />
                    <Route path="/UserList" component={UserList} exact />
                </Switch>
            </Router>
        </>
    );
}

export default App;

3
Did you try this? and remove that onChange event on the Router tag it's pointless.Mohamed Wagih
props.history is undefined.Micah B.

3 Answers

5
votes

Wrapping with withRouter works fine, but from react-router5, they have hooks to avoid it. so that we don't inject unwanted props to the component. In this case, you could make use of useLocation hook.

If you try to run const location = useLocation() inside App.js you'll get: "TypeError: useContext(...) is undefined" on line 712 of react-router/esm/react-router.js. But you can access useLocation() inside a child component (e.g. Route.js) and pass the location back up to the parent via a prop function setPathname() <Router> must be in a parent component in order for useLocation() to work.

App.js

import React, { useEffect } from "react"
import { BrowserRouter as Router, NavLink} from "react-router-dom"
import "./App.css"

function App() {
    
    const setPathname = (pathname) => {
        console.log("**", pathname)
    }

    return (
        <>
            <NavLink exact={true} className="nav-link" to="/AdminDashboard">Admin Dashboard</NavLink>
            <NavLink exact={true} className="nav-link" to="/UserList">User List</NavLink>            
            <Router onChange={routeChange}>
                <Routes setPathname={setPathname} />
            </Router>
        </>
    );
}

export default App

Routes.js

import React from "react"
import { Router, Route, Switch, NavLink, useLocation } from "react-router-dom"
import AdminDashboard from "./pages/AdminDashboard"
import UserList from "./pages/UserList"

function Routes(props) {
    const location = useLocation()
    
    useEffect(() => {
        console.log("*", location.pathname)
        props.setPathname(location.pathname)
    },[location])

    return (
        <>
            <Switch>
                <Route path="/AdminDashboard" component={AdminDashboard} exact />
                <Route path="/UserList" component={UserList} exact />
            </Switch>
        </>
    );
}

export default Routes

The result of this will give you two console logs. One from the child Routes.js and one from the parent App.js. Then you can set your variable based on the pathname in either component, as needed.

  1. *, [pathname]
  2. **, [pathname]
1
votes

I think you need to tie the router into the component using withRouter..

withRouter will pass updated match, location, and history props to the wrapped component whenever it renders.

https://reacttraining.com/react-router/web/api/withRouter

-1
votes

assign a callback on props.history.listen the history property will detect changes and fire your callback