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!
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.
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.
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 updatedupdateStock
– 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 subtractedupdateStockValue
– 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?
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:
#bffdbc3
) background color#ffd1d1
) background colorWhenever 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:
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.
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 was the Product Manager for Kendo UI.