Introducing React

From this point on in the chapter, we are going to use React (sometimes referred to as ReactJs), a JavaScript library originally released by Facebook (http://facebook.github.io/react/) and focused on providing a comprehensive set of functions and tools to build the view layer in our applications. React offers a view abstraction focused on the concept of components, where a component could be a button, a form input, a simple container such as an HTML div, or any other element in your user interface. The idea is that you should be able to construct the user interface of your application by just defining and composing highly reusable components with specific responsibilities.

What makes React different from other view implementations for the web is that it is not bound to the DOM by design. In fact, it provides a higher level abstraction called virtual DOM that fits very well with the web but that can also be used in other contexts, for example, building mobile apps, modeling 3D environments, or even defining the interaction between hardware components.

 

"Learn it once, use it everywhere"

 
 --Facebook

This is the motto often used by Facebook to introduce React. It intentionally mocks the famous Java motto,"Write once, run it everywhere," with the clear intention to take distance from it and to state that every context is different and needs its own specific implementation but, at the same time, you can re-use some convenient principles and tools across contexts once you learn them.

Note

If you are interested in looking at the applications of React in contexts not strictly related to the field of web development, you can have a look at the following projects:

React Native for mobile apps (https://facebook.github.io/react-native)

React Three to create 3D scenes (https://github.com/Izzimach/react-three)

React Hardware (https://github.com/iamdustan/react-hardware)

The main reason why React is so interesting in the context of Universal JavaScript development is because it allows you to render the view code both from the server and on the client using almost the same code. To put it in another way, with React we are able to render all the HTML code that is required to display the page that the user requested directly from a Node.js server, and then, when the page is loaded, any additional interaction and rendering will be performed directly in the browser. This allows us to build Single-Page Applications (SPAs), where most of the things happen on the browser and only the part of the page that needs to be changed is refreshed. At the same time, this gives us the benefit of serving the first page loaded by the user straight off from the server, thus resulting in a faster (perceived) loading time and in a greater and easier indexability of the content for search engines.

It's also worth mentioning that React Virtual DOM is capable of optimizing the way changes are rendered. This means that the DOM is not rendered in full after every change, and instead React uses a smart in-memory diffing algorithm that is able to pre-calculate the minimum number of changes to apply to the DOM in order to update the view. This results in a very efficient mechanism for fast browser rendering, and that's probably another important reason why React is gaining a lot of traction over other libraries and frameworks.

Without further ado, let's start to use React and jump to a concrete example.

First React component

To start playing with React, we are going to build a very simple widget component to show a list of elements in our browser window.

In this example, we will use some of the tools we have already seen throughout this chapter, such as Webpack and Babel, so before starting to write some code, let's install all the dependencies we are going to need:

npm install webpack babel-core babel-loader babel-preset-es2015

We also need React and the Babel preset to turn React code into the equivalent ES5 code:

npm install react react-dom babel-preset-react

Now we are ready to write our first react component in a module called src/joyceBooks.js:

const React = require('react'); 
 
const books = [ 
  'Dubliners', 
  'A Portrait of the Artist as a Young Man', 
  'Exiles and poetry', 
  'Ulysses', 
  'Finnegans Wake' 
]; 
 
class JoyceBooks extends React.Component { 
  render() { 
    return ( 
      <div> 
        <h2>James Joyce's major works</h2> 
        <ul className="books">{ 
          books.map((book, index) => 
            <li className="book" key={index}>{book}</li> 
          ) 
        }</ul> 
      </div> 
    ); 
  } 
} 
 
module.exports = JoyceBooks; 

The first part of the code is very trivial; we are just importing the React module and defining a books array that contains book titles.

The second part is the most interesting one: it's the core of our component. Beware—if this is the first time you are looking at React code, it might look weird!

So, to define a React component, we need to create a class that extends from React.Component. This class must define a function called render, which is used to describe the part of the DOM for which the component is responsible.

But what is inside our render function? We are returning some sort of HTML with some sort of JavaScript code inside and we are not even wrapping all of it with quotes. Yes, in case you were wondering, this is not JavaScript, but it is JSX!

JSX, what?!

As we said before, React provides a high level API for generating and manipulating the virtual DOM. The DOM is a great concept by itself and it can be easily represented with XML or HTML, but if we have to dynamically manipulate its tree dealing with low level concepts such as nodes, parents, and children, it might quickly get very cumbersome. To deal with this intrinsic complexity, React introduced JSX as an intermediate format designed to describe and manipulate the virtual DOM.

Actually, JSX is not a language on its own, and it is in fact a superset of JavaScript that needs to be transpiled to plain JavaScript to be executed. However, it still gives the developer the advantage of using an XML-based syntax with JavaScript. When developing for the browser, JSX is used to describe the HTML code that defines our web components and, as you have seen in the previous example, we can put HTML tags directly in the middle of our JSX code as if they are a part of this enhanced JavaScript syntax.

This approach offers an intrinsic advantage, that is, our HTML code is now dynamically validated at build time and we will get an ahead of time error if we forgot, for instance, to close one tag.

Let's now analyze the render function from the previous example to understand some important details of JSX:

render() { 
  return ( 
    <div> 
      <h2>James Joyce's major works</h2> 
      <ul className="books">{ 
        books.map((book, index) => 
          <li className="book" key={index}>{book}</li> 
        ) 
      }</ul> 
    </div> 
  ); 
} 

As you saw, we can insert a piece of HTML code at any point of our JSX code without having to put any particular indicator or wrapper around it. In this case, we simply defined a div tag, which acts as a container for our component.

We can also put some JavaScript logic within this HTML block; notice the curly brackets within the ul tag. This approach allows us to define parts of the HTML code dynamically in a similar fashion to what you can do with many templating engines. In this case, we are using the native JavaScript map function to iterate over all the available books in the array, and for every one of them we create another piece of HTML to add the book name to the list.

The curly brackets are used to define an expression within an HTML block, and the simplest use case is to use them to print the content of a variable, like we are doing here with {book}.

Finally, notice that we can again put another block of HTML code within this JavaScript content, so that HTML and JavaScript content can be mixed and nested at any level to describe the Virtual DOM.

It is not mandatory to use JSX when developing with React. JSX is just a nice interface on top of the React Virtual DOM JavaScript library. With some additional effort, you can achieve the same results by calling these functions directly and completely skipping JSX and its transpilation step. Just to give you an idea about how React code looks without JSX, have a look at the transpiled version of the render function of our example:

function render() { 
  return React.createElement( 
    'div', 
    null, 
    React.createElement( 
      'h2', 
      null, 
      'James Joyce's major works' 
    ), 
    React.createElement( 
      'ul', 
      { className: 'books' }, 
      books.map(function (book) { 
        return React.createElement( 
          'li', 
          { className: 'book' }, 
           book 
        ); 
      }) 
    ) 
  ); 
} 

As you can see, this code looks much less readable and more prone to errors, so most of the time it is better to rely on JSX and use a transpiler to generate the equivalent JavaScript code.

To complete our quick overview of JSX, let's have a look at how this code will be finally rendered to HTML when executed:

<div data-reactroot=""> 
  <h2>James Joyce's major works</h2> 
    <ul class="books"> 
      <li class="book">Dubliners</li> 
      <li class="book">A Portrait of the Artist as a Young Man</li> 
      <li class="book">Exiles and poetry</li> 
      <li class="book">Ulysses</li> 
      <li class="book">Finnegans Wake</li> 
    </ul> 
</div> 

One last thing to notice here is that in the JSX/JavaScript version of the code, we used the attribute className and it was here converted to class. It's important to underline that when we work with the virtual DOM, we must use the DOM equivalent attributes for HTML attributes; React will then take care to convert them when rendering the HTML code.

Note

A list with all the supported tags and attributes in React is available in the official documentation at https://facebook.github.io/react/docs/tags-and-attributes.html.

If you are interested in knowing more about JSX syntax, you can read the official specification provided by Facebook: https://facebook.github.io/jsx.

Configuring Webpack to transpile JSX

In this section, we will see an example of Webpack configuration that we can use to be able to transpile JSX code to JavaScript code that can be executed in the browser:

const path = require('path'); 
module.exports = { 
  entry:  path.join(__dirname, "src", "main.js"), 
  output: { 
    path: path.join(__dirname, "dist"), 
    filename: "bundle.js" 
  }, 
  module: { 
    loaders: [ 
      { 
        test: path.join(__dirname, "src"), 
        loader: 'babel-loader', 
        query: { 
          cacheDirectory: 'babel_cache', 
          presets: ['es2015', 'react'] 
        } 
      } 
    ] 
  } 
};  

As you might have noticed, this configuration is almost identical to the one we saw in the previous ES2015 Webpack example. The only relevant differences are as follows:

  • We are using the react preset in Babel.
  • We are using the option cacheDirectory. This option allows Babel to use a specific directory as a cache folder (in this case, babel_cache) and be faster while building the bundle file. It is not mandatory but highly encouraged to speed up the development.

Rendering in the browser

Now that we have our first React component ready, we just need to use it and render it in the browser. Let's create our src/main.js JavaScript file to use our JoyceBooks component:

const React = require('react'); 
const ReactDOM = require('react-dom'); 
const JoyceBooks = require('./joyceBooks'); 
 
window.onload = () => { 
  ReactDOM.render(<JoyceBooks/>, document.getElementById('main')) 
}; 

The most important part of the code here is the ReactDOM.render function call. This function takes as arguments a JSX code block and a DOM element, and it will take care to render the JSX block to HTML code and apply it to the DOM node given as a second argument. Also notice that the JSX block we are passing here contains only a custom tag (JoyceBooks). Every time we require a component, it will be available as a JSX tag (where the name of the tag is given by the class name of the component) so that we can easily insert a new instance of this component in other JSX blocks. This is the base mechanism that allows the developer to split the interface into several cohesive components.

Now the last step we need to perform to see our first React example live is to create an index.html page:

<!DOCTYPE html> 
<html> 
  <head> 
    <meta charset="utf-8" /> 
    <title>React Example - James Joyce books</title> 
  </head> 
  <body> 
    <div id="main"></div> 
    <script src="dist/bundle.js"></script> 
  </body> 
</html> 

This is very simple and doesn't require much explanation. We are just adding our bundle.js file to a plain HTML page which contains div with the ID main that will act as a container for our React application.

You can now just launch webpack from the command line and then open the index.html page in your browser.

What is important to understand is what happens with client-side rendering when the user loads the page:

  1. The HTML code of the page is downloaded by the browser and then rendered.
  2. The bundle file is downloaded and its JavaScript content is evaluated.
  3. The evaluated code takes care to generate the real content of our page dynamically and updates the DOM to display it.

This means that if this page is loaded by a browser which has JavaScript disabled (for example, a search engine bot), our webpage will look like a blank webpage without any meaningful content. This might be a very serious problem, especially in terms of SEO.

Later in this chapter, we will see how to render the same React component from the server to overcome this limitation.

The React Router library

In this section, we will improve our previous example of building a very simple navigable app consisting of several screens. We will have three different sections: an index page, the page of the books by James Joyce, and the page of the books by H. G. Wells. We will also have a page to show when the user tries to access a URL that does not exist.

To build this app, we will use the React Router library, (https://github.com/reactjs/react-router), a module that makes it easy to have navigable components in React. So, the first thing we need to do is to download React Router in our project with:

npm install react-router

Now we are ready to create all the components needed to build the sections of this new app. Let's start with src/components/authorsIndex.js:

const React = require('react'); 
const Link = require('react-router').Link; 
 
const authors = [ 
  {id: 1, name: 'James Joyce', slug: 'joyce'}, 
  {id: 2, name: 'Herbert George Wells', slug: 'h-g-wells'} 
]; 
 
class AuthorsIndex extends React.Component { 
  render() { 
    return ( 
      <div> 
        <h1>List of authors</h1> 
        <ul>{ 
          authors.map( author => 
            <li key={author.id}><Link to={`/author/${author.slug}`}>  
                    {author.name}</Link></li> 
          ) 
        }</ul> 
      </div> 
    ) 
  } 
} 
 
module.exports = AuthorsIndex; 

This component represents the index of our application. It displays the name of the two authors. Notice that, again, to keep things simple, we are storing the data necessary to render this component in authors, an array of objects, each one representing an author. Another new element is the Link component. As you might have guessed, this component comes from the React Router library and allows us to render clickable links that can be used to navigate through the available sections of the app. What is important to understand is the property to of the Link component. It is used to specify a relative URI that indicates the specific route to display when the link is clicked. So, it is not very different than a regular HTML <a> tag, the only difference is that, instead of moving to a new page by refreshing the full page, React Router will take care to dynamically only refresh the part of the page that needs to be changed to display the component associated with the new URI. We will see better how this mechanism works when we write the configuration for our router. For now, let's focus on writing all the other components that we want to use in our app. So, now let's rewrite our JoyceBooks components that, this time, will be stored in components/joyceBooks.js:

const React = require('react'); 
const Link = require('react-router').Link; 
 
const books = [ 
  'Dubliners', 
  'A Portrait of the Artist as a Young Man', 
  'Exiles and poetry', 
  'Ulysses', 
  'Finnegans Wake' 
]; 
 
class JoyceBooks extends React.Component { 
  render() { 
    return ( 
      <div> 
        <h2>James Joyce's major works</h2> 
        <ul className="books">{ 
          books.map( (book, key) => 
            <li key={key} className="book">{book}</li> 
          ) 
        }</ul> 
        <Link to="/">Go back to index</Link> 
      </div> 
    ); 
  } 
} 
 
module.exports = JoyceBooks; 

As we might have expected, this component looks very similar to its previous version. The only notable differences are that we are adding Link to go back to the index at the end of the component and that we are using the attribute key inside our map function. With this last change, we are telling React that the specific element is identified by a unique key (in this case, we are using the index of the array for simplicity) so that it can perform a number of optimizations whenever it needs to re-render the list. This last change is not mandatory but it is heavily recommended, especially in larger applications.

Now following the same schema, we can write our components/wellsBooks.js component:

const React = require('react'); 
const Link = require('react-router').Link; 
 
const books = [ 
  'The Time Machine', 
  'The War of the Worlds', 
  'The First Men in the Moon', 
  'The Invisible Man' 
]; 
 
class WellsBooks extends React.Component { 
  render() { 
    return ( 
      <div> 
        <h2>Herbert George Wells's major works</h2> 
        <ul className="books">{ 
          books.map( (book, key) => 
            <li key={key} className="book">{book}</li> 
          ) 
        }</ul> 
        <Link to="/">Go back to index</Link> 
      </div> 
    ); 
  } 
} 
 
module.exports = WellsBooks; 

This component is almost identical to the previous one and, of course, this should ring a bell! We could build a more generic AuthorPage component and avoid code duplication, but this will be the topic for the next section, here we want to focus only on routing.

We also want to have a components/notFound.js component that just displays an error message. We will skip this trivial implementation for the sake of brevity.

So, now let's move to the interesting part—the routes.js component which defines the logic of our routing:

const React = require('react'); 
const ReactRouter = require('react-router'); 
const Router = ReactRouter.Router; 
const Route = ReactRouter.Route; 
const hashHistory = ReactRouter.hashHistory; 
const AuthorsIndex = require('./components/authorsIndex'); 
const JoyceBooks = require('./components/joyceBooks'); 
const WellsBooks = require('./components/wellsBooks'); 
const NotFound = require('./components/notFound'); 
 
class Routes extends React.Component { 
  render() { 
    return ( 
      <Router history={hashHistory}> 
        <Route path="/" component={AuthorsIndex}/> 
        <Route path="/author/joyce" component={JoyceBooks}/> 
        <Route path="/author/h-g-wells" component={WellsBooks}/> 
        <Route path="*" component={NotFound} /> 
      </Router> 
    ) 
  } 
} 
module.exports = Routes; 

The first thing to analyze here is the list of modules that we need to implement the routing component of our app. We are requiring the react-router, which in turn contains three modules that we want to use: Router, Route, and hashHistory.

Router is the main component that holds all the routing configuration. It's the element we use as the root node for our Routes component. The property history specifies the mechanism used to detect which route is active and how to update the URL in the browser bar every time the user clicks on a link. There are commonly two strategies: hashHistory and browserHistory. The first one uses the fragment part of the URL (the one delimited by the hash symbol). With this strategy, our links will look like this: index.html#/author/h-g-wells . The second strategy does not use the fragment but leverages the HTML5 history API (https://developer.mozilla.org/en-US/docs/Web/API/History_API) to display more realistic URLs. With this strategy, every path has its own full URI, such as http://example.com/author/h-g-wells .

In this example, we are using the hashHistory strategy as it is the simplest to set up and doesn't require a web server to refresh the page. We will have chance to use the browserHistory strategy later in this chapter.

The Route component allows us to define an association between a path and a component. This component will be rendered when the route is matched.

Inside our render function, we are summarizing and composing all these concepts and, now that you know the meaning of every component and option, you should be able to make sense of it.

What is important to understand here is the way the Router component works with this declarative syntax:

  • It acts as a container; it doesn't render any HTML code but contains a list of Route definitions.
  • Every Route definition is associated to a component. This time, the component is a graphical component, meaning that it will be rendered in the HTML code of the page, but only if the current URL of the page matches the route.
  • Only one route can be matched for a given URI. In ambiguous cases, the router prefers the less generic routes (for example, /author/joyce over /author).
  • It is possible to define a catch-all route with *, which is matched only when all the other routes are not matched. We are using it here to display our "not found" message.
  • Now the last step to complete this example is to update our main.js to use the Routes component as the main component of our application:
       const React = require('react'); 
       const ReactDOM = require('react-dom'); 
       const Routes = require('./routes'); 
 
       window.onload = () => { 
         ReactDOM.render(<Routes/>, document.getElementById('main')) 
       }; 

Now we just need to run Webpack to regenerate our bundle file and open the index.html to see our new app working.

Try to click around and see how the URL gets updated. Also, if you use any debug tool, you will notice how the transition between one section and another doesn't fully refresh the page neither trigger a new request. The app is in fact completely loaded when we open the index page and the router is used here to basically show and hide the right component given the current URI. Anyway, the router is smart enough that if we try to refresh the page with a specific URI (for example,  index.html#/author/joyce), it will immediately display the correct component.

React Router is a very powerful component and it has a number of interesting features. For example, it allows you to have nested routes to represent multi-level user interfaces (components with nested sections). We will also see in this chapter how it can be extended to load components and data on demand. In the meantime, you can have a break and read the official documentation of the component to discover all the available features.

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

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