React-select: Select.Async - isLoading is true after option selected

Created on 15 Apr 2016  路  10Comments  路  Source: JedWatson/react-select

There is a loading circle present most of the time when viewing my Select.Async component. The circle seems to go away only when the following conditions are true:

  1. The text in the input is not found in the cache
  2. There are matching dropdown entries for the text in the input

Even if those conditions are met, as soon as I select an option, the loading circle returns.

It's difficult to pin down the exact circumstances because the very act of setting breakpoints interferes with the asynchronous nature of this component. At any rate, my loading circle persists almost constantly, making it look like there's a search in progress when in fact results have long since been loaded and an option has been selected.

The component that renders Select.Async looks like this:

export class SelectAsync extends React.Component {
  static propTypes = {
    field: PropTypes.object.isRequired,
    id: PropTypes.string.isRequired,
    label: PropTypes.string.isRequired,
    load: PropTypes.func.isRequired,
    labelKey: PropTypes.string,
    valueKey: PropTypes.string,
    onChange: PropTypes.func.isRequired,
    serverErrors: PropTypes.array
  };

  labelKey() {
    return this.props.labelKey || 'label';
  }

  valueKey() {
    return this.props.valueKey || 'value';
  }

  callback(option) {
    let noop = (option) => { }
    let f = this.props.onChange || noop;
    f(option);
  }

  handleBlur(field) {
    field.onBlur(field.value);
  }

  handleChange(option) {
    let val = option[this.valueKey()] || null;
    this.props.field.onChange(val);
    this.callback(option);
  }

  render() {
    const {
      field,
      id,
      label,
      load,
      onChange,
      serverErrors
    } = this.props;

    return (
      <FormField {...this.props}>
        <Select.Async
          id={id}
          loadOptions={_.debounce(load, 500)}
          labelKey={::this.labelKey()}
          valueKey={::this.valueKey()}
          value={field.value}
          {...field}
          onChange={::this.handleChange}
          onBlur={() => this.handleBlur(field)} />
      </FormField>
    );
  }
}

This component takes a load property, which is the function used to do the search:

export function lookupProducts(input) {
  return api.searchProducts(input).then((response) => {
    return response.data.products;
  }).then((json) => {
    return {
      options: json
    };
  });
}

Everything works fine except for isLoading being reset to true even when there is no AJAX call in progress and an option has been chosen.

Most helpful comment

Don't know if this is helpful but I faced a similar issue.

Basically, when I select an option from the dropdown list, the circle starts spinning for no reason.

Here is my loadOptions method:

getOptions(input, callback) {
      let options = [];
      let optionsMap = [];
      const rawKiList = [];
      if (input !== null && input.trim() !== '') {
        setTimeout(() => {
          callback(null, **api.getKiSuggestions(input, this.props.gcomUsrId, (response)** => {
            if (response != null && response.body != null) {
              const resultNode = response.body;
              Object.keys(resultNode).map((i) => {
                rawKiList.push(
                  resultNode[i]
                );
              });
              options = this.createOptionsFromApi(rawKiList);
              optionsMap = this.createSuggestionsMap(rawKiList);
              // set options {value,label} and optionsMap [{id:id, obj:obj}] in state
              this.setState({
                options,
                optionsMap
              });
            }
          }));
        }, 100);
      } else {
          **// Send a null callback to disable loading icon.
          callback(null);**
      }
    }

I just handled the case where it executes loadOptions unnecessarily after selecting an option (by passing null callback). Plus I added a onBlurResetsInput= false to the code:

<Select.Async
              options={options}
              value={tags}
              loadOptions={this.getOptions}
              onChange={this.handleTagListUpdates}
              filterOptions={(suggestions) => { return suggestions; }}
              multi={true}
              autoload={false}
              cache={false}
              clearable={false}
              onSelectResetsInput={false}
              closeOnSelect={false}
              **onBlurResetsInput={false}**
            />

And it works perfectly!

PS: ** refers to highlighted code

All 10 comments

Yea i'm having the same situation here. isLoading seems to be set to true all the time

componentDidMount() is calling loadOptions which set the isLoading state to true

Update on this.

For me, removing the _.debounce makes the spinny wheel go away, which is good. However, once this happens I can no longer reliably select options. The callback fires when I select the option and I see updates to my page indicating that I have indeed selecting the option, but the Select.Async component's input displays the Select... placeholder text as if I hadn't selected anything.

