Since writing this, I went on to build Fly Me to the Moon and based on that, wrote about rendering react components dynamically and SEO friendly React-based content, which extend this further. I’d strongly recommend reading those as well as this.
How I’m currently building things in React.js, what’s good about React and what work there is still to do
This post details how I’m currently building things, what’s good about React and what work there is still to do. To help demonstrate, I’ve built an example item of content that uses data from Wikipedia via the MediaWiki API for you to check out. Hopefully, you’ll find it useful and interesting, and do feel free to ask me any questions in the comments.
Architecture and JS
I’m a big fan of the idea behind Flux , . If you’ve not come across it before, the concept is similar to Model-View-Adapter. That said, I do feel that it overly complicates things, and would suggest that the approach to handling data interaction and callbacks taken by Redux makes things somewhat easier to manage at scale.
The concept behind how we’re building things with React at Builtvisible is very simple. At the base of the application is the data. This is the only part that we’re not going to talk about in this post, as it’s generally something outside of the application itself, as it usually takes the form of an API, local database, flat file store, whatever you fancy. Next in line we have a dispatcher. This contains the methods that the various UI components will use. For example, if someone clicks on a button, the dispatcher holds the method that will run upon that click happening.
All state and methods are held as high in the application architecture as possible. As a result, any method that’s capable of modifying state sits in the same place as that state and is passed as a prop down to any child components that may need it.
The next level is the stores. These hold the application data and state. In very simple applications, or while I’m prototyping, I’ll often combine the events and state in the same file, to make monitoring the flow of data simpler. However, for larger pieces, these become abstracted out using Redux. The concept of all state in a single place remains the same however. This gives you a simple way of understanding how your application behaviour works; events are managed in one place, your data in another, but both sit in the same level of hierarchy. Debugging then becomes a simple matter of watching the flow of state and data through the application.
The final layer is the views themselves. By having taken the data out, and placed all the main methods in parent classes, these end up as more simple React classes, which take in props and output the UI.
Separation of Concerns
The reason why I enjoy this style of writing an application is very simple – separation of concerns. With every type of component broken out, you have a nice mental model of where everything is. Even when you get more complex callback systems, those callbacks are always returning data to a single level, which flows through in only one way. This gives an easy way to examine the complete state of a system at any time, and when callbacks return outputs, that state is always modified as high up the architectural tree as possible, making management of data far simpler.
It also creates advantages with regards to other helpful traits for code. For example, when time comes to test your application, you can feed in test data incredibly simply – just give the stores the values you want to test with and watch the data flow through the system.
An Example: A Brief History of Humanity
I’ve built a small application about the history of humanity from 1750-2015 to show how I’m using this concept in practice. Before we get into the detail, I’d suggest having a quick glance over the github repo for it, or downloading the source code and looking at it that way.
Note that this application is not the perfect way to do things. However, it’s a simple, easy to read example, designed for people who are new to this. We’ll look at ways you could improve it later in the post. Those who’ve spent more time building things in JS will notice where I’m making things a little more basic than they need to be. So if you’re looking at those things and wondering why, I’m 99% certain that what you’ll be looking at is intentional.
We start off with the functions.js file. I’m going to assume that you’ve figured out that we’re using React.JS so I’m not going to detail that. Functions contains a few smaller things. The first is Qwest, a wonderful little XHR library by Aurélien Delogu. That’s going to handle all our data communication. The next two are tiny helpers. One is a simple wrapper around type of undefined, because it’s really annoying typing that a lot, and the second gives jQuery/Mootools style handlers for getting DOM elements.
The rest of this file deals with the WebGL background in the header of our example. As a result, you can safely ignore most of that, unless you want a quick nose around a super-simple Three.JS app.
Everything from here on out is React based. Since we’re not doing pub/sub style subscription to changes (as this isn’t a “live” application), we can simplify our build a little. Firstly, the code sets up the initial state of the application. Everything starts in an off state, and as it gets populated by data, those states turn on.
With the initial data set, the code then fetches the initial data for our system. This is held in a JSON file, which we fetch and then use to populate our start state. The second function takes the raw data object, parses it, and sets the state of various things as a result of the data that fits the criteria. Loosely, it gets the data between two dates, matching a specific tag, and checks if the current pagination point position works given the new data.
The next parts deal with the display of a single item. Those functions get data from Wikipedia pertaining to the article selected, again using Qwest to fetch data, and updating the state only when the last iteration through the various calls runs to avoid un-necessary re-rendering.
The final functions provide the handlers for the controls, giving the tools required for altering state when UI elements are clicked.
Obviously, there’s room to split this file down further into something handling just the actions and just the parts that store the data. However, to keep things slightly more simple for this, I’ve left them all together.
This leaves us with individual view components, for rendering the map, controls, list items, individual item, and social sharing buttons. Each of these has now become a more simple class, as a result of almost all the state and controls being handled in the dispatcher.js file. The exceptions to this is the item detail component, which holds state to do with moving through images, the social file which holds state as to whether it’s currently open or not, and the controls for whether it’s being viewed on a mobile device. Each of these is dealing with state which only affects that individual component, whereas the state held higher up affects more than one component, so it makes sense to hold it in the dispatcher file.
The idea is to make the component functions into black boxes, with the advantages discussed earlier. Minimal state, with higher state altering methods passed in.
Google Maps UI Component
There’s a particularly interesting UI piece (in terms of React re-rendering) with the Google Maps widget. This gets rendered on the initial load, and then doesn’t get re-rendered. Instead, we simply alter what’s currently displayed, saving render time.
The main reason why we do it this way is a little different though. Obviously don’t want to manipulate the DOM outside of React (to avoid conflicts between the real and virtual DOMs when it re-renders, which could cause at best a bunch of thrash as it tears down the maps component only to re-render exactly the same thing again), but Maps has to to render. To get around this we pass the element it should render to in as a reference. This gives us a tidy way to tell Google Maps to render to, whilst allowing us to still tear everything down if needed.
I’d recommend using this method for dealing with charts, or any other DOM manipulation that you may have to do outside of React.
Improving the Application
For starters, I’d recommend reading What is the Flux Application Architecture? and Beyond the to-do app: Writing complex applications using Flux & React JS. With that done, you’ll spot all sorts of ways you could take the code for this app and improve it. You could use Redux to tidy up the handling of the XHR requests for updating the selectedItems state variable by abstracting away that data. It’d also be possible without too much work to alter the system to use events to notify the stores of changes, which would allow for running the application on Node.JS as you wish.
Other ideas would include changing the components so that they don’t have to re-render all the time. There’s a very simple example of that working using shouldComponentUpdate in the GMap component, but it could be used in the ItemDetail and Controls ones as well. There are lots of ways you could tinker with this to make it more elegant and robust.
Notes and Thanks
If you want do take this and modify it or extend it, ping me on Twitter. I’d love to take a look. Also, I know about the notes on the Three.JS textures. At some point, I may bother to fix these, but I was trying to get this done as quickly as possible, rather than necessarily as perfectly as possible.
Any issues with the data are entirely my fault, and please let me know so I can fix them.