Click here to Skip to main content
15,881,938 members
Articles / Web Development / React
Article

The Best React Data Grid: FlexGrid

5 Dec 2017CPOL15 min read 22.9K   1  
In this article I walk through the steps to add React’s best data grid – FlexGrid – to your web app using only Facebook’s Create React App, Wijmo, and a few lines of JavaScript.

This article is in the Product Showcase section for our sponsors at CodeProject. These articles are intended to provide you with information on products and services that we consider useful and of value to developers.

While React has only recently risen to front-end web development ubiquity, data grids have been one of the most common visual software elements since the advent of UIs themselves. The popularity of React and commodity of data grids means there are a plethora of options for adding one to your application. But among open source, commercial, and homegrown grids, one solution stands out above the rest.

The FlexGrid included with GrapeCity’s Wijmo is the best plug-and-play data grid for React.

Image 1

In this article, I’ll talk about:

  1. What makes data grids so useful as UI elements
  2. How FlexGrid rises above all other data grid components
  3. Some examples to show you how easy it is to use the FlexGrid in a React web application

Really, using the FlexGrid with React is so easy, it’s fun! If you don’t believe me, download Wijmo for free and follow along!

Why Data Grids?

Even if you’re not a developer, you’re probably familiar with the paradigm of a data grid. Grouping and sorting data isn’t just a stress-reliever for the meticulous, it’s human nature. Data grids appeal to the instinct that drives all of us to take in and understand as much data as possible, as quickly as possible.

The usefulness and universality of data grids has allowed them to pervade throughout time, but not without some evolution. Undoubtedly, the advent of screens in the digital age has changed the data grid more than anything else.

Now, grids are more useful than ever not only for displaying data, but also for editing it. Could you imagine creating a sales report or monthly budget in Microsoft Word? With automatic conveniences like sorting and autocomplete, plus advanced features like custom formulas, Microsoft Excel would be the natural choice for most users.

Beyond the helpful and time-saving features, most users would naturally choose Excel because they used it the first time they ever experienced a data grid on a PC. Excel is familiar. Any good UX designer or business analyst will tell you: educating users is difficult and expensive. This is one of the greatest advantages of data grids in general - they are intuitive.

Although Excel has become the most common example of software using a data grid, the paradigm has proliferated throughout the software field. As web apps continue to replace the traditional native applications we’re used to, it only makes sense that data grids would make their way to web app UIs as well. At GrapeCity, we’ve found that industries across the board are including data grid UI components in new web app projects.

Whether you’re building an app to track manufacturing stats or analyze financial data, I’m willing to bet you’ve already thought about the intuitiveness a data grid would add. And, as we’ll see, Wijmo’s FlexGrid can maximize bang for your buck by providing the best data grid UX with the best performance.

Image 2

Wijmo FlexGrid: The Best Data Grid Component Solution

So, now we know that the biggest advantage to using data grids is that they’re inherently intuitive. That’s a huge plus for the end-user experience, but what about the developers writing the application? Creating a data grid UI component from scratch is certainly no small task for any development team. It’s quite possible that building a data grid for your application is just as expensive as educating users about a different UI component you create.

Fortunately, that’s where the FlexGrid comes in! Just add the FlexGrid to your application using Wijmo scripts and attach it to a data source. Voilà! Just a couple of simple steps and your application now includes a full-functioning data grid! If the greatest advantage of using a data grid is that it creates a better end-user experience, the benefit of using a third-party data grid is that it creates a better development experience.

In just a moment, I’ll use some examples to show how Wijmo’s FlexGrid offers the biggest boost in efficiency to development workflow with out-of-the-box React interoperability. But first, let’s explore what sets the FlexGrid apart as a pluggable data grid component in general.

The FlexGrid Packages Performance, Extensibility and Familiarity with a Small Footprint

Creating React’s most powerful data grid component didn’t happen overnight. In fact, FlexGrid was first written in 1996 using C++ for Visual Basic (and even shipped as part of Visual Studio). It has since evolved and been refined in many platforms, the most recent being JavaScript.

Decades of refinement have resulted in a high-performance grid with an extensively documented API. But there’s a reason the FlexGrid has retained its name all this time. Stressing our "Flex Philosophy" is a top priority for us.

Controls should include only the key set of features required and offer everything else through an extensibility model.

Features like sorting, grouping and editing are built-in, while other bells and whistles are offered as optional extensions of FlexGrid. This keeps the controls small and fast and gives both us and our customers the ability to build custom features.

