React-select: React-select is slow when you have more than 1000 items

Created on 19 Oct 2018  ยท  50Comments  ยท  Source: JedWatson/react-select

Low performance on large sets of options

React-select slows down when you have a huge array of data. Mouse
screenshot 2018-10-19 at 15 29 43
screenshot 2018-10-19 at 15 51 21

FPS drops so low on mouseover, that i can barely use it.
In my real case, where i have 1010 items, which i need to show(and i can't load them as user types) - i cannot do anything at all.

You can find a simple example in codesandbox.

https://codesandbox.io/s/q8l6xnvz7w

[Violation] 'mouseover' handler took 346ms
[Violation] 'mouseover' handler took 184ms
[Violation] 'mouseover' handler took 197ms
[Violation] 'mouseover' handler took 172ms
[Violation] 'mouseover' handler took 161ms
[Violation] 'mouseover' handler took 150ms
[Violation] 'mouseover' handler took 167ms
[Violation] 'mouseover' handler took 172ms
[Violation] 'mouseover' handler took 156ms
[Violation] 'mouseover' handler took 166ms

React-select version: 2.1.0

issuenhancement issureviewed

Most helpful comment

Two things to look into that may help with your issues, we also had some issues regarding large lists.

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

Take a look at this reported bug https://github.com/JedWatson/react-select/issues/2850

There's two examples using React-Window, which greatly improves the performance.

https://codesandbox.io/s/lxv7omv65l

All 50 comments

Two things to look into that may help with your issues, we also had some issues regarding large lists.

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

Take a look at this reported bug https://github.com/JedWatson/react-select/issues/2850

There's two examples using React-Window, which greatly improves the performance.

https://codesandbox.io/s/lxv7omv65l

@M1K3Yio I suppose, that the main issue is hover state of item in list. Which is also slow in your example.

Duplicate of #2711

I have a quick work around fix: Pass your own MenuList component, iterate through the childs and remove this props below. It stops lagging then. But you need to add styles for hover via css then.

delete key.props.innerProps.onMouseMove;
delete key.props.innerProps.onMouseOver;

@endbay It worked for me. Thanks!

const { onMouseMove, onMouseOver, ...newInnerProps } = props.innerProps

I am running into the same issue. I have only about 300 items in the list. Looking at the code, removing the onMouseOver and onMouseMove will cause some functionality to not work.

Will this be fixed anytime soon?

Anand

I have a quick work around fix: Pass your own MenuList component, iterate through the childs and remove this props below. It stops lagging then. But you need to add styles for hover via css then.

delete key.props.innerProps.onMouseMove;
delete key.props.innerProps.onMouseOver;

Could you show the code?

        const MenuList = function MenuList(props) {
            const children = props.children;

            if (!children.length) {
                return (<div className="myClassListName">{children}</div>);
            }

            return (
                    <div className="myClassListName">
                        {children.length && children.map((key, i) => {
                            delete key.props.innerProps.onMouseMove; //FIX LAG!!
                            delete key.props.innerProps.onMouseOver;  //FIX LAG!!

                            return (
                                <div className="myClassItemName" key={i}>{key}</div>
                            );
                        })}
                    </div>
            );
        };

    <Select
           components={{
                            MenuList
                        }}
         />

Probably not the best approach. But it's working for me. If anyone has suggestions for improvement, I would be grateful.

@shunfan solution (object destructuring) is cleaner to me

Can someone post a full code sample of the solution that @shunfan proposed?

const Option = ({ children, ...props }) => {
  const { onMouseMove, onMouseOver, ...rest } = props.innerProps;
  const newProps = Object.assign(props, { innerProps: rest });
  return (
    <components.Option
      {...newProps}
    >
      {children}
    </components.Option>
  );
};

Example of @shunfan solution

It is insane that ignoreAccents' default is set to true!

Setting this to false, in my case where I'm pairing react-select with react-virtualized, resulted in huge perforance boosts, with very little lag if any. Been looking for this solution for a long time. Thanks @endbay !

const { onMouseMove, onMouseOver, ...newInnerProps } = props.innerProps

Seems to only be a workaround.
Is it planned to fix this problem ? Are there any ideas to start with ?

The performance penalty for filter each option in a quite large set of data is insanely slow on force reconciliation a new react-select , as https://github.com/JedWatson/react-select/issues/3128#issuecomment-431397942 suggested must be disabled, I even would recommend the author disable it by default.

 <Select filterOption={null}

In my case, I am dealing with a set of 2000 items and I do not need to filter, so I disable the whole thing, the speed bost quite a lot, check the time just the filter method takes.

Screenshot 2019-03-12 at 16 35 38

Furthermore, following the recommendation of https://github.com/JedWatson/react-select/issues/2850 I'm using
react-window with a custom MenuList for only large datasets. Here the implementation in Typescript.

import React, { Component, ReactNode } from "react";
import { FixedSizeList as List } from "react-window";
// <= IOption is my custom interface for each option
import { IOption } from "./FormSelect";
import { MenuListComponentProps } from "react-select/lib/components/Menu";
import { ValueType } from "react-select/lib/types";

const height: number = 35;
class MenuList extends Component<MenuListComponentProps<IOption>> {
    public render(): React.ReactElement<HTMLElement> {
        const { options, children, maxHeight, getValue } = this.props;
        // @ts-ignore
        const [value]: ValueType<IOption> = getValue();
        const initialOffset: number = options.indexOf(value) * height;
        const childrenOptions: React.ReactChild[] = React.Children.toArray(children);

        return (
            <List
                height={maxHeight}
                itemCount={childrenOptions.length}
                itemSize={height}
                initialScrollOffset={initialOffset}
            >
                {this.rendersomething(children)}
            </List>
        );
    }

    public rendersomething = (children: ReactNode): any => {
        return (props: any): React.ReactChild => {
            const { index, style } = props;

            return <div style={style}>{children[index]}</div>;
        };
    }
}

export default MenuList;

I hope that helps, this is still a workaround, but it works for me pretty well.

The performance penalty for filter each option in a quite large set of data is insanely slow on force reconciliation a new react-select , as #3128 (comment) suggested must be disabled, I even would recommend the author disable it by default.

 <Select filterOption={false}

In my case I am dealing with set of 2000 items and I do not need to filter, so I disable the whole thing, the speed bost quite a lot, check the time just the filter method takes.

There is no interest of autocomplete without filtering the options. It's just a simple select otherwise.

If it's the typing lag that you're bumping on (I was) then it's actually the filterOption={createFilter({ignoreAccents: false})} suggestion by @M1K3Yio that's the gem.

Using this massively reduces the lag when you're typing into the select. I've got a sandbox here that illustrates: https://codesandbox.io/s/zn70lqp31m?fontsize=14

And I've blogged it too

Removing the mouse events from Option and providing your own hover styling via CSS improves performance considerably, but it means the isFocused prop doesn't get updated on hover, which results in multiple elements being highlighted when you hover (assuming you have a highlight style set for isFocused

Even a list with relatively few items is very slow with the mouse events enabled, I have a list with <100 items and running my mouse up and down the list is super laggy. I'm not sure what the solution could be here, removing styling for isFocused makes the component impossible to use with the keyboard. Another option might be to make use of React.memo and possibly useCallback or other memoization tricks in order to prevent so many re-renders. In theory when focus changes only two options should need to be updated: the option that is going from not focused to focused, and the option that's going from focused to not focused. I tried the super naive approach of just:

export default React.memo(Option, (prevProps: any, nextProps: any) => {
  return prevProps.isFocused === nextProps.isFocused;
});

But there are definitely more props than just those changing as subsequent focus changes after the first one don't cause any elements to re-render.

If it's the typing lag that you're bumping on (I was) then it's actually the filterOption={createFilter({ignoreAccents: false})} suggestion by @M1K3Yio that's the gem.

Using this _massively_ reduces the lag when you're typing into the select. I've got a sandbox here that illustrates: https://codesandbox.io/s/zn70lqp31m?fontsize=14

And I've blogged it too

This did the trick. Thanks man

Two more hints that may help someone (I hope), that helped me after struggling with even react-modal-solution being a bit laggy. Deadly lags appeared while hovering and "mouseovering" the dropdown elements, with even "poor" 250 options. So hints that may help:

  • Deactivate browser's extensions. Maybe incognito tab may help to do it easily. Noticed that one of "Violation" warnings came from something like "content.js" from one of extensions.
  • Close developer tools. Seriously, it's like twice more fps with them closed xD

Facing slowness too for only 250 items making the widget a no-go for us in production. I'm on version 3.0.4. I tried destructing innerProps to remove the hover props as @endbay and @kotvasili suggested but it still didn't work for me.

@nadzimo The ignore accents help a ton, idk if you've done that already, but I have about that many items if not more in my dropdown. Also using the custom made MenuList item with react-virtualized. I didn't use the specific one in this thread, but another shorter one in another github forum/question. Let me know if you need help finding it.

@willbee28, I wasn't familiar with react-virtualized and just checked it out โ€“ looks slick. Will give it a go.
I tried the ignore accents but no improvement. I think my issue is not related to search, it looks like it's related to the hover events.

I recommend using it with the MenuList component, utilizing the List
component from react-virtualized.

On Wed, Jul 31, 2019, 4:50 PM nadzimo notifications@github.com wrote:

@willbee28 https://github.com/willbee28, I wasn't familiar with
react-virtualized and just checked it out โ€“ looks slick. Will give it a go.

โ€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/JedWatson/react-select/issues/3128?email_source=notifications&email_token=AGI42AODEKOSVLTCIX2EHI3QCH3JRA5CNFSM4F6BPYAKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD3IQR4Q#issuecomment-517015794,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AGI42APFLDEWOZI5LTMHBDDQCH3JRANCNFSM4F6BPYAA
.

Facing slowness too for only 250 items making the widget a no-go for us in production. I'm on version 3.0.4. I tried destructing innerProps to remove the hover props as @endbay and @kotvasili suggested but it still didn't work for me.

I've used the ignore accents when calling the ReactSelect, that worked for me, give it a try..!

<ReactSelect className="react-select" styles={customStyles} ignoreAccents={false} {...props} />

I was able to get a satisfactory user experience by just making it an AsyncSelect.

https://react-select.com/async

Here's my implementation of AsyncSelect (after):

https://github.com/keepforever/deck-app-web/tree/05-add-alt-card/src/comps/Deck/AsyncBoilerAutoComplete

Note, it looks super complex because I'm using a ton of boiler from the Material-Ui integreation. But, I didn't have to change much between the props passed to Select and AsyncSelect.

Here's Select (poor performance) before.

https://github.com/keepforever/deck-app-web/tree/05-add-alt-card/src/comps/Deck/BoilerAutoComplete

Summarized and working for me:

  • custom option component to remove laggy onMouseMove / onMouseHover
  • deactivating ignoreAccents to speed up search
  • custom css hover
  • isFocused false for every option such that no option is getting the standard highlight class of react-select
import React from 'react';
import Select, {createFilter, components} from 'react-select';

class YourComponent extends React.Component{
    constructor(props){
    super(props);
    }
    render(){
    return <>
                    <Select options={yourOptions}                            
                        filterOption={createFilter({ignoreAccents: false})}
                        components={{Option: CustomOption}}/>
    </>;
    }
}

class CustomOption extends React.Component {
    constructor(props) {
        super(props);
    }

    render() {
        const {innerProps, isFocused, ...otherProps} = this.props;
        const {onMouseMove, onMouseOver, ...otherInnerProps} = innerProps;
        const newProps = {innerProps: {...otherInnerProps}, ...otherProps};
        return (
            <components.Option {...newProps} className="your-option-css-class">{this.props.children}
            </components.Option>
        );
    }
}

and your css file:

.your-option-css-class:hover {
     background-color: green;
}

Hope somebody can make use of this too.
I'd still prefer a fix.

Can I apply filterOption={createFilter({ignoreAccents: false})} to <AsyncCreatable/> ?

Actually, I did that but it won't work now.

If I put a filtering logic into loadOptions function, then I correctly filtered.

Can I apply filterOption={createFilter({ignoreAccents: false})} to <AsyncCreatable/> ?

Actually, I did that but it won't work now.

If I put a filtering logic into loadOptions function, then I correctly filtered.

For me filterOption didn't work, so i added ignoreAccents={false} on the <Select /> component.

Soo for example:

import Select from 'react-select';

<Select options={yourOptions} ignoreAccents={false} />

@gydotitulaer Thanks for your response. I intended to focus on async select components such as <AsyncSelect/>, <AsyncCreatable/> which should be along with loadOptions prop.

However, the problem I mentioned previously came out because I made a mistake with importing wrong function createFilter while I tried some sort of ways.

After I imported correct createFilter function from react-select, then it worked.

TLDR;

The following code worked as expected.

import {AsyncCreatable, createFilter} from 'react-select'

loadOptionsAsyncWithoutFiltering = () => {
    // ...
    return Promise.resolve([...someArrays])
}

// ...
render() {
    return (
    // ...
    <AsyncCreatable {...otherProps} filterOption={createFilter({ignoreAccents: false})}
        loadOptions={this.loadOptionsAsyncWithoutFiltering}
    />
    )
}

People coming from Material-UI, follow @LukasMueller187 solution. It's the most comprehensive one.

Had the same issue as everyone else in this thread, in our use case we really never needed to render the entire list if there was no search text entered, so I went with the below approach of just rendering the first 50 elements. Not sure if the useMemo is necessary tbh, someone more familiar with react-select's internals feel free to chime in on that. Either way, this solved the lag problems we were running into.

// The returned MenuList is the default react-select component
function MenuListOverride(props: MenuListComponentProps<OptionType>) {
    const children = React.useMemo(() => React.Children.toArray(props.children), [props.children]);
    return (
          <MenuList {...props}>
              {children.length > 50 ? children.slice(0, 50) : props.children}
          </MenuList>
     );
}

I have almost the same issue, but I'm displaying more than 3.000 elements. What i did was the to use React useMemo and customize the MenuList and the Option component. At start the display list takes 3.6s (3872 items) and later everything takes 2.5s. It's not a huge improvement but it's something. I think you could gain some ms using a normal for loop (well done) instead of a map function and display div instead of the <components.Option /> but that depends of the requirements

import Select, { components } from 'react-select';

function MenuList(props) {
  const makeOptions = useMemo(
    () => props.children.map(child => {
      const { onMouseMove, onMouseOver, ...rest } = child.props.innerProps;
      const tempProps = { ...child.props, ...{ innerProps: rest } };

      return <components.Option {...tempProps} key={rest.id} />;
    }),
    [props.options]
  );

  return <components.MenuList {...props}>{makeOptions}</components.MenuList>;
}

<Select
  components={MenuList}
  options={opt}
  value={value}
/>

We ended up using this:

import React from "react";

import { flavourOptions } from "./docs/data";
import Select, { components } from "react-select";
import { onlyUpdateForKeys, compose, mapProps } from "recompose";
import omit from "lodash/fp/omit";

const omitProps = (...keys) => mapProps(omit(keys));
const FastOption = compose(
  onlyUpdateForKeys(["isDisabled", "isSelected"]),
  omitProps("isFocused")
)(components.Option);

export default () => (
  <Select
    components={{
      Option: FastOption
    }}
    defaultValue={flavourOptions[2]}
    label="Single select"
    options={flavourOptions}
    styles={{
      option: (style, state) => ({
        ...style,
        "&:hover": {
          backgroundColor: state.theme.colors.primary25,
          color: state.theme.colors.neutral90,
        }
      })
    }}
  />
);

Solution for Typescript users:

import * as React from "react";
import { MenuListComponentProps, OptionTypeBase } from "react-select";
import { FixedSizeList } from "react-window";

...

export default class OptimizedMenuList extends React.Component<MenuListComponentProps<OptionTypeBase>> {
    private readonly height = 40;

    public render() {
        const { options, children, maxHeight, getValue } = this.props;
        const selectedValues = getValue();
        const initialOffset = selectedValues && selectedValues[0] ? options.indexOf(selectedValues[0]) * this.height : 0;

        return(
            <FixedSizeList
                height={maxHeight}
                itemCount={children!["length"]}
                itemSize={this.height}
                initialScrollOffset={initialOffset}
                width={""} // 100% width
            >
                {({ index, style }) => <div className="option-wrapper" style={style}>{children![index]}</div>}
            </FixedSizeList>
        );
    }
}
import * as React from "react";
import { components, OptionTypeBase, OptionProps } from "react-select";

...

export default class OptimizedOption extends React.Component<OptionProps<OptionTypeBase>> {

    public render() {
        delete this.props.innerProps.onMouseMove;
        delete this.props.innerProps.onMouseOver;

        return (
            <components.Option
                {...this.props}
            >
                {this.props.children}
            </components.Option>
        );
    }
}
import * as React from "react";
import AutoComplete, { createFilter } from "react-select";
import OptimizedMenuList from "./OptimizedMenuList";
import OptimizedOption from "./OptimizedOption";

...

<AutoComplete
    ...
    components={{MenuList: OptimizedMenuList, Option: OptimizedOption}}
    filterOption={createFilter({ignoreAccents: false})}
    ...
/>

Two things to look into that may help with your issues, we also had some issues regarding large lists.

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

Take a look at this reported bug #2850

There's two examples using React-Window, which greatly improves the performance.

https://codesandbox.io/s/lxv7omv65l

Hi @M1K3Yio , This might seems solving the problem . but when typing in search with no option matches , it would show the no options message

Two things to look into that may help with your issues, we also had some issues regarding large lists.
filterOption={createFilter({ignoreAccents: false})}
Take a look at this reported bug #2850
There's two examples using React-Window, which greatly improves the performance.
https://codesandbox.io/s/lxv7omv65l

Hi @M1K3Yio , This might seems solving the problem . but when typing in search with no option matches , it would show the no options message

I replaced the NoOptionsComponent with the custom no options component as there is something wrong in react-select / react-window which lead to the no options message component cannot be shown

import React from 'react';
import { components } from 'react-select';

const NoOptionsMessage = props => {

    const { children, ...newProps } = props;

    return (
        <components.NoOptionsMessage {...newProps} />
    );
};

export default NoOptionsMessage;

I'd like to update my code. This works like a charm for me ๐Ÿ˜Ž . I was able to work with more than 3000 items.

First of all, I created a new Menu list and I also made use of useMemo hook. I removed useless - for me and the client - props

function MenuList(props) {
  const makeOptions = useMemo(
    () =>
      props.children.map(child => {
        const {
          // this will be removed
          selectOption,
          isFocused,
          hasValue,
          setValue,
          innerRef,
          isRtl,
          //
          innerProps,
          ...restChild
        } = child.props;
        const { onMouseMove, onMouseOver, ...rest } = innerProps;
        const tempProps = { ...restChild, ...{ innerProps: rest } };

        return <components.Option {...tempProps} key={rest.id} />;
      }),
    [props.options]
  );

  return <components.MenuList {...props}>{makeOptions}</components.MenuList>;
}

Then I made use of the CreatetableSelect component which helped me to add items one by one ( or in my case 160 by 160) as soon as I reached the bottom of the list. This seems sort of virtualized list. I noticed that everytime I closed the menu and the list was already huge, when opened it again was slowly; to solve this I clear the list of items, starting everything again from an small amount of items.

function filterColors(data) {
  // You can replace includes for another functions... I was just a little bit lazy ๐Ÿ˜„ 
  return inputValue => data.filter(i => i.label.toLowerCase().includes(inputValue.toLowerCase()));
}

const Component = props => {
const [state, setState] = useState([]);

const AMOUNT_ITEMS = 160;
const filterFnc = filterColors(props.options);

useEffect(() => {
  const { options } = props;
  // Load the first amount of items
  const optSlice = options.slice(0, AMOUNT_ITEMS); 
  onCreateOptionHandler(optSlice);
}, []);

// add new items
function onCreateOptionHandler(tmpOptions) {
    setState([...state, ...tmpOptions]);
}
// remove the added items and left the very first amount of them
function onMenuCloseHandler() {
    setState(options.slice(0, AMOUNT_ITEMS));
}

// We filter the options (the added items and not)
function loadOptionsHandler(inputValue, callback) {
    const time = setTimeout(() => {
      callback(filterFnc(inputValue));
      clearTimeout(time);
    }, 1600);
  }

// add items everytime we reach the end of the menu list
function onMenuScrollToBottomHandler() {
    const { options } = props;
    const tmpOptionSize = state.length;

    if (options.length !== tmpOptionSize) {
        const optSlice = options.slice(tmpOptionSize, tmpOptionSize + AMOUNT_ITEMS);
        onCreateOptionHandler(optSlice);
     }
  }

return (
  <CreatableSelect
     onMenuScrollToBottom={onMenuScrollToBottomHandler}
     onCreateOption={onCreateOptionHandler}
     onMenuClose={onMenuCloseHandler}
     loadOptions={loadOptionsHandler}
     formatCreateLabel={inputValue => ''} // needed to remove the 'Create new ...' label
     options={state}
   />
);
}

Based on the responses above, I came up with this:

import { MenuListComponentProps } from 'react-select/lib/components/Menu'
import { FixedSizeList } from 'react-window'
import { OptionProps } from 'react-select/lib/components/Option'
import { components } from 'react-select'
import React from 'react'

export const optimizeSelect = {
  components: {
    MenuList: OptimizedMenuList,
    Option: OptimizedOption,
  },
}

function OptimizedMenuList(props: MenuListComponentProps<SelectOption>) {
  const { options, children, maxHeight, getValue } = props
  if (!children || !Array.isArray(children)) return null

  const height = 35
  const selectedValues = getValue() as SelectOption[]
  const initialOffset = selectedValues[0] ? options.indexOf(selectedValues[0]) * height : 0

  return (
    <FixedSizeList
      width={''}
      itemSize={height}
      height={maxHeight}
      itemCount={children.length}
      initialScrollOffset={initialOffset}
    >
      {({ index, style }) => (
        <div className="option-wrapper" style={style}>
          {children[index]}
        </div>
      )}
    </FixedSizeList>
  )
}

function OptimizedOption(props: OptionProps<SelectOption>) {
  delete props.innerProps.onMouseMove
  delete props.innerProps.onMouseOver
  return <components.Option {...props}>{props.children}</components.Option>
}

// SelectOption is specific to this example
// and may not work with other projects
type SelectOption = {
  value: string
  label: string
  [key: string]: string
}

Then, whenever I have to optimize a <Select /> I pass a couple of extra props, ie.:

<Select
  {...theOtherProps}
  filterOption={createFilter({ ignoreAccents: false })}
  components={optimizeSelect.components}
/>

Given the required dependencies are installed, others using typescript should be able to create a file for the optimizeSelect and use it as in the example above.

hm, this component was praised as "the best solution" and it cannot handle a couple thousand entries?

after I click, it takes seconds to render the drop down and in the console I have

Warning: React instrumentation encountered an error: RangeError: Maximum call stack size exceeded

a bit disappointed, honestly.

neither ignoreAccents={false} nor filterOption={createFilter({ignoreAccents: false})}

seems to help
we have 9k entries

my whole browser freezes and stutters, it's absolutely unusable

I'd remove Jossnaz's comment above โ˜๏ธ . It's just not useful at all and this person clearly doesn't understand how hard it is to build a component like this. Try using a native browser's dropdown and tell me how far you get in functionality!

Just wanted to say that I found this issue while trying to debug some performance problems I was having and found all the discussion very enriching. I'm using react-virtualized as well and my selects start to lag around 500 elements.

I only wanted to ask two things:
1- Is there any loss in functionality if I remove the mouse handlers?
2- Would it be useful to have a wiki entry explaining this? (edit: I would volunteer for this if that's the case) Like many others, my use case is to provide search in a large dataset, so, knowing this in advance (by referencing this issue in the docs) would be very helpful to plan ahead.

Thanks for the work!

LOL @damianmr yes, go and sushh everyone who is disappointed to have to spend time figuring that one out, then proceed to point out they should write a wiki entry ๐Ÿ˜Š

I recommend you read my comment above โ˜๏ธ. It has the only solution that worked for me. The other solutions as pointed out in my other comment, didn't work for me. I'm sure it's helpful to some, maybe even you. Good luck

Two things to look into that may help with your issues, we also had some issues regarding large lists.

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

Take a look at this reported bug #2850

There's two examples using React-Window, which greatly improves the performance.

https://codesandbox.io/s/lxv7omv65l

THIS WORKED PERFECTLY FOR ME, comment link

I've started a fork of react-select. Feel free to resubmit this issue on the fork and/or submit a PR to resolve this issue on the fork and we can get it merged and released.

EDIT: :tada: I've archived the fork now that we've got some momentum in this repo and Jed is involved again. Sorry for the disturbance!

Worth noting that removing the mouse event handlers breaks keyboard navigation with the arrow keys. The whole reason focus is handled in javascript rather than css in the first place is to be able to navigate with both the mouse and keyboard like with a native select element.

It's a shame because removing those event handlers and applying the hover styles in css performs much much faster, but it's at the expense of breaking keyboard functionality.

I was able to virtualize the MenuList with react-virtuoso and it improves things a decent amount but it still seems to be bottlenecked by having to make all the React.createElement calls in renderMenu https://github.com/JedWatson/react-select/blob/master/packages/react-select/src/Select.js#L1681 every time focus state changes. I don't think there's any exposed way to override that (well... short of extending the Select class but ๐Ÿ˜ฌ, edit: this is actually not possible because of HOCs, nor should it be really).

So I think the way to fix the hover lag while keeping keyboard functionality would be to

  1. Figure out how to not make the React.createElement calls for options that aren't actually being rendered. The virtualization solutions in this thread prevent rendering to the real DOM, which helps, but those virtual DOM calls still kill performance at a certain point.
  2. Memoize the options so on focus change only two options render: the one that lost focus and the one that gained focus. (Currently it renders all of them which is a big waste.)

It'll probably be a while before I come back to this to try to figure out implementing those so I figured I'd leave my thoughts on it here in case anyone wants to try to figure it out.

I think delegation might help. Instead of subscribing every option element to mouseMove and mouseOver events we can subscribe only the parent element.

Also optimizing the component for large lists Thanks @rafaelbiten
https://github.com/JedWatson/react-select/pull/2806#issuecomment-744386432

Greetings,

I think these are important points and appreciate the conversation that taking place here. I have closed https://github.com/JedWatson/react-select/issues/2850 to continue the discussion here. There were several solutions and workarounds presented there, so feel free to browse through that thread should anyone wish to examine any implementations that might help users render more items in a more performant manner.

I agree that this conversation here is helpful for a lot of users that try to use lots of items.
Nevertheless, my opinion is that this issue should get fixed by the maintainers due to the dozens of different workarounds depending on the usecase. It is obvious that this (by the way awesome) react addon should be able to handle lots of items. Without trying several workarounds that are not straightforward.
Thanks in advance!

@LukasMueller187 agreed. Sharing these "work arounds" and solutions in the interim, but there is momentum and Jed has been communicative so it's very promising to see where things are headed.

Was this page helpful?
0 / 5 - 0 ratings