Redux in Plain English: Building Boilerplate
Note: This post was originally published on the Quick Left blog on 4/20/16.
Hopping on a greenfield app is one of software development’s truest joys. There’s no dense context to learn, no screwy hacks to navigate, and there’s a fresh opportunity to get everything just right. React apps have a nontrivial amount setup required, though, given the scope of the library. Unlike something like Ember, React does not seek to be an all-encompassing framework and therefore does not have an analogue to ember new ProjectName
. For this reason, you’ll find a ton of boilerplate and starter app examples in the wild. Where the Ember community has agreed on some style preferences, or conventions, React developers are free to form their own.
Setting up a React app is a nontrivial endeavor for a number of reasons. One of the challenges is the relatively new and rapidly evolving toolset. Webpack is an amazing tool and is at the heart of many Redux applications. However, developers often end up learning about both around the same time, which can make for an intimidating learning curve. Webpack also acts as the main switchboard for Babel, ESlint, hot module reloading, and more. If your head’s spinning trying to figure out how all these integrations work harmoniously, fear not.
This post covers my thought process as I set up a new Redux boilerplate application. Because there are many technologies involved, discussion of each will be kept fairly high level, with links provided for you to explore more deeply. If you’d like to skip straight to the final boilerplate code, you can find that here.
Initialize a new project
Like any good JavaScript devotee, we’ll use npm to manage our dependencies. Do the ever-challenging work of selecting a name for your project, create and navigate to that directory, then run npm init
. Answer the project setup questions as you see fit. Because the application is not intended to be pulled in anywhere as a dependency, the semantic versioning and most other questions are inconsequential. I opted for an MIT license, out of familiarity.
Configuring webpack
On to the main event. Webpack will orchestrate how all the tooling in the app works in concert. If totally unfamiliar, I’d recommend running though the official tutorial to get a quick understanding of what module bundling looks like. This blog postis another great resource for filling in some gaps. A common practice is to have separate webpack configuration files for production and development, because each scenario can (and should) make use of unique tools. If you’d like to skip ahead, here are my webpack.dev.config.js and webpack.prod.config.js files.
At the highest level, webpack takes an entry
file, bundles up the app into one (or more) file, and spits out the result to wherever we specify in the output
. Naturally, the value of webpack isn’t that it turns your app into one file’s worth of JavaScript – it’s the additional plugin integration. A few examples:
Loaders
Webpack uses type-specific loaders to pull in just about everything: JavaScript, JSON, CSS, images, and more. Within a webpack config file, loaders are listed with at least a couple properties: test
andloader
. test
usually provides a regular expression to check appropriate filenames, and loader
specifies the type of loader to use for those files. For more details, read on here. Note that each loader will need to get installed separately via npm.
Babel
You’ve likely noticed that every sample Redux application you’ve seen uses ES6 syntax. Since browsers aren’t quite ready to handle ES6 out of the box yet, Babel bridges that gap for us. We can introduce Babel via webpack by just including another loader:babel-loader
. Install the loader, babel-core
, and any Babel presets you’re interested in. A popular convention for Redux apps is to use babel-preset-es2015
and babel-preset-react
. See my package.json for dependencies, webpack config for the loader implementation, and the .babelrc for preset configuration.
ESlint
Developing with a linter is an absolute must. Webpack’s integration of ESlint is spot on, performing a check on every save of your editor. We’ll install it with a familiar pattern: eslint-loader
. Install the loader, eslint
, and the React plugin: eslint-plugin-react
. Instead of adding this to the list of existing loaders in the webpack config file, ESlint will get special treatment and be nested under a preLoaders
key. This makes intuitive sense, because we want our code to get linted before Babel transforms are applied. As long as the ESlint loader is applied above the Babel loader, it will work as intended, but using a preLoader
is a good safeguard. ESlint has its own handy feature to help you generate a config file: eslint --init
. I used an extensive custom configuration for more granularity, viewable here.
Hot reloading
“Hot reloading” is the best workflow improvement to come along in recent memory. It allows for page refreshes without losing state. So, if you’ve navigated through several pages and filled out several forms to arrive at a corner of your application where you’d like to test a bug, making a change in your application triggers an instant page refresh, while maintaining that state.
Currently, the best way to use this feature is with react-hot-loader. Hot reloading requires a dev server, so there is just a bit more effort in setting this up. For reference, check out my server.js file and related webpack.dev.config.js updates to loaders
, scripts
,entry
, and plugins
. An important note: there are plans to phase out react-hot-loader
in favor of the (currently experimental) react-transform-hmr library.
Redux boilerplate
If you came here looking to learn how Redux works, by now you’ve probably figured out that this post is not the one. Good news though: there is such a post.
My goal for this boilerplate project was to include just enough Redux scaffolding to clearly convey a complete workflow. By personal preference, I’ve nested the Redux app within a src
directory. There you’ll find the usual cast of characters: containers, components, actions, reducers, and the store. I’ve implemented a small list-making feature to illustrate the Redux workflow. Feel free to pause to explore that here.
Routing
Routing data can be represented by a JavaScript object, therefore it can be tracked in our Redux global state object, like anything else. Most Redux example applications I’ve stumbled on utilize react-router. There are a couple routing libraries specific to Redux, though: react-router-redux and redux-router. I’ve had success with both, but the former won the most hearts and earned a spot in the reactjs Github organization.
react-router-redux
is a small library that leverages existing react-router
APIs to keep the Redux state object updated with changes in routing. To implement react-router-redux
, we use the syncHistoryWithStore
function in index.js, render theRouter
component in the Root
component, and build out the routes in routes.js. The final step is to include the routing reducer in our combineReducers
function. At this point, typical React navigation using react-router
will be captured in our Redux global state object.
DevTools
The nice part about capturing routing information in the Redux store is that the Redux DevTools allow us to use it for “time travel” debugging. With the DevTools, we can click through a user’s experience with our application based on each change in state, rewinding and fast-forwarding through their session. Implementing the DevTools is fairly straightforward. We’ll set up some boilerplate in a DevTools component, and instrument them in the store when we’re in development. Notice that there are a couple other dependencies involved in the DevTools component, one of which is the DockMonitor
. Once installed, we can hide or show the tools using Ctrl+h and change its position on the screen with Ctrl+w.
Testing
I’ve chosen to include Mocha, expect and enzyme in this project. The Redux project officially recommends Mocha, and makes extensive use of expect. Enzyme is a newer framework for testing components, written and maintained by the folks at Airbnb. It’s quickly gained popularity for its ease of use and resemblance to jQuery. The only quirk worth noting is that enzyme’s mount
API requires DOM to render to. JSDOM is a common option that requires a sprinkle of boilerplate, however I’ve opted to have this handled by karma via a Chrome browser.
Karma is a test-runner and in its configuration file, we specify our test file paths and other settings. Karma can also speed development time by running the test suite on every file change, providing us rapid feedback cycles. That’s accomplished by setting the singleRun
and autoWatch
properties. Take note of the handful of dependencies prefixed with karma in the package.json.
Wrapping up
Few will claim to be an expert in setting up React projects, because this work generally falls in the “set it and forget it” category. Often someone will go through this process to kick off a project, then rarely need to tweak it again. It’s important for other developers on the project to have some level of understanding about the tooling it uses, though. First, to mitigate your bus factor, but additionally to keep a better eye on new developments in the ecosystem. As webpack and friends continue to quickly evolve, staying current means free boosts in performance, features and/or security. At the time of writing, webpack 2.0 is in beta; check out what’s coming next.
Its important to start your application on a solid foundation and there’s nothing wrong with using a template to help you get there. There’s even a couple generator libraries out there, such as generator-redux. If you use my or any other boilerplate app to get off the ground, do your homework to understand what tools are being used, but then get building! After all, that is the point of a starter kit right?
A lot of personal preferences factor into building a boilerplate app. What did I leave out that you prefer to use? Any tools or style conventions? Let me know in the comments. Unless you prefer no semicolons. Then you’re just wrong. 😉
Note: since writing this, I’ve published redux-starter-v2. Its foundation uses the new React CLI tool, create-react-app.