reactjs / react-autocomplete

WAI-ARIA compliant React autocomplete (combobox) component
MIT License
2.17k stars 532 forks source link

add custom input component #247

Closed jallwine closed 7 years ago

jallwine commented 7 years ago

It would be great to have more control over the input component. What if you allowed an input prop that could be a custom component that wraps an <input> tag? If you don't provide one, it defaults to like it currently does.

I've been playing around with the idea in my own fork: https://github.com/jallwine/react-autocomplete/blob/master/lib/Autocomplete.js

It's on npm as react-autocomplete-custom-input, currently.

As long as the custom input component provides a ref to the actual input node (ref={ input => this.input = input} on the input component) and accepts all the props that Autocomplete uses on the input, it seems to work. This was a quick test, so any thoughts or concerns about doing it this way would be great.

CMTegner commented 7 years ago

Do you have a use-case for this?

As long as the custom input component provides a ref to the actual input node (ref={ input => this.input = input} on the input component)

ref doesn't get passed to the child component, it's only visible to the owner. For this to work the custom input component would have to expose all the native <input> APIs (focus, select, etc.), like Autocomplete does by proxying to refs.input. It would also need to be a DOM element, or else we'll have to use findDOMNode like we do with the items and menu. In other words: it would have to do a lot of what Autocomplete already does.

The other argument is that Autocomplete is quite tightly coupled to the behaviour of the native <input>, especially when it comes to focus management. I fear that any deviation in your custom input component could cause unexpected behaviour.

We want to make Autocomplete as close to the native <input> as we can get. With this in mind, it may be better to pass Autocomplete in place of an <input>, instead of passing a custom input component to Autocomplete.

I think the main argument for this type of behaviour would be to detach Autocomplete from ReactDOM, so it could be used in other environments (e.g. react-native). I haven't worked enough with inputs in react-native to know if this is even possible, but i think the idea is interesting.

jallwine commented 7 years ago

You'll see here that I modified exposeAPI to check if getBoundingClientRect exists (a less than ideal way to check if it's an input element). If it doesn't then assume the custom input exposes a .input property, which can be assigned using ref={input => this.input = input} in the custom component's input. https://github.com/jallwine/react-autocomplete/blob/master/lib/Autocomplete.js#L213-L215

jallwine commented 7 years ago

I was really just experimenting to figure out which direction I should go. I appreciate your feedback!

CMTegner commented 7 years ago

A possible solution for ref is to pass a function to the custom input component which could be used to expose the underlying <input> (or equivalent):

Autocomplete.js

<Input
  inputRef={el => this.input = el}
  ...
/>

Input.js

<input
  ref={props.inputRef}
  ...
/>

As long as it's not called ref (or any of the other of the reserved attribute names) it'll be passed down to the children. There's a section about this in the React docs.

abdennour commented 7 years ago

Guys! @jallwine @CMTegner , I created pull request #262 for the required feature through new props that I named as

import MyInput from './MyInput'
<AutoComplete as={MyInput} {...otherProps} />

Enjoy!

I will be waiting for the next NPM release which includes this feature.

jallwine commented 7 years ago

I ended up going with @CMTegner suggestion to use Autocomplete instead of input in my custom component, rather than trying to pass a custom input component into Autocomplete.

FWIW, though, react-autosuggest seems to have this functionality: https://github.com/moroshko/react-autosuggest#renderInputComponentProp

CMTegner commented 7 years ago

v1.6.0 includes support for props.renderInput which I think covers the current demand for a custom <input> component.