React Navbars with PopStateEvent
Tags: react, javascript
Published: 2020-07-13 01:00:32

Continuing with the React theme, I wanted to touch on a task that always comes up when you're building a website, and that's the building of a navbar. With a navbar, you can easily show the layout  of your website with the use of links neatly organized at the top of your screen, showing the main pages of your website or links relevant to your current activity. I don't think I've seen a website without one. Since I do it so often, I thought it might be worth it to cover step by step on how to implement one. 

I thought it would be particularly useful to go over the steps on how to do this particular navbar. The specific advantage that this method provides is that if you create a navbar that uses a tags with href, the request made from that link is rendering the whole html document onto the window everytime. This can lead to some performance issues. This method actually uses the window's history object and built in methods that allow us to utilize the state of our React Component, without doing the rerender. You will notice this rerender's many network requests when you look at the network tab in your developer tools. 

As a disclaimer, this isn't the only way to make a navbar, and it's actually not the first one that I learned. There is actually a built in method that comes with React Router DOM package that is included when you start a React project. If you are interested in this method, this website has documentation for multiple ways you can implement the method with the included method, [here](https://reactrouter.com/web/guides/quick-start).

There will be three main components that we need to build out when implementing this navbar, and those would be the header, link, and route components. We will be integrating the use of our other components like the page components with these.

**App**

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/8958y5lcf21bqlpej7lz.PNG)

First, let's take a look at an overview at the app level. Here, we can see all the header and the route components that we just talked about. Before moving onto the details of the components builds, there are two details worth noting: 1) We are passing down a prop of route to the component, and 2) we are also nesting the various elements we want to display within the Route component. The first detail's purpose is more obvious, but we need to know that the second one is exactly doing. When you nest anything whether it be some text or a component, the component you are using to nest that element will now be passed down a prop with the name of `children`. This ability to pass down nested components information is important in how we build out this particular navbar.

**Header**

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hj62lrbzpaxbgfyzlnz6.PNG)

Having just talked about some of our Route's functionality, I think it will be easier to follow the function of our app's logic by starting with the header, so we can follow the processing of our user's click. 

So here we have our header, which overall is the basic backbone of our navbar: a div that holds a bunch of Link elements with props passed down similar to the ones we passed down in the Route component, which were the path name(here written as href) and the nested text component passed down as `children`. This particular one has some Semantic UI styling which I find to be much easier to handle.

**Link**

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/dcb7e60m2r0evynejdy4.PNG)

Here we have our Link components. You'll notice that it's just an `a` tag with the props we passed down from the header component. This is the link that the user will be clicking within the navbar that are differentiated based on the props that we passed down from the header component.

The main point of this component is the onClick function that we wrote inside it. The href prop that we passed down to the Link component is used in an onClick function that uses `window.history.pushState({}, '', href)`. The parameters here stand for the state, title and href respectively. Everytime you use the pushState function here you are essentially pushing a new entry onto the history stack. This stack represents your current window's browsing history, which if you console log in the window right now, you will get back a result with a certain length based on how many times you changed pages. This pushState function will essentially be responsible for changing the browser's url, and in our case to the path name we passed down as href. 

The next step is to add a PopStateEvent which is an event that is triggered whenever the user navigates and changes the history object's state. This is an event that is normally triggered whenever the history entry changes, and in our case, we are doing this state change by using `pushState` to change the history object's url. PopStatEvent allows us to detect that this has taken place.

**Route**

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/kbyt9yqyk9f4yj2i32ux.PNG)

Finally, here is our route component. In connection to our Link component, we want to listen for whenever that PopStateEvent is triggered, because that is when window knows the url has been changed. 

Here we add that event listener to the window with the type parameter of 'popstate', which allows us to run a function anytime that PopStateEvent is triggered. In our case, we are using a state object to store the current window location pathname, which we are using to affect each route component. At the bottom we have included a ternary operator to detect if the route's path prop matches the `window.location.pathname` after the PopStateEvent, then we will display that page. At the end we use the cleanup function to remove the eventListener in case the component is removed. 

Now when you run your application, you should notice that the applications navbar is functioning properly, without a rerender 

**Overview**

So that it's easy to visualize, I will just finish with a whiteboard diagram of the navbar component we just created. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/x1qmwp1beu99zquxys06.PNG)