5

What is the best way to render large list of data in a searchable react select dropdown? I've looked at windowing, where DOM nodes are created only for items that are visible in the view port. This can be done by using the react-window package alongside react-select. However, I want to know if there are better approaches than this.

This is the windowing implementation using react-window and react-select

import React, { Component } from "react";
import ReactDOM from "react-dom";
import Select from "react-select";
import { FixedSizeList as List } from "react-window";

import "./styles.css";

const options = [];
for (let i = 0; i < 10000; i = i + 1) {
  options.push({ value: i, label: `Option ${i}` });
}

const height = 35;

class MenuList extends Component {
  render() {
    const { options, children, maxHeight, getValue } = this.props;
    const [value] = getValue();
    const initialOffset = options.indexOf(value) * height;

    return (
      <List
        height={maxHeight}
        itemCount={children.length}
        itemSize={height}
        initialScrollOffset={initialOffset}
      >
        {({ index, style }) => <div style={style}>{children[index]}</div>}
      </List>
    );
  }
}

const App = () => <Select components={{ MenuList }} options={options} />;

ReactDOM.render(<App />, document.getElementById("root"));
blankface
  • 5,757
  • 19
  • 67
  • 114
  • I would implement lazy loading and add to the dropdown when user scroll to the bottom of the dropdown. React will only render the item that are not on the page or updated using the key prop. – Matin Kajabadi Jun 02 '20 at 03:15

2 Answers2

6

I combined the solution you mentioned (react-window), with the filterOption solution, and the lesser-discussed react-async component. This worked pretty well for me.

react-window will do some sort of 'lazy load', while async react with show a loading sign before showing your search query. These together makes it feel a lot more natural (I have 7000+ options).

this is my first ever response post, so let me know if (anyone) has questions and I'll try my best to answer

import React, { Component } from "react";
import AsyncSelect from "react-select/async";
import { FixedSizeList as List } from "react-window";
import { createFilter } from "react-select";

import "./styles.css";

const TestSelect = (vendorOptions) => {


const options = [];
for (let i = 0; i < vendorOptions["vendorOptions"].length; i = i + 1) {
  options.push({ value: vendorOptions["vendorOptions"][i], label: `${vendorOptions["vendorOptions"][i]}` });
}

const loadOptions = (inputValue, callback) => {
    setTimeout(() => {
      callback(options);
    }, 1000);
  };


const height = 35;

class MenuList extends Component {
  render() {
    const { options, children, maxHeight, getValue } = this.props;
    const [value] = getValue();
    const initialOffset = options.indexOf(value) * height;

    return (
      <List
        height={maxHeight}
        itemCount={children.length}
        itemSize={height}
        initialScrollOffset={initialOffset}
      >
        {({ index, style }) => <div style={style}>{children[index]}</div>}
      </List>
    );
  }
}

const TestSelectComponent = () => {
    
    return(
        <div className ="testDropdown">
          <AsyncSelect components={{ MenuList }} cacheOptions defaultOptions loadOptions={loadOptions} filterOption={createFilter({ ignoreAccents: false })}/>
        </div>
    )
}
    return (
    <TestSelectComponent />
    )
}
export default TestSelect
Ariel Vardy
  • 67
  • 1
  • 5
5

If you look you find that the default value is ignoreAccents: true. This default makes react-select invoke an expensive function called stripDiacritics twice. And this cause performance issues.

Include the ignoreAccents: false in react-select.

Example: filterOption={createFilter({ ignoreAccents: false })}

Artem Arkhipov
  • 7,025
  • 5
  • 30
  • 52
Hbas
  • 51
  • 1
  • 3