Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

This is where JSX can truly shine - rather than a mess of e('div', {}, e('div', {}, e('div', {}, ... ))), we can use the cleaner, more familiar syntax of <div><div><div>...</div></div></div>

Below, I have provided a few test files. To see these examples in action, simply copy them to your local machine.

You can then view them by navigating a browser to file:///path/to/example.index.html

Custom Components Written without JSX

...

Now, here is the same component converted to use JSX syntax - note that the browser can no longer directly interpret this code, which must be compiled or "transpiled" using Babel or something similar. Since we will need to compile the JSX down to CommonJS anyways, we can safely use ES6 syntax (such as the "let" keyword or arrow functions) without worrying about browser support, since the compiler should translate these shorthands to their equivalent CommonJS representation.

Notice that we are no longer passing around the values returned from React.createElement ... in fact, we don't even call explicitly call React.createElement at all:

Code Block
languagexml
titlereact-jsx.index.html
linenumberstrue
collapsetrue
    <!-- Load Bootstrap CSS from CDN -->
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">

    <!-- Load React from CDN -->
    <!-- Note: when deploying to production, replace "development.js" with "production.min.js". -->
    <script src="//unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="//unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <div id="react_test">Component failed to load</div>
    
    <!-- STOP! The contents of the following script tag can be compiled with the debugger console, located here: https://babeljs.io/en/repl -->
    <!-- See react-jsx-compiled.index.html for compiled browser-ready results -->
    <script>
        'use strict';

        let e = window.React.createElement;
        let ReactDOM = window.ReactDOM;

        // TypeScript used here - feels like a more formal JavaScript
        class MetadataTable extends React.Component {
            constructor(props) {
                super(props);
                this.state = {
                    headers: [
                        { key: 'name', label: 'Name' },
                        { key: 'type', label: 'Type' },
                        { key: 'lastUpdated', label: 'Last Updated' },
                        { key: 'value', label: 'Value' },
                        { key: 'additionalActions', label: 'More...' }
                    ],
                    data: [
                        { 'name': 0, 'value': 'hello'},
                        { 'name': 1, 'value': 'react'},
                        { 'name': 2, 'value': 'with'},
                        { 'name': 3, 'value': 'jsx'},
                    ],
                    newValue: ''
                };
            }

            getTableHeaders() {
                let tableHeaders = [];
                for(let i = 0; i < this.state.headers.length; i++) {
                    let header = this.state.headers[i];
                    let uniqueKey = 'table_header_' + header.key;

                    // Push table header cells into the header row
                    // Notice that we no longer return an HTML element here
                    tableHeaders.push({ 'key': uniqueKey, 'width': (100 / this.state.headers.length).toString() + '%', 'label': header.label });
                }

                // Accumulate and return table header data
                return tableHeaders;
            }

            getTableRowData() {
                let tableRows = [];
                for(let i = 0; i < this.state.data.length; i++) {
                    let item = this.state.data[i];
                    let uniqueKey = 'table_row_data_' + i.toString();
                    let rowData = [];

                    // Loop over selected headers to print table row values
                    for(let j = 0; j < this.state.headers.length; j++) {
                        let header = this.state.headers[j];
                        let uniqueKey = 'table_' + header.key.toString() + '_' + i.toString();

                        // Push table cell values into a "row"
                        // Notice that we no longer return an HTML element here
                        rowData.push({ 'key': uniqueKey, 'width': (100 / this.state.headers.length).toString() + '%', 'label': item[header.key] });
                    }

                    // Push the row of cell data into the table
                    // Notice that we no longer return an HTML element here
                    tableRows.push({ key: uniqueKey, 'data': rowData });
                }

                // Accumulate and return table row data
                return tableRows;
            }
            
            handleNewValueChange(event) {
                console.debug("OnChange Event:", event);
                this.setState({ 'newValue': event.target.value });
            }
            
            addNew(event) {
                console.debug("OnClick Event:", event);
                let data = this.state.data;
                data.push({ 'name': this.state.data.length, 'value': this.state.newValue });
                this.setState({ 'data': data });
                this.setState({ 'newValue': '' });
            }

            render() {
                // Re-fetch selected headers and row data
                let tableHeaders = this.getTableHeaders();
                let tableRows = this.getTableRowData();

                // Render the table data with React
                // NOTE: JSX syntax is returned - feels a bit like Scala
                return (
                    <div className='table-responsive'>
                        <table className='table table-condensed table-hover'>
                            <thead>
                                <tr>
                                    {tableHeaders.map((item, i) => {   
                                        // For each column header in tableHeaders         
                                        return (<th key={item.key} width={item.width}>{item.label}</th>);
                                    })}
                                </tr>
                            </thead>
                            <tbody>
                                {tableRows.map((row, i) => {   
                                    // For each row in tableRows  
                                    return (
                                        <tr key={row.key}>
                                            {row.data.map((cell, j) => {   
                                                // For each cell in the row        
                                                return (<td key={cell.key} width={cell.width}>{cell.label}</td>);
                                            })}
                                        </tr>
                                    );
                                })}
                                <tr>
                                    <td>
                                        <input placeholder='Dummy text input...' value={this.state.newValue} onChange={this.handleNewValueChange} />
                                    </td>
                                    <td>
                                        <button className='btn btn-xs btn-primary' onClick={(event) => { this.addNew(event, this.state.newValue) }}>Add new</button>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                );
            }
        }

        console.log("Rendering!");
        let domContainer = document.querySelector('#react_test');
        ReactDOM.render(
            <MetadataTable />,
            domContainer
        );
        console.log("Rendered!");
    </script>

...

Compilation Step

JSX requires us to compile down to JavaScript - to do this, we simply pass our source code through a Babel compiler.

...

The result of the compilation might look as follows - Note that the compiler has replaced the JSX parts in the code below with nested calls to React.createElement().

This compiled output can be then , and can be immediately consumed by your browser:

Code Block
languagexml
titlereact-jsx-compiled.index.html
linenumberstrue
collapsetrue
    <!-- Load Bootstrap CSS from CDN -->
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">

    <!-- Load React from CDN -->
    <!-- Note: when deploying to production, replace "development.js" with "production.min.js". -->
    <script src="//unpkg.com/react@16/umd/react.development.js" crossorigin></script>
    <script src="//unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <div id="react_test">Component failed to load</div>

    <!-- STOP! The contents of the following script tag were compiled using the debugger console, located here: https://babeljs.io/en/repl -->
    <!-- See react-jsx.index.html for original compilation source -->
    <script>
        'use strict';
        
        var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
        
        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
        
        function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
        
        function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
        
        var e = window.React.createElement;
        var ReactDOM = window.ReactDOM;
        
        // TypeScript used here - feels like a more formal JavaScript
        
        var MetadataTable = function (_React$Component) {
            _inherits(MetadataTable, _React$Component);
        
            function MetadataTable(props) {
                _classCallCheck(this, MetadataTable);
        
                var _this = _possibleConstructorReturn(this, (MetadataTable.__proto__ || Object.getPrototypeOf(MetadataTable)).call(this, props));
        
                _this.state = {
                    headers: [{ key: 'name', label: 'Name' }, { key: 'type', label: 'Type' }, { key: 'lastUpdated', label: 'Last Updated' }, { key: 'value', label: 'Value' }, { key: 'additionalActions', label: 'More...' }],
                    data: [{ 'name': 0, 'value': 'hello' }, { 'name': 1, 'value': 'react' }, { 'name': 2, 'value': 'with' }, { 'name': 3, 'value': 'jsx' }],
                    newValue: ''
                };
                return _this;
            }
        
            _createClass(MetadataTable, [{
                key: 'getTableHeaders',
                value: function getTableHeaders() {
                    var tableHeaders = [];
                    for (var i = 0; i < this.state.headers.length; i++) {
                        var header = this.state.headers[i];
                        var uniqueKey = 'table_header_' + header.key;
        
                        // Push table header cells into the header row
                        //tableHeaders.push(e('th', { 'key': uniqueKey, 'width': `${(100 / this.state.headers.length}%` }, `${).toString() + '%', 'label': header.label }`));
                    }
    tableHeaders.push({ 'key': uniqueKey, 'width': (100 / this.state.headers.length).toString() + '%', 'label': header.label });
                    }
        
        
                     // Accumulate and return table header data
                    return tableHeaders;
                }
            }, {
                key: 'getTableRowData',
                value: function getTableRowData() {
                    var tableRows = [];
                    for (var i = 0; i < this.state.data.length; i++) {
                        var item = this.state.data[i];
                        var uniqueKey = 'table_row_data_' + i.toString();
                        var rowData = [];
        
                        // Loop over selected headers to print table row values
                        for (var j = 0; j < this.state.headers.length; j++) {
                            var header = this.state.headers[j];
                            var _uniqueKey = 'table_' + header.key.toString() + '_' + i.toString();
        
                            // Push table cell values into a "row"
                            //rowData.push(e('td', { 'key': uniqueKey, 'width': `${100 / this.state.headers.length}%`}, `${item[header.key]}`));
                            rowData.push({ 'key': _uniqueKey, 'width': (100 / this.state.headers.length).toString() + '%', 'label': item[header.key] });
                        }
        
                        // Push the row of cell data into the table
                        //tableRows.push(e('tr', { 'key': uniqueKey }, rowData));
                        tableRows.push({ key: uniqueKey, 'data': rowData });
                    }
        
                    // Accumulate and return table row data
                    return tableRows;
                }
            }, {
                key: 'handleNewValueChange',
                value: function handleNewValueChange(event) {
                    console.debug("OnChange Event:", event);
                    this.setState({ 'newValue': event.target.value });
                }
            }, {
                key: 'addNew',
                value: function addNew(event) {
                    console.debug("OnClick Event:", event);
                    var data = this.state.data;
                    data.push({ 'name': this.state.data.length, 'value': this.state.newValue });
                    this.setState({ 'data': data });
                    this.setState({ 'newValue': '' });
                }
            }, {
                key: 'render',
                value: function render() {
                    var _this2 = this;
        
                    // Re-fetch selected headers and row data
                    var tableHeaders = this.getTableHeaders();
                    var tableRows = this.getTableRowData();
        
                    // Render the table data with React
                    // NOTE: JSX syntax is returned - feels a bit like Scala
                    return React.createElement(
                        'div',
                        { className: 'table-responsive' },
                        React.createElement(
                            'table',
                            { className: 'table table-condensed table-hover' },
                            React.createElement(
                                'thead',
                                null,
                                React.createElement(
                                    'tr',
                                    null,
                                    tableHeaders.map(function (item, i) {
                                        // For each column header in tableHeaders         
                                        return React.createElement(
                                            'th',
                                            { key: item.key, width: item.width },
                                            item.label
                                        );
                                    })
                                )
                            ),
                            React.createElement(
                                'tbody',
                                null,
                                tableRows.map(function (row, i) {
                                    // For each row in tableRows  
                                    return React.createElement(
                                        'tr',
                                        { key: row.key },
                                        row.data.map(function (cell, j) {
                                            // For each cell in the row        
                                            return React.createElement(
                                                'td',
                                                { key: cell.key, width: cell.width },
                                                cell.label
                                            );
                                        })
                                    );
                                }),
                                React.createElement(
                                    'tr',
                                    null,
                                    React.createElement(
                                        'td',
                                        null,
                                        React.createElement('input', { placeholder: 'Dummy text input...', value: this.state.newValue, onChange: this.handleNewValueChange })
                                    ),
                                    React.createElement(
                                        'td',
                                        null,
                                        React.createElement(
                                            'button',
                                            { className: 'btn btn-xs btn-primary', onClick: function onClick(event) {
                                                    _this2.addNew(event, _this2.state.newValue);
                                                } },
                                            'Add new'
                                        )
                                    )
                                )
                            )
                        )
                    );
                }
            }]);
        
            return MetadataTable;
        }(React.Component);
        
        console.log("Rendering!");
        var domContainer = document.querySelector('#react_test');
        ReactDOM.render(React.createElement(MetadataTable, null), domContainer);
        console.log("Rendered!");
    </script>

...