Replacing react-datepicker with HTML input element of type date

I replaced “react-datepicker” in two of my react applications, with the built-in <input type=”date”/> date picker, and the resulting reductions in bundle.js size were so significant I felt the need to blog about it.

I recently was told about <input type=”date”/> by a colleague, who described as “the simplest datepicker you can use”.

At the time, I was using “react-datepicker“, which I had been using since 2018.

react-datepicker_example.png

During the time I’ve used react-datepicker, I’ve had two problems with it:

  1. I was stuck for a long time on the increasingly outdated react-datepicker version 1.5.0, because of an undesirable change in the react-datepicker behaviour. I eventually discovered a workaround that let me upgrade to a recent version.
  2. Earlier versions of react-datepicker had moment as a dependency, and moment is currently deprecated by its authors. The need to remove the moment npm dependency was what finally pushed me to try the above workaround, so that I could upgrade to a version of react-datepicker that used javascript Date instead of moment

With this in mind, I decided to give <input type=”date”/> a try, and it turned out to be straightforward and easy.

One objection to using <input type=”date”/>, is that there is no way to control the way it looks like. The browser determines the way this date picker looks and behaves, so the look and feel will be different across different browsers and/or OSes. On windows chrome, the date picker looks like this:

input_type_date_example.png

Personally, I have no particular preferences for how a date picker should look. For me, it’s enough if users understand that they can adjust a date, and understand how to adjust the date. And in that respect, as far as I can tell, the <input type=”date”/> control is neither better nor worse than react-datepicker.

Earlier, when I was switching from using moment to using javascript Date with react-datetime, I ran into a problem with using Date in redux, because Date isn’t serializable. However, the result of the Date.toISOString() method could be used as a human readable string serialization form. Jackson on the server side would also understand, and parse correctly, dates in ISO string format.

So my solution at that time, was use the output of Date.toISOString() in redux and in JSON payloads sent to the server. This approach also turned out to be debugging-friendly.

A typical Date ISOString, looks like this:

2022-06-18T18:51:28.959Z

Note: Even though it is displayed as a date, what I store is actually a date and time-of-day value. This is so I can rank transactions chronologically within a day (the exact time of day doesn’t matter to me, but the order of transactions matter).

The way the date is displayed in <input type=”date”/> is determined by the locale of the web browser and not settable in any way from inside the webapp, and will change if the locale is changed to a locale that shows the date differently. The screenshot of <input type=”date”/> usage above, is from chrome running on a windows computer with an English (United States) locale setting.

But what’s used internally is independent of the locale setting, and is always an ISO date, i.e. something like this:

2022-06-18

I.e. the date has the same form as the date part of the date-and-time ISO date.

From this lucky coincidence, it was easy to preserve the time-of-day of the ISO date and time in redux, by picking out just the date from the redux value in the component:

const purchaseDate = useSelector(state => state.purchaseTime.split('T')[0]);

and then use the date in the <input type=”date”/> element:

<input
    id="date"
    className="form-control"
    type="date"
    value={purchaseDate}
    onChange={e => dispatch(PURCHASE_DATE_CHANGE(e.target.value))}
/>

and then joining the changed date up with the time-of-day from the existing state of the redux value, in the reducer:

const initialValue = new Date().toISOString();

const purchaseTimeReducer = createReducer(initialValue, {
    [PURCHASE_DATE_CHANGE]: (state, action) =>  action.payload + 'T' + state.split('T')[1],
    [TRANSACTION_RECORDED]: () => new Date().toISOString(),
});

Two real life examples of this change, can be found here:

  • ukelonn (an allowance app for my kids)
  • handlereg (a grocery shopping registration and statistics app)

The change from react-datetime to <input type=”date”/> was simple, used approximately the same number of lines of code, and the end result was functionally equivalent, but not visually identical to the original.

However my main motivation for replacing react-datepicker with <input type=”date”/>, wasn’t simplifying the code. My main motivation was to get rid of npm dependencies and simplify the build.

And the diffs of package-lock.json were big enough that github hid them by default (as can be seen by clicking on the links to the real life examples), which was promising.

Removing the react-datepicker dependency removes 1 direct dependency (i.e. react-datepicker itself), and removes 7 transitive dependencies from package-lock.json.

However, while the package-lock.json diff looked impressive, the actual reduction in size of the package-lock.json files was disappointingly small (all sizes in number of bytes):

project original new diff diff %
ukelonn 508398 501646 6752 1.3
handlereg 491465 483660 7805 1.6

The reduction in the size of bundle.js, however, was significant (all sizes in number of bytes):

project original new diff diff %
ukelonn 715711 483151 232560 32.5
handlereg 517423 295702 221721 42.9

I was interested to see if the significantly reduced bundle.js size had any effect on frontend build times, and it did reduce the build time a little, but not enough to completely turn my day around (all build times in seconds):

project original new diff diff %
ukelonn.web.frontend 19.686 17.309 2.377 12.1
handlereg.web.frontend 16.474 14.108 2.366 14.4

The build time is averaged over 10 runs, with the react-datepicker version getting an extra build first, to fill up all of the extra npm dependencies in the node_modules directory.

The build was done as a reactor build of the parent project, and was done on a computer, with i5-6600 “skylake” 3.3GHz CPU, 16GB RAM, debian 11.3 “bullseye”, maven 3.6.3, java 11.0.15, frontend-maven-plugin 1.12.1, node 16.14.0, webpack 5.37.0.

Summing things up:

  1. <input type=”date”/> is, for my use, feature-equivalent, with react-datepicker, and the code complexity for using it, is the same
  2. No workaround to the standard behaviour required
  3. It gets rid of 1 direct npm dependency, and 7 transitive dependencies, which is a good thing. 8 fewer dependencies to get security alerts, 8 fewer things to break the build
  4. It cuts down the build time a little, which is a good thing
  5. It cuts down bundle.js size a lot, which is a very good thing

So for me, only upsides.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.