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

Closed
VladimirMilenko opened this issue Oct 19, 2018 · 87 comments
Closed

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

VladimirMilenko opened this issue Oct 19, 2018 · 87 comments
Labels
issue/reviewed Issue has recently been reviewed (mid-2020)

Comments

@VladimirMilenko
Copy link

VladimirMilenko 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
Copy link

M1K3Yio 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
Copy link
Author

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

@compwright
Copy link

Duplicate of #2711

@endze1t
Copy link

endze1t 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
Copy link

shunfan commented Nov 15, 2018

@endbay It worked for me. Thanks!

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

@anarasimhan
Copy link

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
Copy link

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?

@endze1t
Copy link

endze1t 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
Copy link

@shunfan solution (object destructuring) is cleaner to me

@CWSites
Copy link

CWSites commented Dec 20, 2018

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

@kotvasili
Copy link

kotvasili 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
Copy link

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
Copy link

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
Copy link

juanpicado 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
Copy link

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
Copy link

johnnyreilly 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
Copy link

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
Copy link

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

@Haosik
Copy link

Haosik commented Jul 24, 2019

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

@nadzimo
Copy link

nadzimo commented Jul 31, 2019

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.

@willbee28
Copy link

@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.

@nadzimo
Copy link

nadzimo commented Jul 31, 2019

@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.

@willbee28
Copy link

willbee28 commented Aug 1, 2019 via email

@ukhanzada-nisum-com
Copy link

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

If you guys are using pretty old version like in my case 1.2.0 directly pass ignoreAccents={false} to Select component.

@rcketscientist
Copy link

@ebonow

We also would like to address the issues caused by Emotion with the hopes of creating an unstyled version that removes that dependency.

What issues specifically, is this a render delay? I believe we're seeing an issue in a large form where potentially hundreds of selects can create a poor UX due to render delay. If this is related, is there an existing issue? I may want to contribute if I can navigate the emotion spaghetti and ripples it'll cause.

@wallzry
Copy link

wallzry commented Sep 28, 2022

Hi everyone! I spent a few days solving the dropdown list freezing issue. I tried using the above described methods of creating MenuList using react-window. I also added a custom Option component, removing props onMouseMove and onMouseOver. Seems this fixed my problem.

Example with react-window codesandbox.

Update:

Example with react-virtuoso codesandbox.

MenuList

import React from "react";
import { FixedSizeList as List } from "react-window";

const OPTION_HEIGHT = 40;
const ROWS = 6;

const MenuList = ({
  options,
  children,
  getValue
}) => {
  const [value] = getValue();
  const initialOffset =
    options.indexOf(value) !== -1
      ? Array.isArray(children) &&
        children.length >= ROWS
        ? options.indexOf(value) >= ROWS
          ? options.indexOf(value) *
              OPTION_HEIGHT -
            OPTION_HEIGHT * 5
          : 0
        : 0
      : 0;

  return Array.isArray(children) ? (
    <List
      height={
        children.length >= ROWS
          ? OPTION_HEIGHT * ROWS
          : children.length * OPTION_HEIGHT
      }
      itemCount={children.length}
      itemSize={OPTION_HEIGHT}
      initialScrollOffset={initialOffset}
    >
      {({ style, index }) => {
        return (
          <div style={style}>
            {children[index]}
          </div>
        );
      }}
    </List>
  ) : (
    <div>{children}</div>
  );
};

export default MenuList;

Option

import React from "react";
import cx from "classnames";

const Option = ({
  children,
  isSelected,
  innerProps
}) => (
  <div
    className={cx("react-select__option", {
      "react-select__option_selected": isSelected
    })}
    id={innerProps.id}
    tabIndex={innerProps.tabIndex}
    onClick={innerProps.onClick}
  >
    {children}
  </div>
);

export default Option;

ReactSelect

import React from "react";
import Select from "react-select";

import MenuList from "./MenuList";
import Option from "./Option";

const ReactSelect = ({
  options,
  value,
  onChange,
  placeholder
}) => {
  return (
    <Select
      options={options}
      value={value && [value]}
      onChange={onChange}
      classNamePrefix="react-select"
      placeholder={placeholder}
      components={{
        MenuList,
        Option
      }}
    />
  );
};

export default ReactSelect;

Works perfectly!
Btw styling can be done inside the MenuList component like this:

<div style={{ ...style, height: 80, padding: 10, cursor: "pointer" }}>
      {children[index]}
</div>

or like this: (tailwind itc)

<div style={style} className="hover:bg-main p-2 cursor-pointer">
       {children[index]}
</div>

@lukebennett88
Copy link
Collaborator

Hi @VladimirMilenko,

In an effort to sustain the react-select project going forward, we're cleaning up and closing old issues and pull requests.

We understand this might be inconvenient but in the best interest of supporting the broader community we have to direct our efforts towards the current major version.

If you aren't using the latest version of react-select please consider upgrading to see if it resolves any issues you're having.

