Want to explore how we plan to create and maintain full-stack web applications with reactive views and universal rendering? Then keep reading. Or just head right to the action.
React by Facebook has gained a lot of traction the last few years.
React gives developers a way of defining the view layer of their apps declaratively. Developers specify how the view should look at any given time given a set of properties, and React will take care of UI updates as data is passed to them.
React is a solid UI library suitable for any situation where component re-use and sharing is wanted along with changing data requirements. It is used and battle-tested by Facebook and Instagram in production.
The ‘universal’ advantage of React
- Performance. The client will have the finished markup for “first view” instantly after the last byte over the wire. No need to wait for the 100kb+ script bundle download and consequent renders on the client side before the product can be used.
- Gradual enhancement. Client will get finished markup instantly from server. Application takes over on the client-side if a runtime is available when it is ready; for a seamless transition.
The advantages of a SPA, without the major disadvantages.
The ‘native’ advantage of React
React also has an initiative to provide UIs in native iOS and Android applications with similar declarative view definitions.
React: The missing pieces for complete web apps
Focused on the view part, React alone does not suffice for fully featured web applications in practice. React alone with some basic HTML skeleton is great for prototyping a concept or idea, but as the requirements for fully featured web applications emerge, developers quickly find themselves overwhelmed and fatigued by tooling, boilerplates, starter kits and library choices. Here is a short list.
- Data flow: React implements one-way reactive data flow. But library choices (like Facebook’s own Flux) that can provide this to your React views are abundant and have some differences. What to choose? How do they work? Do I need this or not? When should it be introduced to your project? How does it work with universal rendering?
- Getting ready for production. Bundling and setting up your application is left entirely up to the individual developer on every project. Learning Webpack and friends is not trivial. Or should you use Browserify? …
- Styling your components? What is the best way for your use case? How do I bundle it into the application or component?
- As the different libraries used get updated, how do you keep up with this across all your products? They will all be tangled in starter-kit boilerplate tightly coupled to your app, potentially complicating migration paths further in the future.
- Making it universal. How do I solve routing? Where do I hook in my flux store correctly? How do i perform data fetching? What if I only want to run a feature on the server in some situations but not all?
In short; the issue we are facing is combining React with all the other required elements in order to build a complete web application, as well as having to invent the glue providing important things like universal rendering. Once a full-stack composition that works for you is complete, it is also prone to being outdated as all the moving parts get updates and new releases individually. This gets worse to handle the more products or components you own and actively maintain.
For an example of the sheer scope involved in a universal react application, check out react redux universal hot example
Why React when it does not solve everything?
React solves this with declarative views and enforcing one-way dataflow clearly separating the concerns. It brings sharing code between server and client to the table, along with tools for creating native applications using familiar technology. A lot of these common pitfalls that give web applications growing pains are solved with the declarative nature of React components. Component sharing and re-use is also an important advantage of using React.
Extra libraries, more problems
While React solves the view part elegantly with established ways of defining how a view should look at any given point in time, it is only one piece of the puzzle if your goal is to create a universal application. You will also need:
- Environment management
- Building and bundling for production
- Developer tools; hot reloading, browsersync ++ across projects
- Keeping your “boilerplates” updated across components
- Updating used libraries as they continue to evolve
- Data flow
- Data fetching
All of these things are available as high quality packages in npm, but they all have to be chosen and then glued together in your application layer. Every time. The individual packages address their own concerns and change over time.
Solving npm library composition here at VG
At VG we realise that React helps us solve the view part of our applications now and into the forseeable future. React native also seems promising to us. We love the concept of unidirectional data flow.
In order to keep an effective developer workflow where we can quickly create and launch products/components at a high velocity without creating a maintenance trap where each project essentially is its own “silo” when it comes to builds and tooling, we need to manage the library compositions that we wish to use in most of our projects. We wish to sweep the complexities of configuration and solutions to common problems into an established ecosystem that is easy and predictable for our developers to interact with. We also want to make it trivial to contribute to different products and components for our developers. To achieve this we need to knock down these barriers introduced by different builds and tooling.
From a birdseye poerspective the model we have come up with is
- Developer interacts with a common **command line interface**.
- The command line interface provides its commands from a given platform “extension” used by the active project. This extension is semantically versioned, and maintained by VG core.
- Platform “extension” provides external libraries and the required runtime glue for common tasks
The advantage of this approach is that the individual developer has a very familiar way of interacting with configuration, builds and setting up the developer environment of any app. The extension is strongly opinionated, but it allows a developer to focus on solving the problem instead of tool choice and library composition. This is important to us.
- Easy to start developing new applications and components
- Tools increasing developer productivity “just work”
- Applications created focus on solving the problem only
- Applications do not perform heavy glue-like operations, they use versioned extensions that will do this for them
- Developers have the option to override and extend functionality where needed, when creating a separate extension does not make sense
- Developers can create their own extensions with the same predictable developer experience if the existing extensions do not suit their needs or expose the preferred libraries
Roc – Composing libraries
We have started solving this for us in a generic way. It is not limited to React, Webpack or Redux – but this is what we have prioritized creating an opinionated extension for. There is nothing stopping anyone from creating an extension that uses Browserify and Angular for example exposing the same command line interface.
The Roc project is open and shared on Github. We hope that it can help or inspire others that face the same challenges as us.
The only real footprint left by Roc in an application/component is the configuration file. The rest is direct use of established open source software, leaving plenty of migration paths open.
A typical Roc application or component only contains relevant application code. In the case of Redux+React this is
- Redux reducers
- Redux actions
- React routes
- React components
The roc-web-react extension glues it all together and makes it easy to configure the location of the above hooks in any given project.
Creating a full-stack Redux+React project with universal rendering
Developing an app
Building for production