This is a series of posts about comparison between components in Angular and React. The idea of these posts is not to judge which one is best, bur rather to show how these two cope with some typical problems and how we, developers, can use them. In the first three parts (Part 1, Part 2, Part 3) I talked about the starting points – how to create a component, how to attach a template to it, how to do data binding. In this fourth part I am going to deep a bit more into the lifecycle of these components. I will discuss what opportunities and points of extensibility one can find in Angular and React and how one can benefit from them.
What is a lifecycle?
Before I start with the real discussion I want to say a few words about what a lifecycle is. We, humans, live in a specific environment (the surrounding world). We appear as babies and diminish at some point of time. The same process is applied by frameworks: a component lives in a specific context, generated by the framework; a component typically has a beginning and an end, determined again by the framework (which acts based on some events). The whole period from the creation of one component, its life, to its end is the lifecycle of that component. During that period the component can change in one or another way.
Component lifecycle
We have already seen how to create components in both Angular and React. In the former we would use
1 |
app.directive() |
, while in the latter –
1 |
React.createClass() |
. Note that by doing this we define a component, i.e. we create a template definition (not to be confused with a HTML template) that the framework can use to create instances of that component.
React
React works with a Virtual DOM so it is very important to be able to do comparisons with the real DOM at any time in order to decide whether to update it. A React component has three main lifecycle phases:
- Mounting – initializing the component and render it for the first time
- Updating – updating the component and re-rendering it (f.x., when the state has changed)
- Unmounting – unloading the component
During these three phases one can inject code at specific points in order to control the behavior of the component. The main such points are:
-
1componentWillMount()
-
1componentDidMount()
-
1componentWillUpdate()1setState()
-
1componentDidUpdate()
-
1componentWillUnmount()
The following example demonstrates the order of execution of these points (JSFiddle).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var Child = React.createClass({ ... render: function() { return <span>{this.props.times}</span>; } }); var Parent = React.createClass({ ... getInitialState: function() { return { times: 0 }; }, render: function() { return <Child times={this.state.times} />; } }); React.render(<Parent />, container); |
Mounting
- Parent: getInitialState()
- Parent: componentWillMount()
- Parent: render()
- Child: getInitialState()
- Child: componentWillMount()
- Child: render()
- Child: componentDidMount()
- Parent: componentDidMount()
Updating
- Parent: shouldComponentUpdate()
- Parent: componentWillUpdate()
- Parent: render()
- Child: componentWillReceiveProps()
- Child: shouldComponentUpdate()
- Child: componentWillUpdate()
- Child: render()
- Child: componentDidUpdate()
- Parent: componentDidUpdate()
Unmounting
-
- Child: componentWillUnmount()
- Parent: componentWillUnmount()
Angular
Angular works directly with the DOM, so that requires a different mechanism. In order to improve performance and memory usage, Angular does a two-phase processing of a directive:
- Compilation – for each directive definition Angular compiles its template and returns a function that will provide its template – a linking function
- Linking – binds the created by the compiler DOM element with a scope
One would typically deal with the linking phase by default. The
1 |
link() |
function of the directive definition provides access to both the DOM element and the scope bound to it. Let’s take a look at a similar example of a parent – child dependence in order to examine the order of execution of the main points of injection (JSFiddle).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
myApp.directive('child', function () { return { scope: { times: '@' }, template: '<div>{{times}}</div>', ... }; }); myApp.directive('parent', function () { return { template: '<child times="{{times}}"></child>', ... }; }); |
Compilation & linking
- Parent: compile()
- Child: compile()
- Parent: pre()
- Child: pre()
- Child: post()
- Parent: post()
Angular does not really have an update phase, as all updates happen locally – at the bindings. If you change the value of a bound property from the scope, then all individual places on UI will be updated. This is due to the Observer pattern implementation of Angular. The
1 |
pre() |
linking function is executed before the linking and the
1 |
post() |
linking function – after the linking. If you don’t want to override the
1 |
compile() |
function, you could just override the
1 |
link() |
function, which is by design the post linking function. The removal of a directive happens when both its scope and the associated DOM element are destroyed. One can attach to
1 |
scope.$on('$destroy', fn) |
in order to perform custom logic on destroy.
Conclusion
Due to their different nature Angular and React manipulate components in a very different way. One must know the precise lifecycle of a component in order to make decisions for where to place a specific piece of code. I hope that I have managed to give you an idea of how two of the most popular JavaScript frameworks deal with that. If you have any questions, you are more than welcome to ask them in the comments below 🙂