left-icon

React.js Succinctly®
by Samer Buna

Previous
Chapter

of
A
A
A

CHAPTER 4

React Components

React Components


User Iinterfaces are defined as components in React. The term component is used by many other frameworks. We can also write web components natively using HTML5 features like custom elements and HTML imports.

Components have many advantages, and whether we are working with them natively using HTML5, or using a library like React, we get the following great benefits.:

Readability

Consider this UI:

Code Listing 11: HTML-based UI

<a href="”http://facebook.com"”>

  <img src="”facebook.png"” />

</a>

What does this UI represent? If you speak HTML, you can parse it quickly here and say, “it'’s a clickable image.” If we'’re to convert this UI into a component, maybe ClickableImage is a good name for it.

Code Listing 12: Component-based UI

<ClickableImage />

When things get more complex, this parsing of HTML becomes harder, so components allow us to know quickly what the UI represents using the language that we'’re comfortable with (English in this case).

Here'’s a bigger example:

Code Listing 13: Component-based UI

<TweetBox>

   <TextAreaWithLimit limit={140} />

   <RemainingCharacters />

   <TweetButton />

</TweetBox>

Without looking at the actual HTML code, we know exactly what this UI represents. Furthermore, if we need to modify the output of the remaining characters section, we know exactly where to go.

Reusability

Think of components as functions. This analogy is actually very close to the truth, especially in React. Functions take input, they do something (possibly on that input), and then give us back an output.

In functional programming, we also have pure functions, which are basically protected against any outside state; if we give them the same input, we'll always get the same output.

In React, a component is modeled after a function. Every component has private properties, which act like the input to that function, and we have a Vvirtual DOM output. If the component does not depend on anything outside of its definition (for example, if it does not use a global variable), then we label that component pure as well.

All React components can be reused in the same application and across multiple applications. Pure components, however, have a better chance at being reused without any problems.

For an example, let's implement our ClickableImage component.:

Code Listing 14: ClickableImage render function

var ClickableImage = function(props) {

  return (

    <a href={props.href}>

      <img src={props.src} />

    </a>

  );

};

ReactDOM.render(

  <ClickableImage href="http://google.com" src="http://goo.gl/QlB7wl" />,

  document.getElementById("react")

);

Having variables for both the href and the src properties is what makes this component reusable.

Note how we defined the component as an actual function. We can create React components in multiple ways;. tThe simplest way is to use a normal JavaScript function that receives the component's props as an argument.

The function’s output is the virtual HTML view, which this component represents.

Don't worry about the syntax now—just focus on the concepts. To reuse this component, we could do something like:

We  rendered ClickableImage with a Google logo:

props = { href: "http://google.com", src: "google.png" }

We can simply reuse the same component with different props:

props = { href: "http://bing.com", src: "bing.png" }

The src properties should be replaced with actual images, as we did in the render function of the previous example.

Composability

We create components to represent views. For ReactDOM, the React components we define will represent HTML DOM nodes.

The ClickableImage component in the last example was composed of two HTML elements. We can think of HTML elements as built-in components in the browser. We can also use our own custom components to compose bigger ones. For example, let's write a component that displays a list of search engines.:

Code Listing 15: SearchEngines Mockup

var SearchEngines = function(props) {

  return (

    <div className="search-engines">

      <ClickableImage href="http://google.com" src="google.png" />

      <ClickableImage href="http://bing.com" src="bing.png" />

    </div>

  );

}

If, for example, we have the data in this format:

Code Listing 16: Search Engines Data

var data = [

  { href: "http://google.com", src: "google.png" },

  { href: "http://bing.com", src: "bing.png" },

  { href: "http://yahoo.com", src: "yahoo.png" }

];

Then, to make <SearchEngines data={data} /> work, we just map the data array from a list of objects to a list of ClickableImage components:

Code Listing 17: SearchEngines render function

var SearchEngines = function(props) {

  return (

    <List>

      {props.data.map(engine => <ClickableImage {...engine} />)}

    </List>

  );

};

...

ReactDOM.render(

  <SearchEngines data={data} />,

  document.getElementById("react")

);

The three dots in ...engine means spread the attribute of an engine as flat properties for ClickableImage, which is equivalent to doing:

href={engine.href} src={engine.src}

This SearchEngines component is now reusable. It can work with any list of search engines we give to it. We also used the ClickableImage component to compose the SearchEngines component.

React’s stateful components

Everything changes eventually. In React, a component manages its changes using a state object. In addition to the data we pass to components as props, React components can also have a private state, which can change over time.

For example, a timer component can store its current timer value in its state.

Code Listing 18: Timer component

