There are any number of ways to structure a React application, and dozens of libraries/frameworks hosted on GitHub to help you quickly bootstrap the next Instagram. They are all pretty fun and interesting and will probably allow you build to something awesome in very little time.
But I find the approach presented in this article to be particularly elegant. It’s simple and powerful and concise and also highly performant. This method is based on omniscient (which is inspired by Om) and the architecture of browser.html, which utilizes a technique conceived by the brilliant Gozala (who drew inspiration from the architecture of Elm).
I’ve embedded several interactive codepen demos in this article which do not work properly in browsers less than 700px wide.
Laying the Groundwork
The component function
A common pattern is to simplify the React component creation process by distilling it down to a single function call that accepts the render function as its only argument:
Note that we passed the named function Hello as an argument. The component function uses this name to assign a displayName to our React component so that it plays nice with the React Dev Tools.
And here is our component function:
Our renderFn receives three arguments. The first is this.props. The second and third arguments are simply properties of this.props and are provided for convenience.
Also notice component has an optional first argument additionalMixins. As you might have suspected, we can use it like so:
With the component function, we can write components that don’t utilize this, and instead enjoy a more functional style. However, components that need additional mixins will still probably need to use this, but I think that’s an acceptable compromise. (An experimental project by aickin does away with this completely by re-wiring the component lifecycle, but I’d rather not move that far away from React’s API.)
The Renderer Component
Renderer will be the bootstrapping mechanism in our application.
Renderer has the following essential features:
- It stores the top-level application state data which will be passed down the component tree. All child components will be stateless which mean they rely on Renderer’s data object for all their state. So, Renderer is the only component that calls setState.
- Renderer passes it’s data object and edit function to it’s child component App. All changes to data from any level of the component tree will utilize this edit function.
Now we can add a button to our Hello component that will allow me to change my name from “gilbox” to “Maude Lebowski”:
Do you notice a little problem with this? Every component into which we pass the edit function will have the ability to change any part of the application state. How can we encapsulate each component so it only has access to the parts of the state that it really needs to change?
Cursors are the most common way to deal with encapsulating components whose state comes from a top-level immutable data structure. Cursors are great but they get complicated. Instead we’ll use functional composition to create a sort of function cursor via a subedit function.
Given an edit function and a path within the immutable data structure, subedit returns a new edit function.
This allows us to refactor the last example in the previous section to fully encapsulate our component:
The mechanics of the subedit function can be a little confusing at first. I’ve created an interactive REPL illustrating the concept so you can mess around with it until it makes sense:
Did you notice that we passed the edit function via the statics prop? This isn’t doing anything special yet. Now, let’s do something special with statics. (It’s worth pointing out that this statics prop is not at all related to React’s built-in statics feature). We can easily optimize shouldComponentUpdate to ignore changes to statics by adding a single line of code to the PureRenderMixin.
if (keysA[i] === ‘statics’) continue;
Let’s name this modified version of PureRenderMixin, ComponentRenderMixin and now we utilize it in component:
Now this looks pretty good, but there is a subtle bug with our latest implementation. Because of this new optimization, calling an edit function can potentially change the wrong piece of data when items are reordered:
We’ll fix this bug in the next section.
The staticFunctionsMixin is the final piece of the puzzle missing from our component implementation. It works by hooking into two React lifecycle methods:
- componentWillMount → Before the component mounts, we will wrap all static functions in a delegate function.
- componentWillRecieveProps → Whenever the component receives props, we will propagate changes to delegate functions by updating their wrapped “delegee” functions.
The staticFunctionsMixin ensures functions passed via the statics prop stay up-to-date without requiring a re-render. This means that the event handlers in our Components will always have the correct edit functions even if they are reordered.
The implementation for staticFunctionsMixin comes directly from omniscient. Here is staticFunctionsMixin along with our final component implementation:
And for posterity, I’ve embedded the fixed version of the previous interactive demo:
In less than 100 lines of code, we have the basis for an incredibly powerful functional architecture for react. Keep in mind, this component function is extremely similar to omniscient’s well-maintained component function. The main difference is that omniscient’s component function supports a jsx-free syntax along with a host of other features, most of which you won’t need if you plan to use the approach I’ve presented in this article.
The choice to use jsx is a personal one. Functional purists often shy away from jsx. However, I find deeply nested jsx somewhat easier to read because of the intuitive child management and the explicit end tags.
Demo: Phone Number Input
Before I became acquainted with React, angularjs was my jam. However, one of my frustrations with angularjs was it’s very complicated ngModel directive. ngModel is the mechanism that angularjs deals with formatting and validating form inputs. If we strip away the complexity, some of ngModel’s concepts can be useful in a React application. More specifically, the concept of an input having a formatter and a parser translates very well into an elegant functional react paradigm.
- parser: a function applied to the user’s input that transforms the value into a machine-readable value.
914–555–2482 → 9145552482
- formatter: a function applied to a machine-readable value that formats the value to make it pretty for the user.
9145552482 → 914–555–2482
In this demo we’ll use a formatter and a parser to create a PhoneInput component in a functional style.
And here is the source code:
Demo: Address Book
Building on the previous example, here’s a more complicated demo with features like add/remove entries and an undo button. It’s probably too much code to comfortably read in codepen (who’s syntax highlighting chokes on a template string), but you can read the source on github.
I’ve created a github repo for the code presented in this article. You can add the component function it to your own react or react-native project with npm install elegant-react.
Is that it? What about Flux?
I’ve been exploring different Flux-like and non-Flux-like approaches that preserve a highly functional style. Dan Abramov’s exploration of pure-functions as Flux stores has provided some inspiration. I’ve also been toying with reactive streams. What do you think? Has this article been interesting to you and would another article be useful to you? Let me know!
Psst… I’ve written a follow-up to this article. Check it out: