Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
VladimirMilenko opened this issue Oct 19, 2018 · 18 comments

Comments

Projects
None yet
@VladimirMilenko
Copy link

commented Oct 19, 2018

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

@M1K3Yio

This comment has been minimized.

Copy link

commented Oct 19, 2018

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

@VladimirMilenko

This comment has been minimized.

Copy link
Author

commented Oct 19, 2018

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

@compwright

This comment has been minimized.

Copy link

commented Oct 26, 2018

Duplicate of #2711

@endbay

This comment has been minimized.

Copy link

commented Oct 29, 2018

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;

@shunfan

This comment has been minimized.

Copy link

commented Nov 15, 2018

@endbay It worked for me. Thanks!

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

This comment has been minimized.

Copy link

commented Nov 25, 2018

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

@malyushkin

This comment has been minimized.

Copy link

commented Nov 26, 2018

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?

@endbay

This comment has been minimized.

Copy link

commented Nov 27, 2018

        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.

@sebastiendan

This comment has been minimized.

Copy link

commented Dec 17, 2018

@shunfan solution (object destructuring) is cleaner to me

@CWSites

This comment has been minimized.

Copy link

commented Dec 20, 2018

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

@kotvasili

This comment has been minimized.

Copy link

commented Jan 7, 2019

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

@willbee28

This comment has been minimized.

Copy link

commented Feb 13, 2019

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 !

@VincentLanglet

This comment has been minimized.

Copy link

commented Mar 11, 2019

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 ?

@juanpicado

This comment has been minimized.

Copy link

commented Mar 12, 2019

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={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 #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.

@VincentLanglet

This comment has been minimized.

Copy link

commented Mar 12, 2019

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.

@johnnyreilly

This comment has been minimized.

Copy link

commented Apr 27, 2019

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

@SeanRoberts

This comment has been minimized.

Copy link

commented May 7, 2019

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.

@mreliopez

This comment has been minimized.

Copy link

commented May 10, 2019

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.