A Redux app without reducers?! Trim your boilerplate with Redux Cornell!

Eyal Eizenberg
Wix Engineering
Published in
7 min readDec 10, 2018

Redux has become one of the most popular Javascript state management libraries in the world. Personally, I love Redux and I use it all the time with my countless React apps. However, I, like many others, feel that there is a lot of boilerplate associated with using Redux…

Enter “Redux Cornell”.

Before we dive in…

In 2017 we lost Chris Cornell who was an amazing singer and song writer. I decided to name this new library after him as a small token of my appreciation to the years of amazing music Chris has given us. We miss you Chris…

“Say hello to heaven…”

Chris Cornell 1964–2017 (Picture credit to weedstream.net)

So what is Redux Cornell?

A Redux library which lets you remove most of the boilerplate associated with writing a Redux application, yet allows you to customize it completely without loosing control.

Motivation

Using Redux usually entails creating a lot of functions and files. You need a reducer, actions, selectors, etc. I noticed that these functions are very similar across unrelated projects and the ‘cycle’ to add new functionality to your app doesn’t change much:

  • Create a reducer which will hold your relevant data.
  • Add the reducer to the your root reducer.
  • Add a new initial value to the reducer.
  • Create selectors in order to create an abstraction layer to the data.
  • Create actions which will modify that initial value.
  • Bring it all together in a container which renders a React component.
  • Repeat.

Sounds familiar? If you’ve ever built a Redux app, you know exactly what I’m talking about. I knew there must be a way to make this process shorter which will allow me to focus on the most important thing — my app.

Convention Over Configuration

I used to do a lot of web development with Ruby On Rails. Yes, I know many web developers have abandoned Rails for Node but there are some great concepts in Rails which I always loved. One of these is “convention over configuration”.

“Convention over configuration is a software design paradigm used by software frameworks that attempt to decrease the number of decisions that a developer using the framework is required to make without necessarily losing flexibility.”

In the world of Rails, this means that a model class called “User” will be associated with a controller called “UsersController”, a db table called “Users” and so on. Only if you need to customize these decisions (like use a table called “RegisteredUsers”) do you actually need to write code to change this default behaviour.

There is an obvious downside to this paradigm — a new developer which starts working in your echo system won’t understand the “magic” and will most likely characterize it as Voodoo. However, I feel the pros outweigh the cons here.

I thought to myself: “We already have conventions for so many things in Redux but you still have to manually configure everything, time to bring some ‘magic’ into Redux”.

OK, sounds interesting, tell me more!

Here is the link to the Github repo and full read me: https://github.com/wix/redux-cornell

Throughout the rest of this post, I am going to use references from a demo app which I’ve built using Redux Cornell. The app fetches a list of “Stranger Things” episodes from IMDB and allows you to toggle between the show’s info and list of episodes. In order to cover all the features of Redux Cornell, I’ve added a text field with a reset button.

The example is in the same Github repo and you can run it locally (follow instructions in the readme), or you can see it online here: https://eyaleizenberg.github.io/imdb_list/index.html

The main concept in Redux Cornell is that you create an instance which receives the initial state of your app, and using some code analysis, you will get:

  1. A “super reducer” which will handle all your dispatched actions.
  2. Relevant selectors.
  3. Relevant Actions.

Let’s go over them in more detail

Initializing & The Super Reducer

First, add the Redux Cornell dependency (Redux Cornell has 0 dependencies so it won’t bloat your app):

npm install --save redux-cornell

So this is my root reducer:

Root reducer with Redux Cornell

As you can see, we are passing an initialState object to the reduxCornell function. Think of the keys in the initialState object as the “reducers” (episodes, showInfo and currentUser) and the key-value pairs under them as the possible attributes (loaded, data, expanded, etc…).

It’s important to give these reducers and possible attributes descriptive names as the names will generate the selectors and actions (more on this later on).

After running our reduxCornell function, we can extract three objects from it: selectors, actions and the superReducer (line 4). We export the selectors and actions so we could use them throughout our app (line 20).

Finally, we create our root reducer with the superReducer object (line 22) and export it (line 24).

That’s it! Did you notice how we don’t have any more reducers? The super reducer will handle all state changes related to the episodes, showInfo and currentUser without having to write a single line of code!

Now that we have our super reducer ready, let’s see how we grab the values from the store using our selectors in a container (connected component).

Using the selectors

