How can I listen for router changes?

I’m going through the process of adding Segment to my project and I’d like to track page views.

Is there any way I can watch the router for changes? Thinking something along the lines of a beforeEach/afterEach hook that’s fired upon every navigation.

Cheers,
Jay

1 Like

Do you think you could use useLocation for that? https://redwoodjs.com/docs/redwood-router.html#uselocation

2 Likes

Hey @jmcmullen, thanks for posting here.

So the relevant bit of the documentation is here: https://redwoodjs.com/docs/redwood-router.html#uselocation

Please note that the types seem to be wrong here, where it complains about useLocation not being an exported method.

Please let me know how you get on, but I just tried to test this and couldn’t see it call through. Report back in a bit if I see it working!

Update: beat me to it @Tobbe! :smiley:

1 Like

@Tobbe :laughing:. yeah I was just asking @danny and we had the exact same idea.

I am trying it in a layout to see.

1 Like

Cheers, that’s working perfectly in my layout.

1 Like

Nice.

Note to readers: this example below isn’t really listening for route changes, it looks for the current location pathname in a layout.

Edit: Well, maybe it is in a way. since the router sets the location. Semantics.

Did you useEffect as well or just useLocation get pathname and use that to send as the page to Analytics?

// in a main, top layout or layout want to track
import { useLocation } from '@redwoodjs/router'

// ...

const AppLayout = ({ children }) => {
  const location = useLocation()

  const [pathname, setPathname] = useState(location.pathname)

  useEffect(() => {
    console.log(pathname) // send to analytics
  })

or

const location = useLocation()
console.log(location.pathname) // send to analytics

Be great if could share a code snippet with the community as I am sure others will encounter this.

Sure, here’s my attempt. With useEffect as well.

import { useEffect } from 'react'
import { useLocation } from '@redwoodjs/router'
import Navbar from 'src/components/Navbar'
import Footer from 'src/components/Footer'

const DefaultLayout = ({ children }) => {
  const { pathname } = useLocation()

  useEffect(() => {
    try {
      window.segment.page(pathname)
    } catch (e) {
      // Adblock
    }
  }, [pathname])

  return (
    <>
      <Navbar />
     {children}
      <Footer />
    </>
  )
}

export default DefaultLayout
1 Like

Of course. Much better.

I actually started a PR that I abandoned to add parsed out params from the hash:

const LocationProvider = ({ location = window.location, children }) => {
  const getContext = React.useCallback(() => {
    const { pathname, search, hash } = location
    return { pathname, search, hash }
    const params = new URLSearchParams(search)
    return { pathname, search, params, hash }
  }, [location])

Because often when sending to Analytics, then pass on those to Page properties:

  const { pathname, params } = useLocation()

  useEffect(() => {
    try {
      window.segment.page(pathname, { params })
    } catch (e) {
      // Adblock
    }
  }, [pathname])

If useful, I can complete and submit.

Fortunately for me Segment already handles the query params, but I reckon it’s still a useful feature to have none the less. Might need to make sure URLSearchParams get’s polyfilled since it’s not supported on IE11 though.

I need to listen for routing changes outside the Routes tag