In the previous post I started a short series of posts about how React and Angular handle components. In this series I do not want to draw any conclusions which of these two is better, bur rather to show you the pros and cons of each (in terms of code, flexibility, features, etc.) in different scenarios. I already covered how one can define components in both React and Angular, how these are rendered and how to configure them by means of properties. In this post I will show how these two handle changes in the model and how they react on these changes.
Compare and contrast
Let’s start with an overview of the process of rendering and re-rendering a component and then see how to handle changes from inside a component.
Re-rendering
In the first post we saw how to render a component in both React and Angular. However, once a component is rendered, a change may occur that requires the component to be re-rendered. The following comparison is mainly about the process of rendering a component in React and Angular respectively.
React
A React component can change only if its parent decides to change any of the component’s properties. This is the default behavior and ensures one way of data flow. However, a component can also change from inside – for example, fetching data from a web service and applying it afterwards. If a component requires any internal dynamics, a state can be used. The state is just a JavaScript object (see the next section below). Every component exposes a function
1 |
setState() |
that is used to update its state. This function takes one parameter – an object to merge with the current state, and triggers a re-render. This will subsequently update the component itself and all its children that rely on the state.
Angular
Angular uses watches to monitor the scope. As the scope is the way to bind data from a controller or directive to a view, it contains all potentially variable fields. Every data binding expression in the view generates a new watch. Watches are basically implementation of the Observer pattern – when a change in the scope occurs (it will trigger also a change in the whole data binding expression), the watch triggers the appropriate listener. If you manually create a watch, then you would provide a callback to be called on changes. The data binding mechanism of Angular automatically creates such a callback for all bindings in the view and when a change is triggered, the view is repainted.
State management
Almost every component needs to maintain some kind of state in order to function properly. As components are reusable units, we want this state to be internal to the component, i.e. no other component that uses our component could know what happens inside it. The only way to work with such a component is via its public interface (f.x., the properties one can tweak).
React
React has two types of state – props and state. The convention is that the former defines the public interface (i.e. what properties our component exposes), while the latter is exclusively used for internal purposes. We can actually say that if our component does not use state, then it is more or less read-only – it relies only on its public interface. The state means that the component can possible change internally. Let’s take a look at the following example (JSFiddle).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
var ColorBox = React.createClass({ getInitialState: function() { return { color: 'red' }; }, render: function() { return ( <div> <h1 style={{color: this.state.color}}>I am {this.state.color}</h1> <select ref="color" onChange={this.handleColorChange}> <option>red</option> <option>green</option> <option>blue</option> </select> </div> ); }, handleColorChange: function() { this.setState({ color: this.refs.color.getDOMNode().value }); } }); |
This will render a select box with 3 colors and a heading, where the heading changes its color based on the selected one from the select box. The first thing to notice is the
1 |
getInitialState() |
function. It returns a JavaScript object that contains the initial state of the component. We can afterwards access this state by calling
1 |
this.state.something |
. As mentioned above, when we change the state by calling
1 |
setState() |
, we make the component re-render itself.
Angular
Angular has only one way of dealing with state – the scope. Every directive can choose what kind of a scope it needs – a child scope, an isolated scope, or no scope at all. Child scopes are created by prototypical inheritance which means that we could access the parent scope from inside. This is not desired in most of the cases as it increases the coupling between our components and makes it harder to reuse them. The following example (JSFiddle) shows the same component in Angular.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
myApp.directive('colorBox', function () { return { scope: {}, template: '<div>' + '<h1 ng-style="{ color: color }">I am {{color}}</h1>' + '<select ng-model="color">' + '<option>red</option>' + '<option>green</option>' + '<option>blue</option>' + '</select>' + '</div>', link: function (scope) { scope.color = 'red'; } }; }); |
Our directive gets an isolated scope and we can manipulate it in the
1 |
link() |
function. Note that Angular 1.x supports two-way data binding. That is why our scope is automatically updated when changing the selection of the color (via the ng-model directive). In Angular 2 this will be removed, i.e. we will have to manually listen for changes and update our model.
Conclusion
In this second post of my short series of comparison between React and Angular, I showed you how these two handle changes in the state and how the components are re-rendered as a consequence of these changes. In the next post I will try to show you how React and Angular deal with dynamic templates, so stay tuned 🙂