DEV Community

Cover image for React Data Tables Made Simple: A Friendly Guide into Data Grids
Maks
Maks

Posted on

React Data Tables Made Simple: A Friendly Guide into Data Grids

Data tables (or so called data grids) are at the core of many modern web applications, especially when dealing with large datasets or complex interactions. React's ecosystem offers several ways to implement a data grid, but today we'll compare two approaches:

  • A simple, custom-built React grid.
  • A robust and highly performant implementation using external libraries.

Let's dive in!

Image description

Creating a Simple React Grid

Before jumping into advanced solutions, let’s start with a basic implementation of a data grid in React using mock data. This example will give you a feel for how React components, hooks, and states can work together to create a functional grid.

Code Example

import React, { useState } from "react";

// Mock data generator
const generateMockData = (count: number) => {
  return Array.from({ length: count }, (_, index) => ({
    id: index + 1,
    name: `Name ${index + 1}`,
    age: Math.floor(Math.random() * 50) + 20,
  }));
};

const SimpleGrid = () => {
  const [data] = useState(generateMockData(50));

  return (
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>Name</th>
          <th>Age</th>
        </tr>
      </thead>
      <tbody>
        {data.map((row) => (
          <tr key={row.id}>
            <td>{row.id}</td>
            <td>{row.name}</td>
            <td>{row.age}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
};

export default SimpleGrid;
Enter fullscreen mode Exit fullscreen mode
  • Mock Data Generator: Creates sample rows with an id, name, and age for demonstration purposes.
  • State Management: Uses useState to manage and render the mock data.
  • HTML Table: Simple and styled to display the data as a grid.

So, you have an app that manages intensive datasets and you might think it’s straightforward enough to handle on your own. After all, implementing virtual scrolling might seem like all it takes.

But what happens when your needs grow?

Suppose you want to pin a row at the top or bottom. Add a column that also needs to be pinned. And then start grouping these elements. Soon, you might find yourself needing cell focus and range selections. This is where the complexity increases significantly.

In scenarios like this, maintaining performance while adding sophisticated grid functionalities can become difficult.
That’s where 3rd party components come in.

Image description

RevoGrid for Advanced Data Grids

We decided to use RevoGrid because we genuinely believe it’s the best data grid framework out there — and yes, we built it ourselves. With RevoGrid, you get React compatibility, unmatched performance, and the ability to handle massive datasets effortlessly. We’ve poured our expertise into creating a framework that’s feature-packed, highly customizable, and optimized to deliver a seamless experience. It’s the tool we’d choose for our own projects, and we’re confident you’ll love it too.

Installation

To get started with RevoGrid, first install the library via npm:

npm install @revolist/react-datagrid
Enter fullscreen mode Exit fullscreen mode

Building a simple Example

Creating a data grid with RevoGrid in React is remarkably simple and efficient. All you have to do is define your data model, and voilà!
In the example below, we define a basic table with two columns — First and Second — and provide a static data source containing two rows.

Using useMemo, we ensure that the column definitions maintain a stable reference to avoid unnecessary re-renders. Similarly, the source data is stored in state with useState.

The component then renders the grid, automatically handling the layout and performance optimizations for the provided configuration.

// App.tsx
import { RevoGrid } from "@revolist/react-datagrid";
import { useState, useMemo } from "react";

function App() {
  // note: columns & source need a "stable" reference
  // in order to prevent infinite re-renders
  const columns = useMemo(
    () => [
      {
        prop: "name",
        name: "First",
      },
      {
        prop: "details",
        name: "Second",
      },
    ],
    []
  );
  const [source] = useState([
    {
      name: "1",
      details: "Item 1",
    },
    {
      name: "2",
      details: "Item 2",
    },
  ]);
  return (
    <>
      <RevoGrid columns={columns} source={source} />
    </>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Try it yourself!

Building an advanced RevoGrid Example

Below is an example implementation of RevoGrid in a React application. This example highlights row selection functionality and shows how to handle large datasets efficiently.

import React, { useState, useMemo, useCallback, useEffect } from "react";
import { ColumnRegular, RevoGrid } from "@revolist/react-datagrid";

// Mock data generator
const generateFakeDataRows = (rowsNumber: number) => {
  const result = [];
  for (let j = 0; j < rowsNumber; j++) {
    result[j] = {
      id: j,
      myRow: `Row ${j}`,
    };
  }
  return result;
};

// Make an App
const App = () => {
  const [rows] = useState(generateFakeDataRows(1000));

  // Selection state management
  const [selected, setSelected] = useState<Set<number>>(new Set());
  const [allSelectedStatus, setAllSelectedStatus] = useState<number>(0);

  const selectAll = useCallback(
    (checked: boolean) => {
      setSelected(new Set(checked ? rows.map((row) => row.id) : []));
    },
    [rows]
  );

  const selectSingle = useCallback((rowId: number, checked: boolean) => {
    setSelected((prevSelected) => {
      const updatedSelected = new Set(prevSelected);
      if (checked) {
        updatedSelected.add(rowId);
      } else {
        updatedSelected.delete(rowId);
      }
      return updatedSelected;
    });
  }, []);

  useEffect(() => {
    if (selected.size === 0) {
      setAllSelectedStatus(0);
    } else if (selected.size === rows.length) {
      setAllSelectedStatus(2);
    } else {
      setAllSelectedStatus(1);
    }
  }, [selected, rows.length]);

  const columns: ColumnRegular[] = useMemo(() => {
    const status = allSelectedStatus;
    return [
      {
        prop: "myRow",
        columnTemplate: (h, v) => {
          return [
            h("input", {
              type: "checkbox",
              indeterminate: status === 1,
              checked: status > 1 || undefined,
              onChange(e: InputEvent) {
                if (e.target instanceof HTMLInputElement) {
                  selectAll(e.target.checked);
                }
              },
            }),
            "Select All",
          ];
        },
        cellTemplate: (h, { model, value, rowIndex }) => {
          return h("label", undefined, [
            h("input", {
              type: "checkbox",
              key: rowIndex,
              checked: selected.has(model.id) || undefined,
              onChange(e: React.ChangeEvent<HTMLInputElement>) {
                if (e.target instanceof HTMLInputElement) {
                  selectSingle(model.id, e.target.checked);
                }
              },
            }),
            value,
          ]);
        },
        size: 400,
      },
    ];
  }, [allSelectedStatus, selected, selectAll, selectSingle]);

  return (
    <RevoGrid
      columns={columns}
      source={rows}
      readonly
      theme="compact"
    />
  );
};
Enter fullscreen mode Exit fullscreen mode

Try it yourself!

For applications requiring high-performance grids, deep customization, and an excellent user experience, RevoGrid is the perfect choice. Start building smarter, faster grids today!

Top comments (0)