blueprint icon indicating copy to clipboard operation
blueprint copied to clipboard

Suggest onQueryChange appears to fire twice because callback changes items prop

Open ixtli opened this issue 6 years ago • 6 comments

Environment

  • Package version(s): core 3.6.1 / select 3.2.0 / react 16.5.2
  • Browser and OS versions: mac, chrome latest

Steps to reproduce

the following is a very simple component that uses Suggest to display a list of users returned by a REST endpoint. searchUsersByName makes a simple fetch() as you'd expect.

export class UserNameLookup extends Component {
  state = { results: [] };

  query = async query => {
    const final = query ? query.trim() : "";
    const results = final ? await searchUsersByName(final) : [];
    this.setState({ results });
  };

  itemRenderer = ({ first, last, uuid }, { handleClick }) => (
    <MenuItem
      className={ITEM_CLASS}
      key={uuid}
      text={`${first} ${last}`}
      onClick={handleClick}
    />
  );

  inputValueRenderer = ({ first, last }) => first + " " + last;

  itemSelect = item => {
    if (this.props.onSelect) {
      this.props.onSelect(item);
    }
  };

  render() {
    return (
      <div className={"user-name-lookup"}>
        <Suggest
          items={this.state.results}
          noResults={
            <MenuItem
              className={ITEM_CLASS}
              disabled={true}
              text={"No results."}
            />
          }
          onQueryChange={this.query}
          itemRenderer={this.itemRenderer}
          inputValueRenderer={this.inputValueRenderer}
          onItemSelect={this.itemSelect}
          inputProps={searchProps}
        />
      </div>
    );
  }
}

UserNameLookup.propTypes = {
  onSelect: PropTypes.func
};

Actual behavior

the query() function is called twice for every keystroke, likely because the result of that call changes items

Expected behavior

it should only be called once even though items changes.

Possible solution

im not sure how to make this work the "ergonomic" way, but obviously i tried to keep track of the query string on my own in the callback in order to avoid firing twice, but that seems impossible because this line https://github.com/palantir/blueprint/blob/develop/packages/select/src/components/query-list/queryList.tsx#L183 in setQuery() calls the callback BEFORE setting the internal state

im not sure how im supposed to do an async call in this situation.

EDIT: also just to be clear, the component works perfectly fine. i only realized it was making two calls when looking at the network tab in dev tools.

ixtli avatar Sep 28 '18 00:09 ixtli

I've ran into same issue with Select

mustafo avatar Oct 09 '18 13:10 mustafo

i only realized it was making two calls when looking at the network tab in dev tools.

@ixtli You can use lodash/debounce function to prevent multiple requests on one keystroke

mustafo avatar Oct 09 '18 18:10 mustafo

Same here the component fires 2 times instead of 1.

I also think that onQueryChange break the custom inputProps = {{ onChange : fn() }} but i can't find anywhere in docs a mention so maybe it's only in my case.

if you just want to avoid double call you can check if event is not undefined :

 onQueryChange = (query, event) => {
    if (event) {
      console.log('should send only once')
    }
  }

LoiKos avatar Oct 15 '18 16:10 LoiKos

Look like onActiveItemChange fired also two times for the same reason

LoiKos avatar Dec 28 '18 16:12 LoiKos

We are having the same problem with Suggest, using onQueryChange method gets fired twice per keystroke. We found the problem is related with the way the component behaves in what is supposed to be a controlled input.

By doing

<Suggest 
  query='',
  onQueryChange={(t, e) => { console.log(t, e); }}
  {...otherProps}
>

In here, the input should never be changed. because we are effectively settings its value to a constant.

This is broken, so not exactly a controlled input.

ernestofreyreg avatar Feb 09 '19 00:02 ernestofreyreg

The problem still exists

troovi avatar Jan 21 '24 17:01 troovi