1

I'm new to React, so I'm sure I'm not understanding the use cases for useLocation - like what it is good for and what it is not intended for.

I'd like to have a method that a specific component can be aware of any location change included those from pushState. Note: I'm converting an Anuglar JS 1.0 code base that just used all query info in the hash. I'd like to use pushState browser feature in this rewrite.

Sample code below (I just have it as the single component in a new React app component:

import React, { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

const RandLocation: React.FC = () => {
    const location = useLocation();

    useEffect(() => {
        console.log('location: ', location);
    }, [location]);
    
    return (
        <div>
            <button 
            onClick={() => {const r =  Math.random(); window.history.pushState({'rnd': r }, '', '/?rnd=' + r)}}>
                Click Me</button>
            <br/>
        </div>
    )
}
export default RandLocation;

I only see the useEffect run on load, and if I move forward or back using the browser buttons. But not when I click the "Click Me" button. What am I missing? Id like to keep this "awareness of location" as simple as possible within the React frontend code. Like is there a technique that works in apps regardless of if you have React Router routes defined?

I am using React version 17.0.2 and react-router-dom version 6.2.2

BuddyJoe
  • 69,735
  • 114
  • 291
  • 466
  • 1
    Where is `RandLocation` rendered? Is it mounted in a stable location so it *can* listen to location changes? Don't use the `window.history` to effect navigation changes as these are considered unintentional side-effects. Use the `navigate` function from the `useNavigate` hook instead, or just use a `Link`. Does something like this help answer your question? https://stackoverflow.com/a/71393138/8690857 It's effectively a RRDv6 version of `history.listen`. Instead of explicitly listening for POP actions you just call the callback when a navigation action is effected. – Drew Reese Sep 21 '22 at 17:40

1 Answers1

6

I think because the window.history.pushState call is outside of React's state management it react-router won't be aware of it. There used to be a way to listen for these events, but I'm not sure something equivalent exist in React Router 6.

You could use the useNavigate hook. Maybe something like:

import React, { useEffect } from "react";
import { useLocation, useNavigate } from "react-router-dom";

const RandLocation = () => {
  const location = useLocation();
  const navigate = useNavigate();

  useEffect(() => {
    console.log("location: ", location);
  }, [location]);

  return (
    <div>
      <button
        onClick={() => {
          const r = Math.random();
          //window.history.pushState({ rnd: r }, "", "/?rnd=" + r);
          navigate("/?rnd=" + r, { state: { rnd: r } });
        }}
      >
        Click Me
      </button>
      <br />
    </div>
  );
};
export default RandLocation;

One issue with this approach, is you'd have to set up a default route to catch anything that no route is defined for like this:

 <BrowserRouter>
    <Routes>
      <Route path="/" element={<App />} />
      <Route path="*" element={<WhereYouWantDefaultRoutesToGoTo />} />
    </Routes>
  </BrowserRouter>

You might also want to take a look at: https://stackoverflow.com/a/70095819/122201

rainkinz
  • 10,082
  • 5
  • 45
  • 73
  • I am getting error: 'useNavigate' (imported as 'useNavigate') was not found in 'react-router-dom' . Secondly I have put the wrapped around , I am getting useLocation as undefined. – Abhinav Saxena Jan 09 '23 at 05:30