Screenshot of Yodagram running with Redux Dev Tools open
Screenshot of Yodagram running with Redux Dev Tools open

Source code for Yodagram hosted by GitHub.

In the beginning, there was jQuery…

I’ll tell you a story, not for my own amusement, but so you can hopefully avoid the proverbial rabbit holes I fell through…

Some time ago, I had no knowledge of how to make anything beyond the most basic website in JavaScript. I knew a little jQuery but didn’t understand it much, and at the time, considered myself to be primarily a Java developer.

I took an opportunity working for an exciting startup and was tasked with creating a fairly complex front-end using plain javascript/jQuery for a fantasy sports web-app. I had thought that I was going to be doing back-end Java/Groovy stuff, but was thrust into the position of a senior front-end developer b/c we were a small team and no-one filled that role yet. We had an excellent UI/UX artist, so that made things easy as far as HTML and styling, but I was doing more research than coding to keep up and needless to say, using vanilla JavaScript with jQuery to handle events and make AJAX calls turned into a nightmare as we expanded.

I wish so badly that I knew about React, Redux, JavaScript design patterns, functional JavaScript, etc… but I didn’t, so I did the best I could, which was terrible, and was basically having a 24 hour a day breakdown trying to maintain the app’s state without the tools I use today. There were bugs that were hard to replicate, I was using Java-style Classical inheritance still (you can read about Prototypal Inheritance VS. Classical Inheritance here), and I can understand why things went downhill so fast as the app grew more complex.

In the end, we hired a front-end Rockstar with 10+ years of experience, who helped us convert to using React, Flux, ES6, etc… I was phased out overnight. I wanted to keep us so badly, but with no knowledge of these things at the time – despite knowing JavaScript basics – it was not enough. Without the time to catch me up, or time for guidance, I simply was not able to function and retired from that position. However, time heals all wounds, and I endeavored to understand everything I didn’t know. Every little thing that killed my opportunity. I was relentless and it paid off.

I am grateful for being able to be involved with such a project (however difficult it may have been) because I was thrown into the pool and had to learn to swim. I learned so much so quick that it’s taken me 6 months to absorb it all. Being able to study the codebase over time has been invaluable to me as well. If you learn from your mistakes, there’s no such thing as failure.

Then there was light…

Flash forward a year and I have discovered React, state management tools for React such as FLUX architecture, and now Redux and life seems worth living again as a web-dev. Almost everybody is using either React, or Angular, or some other similar rendering library, and the gospel which started with FLUX, and was modified by Redux is on it’s way to converting a great number of people.

So, in my research about Redux, I ran across this tutorial on React/Redux called “Learn Redux”, by Wes Bos. I did the tutorial and learned so much and am so grateful for the tutorial. Wes is an amazing “explainer” and writes very logical and easy to read code. The videos are also of a high quality, and I can’t stress it enough that you should find this video series and watch them. I’m sure you’ll glean something from it, even if you are a mid to senior level developer.

Note: From here on out, I am assuming you have seen the Redux tutorial series I mentioned or you understand both the basics of Redux and React! I am also assuming that you know ES6 JavaScript, as well as how NPM modules and Node work. I feel there is so much beginner info out there that why even bother when I know someone has already explained it, so this is more or less written as “the missing manual” to finding out how to take the next step towards what you would see a real app. This post is possibly intended for non-beginners, but beginners may also be able to take something away from it. If you are a beginner, I suggest that you start with a simpler and better-written explanation of how Redux and React both work. Moving on.

This is great, but every tutorial uses static data… What about the real-world?

Needless to say, I got a lot out of the tutorial. But I kept thinking, “how would I incorporate real-world scenarios into the Redux philosophy, without breaking any of the 3 principle rules?” The I realized that those rules are actually very very simple to follow.

When I did add real API calls, a real server, and a real database, all working asynchronously, I was astonished at how easy it was to accomplish my goal, given the correct tools. The Redux skills that were learned in the tutorial were my weapon. Taking them that one step further was simply an extension of what I learned combined with a little research and some reasoning about databases. Most things worth learning require at least 25 visits to Stack Overflow though, right?

How I changed the app to be reliant on a REST API server?

To host the Data, I added a REST API server using Express.js, a MongoDB database, and a modified the client to make HTTP requests instead of just playing with dummy data like the tutorial did.

We will start first with finishing the client-side code in preparation for what we will write for the server.

You can follow along with the source code posted on Git-Hub. I changed my project’s name to Yodagram.

I really am trying to not change as much of the original front-end code from the original Learn-Redux tutorial, except that I felt strange using someone else’s photos, and love Yoda, so the name was changed, and images were replaced, which makes for some humorous comments, since I left all other data intact. But for the most part, I didn’t have to alter much, and with more time, could probably do a better job to make it change even less, but I’ll get to it when I have time. I’m also encouraging people to contribute.

Three principles of Redux

I will go over Redux and the three cardinal rules, but you could also just read the docs.

1) Single Source of Truth:

The state of your whole application is stored in an object tree within a single store.

Redux is a state management library that is used in conjunction with mostly React, that helps maintain complex states in apps. It forces you to organize the state of your app as one big object tree. This means that there is no duplication of data or conflicts of interest, you just have one big object with nested keys and values. Anyone who has done any front-end work can easily zip through objects using for in loops or whatever. But what about messing up the state? Nope.

2) State is read-only:

The only way to change the state is to emit an action, an object describing what happened.

