React - Basic

react

What is the first step that we should do when working on a React project?

Break the UI into a component hierarchy. Start with a mock. Imagine that we already have a JSON API and a mock from our designer. The first thing you'll want to do is to draw boxes around every component (and subcomponent) in the mock and give them all names. If you're working with a designer, they may have already done this, so go talk to them! Their Photoshop layer names may end up being the names of your React components!

But how do you know what should be its own component? Just use the same techniques for deciding if you should create a new function or object. One such technique is the single responsibility principle, that is, a component should ideally only do one thing. If it ends up growing, it should be decomposed into smaller subcomponents.

Since you're often displaying a JSON data model to a user, you'll find that if your model was built correctly, your UI (and therefore your component structure) will map nicely. That's because UI and data models tend to adhere to the same information architecture, which means the work of separating your UI into components is often trivial. Just break it up into components that represent exactly one piece of your data model.

thinking-in-react-components.png

You'll see here that we have five components in our simple app. I've italicized the data each component represents.

  1. FilterableProductTable (orange): contains the entirety of the example
  2. SearchBar (blue): receives all user input
  3. ProductTable (green): displays and filters the data collection based on user input
  4. ProductCategoryRow (turquoise): displays a heading for each category
  5. ProductRow (red): displays a row for each product

If you look at ProductTable, you'll see that the table header (containing the "Name" and "Price" labels) isn't its own component. This is a matter of preference, and there's an argument to be made either way. For this example, I left it as part of ProductTable because it is part of rendering the data collection which is ProductTable's responsibility. However, if this header grows to be complex (i.e. if we were to add affordances for sorting), it would certainly make sense to make this its own ProductTableHeader component.

Now that we've identified the components in our mock, let's arrange them into a hierarchy. This is easy. Components that appear within another component in the mock should appear as a child in the hierarchy:

  • FilterableProductTable
    • SearchBar
    • ProductTable
      • ProductCategoryRow
      • ProductRow

What is the second thing that we should do when working on a React project?

Build a static version in React. Now that you have your component hierarchy, it's time to implement your app. The easiest way is to build a version that takes your data model and renders the UI but has no interactivity. It's best to decouple these processes because building a static version requires a lot of typing and no thinking, and adding interactivity requires a lot of thinking and not a lot of typing.

To build a static version of your app that renders your data model, you'll want to build components that reuse other components and pass data using props. props are a way of passing data from parent to child. If you're familiar with the concept of state, don't use state at all to build this static version. State is reserved only for interactivity, that is, data that changes over time. Since this is a static version of the app, you don't need it.

You can build top-down or bottom-up. That is, you can either start with building the components higher up in the hierarchy (i.e. starting with FilterableProductTable) or with the ones lower in it (ProductRow). In simpler examples, it's usually easier to go top-down, and on larger projects, it's easier to go bottom-up and write tests as you build.

At the end of this step, you'll have a library of reusable components that render your data model. The components will only have render() methods since this is a static version of your app. The component at the top of the hierarchy (FilterableProductTable) will take your data model as a prop. If you make a change to your underlying data model and call ReactDOM.render() again, the UI will be updated. It's easy to see how your UI is updated and where to make changes since there's nothing complicated going on. React's one-way data flow (also called one-way binding) keeps everything modular and fast.

What is the third step that we should do when working on a React project?

Identify the minimal (but complete) representation of UI state. To make your UI interactive, you need to be able to trigger changes to your underlying data model. React makes this easy with state.

To build your app correctly, you first need to think of the minimal set of mutable state that your app needs. The key here is DRY: Don't Repeat Yourself. Figure out the absolute minimal representation of the state your application needs and compute everything else you need on-demand. For example, if you're building a TODO list, just keep an array of the TODO items around; don't keep a separate state variable for the count. Instead, when you want to render the TODO count, simply take the length of the TODO items array.

Think of all of the pieces of data in our example application. We have:

  1. The original list of products
  2. The search text the user has entered
  3. The value of the checkbox
  4. The filtered list of products

Let's go through each one and figure out which one is state. Simply ask three questions about each piece of data:

  1. Is it passed in from a parent via props? If so, it probably isn't state.
  2. Does it remain unchanged over time? If so, it probably isn't state.
  3. Can you compute it based on any other state or props in your component? If so, it isn't state.

The original list of products is passed in as props, so that's not state. The search text and the checkbox seem to be state since they change over time and can't be computed from anything. And finally, the filtered list of products isn't state because it can be computed by combining the original list of products with the search text and value of the checkbox. So finally, our state is:

  1. The search text the user has entered
  2. The value of the checkbox

What is the 4th step that we should do when working on a React project?

Identify where your state should live. OK, so we've identified what the minimal set of app state is. Next, we need to identify which component mutates, or owns, this state.

Remember: React is all about one-way data flow down the component hierarchy. It may not be immediately clear which component should own what state. This is often the most challenging part for newcomers to understand, so follow these steps to figure it out:

For each piece of state in your application:

  1. Identify every component that renders something based on that state.
  2. Find a common owner component (a single component above all the components that need the state in the hierarchy).
  3. Either the common owner or another component higher up in the hierarchy should own the state.
  4. If you can't find a component where it makes sense to own the state, create a new component simply for holding the state and add it somewhere in the hierarchy above the common owner component.

Let's run through this strategy for our application:

  1. ProductTable needs to filter the product list based on state and SearchBar needs to display the search text and checked state.
  2. The common owner component is FilterableProductTable.
  3. It conceptually makes sense for the filter text and checked value to live in FilterableProductTable

Cool, so we've decided that our state lives in FilterableProductTable. First, add a getInitialState() method to FilterableProductTable that returns {filterText: '', inStockOnly: false} to reflect the initial state of your application. Then, pass filterText and inStockOnly to ProductTable and SearchBar as a prop. Finally, use these props to filter the rows in ProductTable and set the values of the form fields in SearchBar.

You can start seeing how your application will behave: set filterText to "ball" and refresh your app. You'll see that the data table is updated correctly.

What is the fifth step that we should do when working on a React project?

Add inverse data flow. So far, we've built an app that renders correctly as a function of props and state flowing down the hierarchy. Now it's time to support data flowing the other way: the form components deep in the hierarchy need to update the state in FilterableProductTable.

React makes this data flow explicit to make it easy to understand how your program works, but it does require a little more typing than traditional two-way data binding. If you try to type or check the box in the current version of the example, you'll see that React ignores your input. This is intentional, as we've set the value prop of the input to always be equal to the state passed in from FilterableProductTable.

Let's think about what we want to happen. We want to make sure that whenever the user changes the form, we update the state to reflect the user input. Since components should only update their own state, FilterableProductTable will pass a callback to SearchBar that will fire whenever the state should be updated. We can use the onChange event on the inputs to be notified of it. And the callback passed by FilterableProductTable will call setState(), and the app will be updated.

What is included in the React Starter Kit?

The starter kit includes prebuilt copies of React and React DOM for the browser, as well as a collection of usage examples to help you get started. You can download the React Starter Kit from: https://facebook.github.io/react/downloads/react-15.3.2.zip

In the root directory of the starter kit, create a helloworld.html with the following contents:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
    <script src="build/react.js"></script>
    <script src="build/react-dom.js"></script>
    <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
  </head>
  <body>
    <div id="example"></div>
    <script type="text/babel">
      ReactDOM.render(
        <h1>Hello, world!</h1>,
        document.getElementById('example')
      );
    </script>
  </body>
</html>

How can we use JSX?

To use JSX, we need Babel.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
    <script src="build/react.js"></script>
    <script src="build/react-dom.js"></script>
    <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
  </head>
  <body>
    <div id="example"></div>
    <script type="text/babel">
      ReactDOM.render(
        <h1>Hello, world!</h1>,
        document.getElementById('example')
      );
    </script>
  </body>
</html>

Specifically:

<script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>

// and:
<script type="text/babel">
  ReactDOM.render(
    <h1>Hello, world!</h1>,
    document.getElementById('example')
  );
