Smart solutions to avoid JS problems and hacks
Tags: javascript, react, css, webdev
Published: 2020-12-05 12:51:55

In this day and age being a developer and being in a fast-paced environment we have to deliver as quickly as possible.

While trying to deliver quickly, we end up building functionality in an `un-optimised` way. We start using the first solution on stack-overflow which might not always be the most efficient or correct solution and may even be a `hack`.

I have listed down some of the inefficient code snippets and hacks that i have seen in multiple codebases and the correct way to tackle those. Let's Begin!

___

![Showtime](https://i.giphy.com/media/f6z5TkrTIBZILYOd1t/source.gif)
___


## **1. Native way to reset the window scroll position when navigating between web pages**

Today many modern browsers have a tendency to remember the scroll position when navigation between pages on a website, while sometimes that can be very helpful, but at the same time it can cause a problem as well.

When you want to reset the page data or make a API call every time the page is loaded to keep the page updated, this can cause major problems. 

Since the browser will always scroll to the previous scroll position and not on the top as expected.

Now in multiple codebases I have seen this handled using `window.scrollTo(0,0)` on the page mounts. It's a bit laggy since it works after the first paint has happened.

But if we can disable the functionality of the browser to remember the scroll position, then we don’t need to add the hack. That's it.

```javascript
if (window.history.scrollRestoration) {
  window.history.scrollRestoration = 'manual'; //default is 'auto'
}
```

![solved](https://i.giphy.com/media/5z0cCCGooBQUtejM4v/giphy-downsized.gif)
___

## **2. Easy and precise way to validate a URL without regex**

I think one of the most searched questions and the most answered one is how to validate a basic URL in JS. And I have seen many different types of regex, string matching solutions for it.

But there is a simpler solution using new native URL constructor.

```javascript
const validateURL = url => {
  try {
   new URL(url)
   return true
  } catch {
   return false
  }
}

```

![shock](https://i.giphy.com/media/5VKbvrjxpVJCM/giphy-downsized.gif)
___

## **3. Always add throttle or debounce on event listeners like scroll or resize**

Whenever you’re listening for events on the page, it’s important to make sure that the event listeners don’t get overwhelmed with processing incoming requests. 

Otherwise they can quickly become a bottleneck and cause an unnecessary performance hit.

Where this often turns into an issue is when you have listeners that fire off events in rapid succession, like for scroll on mouse-move or keydown events. 

Since scroll events, for instance, can fire off at such a high rate, it’s critical to make sure that the event handler isn’t doing computationally expensive operations. Because if it is, it will be all the more difficult for the browser to keep up.

* Throttled Version:

```javascript
const throttle = (fn, wait) => {
    let time = Date.now()
    return () => {
        if ((time + wait - Date.now()) < 0) {
            fn()
            time = Date.now()
        }
    }
}

const cbFuncOnScroll = () => {
    console.log('scroll')
}

const throttledfunc = throttle(cbFuncOnScroll, 200)

document.addEventListener('scroll', throttledfunc)
```

* Debounced Version:

```javascript
const debounce = (func, delay) => {
    let timeout = ''
    return function() {
        clearTimeout(timeout)
        const context = this
        const args = arguments
        timeout = setTimeout(() => {
            func.apply(context, args)
        }, delay || 500)
    }
}

const cbFuncOnScroll = () => {
    console.log('scroll')
}

const debouncedFunc = debounce(cbFuncOnScroll, 200)

document.addEventListener('scroll', debouncedFunc)
```

* Bonus: Debounce with Window RequestAnimation Frame (**Best**)

```javascript
const debounceUsingAnimationFrame = (fn, ...args) => {
    // Setup a timer
    let timeout
    // Return a function to run debounced
    return () => {
        // Setup the arguments
        const context = this

        // If there's a timer, cancel it
        if (timeout) {
            window.cancelAnimationFrame(timeout)
        }

        // Setup the new requestAnimationFrame()
        timeout = window.requestAnimationFrame(() => {
            fn.apply(context, args)
        })
    }
}

const cbFuncOnScroll = () => {
    console.log('scroll')
}

const debouncedAnimationFrameFunc = 
        debounceUsingAnimationFrame(cbFuncOnScroll, 200)

document.addEventListener('scroll', debouncedAnimationFrameFunc)
```

> ####Protip: `document.addEventListener('scroll', cbFuncOnScroll, { passive: true })`, here passive which is set to `true` will tell the browser that you just want to do your stuff and you are not gonna call preventDefault. Here is the video showing the performance improvement caused by this property - https://youtu.be/NPM6172J22g
___

## **4. Cross browser styling can be achieved from CSS**

Cross browser development is one of the most important skills that a Frontend developer should have, and we have always been there when we might need to tweak the styling of a component on a different browser, due to incompatibility of certain css properties.

What do you do to achieve this, the most common solution I have seen is via JS where we extract the UserAgent or Platform and based upon that, we apply styles on the component.

But is that the correct and only way to do it ?

Here is my solution

* Safari target CSS query

```CSS
@supports (-webkit-touch-callout: none) {
   // add styles here to override for safari
}
```

* Mozilla target CSS query

```CSS
@-moz-document url-prefix() {
   // add styles here to override for mozilla firefox
}
```

* IE11 target CSS query

```CSS
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
  // add styles here to override for IE11
}
```

This is a simple way to override or add styles for specific browsers without JS.

![easy](https://i.giphy.com/media/Vgxo23W1roB2QUrULx/giphy-downsized.gif)

___

## **5. Lazy render the components using CSS**

We have worked on large components which consist of multiple small components, among those small components not every component is actually visible inside the viewport initially.

But only visible when a user scrolls, but we normally load all the components and render them on the viewport.

A good JS solution here is to use IntersectionObserver API to handle the rendering of the component only when they are in focus. This solution is a good one since Observers work on a different thread and not hamper the performance on the main thread. 

But what if i tell you there is a better solution to it without using JS but only CSS.

Here comes `content-visibility` property which enables the user agent to skip an element's rendering work, including layout and painting, until it is needed. 

Because rendering is skipped, if a large portion of your content is off-screen, leveraging the content-visibility property makes the initial user load much faster. 

It also allows for faster interactions with the on-screen content. Pretty neat.

```CSS
.element {
  content-visibility: auto;
}
```

> ####Protip: Lots of detailed explanation on content-visibility - https://web.dev/content-visibility/

___

## **6. Avoid code redundancy when adding try catch to API side-effect calls**

The most common task we always perform when developing features is to make API calls in order to fetch data to display it on the page.

But since it's a side-effect and we have dependency on other services. 

We tend to always wrap our API calls inside a try and catch statement to be on the safer side and handle errors gracefully.

But don’t you feel it adds too much boiler-plate code to every API call that we make ?

Here is a simple promise based solution to avoid excessive use of try-catch block

```javascript
const sideEffectAPIWrapper = (promise) =>
    promise
    .then(result => [result, null])
    .catch(err => [null, err])

const sampleFunc = async () => {
    const [result, error] = await sideEffectAPIWrapper(callAPI())
    if (result) {
        console.log('success')
        return
    }
    if (error) {
        console.log('failure')
        return
    }
}
```

![done that's it](https://i.giphy.com/media/41fcwvXbVM7rACpN52/giphy.gif
)

___


## **Conclusion**

All the points I have mentioned are problems that I have faced and seen in development of web applications. I am sure you might have also encountered these points in your codebases.

One simple way to avoid hacks and redundancy is to ask yourself can there be a better alternate way to achieve this functionality. 

This simple question when you ask yourself while writing or reviewing the code will always help you make a good decision and avoid future problems on performance and code efficiency in your codebase. 

That's all folks, adios amigos

![adios amigos](https://i.giphy.com/media/ZBVhKIDgts1eHYdT7u/giphy.gif)