Performance

Many React developers choose the framework for its emphasis on out-of-the-box performance and lightweight nature. FlexGrid ensures you don’t have to sacrifice either of those advantages by also focusing on maintaining speed and a small footprint.

The Wijmo development team constantly benchmarks FlexGrid against other third-party data grid controls to ensure we’re delivering the fastest grid available. We also keep the footprint of FlexGrid small by requiring no external dependencies. As a matter of fact, the FlexGrid only adds about 25K (gzipped) to your application. (You can run the benchmark yourself using Wijmo’s free online demo.)

Lastly, similar to React’s handling of the component DOM, FlexGrid virtualizes all of its child elements. You can load millions of rows of data in a matter of seconds - all on the client. This is extremely important to React interoperability. FlexGrid is the most performant React data grid because it works seamlessly with React’s highly optimized virtualization algorithms.

Familiarity

FlexGrid bases nearly all of its interaction behavior on Excel, which - as stated previously - is probably the most common grid/table used by any end-user. People expect certain behaviors when scrolling, clicking, and especially using key commands (including clipboard functions).

Instead of inventing our own behaviors, we mimic Excel’s, and end users immediately feel comfortable using our grid. Surprisingly, many other grids either invent their own behavior or don’t fully support keyboard actions or scrolling. For example, if you select a row in a grid and hold the down arrow key, many grids don’t function as you’d expect.

Accessibility

We’re extremely excited that our FlexGrid control now offers full accessibility support for the most popular modern browsers. Making a control as dense and complex as a data grid accessible is a conundrum at best, and no other grid components offer the FlexGrid’s level of framework interoperability and accessibility.

If you’d like to check out the details of how FlexGrid makes your data accessible to everyone, read our blog post about accessibility development. Whether you have a standalone accessibility team or not, you can ensure that your app complies to the latest standards by using FlexGrid.

FlexGrid and React: A Match Made in Web Development Heaven

FlexGrid does much more than seamlessly integrate with React’s powerful, high-performance render engine. Our grid component makes it fun, easy and natural to add data grids to your web app UIs.

Now for the fun stuff! Let’s take a look at some specific example that illustrate the advantages of FlexGrid’s React interoperability.

Up and Running in Seconds with create-react-app

Wijmo ships with CommonJS node modules for all of its components, including the FlexGrid! And since Wijmo is written with an interoperability layer for React, you can import FlexGrid just like any other React class component.

All of this means you can create a React project and add FlexGrid with just a few simple steps. Thanks to Facebook’s infinitely useful create-react-app project, you don’t even need to worry about bootstrapping or tooling setup.

To illustrate, let’s run through a simple example of setting up FlexGrid in a Create React App project. Here’s a sneak peek of what the finished product will look like:

Image 3

For example’s sake, let’s say you already followed the steps to install create-react-app and you downloaded Wijmo to C:\Users\dev\wijmo.

In the C:\Users\dev directory, here’s all you would need to do to create a project and add Wijmo:

create-react-app grid-ui
cd grid-ui
npm install --save ../wijmo/C1Wijmo/NpmImages/wijmo-commonjs-min

Congratulations! Now you have a fully bootstrapped React app with Wijmo at your disposal. If you want to access the FlexGrid component, just add this line near the top of src/App.js:

JavaScript
import { FlexGrid } from 'wijmo/wijmo.react.grid';

That’s all, folks! You now have complete access to FlexGrid and its API, and you can even use it directly in JSX markup! Let’s find out how to do that next.

Declare Grids in JSX Markup

Continuing with the create-react-app example outlined above, let’s see how we can actually render a FlexGrid now that we’ve imported the Wijmo code into the App component.

There will be some default JSX markup inside of the App class’ render() function. Go ahead and delete that default JSX, and replace it with the following:

HTML
<div className="App">
    <FlexGrid
      itemsSource={this.state.data}
      className="grid"
    />
</div>

For a 5-line code snippet, there is a lot going on here. Before I talk about what makes declaring components in markup so important, let’s finish off the example. Can you spot what we’re missing to properly load up the FlexGrid?

You may have noticed that the itemsSource FlexGrid property references the App component state. Since there currently is no default state defined for App, the FlexGrid will be blank! To fix this, simply add the following code within the App class component:

