As stated in the introduction of this chapter, with React, you declaratively write the interface code through components.
The concept of a React component is analogous to the component concept presented in Chapter 3, Testing Frontend Code, so expect to see some similarities next.
With that in mind, let's create our very first component. To better understand what a React component is, we are going to use a very simple acceptance criterion and as usual start from the spec.
Let's implement "InvestmentListItem should render". It's very simple and not really feature oriented but is a good example to get us started.
With what we learned in Chapter 3, Testing Frontend Code, we could start coding this spec by creating a new file called InvestmentListItemSpec.js
and save it in the components
folder inside the spec
folder:
describe("InvestmentListItem", function() { beforeEach(function() { // render the React component }); it("should render", function() { expect(component.$el).toEqual('li.investment-list-item'), }); });
Add the new file to the SpecRunner.html
file, as already demonstrated in previous chapters.
At the spec, we are basically using the jasmine-jquery
plugin to expect that the encapsulated DOM element of our component is equal to a specific CSS selector.
How would we change this example to be a test of a React component? The only difference is the API to get the DOM node. Instead of $element
with a jQuery object, React exposes a function called getDOMNode()
that returns what it states—a DOM node.
With that, we can use the same assertion as before and have our test ready, as follows:
it("should render", function() {
expect(component.getDOMNode()).toEqual('li.investment-list-item'),
});
That was easy! So, the next step is to create the component, render it, and attach it to the document. That is simple as well; take a look at the following gist:
describe("InvestmentListItem", function() { var component; beforeEach(function() { setFixtures('<div id="application-container"></div>'), var container = document.getElementById('application-container'), var element = React.createElement(InvestmentListItem); component = React.render(element, container); }); it("should render", function() { expect(component.getDOMNode()).toEqual('li.investment-list-item'), }); });
It might seem like a lot of code, but half of it is just boilerplate to set up a document element fixture that we can render the React component in:
setFixtures
function from jasmine-jquery
to create an element in the document with the application-container
ID. Then, using the getElementById
API, we query for this element and save it in the container
variable. The next two steps are the ones specific to React:React.createElement
function, as follows:var element = React.createElement(InvestmentListItem);
React.render
function, as follows:component = React.render(element, container);
render
function accepts the following two parameters:ReferenceError: InvestmentListItem is not defined
.
src
folder, name it InvestmentListItem.js
, and add it to the spec runner. This file should follow the module pattern we've been using until now.React.createClass
method:(function (React) { var InvestmentListItem = React.createClass({ render: function () { return React.createElement('li', { className: 'investment-list-item' }, 'Investment'), } }); this.InvestmentListItem = InvestmentListItem; })(React);
React.createClass
method expects a single render
function that should return a tree of React elements.React.createElement
method to create the element that is going to be the root of the rendering tree, as follows:React.createElement('li', { className: 'investment-list-item' }, 'Investment')
The difference from its previous usage in the beforeEach
block is that here, it is also passing a list of
props (with className
) and a single child containing the text Investment
.
We will get deeper into the meaning of the props parameter, but you can think of it as analogous to the attributes of an HTML DOM element. The className
prop will turn into the class HTML attribute of the li
element.
The React.createElement
method signature accepts three arguments:
div
, h1
, p
) or a React component classInvestment
stringOn rendering this component (by invoking the React.render()
method), the result will be:
<li class="investment-list-item">Investment</li>
This is a direct representation of the JavaScript code that generated it:
React.createElement('li', { className: 'investment-list-item' }, 'Investment'),
Congratulations! You've built your first fully tested React component.