09 August 2022
6 mins read

Chasing Dependencies

More bundle size improvements!

Shashwat
Shashwat TheTrio

As has been the theme for the past couple of weeks, my efforts have mostly been to reduce the size of the bundle by removing/replacing/upgrading dependencies. And this week is no exception.

Get ready to see a lot of numbers!

Note: This is my 5th Bi-Weekly blog. You can find the rest of them here

Summary

As always, here’s the list of PRs that I made during these past two weeks.

Issue(s) Description Pull Request Status
- remove references to lodash #5090 Merged
- remove react spring #5088 Merged
- Update react dnd #5093 Merged
- reduce usage of JQuery #5098 Merged

And now, let’s go into a bit more detail.

React Spring

It’s a bit of a shame that we were using React Spring to handle transition states for drag and drop. It’s a great library no doubt but we were using just one function from it - useSpring - for interpolating between two values.

While React Spring can also be used to implement drag and drop in itself, it is not a good fit for our use case. Because of this, we were using React DnD to handle drag and drop. This is a rather low-level library but it provides a lot more control over the drag and drop behavior.

Regardless, none of this would’ve mattered if React Spring was tree shakable - that is, the bundle only included the functions we used. This however was not the case. There was an issue requesting support for tree shaking but it was closed so it was unlikely that it would be implemented.

This is why I decided to replace React Spring with a more lightweight alternative. I first tried out Auto-Animate - a great library considering it is implemented in less than 1000 lines of code.

But this library was a bit too progressive. It didn’t support CJS at all and provided only ES6 exports. When I contacted the author, he said this

ESM is the future and I don’t mind using this package as a little bit of coercion to get people to move to modern build tools.

I understand his point but since we needed the CJS version to run our test suite(jest support for ES6 modules is still “experimental” to put it politely), I had to look for another library. I settled on React Flip Toolkit. While this is a lot larger than Auto-Animate, unlike React Spring, it is more flexible and most importantly, tree shakable.

After this migration, our vendors’ bundle size went from 1.37MB to 1.35MB

React DnD

I’ve already talked about React DnD so I won’t repeat it here. The thing was, the version of React DnD we were using was a lot larger than the latest one. Unfortunately, upgrading it wasn’t as simple as doing a yarn upgrade. This was because v16 of React DnD didn’t support class-based components. It was built entirely around hooks.

Previously, we were using a higher order component to wrap our blocks to make them draggable. This was a bit of a pain because we needed to pass down the props to the nested component and it made it quite difficult to follow the code. So switching to hooks was a good idea even if we weren’t upgrading React DnD.

I ended up writing a custom hook and rewriting the entire drag-and-drop logic from scratch. This felt a lot easier than finding out the hooks equivalent to the old API.

In the end, however, it was all worth it. The bundle size went from 1.35MB to 1.32MB Yay!

JQuery

Ah, the joys of jQuery. This is the first time I’ve used it and it’s funny how much I’ve learned about it from just converting jQuery to vanilla JavaScript.

While I had initially hoped to get rid of it entirely, I soon realized that that was a lot more difficult than I anticipated. So I settled for restricting it to a couple of routes that were the most complex to rewrite - the campaigns and survey routes.

Unfortunately, even in 2022, vanilla JS can’t do everything jQuery can. Take JS animations for example. They are still a lot more cumbersome to write and I’d rather just use CSS animations instead(which is what I ended up doing).

jQuery also provides a lot of “magic” which JS simply doesn’t. For example, I found out that jQuery’s data method converts a string into an object implicitly if it starts with a [ or {. With JS, I had to call JSON.parse explicitly.

And this goes the other way around as well - if you assign a data attribute something other than a string, it will still work. With JS, it will error out. This is because, unlike JS, jQuery’s data method doesn’t manipulate the DOM - instead, it simply stores the value in a Map where the element is the key. You could implement this in JS via a WeakMap with a few lines of code but it still isn’t as elegant as the jQuery solution.

There’s a lot more I could say here, but it’s probably going to be a boring read so I’ll cut to the chase - in the end, the bundle size goes from 1.32MB to 1.27MB after getting rid of these jQuery parts. Nice!

Conclusion

To see how far we’ve come, here’s a graph of the bundle size over the past month.

IGNORE

Overall, that’s a size reduction of almost 14%. There are still a lot of things we could do to make this better but for the time being, I’m happy with the result.

So what’s next? I’m not entirely sure. While I’m still looking, I’m yet to find newer things to work on.

Unless anything major comes up, I’ll try to wrap up everything that I started but wasn’t able to finish. This includes

  1. converting server rendered lists to React components
  2. converting a few class-based components to functional ones

None of these are that important(or easy for that matter), but since I have some time, I’ll give it a go.

And that’s all for today. Have a great day!

Categories

GSoC GSoC-22