At my job, we’ve been working on a few front-end projects using React and Redux. We’ve also been using the Ramda library to help us work effectively with Redux. This post contains a few of the ways that we’ve used Ramda in our React/Redux applications.

Background

In case you’re not already familiar with these libraries, here’s a quick overview.

React

React is “a JavaScript library for building user interfaces.” This post isn’t a React tutorial, and most of what I’m talking about here is independent of React. If you’d like to get a sense of what React is about, start with Pete Hunt’s Thinking in React article.

For this post, the main thing to know is that React emphasizes decomposing the UI into a tree of components. Each component is given “properties” (often just called props) that configure that component for a given context. A component might also have some internal state that can be mutated.

Redux

Redux is “a predictable state container for JavaScript apps.” Redux implements something like the Flux architecture using ideas from Elm. While not coupled to React, Redux is often used alongside it.

This post is also not a Redux tutorial; for that I highly recommend watching Dan Abramov’s video series on egghead.io. Even if you don’t think you’ll ever use Redux, it’s worth watching the videos to learn how to effectively teach a subject. The instructional flow of these videos is incredible.

The basic architecture of a Redux application consists of a single object containing all of the state of the application. The current state is held in a store. The UI is rendered as a pure function of the state, which makes React a great fit. The state is never modified directly; rather, user actions and/or async activities create actions which are dispatched through the store. The store feeds the current state and the action through a reducer which returns the new state of the application. The store replaces its state with the new state and updates the UI with it, completing the cycle.

There are some really nice patterns here for decomposing the UI into components, and decomposing the reducers into pieces that work on isolated parts of the state. It’s a design that lets you start small and then grow a nicely-modular architecture. I was skeptical of that at first, because the store feels like a giant global object that everything can get at. But if you follow the standard patterns, the state is only accessed in well-defined ways which keeps things from becoming a mess.

When used with React, almost all state that might be held in individual React components is moved out to the store instead. Most React components in a Redux app use only props to do their job.

The important parts of the architecture for this post are the reducer, which is defined as a pure function from (currentState, action) -> newState, and the mapping of the state into the UI components. In Redux with React, this latter job is accomplished with a pure function mapStateToProps(state, ownProps) -> extraProps. ownProps are the properties that were used to instantiate the component, and extraProps are additional properties to pass to the component along with ownProps.

Ramda

Ramda bills itself as “a practical functional library for JavaScript programmers.” It provides many of the capabilities that functional programmers are used to. Unlike something like Immutable, it works with plain JavaScript objects.

I hadn’t done much functional programming yet, but I’m finding Ramda to be a pretty approachable way to get into it, and it really seems to fit well with the style that Redux encourages.

Let’s look at some ways we can use Ramda when writing Redux code.

Writing Reducers

There are a number of ways to use Ramda when writing reducers.

Updating Object Properties

In the Redux documentation, there’s a Todo List example. One of the reducers contains this snippet of code:

Object.assign Reducer
Object.assign({}, state, {
completed: !state.completed
})

If you’re using Babel and the ES7 Object Spread notation, you can write this a bit more succinctly as:

Object Spread Reducer
{
...state,
completed: !state.completed
}

There are a few ways to write this Ramda. Here’s a pretty direct port:

Ramda Assoc Reducer
assoc('completed', !state.completed, state)

If the property to adjust is deeper in the object structure, you can use assocPath instead.

Another way to write this is to use evolve:

Ramda Evolve Reducer
evolve({
completed: not
}, state)

evolve takes an object that specifies a transformation function for each key. In this case, we’re specifying that the value of the completed property should be transformed by the not function.

Using evolve, you can specify multiple transformations of the state in one compact notation.

When I use evolve, I tend to go one step further and use __ to make it a little bit nicer to read:

Ramda Evolve and __ Reducer
evolve(__, state)({
completed: not
})

For a transformation this simple, I’d likely stick with the ES7 object spread syntax. But as your reducers get a bit more complicated, using Ramda can greatly simplify the code, as we’ll see in a moment.

Updating Array Elements

One of the learning apps we worked on was a simple Tic-Tac-Toe game. In this app, our state might look like this part way through the game:

Tic Tac Toe State
{
board: ['X', 'O', 'X', ' ', 'O', ' ', ' ', 'X', ' '],
nextToken: 'O'
}

