JedWatson / JedWatson/react-select

Async menu list height remains unchanged after options loaded

Open
#4,146 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

issue/bug-unconfirmed issue/reviewed menu-bug
Dominant language
TypeScript
Stars
28k
Forks
4.1k
PR merge metrics
No merged PRs in 30d

Description

I've set a maxMenuHeight on an Async component, but after the options are loaded, the max height is not calculated and instead is set to the maxMenuHeight that is provided. This causes the menu to be cut off by the bottom of the screen and not flip to the top of the select when enough options are returned. I would expect the menu to not flow beyond the window after the options are loaded and still not flip to the top if minMenuHeight has not been reached either.

I was able to write a hack around this by adding a custom component for Menu and forcing innerRef to be called (so that getPlacement on MenuPlacer will be triggered and the max height recalculated) whenever the supplied options have changed (using an MD5 hash for this to ensure that the options truly have changed).

Version: 3.1.0

Test Case:

import ReactSelectAsync from "react-select/async";

export default function TestCase() {
  return (
    <ReactSelectAsync
      maxMenuHeight={2000}
      loadOptions={() => new Promise((resolve) => {
        setTimeout(function() {
          const ret = [];

          for (let i = 0; i < 50; i++) {
            ret.push({
              value: i,
              label: `Item ${i}`
            });
          }

          resolve(ret);
        }, 1000);
      }}
    />
  );
}

My Workaround:

import { components } from "react-select";
import { MD5 } from "object-hash";

export class CustomMenu extends React.Component {
  _isFirstRender = true;
  _menuRef = React.createRef();

  render() {
    const { children, ...props } = this.props;

    return (
      <components.Menu {...props} innerRef={this._innerRef}>
        {children}
      </components.Menu>
    );
  }

  componentDidUpdate() {
    const optionsHash = MD5(this.props.options);

    if (this._prevOptionsHash !== optionsHash) {
      this._prevOptionsHash = optionsHash;

      if (!this._isFirstRender && this._menuRef.current != null) {
        this.props.innerRef(this._menuRef.current);
      }
    }

    this._isFirstRender = false;
  }

  _innerRef = (...args) => {
    processDomRef(this._menuRef, ...args);
    this.props.innerRef(...args);
  };
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the supplied ReactSelectAsync test case, then trace the Async menu flow into MenuPlacer and its getPlacement logic. Verify that loading options recalculates the available max height and keeps the menu within the window, including the minMenuHeight behavior described in the issue.

Written by the indexing model from the issue text.

Assessment

Tech stack
react
Domain
frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.