Skip to content
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

Reference to input #4039

Closed
artooras opened this issue May 5, 2020 · 13 comments
Closed

Reference to input #4039

artooras opened this issue May 5, 2020 · 13 comments
Labels
issue/reviewed Issue has recently been reviewed (mid-2020)

Comments

@artooras
Copy link

artooras commented May 5, 2020

This is not a feature request per se, but I cannot seem to find this in the documentation. I have also asked a question about this on SO.

How do I get a reference to the input element so I can .select() the text inside of it?

@artooras
Copy link
Author

artooras commented May 9, 2020

Well... onChange responds to the user input. But what I want to achieve is to .select() text in the input field programmatically.

@artooras
Copy link
Author

Would you mind sharing an example of how I can get a ref to the input that is embedded inside the react-select?

@artooras
Copy link
Author

@manualva7 , with all due respect, this is an issue related to react-select, because the input element that I need is internal to react-select. I have done research as much as I could, and I cannot find how to get a reference to that internal input element, hence my question on SO and here.

@artooras
Copy link
Author

If you want to see why I wanted to find a reference to the input element, see the answer on my SO question - someone actually suggested a working solution. I still think it's a bit hacky as it relies on querying for a certain id, which may change, but I'll go with that for now while there isn't a more direct reference to the input element provided.

@ebonow
Copy link
Collaborator

ebonow commented May 29, 2020

@artooras I believe you might be looking for the following answer and also replied to the SO post. While the select reference does support a few methods directly on the input element (focus and blur), it does not expose a select method in the same manner.

For that you would need to directly access the inner component input reference which conceptually would look like the following:

selectRef -> current (StateManager) -> select(Select Component) -> inputRef (Input Component)

selectRef.current.select.inputRef.select()

I hope this helps and if so, please consider closing this issue.

@artooras
Copy link
Author

Thanks @ebonow . But with your suggestion I get:

TypeError: ref.current.select.inputRef is undefined

I'm calling it right after focus():

ref.current.focus()
ref.current.select.inputRef.select()

@bladey bladey added the issue/reviewed Issue has recently been reviewed (mid-2020) label Jun 19, 2020
@ebonow
Copy link
Collaborator

ebonow commented Jun 20, 2020

@artooras. Sorry for the late reply. I lost my last draft while working on a sandbox solution for you. There are a few things to this....

  1. Targeting the input ref also depends on the type of Select. This is a mild annoyance of mine but here's how it's done depending on your use-case...

Select: ref.current.select.inputRef.select()
Createable or Async: ref.current.select.select.inputRef.select()
Createable Async: ref.current.select.select.select.inputRef.select()

  1. React-Select clears the input text when an option is selected. You stated that you were able to get a working answer through Stackoverflow, but just wanted to confirm that it worked as you had intended as the onBlur method generally will clear out the input unless you override the behavior.

One of the more asked for requests with React-Select is to expose the inputValue as editable. Here is what I am doing to make this happen which unfortunately involves making React-Select a controlled component. I may take another stab at this by leveraging the StateManager, but for now, this fits my needs.

  const [ value, setValue ] = useState();
  const [ inputValue, setInputValue ] = useState('');

  const onInputChange = (inputValue, { action }) => {
    if (action === 'input-blur') {
      // onBlur reset label to last selected value label
      setInputValue(value ? value.label : '');
    }
    
    else if (action === 'input-change') {
      setInputValue(inputValue);
    }

    return;
  }

  const onChange = option => setValue(option);

  <Select 
    className="react-select--editable"
    classNamePrefix="react-select"
    value={value}
    inputValue={inputValue}
    onInputChange={onInputChange}
    onChange={onChange}
  />

Then for my CSS I do that following...

.react-select--editable {
  .react-select__input input {
    opacity: 1 !important;
  }

  .react-select__control--is-focused {
    .react-select__single-value {
      display: none;
    }
  }
}

@ebonow
Copy link
Collaborator

ebonow commented Jun 21, 2020

Here's a version that does not require any extra styling that I tested today found here

@ebonow
Copy link
Collaborator

ebonow commented Jun 21, 2020

@artooras Here is your solution.

@manualva7 100% incorrect. Here is the working "solution" that you said isn't possible: https://codesandbox.io/s/react-select-editable-and-selectable-6nfdv

Please consider reading before refuting so assuredly what's possible and not possible in the future.

To set the record straight, the fact is that React-Select DOES render an Input (when the searchable prop is true). What also does happen is that the opacity is set to 0 based on whether or not it thinks it should display based on a isHidden prop sent to the Input component (see here for the implementation) What we need to do is keep it visible and do away with the SingleValue/MultiValues with the controlShouldRenderValue prop.

If you care to learn, here are some of the other "suggested" approaches that could/should have resolved this with hopes that either someone can point out where proposed solution #1 went wrong or to push for making this a much easier internal solution (autoHideInput to override default behavior, editableInput which could manage these state changes internally, easier to override styling, etc...)

Custom Component: (Does not work today for me on my computer)

const Input = inputProps => <components.Input {...inputProps} isHidden={false} />

<Select components={{Input}} ...>

Creating a Custom Component for Input should work, but when testing today, the controlled component seems to be re-rendering the input on every keypress which results in losing focus and winds up setting the document.activeElement to body instead of the input. I am not sure what changed between testing locally on some existing React-Selects and today's Sandbox, but I had to remove it.

Custom Styling: (Does not work)

styles = { input: css => ({ ...css, opacity: 1 })
<Select styles={{styles}} ...>

Theoretically, this should also work, but the way the Input component was written, overwrites the opacity style based on the prop isHidden which is sent to the Input.

CSS: (Works)

.react-select---editable .react-select__input input { opacity: 1 !important }

<Select className="react-select--editable" classNamePrefix="react-select" ...>

I understand specificity and the stigma around !important but it exists for reasons like this; that being a 3rd party library with no apparent alternatives to overwriting inline styles which affect the desired behavior.

That said, I appreciate having a community so we can help solve similar problems. It's why we are all here. It's just not helpful when so much free time is taken to help others only to blindly discredit the methodology and approach without any thought for what you might be missing.

I've built some cool stuff with this tool there's still a lot that I've learned just this past weekend. Looking forward to building more tools and community together.

@bladey bladey added the awaiting-author-response Issues or PRs waiting for more information from the author label Jun 22, 2020
@artooras
Copy link
Author

Thanks @ebonow . Is the following

Select: ref.current.select.inputRef.select()
Createable or Async: ref.current.select.select.inputRef.select()
Createable Async: ref.current.select.select.select.inputRef.select()

documented anywhere? Because I think it should be :)

In my case, I ended up using a workaround which, though may not be entirely correct, works for me quite reliably. This is based on the assumption that when I call focus() on the react-select, the input that is in focus in document is that of react-select:

ref.current.focus()
document.activeElement?.select()

@ebonow
Copy link
Collaborator

ebonow commented Jun 22, 2020

That actually is quite brilliant and simplistic. Are you using a controlled component by passing in the inputValue? Just curious to how you are maintaining a value in the input that could be selected.

@artooras
Copy link
Author

I set defaultInputValue as ... and have it selected, which was my intended use case - the user clicks on a button to add a new value in react-select and, as a result, gets the ... default value selected in the input (and the menu under the input showing Add new value: ...), allowing the user to type straight away.

@bladey bladey removed the awaiting-author-response Issues or PRs waiting for more information from the author label Jun 23, 2020
@bladey
Copy link
Contributor

bladey commented Aug 24, 2020

Thanks for the reply @ebonow and @artooras! I'm going to close this issue for now, please let me know if it should remain open.

@bladey bladey closed this as completed Aug 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
issue/reviewed Issue has recently been reviewed (mid-2020)
Projects
None yet
Development

No branches or pull requests

3 participants