How can I focus on a react-select component at will?

Brady Dowling picture Brady Dowling · Mar 23, 2019 · Viewed 12.3k times · Source

Using react-select v2, I want to show and focus on the Select element when the user hits a certain key. Following are some things I've tried or paths I've gone down.

When I set a ref to the Select element and try to call .focus on it, it says no focus function is found. Perhaps I should somehow get a child element of it and then call focus on that?

There doesn't seem to be any prop I can pass that will trigger a focus function. There is an openOnFocus but not a focusOnOpen. The only thing I can think of would be to enable autoFocus and then somehow trigger a remount but there doesn't seem to be a simple way to do this and it feels hacky. Alternatively, I could enable just create the Select component each time the key is pressed instead of showing it, then unmount it instead of hiding it.

How can I properly get the react-select element to gain focus when I want it to?

I'm using a wrapper component around my component. Here's the render method for my wrapper:

  render() {
    return (
      <Select
        options={this.props.options}
        value={this.state.value}
        ref={this.selectRef}
      />
    );
  }

And here's where I'm calling that wrapper:

        <Wrapper
          options={this.props.labelOptions}
          ref={this.wrapperRef}
        />

I then try calling focus using either this.dropdownNode.focus() or this.dropdownNode.current.focus() and both say no focus method is found.

Answer

Brady Dowling picture Brady Dowling · Mar 23, 2019

Because you're wrapping the Select component, you can't call Select's .focus() function from the ref you're giving to the wrapper. Since ref is a special kind of prop, the ref for that wrapper is only referring to Wrapper itself, not the component it wraps (Select).

To access the actual Select component's ref, you have to pass a ref down to it as a prop with a different, non-magic name, like innerRef (react-select code actually gives a good example of this as it's accessing the actual input element and focusing on that).

Here's the change that fixed it. Here is the wrapper component where Select is actually used (and it's taking in the ref passed to it):

  render() {
    return (
      <Select
        options={this.props.options}
        value={this.state.value}
        ref={this.props.innerRef}
      />
    );
  }

And here's the component that's calling that wrapper. In the constructor I'm creating the ref with this.selectRef = React.createRef() then I pass it in as a prop in this render method:

        <Wrapper
          options={this.props.labelOptions}
          innerRef={this.selectRef}
        />

Then I can call focus on the Select component itself by running this.selectRef.current.focus() anywhere I want to.

Notes: Thanks to BoyWithSilverWings answer. This question pertains to React 16.3. There is also a new React.ForwardRefs method but this way seems simpler to me.