Skip to content

onMenuScrollToBottom not fired on desktop when the scrollbar is mouse-dragged to the bottom #3232

Open

Description

@cdax

Greetings!

Thank you so much for taking time out to maintain react-select

One of our users reported that the dropdowns in our product (the ones powered by react-select) were not loading further options when she scrolled to the bottom. On further investigation, we realized that this was because react-select does not fire onMenuScrollToBottom when the scrolling is performed by dragging the scrollbar to the bottom using a mouse-drag. Unfortunately for us and for this user, her mouse lacks a scroll wheel and so this is the only way she can scroll to the bottom.

Here's a code sandbox demonstrating this issue: https://codesandbox.io/s/x20ppz92xw Note that when scrolled using the mousewheel, an alert dialog is displayed when the menu is scrolled to the bottom. However, when the scrollbar is dragged to the bottom using the mouse, the alert dialog isn't displayed.

Looking at the source code, I can see that the ScrollCaptor is only listening for the wheel, touchstart and touchmove events. The use-case of dragging the scrollbar with a mouse is ignored.

I've confirmed that this works in the previous major version (v1.2.1)

Activity

  1. vtaits commented on Dec 24, 2018

    @vtaits

    I understood that onMenuScrollToBottom is weakness of react-select. It also not working with keyboard navigation and for asynchronous select. My workaround is redefine MenuList component.

    https://github.com/vtaits/react-select-async-paginate/blob/master/src/wrap-menu-list.jsx

    It is my decorator, but is uses custom handler handleScrolledToBottom, you can replace it with onMenuScrollToBottom. Looped setTimeout is not beautiful but works.

  2. Mawaheb commented on Jan 8, 2019

    @Mawaheb

    any updates on this one? I am having the same issue, I am relying on onMenuScrollToBottom to fetch and append paginated values to a drop down, it won't work if the user used keyboard or scrollbar dragging :/.

  3. jdt3969 commented on Feb 5, 2019

    @jdt3969

    Also looking for an update. This seems like a core bug and necessary for accessibility

  4. kaiz-rently commented on May 16, 2019

    @kaiz-rently

    Any updates on this one ?

  5. M-Yankov commented on Jun 20, 2019

    @M-Yankov

    I understood that onMenuScrollToBottom is weakness of react-select. It also not working with keyboard navigation and for asynchronous select. My workaround is redefine MenuList component.

    https://github.com/vtaits/react-select-async-paginate/blob/master/src/wrap-menu-list.jsx

    It is my decorator, but is uses custom handler handleScrolledToBottom, you can replace it with onMenuScrollToBottom. Looped setTimeout is not beautiful but works.

    Here is the correct link of the wrapper https://github.com/vtaits/react-select-async-paginate/blob/master/packages/react-select-async-paginate/src/wrap-menu-list.jsx
    https://github.com/vtaits/react-select-async-paginate/blob/master/packages/react-select-async-paginate/src/wrapMenuList.tsx

  6. added
    issue/bug-unconfirmedIssues that describe a bug that hasn't been confirmed by a maintainer yet
    on May 29, 2020
  7. added
    issue/has-prIssue has a PR attached to it that may solve the issue
    on Jul 6, 2020
  8. linked a pull request that will close this issueFix scrolling issues #4484on Mar 12, 2021
  9. OS-rafaelduarte commented on Dec 28, 2021

    @OS-rafaelduarte

    Any updates on this? @ebonow

  10. Duarte10 commented on Dec 28, 2021

    @Duarte10

    Created PR #4970 to fix this issue.

  11. faraazHasan commented on Jan 19, 2023

    @faraazHasan

    Please merge #4970 to fix this issue.

  12. imranisdev commented on Mar 24, 2023

    @imranisdev

    HI Please can we get thie MR merged. That you

  13. jacobsickels commented on May 11, 2023

    @jacobsickels

    Bumping for visibility, please merge the above MR

  14. jannnik commented on Nov 29, 2023

    @jannnik

    We are also waiting for this fix!

  15. AbdullahPS commented on Nov 30, 2023

    @AbdullahPS

    Another one who is wating :(

  16. malinindev commented on Feb 29, 2024

    @malinindev

    Why is this PR #4970 are still not merged? 3 year have passed, fix contains few minor changes. Are there any contributors?

  17. khachatryna commented on May 10, 2024

    @khachatryna

    I've set captureMenuScroll property true and it works on my side.

  18. DavidSentiurin commented on Jun 5, 2024

    @DavidSentiurin

    Thank you for the helpful select library and your work, guys!

    I am also waiting for this fix.

  19. amaralpeek commented on Jun 18, 2024

    @amaralpeek

    Any update on this issue? It still doesn't work, even with khachatryna suggestion to use captureMenuScroll.

    Does anyone have a workaround for this problem?

  20. malinindev commented on Jun 18, 2024

    @malinindev

    Does anyone have a workaround for this problem?

    I've created a custom onMenuScrollToBottom function as a workaround in my project.

    Firstly, we need to declare new prop type for our select. Lets create file react-select.d.ts:

    import { Props } from 'react-select/dist/declarations/src/Select';
    
    declare module 'react-select/dist/declarations/src/Select' {
      export interface Props {
        customOnMenuScrollToBottom?: () => void;
      }
    }

    Now you can pass this custom prop to the ReactSelect component.
    Also you need to create custom MenuList component and pass it to ReactSelect too:

    import { YoutCustomMenuListComponent } from './components/YoutCustomMenuListComponent';
    
    <ReactSelect
      customOnMenuScrollToBottom={() => {
        console.log('scrollToBottom');
      }}
      components={{
        MenuList: YourCustomMenuListComponent,
      }}
    ....

    Now lets create a YourCustomMenuListComponent:

    import { SelectOption } from '../../type'; // Your select option type.
    
    type YourCustomMenuListComponentProps = ReactSelectMenuListProps<
      SelectOption,
      false,
      GroupBase<SelectOption>
    >;
    
    export const YourCustomMenuListComponent = ({
      children,
      ...rest
    }: YourCustomMenuListComponentProps) => {
      const { customOnMenuScrollToBottom } = rest.selectProps;
    
      const onScrollToBottomTrigger = useInfiniteScroll(customOnMenuScrollToBottom);
    
      return (
        <components.MenuList<SelectOption, false, GroupBase<SelectOption>>
          className={styles.menuList}
          {...rest}
        >
          {children}
          {customOnMenuScrollToBottom && onScrollToBottomTrigger}
        </components.MenuList>
      );
    };

    And useInfiniteScroll is a pretty standard hook which uses Intersection Observer, like this:

    export const useInfiniteScroll = (callback?: () => void): JSX.Element => {
      const observerRef = useRef<IntersectionObserver | null>(null);
      const lastElementRef = useRef<HTMLDivElement | null>(null);
    
      useEffect(() => {
        if (!callback) {
          return undefined;
        }
    
        if (lastElementRef.current) {
          observerRef.current = new IntersectionObserver((entries) => {
            if (entries[0].isIntersecting) {
              callback();
            }
          });
          observerRef.current.observe(lastElementRef.current);
        }
    
        return () => {
          if (observerRef.current) {
            observerRef.current.disconnect();
            observerRef.current = null;
          }
        };
      }, [callback]);
    
      return <div ref={lastElementRef} />;
    };

    We are finished. The callback customOnMenuScrollToBottom will be triggered each time when we scroll to the bottom of the component. I created an infinity scroll with data fetching inside the Select component based on this solution. Hope it helps someone

  21. amaralpeek commented on Jun 19, 2024

    @amaralpeek
    import { Props } from 'react-select/dist/declarations/src/Select';
    
    declare module 'react-select/dist/declarations/src/Select' {
      export interface Props {
        customOnMenuScrollToBottom?: () => void;
      }
    }

    Hey malininss thanks for the solution it will do for now, it works and does the job. This is something that should be in the component, but anyway I appreciate your help!

  22. malinindev commented on Jun 20, 2024

    @malinindev

    This is something that should be in the component

    Hi @amaralpeek. Do you mean that we are supposed to place this declaration inside the Select component?
    If yes, I prefer to place all declarations (such as this third party lib interface extension) in a separate file with a d.ts postfix. It is more transparent because this types are used not only inside the component, but also in other parts of the application.

    Maybe I didn't get you right. Anyway, it depends on your coding style and can be done in different ways

  23. amaralpeek commented on Jun 25, 2024

    @amaralpeek

    Hey malininss, Sorry for the confusion, When I reply I used the "Quote Reply" and the final part was related to the react-select package which should be a fixed part of the component, nothing related with your solution and thanks again for that!

  24. samdark commented on Aug 16, 2024

    @samdark

    We're affected by it as well. @JedWatson is it possible to review/merge #4970?

  25. mKlus commented on Aug 29, 2024

    @mKlus

    We're affected by it as well. @JedWatson is it possible to review/merge #4970?

  26. SairamAlagapan commented on Nov 4, 2024

    @SairamAlagapan

    @JedWatson Is it possible to review/merge #4970 ?

  27. GilTorch commented on Jan 21, 2025

    @GilTorch

    Does anyone have a workaround for this problem?

    I've created a custom onMenuScrollToBottom function as a workaround in my project.

    Firstly, we need to declare new prop type for our select. Lets create file react-select.d.ts:

    import { Props } from 'react-select/dist/declarations/src/Select';

    declare module 'react-select/dist/declarations/src/Select' {
    export interface Props {
    customOnMenuScrollToBottom?: () => void;
    }
    }
    Now you can pass this custom prop to the ReactSelect component. Also you need to create custom MenuList component and pass it to ReactSelect too:

    import { YoutCustomMenuListComponent } from './components/YoutCustomMenuListComponent';

    <ReactSelect
    customOnMenuScrollToBottom={() => {
    console.log('scrollToBottom');
    }}
    components={{
    MenuList: YourCustomMenuListComponent,
    }}
    ....
    Now lets create a YourCustomMenuListComponent:

    import { SelectOption } from '../../type'; // Your select option type.

    type YourCustomMenuListComponentProps = ReactSelectMenuListProps<
    SelectOption,
    false,
    GroupBase

    ;

    export const YourCustomMenuListComponent = ({
    children,
    ...rest
    }: YourCustomMenuListComponentProps) => {
    const { customOnMenuScrollToBottom } = rest.selectProps;

    const onScrollToBottomTrigger = useInfiniteScroll(customOnMenuScrollToBottom);

    return (
    <components.MenuList<SelectOption, false, GroupBase>
    className={styles.menuList}
    {...rest}
    >
    {children}
    {customOnMenuScrollToBottom && onScrollToBottomTrigger}
    </components.MenuList>
    );
    };
    And useInfiniteScroll is a pretty standard hook which uses Intersection Observer, like this:

    export const useInfiniteScroll = (callback?: () => void): JSX.Element => {
    const observerRef = useRef<IntersectionObserver | null>(null);
    const lastElementRef = useRef<HTMLDivElement | null>(null);

    useEffect(() => {
    if (!callback) {
    return undefined;
    }

    if (lastElementRef.current) {
      observerRef.current = new IntersectionObserver((entries) => {
        if (entries[0].isIntersecting) {
          callback();
        }
      });
      observerRef.current.observe(lastElementRef.current);
    }
    
    return () => {
      if (observerRef.current) {
        observerRef.current.disconnect();
        observerRef.current = null;
      }
    };
    

    }, [callback]);

    return

    ;
    };
    We are finished. The callback customOnMenuScrollToBottom will be triggered each time when we scroll to the bottom of the component. I created an infinity scroll with data fetching inside the Select component based on this solution. Hope it helps someone

    I tried this implementation and the only issue I'm having is that it constantly calls the customOnMenuScrollToBottom prop when you scroll down. I'm looking for a way to fix that

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    issue/bug-unconfirmedIssues that describe a bug that hasn't been confirmed by a maintainer yetissue/has-prIssue has a PR attached to it that may solve the issueissue/reviewedIssue has recently been reviewed (mid-2020)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions