How to avoid refactoring in your first React.js application
Popularity of React.js is growing pretty fast and it seems like React became a compact and beautiful way of how to develop your applications. I have worked on a side project and felt like it is right time to try React. During development of my first project in React I had to do several refactorings. In this short blog post I will try to point out some pitfalls, which you should take care of if you want to avoid refactoring in your first React.js application.
The Begging
Over the internet you can find a lot of tutorials how to start with React.js with basic examples. I recommend Ken Wheeler’s article Learning React.js: Getting Started and Concepts and Chris Harrington’s article React Tutorial: Creating a Simple Application Using React JS and Flux Architecture.
Props vs State
After reading some introductions to React you probably noticed components properties props and state. When I developed my first React application I didn’t pay attention to these properties and thought it’s clear to me. I underestimated the potential issues of not using them properly. Later, I realised that I used these properties in the wrong way. By misusing these properties you can end up with a lot of bugs, which will cost you a lot of time to fix. If you know the difference between state and props and are not sure which one to use in your component, read this article. I like especially the decision table and after couple of times using this table, you can easily keep them in mind and avoid misusing these properties.
– | props | state |
---|---|---|
Can get initial value from parent Component? | Yes | Yes |
Can be changed by parent Component? | Yes | No |
Can set default values inside Component?* | Yes | Yes |
Can change inside Component? | No | Yes |
Can set initial value for child Components? | Yes | Yes |
Can change in child Components? | Yes | No |
Try to keep as many of your components as possible stateless. Dan Abramov wrote great article about separation Presentational and Container Components. In React.js documentation we can find:
A common pattern is to create several stateless components that just render data, and have a stateful component above them in the hierarchy that passes its state to its children via props. The stateful component encapsulates all of the interaction logic, while the stateless components take care of rendering data in a declarative way.
Lifecycle methods
React components have their own lifecycle which executes a sequence of various methods. Lifecycle methods are different, when component is mounted and updated, you can see lifecycles and sequence of methods in this table, which is more readable than list of methods in documentation.
Anti-patterns and best practices
When I play with a new technology I always try to find some anti-pattern and best practices. When I started with React, I found only one anti-pattern. In terms of best practices I like John Cobb’s An opinionated guide to React.js best practices and conventions and Jan Carlo Viray’s article React Best Practice Compilation. From the beginning, try to organize application file structure. Recently, I read React.js Best Practices for 2016 and highly recommend it.
ES6
When I developed my first React.js components, I followed some tutorials and used ES5 syntax. You can define React component as an ES6 class and use Babel for transpilation your code to ES5. In the beginning I didn’t have any experience with Babel and wasn’t sure if transpilation works correctly. I decided to try ES6 syntax and Babel, because I wanted to have my source code cleaner, more readable and quicker to develop. Since I tried it, I can’t imagine writing my code in ES5. ES6 provides so many great features, your code is more readable, you can develop more effectively and Babel works very well. Thus, I don’t see any reasons why not using ES6. If you want to refactor your components, follow steps of Kris Jordan’s article Refactoring React Components to ES6 Classes and some others tips you can find here. If you really need mixins in your component, just use react-mixin.
shouldComponentUpdate()
React’s diff algorithm between virtual DOM and actual DOM is innovation which boosts the performance of your application and some other frameworks try to follow. If performance of your first React.js application is a critical aspect, you should consider using this method and immutable data structure for React components. This method is triggered before the re-rendering process of component giving the developer the ability not to perform render method and diffing algorithm. If you use immutable data structure, it makes tracking changes cheap and “previous” props and stats can be compared with the new one. I recommend Tomas Weiss’s article Turbocharge Your React Application with shouldComponentUpdate and Immutable.js, where you can find result of basic benchmark with and without shouldComponentUpdate method.
There are more use cases, when this method is very handy. Let’s imagine we need a big grid component, which does some complex things with cells. For our grid component we find only jQuery library and their extensions. Problem solved, we decide to use this library, otherwise we spend long time to rewrite this library with extensions to React. If we want to change the data of our big grid component, we have to re-initialize our grid as a jQuery object in React using the componentDidUpdate
method. If our jQuery library provides a method $(gridObj).updateCellOnCoordinates(x, y, data);
to update a particular cell instead of a whole grid and we use immutable data structure, in the shouldComponentUpdate
method we can just diff data for our jQuery object and use the updateCellOnCoordinates
method without re-initializing jQuery object in the componentDidUpdate
method.
let diff = require('immutablediff'); | |
shouldComponentUpdate(nextProps, nextState) { | |
let propsDiff = diff(this.props.data, nextProps.data); | |
if (propsDiff.size === 1) { | |
let operation = propsDiff.get(0).get("op"); | |
let replacedObj = propsDiff.get(0).get("value"); | |
switch (operation) { | |
case "replace": | |
if (Immutable.Map.isMap(replacedObj)) { | |
let editedObj = {}; | |
let xCoordinate; | |
let yCoordinate; | |
for (let entry of replacedObj.entries()) { | |
if (entry[0] === 'x-coordinate'){ | |
xCoordinate = entry[1]; | |
} | |
if (entry[0] === 'y-coordinate'){ | |
yCoordinate = entry[1]; | |
} | |
editedObj[entry[0]] = entry[1]; | |
} | |
$(ReactDOM.findDOMNode(this.refs.dataGrid)) | |
.updateCellOnCoordinate(xCoordinate, yCoordinate, editedObj); | |
return false; | |
} | |
break; | |
case "remove": | |
… | |
break; | |
case "add": | |
… | |
break; | |
default: | |
return true; | |
} | |
} | |
return true; | |
} |
Conclusion
In this article I wanted to point out pitfalls and how to avoid them if you are going to develop your first react application. The most important thing is not to misusing state and props. With ES6 and Babel you can develop faster and cleaner code. If you are sure your application will re-render thousands of components or you are going to use a library, which complex objects have to be re-initialized in componentDidUpdate
method, consider using immutable data structure of your store along with the shouldComponentUpdate
method.
1 COMMENT
[…] How to avoid refactoring in your first React.js application […]