Starting a React project at the end of the year: 2015 VS. 2016
About a year ago, team members and I were thinking about an ideal front-end stack to fulfill a client's business requirements. We ended up with a React setup. A year later, we can say that the technologies we used were valid choices for the project. On this December (2016) I started working on a new project. The new project also includes React, but new best practices, libraries, and ideas have come up that had to be evaluated. Using the current and earlier project as an example, I'll demonstrate what has happened in the React realm lately.
Late 2015 project
Overview
I started working on this project in December 2015. It was an existing service that had been built as a multi-page application where user-interface logic was simple and most of the actions did a page load. New requirements pushed towards React as implementation had to be more of a single-page app than multi-page.
In the client's other projects, TypeScript had provided plenty of productivity and quality improvements, and we therefore chose to adopt TypeScript instead of the ES2015/ES2016 Babel setup. Having auto-complete for third-party libraries and our own React components was worth the trouble. When the project started, the type definition files were scattered, and the definition files had to be downloaded with a separate tool. Today the situation is better as TypeScript is moving towards using npm to resolve definition files.
Let's see what was included besides React and the TypeScript language.
Libraries
The front-end stack for the project (which started a year ago) consisted of the following libraries:
- React
- Redux
- redux-thunk
- immutable.js
- moment.js
- lodash
- classnames
The chosen libraries have been surprisingly stable; for example, breaking changes in the React and Redux API have been very rare.
Some might remember the times when there were a gazillion Flux implementations, such as Fluxible and Alt. Even though Redux isn't a straight Flux implementation, it was clear a year ago that Redux would handle most of the state related cases and was the number one choice.
Immutable.js was and still is a very stable and powerful way to bring immutability and various data structures to JavaScript. We used Immutable.js only on the state of the application, and Redux reducers were doing the manipulation. React components were reading the immutable data.
I really enjoyed using functions like .setIn():
return state.setIn(['tree', 'branch', 'selectedItemId'], 1)
Instead of writing plain old JavaScript:
if(state.tree && state.tree.branch && state.tree.branch.selectedItemId) {
state.tree.branch.selectedItemId = 1;
}
return state;
This example may not be the best, with production-quality code, you can structure your reducers in a way that would simplify the if statement.
Building and testing
Mocha and Chai were installed and configured for unit testing. Most of the tests were written for the Redux reducers. Using Immutable.js reduced the number of tests as we didn't have to verify if the state had been mutated.
As far as I can remember, in the blog posts React gurus tested components by verifying that a certain DOM element or elements were available and that the component had been mounted. There are now new ways to do component testing (more on that later).
The old pages of the service that were non-React were already using Grunt as a build tool, so we decided to continue that. The cool kids would have already rewritten build script to use Broccoli, npm scripts, etc. We decided to stick with Grunt because the value of a rewrite didn't outweigh the cost. We used Browserify to manage module dependencies.
Late 2016 project
Overview
The project that I am working on now is based on the Create React App, which is an excellent starting point for any React application. The idea is that you have an application up and running in a matter of seconds without writing any configuration files. The directory structure is easy to read and contains files in which you can type your HTML, CSS, and JavaScript. A browser tab will refresh its contents when files have changed.
Unfortunately, hot module reloading (HMR) is not currently available in the Create React App. It seems that Hot Module Reloading is supported in the Create React App, but not using react-hot-loader. Mark Erikson wrote a blog post series called Practical Redux, it's third part contains information about Create React App setup and HMR.
How does this differ from the dozens of starter kits with pre-configured components? The beauty is that, in the Create React App's initial state, it can be updated and remain a black box. When you need to, however, you can run the command npm run eject to reveal all the configuration files and tools. There is no coming back after you have done that, so eject works as a scaffolding tool.
The Create React App contains modern build and testing setup:
- Babel with extensions
- Autoprefixer
- ESLint
- Webpack with various loaders
- Jest as a testing framework
etc.
The project has gotten a lot of traction, so there is documentation of common tasks. This will save time. Writing React tutorials is also much easier for blog authors: no more multi-paragraph explanations of the way to set up the development environment; with just a few steps, you can start writing about things that matter.
Towards more component-based thinking
The way React components are now written differs from previous React development and best practices. React Stateless Functional Components (introduced in React 0.14) are now preferred over class-based. If the component doesn't require anything from React Component class then Stateless functional components are the way to go; easy to test and create less noise in code than the class-based components.
In this project, I like that the style and test files are next to the component.
Components/
DoorKnob.js
DoorKnob.test.js
DoorKnob.css
When you're working on a component it is easy to spot related files as components may or may not have styles or tests.
Webpack's CSS Module Loader makes sure that there won't ever be a name collision when I type the class name doorKnob. doorKnob will be transformed into a unique identifier, such as _23_aKvs-b8bW2Vg3fwHozO.
Testing
This is my first project using Jest. In addition to regular JavaScript unit tests, there is snapshot testing, which is an interesting idea.
Let's assume we have a component called Tree. The Jest snapshot test would work like this:
- Tree component renders correctly onscreen.
- you run a test:
test("renders correct branches", () => {
const tree = renderer.create(
<Tree branches={items}} />
).toJSON();
expect(tree).toMatchSnapshot();
});
- A snapshot file is created.
- You later make changes to the Tree component.
- When you run tests, Jest will compare snapshots.
- Snapshots differ, so you then need to decide whether the change was intended.
- If you choose that new snapshot to represent what you wanted to achieve, the snapshot will be updated.
What is this snapshot? It is a JavaScript file that contains HTML output from the component in a template literal. In plain English, it is basically an auto-generated JavaScript file of the exported HTML structure that is expected for each test.
Here is a snapshot from Jest examples:
exports[`test changes the class when hovered 1`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}>
Facebook
</a>
`;
Overview
React's stability over the past year has been a positive surprise. Redux is still the most popular choice for state management. Developers have built solutions on top of Redux when they have seen complex problems, for example, redux-saga when dealing with asynchronicity.
New ideas are coming that will be a useful addition to React, such as, MobX which is a library that does state management but takes a bit different angle than Redux.
The official React documentation has been updated with a lot of new information: the recipes section contained new ideas like Computing Derived Data. If it has been months from the last visit to the official documentation, I highly recommend checking the updates.
Overall, I am thrilled at the current state React and the direction in which React and its libraries are going.