JSX

If you have any experience writing frontend JavaScript applications, you might be familiar with a few template languages. At this moment, you might be wondering where can you use your favorite template language (such as Handlebars) with React. And the answer is that you can't.

React doesn't make any distinction between markup and logic; in a React component, they are effectively the same.

However, what happens when we start crafting more complicated components? How would the form we built in Chapter 3, Testing Frontend Code, translate into a React component?

To just render it without any other logic, it would take a bunch of React.createElement calls, as follows:

var NewInvestment = React.createClass({
  render: function () {
    return React.createElement("form", {className: "new-investment"},
      React.createElement("h1", null, "New investment"),
      React.createElement("label", null,
        "Symbol:",
        React.createElement("input", {type: "text", className: "new-investment-stock-symbol", maxLength: "4"})
      ),
      React.createElement("label", null,
        "Shares:",
        React.createElement("input", {type: "number", className: "new-investment-shares"})
      ),
      React.createElement("label", null,
        "Share price:",
        React.createElement("input", {type: "number", className: "new-investment-share-price"})
      ),
      React.createElement("input", {type: "submit", className: "new-investment-submit", value: "Add"})
    );
  }
});

This is very verbose and hard to read. So, given that a React component is both markup and logic, wouldn't it be better if we could write it as a mixture of HTML and JavaScript? Here's how:

var NewInvestment = React.createClass({
  render: function () {
    return <form className="new-investment">
      <h1>New investment</h1>
      <label>
        Symbol:
        <input type="text" className="new-investment-stock-symbol" maxLength="4" />
      </label>
      <label>
        Shares:
        <input type="number" className="new-investment-shares" />
      </label>
      <label>
        Share price:
        <input type="number" className="new-investment-share-price" />
      </label>
      <input type="submit" className="new-investment-submit" value="Add" />
    </form>;
  }
});

That is JSX, a JavaScript syntax extension that looks like XML and was built to be used with React.

It transforms into JavaScript, so given the latter example, it would compile directly to the plain JavaScript code presented before.

An important feature of the transformation process is that it doesn't change the line numbers; so, line 10 in the JSX will translate into line 10 in the transformed JavaScript file. This helps while debugging the code and doing static code analysis.

For more information about the language, you can check the official specification at http://facebook.github.io/jsx/, but for now, you can just follow the next examples as we dive into the features of this language.

It is important to know that it is not a requirement to use JSX while implementing React components, but it makes the process a lot easier. With that in mind, we are going to keep using it for now.

Using JSX with Jasmine

In order for us to use JSX with our Jasmine runner, there are a few changes we need to make.

First, we need to rename the files with which we want to use the JSX syntax to .jsx. Although this is not a requirement, it allows us to easily identify when a file is using this special syntax.

Next, on the SpecRunner.html file, we need to change the script tags to indicate that these are not regular JavaScript files, as follows:

<script src="src/components/InvestmentListItem.jsx" type="text/jsx"></script>
<script src="spec/components/InvestmentListItemSpec.jsx" type="text/jsx"></script>

Unfortunately, these are not the only changes we need to make. The browser doesn't understand JSX syntax, so we need to load a special transformer that will first transform these files into regular JavaScript.

This transformer comes bundled in the React starter kit, so just load it right after loading React, as follows:

<script src="lib/react-with-addons.js"></script>
<script src="lib/JSXTransformer.js"></script>

With this setup done, we should be able to run the tests, shouldn't we? Unfortunately, there is one more step.

If you try to open the SpecRunner.html file in the browser, you will see that the tests of InvestmentListItem are not being executed. That is because the transformer works by loading the script files through AJAX, transforming them and finally attaching them to the DOM. By the time this process is complete, Jasmine has already run the tests.

We need a way to inform Jasmine to wait for these files to load and be transformed.

The simplest way to do that is to change Jasmine's boot.js file placed in the jasmine-2.1.3 folder, inside the lib folder.

In the original file, you are going to need to find the line that contains the env.execute(); method and comment it out. It should be something like the following code:

window.onload = function() {
  if (currentWindowOnload) {
    currentWindowOnload();
  }
  htmlReporter.initialize();

  
// delays execution so that JSX files can be loaded
  // env.execute();
};

Everything else in the file should remain the same. After this change, you will see that the tests are no longer running—none of them.

The only missing piece is invoking this execute method once the JSX files are loaded. To do so, we are going to create a new file called boot-exec.js inside the jasmine.2.1.3 folder with the following content:

/**
  Custom boot file that actually runs the tests.
  The code below was extracted and commented out from the original boot.js file.
 */
(function() {

  var env = jasmine.getEnv();
  env.execute();

}());

As you see, it is only executing the previously commented code from the original boot file.

To run this custom boot is very simple. We add it to the last line of the <head> tag of SpecRunner.html as a JSX type:

  
<!-- After all JSX files were loaded and processed, the tests can finally run -->
  <script src="lib/jasmine-2.1.3/boot-exec.js" type="text/jsx"></script>

</head>

The JSXTransformer library guarantees that the scripts are loaded in the order they are declared. So, by the time the boot-exec.js file is loaded, the source and test files are already loaded.

With that, our test runner now supports JSX.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset