Switching to immer.js for Immutability in React: Worth the Hype?
Immer.js is a tool for enforcing immutable data structures and is becoming increasingly popular in the JavaScript community. One of Praxent’s software engineers shares his experience using immer.js for immutability in a React application.
What Is Immutability?
In programming, immutability refers to an object that cannot be changed, or whose state cannot be altered after it’s created. JavaScript strings and numbers are immutable by design.
Immutability offers a variety of benefits, including thread safety, simplicity in testing, and good encapsulation. Immutability offers more readable code, which is easier to maintain and less likely to contain errors.
Immutability is not built into all languages, however, which necessitates either the use of methods that enforce immutability with objects and arrays, or the use of a third-party library. When working with JavaScript, there are a few such libraries to choose from including the standard immutable.js (a third-party library written by developers at Facebook) and the newer immer.js, which has been picking up steam since early 2018.
The History of Immutability Libraries
As Shawn Wang notes in his article The Rise of Immer in React, immutability and React go way back to 2013, not long after React was open sourced. In 2015, Immutable.js came along and became the dominant immutability library. It retained this dominance until 2018, when immer.js was released. Immer quickly gained popularity, as it built on historical learning to offer improved solutions to standard problems. At its core, immer solves the same problem that Immutable does — but it solves them in a more streamlined way.
Making the Switch: From Immutable.js To Immer.js
I recently started using immer.js to handle immutability in React. This decision marked a departure from Praxent’s typical use of Immutable.js with React applications, which is a standard for many engineering teams.
The Problem
To start, let’s take a high-level view of the problem that immutability libraries solve. After all, Redux is based upon the idea of not mutating state. If one is using Redux correctly, why do these libraries need to exist in the first place?
Principally, libraries like immer and Immutable exist to enforce immutability, which ensures that your data will be updated as you expect. Though Redux enshrines a read-only state that is updated with pure functions, it’s possible to mistakenly mutate the redux store. A direct mutation of the Redux store results in hard-to-debug side effects and other unintended behavior. As such, we can use some extra help to make sure that our state updates predictably.
The Decision Points
While making a major switch like this is never an easy decision, I decided to try immer.js for two main reasons.
The simple API: Immer’s API is easy-to-use, and I was able to grasp the core concepts by skimming the documentation a few times. The simple API was an appealing choice for writing Redux reducers.
The Immutable.js data type doesn’t exist natively in JavaScript: I had never used Immutable and didn’t want the additional cognitive overhead of learning data types that don’t exist natively in JavaScript. As the Redux documentation states, data types like Map and List require extra effort to work with because they’re not interoperable with regular JavaScript.
The Implementation
Overall, immer.js has been excellent to work with. Writing Redux reducers and tests with it was a dream. The syntax provides a clear picture of inputs and outputs, and allows the developer to avoid doing a lot of destructuring and object creation. There were a few “gotchas” with immer that definitely took me by surprise, but they were workable.
The biggest downside with immer for me is that it doesn’t support JavaScript classes. This caused some issues when testing this as an immutability solution, as our React boilerplate uses OOP to model our database resources. It took me some time to realize that one needs to add a special flag to allow a class to be copied. Even so, you lose the constructor, so classes behave like normal objects. This caused some bugs that were difficult to diagnose. Though the nature of classes is explicit in the documentation, it took me a while to realize the limited support. I was mutating state and then wondering why my app was not working as expected.
While this feature was a little disappointing, it seems unlikely immer will make significant changes around classes. According to its author, adding class support would be in conflict with the architectural principle of an immutable state tree. For the time being, I’ll need to factor this into how I’m using OOP and classes in my React apps.
The Verdict: Immer.js For The Win
Overall, I enjoyed working with immer and definitely prefer it to Immutable in React. Immer proved to be an excellent choice for solving the problem it addresses. I’m glad that the JavaScript community has produced a much simpler way of dealing with immutability in React, and I’m looking forward to further enhancements to immer and general improvements to JS tooling as well.
If you’d like to learn more about using immer, I recommend checking out this blog post.
Legacy software is an asset, but only if you know how to leverage it.
>> Download our free CodePath Assessment e-book for evaluating the health of legacy software assets so you can innovate risk-free.