Web Front-end10 minute read

React.js View State Management Tutorial

One of the biggest and most common problems in front-end web development is state management. A developer is constantly focused on keeping the state object in sync with its view and the DOM representation. Users can interact with the application in many ways and it’s a big task to provide a clean transition from one view state to another.

We will see how using React JavaScript library can help us reduce application complexity and offload UI transitions from our application.


Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

One of the biggest and most common problems in front-end web development is state management. A developer is constantly focused on keeping the state object in sync with its view and the DOM representation. Users can interact with the application in many ways and it’s a big task to provide a clean transition from one view state to another.

We will see how using React JavaScript library can help us reduce application complexity and offload UI transitions from our application.


Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.
Leonardo Andrés Garcia Crespo
Verified Expert in Engineering

Leonardo is a life-long tech lover, he is always trying to learn something new while keeping up to date with favorite technologies.

Read More

PREVIOUSLY AT

Tata Consultancy Services
Share

One of the biggest and most common problems in front-end web development is state management. Freelance front-end developers like me are constantly focused on keeping the state object in sync with its view and the DOM representation. Users can interact with the application in many ways and it’s a big task to provide a clean transition from one state to another.

With this React.js tutorial, learn more about view state management.

A Bit Of History

Not that long ago, web applications had a much simpler data flow. The browser would send a request to the server, all application logic would be executed on the server, and a full HTML view would be sent back to the browser for presentation to the user. Subsequent user actions (such as clicks, form submissions, etc.) would trigger the same flow again. Applications did not have to worry about the user state and each view could be regenerated by sending a new request to the server.

However, web applications grew in their complexity and user demands of the UI/UX were advancing as well. Reloading the whole page, when just one part of it changes, was ineffective and slow. We needed a quick, snappy, and responsive interaction with immediate impact on the UI.

JavaScript came to the rescue. Developers started writing significant amounts of code that was executed in the browser before a request was sent to the server. jQuery also brought significant advances to front-end web development, as it provided simple and effective out-of-the-box capabilities such as client side validation, modal windows, alert messages, animations, and even Ajax-based partial page updates.

Understanding Complexity

Let’s take a look at a simple example of evaluating the strength of a password. If the password is OK, the input box should have a green border and should display a nice message. If the password is weak, the input box should have a red border and should display a warning message. We might also show a smiley face when a password is sufficiently strong.

The following code demonstrates how this could be done by DOM manipulation. There are a lot of “ifs” here, and the code is not very easy to read.

if (hasInputBorder()) {
  removeInputBorder();
}
if (text.length === 0) {
  if (hasMessage()) {
    removeMessage();
  }
  if (hasSmiley()) {
    removeSmiley();
  }
}
else {
  var strength = getPasswordStrength(text);
  if (!hasInputBorder()) {
    addInputBorder();
  }
  var color = (strength == 'weak' ? 'red' : 'green');
  setInputBorderColor(color);
  var message = (strength == 'weak' ?
                 "Password is weak" :
                 "That's what I call a password!");
  if (hasMessage()) {
    setMessageText(message);
  } else {
    addMessageWithText(message);
  }
  if (strength == 'weak') {
    if (hasSmiley()) {
      removeSmiley();
    }
  } else {
    if (!hasSmiley()) {
      addSmiley();
    }
  }
}

As shown above, first we need to check if the user provided any password at all and handle the case where the password field is empty. And in all cases, we need to make sure that all related DOM elements are properly updated. This includes the message, the border, and the smiley face.

Our password field can be in one of three states: empty, weak, or strong. And as noted, we have three different DOM elements that are affected by the password field state. Handling all combinations, and making sure that our view is properly displayed, increases cyclomatic complexity for even a simple piece of code like this.

The DOM works in a retained mode, which means that it only remembers the current state. In order to modify our view, we need to provide instructions for each DOM element and program the transition.

Coding transitions instead of states can be complex. The number of branches and checks we need to perform in our code grows exponentially with the number of view states to manage.

In our example, we defined three view states, which gave us 3 * 2 = 6 transitions. In general, given N states, we have N * (N - 1) = N^2 - N transitions that we would need to model. Just think about the increased complexity if we added a fourth state to our example.

There is typically too much code related to modeling the transitions. It would be much better if we could just define our view states and not worry about all the details of transitioning from one state to another.

Reducing Complexity

Assuming that we could declare the view state based on the model state, instead of explicitly coding the transition from one state to another, we could have something like this:

var strength = getPasswordStrength(text);
if (text.length == 0) {
  return div(input({type: 'password', value: text}));
} else if (strength == 'weak') {
  return div(
    input({type: 'password', value: text, borderColor: 'red'}),
    span({}, "Weak")
  );
} else {
  return div(
    input({type: 'password', value: text, borderColor: 'green'}),
    span({}, "That's what I call a password!"),
    img({class: 'icon-smiley'})
  );
}

Here we have three simple branches of code, representing the three possible states of our app. We just return the specification of the view in each branch, depending on the model state. All DOM manipulation code is removed; we just provide the information about what we want, and not how to get there.

While this approach does significantly reduce the code complexity, it also assumes there is someone or something else to take care of the actual DOM manipulation on our behalf.

This is where React comes in. React will make sure that a view state is immediately managed and updated based on the state of the underlying data model.

Reaction

React is a JavaScript library created by Facebook. It is designed to handle the UI part of web applications. You can think of it as the V in MVC architecture. It is very focused. It makes no assumptions about the rest of your technology stack and it doesn’t handle anything other than rendering components. It does not provide any routing mechanisms, models, or other features that are usually bundled in larger frameworks. Thus, you can mix it and use it with any other library or framework you want.

React allows us to define UIs as trees of composite components. A React developer defines those components by specifying a render function that describes the component, given the input state. That function should be pure (i.e., it shouldn’t have any side effect or depend on anything other than its explicit input).

The render function returns a view description, which React calls a Virtual DOM. Think of it as a JavaScript object corresponding to the rendered DOM element.

When you change the component’s state, it just re-renders itself and all of its child elements, returning a new Virtual DOM.

Moreover, React will not do a simple HTML replacement, when transitioning from one state to another. It will find the diff between the previous and the new state, and calculate the most effective set of DOM operations to execute a transition.

Even without performance in mind, the reduction in code complexity is itself significant and allows us to focus our efforts on the more unique and complex parts of our application.

To get a little bit more concrete, this is how our tutorial example would be made using React to manage view states.

NOTE: The following code sample is written in JSX preprocessor, which is a common way to write a React based UI.

function getPasswordStrength(text) {
    // Some code that calculates the strength given the password text.
}

var PasswordWithStrength = React.createClass({
    getInitialState: function() {
        return {value: ''};
    },

    render: function() {
        var strength = getPasswordStrength(this.state.value);
        if (this.state.value.length == 0) {
            return <div>
                <input type="password" value={this.state.value} onChange={this.handleInputChange} />
            </div>;
        } else if (strength == 'weak') {
            return <div>
                <input type="password" value={this.state.value} onChange={this.handleInputChange} style={ {border: '1px solid red'} } />
                <span style={{color: 'red'}}>Weak!</span>
            </div>;
        } else {
            return <div>
                <input type="password" value={this.state.value} onChange={this.handleInputChange} style={ {border: '1px solid green'} } />
                <span style={{color: 'green'}}>That's what I call a password!</span>
                <Emoji value="smiley" />
            </div>;
        }
    },

    handleInputChange: function(ev) {
        this.setState({value: ev.target.value});
    }
});

React.render(<PasswordWithStrength />, document.body);

The Emoji component that is rendered when the password strength is OK with <Emoji value="smiley" /> is just another custom component (just like PasswordWithStrength). It is defined like this:

var Emoji = React.createClass({
    render: function() {
        var emojiSrc = this.props.value + '.png';
        return <img src={emojiSrc}></img>;
    }
});

React.js vs. Others

In fairness, though, there are other client-side JavaScript frameworks (such as Ember, Angular, Knockout, and others) that solved the view state management problem as well, and even added more features to it. So, why would you want to use React instead of any other framework?

There are two key advantages React has, compared to most of the other libraries.

No Data Binding

Some of the other alternative frameworks use data binding to map DOM elements to state properties, and keep them in sync by observing the property changes. This approach allows rendering the view once, with each change then triggering only modifications of the affected DOM elements. Other alternatives use dirty checking; that is, instead of observing individual property changes, they just perform a diff between the previous state and the new one. React is more similar to the latter approach but, instead of comparing states, it compares the view representations.

React has no data binding. A developer is expected to call the setState method, or re-render the top component, when the state is changed. It embraces a one-directional flow, from the state to the view.

This concept is easy to adopt as developers generally do not think about data binding. The focus is on the visual representation of the data. You therefore do not need to think about dependent properties, formatting, binding special HTML tags, etc. With React, you simply re-render the component when the model changes.

To understand the difference in view state management here, let’s compare Ember and React. We will create an object person that will output the full name in uppercase. After two seconds, we will simulate the change and update the view.

// EXAMPLE USING EMBER

App = Ember.Application.create();

App.Person = Ember.Object.extend({
  firstName: null,
  lastName: null,

  fullName: function() {
    return this.get('firstName') + ' ' + this.get('lastName');
  }.property('firstName', 'lastName')
});

var person = App.Person.create({
  firstName: "John",
  lastName:  "Doe"
});

Ember.Handlebars.helper('upcase', function(value) {
  return value.toUpperCase();
});

App.IndexRoute = Ember.Route.extend({
  model: function () {
    return person;
  }
});

setTimeout(function() {
  person.set('firstName', 'Harry');
}, 2000);

