DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for The Chronicles of a Rookie Developer: JavaScript Insights
Joohyun "Steve" Kim
Joohyun "Steve" Kim

Posted on

The Chronicles of a Rookie Developer: JavaScript Insights

Pretext

After building out my first-ever vanilla JS + Rails API application, I thought back on the experience and skimmed through my code to gather ideas on what I could blog about. The truth is, there were many bits and pieces that I learned but I didn't feel like I knew nearly enough about each topic to solely dedicate an entire blog post. (Not to mention I also suffer from chronic indecisiveness.) So I apologize in advance but I decided to just list them all. Here are 4 things I learned, attempted, or simply found interesting about JavaScript.

One. To function, or not to function, that is the question.

In JavaScript, and particularly vanilla JS, you're most likely going to declare and use these so-called node getters where you grab elements from the DOM using a method such as (but not limited to) querySelector() in combination with one or more valid CSS selector strings (like classes or ids). And when you find yourself querying for the same node multiple times at difference places in your code, it makes sense to assign it to a variable and extract it to a more global scope, right?
Now, I know that when we first start learning to code, this notion of declaring variables in the global scope is a big no-no and it gets reiterated over and over as we're often told that it can lead to some unexpected behaviors down the road and should therefore -- when possible -- be avoided. But for now, let's just assume that our application is extremely small and there is very little to no reason to sound the alarm. The question, then, is whether to wrap these node getters into a function or to just have it point directly to the node.
For instance, you might've seen someone do something like
const mainContainer = () => document.querySelector('div.main')
versus something like
const header = document.querySelector('#header').
So which way is better? The answer, I've found, is that it really depends.

There's this subtle yet sweet balance between what you are trying to accomplish with the node and whether the node exists or doesn't exist inside a particular execution context.

The benefit of using a function to grab your node is that you can always ensure you are selecting the most recent version of it since you are calling the query at the exact moment you need it. This is particularly useful when perhaps you need to grab a node that doesn't exist immediately on initial load but is something that gets created later in your code (as a result of a callback, for instance). I'd say 9 times out of 10, this is probably going to be your preferred method.

However, the opposite can also be true. I ran into an occasion where it made more sense to point directly to the node, and it was something as simple as wanting to remove() a header from the page and later re-attach that same exact node back on the DOM. In this case, if we were to use a function, we would not be able to find the node since it would no longer exist on the document and we would instead have to re-create the element. But luckily for us, JavaScript has garbage collection, meaning it will store it in memory and give us access to it even after it's been removed from the DOM as long as we point it to a variable. It then goes without saying that this would probably merit some thought in all future applications in order to ensure that we are always effectively preventing unnecessary memory leaks.

Two. Two is always better than one, or is it...?

The thing I'm talking about here is none other than the DRY principle of programming and my humble attempt to combine two functions into one. I've learned by now that refactoring code is just a regular part of any programmer's life and should become second nature sooner rather than later. So as I examined for areas to abstract, I noticed that my fetch functions were carrying a lot of code and particularly noticed similarities between my POST and PATCH requests. The data being sent was identical, the fetch url was identical except for the presence of an id (thanks to RESTful routing), and the init object was identical except for the HTTP method. It seemed to check everything off the list for a potential refactor.

First I should start by mentioning that I've built my application in an object-oriented pattern using ES6 class syntax and therefore have a separate Adapter class that handles all fetches.
Sticking to conventions of CRUD, I've got a static createRecord() and updateRecord() method within the Adapter class, each responsible for sending the appropriate fetch request. I also have one other static property, baseURL, and two other static methods defined, setDataObj() and setInitObj(). baseURL (as the name suggests) simply stores the base URL to which we will be sending our fetch requests while setDataObj() is responsible for constructing the object that we will be sending off to the backend and contains all of the input values from a form. It looks something like this:

static setDataObj = () => {
    const name = form().querySelector('#name').value;
    const description = form().querySelector('#description').value;

    const data = {
        name,
        description
    };

    return data;
};
Enter fullscreen mode Exit fullscreen mode

Notice that it returns the data object, as this will be useful to us in the next step.
Then in my setInitObj() method, I take in a string argument for the type of request -- POST, PATCH, or PUT -- then use it to build out an options object.

static setInitObj = (method) => {
    const options = {
        method: method,
        headers: {
            "Content-Type": "application/json",
            Accept: "application/json"
        },
        body: JSON.stringify(Adapter.setDataObj())
    };

    return options;
};
Enter fullscreen mode Exit fullscreen mode

Notice that I am now able to simply pass in Adapter.setDataObj() to JSON.stringify() since we made it return the object we need.
setInitObj() then returns yet another object, which we can use in our fetch method both in createRecord() or updateRecord(), passing in the proper HTTP verb with it:

static createRecord = () => {
    fetch(Adapter.baseURL, Adapter.setInitObj('POST'))
        ...
};
Enter fullscreen mode Exit fullscreen mode
static updateRecord = (id) => {
    fetch(Adapter.baseURL + id, Adapter.setInitObj('PATCH'))
        ...
};
Enter fullscreen mode Exit fullscreen mode

Now in my event handlers, or anywhere in my code where I need to send off the form data, I can simply call Adapter.createRecord() or Adapter.updateRecord(id).

Three. Being conscious of how often we fetch.

Personally, this was more of a food for thought than anything else because I'm guilty of not following my own advice but I did realize the importance of code design, particularly how I wish to maintain the data on the front-end after a fetch. Because this was my first experience with JavaScript, and especially because I was using functional vanilla JS instead of object-oriented JS or some other library, I chose the easy way out and saved myself a few headaches by re-fetching at times. Not ALL the time, but in certain situations. At least now I definitely see how I could have done things and it will come in handy as I think about incorporating more features on my app like sorting and filtering.
(Note: This was something I wanted to share at the time of originally writing this post but have since rebuilt my entire project into object-oriented JavaScript and am now dealing with my front-end data in a much different manner.)

Four. Default behavior of an HTML5 button is submit.

Last but not least, I was enlightened during one of my debugging sessions that, apparently, the default behavior of a button is submit unless specified otherwise. Maybe this was just me but for some reason I always thought <button> tags did not have a default behavior as opposed to <input type="submit">. This would matter less if the button isn't enclosed in a <form> but if you're ever trying to use buttons inside a form for something other than submitting the form, you have to either specify the button's type attribute as type="button" or, alternatively, use preventDefault() in its event handler. Again, this might've been obvious to most but I hope someone else out there might find it useful to know!

If you've made it this far, thank you for reading. Stay tuned for the next edition of The Chronicles of a Rookie Developer as I continue my journey! Until next time!

Top comments (0)

πŸ‘‹ Hey, my name is Noah and I’m the one who set up this ad. My job is to get you to join DEV, so if you fancy doing me a favor, I’d love for you to create an account.

If you found DEV from searching around, here are a couple of our most popular articles on DEV: