Categories

React Grid Component

Posted on: January 4, 2015 by Dimitar Ivanov

Introduction

In this tutorial I'm going to show you how to use Facebook's React library for building better user interfaces. For this purpose I will use the JSX syntax to write the react grid component. So let's get started.

First create a examples/index.html with the following contents.

<!DOCTYPE html>
<html>
    <head>
        <script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
        <script src="//fb.me/react-0.12.2.min.js"></script>
        <script src="//fb.me/JSXTransformer-0.12.2.js"></script>
        <link href="grid.css" rel="stylesheet">
    </head>
    <body>
        <div id="grid"></div>
        <script src="../src/grid.js"></script>
    </body>
</html>

Then create src/grid.js. This is the place where your React JSX code resides.

React.render(
    <Table url="ajax.php?action=grid" />,
    document.getElementById("grid")
);

How to transform JSX

Since we intend to use this code in browser, we need to translate the JSX to plain JavaScript, so the browser can understand it. There are two ways to achieve this:

  • In-Browser Transform - that's what the JSXTransformer do in the examples/index.html. Use this method in dev environment, if you'd like. But in production you should use the offline transform for better performance.
  • Offline Transform - first install the command-line tools:
npm install -g react-tools

Then start a watcher to translate your JSX to JavaScript:

jsx --watch src/ build/

The above command will auto-generate build/grid.js. If you choose this method, remove the JSXTransformer and replace src/grid.js with build/grid.js at the examples/index.html.

<!DOCTYPE html>
<html>
    <head>
        <script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
        <script src="//fb.me/react-0.12.2.min.js"></script>
        <link href="grid.css" rel="stylesheet">
    </head>
    <body>
        <div id="grid"></div>
        <script src="../build/grid.js"></script>
    </body>
</html>

The Source Code

It's time to go deep into JSX. Let's see the src/grid.js. The html table in the code is separate into several react components.

var Table = React.createClass({
    loadData: function () {
        $.ajax({
            url: this.props.url,
            data: {
                page: this.state.data.paginate.page,
                row_count: this.state.data.paginate.row_count,
                col_name: this.state.data.paginate.col_name,
                direction: this.state.data.paginate.direction
            },
            dataType: "json",
            success: function (data) {
                this.setState({paginate: data.paginate});
                this.setState({data: data});
            }.bind(this),
            error: function (xhr, status, err) {
                console.log(this.props.url, status, err.toString());
            }.bind(this)
        });
    },
    getInitialState: function () {
        return {
            data: {
                columns: [],
                items: [],
                paginate: {
                    page: 1,
                    pages: 1,
                    offset: 0,
                    row_count: 5,
                    total: 0,
                    col_name: "Name",
                    direction: "asc"
                }
            }
        };
    },
    componentDidMount: function () {
        this.loadData();
    },
    getFirst: function () {
        this.setState({paginate: $.extend(this.state.paginate, {
            page: 1
        })});
        this.loadData.call(this);
    },
    getPrev: function () {
        this.setState({paginate: $.extend(this.state.paginate, {
            page: this.state.paginate.page - 1
        })});
        this.loadData.call(this);
    },
    getNext: function () {
        this.setState({paginate: $.extend(this.state.paginate, {
            page: this.state.paginate.page + 1
        })});
        this.loadData.call(this);
    },
    getLast: function () {
        this.setState({paginate: $.extend(this.state.paginate, {
            page: this.state.paginate.pages
        })});
        this.loadData.call(this);
    },
    changeRowCount: function (e) {
        var el = e.target;
        this.setState({paginate: $.extend(this.state.paginate, {
            row_count: el.options[el.selectedIndex].value
        })});
        this.loadData.call(this);
    },
    sortData: function (e) {
        e.preventDefault();
        var el = e.target,
            col_name = el.getAttribute("data-column"),
            direction = el.getAttribute("data-direction");
        this.setState({paginate: $.extend(this.state.paginate, {
            col_name: col_name,
            direction: direction
        })});
        this.loadData.call(this);
    },
    render: function () {
        return (
            <table className="r-table">
                <Head data={this.state.data} onSort={this.sortData} />
                <Body data={this.state.data} />
                <Foot data={this.state.data} onFirst={this.getFirst} onPrev={this.getPrev} onNext={this.getNext} onLast={this.getLast} onChange={this.changeRowCount} onRefresh={this.loadData}/>
            </table>
        );
    }
});

