Ext JS to React: Basic Grid

   JavaScript
Ext JS to React: Basic Grid

This is part of the Ext JS to React blog series.

The grid is the defining component of the Ext JS framework. When we think of Ext JS, the first image that comes to mind is its grid and for good reason. Ext JS’s grid is unmatched in features and quality. However, depending on the needs of your application, you may consider writing your own grid. You may be surprised at how easy it is with React.

Sample data generator

Ext JS relied heavily on its data package for its views consuming that data. While a mature data module is great for complex modeling, for most views we can just furnish the bits that are needed. For this and subsequent grid blogs, we will use the following data module:

const companies = [
  'Airconix', 'Qualcore', 'Hivemind', 'Thermolock', 'Sunopia'
];
const firstNames = [
  'Raymond', 'Vernon', 'Dori', 'Jason', 'Rico'
];
const lastNames = [
  'Neal', 'Dunham', 'Seabury', 'Pettey', 'Muldoon'
];

const random = (array) => array[ Math.floor(Math.random() * array.length) ];

const dataSync = ({ num = 50, startRow = 0, total = 50000 } = {}) => {
  const data = [];

  for (let i = 0; i < num; i++) {
    const company = random(companies);
    const first = random(firstNames);
    const last = random(lastNames);

    data.push({
      id: i + startRow,
      name: `${first} ${last}`,
      company,
      email: `${first.toLowerCase()}.${last.toLowerCase()}@${company.toLowerCase()}.com`
    });
  }

  return { data, total };
};

const dataAsync = async ({ delay = 500, num, startRow, total } = {}) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(dataSync({ num, startRow, total }));
    }, delay);
  });
};

export default dataSync;
export { dataAsync }; 

Note: In the data module above, you’ll see that there are two export statements. The export default dataSync statement indicates that any class importing from the file with this module will receive the dataSync object. The name of the object imported can be whatever suits the importing class as we see in the import getData from './data' example statement below. The export { dataAsync } statement enables another class to request the dataAsync function by name. For more information on the ECMAScript 2015 (ES6) export convention, checkout the named export and default export sections on the MDN export guide.

Lightweight React grid class

We’ve long heard that people love Ext JS’s grid, but sometimes they need a lightweight version. With Ext JS, there simply is no lightweight grid. It has to support so many features yet come to an agreement regarding the minimal functionality people would expect. If you want to display data in a tabular format, but don’t need all the functionality under the sun, you can easily write a simple React component to be just that:

import React, { Component } from 'react';
import getData from './data';

const { data } = getData();

class Grid extends Component {
  render () {
    const { className } = this.props;

    return (
      <table className={`grid ${className ? className : ''}`}>
        <thead>
          <tr>
            <th>Name</th>
            <th>Company</th>
            <th>Email</th>
          </tr>
        </thead>
        <tbody>
          {
            data.map(item => (
              <tr key={item.id}>
                <td>{item.name}</td>
                <td>{item.company}</td>
                <td>{item.email}</td>
              </tr>
            ))
          }
        </tbody>
      </table>
    );
  }
}

export default Grid; 


Ext JS to React: Basic Grid, Lightweight Grid


This component shows our data in 3 columns in a table that is as lightweight as you can get. The headers will be bolded automatically as the default styling of the <th> nodes. With a few additional simple styles, you can set the background color on the header along with row and column lines. By using a small React component class and a few styles rules, you now have a lightweight grid!

Sorting the React grid

Currently, we have a lightweight grid that simply displays our data. If the data arrives unsorted or you want to allow users to sort a column, then we need to add sorting functionality to the grid. To enable sorting, we’ll need to make a couple of changes:

  1. Add an onClick listener to the headers in order to set the sort state when a user clicks on the header. This will also toggle the existing sort state between ascending and descending.
  2. Display sort status in the headers
  3. Sort the data before creating the grid rows

The updated grid would then look like:

import React, { Component } from 'react';
import getData from './data';

const { data } = getData();

class Grid extends Component {
  state = {}

  render () {
    const { className, data } = this.props;
    const { sort } = this.state;

    return (
      <table className={`grid ${className ? className : ''}`}>
        <thead>
          <tr>
            <th onClick={this.handleHeaderClick.bind(this, 'name')}>
              Name{this.getSort('name', sort)}
            </th>
            <th onClick={this.handleHeaderClick.bind(this, 'company')}>
              Company{this.getSort('company', sort)}
            </th>
            <th onClick={this.handleHeaderClick.bind(this, 'email')}>
              Email{this.getSort('email', sort)}
            </th>
          </tr>
        </thead>
        <tbody>
          {
            this.sortData(data, sort).map(item => (
              <tr key={item.id}>
                <td>{item.name}</td>
                <td>{item.company}</td>
                <td>{item.email}</td>
              </tr>
            ))
          }
        </tbody>
      </table>
    );
  }

  getSort (dataIndex, sort) {
    return sort && sort.dataIndex === dataIndex ?
      ` (${sort.direction})` :
      null;
  }

  handleHeaderClick = (dataIndex) => {
    const { sort } = this.state;

    const direction = sort && sort.dataIndex === dataIndex ?
      (sort.direction === 'ASC' ? 'DESC' : 'ASC') :
      'ASC';

    this.setState({
      sort: {
        dataIndex, direction
      }
    });
  }

  sortData (data, sort) {
    if (sort) {
      const { dataIndex, direction } = sort;
      const dir = direction === 'ASC' ? 1 : -1;

      return data.slice().sort((A, B) => {
        const a = A[ dataIndex ];
        const b = B[ dataIndex ];

        if (a > b) {
          return 1 * dir;
        }

        if (a < b) {
          return -1 * dir;
        }

        return 0;
      });
    }

    return data;
  }
}

export default Grid; 

React grid class instantiated

To instantiate the Grid, we pass the data prop with the data set from our example above:

import React, { Component } from 'react';
import Grid from './Grid';
import getData from './data';

const { data } = getData();

class App extends Component {
  render() {
    return (
      <Grid data={data} />
    );
  }
}

export default App; 


Ext JS to React: Basic Grid, Sorted Grid



Now we have a very lightweight, sortable grid and all in less than 90 lines of code!

Conclusion

You may have never thought about writing your own grid when coming from Ext JS. If all you need is a lightweight grid with optional sorting, try writing your own with React. While you will likely require a more feature-rich grid selectively throughout your application, it’s a great idea to start with vanilla React. This will help your understanding of how React works versus Ext JS. In the next blog, we will look at a third-party grid component and how to use selection models like you would with Ext JS.


Like What You See?

Got any questions?