<Timer initialSeconds={42} />

This timer will start at 42 seconds and count down to 0, decrementing its state every second. At second 0, its private state will be 42; at second 1, the private state will be 41; and at second 42, the private state will be 0.

With React, we just make the timer component display its private state.:

Code Listing 19: Timer render function

function() {

  return (

    <div>

      {this.state.counter}

    </div>

  )

}

Every second, we decrement the counter state.:

Code Listing 20: Changing the State (Pseudocode)

Every second:

  state.counter = state.counter - 1

  if state.counter reaches 0

    stop the timer

Here's the great news: React components recognize the private state changes. When changes happen, React automatically hits the Refresh button for us and re-renders the UI of a component. This is where React gets its name—it will react to the state changes, and reflect them in the UI.

Creating React components

It's time to officially learn how to create React components.

Let’s define our ClickableImage component, which understands two input properties, href and& src. Once we have this ClickableImage component ready, we can mount it in the browser using the ReactDOM render function.:

Code Listing 21: Rendering a React Component to the DOM

ReactDOM.render(

  <ClickableImage href="google.com", src="google.com" />,

  document.getElementById("react")

);

Note: ReactDOM is a library that's maintained separately and can be used with React to work with a browser’s DOM. To be able to use it, you need to include its CDN entry or import it in your project. This JSBin template has a working exampleworking example of a component mounted with ReactDOM.

The first argument to the ReactDOM.render method is the React element that needs to be rendered, and the second argument is where to render that element in the browser. In this case, we’re rendering it to the HTML node with id="react".

There are three main ways to define a React component:

  • Stateless function components
  • React.createClass
  • React.Component

Stateless function components

Since components are modeled after functions, we can use a vanilla JavaScript function to write pure components:

Code Listing 22: Stateless Function Component

var ClickableImage = function(props) {

  return (

    <a href={props.href}>

      <img src={props.src} />

    </a>

  );

};

When we use a function component, we’re not creating an instance from a component class; rather, the function itself represents what would be the render method in a regular component definition. If we design our application in a functional and declarative way, most of our components could just be simple stateless function components.

Stateless function components can’t have any internal state, they don’t expose any lifecycle methods, and we can’t attach any refs to them. If we need any of these features (which I will explain in later chapters), we will need a class-based component definition.

With the new ES2015 arrow function syntax, the ClickableImage component can be defined more concisely with:

Code Listing 23: Stateless Function Component with Arrow Function

var ClickableImage = props => (

  <a href={props.href}>

    <img src={props.src} />

  </a>

);

React.createClass

React has an official API to define a stateful component. Our simple ClickableImage component would be:

Code Listing 24: React.createClass syntax

var ClickableImage = React.createClass({

  render: function() {

    return (

      <a href={this.props.href}>

        <img src={this.props.src} />

      </a>

    );

  }

});

The createClass function takes a single argument, a JavaScript configuration object. That object requires one property, the render property, which is where we define the component’s function that describes its UI.

Note how with createClass, we don’t pass the props object to the render call. Instead, elements created from this component class can access their properties using this.props within the render function.

The this keyword references the instance of the component that we mounted in the DOM (using ReactDOM.render). Every time we mount a <ClickableImage /> element, we’re creating an instance of the ClickableImage component class.

In object-oriented programming terms, ClickableImage is the class, and <ClickableImage ClickableImage /> is the object instantiated from that class.

With createClass, we can use the private state of the component object, and we can invoke custom behavior in its lifecycle methods. To demonstrate both concepts, let's implement a Timer component.

First, here's how we’re going to use this Timer component:

Code Listing 25: Rendering the Timer Component

ReactDOM.render(

  <Timer initialSeconds={42} />,

  document.getElementById("react")

);

The simple definition of this component, before considering the private state or the tick operation, is:

Code Listing 26: Timer Component render() Function

var Timer = React.createClass({

  render: function() {

    return (

      <div>{this.state.counter}</div>

    );

  }

});

The counter variable is part of the private state within a component instance. The private state object can be accessed using this.state.

Our Timer component has the property initialSeconds, which is where the counter should start. Properties of a component instance can be accessed using this.props, so if we need to read the value that we're passing to the initialSeconds property, we do use this.props.initialSeconds.

The state of a React component can be initialized using a getInitialState function in the createClass definition object. Anything we return from the getInitialState function will be used as the initial private state for a component instance.

We want the initial state for our counter variable to start as this.props.initialSeconds, so we do the following:

Code Listing 27: Timer Component Initial State

var Timer = React.createClass({

  getInitialState: function() {

    return {

      counter: this.props.initialSeconds

    };

  },

  render: function() {

    return (

      <div>{this.state.counter}</div>

    );

  }

});

Let's now define the “tick” operation. We can use a vanilla setInterval function to tick every second (1000 milliseconds). Inside the interval function, we need to change our component state and decrement the counter.

The ticking operation should start after the component gets rendered to the DOM so that we’re sure there is a div in the DOM whose content we can now control. For that, we need a lifecycle method.

Lifecycle methods act like hooks for us to define custom behavior at certain points in the lifecycle of a React component instance. The one we need here is componentDidMount(), and it allows us to define a custom behavior right after the component gets is mounted in the DOM.

Code Listing 28: Timer Interval In componentDidMount()

var Timer = React.createClass({

  getInitialState: function() {

    return { counter: this.props.initialSeconds };

  },

  componentDidMount: function() {

    var component = this;

    setInterval(function() {

      component.setState({

        counter: component.state.counter - 1

      });

    }, 1000);

  },

  render: function() {

    return <div>{this.state.counter}</div>;

  }

});

There are a couple of things to notice here:

  • We had to use a closure around the setInterval so that we have access to the this keyword in there. It’s an old-school JavaScript trick (which we don't need anymore with the new ES2015 arrow function syntax).
  • To change the state of the component, we used the setState function. In React, we should never mutate the state directly as a variable. All changes to the state should be done using setState.

This Timer component is ready, except that the timer will not stop, and it will keep going to the negative side. We can use the clearTimeout function to stop the timer. Go ahead and try to do that for our component, and come back to see the following full solution.:

Code Listing 29: Timer Component Full Definition

var Timer = React.createClass({

  getInitialState: function() {

    return { counter: this.props.initialSeconds };

  },

  componentDidMount: function() {

    var component = this, currentCounter;

    component.timerId = setInterval(function() {

      currentCounter = component.state.counter;

      if (currentCounter === 1) {

        clearInterval(component.timerId);

      }

      component.setState({ counter: currentCounter - 1 });

    }, 1000);

  },

  render: function() {

    return <div>{this.state.counter}</div>;

  }

});

ReactDOM.render(

  <Timer initialSeconds={42} />,

  document.getElementById("react")

);

React.Component

ES2015 was finalized in 2015, and with it, we can now use the class syntax. A class is syntax sugar for JavaScript’s constructor functions, and classes can inherit from each other using the extends keyword.:

Code Listing 30: ES2015 Class Syntax

class Student extends Person { }

With this line, we define a new Student class that inherits from a Person class.

The React API has a class that we can extend to define a React component. Our ClickableImage definition becomes:

Code Listing 31: React.Component Syntax

class ClickableImage extends React.Component {

  render() {

    return (

      <a href={this.props.href}>

        <img src={this.props.src} />

      </a>

    );

  }

}

Within the class definition, the render function is basically the same, except that we used a new ES2015 syntax to define it. The word function can be completely avoided in ES2015.

Let's look at our Timer example using the ES2015 syntax. Try to identify the differences.:

Code Listing 32: Timer Component Using Class Syntax

class Timer extends React.Component {

  constructor(props) {

    super(props);

    this.state = { counter: this.props.initialSeconds };

  }

  componentDidMount() {

    let currentCounter;

    this.timerId = setInterval(() => {

      currentCounter = this.state.counter;

      if (currentCounter === 1) {

        clearInterval(this.timerId);

      }

      this.setState({ counter: currentCounter - 1 });

    }, 1000);

  }

  render() {

    return (

      <div>{this.state.counter}</div>

    );

  }

}

Here are the differences explained:

  • Instead of getInitialState, we now use the ES2015 class constructor function, and just assign a value to this.state in the constructor. We need to invoke the super() call there as well to fire the React.Component constructor call.
  • In the setInterval, we used an arrow function, i.e. () => { }. With an arrow function, we don't need to do the closure trick we did before, because arrow functions have a lexically bound “this” that captures the enclosing context by default.
  • All the functions are defined using the new function property syntax., For example,  e.g. componentDidMount() { ... }.

Component classes, elements, and instances

Sometimes you’ll find these terms mixed up in guides and tutorials. It’s important to understand that we have three different things here:

  • What’s usually referred to as “Component” is the class. The blueprint. The global definition. In the Timer example, the variable Timer itself is the component class.
  • <Timer /> on the other hand, is a React element that we constructed from the Timer component class. This is a stateless, immutable Vvirtual DOM object.
  • When a React element getis mounted in the browser’s DOM, it becomes a component instance, which is stateful. The result of a ReactDOM.render call is a component instance.
Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.