React Routing: Detect route change

10,820

Solution 1

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]

Solution 2

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

Share:
10,820
Micah B.
Author by

Micah B.

A C#, MVC, SQL, Web and Windows developer with over 17 years experience in the software development industry with extensive experience in architecture, design and development of secure, high-performance applications, including e-commerce applications, with a variety of standards based technologies. Have worked as a consultant for years directly with high level managers and executives of various companies. I’ve learned their vision and direction, and to analyzed business needs to make recommendations for custom development solutions and integration with existing systems. Have worked with high capacity production databases with up to 500 million rows. Have worked primarily SQL Server, but I have also worked with Oracle and MySQL. Have performance tuned databases, using replication, de-normalization (as well as indices) and other methods to give high-speed access to large data sets. Have several years of experience using Microsoft Azure PaaS and IaaS cloud solutions, including writing applications that run on multiple servers and handle requests for transactional operations while maintaining data integrity with optimistic and pessimistic concurrency.

Updated on June 05, 2022

Comments

  • Micah B.
    Micah B. almost 2 years

    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;
    
    
  • Micah B.
    Micah B. about 4 years
    props.history is undefined.
  • TZiebura
    TZiebura about 4 years
    history prop is inside every component passed to Route so I'm not really sure what is the case, you might try import { createBrowserHistory } from 'history'; const history = createBrowserHistory() and assign the listen function that history
  • TZiebura
    TZiebura about 4 years
    yep that's one of the options as well either operating on history prop or using the hook
  • madannes
    madannes about 4 years
    This is cleaner and more intuitive than my answer =)
  • Micah B.
    Micah B. about 4 years
    I've tried this method, but when I run "const location = useLocation()" I get an error: "TypeError: useContext(...) is undefined" on line 712 of react-router/esm/react-router.js
  • Vishnu
    Vishnu about 4 years
    Its because the react-router context isn't set in that component. Since its the <Router> component that sets the context you could use useHistory in a sub-component, but not in that one. Sorry, i didn't notice this before. Updated my answer. Please check
  • Micah B.
    Micah B. about 4 years
    Moving the routes to a child component and then sending those back to the parent via a props function works well. I clarified this, and added information about the error I mentioned in a previous comment inside the answer which edit is awaiting community approval.
  • Ievgen
    Ievgen almost 4 years
    how to get before and after changed?
  • Mike Willis
    Mike Willis over 3 years
    This worked great for me, but I had to make some minor adjustments: App.js: the onChange prop for Router did not seem to be used so I removed it. In Routes.js: import React, { useEffect } from "react".