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.
During the time I’ve used react-datepicker, I’ve had two problems with it:
- 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.
- 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:
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:
- <input type=”date”/> is, for my use, feature-equivalent, with react-datepicker, and the code complexity for using it, is the same
- No workaround to the standard behaviour required
- 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
- It cuts down the build time a little, which is a good thing
- It cuts down bundle.js size a lot, which is a very good thing
So for me, only upsides.