Telerik blogs
KendoReactT2_1200x303

In this blog post we take an initial look at how to bind real-time data to the KendoReact Data Grid.

There are plenty of examples out there of building a data grid using static data, but what about live streaming data?

In this blog post, I want to highlight how you can create a real-time grid that uses the KendoReact Data Grid and some live streaming data to create a table that updates the appropriate data cells and highlights the changes live by updating the colors and look and feel of the cells depending on the type of change that has happened.

Let’s jump straight in!

The Finished Project

For those who prefer to have the entire project up at once and then follow along the blog post, here’s a direct link to the StackBlitz project, which I will be referencing as we go along.

Setting the Stage—Data Binding the React Data Grid

Note: Throughout this blog post, I will be dealing with the KendoReact Data Grid as well as the KendoReact Button components. To learn how to get up and running with these components, I highly recommend reviewing the Get Started with KendoReact article, which will get you up to speed with how to use our React data grid.

You can also follow the basic steps outlined in the React Data Table Getting Started article to get to a point where you can follow along with the code below.

Initially we just load an array of data items into our KendoReact Data Table.

import  *  as  React  from  'react';
import  *  as  ReactDOM  from  'react-dom';
import { Grid, GridColumn  as  Column } from  '@progress/kendo-react-grid';
import  products  from  './products.json';
const  App = () => {
const [data, setData] = React.useState(products);

return (
  <>
    <Grid  data={data}>
      <Column  field="ProductID" title="ID" width="80px" filterable={false}  />
      <Column field="ProductName"  title="Name"  width="250px"  />
      <Column field="UnitsInStock" title="In stock" filter="numeric" width="100px" cell={InStockCell} />
      <Column field="UnitPrice" title="Price" filter="numeric" width="150px" />
    </Grid>
  </>
);
};

The data in this case is just a JSON object representing 10 data items with the fields we defined in the columns collection of the Grid and a few more.

Next, we will need to take this array of data and add a little streaming to it.

Randomly Updating Data

A quick disclaimer: The code I’ve written to update data at random intervals is just for the sake of providing a simple showcase of data changing at random intervals. Most likely you already have a strategy for updating data yourself, or you’re streaming data from somewhere and don’t even worry about handling the data yourself.

All that is to say, this code’s only purpose is to illustrate how to set up real-time updates with the KendoReact Data Grid and it will certainly not win any awards for clean code. 😉

For those copying and pasting as we go along, I’ve added the following collection of variables that will help us with the state of this particular component. Don’t worry too much about what they are responsible for yet.

const [data, setData] = React.useState(products);
const [pausedTimer, setPausedTimer] = React.useState(true);
const [buttonLabel, setButtonLabel] = React.useState('Start');
const changeIntervalRef = React.useRef(null);

To make this easier to follow, and to simplify the sample, I’m randomly updating a single field, the UnitsInStock field to add or remove a random number of units (between -4 and 4).

This is handled by three functions that I’ve added in the code:

  • randomizeData – takes a data collection and randomly selects entries in the collection to be updated
  • updateStock – takes the selected entry from randomizeData and determines how much will be added or subtracted from UnitsInStock; it also sets isChanged to true (to indicate that this field was updated) and we say if this change was negative or positive depending on if we added or subtracted
  • updateStockValue – is used by UpdateStock to add or subtract a number between 0–4
// Randomly selects a set of data items from our data and updates the UnitsInStock field
const  randomizeData = (passedData) => {
	let  newData = passedData.slice();
	for (
	  let  i = Math.round(Math.random() * 10);
	  i < newData.length;
	  i += Math.round(Math.random() * 10)) {
	  updateStock(newData[i]);
	}
	return  newData;
};

// Randomly adds or removes 0-4 from UnitsInStock and changes the changeType from negative to positive.
const  updateStock = (passedRow) => {
	let  oldUnitsInStock = passedRow.UnitsInStock;
	let  updatedUnitsInStock = updateStockValue();

	updatedUnitsInStock < 0 ? (passedRow.changeType = 'negative') : (passedRow.changeType = 'positive');

	passedRow.isChanged = true;

	passedRow.UnitsInStock = oldUnitsInStock - updatedUnitsInStock;
};

