Redux state updates with Ramda - Arrays
splice. However these methods mutate the original array, and in Redux that’s not allowed. Reducers must be pure functions, which means that:
- given the same arguments they will always return the same result
- they should not depend on or modify any external state
Inserting items into an array
Given that reducers are pure functions, here’s what inserting a new element into a certain position of an array would typically look like with Redux:
const insertItem = (array, action) => [ ...array.slice(0, action.index), action.item, ...array.slice(action.index) ]
This function doesn’t mutate the original array, it creates and returns a new array through the use of the spread operator and the
slice array method. With Ramda we can accomplish the same result using the
insert function it provides:
const insertItem = (array, action) => R.insert(action.index, action.item, array)
The first argument in Ramda’s
insert function specifies the position to insert the element. The second argument specifies the item to be inserted. And the third argument is the array. As with all list operations in Ramda, this is non-destructive; it returns a copy of the array with the changes. So it does the exact same thing as the original example does, just in a more declarative that requires a lot less code.
If you need to insert an item to the start or end of an array, Ramda provides us a couple of functions that are even more convenient. Here’s how we can add an item to the start of an array (AKA prepend):
const prependItem = (array, action) => R.prepend(action.item, array)
prepend function returns a new array with the given element at the front. And here’s how we can add an item to the end of the array (AKA append):
const appendItem = (array, action) => R.append(action.item, array)
prepend is the compliment of
append. These functions are now nice and short and easy to understand at first glance.
Removing items from an array
const removeItem =(array, action) => [ ...array.slice(0, action.index), ...array.slice(action.index + 1) ]
With Ramda we can utilise
remove function to accomplish the same thing:
const removeItem = (array, action) => R.remove(action.index, action.item, array)
remove function signature looks a lot like the
Removing items from an array by ID
const removeItem = (state, action) => state.filter(item => item.id !== action.id)
This code is nice and terse however, there’s a lot for us to process in order to understand exactly what’s happening. Let’s see how Ramda can help make this a bit more declarative.
const removeItem = (state, action) => reject(item => equals(item.id, action.id), state)
Okay, so this isn’t a lot nicer, and we can do better, but first I’ll explain what’s going on here. Instead of using the native
filter array method we’re using Ramda’s
reject is the compliment of
filter, which means that it will return a new array with items that do not satisfy the predicate function (in other words; items that return
The first argument of the
reject function is the predicate function and the second is the array. Inside the
reject predicate function we are utilising the
equals function returns true if the arguments supplied are equivalent.
As I mentioned earlier, we can improve upon the above implementation, and here it is:
const removeItem = (state, action) => reject(eqProps('id', action), state)
We’ve now removed the need for the extra arrow function inside the
reject predicate function. Ramda’s
eqProps function checks whether two objects have a particular property with the same value. We can do this thanks to the functional programming concept known as currying. All functions in Ramda are automatically curried, which means that we don’t need to pass all of the arguments to it right away.
eqProps actually takes in three arguments, but we’re initially only calling it with two arguments. Since it’s a curried function, it will return a new function that expects one more argument. When the
reject function calls the
eqProps function for each item in the array it will pass the current item in as the third argument, which will then return the result.
That’s it for now
This post has just scratched the surface of Ramda and how it can be utilised in Redux applications. While I’m no expert in functional programming, I hope this has given you a little taste of what’s possible with the convenient functions available in Ramda. I’m planning on writing a similar post on updating objects (including objects in arrays) soon.
References: Immutable Update Patterns