If you’re doing anything non-trivial in React, I have a hard time seeing how you can keep your code maintainable without including some kind of state management library into the mix.
While popularity isn’t everything, my take is that there’s value in following the well worn path. It’s more likely if you have a question or a problem someone else has already addressed it on StackOverflow; and, if you need to get other developers involved in the codebase, a more popular library will likely result in a lower friction onboarding process.
However, if you’re using Redux, you need to know how to modify your state immutably (without changing it). I found this awkward at first. Every update to your app’s state involves creating an entirely new version of it with the changes applied.
What’s the big deal with immutability?
I read that while I was learning Redux. It cracked me up because using Redux felt like using a bulldozer to do some light gardening. There’s so much formality and indirection around managing your state. It seemed unnecessary. I mean, I already spend way too much time shaving yaks.
I still have some complaints about Redux, but I’ve largely grown to like it at this point. I don’t like writing it, but I like what I get as a result. So, it’s functionally solid <pun cringe>, but there’s maybe a better surface API? [one option]
But, I think part of my problem while learning it was that Redux is not useful in the context of a trivial app, and I mainly started learning Redux by building trivial apps. Redux shines when your app starts to get complicated.
Redux is like an accounting system for your app’s data or state. Coupled with the Redux dev-tools, you essentially get a balance sheet that shows you how your data changes over time, which can be remarkably useful when your app starts to do more than just adding an item to a list.
Modifying state immutably is what allows you to keep an accurate accounting of your previous states. If you change your state by mutating it, you essentially make your balance sheet inaccurate by changing its history (Enron Driven Development™).
Here’s a quick example to show you what I mean:
Say we have an array:
Let’s keep track of changes to the array:
Now let’s add an element to the array and save the new state in the
The problem is that the
stateChanges array now has two references to the same array. We’ve lost the previous, initial state of the characters array because we’ve mutated it.
By using immutable state changes, we can save the previous state:
Now we’ve stored two different arrays in our
stateChanges array and we have an accurate accounting of how the state has changed.
Redux just takes this idea and fleshes it out. With Redux, you can see how your app got into its current state, which can be remarkably helpful when trying to debug how your app got into a weird state.
Immutable data modification patterns
While digging into Redux I found myself repeatedly saying, “wait, how do I do this immutably, again?”.
So, I set out to catalog the approaches I could use to immutably change the state in my Redux store.
I’ve ended up favoring the mostly vanilla JS approaches, so that’s what I’m including here. But, initially I did some explorations with multiple approaches, like using Ramda.js, Immutable.js, and even a simple utility file of my own. Here’s a gist with those explorations if you’re interested.
Adding an item to an array:
Let’s start out with an array of character names and work on adding a new character:
Adding “Maude” using the ES6 spread operator (my preferred approach):
You can also copy the array and mutate the copy:
This may seem silly given the previous options, but sometimes it can be helpful to make a copy and then use normal mutation functions on the copy.
If you’re worried about getting duplicates in the array, you can use the
Set data type to ensure uniqueness:
Let’s say you wanted to add “Maude” at a certain position in the array, like just after “Jeffrey”. You can use
splice to achieve this:
Adding an object key/value:
Let’s start with a collection of character data.
Adding Maude with the spread operator (my preferred approach):
Using the object
Creating a copy and then mutating the copy:
Updating an item in an array:
Let’s say we want to change “Jeffrey” to “The Dude”.
If you don’t have the index for the array item, you can get it with:
You can alter the item with the spread operator:
You can use the
map function (my preferred approach):
You could copy the array and mutate the copy:
Updating an object key/value:
Let’s update the characters object from above:
Using the spread operator (my preferred approach):
Using the object
Or, you could copy the object and mutate the copy:
Deleting an item from an array:
Let’s remove Donald from the characters list (so sad):
Using the spread operator and the
filter method (my preferred approach):
Deleting an object key/value:
Let’s remove Donald from the characters object literal above:
Using the spread operator and destructuring (my preferred approach):
You can use the
Lodash also has a nice utility method
Applying the immutable modification patterns in a Redux example
The Redux folks recommend normalizing your state and keeping it in flat structures, which allows you to treat it like a database. Let’s apply this approach to the character data and use our immutable data modification patterns in a Redux example.
I’m using a kind of slightly modified ducks pattern, keeping types, actions and reducers in a single file:
I usually create a
store directory in my
src directory where I put my duck-like files.
If you’re interested, I would test this file like this.
However, I think the recommendation is to create separate reducers for managing state changes to
allIds. I don’t often do this because I find the multiple switch statements unappealing, but it simplifies the reducer logic a bit, which is a win.
The changes would look like this:
Other immutable options
I’m mostly using vanilla JS for immutable state changes at this point, but there’s a couple of libraries that have caught my eye.
I also find the Redux ORM library very compelling. Most of my backend application servers are developed with the help of an ORM, like ActiveRecord in Rails. So, it feels natural to have an ORM to manage my data on the front-end. When I started with React, I lamented that Ember data wasn’t easily usable as a standalone library. That still would be awesome, but you can get something in the ballpark with Redux and Redux ORM. And, using the Redux ORM API makes immutable state changes look a little nicer than using vanilla JS.
If you’re still here, thanks for reading! ❤️ 🙏
If you’re interested, here’s a repo with some code from this article.