We have a PLACE_TOKEN action that includes the index at which to place the next token.

Using ES6’s array spread notation and ES7’s object spread notation, we might write the reducer for this as follows, after extracting a little nextToken helper function:

Object Spread Reducer
{
...state,
board: [
...state.board.slice(0, index),
state.nextToken,
...state.board.slice(index + 1)
],
nextToken: nextToken(state.nextToken)
}
function nextToken(token) {
return token === 'X' ? 'O' : 'X'
}

This use of array spread is becoming a pretty common idiom for immutable updates to JavaScript arrays.

To simplify this code, we can first use Ramda’s update function to update the board:

Using Ramda update
{
...state,
board: update(index, state.nextToken, state.board),
nextToken: nextToken(state.nextToken)
}
function nextToken(token) {
return token === 'X' ? 'O' : 'X'
}

update produces a new array that updates the element at index with the provided value. If you need to transform the existing array element instead of replacing it, you can use adjust instead.

Next we can use evolve as outlined above to take this one step further:

Simplifying With Evolve
evolve(__, state)({
board: update(index, state.nextToken),
nextToken
}
function nextToken(token) {
return token === 'X' ? 'O' : 'X'
}

In case you’re not familiar with ES6, { nextToken } is shorthand notation for { nextToken: nextToken }.

Note that update takes three arguments, but we’re only providing it with two. By only providing some of the arguments, we’re getting back another function that will eventually take the remaining argument and perform the update. This is known as currying. Every function in Ramda can be curried in this way, and it becomes a pretty powerful pattern once you get used to it.

In this case, evolve will call our curried update function with the original value of state.board, just like it will call our nextToken function with the original value of state.nextToken.

Mapping State to Props

Ramda can also be useful when mapping state to props for a component. In the Tic-Tac-Toe app, the Board component needs both the board and nextToken elements of the state. The traditional way to write this is:

Traditional mapStateToProps()
function mapStateToProps(state) {
return {
board: state.board,
nextToken: state.nextToken
}
}

This can be simplified using Ramda’s pick function:

mapStateToProps() with Ramda pick
function mapStateToProps(state) {
return pick(['board', 'nextToken'], state)
}

Notice that we’re passing state into our function, and then passing it along to pick. When we see this pattern, it’s a clue that we can take advantage of currying. Let’s do that here:

mapStateToProps() with Currying
const mapStateToProps = pick(['board', 'nextToken'])

Here again, we’ve only provided one of the two arguments that pick needs, so we’re getting back a curried function that’s waiting for the state to be passed in. That will happen when Redux calls the mapping function for us.

Creating Reducers

In the Reducing Boilerplate section of the Redux documentation, it suggests that it is possible to write a createReducer function that takes an initialState and an object containing handlers, like so:

Redux createReducer()
function createReducer(initialState, handlers) {
return function reducer(state = initialState, action) {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action)
} else {
return state
}
}
}

Using Ramda’s propOr and identity functions, we can simplify the body of the reducer function quite a bit:

Ramda createReducer()
function createReducer(initialState, handlers) {
return function reducer(state = initialState, action) {
propOr(identity, action.type, handlers)(state, action)
}
}

propOr takes a default value, a property name, and an object. If the object has an own-property matching the property name, the value of that property is returned. Otherwise, the default value is returned. From that, we get back a function to call on the state and action. If that function is identity, it will just return the first parameter, which is the state. So this code is doing the exact same thing as the code above.

We can an ES6 arrow function to simplify this further:

Using Arrow Functions
function createReducer(initialState, handlers) {
return (state = initialState, action) =>
propOr(identity, action.type, handlers)(state, action)
}

Wrapping Up

I’m not a functional programming guru. I still couldn’t tell you what Monad is. There are parts of Ramda that are deeper than I’ve wanted to go so far. But even a few of the simpler functions can make it much easier to write Redux code.

I’m sure we’ll come up with new and interesting ways to use Ramda in Redux as we continue to work with it, but this is a good start.

If you find other ways to make use of Ramda in Redux, please share them. I haven’t seen much written about this combination, so it’d be good to have more ideas for people to explore.

Acknowledgements

Thanks to my co-worker Luke Barbuto for introducing me to Ramda. Most of these examples are things we discovered while pair-programming on several projects.