-
Notifications
You must be signed in to change notification settings - Fork 839
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
[EuiSearchBar] use EuiFieldSearch as controlled input #535
Conversation
It looks like the problem has more to do with the fact that The latest commit updates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to play around with this some more to make sure my feedback is accurate, but am going to send this out now in case you can correct me.
Also I'm not sure if this is the bug you are running into, but search results can come back in a different order. I ran into this in the visualize listing table (https://github.com/elastic/kibana/blob/master/src/core_plugins/kibana/public/visualize/listing/visualize_listing_table.js#L60).
onKeyUp = (event) => { | ||
const { onKeyUp, onSearch, incremental } = this.props; | ||
|
||
if (onKeyUp) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't want to add a debounce in this component, or a way to pass one in, should still use one in dashboard listing table (haven't gotten to that PR so maybe you have already!). Just so we don't send a request for every key press.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
debounce is handled by the consumer (like dashboard listing table). The neat thing is that search only hits the server when the number of dashboards exceeds the listingLimit. If that limit is not breached then all search stays local on the client.
if (this.props.onKeyUp) { | ||
this.props.onKeyUp(event); | ||
componentWillReceiveProps(nextProps) { | ||
this.setState({ unsubmittedSearch: nextProps.value }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the order of onKeyUp and onChange guaranteed? I think onKeyUp occurs first and onChange occurs second, when the input box blurs.
If so, I don't think this is going to work right (though haven't tested it so could be wrong). If the search box is initialized with value a
, then the user presses b
, won't onSearch be called with a
, not ab
, but once onChange is fired, the search box will show ab
so the results won't match what's in the input box?
Also, do we really need a new onChange prop? Just seems to complicate this component. Which one should I use, onKeyUp, onSearch, or onChange? Some propTypes with descriptions might help.
One more thing to note is that user can paste text into the search box by right clicking which means onKeyUp won't get fired.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the input is controlled, onKeyUp event.target.value
is undefined since react is now tracking the value. This puts us in the weird situation where when the component is controlled, onChange has to be used to track the input value and onKeyUp has to used to submit onSearch. I do not think we can get around this and the input must be controlled.
In Chrome, onChange is getting called before onKeyUp.
let searchValue; | ||
if (value || value === '') { | ||
searchValue = this.state.unsubmittedSearch; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason to skip this if value is null or undefined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It causes react to complain when defaultValue
is passed because then the input is uncontrolled and controlled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nreese It might be a good idea to leave your explanation as a comment here for the next person who asks the same question.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Just had some suggestions about comments and tests.
@@ -92,21 +92,25 @@ export class EuiSearchBar extends Component { | |||
} | |||
|
|||
onSearch = (queryText) => { | |||
this.setState({ queryText }, this.handleQueryTextChange); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a verbose comment here to explain the intention behind organizing the logic like this? Here's my suggestion based on your PR description:
/**
* We set `queryText` in one phase in this callback and then set subsequent derived state in a
* second phase in `handleQueryTextChange`. This ensures the UI updates with all of the user's
* input. Otherwise we could get into a race condition in which the parsing logic is still executing
* while the user types rapidly, resulting in a UI which doesn't contain characters that the user
* has typed.
*/
this.setState({ queryText }, this.handleQueryTextChange);
this.setState({ unsubmittedSearch: nextProps.value }); | ||
} | ||
|
||
onKeyUp = (event) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add tests for onKeyUp
, onChange
, and onSearch
? Because we're intercepting the first two callbacks it will be good to protect against a breaking change to their behavior, and the last one is a new callback which needs documentation.
fec768f
to
86846c9
Compare
There is a race condition in EuiSearchBar where sometimes user input is entered before the last
onSearch
method call can updatequeryText
. This results in a confusing user experience because the input looses characters. For example, the user may typeredis
but the input will showreds
. I am consistently seeing this while running functional tests in Kibana that type long strings in the search bar.This PR removes a couple of performance problems like using bind for property values. The PR removes the race condition by updating
queryText
before callingparse
, ensuring that user input is always captured.In
search_box
, the query was getting passed toEuiFieldSearch
twice, once viadefaultValue
and then again incomponentWillUpdate
. The PR only passes the initial query value asdefaultValue
and all other updates are done viacomponentWillUpdate