The screenshot below is an example of this. The $199.99 matches the price of the product I just selected, which tells me that the correct option was passed to this.props.change in the callback function. Also, when I debug the render method, I can see that field.value indeed holds the SKU of the product that I just selected.

screenshot from 2016-04-26 10-50-39

It's worth noting that this only occurs for some options. Others, I can select just fine and everything updates accordingly.

screenshot from 2016-04-26 10-56-59

Here's a video demonstration showing these scenarios. It also shows another issue (don't know if it's related, if not I'll link to it again in a separate issue) that only shows up in deployed environments.

https://drive.google.com/file/d/0Bzbw-6Q_sVTyQk81czNOMFdBUG8/view?usp=sharing

@JedWatson is there a possibility of looking into this issue? We make ample use of this component in our app which we are planning to release very soon.

I'm also having this issue with beta9. The control starts behaving as if isLoading is true when the page is first rendered (even though autoload={false} is one of the parameters). The first async call should happen when the user types one letter.

There is an unfortunate side effect; the spinner replaces the x button that should allow the selected value to be cleared. Since the spinner never goes away, the user is stuck with whatever options start with the first letter they typed, because there is no way to clear the selected item. Let me know if you want more code examples from me.

OK, following up. This behavior exists in beta9, but not in top of tree / head. In top of tree, the isLoading is wrong on initial load, but OK after the first search. The main issue (for me) here is that in head the autoload property is not implemented. I implemented it with some small code changes:

in propTypes, add:

autoload: React.PropTypes.bool // whether to call loadOptions after componentDidMount

in getDefaultProps, add:

autoload: true

in componentDidMount, wrap this.loadOptions() with a conditional on this.props.autoload:

if (this.props.autoload) { this.loadOptions(''); }

If anyone wants a git diff to check this in, let me know.

What I tried doing is call the callback method in the loadOptions method on init. So something like this:

      if(!this.state.hasSelectInit) {
        this.setState({ hasSelectInit: true });
        callback(null, {options: [], complete: false});
      }

This stopped it from happening.

Hello, Im on rc-2 and its working for me but you I need it to call the callback in the then of the promise
for example

export function lookupProducts(input, callback) {
...
  }).then((json) => {
     callback(null, {
      options: json
    });
  });
}

So I dont know if something is missing or the doc need to be updated

Don't know if this is helpful but I faced a similar issue.

Basically, when I select an option from the dropdown list, the circle starts spinning for no reason.

Here is my loadOptions method:

getOptions(input, callback) {
      let options = [];
      let optionsMap = [];
      const rawKiList = [];
      if (input !== null && input.trim() !== '') {
        setTimeout(() => {
          callback(null, **api.getKiSuggestions(input, this.props.gcomUsrId, (response)** => {
            if (response != null && response.body != null) {
              const resultNode = response.body;
              Object.keys(resultNode).map((i) => {
                rawKiList.push(
                  resultNode[i]
                );
              });
              options = this.createOptionsFromApi(rawKiList);
              optionsMap = this.createSuggestionsMap(rawKiList);
              // set options {value,label} and optionsMap [{id:id, obj:obj}] in state
              this.setState({
                options,
                optionsMap
              });
            }
          }));
        }, 100);
      } else {
          **// Send a null callback to disable loading icon.
          callback(null);**
      }
    }

I just handled the case where it executes loadOptions unnecessarily after selecting an option (by passing null callback). Plus I added a onBlurResetsInput= false to the code:

<Select.Async
              options={options}
              value={tags}
              loadOptions={this.getOptions}
              onChange={this.handleTagListUpdates}
              filterOptions={(suggestions) => { return suggestions; }}
              multi={true}
              autoload={false}
              cache={false}
              clearable={false}
              onSelectResetsInput={false}
              closeOnSelect={false}
              **onBlurResetsInput={false}**
            />

And it works perfectly!

PS: ** refers to highlighted code

Hello -

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

We understand this might be inconvenient but in the best interest of supporting the broader community we have to direct our limited efforts to maintain the latest version.

If you feel this issue / pull request 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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

coder-guy22296 picture coder-guy22296  路  3Comments

MindRave picture MindRave  路  3Comments

yrabinov picture yrabinov  路  3Comments

MalcolmDwyer picture MalcolmDwyer  路  3Comments

pashap picture pashap  路  3Comments