Since the state is read-only, we can eliminate a number of bugs, and if we do have them, they are most often easier to reproduce because you’ve been forced by Redux to make good architectural choices, and things will be easier to manage. Phew! But, “how do I update data?” you might ask. That’s where reducers come in.

3) Changes are made with pure functions (reducers):

Reducers are just pure functions that take the previous state and an action, and return the next state.

“You can start with a single reducer, and as your app grows, split it off into smaller reducers that manage specific parts of the state tree. Because reducers are just functions, you can control the order in which they are called, pass additional data, or even make reusable reducers for common tasks.” – From Redux Docs

So, you see, we have a philosophy and pattern to stick to which actually allows us to create actions, update the state, and hydrate component data now that is more reliable than a thousand jQuery.on('something', function() { potentially fuck up the state here... })‘s.

Updating and adding new Actions and Reducers

My method for updating the original tutorial was to replace the actions with actual API calls that did their own asynchronous thing. I didn’t know how to make a server in JavaScript, so I learned a bit about Express.js and discovered that it took me a fraction of the time to create in Node than doing it using Groovy, or Rails, or the other things I’ve used in the past. Going from zero to her took no time at all. OMG life is so much easier when not everything you write has to be some kind of pseudo-clever classical inheritance scheme. But that is an argument for another time.

I tried my best to be faithful to the author’s way of structuring data, which was originally just two files. One of them was a file of posts that looked like this:

The other was a file with comments data. The comments data is a little trickier b/c in the original code, the comments were all just organized by the post codes as keys and the posts as values in one big Object Literal. This has the advantage of being an O(1) lookup time and is essentially a hash table data structure. Great for a static file, but more difficult to abstract into a database schema. I’ll get to those in a second.

Anyway, the basic idea of Redux is that you have actions, and those actions are just an object with an action type and some variables that will be needed for the next state. In my app, an action function would look like this:

So in the original app, when you added a comment, it would call the addComment() method from inside React, and pass the variables needed, and then the action with type ADD_COMMENT is fired off, and the comment’s reducer would return a new state with the new comment spliced into the state data.

If you’re unsure of how the reducers are magically listening all of the time, then remember that they are combined into a root reducer and passed into the createStore() function provided by Redux. When we add the data-service.js, our special reducer, we will also add it to the store, in the same way, only we will apply it as middleware. When we do this, we are registering them and requiring them to always be listening. That makes things like a million times easier, right? You can read about middleware in the docs. Thank’s Redux.

For the next step, to update the app to make asynchronous AJAX calls I was going to need more actions. Since a server can either blow up, or return data, we can deduce that we would need 3 actions for every attempt to mutate the state:

  • One for the actual action
  • One for a potential failure
  • One for a successful data return from the server.

So I created a new file for these special data API related reducers in a folder called services. I have used the wonderful NPM package Superagent to make things easier. Read up on Superagent, and also, to understand the way I have structured the service reducer, see this amazing blog post that helped me. This is a simplified version of what the add comments actions in my data-service reducer look like.

Basically, When an ADD_COMMENT action is called, it will certainly either make a failure or success action. I then updated the comments reducer to listen for the ADD_COMMENT_DATA_RECEIVED instead of ADD_COMMENT, leaving much of the original code intact. Anyway, the new comment.js reducer looks like this:

Now we are ready to create a server that interfaces with a database that, when requests are made, it will respond with a successful JSON message from a database query, or an error.

The Database and Server

For the database, I have used MongoDB, and the NPM package Mongoose. Mongoose makes dealing with the database operations and routing easier than native JavaScript. It will make sense when you see the code. For the server and API, I am using Express.js.

Note that we can’t use our fancy ES6 here. There is no Babel interpreter, so you have to use plain old JavaScript, but that shouldn’t be a problem.

Here is my server.js file:

As you can see, it imports the API code, connects to the database using mongoose, there is a custom function that is imported that we will go over later that populates the database, and the server is started at port 3333.

Before showing you the API code, I should probably show you my DB schema. I suggest you take a quick look at the Mongoose docs on schemas if you are unfamiliar with Mongoose and are trying to interpret my code.

Since the posts file is more or less just an array of posts, I was easily able to abstract a similar schema in the database, the only difference being that MongoDB automatically injects an ObjectId to every document. So my Posts schema looks something like this:

The comments were trickier like I said earlier, b/c there’s no way to just have one big object with key/values without having one big database entry, and that’s weird, so I took some liberties. Normally I would say that I would add a comments array to the Posts schema and store the comments in those instead of their own collection, but to make this sync up with the tutorial I made a separate collection of comments grouped by the post’s code it’s associated with. Here is the Comments collection schema:

Script to populate data in static file to the database:

The API

Now for the final step, we need to create a simple REST API that performs basic CRUD operations. Here is a simplified version of the API code, with only the add comment functionality for brevity, but the full code cn be found here.

If you’ve fired up the app, you know already, but if you’re just reading along, we are now done! The original app has the basic actions.

  • Adding a comment
  • Deleting a comment
  • incrementing likes

I only walked through the add comments code, but the full code has all three of those actions now working the same way – Through server responses and API calls!

Here is a screenshot of what the app looks like. I took a few small style liberties to render a dark color scheme more fitting of Yodagram, but I hope you learn something both from the original Learn Redux tutorial, and then a little bit more about how things might look in a real app by the additional work I have done.

-Joshua

Share on FacebookEmail this to someoneTweet about this on TwitterShare on Google+Share on LinkedInShare on Reddit