As I mentioned earlier, the keys in the initialState object will become our “reducers” and the key-value pairs which are nested in them will be the properties.

Each property from the initialState object will automatically get it’s own selector in the following format (camelCased):

get<Reducer><Property>

These selectors only receive one parameter — the state object. So in our example, we will get the following selectors:

  • getEpisodesLoaded
  • getEpisodesData
  • getEpisodesExpanded
  • getShowInfoVisible
  • getCurrentUserName

Here is a part of what will be our app container:

We are importing the standard connect method from react-redux (line 1) and the selectors which we generated in our root reducer (line 2). In our mapStateToProps we are using the getShowInfoVisible selector and passing it the state object (line 5). Under the hood, the selector knows to look in the super reducer for the showInfo ‘reducer’ and the ‘visible’ property.

Neat right?

I like it so far! Let’s fire up some actions!

Every Redux app needs to have actions in order to change the data in it’s store. When you instantiated the reduxCornell class in your root reducer, many actions were automatically generated for you to manipulate your data, according to your initialState object. If the value of your property is a:

Boolean: You will get an action in the following format:

toggle<Reducer><Property>

In our example, you will get the following toggle actions:

toggleEpisodesLoaded
toggleShowInfoVisible

The actions don’t receive any parameters and they simply toggle false to true and vice versa.

Array: You will get an action in the following format:

concat<Reducer><Property>

So in our example you will get the following action:

concatEpisodesData

The action accepts an array as the parameter which will be concatenated to the existing array.

Object: You will get an action in the following format:

extend<Reducer><Property>

So in our example you will get the following action:

extendEpisodesExpanded

The action accepts an object as a parameter which will be assigned to the existing object in the store

Set & Nullify actions: In addition to the actions above, a set and nullify action will be generated for each property, giving you full control to override and set whatever value you need. The formats are:

set<Reducer><Property>nullify<Reducer><Property>

The ‘set action’ receives a parameter which will be the value of said property. The ‘nullify action’ doesn’t receive any parameters. In our example, you will get the following actions:

setEpisodesLoaded
nullifyEpisodesLoaded
setEpisodesData
nullifyEpisodesData
setEpisodesExpanded
nullifyEpisodesExpanded
setShowInfoVisible
nullifyShowInfoVisible
setCurrentUserName
nullifyCurrentUserName

In the “Stranger Things” demo app I used the setCurrentUserName action to set (you guessed it) the user’s name when he types it in a text field.

If we sum it up, we just got 14 actions just by passing the initialState object to Redux Cornell. Sweet!

Here is what our app container looks like now:

We imported the actions from our root reducer file (line 2) and then we extracted the toggleShowInfoVisible from the actions object (line 9). Finally, we pass the toggleShowInfoVisible as a prop to the App component. Once again, we didn’t add any additional “Redux code”, we just had to pass it as a prop to our React component.

In the App component there is a Toggle component which receives the toggleShowInfoVisible prop and fires it “onClick”. The super reducer knows exactly which property to change, and that’s how we toggle between the show’s info and the episode list.

That’s cool! But how do I customize it?

In our “Stranger Things” app, we need to fetch the list of episodes from IMDB (I’m using the imdb-api/OMDB library for it). We need to fire an action which does asynchronous fetching and then dispatches the data. Let’s do it!

We import the imdb-api library which is used for fetching the data (line 1) and the actions object from our root reducer. I am using redux-thunk because I need to fire the action asynchronously. After fetching all the data I need from the server, I finally dispatch the ‘concatEpisodesData’ action with the episodes as the data I want to concat (line 10). That’s it! The super reducer will handle the rest for me!

Here is what our final app_container will look like:

I brought in our newly created action ‘fetchStrangerThings’ (line 5) and I’ve expanded our container so now it will fetch the episodes when the application loads (line 14).

Need a custom selector? No problem, here is an example of how to get the length of the episodes data array:

Just import it and you can use it wherever you need.

Conclusion

Redux Cornell helps you save a lot of time and lets you focus on the most important thing — making your app look and feel great, yet it lets you customize it without loosing control.

I’m sure that there are many more auto generated actions which can (and will) be added, and this is where YOU come in. Bugs? Suggestions? Hit me up on Twitter/Github/mail pigeon and lets take Redux Cornell to the next level!

If you liked this post, please follow me on Medium and show some love by clicking on the ‘Clap’ icon several times. You can also follow me on Twitter and Github.

--

--