javascriptreactjsreact-routerwindow.location

useLocation location members are empty within HashRouter


When using a HashRouter, and within a component using useLocation, there is a discrepancy between the window.location object and the location object from useLocation.

As I am writing this I have noticed there may be a confusion around what useLocation is actually pointing to. The pathname from useLocation seems to actually be the window.location.hash without the #. But the hash seems to not be pointing at anything at all.

Honestly I wasn't sure if I should've posted this as an issue on react-routers Github, but I wanted to post this here in case there is some fundamental piece of info I'm missing about how useLocation is supposed to work (more importantly in relation to what router you are using).

Does useLocation behave differently when you wrap it in <BrowserRouter> vs <HashRouter> vs <MemoryRouter> etc.?

Values from window.location:

{
  pathname: "/myPage/"
  hash: "#/testpath/123"
  search: ""
}

Values from location = useLocation():

{
  pathname: "/testpath/123"
  hash: ""
  search: ""
  key: "abc123"
  state: null
}

Solution

  • When using a HashRouter, and within a component using useLocation, there is a discrepancy between the window.location object and the location object from useLocation.

    As I am writing this I have noticed there may be a confusion around what useLocation is actually pointing to. The pathname from useLocation seems to actually be the window.location.hash without the #. But the hash seems to not be pointing at anything at all.

    The HashRouter router uses the path defined after the first hash of the URL.

    For example, given:

    The location returned from the useLocation hook may look like:

    {
        "pathname": "/testpath/123",
        "search": "",
        "hash": "",
        "state": null,
        "key": "ff78cgdt"
    }
    

    As compared to window.location

    {
        "ancestorOrigins": {
            "0": "https://...."
        },
        "href": "https://..../#/testpath/123",
        "origin": "https://....",
        "protocol": "https:",
        "host": "....",
        "hostname": "....",
        "port": "",
        "pathname": "/",
        "search": "",
        "hash": "#/testpath/123"
    }
    

    Given:

    Then the location object would look like:

    {
        "pathname": "/testpath/123",
        "search": "",
        "hash": "#foo",
        "state": null,
        "key": "r7bfxju4"
    }
    
    {
        "ancestorOrigins": {
            "0": "https://...."
        },
        "href": "https://..../#/testpath/123#foo",
        "origin": "https://....",
        "protocol": "https:",
        "host": "....",
        "hostname": "....",
        "port": "",
        "pathname": "/",
        "search": "",
        "hash": "#/testpath/123#foo"
    }
    

    Notice the only difference now is that the "foo" is now part of the location object, the "hash" of the route path the app's router is using. It also is included in the hash of window.location, e.g. everything after the first hash.

    Does useLocation behave differently when you wrap it in <BrowserRouter> vs <HashRouter> vs <MemoryRouter> etc.?

    No, not really. These routers all manage and/or maintain their own internal history object and state, e.g. what location is in each context. The BrowserRouter couples internal routing/navigation actions to a browser's DOM, and similarly the HashRouter uses just the hash of what is effectively a static location, e.g. an app hosted/served from a specific directory on a server. The MemoryRouter is intended to be used in non-DOM environments, e.g. Node environments like Jest testing, etc.

    Depending on which router you use, and what the URL path/etc value is, some of the location properties may not be set, but the useLocation hook and location object is basically the same regardless.

    I don't believe there is a bug or issue to file here as everything you described seems like reasonable behavior given the HashRouter and the console logging you used.