Back

Learning React(JS) the hard way Part 4

Hey there pal,
another week another episode of by story on how I learned React(JS) the hard way. Since I never said that before and really want to clarify some things, a short passage before the real article begins.

The following post contains code, which is tested by myself. If you intend to use this piece of software feel free to do so. Please be aware that this is by far not production ready and should be reviewed by a person with an advanced knowledge of JavaScript. I don't claim to have this knowledge, neither do I want to transfer you knowledge on how to write a specific program nor is this a "How to JavaScript 101". I intend to tell you a personal and hopefully interesting story of my life. And I want to prevent people doing the same mistakes I did.

One of my biggest problems from the beginning was to decide between packages. I started reading a huge pile of blog posts about which package should be used in what some kind of case, but mostly it is opinion based. What I did not know was the fact that there could be packages which are better to use on frontend and packages you should definitely not use in your frontend application. Sucks, huh? I was searching for an extended Date API. As mentioned in my previous blog post each of my applications depends on calculating a bunch of dates in different units. For example adding 2 years to a date, or to determine if a user is above 18 or not. Basically all that stuff we learned in elementary school. But not for computers since they know how to use logical and mathematical operators by themselves they decided to drop out of school.

Since I was under a bit of time pressure and needed to get things done fast, I decided to check out several libraries. Keep in mind that I started this app literally from zero. No knowledge about how this new JS works and how to use it properly. And got stuck at some fancy ass library called moment-js. Spoiler alert, this is a huge library and it is able to do way more stuff than I needed. My thoughts about that tended to be super positive. I mean c'mon it's better to have what you don't need than needing something and don't have, right?

The downside

React is designed to serve a client rendered single page application.
... Can't you see it coming?

Turns out that sending big packages to your client is not so nice as you think!

I guess some people are thinking right now

Why do you stick with a library that does not fulfill your requirements?

Imagine the following scenario:
Since you are super interested in airplanes you decide to travel on your next vacation by plane, it turns out that you're the only person taking that particular flight, but the airline is using an AirBus A380, "Just for you".

Side facts:
If you deny using this aircraft because of the fact that this aircraft is way too big just for one person, you would need to try to take the next airplane with a 99.9% chance of not getting a seat, because this flight is already overbooked. Basically you would miss your holiday.

Now ask yourself these questions: Would you deny your flight? Or would you maybe start tearing apart a plane during flight just because you want to know how it works?

I know this scenario isn't as real as mine, I just wanted to put you in the same position in which I was not that long ago.

For those of you who read since post No. 1:

I know I mentioned in the previous posts that I am working with next-js, so the date calculation could take part during the cycle of rendering on server side. The following is just an example, which I still feel more comfortable about than using a huge 3rd party package. I can test and adjust the functionality as I want. This philosophy is manifested in my mind since I needed to write every single functionality of the software I created during assignments and tasks in university.

One day I came up with a plan, which is straight forward and was the first thing that came to my mind.

I gotta need to replace that flagship!

But you can't kill what you don't know. And knowing everything about your enemy isn't bad at all. I mean how else would you stalk someone if you don't know where he is, right? ┬┴┬┴┤(・_├┬┴┬┴

The plan

Let's break it down real quick. All we need to do is

  1. determine used functionality
  2. create requirements
  3. start creating the small package
  4. replace only imports
  5. fix bugs
  6. be happy

So all we need to do is replace the currently used package with a self written version just containing the required set of features. AWESOME!

I still haven't written any piece of code with raw JS OOP

First things first!

/*
* Constructors:
* - new Moment()
* - new Moment(secondDate, one of ['DD.MM.YYYY', 'YYYY-MM-DD', 'DD.MM.YYYY HH:mm'])
*
* Methods:
*   // diff between two Moment's
* - .diff(secondDate, one of ['m', 'd', 'y', 'minute', 'second', 'millisecond']])
*   // return in formatted string
* - .format(one of ['YYYY-MM-DD', 'DD.MM.YYYY'])
*   // add time unit to obj
* - .add(number, one of ['m', 'd', 'y', 'minute', 'second', 'millisecond']])
*   // determines if obj is valid
* - .isValid()
*   // getter for date units
* - .get(one of ['m', 'd', 'y', 'minute', 'second', 'millisecond'])
*   // explicit getter for Moment's weekday (and for real, we need to do it right!)
* - .weekday()
*/

I started object oriented programming by being forced to use C++.

Today is the day! I am finally able to announce that I miss C++.

You said you don't want to bash JS anymore!

True. Immutability of objects can be gained by 3rd party packages, and there are even some packages which give you the ability to override operators. Like immutable and operators. There're even some articles on DYI operator overriding which are damn good and super helpful. But on the other hand, again you depend on 3rd party software and the knowledge of people writing articles. Do we really trust them enough to ship production ready code? Do we want our customers to may be scammed or having a really bad experience, because the code wasn't cross platform compatible? I may sound like a total moron and I should give more trust in the OS community. But the fact that I keep bumping into outdated packages is soooo fucking annoying. For real.

Anyway, today I'm gonna start with my class Moment

Let's see, I know from React that there's a keyword class, but I also know – from reading some articles – that one can use function for classes as well. Still don't know the difference yet, so I would prefer the class version of a class.

class Moment { };

There we go. React also introduced me to a constructor of an inherited model, which is obviously called operator. If you inherit from another class you should add some parameter to get passed to constructor and "super"-call them, to execute the parent constructor.

Since we don't inherit and want to have a base class for our purpose we can ignore that.

class Moment {
    constructor() { };
}

Okay, now we have three requirements:

First task? easy.

class Moment {
    constructor (date, format) { };
}

You wish! Second task. The docs weren't saying anything about that, which feels super fucked up... BUT google revealed the holy grail which is basically saying one should tell the constructor all possible parameters, than handle each combination as needed. MANUALLY. At least we stay within our DRY ideology, right? I was even reading about a way to get rid of keyword new. ²
Awesome, let's do it!

Before revision

Update 2019-06-09

Unfortunately the "hack" to replace the keyword new is not working as expected, hence I removed it.

class Moment {
    // passing the needed parameters
    constructor (date, format) {
        // using this fancy hack to replace `new`
        if (!(this instanceof Moment)) return new Moment(date, format)¹;
        // handling each type by itself…
        if(typeof date === 'undefined' && typeof format === 'undefined') 
            // parse date
        else if (typeof date === 'string' && typeof format === 'string') 
            // parse date
        else if (typeof date === 'object' || typeof date === 'number') 
            // parse date
        else throw 'Construction Error';
    };
}

class Moment {
    // passing the needed parameters
    constructor (date, format) {
        // handling each type by itself…
        if(typeof date === 'undefined' && typeof format === 'undefined') 
            // parse date
        else if (typeof date === 'string' && typeof format === 'string') 
            // parse date
        else if (typeof date === 'object' || typeof date === 'number') 
            // parse date
        else throw 'Construction Error';
    };
}

Wow, I even added a meme...

Me: passing wrong params to Moment
Moment: CONSTRUCTION ERROR!!!!!!1!11!

Not that bad for a first approach, one step left until we can use this rocket. Parsing the data is probably the most important thing for a construction, just joking. But for real, how to parse it in a nice way to avoid duplication and to use the data as needed?

class Moment {
    // passing the needed parameters
    constructor (date, format) {
        // handling each type by itself…
        // create as today
        if(typeof date === 'undefined' && typeof format === 'undefined')
            this.date = new Date();
        // parse date through string
        else if (typeof date === 'string' && typeof format === 'string')
            this.date = new Date(date);
        // parse date through Date object 
        // or number (milliseconds from `new Date().valueOf()`)
        else if (typeof date === 'object' || typeof date === 'number')
            this.date = new Date(date);
        else throw 'Construction Error';
    };
}

But wait, that's shit!

We don't use format at all, why do we even need it?

It was part of the requirement, moment-js parses strings with a format string. I want to keep that to minimize the refactoring costs (even though it's almost nothing).

We can simplify the 2nd and 3rd statement to share one case

Cheers, mate! That's some fine idea. I would also remove the test for format, since we don't care about it at all. Another improvement I had in mind was to keep the raw value of a Date object, since we are so smart and calculate with milliseconds like there's no tomorrow. Keeping the data type simple to gain performance and built-in features feels like a good decision.

Improvement

class Moment {
    // passing the needed parameters
    constructor (date, format) {
        // handling each type by itself…
        if(typeof date === 'undefined' && typeof format === 'undefined')
            this.date = new Date();
        else if (typeof date === 'string'
            || typeof date === 'object'
            || typeof date === 'number')
            this.date = new Date(date);
        else throw 'Construction Error';
        // parse the value for faster operations
        if(this.date) this.value = this.date.valueOf();
    };
}

Don't you hate me?

This series of blog posts turns out to be waaaaay bigger than expected, but I am happy every day to see this blog evolving and I am really excited for each visitor on my page.

For all of you who are into security and the fact that even in the "free web" you are watched by everyone, I totally get the point of protecting your own privacy. This page uses – as you can see within it's code – Google Analytics. (For more information check out the privacy page). Which will be one of the topic of one of my future posts. But maybe in a way you don't know it yet. (̿▀̿ ̿Ĺ̯̿̿▀̿ ̿)̄

Next week I will continue creating the small date library to get finally rid of 50% of the size of my app! Incredible un-zipped 200kB.

Enjoy your weekend

Catch you on the flip flop!

¹: Updated 2019-06-09

Credits