Hooking into Gatsby's navigation changes
Published: 2022-05-09 13:57:04

---
title: Hooking into Gatsby's navigation changes
published: true
date: 2020-07-19 00:00:00 UTC
tags: 
canonical_url: https://carl-topham.com//articles/hooking-gatsby-navigation-changes
---

On my site there is a toggling navigation menu. But when I toggle it open and then click a link it stays open. This is not right - it should close when you change location. Let's fix it using some really simple hooks in a few minutes. First lets see what we have to start with.

```
// Original basic version of my Nav component
import React, { useState } from "react";

const Nav = ({ nav }) => {
    const [open, setOpen] = useState(false);

    return (
      <div>
       <button
          type="button"
          onClick={() => {
              setOpen(!open);
          }}
      >
        Toggle
      </button>
        <div className={`${open ? "show" : null}`}>
          <Link to="/">Home</Link>
          <Link to="/about/">About</Link>
        </div>
      </div>
    )
}

```

This will let the user toggle the nav and see the links by toggling `setOpen`. They can then navigate to a new page, but because the `Nav` exists higher up the nesting it never gets re-mounted so `open` stays in the same state which is `true` - which is open.

We need to hook into navigation changes - these are from the router and (luckily) are in the context sent to the page. Let's pull out the data.

```
// Now with some location awareness
import React, { useState } from "react";
import { useLocation } from "@reach/router"; // NEW

const Nav = ({ nav }) => {
    const [open, setOpen] = useState(false);
    const location = useLocation(); // NEW

    return (
      <div>
       <button
          type="button"
          onClick={() => {
              setOpen(!open);
          }}
      >
        Toggle
      </button>
        <div className={`${open ? "show" : null}`}>
          <Link to="/">Home</Link>
          <Link to="/about/">About</Link>
        </div>
      </div>
    )
}

```

The component is now aware of the location, but had no lasting memory so it does not know if anything has changed. Luckily we can use a hook to track that.

```
// Now with some location awareness
import React, { useState, useRef } from "react"; // UPDATED
import { useLocation } from "@reach/router";

// New hook
const usePrevious = (value) => {
    const ref = useRef();
    useEffect(() => {
        ref.current = value;
    });
    return ref.current;
};

const Nav = ({ nav }) => {
    const [open, setOpen] = useState(false);
    const location = useLocation(); 
    const prevLocation = usePrevious(location); // NEW

    return (
      <div>
       <button
          type="button"
          onClick={() => {
              setOpen(!open);
          }}
      >
        Toggle
      </button>
        <div className={`${open ? "show" : null}`}>
          <Link to="/">Home</Link>
          <Link to="/about/">About</Link>
        </div>
      </div>
    )
}

```

Now we have the current and previous locations it's a simple step to compare them. To make sure this only happens when the location changes we will use a `useEffect()` and set the inputs to `location` and `prevLocation`.

```
// Now with some location awareness
import React, { useState, useRef, useEffect } from "react"; // UPDATED
import { useLocation } from "@reach/router";

const usePrevious = (value) => {
    const ref = useRef();
    useEffect(() => {
        ref.current = value;
    });
    return ref.current;
};

const Nav = ({ nav }) => {
    const [open, setOpen] = useState(false);
    const location = useLocation(); 
    const prevLocation = usePrevious(location);

    // NEW
    useEffect(() => {
        if (location !== prevLocation) {
            setOpen(false);
        }
    }, [location, prevLocation, setOpen]);
    
    return (
      <div>
       <button
          type="button"
          onClick={() => {
              setOpen(!open);
          }}
      >
        Toggle
      </button>
        <div className={`${open ? "show" : null}`}>
          <Link to="/">Home</Link>
          <Link to="/about/">About</Link>
        </div>
      </div>
    )
}

```

With the final update the `Nav` will now "listen" to location changes. When it changes and the location does not equal the previous one we set the state to false, closing the toggled menu.