However, if you feel this issue is still relevant and you'd like us to review it - please leave a comment and we'll do our best to get back to you!

@loiclecolier
Copy link

loiclecolier commented Nov 25, 2022

I have 50,000 items in my Select from react-select. I tried many things to improve performance: set ignoreAccents to false, disable onMouseMove and onMouseOver events and virtualization. But it's still slow.

Taking up @lynxtaa 's idea, I found the react-select-async-paginate library which allows you to "paginate" the items, and to retrieve the ones you want with the search.

The library : https://www.npmjs.com/package/react-select-async-paginate
Example: https://codesandbox.io/s/10r1k12vk7?file=/src/App.jsx:673-688

@aprilmintacpineda
Copy link

I have just 1000 items, it's already very laggish.

@keepforever
Copy link

i love how this thread is still going after all this time.

just ask GPTChat to write your implementation.

Or paste (a simplified version) of your implementation into GPTChat and ask it to correct. Tell it what's wrong. Show it where it hurts. It will help you.

🤖🤖🤖

@aprilmintacpineda
Copy link

Yeah, well, I was brought here in search of a built-in option for virtualization, honestly, I believe that a library that handles select dropdowns should have that option built-in. The fact that this is still going since 2018 should already tell everyone that there's real demand for it.

But since react-select seems to not be bothered by it, I just followed the react-window approach.

@rcketscientist
Copy link

Yeah, well, I was brought here in search of a built-in option for virtualization, honestly, I believe that a library that handles select dropdowns should have that option built-in. The fact that this is still going since 2018 should already tell everyone that there's real demand for it.

But since react-select seems to not be bothered by it, I just followed the react-window approach.

The entitlement of kids these days.

@aprilmintacpineda
Copy link

Yeah, well, I was brought here in search of a built-in option for virtualization, honestly, I believe that a library that handles select dropdowns should have that option built-in. The fact that this is still going since 2018 should already tell everyone that there's real demand for it.
But since react-select seems to not be bothered by it, I just followed the react-window approach.

The entitlement of kids these days.

Such a judgemental state of mind.

@SeanRoberts
Copy link

Yeah, well, I was brought here in search of a built-in option for virtualization, honestly, I believe that a library that handles select dropdowns should have that option built-in. The fact that this is still going since 2018 should already tell everyone that there's real demand for it.

I'm pretty sure this project is accepting PRs

@aprilmintacpineda
Copy link

There's a PR addressing the performance on large lists. The solution followed the memoization of buildCategorizedOptions rather than virtualization.

@Methuselah96
Copy link
Collaborator

If you're referring to #5450, virtualization and disabling stripping diacritics have a much bigger impact on performance for large lists. That PR just makes it even faster assuming you're already doing those performance improvements.

@gydotitulaer
Copy link

gydotitulaer commented Dec 17, 2022

Hi @VladimirMilenko,

In an effort to sustain the react-select project going forward, we're cleaning up and closing old issues and pull requests.

We understand this might be inconvenient but in the best interest of supporting the broader community we have to direct our efforts towards the current major version.

If you aren't using the latest version of react-select please consider upgrading to see if it resolves any issues you're having.

However, if you feel this issue is still relevant and you'd like us to review it - please leave a comment and we'll do our best to get back to you!

We’ve updated the tool from v2.x to v5.x last week and the performance issues are still there. I wanted to let you know.

i’ve seen couple of workarounds and tips how to improve the performance, we are going to look into them next week and will let you guys know our findings.

Edit:
We did not check other options for performance gains apologies

@vikrant-gembrill
Copy link

ignoreAccents

if i am using the filterOption for custom logic, then how can i set ignoreAccents:
i am using filterOption like this

filterOption={(option, inputValue) => {
                   if (option?.data?.text) {
                     return (
                       (
                         option.data.text
                           .toString()
                           .toLowerCase()
                           .match(inputValue.toString().toLowerCase()) || []
                       ).length > 0
                     );
                   }
                 }}

@timjahn93
Copy link

Hi @vikrant-gembrill,

Same problem. Did you find a solution?

@vikrant-gembrill
Copy link

@timjahn93 did not find a solution to filter option issue but, what i found was react select was very slow when the developer tools was open in chrome. Also, if there is huge data, better use virtualization with react window. Also check your extensions.

@timjahn93
Copy link

timjahn93 commented Oct 28, 2023

@vikrant-gembrill Okay, I am using virtualization already, but using my own filter option, react select is too slow with high data and the page collapses. With createFilter and ignoreAccents it is way more performant.

@Tibesti
Copy link

Tibesti commented Mar 12, 2024

Just as a reference for anyone else who might encounter this problem, the solution using filterOption works like magic:
filterOption={createFilter({ ignoreAccents: false })}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
issue/reviewed Issue has recently been reviewed (mid-2020)
Projects
None yet
Development

No branches or pull requests