var Head = React.createClass({
    render: function () {
        var that = this;
        return (
            <thead>
                <tr>
                {this.props.data.columns.map(function (column, i) {
                    return <HeadCell key={i} column={column} direction={that.props.data.paginate.direction} onSort={that.props.onSort} />
                })}
                </tr>
            </thead>
        );
    }
});

var Foot = React.createClass({
    render: function () {
        return (
            <tfoot>
                <tr>
                    <td colSpan={this.props.data.columns.length}>
                        <div className="r-paginate">
                            <Button text="<< First" onClick={this.props.onFirst} disabled={this.props.data.paginate.page === 1} />
                            <Button text="< Prev" onClick={this.props.onPrev} disabled={this.props.data.paginate.page === 1} />
                            <Button text="Next >" onClick={this.props.onNext} disabled={this.props.data.paginate.page === this.props.data.paginate.pages} />
                            <Button text="Last >>" onClick={this.props.onLast} disabled={this.props.data.paginate.page === this.props.data.paginate.pages} />
                            <Button text="Refresh" onClick={this.props.onRefresh} disabled={false} />
                        </div>
                        <div className="r-rowcount">
                        <select onChange={this.props.onChange} name="row_count">
                            <Option value="5" />
                            <Option value="10" />
                        </select> rows per page
                        </div>
                        <div className="r-stats">
                            <span className="">Page {this.props.data.paginate.page} of {this.props.data.paginate.pages}</span>
                        </div>
                    </td>
                </tr>
            </tfoot>
        );
    }
});

var Button = React.createClass({
    render: function () {
        return (
            <button type="button" onClick={this.props.onClick} disabled={this.props.disabled}>{this.props.text}</button>
        );
    }
});

var Option = React.createClass({
    render: function () {
        return (
            <option value={this.props.value}>{this.props.value}</option>
        );
    }
});

var Body = React.createClass({
    render: function () {
        var that = this;
        return (
            <tbody>
            {this.props.data.items.map(function(item, i) {
                return <Row key={i} item={item} columns={that.props.data.columns} />
            })}
            </tbody>
        );
    }
});

var Row = React.createClass({
    render: function () {
        var that = this;
        return (
            <tr>
            {this.props.columns.map(function (column, i) {
                return <Cell key={i} column={column} value={that.props.item[column.key]} />
            })}
            </tr>
        );
    }
});

var HeadCell = React.createClass({
    render: function () {
        return (
            <th><a href="#" data-column={this.props.column.key} data-direction={this.props.direction === "desc" ? "asc" : "desc"} role="button" tabIndex="0" onClick={this.props.onSort}>{this.props.column.label}</a></th>
        );
    }
});

var Cell = React.createClass({
    render: function () {
        return (
            <td>{Draw(this.props.column, this.props.value)}</td>
        );
    }
});

var Draw = function (column, value) {
    switch (column.type) {
        case 'Number':
            return value;
            break;
        case 'String':
            return value;
            break;
        case 'Image':
            return React.createElement('img', {src: value}, null);
            break;
    }
}

React.render(
    <Table url="ajax.php?action=grid" />,
    document.getElementById("grid")
);

Demo

Play with the react grid demo on JSFiddle.net

Conclusion

If you want to write modular and reusable react components you may want to read more about browserify and webpack. Source code is available at GitHub.

See also
Share this post

If you have questions about the react grid component, please leave a comment below. Thanks so much for reading! And do not shy to share this article.


1 Comment

Nagendra
Is there any configuration files required to write React Js code in JavaScript file.
If Yes, can you please help me on that.

Thank you.
Nagendra

Leave a comment

Captcha