Smart Navbar With React And Sass
Tags: scroll, nav, tutorial
Published: 2022-09-09 12:38:56

## Introduction

>This tutorial assumes you have some knowledge on React and Sass (scss).

Sticky or fixed navigation bars is a popular design choice since it gives users an easy-to-reach access to navigate a web application or website. 
However, it takes up space especially on smaller screens and might cover content in a way that's not appealing.

### Possible Solution :- Smart Navbar

A smart navbar stays visible when a user is at the top of the page. The navbar is also visible when the user makes a scroll upwards from wherever they are on the page and it is hidden when the user makes a scroll downwards of the page.


#### Let's look at the code for the .jsx

```jsx
function Navbar(){

  return (
    <nav className='nav'>
      <div className='mainDiv'>
        <span>
          Logo
        </span>

        <ul>
          <li>Link 1</li>
          <li>Link 2</li>
          <li>Link 3</li>
        </ul>
      </div>
    </nav>
}
```

#### And the code for the .css

```scss
.nav {
  position: fixed;
  width: 100%;
  z-index: 999;
  top: 0;
  right: 0;
  left: 0;
  padding: 0.3rem 0;
  transition: all 200ms ease-in-out;

  .mainDiv {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
}
```
 
We can define some state variables to keep track of the nav and modify it accordingly 


```jsx
const [atTop, setAtTop] = useState(true);
const [scrollDirection, setScrollDirection] = useState('');
```

We need to detect when a user is scrolling the page and the direction of their scroll. A user is scrolling down if the value of their last scroll position is less than the value of their current scroll position.

```jsx
useEffect(() => {
    const threshold = 0;
    let lastScrollY = window.pageYOffset;
    let ticking = false;

    const updateScrollDirection = () => {
      const scrollY = window.pageYOffset;

      if (Math.abs(scrollY - lastScrollY) < threshold) {
        ticking = false;
        return;
      }
      setScrollDirection(scrollY > lastScrollY ? "down" : "up");
      lastScrollY = scrollY > 0 ? scrollY : 0;
      ticking = false;
    };

    const onScroll = () => {
      if (!ticking) {
        window.requestAnimationFrame(updateScrollDirection);
        ticking = true;
      }
    };

    window.addEventListener("scroll", onScroll);

    return () => window.removeEventListener("scroll", onScroll);
  }, [scrollDirection]);
```

We can use this logic to detect when the page is scrolling down and toggle our styling 

```jsx
// navbar.jsx

<nav className={'nav ${scrollDir === 'down' ? 'isScrollingDown' : '' }'}>
...
</nav>
```

If the user is scrolling down we add the `.isScrollingDown` class and if the user is scrolling up, we simply remove the class

```scss
// style.scss

.isScrollingDown {
  transform: translateY(-100%);
}
```

## Performance

When working with scroll events, it is important to pay great attention to performance as we might call a function too many times which can have adverse effects.
In the above `useEffect` we only update the state `scrollDirection` when the user scrolls in the opposite direction. 

We can also check for when the user is at the top of the page and utilize a throttle function to update styles or perform other logic. 

Briefly, a throttle function can be defined as a higher order function that acts as a timer for the function passed into it.
Let's see an example of a throttle function

```jsx
// initialize a throttleWait variable for performance
  let throttleWait;

  const throttle = (callback, time) => {
    // if the variable is true, don't run the function
    if (throttleWait) return;

    // set the wait variable to true to pause the function
    throttleWait = true;

    // use setTimeout to run the function within the specified time
    setTimeout(() => {
      callback();

      // set throttleWait to false once the timer is up to restart the throttle function
      throttleWait = false;
    }, time);
  };
```

We can make use of the throttle function in a useEffect to perform logic as we wish.

```jsx
const navbarControl = () => {
   if (window.scrollY < 50) {
     setAtTop(true);
   } else {
     setAtTop(false);
   }
};

useEffect(() => {
    window.addEventListener("scroll", () => throttle(navbarControl, 500));

    return () => {
      window.removeEventListener("scroll", throttle);
    };
  }, []);
```

With this implementation, the `navbarControl` function is called only once every 500 milliseconds


## Conclusion

So, there we have it: a smart navigation implementation with React and Sass for styling. Now users have easy access to navigate the web app/website without losing real estate in a way that blocks content on the page.