JavaScript
state = {
    data: [
      {
        id: 0,
        name: 'Christian',
        job: 'React Dev',
      },
      {
        id: 1,
        name: 'Connor',
        job: 'Front-End Web Dev',
      },
      {
        id: 2,
        name: 'Sarah',
        job: 'Chemist',
      },
    ],
  };

Now go ahead and fire up your app by running yarn start (or npm start) and behold the FlexGrid in all its glory! Before moving on to exploring some of the more interesting features offered by FlexGrid in React, think about the implications of being able to entirely declare a complex data grid control in markup.

If you just can’t wait to see this app in action, check out the live sample on Glitch.

Declarative markup is ideal for following the MVVM design pattern, and we can fully configure our components within the view (markup). FlexGrid supports declaring its entire API using natural, unextended JSX markup. You can set properties, attach events, and even configure child components (like columns) entirely in markup.

You might have already guessed that there’s something special about the FlexGrid’s itemsSource being connected to the component state. More on that soon…

Easily Bind FlexGrid API Events to Component Functions

Since it was so easy to attach FlexGrid properties to component state, it follows that attaching FlexGrid events to handler functions should be a breeze! If that’s what you were thinking after reading the last section, you’re absolutely correct! Just as data from the App component’s state could be passed to the FlexGrid via props, component functions can be passed as handlers for FlexGrid events.

To demonstrate, let’s add a new feature to our example. Since we already have a working FlexGrid with some data, we have access to dozens of events that the FlexGrid API supports out-of-the-box. We’ll take advantage of two of those events - beginningEdit and cellEditEnded - to display a descriptive message when a user starts editing a cell in the grid.

Before we move any further, however, double-check to make sure the code for your App component (in App.js) looks like this:

JavaScript
import React, { Component } from 'react';

import { FlexGrid } from 'wijmo/wijmo.react.grid';

import logo from './logo.svg';
import './wijmo.min.css';
import './App.css';

class App extends Component {
  state = {
    users: [
      {
        id: 0,
        name: 'Christian',
        job: 'React Dev',
      },
      {
        id: 1,
        name: 'Connor',
        job: 'Front-End Web Dev',
      },
      {
        id: 2,
        name: 'Sarah',
        job: 'Chemist',
      },
    ],
  };

  render() {
    return (
      <div className="App">
        <FlexGrid
          itemsSource={this.state.users}
          className="grid"
        />
      </div>
    );
  }
}

export default App;

Now that we’re on the same page, let’s press ahead and add our edit-tracking feature! First, we’ll need to add some new properties to the App component’s state to track whether or not a cell is being edited along with the coordinates of the cell in edit mode. Go ahead and add these two properties to the App class’ state property:

JavaScript
cellEdit: false, // whether or not any cell is in edit mode
editingCell: '', // a string representation of the coordinates for the cell being edited

By default, when the app loads, we assume it isn’t possible for a cell to be in edit mode. Next, we should add a textual element that’s visible when a cell is in edit mode, and hidden when no cells are being edited. To give the user additional context, the text in the element will include the coordinates of the cell they’re editing.

To make sure our helpful, informative text is noticeable, we’ll place it in an <h2> tag. Add the following directly below the FlexGrid JSX markup:

HTML
<h2 style={{ display: this.state.cellEdit ? 'block' : 'none' }}>
  Currently editing cell at {this.state.editingCell}
</h2>

All of that code is pretty fundamental React - it was a piece of cake, right?! Now for the big finale…connecting the new state properties we created to FlexGrid events. Surely this must require some complex observable handlers or React anti-patterns, right?

Nope! We can hook up the FlexGrid’s events to component class functions just like you’d expect from a React component you’ve written yourself. First, add the handler functions to the App class:

JavaScript
beginEdit(args) {
    // Cell is now being edited - cell coords are passed in args object
    this.setState({
      cellEdit: true,
      editingCell: `${args.col}, ${args.row}`,
    });
}

finishEdit() {
    // Cell edit was committed or canceled - make sure the editing alert is hidden
    this.setState({
      cellEdit: false,
    });
}

Again, some pretty straightforward React component functions designed to update state based on whether or not a cell is being edited. Note the args object passed to the beginEdit function. The FlexGrid will automatically pass relevant context to all of its event handlers. To figure out what information is passed for a specific event, consult the extensive FlexGrid API documentation.

Now, to finish off this handy new feature, we have to tell the FlexGrid to actually call these event handlers. Fortunately, because the FlexGrid is a native React component, we can easily hook up these handlers in markup. Just add these properties to the FlexGrid declaration:

HTML
beginningEdit={(sender, args) => {
    this.beginEdit(args);
}}
cellEditEnded={() => {
    this.finishEdit();
}}

When all is said and done, your App class should look like this:

JavaScript
class App extends Component {
  state = {
    users: [
      {
        id: 0,
        name: 'Christian',
        job: 'React Dev',
      },
      {
        id: 1,
        name: 'Connor',
        job: 'Front-End Web Dev',
      },
      {
        id: 2,
        name: 'Sarah',
        job: 'Chemist',
      },
    ],
    cellEdit: false,
    editingCell: '',
  };

  beginEdit(args) {
    this.setState({
      cellEdit: true,
      editingCell: `${args.col}, ${args.row}`,
    });
  }

  finishEdit() {
    this.setState({
      cellEdit: false,
    });
  }

  render() {
    return (
      <div className="App">
        <FlexGrid
          itemsSource={this.state.users}
          className="grid"
          beginningEdit={(sender, args) => {
            this.beginEdit(args);
          }}
          cellEditEnded={() => {
            this.finishEdit();
          }}
        />
        <br />
        <h2 style={{ display: this.state.cellEdit ? 'block' : 'none' }}>
          Currently editing cell at {this.state.editingCell}
        </h2>
      </div>
    );
  }
}

When you’re all set, run your app and play around with it in the browser! When you’re ready to be amazed, simply double-click on any cell in the FlexGrid. Tada! The beginningEdit event is immediately dispatched and the App component’s state is updated, showing the label with the proper cell coordinates. If you click outside of the grid, the label should disappear.

Automatically Update FlexGrid with Data-Binding

Now that our app handles displaying some 2D data in a grid and responding to user edit events, maybe it would be helpful to provide a form for users to add someone to the list of data. Providing a form to update the state shouldn’t be too hard, but how can we make sure that change propagates to the FlexGrid? Remember taking note of the state-bound itemsSource FlexGrid property earlier? If you guessed that passing a state property to the FlexGrid automatically provided two-way data binding, you are correct!

Since the grid’s data is already bound to a state property, all the app needs to do is update that property, and the FlexGrid will update accordingly. Since each user in our system has two unique characteristics - a name and a job - add two new corresponding properties to the App class’ state:

JavaScript
nameInput: '',
jobInput: '',

Next, add a couple of quick functions that update the state with input from the ‘add user’ form and allow the App component to add a new object to the stateful users array:

JavaScript
addUser() {
    if (this.state.nameInput.length === 0 || this.state.jobInput.length === 0) {
      return;
    }
    this.setState({
      users: [
        // Create a new array, but keep the previous users using ES2015 spread operator
        ...this.state.users,
        // Add the new user to the array
        {
          id: this.state.users.length,
          // Use the input values saved in state
          name: this.state.nameInput,
          job: this.state.jobInput,
        },
      ],
      // Reset the inputs to blank in the process
      nameInput: '',
      jobInput: '',
    });
}

updateInputValue(name, evt) {
    // Inputs in markup will pass a state property name to set
    // Retrieve the input value manually from the DOM event object
    this.setState({
      [name]: evt.target.value,
    });
}

Again, these functions are just plain old React component class functions that will allow components to update the app’s state in specific ways. The last step is to add the actual markup for the form:

HTML
<h2>Add User</h2>
<div>
  <label htmlFor="userName">Name: </label>
  <input
    type="text"
    id="userName"
    value={this.state.nameInput}
    onChange={evt => this.updateInputValue('nameInput', evt)}
  />
</div>
<br />
<div>
  <label htmlFor="userJob">Job: </label>
  <input
    type="text"
    id="userJob"
    value={this.state.jobInput}
    onChange={evt => this.updateInputValue('jobInput', evt)}
  />
</div>
<br />
<input type="button" value="Add User" onClick={() => this.addUser()} />

Note that we produce two <input> elements here, each calling the same updateInputValue function when modified. The important difference between the two function calls is the property name passed as the first argument. If you compare these arguments to the properties you just added to the state object, you’ll see that they match up exactly. The updateInputValue function uses this argument to make sure the job and name properties don’t get mixed up for the new user!

I know the suspense is excruciating, but before you take this baby for a spin, double-check your code with the final App class code:

JavaScript
class App extends Component {
  state = {
    users: [
      {
        id: 0,
        name: 'Christian',
        job: 'React Dev',
      },
      {
        id: 1,
        name: 'Connor',
        job: 'Front-End Web Dev',
      },
      {
        id: 2,
        name: 'Sarah',
        job: 'Chemist',
      },
    ],
    nameInput: '',
    jobInput: '',
    cellEdit: false,
    editingCell: '',
  };

  addUser() {
    if (this.state.nameInput.length === 0 || this.state.jobInput.length === 0) {
      return;
    }
    this.setState({
      users: [
        // Create a new array, but keep the previous users using ES2015 spread operator
        ...this.state.users,
        // Add the new user to the array
        {
          id: this.state.users.length,
          // Use the input values saved in state
          name: this.state.nameInput,
          job: this.state.jobInput,
        },
      ],
      // Reset the inputs to blank in the process
      nameInput: '',
      jobInput: '',
    });
  }

  updateInputValue(name, evt) {
    // Inputs in markup will pass a state property name to set
    // Retrieve the input value manually from the DOM event object
    this.setState({
      [name]: evt.target.value,
    });
  }

  beginEdit(args) {
    this.setState({
      cellEdit: true,
      editingCell: `${args.col}, ${args.row}`,
    });
  }

  finishEdit() {
    this.setState({
      cellEdit: false,
    });
  }

  render() {
    return (
      <div className="App">
        <FlexGrid
          itemsSource={this.state.users}
          className="grid"
          allowAddNew={true}
          beginningEdit={(sender, args) => {
            this.beginEdit(args);
          }}
          cellEditEnded={() => {
            this.finishEdit();
          }}
        />
        <br />
        <h2>Add User</h2>
        <div>
          <label htmlFor="userName">Name: </label>
          <input
            type="text"
            id="userName"
            value={this.state.nameInput}
            onChange={evt => this.updateInputValue('nameInput', evt)}
          />
        </div>
        <br />
        <div>
          <label htmlFor="userJob">Job: </label>
          <input
            type="text"
            id="userJob"
            value={this.state.jobInput}
            onChange={evt => this.updateInputValue('jobInput', evt)}
          />
        </div>
        <br />
        <input type="button" value="Add User" onClick={() => this.addUser()} />
        <br />
        <h2 style={{ display: this.state.cellEdit ? 'block' : 'none' }}>
          Currently editing cell at {this.state.editingCell}
        </h2>
      </div>
    );
  }
}

If everything looks good, go ahead and start it up! You should see a brief, inviting form with two inviting fields just screaming "create a new user!" If you give in and fill out your new creation, you should see the grid immediately update with the user you just added. That’s the magic of FlexGrid’s support for native two-way data binding.

The form we created demonstrates one direction of the two-way data binding setup between the app’s state and FlexGrid. But what about the other direction? Well, if you’re using the always helpful React Developer Tools, you can see it in action right away! Simply open up the dev tools and examine the app’s state. You should see the users array with all of the current user objects. If you edit one of the rows in the grid (by double clicking in a cell), you’ll see the app’s state immediately update to reflect the changes.

There you have it! Quick, easy and natural two-way data binding with FlexGrid and React! I won’t inundate you with more code, but I’ll leave you with one last hint: there’s an easier way to do what we just did with FlexGrid. (If you’re intrigued, check out the FlexGrid’s allowAddNew property.)

Take the Next Step: Build Your Own FlexGrid App!

If you’re a huge React lover like I am and the FlexGrid examples above piqued your interest, you have nothing to lose by giving Wijmo a try for yourself! Join the hundreds of individual users and companies who use Wijmo to supercharge their UI development workflows.

"We purchased Wijmo and their team is doing a great job: good-looking, well-thought-out architecture; documentation; keeping up with ever-changing landscape like no others." — BJ Jeong, Cisco

If my words still haven’t convinced you, I encourage you to try FlexGrid and prove me right or wrong. If we’re wrong and you find a better data grid solution, we want to know. We haven’t stopped refining and improving our grid for 20 years, and we won’t stop anytime soon.

Download the source code for this sample

View sample source code on Glitch or GitHub

Run the sample on Glitch

Download a free trial of Wijmo

License

All content and files distributed by GrapeCity or Wijmo are licensed according to the GrapeCity EULA.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer GrapeCity, Inc.
United States United States
Christian Gaetano is an enthusiastic React developer, devoted dog dad, and caffeinated researcher and writer of all things JavaScript in the Seattle area. He's currently focused on making the front-end web a portal to developer happiness at GrapeCity, Inc.

Comments and Discussions

 
-- There are no messages in this forum --