const  updateStockValue = () => {
  return  Math.floor(Math.random() * 4) * (Math.round(Math.random()) ? 1 : -1);
};

Once we have these functions set up, it’s a question of updating cells randomly at a set interval. This can be done by using the setInterval() and clearInterval() JavaScript functions.

I set up the startDataChange() and pauseDataChange() functions for this.

// Kicks off when we click on the "Start" button and updates data randomly every second
const  startDataChange = () => {
  clearInterval(changeIntervalRef.current);
  changeIntervalRef.current = setInterval(() => {
	  let  newData = randomizeData(data);
	  setData(newData);
  }, 1000);
};

// Pauses the data being updated
const  pauseDataChange = () => {
  clearInterval(changeIntervalRef.current);
};

This will now call the randomizeData function every 1 second, which will update a few random rows with either an increase or decrease of UnitsInStock.

To create an event that allows us to start or stop our “live” data, something like this click handler can work:

// Start or stop our "live" data
const  onStartStopClick = () => {
	updateButtonLabel();
	if (pausedTimer) {
		startDataChange();
		setPausedTimer(!pausedTimer);
	} else {
		pauseDataChange();
		setPausedTimer(!pausedTimer);
	}
};
	const  updateButtonLabel = () => {
	pausedTimer ? setButtonLabel('Stop') : setButtonLabel('Start');
};

If we build our project and hit the Start button, we will see cells in the “In Stock” column updating randomly! However, it is hard to see when the changes happen and where since nothing changes visually, just the value of the cell.

What this means is that to have live and updating data in the KendoReact Data Grid, you simply need to update the underlying data itself. If this is already a stream of data coming from your backend or living somewhere in your React application, you can simply point the data set to the KendoReact Data Table and you’re off to the races!

There is more we can do, though. What if we want to update the style of the cells based on whether the value has increased or decreased?

Updating Cell Styles for Our Real-Time Grid

Now that we have React Data Table, which updates automatically through live streaming data, we can see how we can update the UI to highlight that a change has happened.

In our current React app, we are focusing on the “In Stock” field, and numbers are either going up or down. Since we only have these two states, it makes sense to focus on applying a style to indicate a positive change, and another to indicate negative change. Green is usually associated with growth, while red is the color we go to when something shrinks.