</script>

In order to translate it to vanilla JavaScript we use <script type="text/babel"> and include Babel to actually perform the transformation in the browser. Your React JSX code can live in a separate file. Create the following src/helloworld.js:

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('example')
);

Then reference it from helloworld.html:

<script type="text/babel" src="src/helloworld.js"></script>

Note that some browsers (Chrome, e.g.) will fail to load the file unless it's served via HTTP.

How can we create a React component?

We need the following files:

  1. https://fb.me/react-0.14.3.js
  2. https://fb.me/react-dom-0.14.3.js
  3. https://babeljs.io/

The boilderplate:

<!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>React Tutorial</title>
    <script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
    <script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
    <script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>
    <script src="https://unpkg.com/jquery@3.1.0/dist/jquery.min.js"></script>
    <script src="https://unpkg.com/remarkable@1.6.2/dist/remarkable.min.js"></script>
  </head>
  <body>
    <div id="content"></div>
    <script type="text/babel" src="scripts/example.js"></script>
    <script type="text/babel">
      // To get started with this tutorial running your own code, simply remove
      // the script tag loading scripts/example.js and start writing code here.
    </script>
  </body>
</html>

To create React Components, use the createClass function of the React object. The createClass function expects an object configuring the component to be passed in. The createClass function is a helper function for creating new components which inherit from React.Component. If you are using using ES2015 natively in the browser or through a transpiler such as Babel, then it’s possible to inherit directly from React.Component using the new class and extends keywords. To use Babel in CodePen, click on “Settings”, then “JavaScript”, and select it from the “JavaScript Preprocessor” drop down list.

// HTML:
<main></main>

// JS:
"use strict";

var HelloWorld = React.createClass({

  render: function() {
    return React.createElement("h1", null, "Hello World!");
  },

});

var mainElement = document.querySelector("main");

ReactDOM.render(React.createElement(HelloWorld), mainElement);

// If we are using Babel:
"use strict";

class HelloWorld extends React.Component {

  render() {
    return React.createElement("h1", null, "Hello World!");
  }

}

var mainElement = document.querySelector("main");

ReactDOM.render(React.createElement(HelloWorld), mainElement);

Regardless of the approach to creating the class structure for the component, the result is the same. The only required property for a component is the render property, which points to a function object which is used to actually render the DOM of the component. The implementation of the render function introduces a new function, createElement, that is provided by the React object. The createElement function is used to create new DOM elements with React. The function expects up to three parameters.

What is the purpose of the createElement function?

The createElement function is used to create new DOM elements with React.

How many parameters can the React.createElement function take?

It can take up to 3 parameters.

  1. The first parameter is the name of the HTML element or React Component to create. HTML elements should be a lowercase string containing only the name of the element without the angle brackets and no attributes. Examples of acceptable HTML element arguments include “h1”, “p”, etc. In addition to HTML element names, React Component objects can be passed in. For React Components, the object itself, not a string name of the object, is passed in.
  2. The second parameter is an object of the properties to pass in. For HTML elements, these properties correspond to the attributes of the HTML element. For React Components, these properties correspond to stateless data for use when rendering the component.
  3. The third parameter represents the child elements of the element being created. In the “Hello World” example, the child content of the h1 element is the content “Hello World!” In addition to textual content, element objects can be passed in.

What are the 3 paramters for the createElement function?

The createElement function is used to create new DOM elements with React. The function expects up to three parameters.

  1. The first parameter is the name of the HTML element or React Component to create. HTML elements should be a lowercase string containing only the name of the element without the angle brackets and no attributes. Examples of acceptable HTML element arguments include “h1”, “p”, etc. In addition to HTML element names, React Component objects can be passed in. For React Components, the object itself, not a string name of the object, is passed in.
  2. The second parameter is an object of the properties to pass in. For HTML elements, these properties correspond to the attributes of the HTML element. For React Components, these properties correspond to stateless data for use when rendering the component.
  3. The third parameter represents the child elements of the element being created. In the “Hello World” example, the child content of the h1 element is the content “Hello World!” In addition to textual content, element objects can be passed in.

Can we pass multiple child elements to the createElement function?

Yes. By using an array, multiple child elements can be passed as the third parameter to the createElement function.

"use strict";

var ItemList = React.createClass({

  render: function() {
    return React.createElement("ul", null, [
      React.createElement("li", null, "Item 1"),
      React.createElement("li", null, "Item 2"),
      React.createElement("li", null, "Item 3"),
    ]);
  },

});

var mainElement = document.querySelector("main");

ReactDOM.render(React.createElement(ItemList), mainElement);

How can we render or utilize a component?

ReactDOM.render(React.createElement(HelloWorld), mainElement);

To utilize the React Components in a web page, the ReactDOM object’s render function is used. It expects an element object, and a root element to which the DOM of the element object will be appended. In the code demonstration, the createElement function is used to create an instance of the HelloWorld component, while document.querySelector is used to select the main element to which the instantiated and rendered HelloWorld component is appended. Once appended, the component appears in the web page, and the React demonstration is complete.

What does JSX abbreviate for?

JavaScript Syntax Extension. The compromise between ease of coding and not introducing logic into the template was achieved through a technique called JSX (JavaScript syntax extension). JSX is an optional syntax extension, in case you prefer the readability of HTML to raw JavaScript.

What is the purpose of JSX?

The compromise between ease of coding and not introducing logic into the template was achieved through a technique called JSX (JavaScript syntax extension). JSX is an optional syntax extension, in case you prefer the readability of HTML to raw JavaScript.

"use strict";

var HelloWorld = React.createClass({

  render: function() {
    return (
      <h1>Hello World!!!</h1>
    );
  },

});

var mainElement = document.querySelector("main");

ReactDOM.render(<HelloWorld></HelloWorld>, mainElement);

As you can see, we change the render function from:

return React.createElement("h1", null, "Hello World!");

to:

return (<h1>Hello World!!!</h1>);

The result of the JSX transpilation is JavaScript with createElement calls. Babel is used to transpile the JSX to JavaScript. Originally, Babel was simply an ES6 JavaScript transpiler. With the completion of ES6 (ES2015), Babel has been extended to serve as a platform for creating JavaScript code including plugins such as the one for JSX.

The React web site recommends the use of JSX, and many developers use it. Nevertheless, using JSX is not required, and React.js applications work the same regardless of its usage.

Why should we use JSX?

JSX is designer-friendly. JSX is basically HTML, so it is designer-friendly. Instagram is a "single page" web app built entirely with React and Backbone.Router. Designers regularly contribute React code with JSX.

What are the two kinds of data associate with React Components?

React Components have two kinds of data: state and properties. State data represents data that will be updated by a user or from the server. While state is important, it should be limited to as little as needed, with emphasis put on the properties which can be passed data (including state data from a component which manages state) to be rendered in the DOM.

Properties are the preferred way of configuring and managing data for a component.

Does React support two-way data-binding by default?

No. React does not support two-way data binding by default. https://www.sitepoint.com/getting-started-react/

How can we access properties that are passed to React components?

Properties were discussed in conjunction with the createElement function. Within React Components, properties passed to the component can be accessed through the props property of the component.

    HTML Babel Result 

Edit on

"use strict";

var PageTitle = React.createClass({

  render: function() {
    return (
      <header>
        <h1>{this.props.label}</h1>
      </header>
    );
  },

});

var mainElement = document.querySelector("main");

ReactDOM.render(<PageTitle label="Welcome to React.js!"></PageTitle>, mainElement);

Notice how the PageTitle component was defined, and especially how it is rendered with ReactDOM.render function in the above code. Notice that the label "Welcome to React.js" is passed as a property. The property had a static value and was passed in using an attribute in the JSX syntax. In addition to passing a static value, a value from a JavaScript variable can passed in using the curly brace template variable syntax.

The transpiled version of the above code:

"use strict";

var PageTitle = React.createClass({
  displayName: "PageTitle",

  render: function render() {
    return React.createElement(
      "header",
      null,
      React.createElement(
        "h1",
        null,
        this.props.label
      )
    );
  }

});

var mainElement = document.querySelector("main");

ReactDOM.render(React.createElement(PageTitle, { label: "Welcome to React.js!" }), mainElement);

How can we use a plain JavaScript object can be used with the non-JSX version?

"use strict";

var PageTitle = React.createClass({

  render: function() {
    return (
      <header>
        <h1>{this.props.label}</h1>
      </header>
    );
  },

});

var mainElement = document.querySelector("main");

ReactDOM.render(React.createElement(PageTitle, { label: "Welcome to React.js!" }), mainElement);

The transpiled version of the above code:

"use strict";

var PageTitle = React.createClass({
  displayName: "PageTitle",

  render: function render() {
    return React.createElement(
      "header",
      null,
      React.createElement(
        "h1",
        null,
        this.props.label
      )
    );
  }

});

var mainElement = document.querySelector("main");

ReactDOM.render(React.createElement(PageTitle, { label: "Welcome to React.js!" }), mainElement);

What is the purpose of Reconciliation?

When component properties (or state) change, React performs a process called Reconciliation to determine what DOM updates are needed to render the updated property and state information. React’s mechanism for this is quite sophisticated and even involves the use of virtual DOM to compare changes to make selective and fast updates to the DOM without having to re-render everything.

What is the definition of composable components?

React Components are designed to be composable, they can be combined together to build larger more sophisticated components. Consider the example of an HTML table. Typically, tables include a header and body sections. The header row is static while there are a variable number of body rows dependent upon the data available at any given moment. Such a table is a perfect example of composable component. The table itself can be a component with each row of data being a different component.

Other JavaScript solutions such as Angular.js and Handlebars use template logic instead of composable components.

"use strict";

var widgets = [
  { name: "Widget 1", color: "blue", size: "small", qty: 10 },
  { name: "Widget 2", color: "red", size: "large", qty: 20 },
  { name: "Widget 3", color: "green", size: "medium", qty: 15 }
];

var WidgetTable = React.createClass({
  render: function() {

    var widgetRows = this.props.widgets.map(function(widget) {
      return React.createElement(WidgetRow, { widget: widget });
    });

    return (
      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>Color</th>
            <th>Size</th>
            <th>Qty</th>
          </tr>
        </thead>
        <tbody>
          {widgetRows}
        </tbody>
      </table>
    );
  }

});

var WidgetRow = React.createClass({
  render: function() {
    return (
      <tr>
        <td>{this.props.widget.name}</td>
        <td>{this.props.widget.color}</td>
        <td>{this.props.widget.size}</td>
        <td>{this.props.widget.qty}</td>
      </tr>
    );
  }
});

var mainElement = document.querySelector("main");

ReactDOM.render(<WidgetTable widgets={widgets} />, mainElement);

ReactDOM.render(React.createElement(WidgetTable, {widgets:widgets}), mainElement);

React is all about modular, composable components. For our comment box example, we'll have the following component structure:

- CommentBox
  - CommentList
    - Comment
  - CommentForm
var CommentList = React.createClass({
  render: function() {
    return (
      <div className="commentList">
        Hello, world! I am a CommentList.
      </div>
    );
  }
});

var CommentForm = React.createClass({
  render: function() {
    return (
      <div className="commentForm">
        Hello, world! I am a CommentForm.
      </div>
    );
  }
});

// tutorial3.js
var CommentBox = React.createClass({
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList />
        <CommentForm />
      </div>
    );
  }
});

Notice how we're mixing HTML tags and components we've built. HTML components are regular React components, just like the ones you define.

How does React make server-side rendering easier?

React makes server-side rendering trivial because components can be thought of as functions that take data and return HTML. This design makes it easy to integrate server-side rendering into any server-side programming language or framework.

In the early days of client-side MVC we broke the web with things like hashbang routing and huge loading times before anything made its way to the screen. We also broke search engine crawlers by rendering everything with JavaScript when the page had loaded. Since then we’ve learned from our mistakes and are taking these core tenets of the web seriously again — URLs, server rendered HTML and fast load times. React shines here where other frameworks have struggled.

Does React component define transition between states?

No. React components don’t define the transition between states. Instead, they explicitly render the view based on their current state, completely eliminating this task of manually tweaking the DOM. Its one-way data flow prevents the DOM from being the source of truth.

Admittedly, this makes certain tasks such as animation more difficult because those are cases where you do want to define transitions between states. For the vast majority of cases though, it’s much simpler to only concern yourself with the final state of how the component should be rendered.

What are the core ideas of React?

React will continue to grow in popularity and we’ll see more supporting tools and projects. As the ecosystem around React matures, the library may change but the core ideas of one-way data flow, component hierarchies, explicit renders and virtual DOM reconciliation will live on.

What is the naming convention for React class / component?

Note that native HTML element names start with a lowercase letter, while custom React class names begin with an uppercase letter.

Do we have to return basic HTML?

No. You do not have to return basic HTML. You can return a tree of components that you (or someone else) built. This is what makes React composable: a key tenet of maintainable frontends. For example, a comment component may contain:

- CommentBox
  - CommentList
    - Comment
  - CommentForm

What is the purpose of the ReactDOM.render() method?

ReactDOM.render() instantiates the root component, starts the framework, and injects the markup into a raw DOM element, provided as the second argument.

When should we invoke the ReactDOM.render method?

It is important that ReactDOM.render remain at the bottom of the script for this tutorial. ReactDOM.render should only be called after the composite components have been defined.

How can a child component access its data passed through the parent component?

Let's create the Comment component, which will depend on data passed in from its parent, CommentList. Data passed in from a parent component is available as a 'property' on the child component. These 'properties' are accessed through this.props. Using props, we will be able to read the data passed to the Comment from the CommentList, and render some markup:

var Comment = React.createClass({
  render: function() {
    return (
      <div className="comment">
        <h2 className="commentAuthor">
          {this.props.author}
        </h2>
        {this.props.children}
      </div>
    );
  }
});

What is the significance of using braces inside JSX?

By surrounding a JavaScript expression in braces inside JSX (as either an attribute or child), you can drop text or React components into the tree. We access named attributes passed to the component as keys on this.props and any nested elements as this.props.children.

var Comment = React.createClass({
  render: function() {
    return (
      <div className="comment">
        <h2 className="commentAuthor">
          {this.props.author}
        </h2>
        {this.props.children}
      </div>
    );
  }
});

How can we hook up the data model?

var data = [
  {id: 1, author: "Pete Hunt", text: "This is one comment"},
  {id: 2, author: "Jordan Walke", text: "This is *another* comment"}
];

// tutorial9.js
var CommentBox = React.createClass({
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.props.data} />
        <CommentForm />
      </div>
    );
  }
});

ReactDOM.render(
  <CommentBox data={data} />,
  document.getElementById('content')
);

var CommentList = React.createClass({
  render: function() {
    var commentNodes = this.props.data.map(function(comment) {
      return (
        <Comment author={comment.author} key={comment.id}>
          {comment.text}
        </Comment>
      );
    });
    return (
      <div className="commentList">
        {commentNodes}
      </div>
    );
  }
});

How can we hook up the data model from the server?

var CommentBox = React.createClass({
  getInitialState: function() {
    return {data: []};
  },
  componentDidMount: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} />
        <CommentForm />
      </div>
    );
  }
});

Here, componentDidMount is a method called automatically by React after a component is rendered for the first time. The key to dynamic updates is the call to this.setState(). We replace the old array of comments with the new one from the server and the UI automatically updates itself. Because of this reactivity, it is only a minor change to add live updates. We will use simple polling here but you could easily use WebSockets or other technologies.

// tutorial14.js
var CommentBox = React.createClass({
  loadCommentsFromServer: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  getInitialState: function() {
    return {data: []};
  },
  componentDidMount: function() {
    this.loadCommentsFromServer();
    setInterval(this.loadCommentsFromServer, this.props.pollInterval);
  },
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} />
        <CommentForm />
      </div>
    );
  }
});

