Virtual Scrolling in React Pivot Table Component
22 Sep 202524 minutes to read
Virtual Scrolling
Virtual scrolling enables efficient handling of large datasets by rendering only the rows and columns visible in the current viewport. This approach prevents performance degradation when working with substantial amounts of data, as content refreshes dynamically during vertical or horizontal scrolling. This feature can be enabled by setting the enableVirtualization property to true.
NOTE
The Virtualization and Paging features in the Pivot Table should not be enabled simultaneously. You can use either feature at a time, but not both together, as they are designed to handle data rendering differently and may conflict when used together.
To use the virtual scrolling feature, inject the VirtualScroll
module into the Pivot Table.
import { PivotViewComponent, VirtualScroll, Inject } from '@syncfusion/ej2-react-pivotview';
import * as React from 'react';
let date1;
let date2;
function data(count) {
let result = [];
let dt = 0;
for (let i = 1; i < (count + 1); i++) {
dt++;
let round;
let toString = i.toString();
if (toString.length === 1) {
round = '0000' + (i);
}
else if (toString.length === 2) {
round = '000' + i;
}
else if (toString.length === 3) {
round = '00' + i;
}
else if (toString.length === 4) {
round = '0' + i;
}
else {
round = toString;
}
result.push({
ProductID: 'PRO-' + round,
Year: "FY " + (dt + 2013),
Price: Math.round(Math.random() * 5000) + 5000,
Sold: Math.round(Math.random() * 80) + 10,
});
if (dt / 4 == 1) {
dt = 0;
}
}
return result;
};
function App() {
let dataSourceSettings = {
dataSource: data(1000),
enableSorting: false,
expandAll: true,
formatSettings: [{ name: 'Price', format: 'C0' }],
rows: [{ name: 'ProductID' }],
columns: [{ name: 'Year' }],
values: [{ name: 'Price', caption: 'Unit Price' }, { name: 'Sold', caption: 'Unit Sold' }]
};
let pivotObj;
return (<PivotViewComponent ref={d => pivotObj = d} id='PivotView' height={350} enableVirtualization={true} dataSourceSettings={dataSourceSettings}><Inject services={[VirtualScroll]}/></PivotViewComponent>);
};
export default App;
import { IDataSet, PivotViewComponent, VirtualScroll, Inject } from '@syncfusion/ej2-react-pivotview';
import { DataSourceSettingsModel } from '@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';
import * as React from 'react';
let date1: number;
let date2: number;
function data(count: number) {
let result: Object[] = [];
let dt: number = 0;
for (let i: number = 1; i < (count + 1); i++) {
dt++;
let round: string;
let toString: string = i.toString();
if (toString.length === 1) {
round = '0000' + (i);
}
else if (toString.length === 2) {
round = '000' + i;
}
else if (toString.length === 3) {
round = '00' + i;
} else if (toString.length === 4) {
round = '0' + i;
} else {
round = toString;
}
result.push({
ProductID: 'PRO-' + round,
Year: "FY " + (dt + 2013),
Price: Math.round(Math.random() * 5000) + 5000,
Sold: Math.round(Math.random() * 80) + 10,
});
if (dt / 4 == 1) {
dt = 0;
}
}
return result;
};
function App() {
let dataSourceSettings: DataSourceSettingsModel = {
dataSource: data(1000),
enableSorting: false,
expandAll: true,
formatSettings: [{ name: 'Price', format: 'C0' }],
rows: [{ name: 'ProductID' }],
columns: [{ name: 'Year' }],
values: [{ name: 'Price', caption: 'Unit Price' }, { name: 'Sold', caption: 'Unit Sold' }]
}
let pivotObj: PivotViewComponent;
return (<PivotViewComponent ref={ (d: PivotViewComponent) => pivotObj = d } id='PivotView' height={350} enableVirtualization={true} dataSourceSettings={dataSourceSettings}><Inject services={[VirtualScroll]}/></PivotViewComponent>);
};
export default App;
The
height
andwidth
properties should be set for virtual scrolling. If it is not defined, then the Pivot Table will consider its value as300px
and800px
respectively.
Virtual Scrolling with Single Page Mode
When virtual scrolling is enabled, the Pivot Table renders not only the current view page but also the adjacent previous and next pages by default. While this approach supports smooth navigation, it can increase computational load and reduce performance when working with extensive datasets, as additional rows and columns from surrounding pages are processed.
To optimize performance, set the allowSinglePage property to true within the virtualScrollSettings. Enabling this property ensures that only the rows and columns for the current view page are rendered during virtual scrolling. This significantly enhances the performance of the Pivot Table, especially during initial rendering and user actions such as drilling up, drilling down, sorting, filtering, and more.
import { PivotViewComponent, Inject } from '@syncfusion/ej2-react-pivotview';
import * as React from 'react';
function data(count) {
let result = [];
let dt = 0;
for (let i = 1; i < (count + 1); i++) {
dt++;
let round;
let toString = i.toString();
if (toString.length === 1) {
round = '0000' + (i);
}
else if (toString.length === 2) {
round = '000' + i;
}
else if (toString.length === 3) {
round = '00' + i;
}
else if (toString.length === 4) {
round = '0' + i;
}
else {
round = toString;
}
result.push({
ProductID: 'PRO-' + round,
Year: "FY " + (dt + 2013),
Price: Math.round(Math.random() * 5000) + 5000,
Sold: Math.round(Math.random() * 80) + 10,
});
if (dt / 4 == 1) {
dt = 0;
}
}
return result;
};
function App() {
var pivotObj;
var virtualScrollSettings = {
allowSinglePage: true
};
var dataSourceSettings = {
dataSource: data(1000),
enableSorting: false,
expandAll: true,
formatSettings: [{ name: 'Price', format: 'C0' }],
rows: [{ name: 'ProductID' }],
columns: [{ name: 'Year' }],
values: [{ name: 'Price', caption: 'Unit Price' }, { name: 'Sold', caption: 'Unit Sold' }]
};
return (
<PivotViewComponent ref={d => pivotObj = d} id='PivotView' height={350} dataSourceSettings={dataSourceSettings}
virtualScrollSettings={virtualScrollSettings}><Inject services={[GroupingBar]}/>
</PivotViewComponent>
);
};
export default App;
import { PivotViewComponent, Inject, VirtualScroll, VirtualScrollSettingsModel } from '@syncfusion/ej2-react-pivotview';
import { DataSourceSettingsModel } from '@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';
import * as React from 'react';
function data(count: number) {
let result: Object[] = [];
let dt: number = 0;
for (let i: number = 1; i < (count + 1); i++) {
dt++;
let round: string;
let toString: string = i.toString();
if (toString.length === 1) {
round = '0000' + (i);
}
else if (toString.length === 2) {
round = '000' + i;
}
else if (toString.length === 3) {
round = '00' + i;
} else if (toString.length === 4) {
round = '0' + i;
} else {
round = toString;
}
result.push({
ProductID: 'PRO-' + round,
Year: "FY " + (dt + 2013),
Price: Math.round(Math.random() * 5000) + 5000,
Sold: Math.round(Math.random() * 80) + 10,
});
if (dt / 4 == 1) {
dt = 0;
}
}
return result;
};
let virtualScrollSettings: VirtualScrollSettingsModel = {
allowSinglePage: true
} as VirtualScrollSettingsModel;
function App() {
let pivotObj: PivotViewComponent;
let dataSourceSettings: DataSourceSettingsModel = {
dataSource: data(1000),
enableSorting: false,
expandAll: true,
formatSettings: [{ name: 'Price', format: 'C0' }],
rows: [{ name: 'ProductID' }],
columns: [{ name: 'Year' }],
values: [{ name: 'Price', caption: 'Unit Price' }, { name: 'Sold', caption: 'Unit Sold' }]
}
return (
<PivotViewComponent ref={ (d: PivotViewComponent) => pivotObj = d } id='PivotView' height={350} dataSourceSettings={dataSourceSettings}
virtualScrollSettings={virtualScrollSettings}><Inject services={[VirtualScroll]}/>
</PivotViewComponent>
);
};
export default App;
Limitations for Virtual Scrolling
- In virtual scrolling, the columnWidth property in gridSettings should be in pixels, and percentage values are not accepted.
- Features such as auto fit, column resizing, text wrapping, and setting specific column widths through events can dynamically affect the row height and column width in the Pivot Table at runtime. However, these changes are not considered in the scroller calculations, particularly with large datasets. This can lead to performance issues and problems with UI functionality during scrolling. Therefore, it is not recommended to use these features alongside virtualization in the Pivot Table.
- Grouping, which takes additional time to split the raw items into the provided format.
- Date Formatting, which takes additional time to convert date format.
- Date Formatting with sorting, here additionally full date time format should be framed to perform sorting along with the provided date format, which lags the performance.
- When using OLAP data, subtotals and grand totals are only displayed when measures are bound at the last position in the rows or columns axis. Otherwise, the data from the Pivot Table will be shown without summary totals.
- Even if virtual scrolling is enabled, not only is the current viewport data retrieved, but also the data for the immediate previous page and the immediate next page. As a result, when the end user scrolls slightly ahead or behind, the next or previous page data is displayed immediately without requiring a refresh. Note: If the Pivot Table’s width and height are large, the loading data count in the current, previous, and next viewport (pages) will also increase, affecting performance.
Virtual Scrolling for Static Field List
Virtual scrolling works automatically with “Popup” field lists when you set the enableVirtualization property in the Pivot Table to true. However, when using a static field list (displayed as a separate component), you need to connect both components manually.
Here’s how to make virtual scrolling work with a static field list:
- Enable virtual scrolling in the Pivot Table component by setting the enableVirtualization property to true, which improves performance for large datasets.
- Connect the PivotFieldList component to the Pivot Table component using the load event.
- Ensure synchronization between the Pivot Table and field list by updating the Pivot Table’s report configuration with the field list’s report configuration during the load event.
import { CalculatedField, PivotFieldListComponent, Inject, PivotViewComponent, VirtualScroll } from '@syncfusion/ej2-react-pivotview';
import * as React from 'react';
const SAMPLE_CSS = `
.e-pivotview {
width: 58%;
height: 100%;
float: left;
}
.e-pivotfieldlist {
width: 42%;
height: 100%;
float: right;
}
.e-pivotfieldlist .e-static {
width: 100% !important;
}`;
function data(count) {
let result = [];
let dt = 0;
for (let i = 1; i < (count + 1); i++) {
dt++;
let round;
let toString = i.toString();
if (toString.length === 1) {
round = '0000' + (i);
}
else if (toString.length === 2) {
round = '000' + i;
}
else if (toString.length === 3) {
round = '00' + i;
}
else if (toString.length === 4) {
round = '0' + i;
}
else {
round = toString;
}
result.push({
ProductID: 'PRO-' + (i % 1000),
Year: "FY " + (dt + 2013),
Price: Math.round(Math.random() * 5000) + 5000,
Sold: Math.round(Math.random() * 80) + 10,
});
if (dt / 4 == 1) {
dt = 0;
}
}
return result;
};
function App() {
let pivotGridModule;
let dataSourceSettings = {
dataSource: data(1000),
enableSorting: false,
expandAll: true,
formatSettings: [{ name: 'Price', format: 'C0' }],
rows: [{ name: 'ProductID' }],
columns: [{ name: 'Year' }],
values: [{ name: 'Price', caption: 'Unit Price' }, { name: 'Sold', caption: 'Unit Sold' }]
};
let pivotObj;
let fieldListObj;
return (<div className="control-section">
<style>{SAMPLE_CSS}</style>
<PivotViewComponent id='PivotView' ref={d => pivotObj = d} enableVirtualization={true} enginePopulated={afterPivotPopulate.bind(this)} width={'99%'} height={'530'}><Inject services={[VirtualScroll]}/></PivotViewComponent>
<PivotFieldListComponent id='PivotFieldList' ref={d => fieldListObj = d} load={onLoad} enginePopulated={afterPopulate.bind(this)} dataSourceSettings={dataSourceSettings} renderMode={"Fixed"} allowCalculatedField={true}><Inject services={[CalculatedField]}/></PivotFieldListComponent></div>);
function afterPopulate() {
pivotObj = document.getElementById('PivotView').ej2_instances[0];
fieldListObj = document.getElementById('PivotFieldList').ej2_instances[0];
fieldListObj.updateView(pivotObj);
}
function afterPivotPopulate() {
pivotObj = document.getElementById('PivotView').ej2_instances[0];
fieldListObj = document.getElementById('PivotFieldList').ej2_instances[0];
fieldListObj.update(pivotObj);
}
function rendereComplete() {
fieldListObj.updateView(pivotObj);
fieldListObj.update(pivotObj);
}
function onLoad() {
//Getting component instance.
pivotObj = document.getElementById('PivotView').ej2_instances[0];
fieldListObj = document.getElementById('PivotFieldList').ej2_instances[0];
fieldListObj.pivotGridModule = pivotObj;
//Assigning report to pivot table component.
pivotObj.dataSourceSettings = fieldListObj.dataSourceSettings;
//Generating page settings based on pivot table component’s size.
pivotObj.updatePageSettings(true);
//Assigning page settings to field list component.
fieldListObj.pageSettings = pivotObj.pageSettings;
}
};
export default App;
import { CalculatedField, PivotFieldListComponent, Inject, PivotViewComponent, VirtualScroll } from '@syncfusion/ej2-react-pivotview';
import { DataSourceSettingsModel } from '@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';
import * as React from 'react';
const SAMPLE_CSS = `
.e-pivotview {
width: 58%;
height: 100%;
float: left;
}
.e-pivotfieldlist {
width: 42%;
height: 100%;
float: right;
}
.e-pivotfieldlist .e-static {
width: 100% !important;
}`;
function data(count: number) {
let result: Object[] = [];
let dt: number = 0;
for (let i: number = 1; i < (count + 1); i++) {
dt++;
let round: string;
let toString: string = i.toString();
if (toString.length === 1) {
round = '0000' + (i);
}
else if (toString.length === 2) {
round = '000' + i;
}
else if (toString.length === 3) {
round = '00' + i;
} else if (toString.length === 4) {
round = '0' + i;
} else {
round = toString;
}
result.push({
ProductID: 'PRO-' + (i % 1000),
Year: "FY " + (dt + 2013),
Price: Math.round(Math.random() * 5000) + 5000,
Sold: Math.round(Math.random() * 80) + 10,
});
if (dt / 4 == 1) {
dt = 0;
}
}
return result;
};
function App() {
let dataSourceSettings: DataSourceSettingsModel = {
dataSource: data(1000),
enableSorting: false,
expandAll: true,
formatSettings: [{ name: 'Price', format: 'C0' }],
rows: [{ name: 'ProductID' }],
columns: [{ name: 'Year' }],
values: [{ name: 'Price', caption: 'Unit Price' }, { name: 'Sold', caption: 'Unit Sold' }]
}
let pivotObj: PivotViewComponent;
let fieldListObj: PivotFieldListComponent;
return (<div className="control-section">
<style>{SAMPLE_CSS}</style>
<PivotViewComponent id='PivotView' ref={ (d: PivotViewComponent) => pivotObj = d } enableVirtualization={true} enginePopulated={afterPivotPopulate.bind(this)} width={'99%'} height={'530'}><Inject services={[VirtualScroll]}/></PivotViewComponent>
<PivotFieldListComponent id='PivotFieldList' ref={ (d: PivotFieldListComponent) => fieldListObj = d } load={onLoad} enginePopulated={afterPopulate.bind(this)} dataSourceSettings={dataSourceSettings} renderMode={"Fixed"} allowCalculatedField={true}><Inject services={[CalculatedField]} /></PivotFieldListComponent></div>);
function afterPopulate(): void {
pivotObj = document.getElementById('PivotView').ej2_instances[0];
fieldListObj = document.getElementById('PivotFieldList').ej2_instances[0];
fieldListObj.updateView(pivotObj);
}
function afterPivotPopulate(): void {
pivotObj = document.getElementById('PivotView').ej2_instances[0];
fieldListObj = document.getElementById('PivotFieldList').ej2_instances[0];
fieldListObj.update(pivotObj);
}
function rendereComplete(): void {
fieldListObj.updateView(pivotObj);
fieldListObj.update(pivotObj);
}
function onLoad(): void {
//Getting component instance.
pivotObj = document.getElementById('PivotView').ej2_instances[0];
fieldListObj = document.getElementById('PivotFieldList').ej2_instances[0];
fieldListObj.pivotGridModule = pivotObj;
//Assigning report to pivot table component.
pivotObj.dataSourceSettings = fieldListObj.dataSourceSettings;
//Generating page settings based on pivot table component’s size.
pivotObj.updatePageSettings(true);
//Assigning page settings to field list component.
fieldListObj.pageSettings = pivotObj.pageSettings;
}
};
export default App;