To make it easier for everyone following along, here are the styles and colors we will be using:

  • Positive: Green text with a light green (#bffdbc3) background color
  • Negative: Red text with a light red (#ffd1d1) background color

Whenever we need to customize behavior of a grid cell in our React Data Grid, we have to take advantage of the custom cells feature, which lets us pass in any React component to reflect our cell.

As a baseline, we can create this function which returns the same cell as we would normally have in our React Data Grid. We can customize this further to add in our updates to styles later.

const InStockCell = (props) => {
	const field = props.field || '';
	const value = props.dataItem[field];
	return (
	  <td
		colSpan={props.colSpan}
		role={'gridcell'}
		aria-colindex={props.ariaColumnIndex}
		aria-selected={props.isSelected}
      >
        {value === null ? '' : props.dataItem[field].toString()}
      </td>
    );
}

While we have some additional props on this <td> element (related to accessibility), the one takeaway is that we are dealing with the same elements that we would be dealing with if we wrote a data table ourselves.

To replace the default rendering of the “In Stock” field to use this new custom cell, we would just update the column configuration to use the cell prop, like this:

<Column
	field="UnitsInStock"
	title="In Stock"
	filter="numeric"
	width="100px"
	cell={InStockCell}
/>

Now comes the time where we can update the style of the cell depending on if we have a positive or negative change occurring in our live streaming data.

For this sample, all that is needed is to update the style prop of our <td> element. You could also work with the className prop if you had an external class to apply. In my case, I’m going to create an object called cellColors that will hold color and background color properties. I will then use this to update the style of my cell like this:

return (
	<td
	  style={{
	    color: cellColors.color,
	    background: cellColors.backgroundColor,
	  }}
	  colSpan={props.colSpan}
	  role={'gridcell'}
	  aria-colindex={props.ariaColumnIndex}
	  aria-selected={props.isSelected}
	>
	  {value === null ? '' : props.dataItem[field].toString()}
	</td>
);

I’ll cover the logic for how I create cellColors next, but I wanted to highlight just how small of a change we need in the rendering of the UI to automatically update our real-time grid to show when underlying values have gone up or down.

Addressing the rest of the logic in this InStockCell component, I want to mention a field that I showed previously: isChanged. This field is on all data items and will allow us to flag if a particular data item has been changed or not.

This type of field is not necessary—I just added it to this example to highlight that you can branch off within these custom cells to create different rendering options depending on your data.

The next field to recall is the changeType field, which will either be positive or negative.

const  InStockCell = (props) => {
	const  checkChange = props.dataItem.isChanged || false;
	const  field = props.field || '';
	const  value = props.dataItem[field];

	if (checkChange === true) {
      let  changeType = props.dataItem.changeType;
	  let  cellColors = {};

	  changeType === 'positive' ? ((cellColors.color = 'green'), (cellColors.backgroundColor = '#bfdbc3')) : ((cellColors.color = 'red'), (cellColors.backgroundColor = '#ffd1d1'));
      
      return (
        <td
          style={{
            color: cellColors.color,
            background: cellColors.backgroundColor,
          }}
         colSpan={props.colSpan}
         role={'gridcell'}
         aria-colindex={props.ariaColumnIndex}
         aria-selected={props.isSelected}
        >
          {value === null ? '' : props.dataItem[field].toString()}
        </td>
      );
      } else { // Handles our initial rendering of the cells and can be used to restore cells that have not been updated in a while.
        return (
          <td
            colSpan={props.colSpan}
            role={'gridcell'}
            aria-colindex={props.ariaColumnIndex}
            aria-selected={props.isSelected}
          >
            {value === null ? '' : props.dataItem[field].toString()}
          </td>
        );
    }
};

When we build our application and hit start, we will now see live streaming data in our React data grid where cells automatically update their style depending on how the value has changed!

For reference, here’s a StackBlitz project that has everything up and running:

In-Depth Real-Time Data Grid

If you want a more advanced sample application that showcases more ways to create your own live streaming data, or other ways you can approach updating the content of the KendoReact Data Grid, then I recommend visiting the KendoReact Data Grid Live Data Updates documentation article. This demo focuses specifically on the Data Grid, but if you prefer a more full-fledged application with multiple UI components and real-time data, we have the KendoReact Financial Dashboard application example as well.

The Live Data Updates article showcases a data table bound to data around cryptocurrencies (not bound to a live API—the data is all local). This build on top of what I covered here and has a more advanced setup for updating data on the fly.

Live Stream Data on Your Own

Hopefully this post helped you understand the basics of creating a real-time grid with React on your own.

Most of the content went into the logic for building out our fake data and making it update randomly. Chances are you can cut this out and just bind your existing data streams to the KendoReact Data Grid.

We also just scratched the surface when it comes to updating the appropriate cells to highlighting changes. Since we are using CSS, we can apply all sorts of neat effects like, for example, highlighting in a new color, then slowly fading back to the default style. As long as we have some way of finding out which data items or just individual fields have been changed, we have full control over the look and feel of our React Data Grid!


Carl Bergenhem
About the Author

Carl Bergenhem

Carl Bergenhem is the Product Manager for Kendo UI. Throughout his time at Telerik/Progress he has covered the entire portfolio of products offered, but mainly spent his time in the web development arena covering ASP.NET Ajax, ASP.NET MVC, and JavaScript-based products. He’s passionate about web technologies and tries to stay on top of all JavaScript libraries and frameworks relating to Kendo UI, developing with jQuery, Angular, React, and Vue.js on a regular basis. In his free time Carl enjoys slaying virtual monsters, snowboarding and playing his guitar.

Related Posts

Comments

Comments are disabled in preview mode.