Syncfusion AI Assistant

How can I help you?

Virtualization in DropDown List

21 Feb 202624 minutes to read

Dropdown list virtualization is a technique used to efficiently render extensive lists of items while minimizing the impact on performance. This method is particularly advantageous when dealing with large datasets because it ensures that only a fixed number of DOM (Document Object Model) elements are created. When scrolling through the list, existing DOM elements are reused to display relevant data instead of generating new elements for each item. This recycling process is managed internally.

During virtual scrolling, the data retrieved from the data source depends on the popup height and the calculation of the list item height. Enabling the enableVirtualization option in a dropdown list activates this virtualization technique.

When fetching data from the data source, the actionBegin event is triggered before data retrieval begins. Then, the actionComplete event is triggered once the data is successfully fetched.

Furthermore, Incremental Search is supported with virtualization in the DropDownList component. When a key is typed, the focus is moved to the respective element, and the value is updated in the component in the open popup state. In the closed popup state, the respective value is updated in the component based on the typed key. The Incremental Search functionality is well-suited for scenarios involving remote data binding.

When the enableVirtualization property is enabled, the skip and take properties provided by the user within the Query class at the initial state or during the actionBegin or actionComplete events will not be considered, since it is internally managed and calculated based on certain dimensions with respect to the popup height.

Binding local data

The DropDownList generates list items from an array of complex data by mapping appropriate columns to the fields property. With virtual scrolling, the list updates based on scroll offset, triggering data requests from the server. The actionBegin event fires before data retrieval begins, and the actionComplete event fires when data retrieval completes.

In the following example, the id and text columns are mapped to the value and text fields respectively.

[Class-component]

import { DropDownListComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
export default class App extends React.Component {
    // define the array of string
    constructor(props) {
        super(props);
        this.records = Array.from({ length: 150 }, (_, i) => ({
            id: 'id' + (i + 1),
            text: `Item ${i + 1}`,
        }));
    }
    fields = { text: 'text', value: 'id' };

    render() {
        return (
            // specifies the tag for render the DropDownList component
            <DropDownListComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={false} fields={this.fields} popupHeight="200px" >
                <Inject services={[VirtualScroll]} />
            </DropDownListComponent>);
    }
}
ReactDOM.render(<App />, document.getElementById('sample'));
import { DropDownListComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';

export default class App extends React.Component<{}, {}> {
  // maps the appropriate column to fields property
  private fields: object = { text: 'text', value: 'id' };

   // define the array of string
   private records: { [key: string]: Object }[] = [];
   
   // define the array of string
   constructor(props) {
    super(props);
    this.records = Array.from({ length: 150 }, (_, i) => ({
        id: 'id' + (i + 1),
        text: `Item ${i + 1}`,
    }));
  }

  public render() {
    return (
      // specifies the tag for render the DropDownList component
      <DropDownListComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={false} fields={this.fields} popupHeight="200px" >
      <Inject services={[VirtualScroll]}/>
  </DropDownListComponent>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('sample'));

Binding remote data

The DropDownList supports the retrieval of data from remote data services with the help of the DataManager component, triggering the actionBegin and actionComplete events, and then updating the list data. During virtual scrolling, additional data is retrieved from the server, triggering the actionBegin and actionComplete events at that time as well.

The following sample displays the OrderId from the Orders Data Service.

[Class-component]

import { DropDownListComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import { DataManager, WebApiAdaptor , Query } from '@syncfusion/ej2-data';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
export default class App extends React.Component {
    customerData = new DataManager({
        url: 'https://services.syncfusion.com/react/production/api/Orders',
        adaptor: new WebApiAdaptor,
        crossDomain: true
    });
    customerField = { text: 'OrderID', value: 'OrderID' };

    render() {
        return (
            // specifies the tag for render the DropDownList component
            <DropDownListComponent id="datas" dataSource={this.customerData} placeholder="OrderID" enableVirtualization={true} allowFiltering={true} fields={this.customerField} popupHeight="200px" >
                <Inject services={[VirtualScroll]} />
            </DropDownListComponent>);
    }
}
ReactDOM.render(<App />, document.getElementById('sample'));
import { DropDownListComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import { DataManager, WebApiAdaptor , Query } from '@syncfusion/ej2-data';

import * as React from 'react';
import * as ReactDOM from 'react-dom';

export default class App extends React.Component<{}, {}> {
  // maps the appropriate column to fields property
  private customerField: object = { text: 'OrderID', value: 'OrderID' };
   
  private customerData: DataManager = new DataManager({
    url: 'https://services.syncfusion.com/react/production/api/Orders',
    adaptor: new WebApiAdaptor,
    crossDomain: true
  });

  public render() {
    return (
      // specifies the tag for render the DropDownList component
      <DropDownListComponent id="datas" dataSource={this.customerData} placeholder="OrderID" enableVirtualization={true} allowFiltering={true} fields={this.customerField} popupHeight="200px" >
      <Inject services={[VirtualScroll]}/>
  </DropDownListComponent>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('sample'));

Customizing items count in virtualization

When the enableVirtualization property is enabled, the take property in the Query parameter at initialization or during the actionBegin event is respected. Internally, the component calculates the number of items that fit on the current viewport based on popup height. If the specified take value is less than the minimum items required to fill the viewport, the internal calculation takes precedence.

The following example demonstrates customizing items count in virtualization:

[Class-component]

import { DropDownListComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import { Query } from '@syncfusion/ej2-data';

import * as React from 'react';
import * as ReactDOM from 'react-dom';
export default class App extends React.Component {
    // define the array of string
    constructor(props) {
        super(props);
        this.records = Array.from({ length: 150 }, (_, i) => ({
            id: 'id' + (i + 1),
            text: `Item ${i + 1}`,
        }));
    }
    fields = { text: 'text', value: 'id' };
    // bind the Query instance to query property
    query = new Query().take(40);

    Begin(args) {
        args.Query = new Query().take(45);
      }

    render() {
        return (
            // specifies the tag for render the DropDownList component
            <DropDownListComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} query={this.query}  allowFiltering={false} actionBegin={this.Begin} fields={this.fields} popupHeight="200px" >
                <Inject services={[VirtualScroll]} />
            </DropDownListComponent>);
    }
}
ReactDOM.render(<App />, document.getElementById('sample'));
import { DropDownListComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import { Query } from '@syncfusion/ej2-data';

import * as React from 'react';
import * as ReactDOM from 'react-dom';

export default class App extends React.Component<{}, {}> {
  // maps the appropriate column to fields property
  private fields: object = { text: 'text', value: 'id' };

   // define the array of string
   private records: { [key: string]: Object }[] = [];
   private query: Query = new Query().take(40);

    public Begin(e: any): void {
      e.query = new Query().take(45);
    }
   // define the array of string
   constructor(props) {
    super(props);
    this.records = Array.from({ length: 150 }, (_, i) => ({
        id: 'id' + (i + 1),
        text: `Item ${i + 1}`,
    }));
  }

  public render() {
    return (
      // specifies the tag for render the DropDownList component
      <DropDownListComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" query={this.query} actionBegin={this.Begin} enableVirtualization={true} allowFiltering={false} fields={this.fields} popupHeight="200px" >
      <Inject services={[VirtualScroll]}/>
  </DropDownListComponent>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('sample'));

Grouping

The DropDownList component supports grouping with virtualization. Organize elements into groups based on categories using the groupBy field in the data table. After grouping, virtualization functions like local data binding. When binding to remote data, an initial request retrieves all data for grouping purposes. Subsequently, the grouped data works the same way as virtualized local data.

The following example demonstrates grouping with virtualization:

[Class-component]

import { DropDownListComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
export default class App extends React.Component {
    // define the array of string
    records = [];
    constructor(props) {
        super(props);
        for (let i = 1; i <= 150; i++) {
            let item = {};
            item.id = 'id' + i;
            item.text = `Item ${i}`;
            // Generate a random number between 1 and 4 to determine the group
            const randomGroup = Math.floor(Math.random() * 4) + 1;
            switch (randomGroup) {
                case 1:
                    item.group = 'Group A';
                    break;
                case 2:
                    item.group = 'Group B';
                    break;
                case 3:
                    item.group = 'Group C';
                    break;
                case 4:
                    item.group = 'Group D';
                    break;
                default:
                    break;
            }
            this.records.push(item);
        }
    }
    fields = { groupBy: 'group', text: 'text', value: 'id' };

    render() {
        return (
            // specifies the tag for render the DropDownList component
            <DropDownListComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={true} fields={this.fields} popupHeight="200px" >
                <Inject services={[VirtualScroll]} />
            </DropDownListComponent>);
    }
}
ReactDOM.render(<App />, document.getElementById('sample'));
import { DropDownListComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';

export default class App extends React.Component<{}, {}> {
  // maps the appropriate column to fields property
  private fields: object = {groupBy: 'group', text: 'text', value: 'id' };

   // define the array of string
   private records: { [key: string]: Object }[] = [];
   
   // define the array of string
  constructor(props) {
    super(props);
    for (let i = 1; i <= 150; i++) {
      const item: { id: string, text: string, group: string } = {
        id: 'id' + i,
        text: `Item ${i}`,
        group: ''
      };

      // Generate a random number between 1 and 4 to determine the group
      const randomGroup = Math.floor(Math.random() * 4) + 1;
      switch (randomGroup) {
        case 1:
          item.group = 'Group A';
          break;
        case 2:
          item.group = 'Group B';
          break;
        case 3:
          item.group = 'Group C';
          break;
        case 4:
          item.group = 'Group D';
          break;
        default:
          break;
      }
      this.records.push(item);
    }
  }

  public render() {
    return (
      // specifies the tag for render the DropDownList component
      <DropDownListComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={true} fields={this.fields} popupHeight="200px" >
      <Inject services={[VirtualScroll]}/>
  </DropDownListComponent>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('sample'));

Filtering with Virtualization

The DropDownList component supports filtering with virtualization. When allowFiltering is enabled, the component filters data based on typed characters. The DropDownList sends requests to the server using the full data source for filtering. The actionBegin event fires before the request, and the actionComplete event fires after successful data retrieval. Initial data loads when the popup opens, and the popup closes after selection or filtering completion.

The following example demonstrates filtering with virtualization:

[Class-component]

import { DropDownListComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
export default class App extends React.Component {
    // define the array of string
    constructor(props) {
        super(props);
        this.records = Array.from({ length: 150 }, (_, i) => ({
            id: 'id' + (i + 1),
            text: `Item ${i + 1}`,
        }));
    }
    fields = { text: 'text', value: 'id' };

    render() {
        return (
            // specifies the tag for render the DropDownList component
            <DropDownListComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={true} fields={this.fields} popupHeight="200px" >
                <Inject services={[VirtualScroll]} />
            </DropDownListComponent>);
    }
}
ReactDOM.render(<App />, document.getElementById('sample'));
import { DropDownListComponent, Inject, VirtualScroll } from '@syncfusion/ej2-react-dropdowns';
import * as React from 'react';
import * as ReactDOM from 'react-dom';

export default class App extends React.Component<{}, {}> {
  // maps the appropriate column to fields property
  private fields: object = { text: 'text', value: 'id' };

   // define the array of string
   private records: { [key: string]: Object }[] = [];
   
   // define the array of string
   constructor(props) {
    super(props);
    this.records = Array.from({ length: 150 }, (_, i) => ({
        id: 'id' + (i + 1),
        text: `Item ${i + 1}`,
    }));
  }

  public render() {
    return (
      // specifies the tag for render the DropDownList component
      <DropDownListComponent id="datas" dataSource={this.records} placeholder="e.g. Item 1" enableVirtualization={true} allowFiltering={true} fields={this.fields} popupHeight="200px" >
      <Inject services={[VirtualScroll]}/>
  </DropDownListComponent>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('sample'));