// Templates:
<script type="text/x-handlebars">
  <h2>Welcome to Ember.js</h2>

  {{outlet}}
</script>

<script type="text/x-handlebars" data-template-name="index">
  The current user is: {{upcase model.fullName}}
</script>

We created an object with firstName, lastName, and fullName properties. Since Ember is observing property changes, we had to specify that fullName depends on the firstName and the lastName. To do this we added .property('firstName', 'lastName') when we defined the fullName.

After two seconds, person.set('firstName', 'Harry'); is executed. This triggered the update of the view and its binding.

Now let’s do the same in React.

// EXAMPLE USING REACT

var CurrentUser = React.createClass({
  render: function() {
    return <div>The current user is: {this.props.user.fullName().toUpperCase()}</div>;
  }
});

var person = {
  firstName: 'John',
  lastName: 'Doe',
  fullName: function() {
    return this.firstName + ' ' + this.lastName;
  }
};

var currentUser = React.render(<CurrentUser user={person}/>, document.body);

setTimeout(function() {
  person.firstName = 'Harry';
  currentUser.setProps({user: person});
}, 2000);

Even though Ember code is simple and easy to read, it is obvious that React wins in simplicity. The person is a plain JavaScript object, with the fullName simply being a function.

No Templating

Each alternative framework has a different way of handling templates. Some of them use strings which are compiled into JavaScript, while others use DOM elements directly. Most of them use custom HTML attributes and tags which are then “compiled” into HTML.

Templates are not part of the JavaScript code. Because of this, each alternative needs a custom way to represent common operations, conditionals, iterations, calling functions, etc. They all end up creating a new pseudo-language that developers must learn.

There is no templating in React, everything is just plain old JavaScript.

React uses the full power of JavaScript in order to generate the view. The component’s render method is a JavaScript function.

JSX is available as a preprocessor that turns “HTML-like syntax” into normal JavaScript, but JSX is optional and you are free to use standard JavaScript without any preprocessors. You can also take advantage of existing JavaScript tools. Linters, preprocessors, type annotations, minification, dead code elimination, etc.

Let’s again use a concrete example to compare React to one of the alternative frameworks for view state management.

The following tutorial is an example of using AngularJS to list hashtags and the tweet count for each of them. The list is sorted by count, and a message is shown if there are no hashtags.

<!-- EXAMPLE USING ANGULAR -->

<div ng-controller="MyCtrl">
  <ul ng-show="hashTags.length > 0">
    <li ng-repeat="hashTag in hashTags | orderBy:'tweetCount':true">
      {{hashTag.name}} - {{hashTag.tweetCount}} tweets
    </li>
  </ul>
  <span ng-show="hashTags.length == 0">No hashtags found!</span>
</div>

To be able to make this list, a developer must learn about AngularJS directives, ng-show and ng-repeat. Then he needs to learn about AngularJS filters to understand orderBy. A lot of work for a simple thing like outputting a list!

Now let’s consider the React example that does the same thing:

// EXAMPLE USING REACT

function byTweetCountDesc(h1, h2) {
  return h2.tweetCount - h1.tweetCount;
}

//...
render: function() {
  if (this.state.hashTags.length > 0) {
    var comps = this.state.hashTags.sort(byTweetCountDesc).map(function(hashTag, index) {
      return <li key={index}>
        {hashTag.name} - {hashTag.tweetCount} tweets
      </li>;
    });
    return <ul>{comps}</ul>;
  } else {
    return <span>No hashtags found!</span>
  }
}

Even if we used the “more advanced” approach and JSX, every web developer with a basic understanding of JavaScript can easily read the code above and understand what it does. Standard conditional check using if, iteration using map(), and a standard ‘sort()’ comes naturally to any developer, so there is no extra syntax or other concepts to learn.

Conclusion

The main takeaway from this React.js tutorial is the fact that React enables you to focus on the actual view state management rather than transitions, thereby simplifying your work and your application.

The learning curve for adopting React is fairly trivial. No custom templating language to master, no data binding to worry about, and everything comes down to JavaScript functions that describe UI elements.

To learn more about simplifying your application code using React, take a look at this talk by Steven Luscher, Decomplexifying Code with React.

Here is some additional reading for anyone who wants to take the next step and start using React:

Further Reading on the Toptal Blog:

Hire a Toptal expert on this topic.
Hire Now
Leonardo Andrés Garcia Crespo

Leonardo Andrés Garcia Crespo

Verified Expert in Engineering

London, United Kingdom

Member since February 10, 2014

About the author

Leonardo is a life-long tech lover, he is always trying to learn something new while keeping up to date with favorite technologies.

Read More
authors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

PREVIOUSLY AT

Tata Consultancy Services

World-class articles, delivered weekly.

Subscription implies consent to our privacy policy

World-class articles, delivered weekly.

Subscription implies consent to our privacy policy

Join the Toptal® community.