ReactDOM.render(
  <CommentBox url="/api/comments" pollInterval={2000} />,
  document.getElementById('content')
);

All we have done here is move the AJAX call to a separate method and call it when the component is first loaded and every 2 seconds after that. Try running this in your browser and changing the comments.json file (in the same directory as your server); within 2 seconds, the changes will show!

Why are props immutable?

So far, based on its props, each component has rendered itself once. props are immutable: they are passed from the parent and are "owned" by the parent. To implement interactions, we introduce mutable state to the component. this.state is private to the component and can be changed by calling this.setState(). When the state updates, the component re-renders itself.

render() methods are written declaratively as functions of this.props and this.state. The framework guarantees the UI is always consistent with the inputs. When the server fetches data, we will be changing the comment data we have. Let's add an array of comment data to the CommentBox component as its state:

var CommentBox = React.createClass({
  getInitialState: function() {
    return {data: []};
  },
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} />
        <CommentForm />
      </div>
    );
  }
});

getInitialState() executes exactly once during the lifecycle of the component and sets up the initial state of the component.

How can we create form components with React?

At first, it seems that there are nothing special with form, and they can be created:

var CommentForm = React.createClass({
  render: function() {
    return (
      <form className="commentForm">
        <input type="text" placeholder="Your name" />
        <input type="text" placeholder="Say something..." />
        <input type="submit" value="Post" />
      </form>
    );
  }
});

However, there is more than that. With the traditional DOM, input elements are rendered and the browser manages the state (its rendered value). As a result, the state of the actual DOM will differ from that of the component. This is not ideal as the state of the view will differ from that of the component. In React, components should always represent the state of the view and not only at the point of initialization.

Hence, we will be using this.state to save the user's input as it is entered. We define an initial state with two properties author and text and set them to be empty strings. In our <input> elements, we set the value prop to reflect the state of the component and attach onChange handlers to them. These <input> elements with a value set are called controlled components. Read more about controlled components on the Forms article.

var CommentForm = React.createClass({
  getInitialState: function() {
    return {author: '', text: ''};
  },
  handleAuthorChange: function(e) {
    this.setState({author: e.target.value});
  },
  handleTextChange: function(e) {
    this.setState({text: e.target.value});
  },
  render: function() {
    return (
      <form className="commentForm">
        <input
          type="text"
          placeholder="Your name"
          value={this.state.author}
          onChange={this.handleAuthorChange}
        />
        <input
          type="text"
          placeholder="Say something..."
          value={this.state.text}
          onChange={this.handleTextChange}
        />
        <input type="submit" value="Post" />
      </form>
    );
  }
});

Is React a client-side-only framework?

No and yes. It is not an MVC framework. It is a component framework / library. By itself, It can be used on the client-side or on the server-side unless we use it with another library that use features that are only available in the browser.

How does React handle events?

React attaches event handlers to components using a camelCase naming convention. We attach onChange handlers to the two <input> elements. Now, as the user enters text into the <input> fields, the attached onChange callbacks are fired and the state of the component is modified. Subsequently, the rendered value of the input element will be updated to reflect the current component state.

(The astute reader may be surprised that these event handlers work as described, given that the method references are not explicitly bound to this. React.createClass(…) automatically binds each method to its component instance, obviating the need for explicit binding.)

How can we handle form submit?

var CommentForm = React.createClass({
  getInitialState: function() {
    return {author: '', text: ''};
  },
  handleAuthorChange: function(e) {
    this.setState({author: e.target.value});
  },
  handleTextChange: function(e) {
    this.setState({text: e.target.value});
  },
  handleSubmit: function(e) {
    e.preventDefault();
    var author = this.state.author.trim();
    var text = this.state.text.trim();
    if (!text || !author) {
      return;
    }
    // TODO: send request to the server
    this.setState({author: '', text: ''});
  },
  render: function() {
    return (
      <form className="commentForm" onSubmit={this.handleSubmit}>
        <input
          type="text"
          placeholder="Your name"
          value={this.state.author}
          onChange={this.handleAuthorChange}
        />
        <input
          type="text"
          placeholder="Say something..."
          value={this.state.text}
          onChange={this.handleTextChange}
        />
        <input type="submit" value="Post" />
      </form>
    );
  }
});

