Reactive table

September 10th, 2013. Tagged: JavaScript, react

In the previous post I mentioned the pains, misery and suffering attached to creating and updating an HTML table in DOM land. Now let's see how you do this simple task in React.

Demo

React is all about components. So let's build one.

Let's call it Table (to avoid any confusion what the component is about).

var Table = React.createClass({
  /*stuff goeth here*/
});

You see that React components are defined using a regular JS object. Some properties and methods of the object such as render() have special meanings, the rest is upforgrabs.

<rant>createClass should've been named createComponent, but... naming is hard </rant>

Data, sweet data

A table is a way to neatly and orderly present some data. The data is front and center, the most important thing. And data is what you should focus on - retrieving, updating, saving, etc.

Once you have the data, you let React take care of the presentation part.

You want the UI to react to changes in the data. So you need a stateful component. Meaning one that has state. This is easier than it sounds.

State

Here's how to setup the obligatory getInitialState() method that gives you the initial state of the state.

var Table = React.createClass({
  getInitialState: function() {
    return {data: this.props.data};
  }
});

As you can see the state is an object. Simple as that, the old familiar {key: value} situation. In this case the state is just one property of the state object, appropriately named data.

The initial state comes from the data property I decided to use to initialize the component (your components can have properties which the users of the components can define, just like an HTML element can have attributes). This is optional but I think convenient.

Using the component

In order to use the new component, you go:

React.renderComponent(
  Table({data: [/*sweet data*/] }), 
  where);

Here where is the DOM node you point React to. In an all-react app, this is the only time you touch the DOM yourself. Then you let React run wild!

Render already

Now React knows about the data and can react to changes in it. The only missing piece is the render() method each component must implement. As you know all the UI is built entirely in JS.

In this case the rendering is about creating table and tbody and then looping through the data to create trs and tds. React gives you wrappers to these html elements via the React.DOM.* functions.

render: function () {
    return (
      React.DOM.table(null, React.DOM.tbody(null,
        this.state.data.map(function (row) {
          return (
            React.DOM.tr(null, 
              row.map(function (cell) {
                return React.DOM.td(null, cell);
              })
            )
          );
        })
      ))
    );
  }

As you see, you use array's map() to traverse this.state.data and do rows. Then another map() does the cells in each row. Easy enough.

All together now

You put the whole thing in an HTML file.

<div id="app"></div><!-- app goes here -->
 
<script src="build/react.min.js"></script> <!-- include React, ~18K gzipped-->
 
<script>

//
// create component
//
var Table = React.createClass({
  getInitialState: function() {
    return {data: this.props.data};
  },
  render: function () {
    return (
      React.DOM.table(null, React.DOM.tbody(null,
        this.state.data.map(function (row) {
          return (
            React.DOM.tr(null, 
              row.map(function (cell) {
                return React.DOM.td(null, cell);
              })
            )
          );
        })
      ))
    );
  }
});
 
//
// get data somehow
//
var data = [[1,2,3],[4,5,6],[7,8,9]];
 
//
// Render the component in the "app" DOM node
// giving it the initial data
//
var table = React.renderComponent(
  Table({data: data}),
  app);
 
</script>

That's all there is to it!

Updates

Data, like life, changes. What's a dev to do?

In React you simply pass the data to the rendered component and forget!

The component was assigned to the table var. So you do:

table.setState({data: [/* up-to-date data */] })

React then takes care of updating the DOM very very efficiently, only where necessary. If only one cell changes, only one is updated!

Go to the demo and play in the console, for example:

// single cells
data[0][0] = "baba"; table.setState({data: data});
data[1][1] = "bubu"; table.setState({data: data});
// different tables altogether
data = [[5,6],[2,3],[0,0]]; table.setState({data: data});
data = [[5,6,7,6,7,8],[1,1,1,1,2,3],[1,1,1,0,0,0]]; table.setState({data: data});

You can open your console tools and observe what part of the DOM is updated.

One more thing: JSX

How about that render() method, eh? Lots of function calls and parens to close? Enters JSX. Boom! It lets you write the all-familiar HTML (ok, XML) inside the JavaScript.

Here's how the rendering code will look like when you use JSX:

  <table><tbody>
    {this.state.data.map(function(row) {
      return (
        <tr>
          {row.map(function(cell) {
            return <td>{cell}</td>;
          })}
        </tr>);
    })}
  </tbody></table>

Now you obviously need to transform this code to the regular JavaScript calls you saw before. It's surprisingly easy and convenient, but I'll leave it for the next blog post. Meanwhile I leave you with the JSX docs. And remember, JSX is optional (but so darn convenient!), you can write the JS function calls yourself.

Yeah, and the react-to-dom init can look like:

var data = [[1,2,3],[4,5,6],[7,8,9]];
var table = React.renderComponent(
  <Table data={data}/>,
  app);

Now, that's so much more HTML-y.

Take care now, bye-bye then

And, just not to give you the wrong impression about React's capabilities with my ugly tables, here's a game called Wolfenstein with the rendering layer written in React: you can play here.

Tell your friends about this post on Facebook and Twitter

Sorry, comments disabled and hidden due to excessive spam.

Meanwhile, hit me up on twitter @stoyanstefanov