Virtual scrolling in React Pivot Table
10 Aug 202624 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. Content refreshes dynamically as the user scrolls vertically or horizontally. This feature is enabled by setting the enableVirtualization property to true (default false).
Virtualization and Paging cannot be enabled at the same time. Use one or the other; they are designed to handle data rendering differently and may conflict when combined.
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
heightandwidthproperties should be set for virtual scrolling. If they are not defined, the Pivot Table defaults to300pxheight and800pxwidth.
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, and filtering.
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;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.
To make virtual scrolling work with a static field list:
- Enable virtual scrolling in the Pivot Table component by setting the
enableVirtualizationproperty to true. - Connect the PivotFieldList component to the Pivot Table component using the
loadevent. - Synchronize the Pivot Table and field list by updating the Pivot Table’s report configuration with the field list’s report configuration inside the
loadevent.
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;Limitations
- In virtual scrolling, the
columnWidthproperty ingridSettingsmust be in pixels; percentage values are not accepted. - Features such as auto fit, column resizing, text wrapping, and setting column widths through events can dynamically affect the row height and column width at runtime. These changes are not considered in the scroller calculations, particularly with large datasets, which can cause performance issues. It is not recommended to use these features alongside virtualization.
- Grouping, which takes additional time to split raw items into the provided format.
- Date formatting, which takes additional time to convert date format.
- Date formatting with sorting requires the full date-time format to perform sorting along with the provided date format, which lags performance.
- When using OLAP data, subtotals and grand totals are only displayed when measures are bound at the last position in the
rowsorcolumnsaxis. Otherwise, the Pivot Table will show data without summary totals. - Even with virtual scrolling, the current viewport data plus the immediate previous and next pages are all retrieved. As a result, when the user scrolls slightly ahead or behind, the next or previous page data is shown immediately. Note: Large
widthandheightvalues increase the loading count across the current, previous, and next viewports, which can affect performance.