We attach an onSubmit handler to the form that clears the form fields when the form is submitted with valid input. Call preventDefault() on the event to prevent the browser's default action of submitting the form.

How can we pass data from the child component back up to its parent?

When a user submits a comment, we will need to refresh the list of comments to include the new one. It makes sense to do all of this logic in CommentBox since CommentBox owns the state that represents the list of comments.

We need to pass data from the child component back up to its parent. We do this in our parent's render method by passing a new callback (handleCommentSubmit) into the child, binding it to the child's onCommentSubmit event. Whenever the event is triggered, the callback will be invoked:

var CommentBox = React.createClass({
  loadCommentsFromServer: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  handleCommentSubmit: function(comment) {
    // TODO: submit to the server and refresh the list
  },
  getInitialState: function() {
    return {data: []};
  },
  componentDidMount: function() {
    this.loadCommentsFromServer();
    setInterval(this.loadCommentsFromServer, this.props.pollInterval);
  },
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} />
        <CommentForm onCommentSubmit={this.handleCommentSubmit} />
      </div>
    );
  }
});

Now that CommentBox has made the callback available to CommentForm via the onCommentSubmit prop, the CommentForm can call the callback when the user submits the form:

var CommentForm = React.createClass({
  getInitialState: function() {
    return {author: '', text: ''};
  },
  handleAuthorChange: function(e) {
    this.setState({author: e.target.value});
  },
  handleTextChange: function(e) {
    this.setState({text: e.target.value});
  },
  handleSubmit: function(e) {
    e.preventDefault();
    var author = this.state.author.trim();
    var text = this.state.text.trim();
    if (!text || !author) {
      return;
    }
    this.props.onCommentSubmit({author: author, text: text});
    this.setState({author: '', text: ''});
  },
  render: function() {
    return (
      <form className="commentForm" onSubmit={this.handleSubmit}>
        <input
          type="text"
          placeholder="Your name"
          value={this.state.author}
          onChange={this.handleAuthorChange}
        />
        <input
          type="text"
          placeholder="Say something..."
          value={this.state.text}
          onChange={this.handleTextChange}
        />
        <input type="submit" value="Post" />
      </form>
    );
  }
});

Now that the callbacks are in place, all we have to do is submit to the server and refresh the list:

var CommentBox = React.createClass({
  loadCommentsFromServer: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  handleCommentSubmit: function(comment) {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      type: 'POST',
      data: comment,
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  getInitialState: function() {
    return {data: []};
  },
  componentDidMount: function() {
    this.loadCommentsFromServer();
    setInterval(this.loadCommentsFromServer, this.props.pollInterval);
  },
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} />
        <CommentForm onCommentSubmit={this.handleCommentSubmit} />
      </div>
    );
  }
});

How can we implement optimistic UI with React?

Our application is now feature complete but it feels slow to have to wait for the request to complete before your comment appears in the list. We can optimistically add this comment to the list to make the app feel faster.

var CommentBox = React.createClass({
  loadCommentsFromServer: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  handleCommentSubmit: function(comment) {
    var comments = this.state.data;
    // Optimistically set an id on the new comment. It will be replaced by an
    // id generated by the server. In a production application you would likely
    // not use Date.now() for this and would have a more robust system in place.
    comment.id = Date.now();
    var newComments = comments.concat([comment]);
    this.setState({data: newComments});
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      type: 'POST',
      data: comment,
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        this.setState({data: comments});
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },
  getInitialState: function() {
    return {data: []};
  },
  componentDidMount: function() {
    this.loadCommentsFromServer();
    setInterval(this.loadCommentsFromServer, this.props.pollInterval);
  },
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} />
        <CommentForm onCommentSubmit={this.handleCommentSubmit} />
      </div>
    );
  }
});
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License