Inside the DOM Abstraction
In the previous chapter, you saw that React abstracts away the DOM, providing a simpler programming model, better performance, and the possibility to render components on the server and even power native mobile apps.
This chapter will cover JSX, the JavaScript language extension used to describe the UI.
Events in React
React implements a synthetic event system that brings consistency and high performance to React applications and interfaces.
It achieves consistency by normalizing events so that they have the same properties across different browsers and platforms.
It achieves high performance by automatically using event delegation. React doesn’t actually attach event handlers to the nodes themselves. Instead, a single event listener is attached to the root of the document; when an event is fired, React maps it to the appropriate component element. React also automatically removes the event listeners when a component unmounts.
HTML has always provided a beautiful and easy-to-understand event handling API for tag attributes: onclick, onfocus, etc. The problem with this API (and the reason why it is not used in professional projects) is that it’s full of undesirable side effects: it pollutes the global scope, it’s hard to track in the context of a big HTML file, it can be slow, and it can lead to memory leaks, just to name a few issues.
JSX makes use of a similarly easy-to-use and understand API but removes the undesired side effects from the HTML counterpart. Callback functions are scoped to the component (which, as you’ve seen, is responsible for just one part of the UI and tends to contain small markup), and it’s smart enough to use event delegation and auto manage unmounting. Notice, however, that there are some minor differences in contrast with the original HTML implementation. In React, the properties are camel cased (“onClick” instead of “onclick”). Built to be consistent across browsers and devices, it implements a subset of all the variations found in different versions of different browsers. Tables 2-1 through 2-4 show the available events.
Table 2-1. Touch and Mouse Events
Table 2-4. Other Events
Kanban App: Managing the DOM Event
In the last iteration of the Kanban app, you added the following inline function (using the fat arrow =>) inside the onClick event handler:
<div className="card__title" onClick={
()=>this.setState({showDetails: !this.state.showDetails})
}>
This is practical but not very flexible. Let’s change this implementation to use a new method called toggleDetails inside the class to handle the event:
class Card extends Component {
constructor() {
super(...arguments);
this.state = {
showDetails: false
};
}
toggleDetails() {
this.setState({showDetails: !this.state.showDetails});
}
render() {
let cardDetails;
if (this.state.showDetails) {
cardDetails = (
<div className="card__details">
{this.props.description}
<CheckList cardId={this.props.id} tasks={this.props.tasks} />
</div>
);
}
return (
<div className="card">
<div className="card__title" onClick={this.toggleDetails.bind(this)}>
{this.props.title}
</div>
{cardDetails}
</div>
)
}
}
Note Earlier React versions (specifically prior to the use of ES6 classes) had a built-in “magic” feature that bound all methods to this automatically. Since this could be confusing for JavaScript developers that are not used to this feature in other classes, it was removed. In the current versions, the developer has to explicitly bind the function to context. This can be done in different ways, the simplest one being to simply use .bind(this) to generate a bound function. Bind and other functional JavaScript methods are discussed in Appendix B.
Digging Deeper in JSX
JSX is React’s optional extension to the JavaScript syntax used for writing declarative XML-style syntax inside JavaScript code.
For web projects, React’s JSX provides a set of XML tags that are similar to HTML, but there are other use cases in which another set of XML tags are used to describe the user interface (such as React with SVG, React Canvas, and React Native).
When transpiled (converted to plain JavaScript, so the browser or server can interpret the code), the XML is transformed into a function call to the React Library.
This
<h1>Hello World</h1>
becomes
React.createElement("h1", null, "Hello World");
The use of JSX is optional. However, embracing it has the following benefits:
JSX vs. HTML
For web usage, JSX looks like HTML, but it’s not an exact implementation of the HTML specification. React’s creators went so far to make JSX similar enough to HTML so it could be used to describe web interfaces properly, but without losing sight of the fact that it should also conform to JavaScript style and syntax.
Differences Between JSX and HTML
There are three important aspects you should be aware of when writing HTML syntax with JSX:
Let’s review them now.
Tag Attributes Are Camel Cased
For example, in HTML, the input tag can have an optional maxlength attribute:
<input type="text" maxlength="30" />
In JSX, the attribute is written as maxLength (note the uppercase “L”):
return <input type="text" maxLength="30" />
All Elements Must be Balanced
Since JSX is XML, all elements must be balanced. Tags such as <br> and <img>, which don’t have ending tags, need to be self-closed. So, instead of <br>, use <br/> and instead of <img src="...">, use <img src="..." />.
Attribute Names are Based on the DOM API
This can be confusing, but it is actually very easy. When interacting with the DOM API, tag attributes may have different names than those you use in HTML. One of such example is class and className.
For example, given this regular HTML
<div id="box" class="some-class"></div>
if you want to manipulate the DOM and change its class name using plain JavaScript, you would do something like
document.getElementById("box").className="some-other-class"
As far as JavaScript is concerned, that attribute is called className, not class. Since JSX is just a syntax extension to JavaScript, it conforms to the attribute names as defined in the DOM. That same div should be expressed in JSX as
return <div id="box" className="some-class"></div>
JSX Quirks
JSX can be tricky sometimes. This section groups small techniques, tips, and strategies to deal with common problems you may face when building components with JSX.
Single Root Node
React components can only render a single root node. To understand the reasons for this limitation, let’s look at this sample return from a render function:
return(
<h1>Hello World</h1>
)
It is transformed into a single statement:
return React.createElement("h1", null, "Hello World");
On the other hand, the following code isn’t valid:
return (
<h1>Hello World</h1>
<h2>Have a nice day</h2>
)
To be clear, this is not a JSX limitation, but rather a JavaScript characteristic: a return statement can only return a single value, and in the previous code we were trying to return two statements (two calls to React.createElement). The alternative is very simple: as you would do in plain JavaScript, wrap all return values in a root object.
return (
<div>
<h1>Hello World</h1>
<h2>Have a nice day</h2>
</div>
)
This works perfectly because it would be transformed into
return React.createElement("div", null,
React.createElement("h1", null, "Hello World"),
React.createElement("h2", null, " Have a nice day"),
)
thus returning a single value, and done via valid JavaScript.
If statements doesn’t fit well in JSX, but what may be seen as a JSX limitation is actually a consequence of the fact that JSX is just plain JavaScript. To better explain, let’s start by reviewing how JSX gets transformed into plain JavaScript.
JSX like
return (
<div className="salutation">Hello JSX</div>
)
gets transformed into JavaScript like
return (
React.createElement("div", {className: "salutation"}, "Hello JSX");
)
However, if you try to write an if clause in the middle of the JSX, like
<div className={if (condition) { "salutation" }}>Hello JSX</div>
it would be transformed into an invalid JavaScript expression, as shown here and in Figure 2-1:
React.createElement("div", {className: if (condition) { "salutation"}}, "Hello JSX");
Figure 2-1. Syntax error when trying to use an if expression inside JSX
What Are the Alternatives?
Although not being possible to use an “if” statement inside JSX, there are alternatives to render content conditionally, including using ternary expressions and assigning conditionally to a variable (null and undefined values are treated by React and outputs nothing when escaped in JSX).
If you have a very simple expression, you can use the ternary form:
render() {
return (
<div className={condition ? "salutation" : “”}>
Hello JSX
</div>
)
}
This will be transformed into a valid JS:
React.createElement("div", {className: condition ? "salutation" : “”}, "Hello JSX");
The ternary form also works for conditionally rendering entire nodes:
<div>
{condition ?
<span>Hello JSX</span>
: null}
</div>
Move the Condition Out
If a ternary expression isn’t robust enough for your case, the alternative is to not use conditionals in the middle of JSX. Simply move the conditional’s clauses outside (as you did in Chapter 2 for hiding or showing the ContactItem details).
Instead of
render() {
return (
<div className={if (condition) { "salutation" }}>
Hello JSX
</div>
)
}
move the conditional outside of JSX, like
render() {
let className;
if(condition){
className = "salutation";
}
return (
<div className={className}>Hello JSX</div>
)
}
React knows how to handle undefined values and won’t even create a class attribute in the div tag if the condition is false.
Kanban App: Indicating Whether a Card Is Open or Closed
In the first chapter, you used this technique of moving the condition out for toggling the Card details. Let’s also use the ternary form to add a className conditionally to the Card Title (some of the original code is omitted for brevity). The results are shown in Figure 2-2.
class Card extends Component {
constructor() { ... }
toggleDetails() { ... }
render() {
let cardDetails;
if (this.state.showDetails) {
cardDetails = (
<div className="card__details">
{this.props.description}
<CheckList cardId={this.props.id} tasks={this.props.tasks} />
</div>
);
}
return (
<div className="card">
<div className={
this.state.showDetails? "card__title card__title--is-open" : "card__title"
} onClick={this.toggleDetails.bind(this)}>
{this.props.title}
</div>
{cardDetails}
</div>
)
}
}
Figure 2-2. Conditional class
Blank Space
This is a very small and quick tip: in HTML, browsers usually render a space between elements in multiple lines. React’s JSX will only render a space if you specifically tell it to do so. For example, the following JSX will render as shown in Figure 2-3.
return (
<a href="http://google.com">Google</a >
<a href=“http://facebook.com">Facebook</a>
)
Figure 2-3. JSX doesn’t produce a space between lines
To explicitly insert a space, you can use an expression with an empty string {" "}:
return(
<a href="http://google.com">Google</a>{" "}
<a href="http://facebook.com">Facebook</a>
)
This renders the desired output, as shown in Figure 2-4.
Figure 2-4. Using an expression to render a space
Comments in JSX
Another quirk derived from the fact that JSX isn’t HTML is the lack of support for HTML comments (e.g. <!-- comment -->). Although traditional HTML tag comments are not supported, since JSX is made of JavaScript expressions, it’s possible to use regular JS comments. You just need to be careful to put {} around the comments when you are within the child section of a tag.
let content = (
<Nav>
{/* child comment, put {} around */}
<Person
/* multi
line
comment */
name={window.isLoggedIn ? window.name : ’’} // end of line comment
/>
</Nav>
);
Rendering Dynamic HTML
React has built-in XSS attack protection, which means that by default it won’t allow HTML tags to be generated dynamically and attached to JSX. This is generally good, but in some specific cases you might want to generate HTML on the fly. One example would be rendering data in markdown format to the interface.
Note Markdown is a format that allows you to write using an easy-to-read, easy-to-write plain text format. For example, surrounding text with double asterisks will make it strong (bold).
React provides the dangerouslySetInnerHTML property to skip XSS protection and render anything directly.
Kanban App: Rendering Markdown
Let’s see this in action by enabling markdown on the Kanban app Card’s description. You will start by changing the card descriptions on your data model to include some markdown formatting.
let cardsList = [
{
id:1,
title: "Read the Book",
description: "I should read the **whole** book",
status: "in-progress",
tasks: []
},
{
id:2,
title: "Write some code",
description: "Code along with the samples in the book. The complete source can be found
at [github](https://github.com/pro-react)",
status: "todo",
tasks: [
{id: 1, name:"ContactList Example", done:true},
{id: 2, name:"Kanban Example", done:false},
{id: 3, name:"My own experiments", done:false}
]
},
];
You will need a JavaScript library to convert the markdown used in the card descriptions to HTML. There are many open source libraries available. In this example, you’re going to use one called marked (https://github.com/chjj/marked).
If you’re following along with this book’s examples and using a module system, import the library on your package.json and install it (both can be done with the single command npm install --save marked). Don’t forget to import the marked module on the beginning of your file.
Using the marked module, your code will look like this:
import React, { Component } from ’react’;
import CheckList from ’./CheckList’;
import marked from ’marked’;
Then, you’re going to use the function marked() provided by the library to convert the markdown to HTML (I have omitted some code not pertinent to this example for brevity):
class Card extends Component {
constructor() {...}
toggleDetails() {...}
render() {
let cardDetails;
if (this.state.showDetails) {
cardDetails = (
<div className="card__details">
{marked(this.props.description)}
<CheckList cardId={this.props.id} tasks={this.props.tasks} />
</div>
);
}
return (
<div className="card">
<div className={
this.state.showDetails? "card__title card__title--is-open" : "card__title"
} onClick={this.toggleDetails.bind(this)>
{this.props.title}
</div>
{cardDetails}
</div>
)
}
}
But as expected, React by default won’t allow any HTML tags to be rendered inside your JSX so the output will look like Figure 2-5.
Figure 2-5. React escaping HTML by default
Using dangerouslySetInnerHTML, you can achieve the desired final result, as shown here and in Figure 2-6:
cardDetails = (
<div className="card__details">
<span dangerouslySetInnerHTML={{__html:marked(this.props.description)}} />
<CheckList cardId={this.props.id} tasks={this.props.tasks} />
</div>
);
Figure 2-6. React rendering dynamically generated HTML with dangerouslySetInnerHTML
React Without JSX
JSX brings a concise and familiar syntax for describing UIs as tree structures. It does so by enabling the use of XML inside JavaScript code without altering the semantics of JavaScript. React was designed with JSX in mind; however, it’s absolutely possible to use React without JSX. Although you’ll continue using JSX for all the examples in this book, this section will briefly explore how to work with React without JSX.
React Elements in Plain JavaScript
You can create React elements in plain JavaScript using React.createElement, which takes a tag name or component, a properties object, and variable number of optional child arguments.
let child1 = React.createElement(’li’, null, ’First Text Content’);
let child2 = React.createElement(’li’, null, ’Second Text Content’);
let root = React.createElement(’ul’, { className: ’my-list’ }, child1, child2);
React.render(root, document.getElementById(’example’));
For convenience, React provides short-hand factory functions under React.DOM for common HTML tags. Let’s put this together with a more advanced example:
React.DOM.form({className:"commentForm"},
React.DOM.input({type:"text", placeholder:"Name"}),
React.DOM.input({type:"text", placeholder:"Comment"}),
React.DOM.input({type:"submit", value:"Post"})
)
The above is equivalent to the following JSX:
<form className="commentForm">
<input type="text" placeholder="Name" />
<input type="text" placeholder="Comment" />
<input type="submit" value="Post" />
</form>
Using destructuring assignment, it’s possible to tidy things up for a more concise syntax:
import React, { Component } from ’react’;
import {render} from ’react-dom’;
let {
form,
input
} = React.DOM;
class CommentForm extends Component {
render(){
return form({className:"commentForm"},
input({type:"text", placeholder:"Name"}),
input({type:"text", placeholder:"Comment"}),
input({type:"submit", value:"Post"})
)
}
}
It’s also possible to create factories for custom components, like so:
let Factory = React.createFactory(ComponentClass);
...
let root = Factory({ custom: ’prop’ });
render(root, document.getElementById(’example’));
By authoring React components using JSX, you’re combining UI definition (content markup) and interaction (JavaScript) in the same file. As discussed, the separation of concerns in this scenario comes from discrete, well-encapsulated, and reusable components for each concern. But there’s another important factor to user interfaces besides content and interaction: styling.
React provides the capacity to write inline styles using JavaScript. At first, the idea to write styles in JavaScript can seem a little strange, but it can provide some benefits over traditional CSS:
Note that JavaScript is highly expressive and so by using it you automatically gain variables, functions, and full range of control flow constructs.
Defining Inline Styles
In React’s components, inline styles are specified as a JavaScript object. Style names are camel cased in order to be consistent with DOM properties (e.g. node.style.backgroundImage). Additionally, it’s not necessary to specify pixel units - React automatically appends the correct unit behind the scenes. The following example shows an example of inline styling in React:
import React, { Component } from ’react’;
import {render} from ’react-dom’;
class Hello extends Component {
render() {
let divStyle = {
width: 100,
height: 30,
padding: 5,
backgroundColor: ’#ee9900’
};
return <div style={divStyle}>Hello World</div>
}
}
Kanban App: Card Color via Inline Styling
While it’s possible to completely ditch CSS in favor of inline styling using JavaScript, generally an hybrid approach is more appropriate, where CSS (or CSS preprocessors such as Sass or Less) is used for major style definitions and inline styling inside React components is used for dynamic, state-based appearance.
In the next steps, you’re going to add custom color to mark a card.
let cardsList = [
{
id:1,
title: "Read the Book",
description: "I should read the book",
color: ’#BD8D31’,
status: "in-progress",
tasks: []
},
{
id:2,
title: "Write some code",
description: "Code along with the samples ... at [github](https://github.com/pro-react)",
color: ’#3A7E28’,
status: "todo",
tasks: [
{id: 1, name:"ContactList Example", done:true},
{id: 2, name:"Kanban Example", done:false},
{id: 3, name:"My own experiments", done:false}
]
},
];
class List extends Component {
render() {
let cards = this.props.cards.map((card) => {
return <Card id={card.id}
title={card.title}
description={card.description}
color={card.color}
tasks={card.tasks} />
});
return (
...
)
}
}
class Card extends Component {
constructor() {...}
toggleDetails() {...}
render() {
let cardDetails;
if (this.state.showDetails) {...}
let sideColor = {
position: ’absolute’,
zIndex: -1,
top: 0,
bottom: 0,
left: 0,
width: 7,
backgroundColor: this.props.color
};
return (
<div className="card">
<div style={sideColor}/>
<div className={
this.state.showDetails? "card__title card__title--is-open" : "card__title"
} onClick={this.toggleDetails.bind(this)}>
{this.props.title}
</div>
{cardDetails}
</div>
)
}
}
Figure 2-7 shows the rendered result.
Figure 2-7. Inline styles for dynamic card colors
In React, a component’s internal state is kept to minimum because every time the state changes, the component is rendered again. The purpose of this is to have an accurate representation of the component state in your JavaScript code and let React keep the interface in sync.
For this reason, form components such as <input>, <textarea>, and <option> differ from their HTML counterparts because they can be mutated via user interactions.
React provides two ways of handling forms as components and lets you choose based on your app characteristics or personal preference. The two available ways to handle a form field are as a controlled component or an uncontrolled component.
Controlled Components
A form component with a value or checked prop is called a controlled component. In a controlled component, the value rendered inside the element will always reflect the value of the prop. By default the user won’t be able to change it.
That’s the case for your Kanban cards checklist. If you try clicking one of the task’s checkboxes, it won’t change. They are reflecting the value hardcoded in your cardsList array and will only change if you change the array itself.
Before heading back to the Kanban project, though, let’s see another example. Start with a Search component that contains an input field:
import React, { Component } from ’react’;
import {render} from ’react-dom’;
class Search extends Component {
render() {
return (
<div>
Search Term: <input type="search" value="React" />
</div>
)
}
}
render(<Search />, document.body);
This will render a form field displaying an immutable value of “React.” Any user input will have no effect on the rendered element because React has declared the value to be ”React,” as shown in Figure 2-8.
Figure 2-8. The form element
To be able to make this value change, you need to handle it as a component state. This way, any changes to the state value will be reflected in the interface.
class Search extends Component {
constructor() {
super();
this.state = {
searchTerm: "React"
};
}
render() {
return (
<div>
Search Term:
<input type="search" value={this.state.searchTerm} />
</div>
)
}
}
You could even give the end user the ability to update the state value using the onChange event.
class Search extends Component {
constructor() {
super();
this.state = {
searchTerm: "React"
};
}
handleChange(event) {
this.setState({searchTerm: event.target.value});
}
render() {
return (
<div>
Search Term:
<input type="search" value={this.state.searchTerm}
onChange={this.handleChange.bind(this)} />
</div>
)
}
}
This may look like a convoluted way to deal with forms, but it has the following advantages:
this.setState({searchTerm: event.target.value.substr(0, 50)});
Special Cases
There are a few special cases to remember when creating controlled form components: TextArea and Select.
In HTML, the value of <textarea> is usually set using its children:
<textarea>This is the description.</textarea>
For HTML, this easily allows developers to supply multiline values. However, since React is JavaScript, you do not have string limitations (you use if you want newlines, for example). To keep consistent across other form elements, React uses the value prop to set <textarea> values:
<textarea value="This is a description." />
In HTML, you set the selected option using the “selected” attribute on the option tag. In React, in order to make components easier to manipulate, the following format is adopted instead:
<select value="B">
<option value="A">Mobile</option>
<option value="B">Work</option>
<option value="C">Home</option>
</select>
Uncontrolled Components
Controlled components adhere to React’s principles and have their advantages. While uncontrolled components are an anti-pattern for how most other components are constructed in React, sometimes you don’t need to oversee the user input field by field.
This is especially true in longer forms, where you want to let the user fill in the fields and then process everything when the user is done.
Any input that does not supply a value is an uncontrolled component, and the value of the rendered element will reflect the user’s input. For example,
return (
<form>
<div className="formGroup">
Name: <input name="name" type="text" />
</div>
<div className="formGroup">
E-mail: <input name="email" type="mail" />
</div>
<button type="submit">Submit</button>
</form>
)
will render two input fields that start off with an empty value. Any user input will be immediately reflected by the rendered elements.
Tip If you want to set up an initial value for an uncontrolled form component, use the defaultValue prop instead of value.
It’s still possible to handle uncontrolled component forms using onSubmit, like so:
handleSubmit(event) {
console.log("Submitted values are: ",
event.target.name.value,
event.target.email.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="formGroup">
Name: <input name="name" type="text" />
</div>
<div className="formGroup">
E-mail: <input name="email" type="mail" />
</div>
<button type="submit">Submit</button>
</form>
)
}
Kanban App: Creating a Task Form
Regarding your Kanban app, you already have controlled components: the tasks checkboxes. Let’s add an uncontrolled component this time: a text field to include new tasks.
class CheckList extends Component {
render(){
let tasks = this.props.tasks.map((task) => (...);
return (
<div className="checklist">
<ul>{tasks}</ul>
<input type="text"
className="checklist--add-task"
placeholder="Type then hit Enter to add a task" />
</div>
)
}
}
Since you didn’t specify a value prop, the user can freely write inside the text field. In the next chapter, you will wire the form fields in the checklist to add and mark tasks as done.
To finish, let’s add some CSS to style the form element:
.checklist--add-task {
border: 1px dashed #bbb;
width: 100%;
padding: 10px;
margin-top: 5px;
border-radius: 3px;
}
Virtual DOM Under the Hood
As you’ve seen so far, one of React’s key design decisions is to make the API seem like it re-renders the whole app on every update. DOM manipulation is a slow task for a variety of reasons, so in order to make this possible in a performant way, React implements a virtual DOM. Instead of updating the actual DOM every time the application state changes, React simply creates virtual tree that looks like the DOM state that you want. Then it figures out how to make the DOM look like this efficiently without recreating all of the DOM nodes.
This process of finding the minimum number of changes that must be made in order to make the virtual DOM tree and the actual DOM tree identical is called reconciliation, and in general it is a very complex and extremely expensive operation. Even after many iterations and optimizations, this remains a very difficult and time-consuming problem. To make this tractable, React makes a few assumptions about how typical applications work that allow for a much faster algorithm in practical use cases. Some assumptions include:
The same logic is used for custom components. If they are not of the same type, React is not going to even try to match what they render. It is just going to remove the first one from the DOM and insert the second one.
Keys
Although React’s Virtual DOM and differing algorithms are very smart, in order to be fast, React makes some assumptions and takes a naive approach in some cases. Lists of repeating items are especially tricky to handle. To understand why, let’s start with an example. Listings 2-1 and 2-2 represent a previous and current render.
The difference between the two lists seems pretty obvious, but which is the best approach to transform one list into the other? Adding a new item (Apple) to the beginning of the list and deleting the last one (Banana) is a possible operation, but changing the last item’s name and position is also a possibility. In bigger lists, different possibilities arise and each of them can possibly cause side effects. Considering that nodes can be inserted, deleted, substituted, and moved, it is pretty hard to determine best approaches for all possible cases with an algorithm.
For this reason, React introduced the key attribute. Keys are unique identifiers that allow for fast lookups between trees for finding insertions, deletions, substitutions, and moves. Every time you create components in a loop, it’s a good idea to provide a key for each child in order to help the React Library match and avoid performance bottlenecks.
Kanban App: Keys
Your previous Kanban app example is already warning about child elements without keys in the browser console (see Figure 2-9).
Figure 2-9. A React warning about missing key props on the List and Checklist components
The key prop can contain any value that is unique and constant. Your card’s data contains an ID for each card. Let’s use it as the key prop in the List component:
class List extends Component {
render() {
let cards = this.props.cards.map((card) => {
return <Card key={card.id}
id={card.id}
title={card.title}
description={card.description}
color={card.color}
tasks={card.tasks} />
});
return (
<div className="list">
<h1>{this.props.title}</h1>
{cards}
</div>
)
}
}
You also have an array in Checklist. Let’s add a key there, too:
class CheckList extends Component {
render(){
let tasks = this.props.tasks.map((task) => (
<li key={task.id} className="checklist__task">
<input type="checkbox" defaultChecked={task.done} />
{task.name}{’ ’}
<a href="#" className="checklist__task--remove" />
</li>
));
return (...);
}
}
Refs
In the React way of working, when rendering a component, you’re always dealing with the virtual DOM. If you change a component’s state or send new props to a child, for example, they are reactively rendered to the virtual DOM. React will then update the actual DOM after the reconciliation phase.
This means that as developers you’re never touching the real DOM. In some cases, though, you may find yourself wanting to "reach out" for the actual DOM markup rendered by a component. Think twice before manipulating the actual DOM because in almost every case there’s a clearer way to structure your code within the React model. However, for the few cases where it still might be necessary or beneficial, React provides an escape hatch known as refs.
Refs can be used as a string prop on any component, like so:
<input ref="myInput" />
The referenced DOM markup can then be accessed via this.refs, like so:
let input = this.refs.myInput;
let inputValue = input.value;
let inputRect = input.getBoundingClientRect();
In this book, we will use refs very sparingly because there are few circumstances where they are really necessary. As an example, let’s create a component consisting of only a text input and a button that, when clicked, focuses the text input:
class FocusText extends Component {
handleClick() {
// Explicitly focus the text input using the raw DOM API.
this.refs.myTextInput.focus();
}
render() {
// The ref attribute adds a reference to the component to
// this.refs when the component is mounted.
return (
<div>
<input type="text" ref="myTextInput" />
<input
type="button"
value="Focus the text input"
onClick={this.handleClick.bind(this)}
/>
</div>
);
}
}
Summary
In this chapter, you examined the details about React’s DOM abstraction and the techniques the library uses to achieve fast performance, like event delegation and its diff and reconciliation characteristics (including the need for key props). You also learned about JSX in depth (and how React can be used without JSX, if desired), inline styles, and forms.
3.17.157.6