5

I am working on a React project using TypeScript. They wrapped the react-select component into another component. The wrapped component is the following:

import * as React from "react";
import Select from "react-select";
import { Props as SelectProps } from "react-select/lib/Select";

export interface SelectValue {
  label: string;
  value: string;
}

export interface SelectFieldProps<TValue> extends SelectProps<TValue> {
  label?: string;
}

type GenericSelectField<TValue> = React.StatelessComponent<
  SelectFieldProps<TValue>
>;

const SelectField: GenericSelectField<SelectValue> = ({
  label = "",
  ...rest
}) => (
    <div className="react-select-wrapper">
      {label ? <span className="input__label">{label}</span> : null}
      <Select classNamePrefix="react-select" {...rest} />
    </div>
  );

export default SelectField;

I would like to access the method blur from react-select:

React-select exposes two public methods:
...
blur() - blur the control programatically

But, I don't know how to expose it in my Component, so I could invoke it. Any ideas?

Aldo
  • 1,199
  • 12
  • 19
  • Where exactly do you want to use `blur`? – Rallen May 09 '19 at 12:56
  • I am gonna call blur in another Component as a result of an user action. I don't think relavant to show here, because there it is just a method call. – Aldo May 09 '19 at 13:00

3 Answers3

2

You can use ref property on the Select component to get a reference to the instance of this component.

Then you can call the blur method of this instance.

const SelectField: GenericSelectField<SelectValue> = ({label = "",...rest}) => {
  const selectEl = React.useRef(null);      

  // this function could be a callback
  function handleBlur() {
    selectEl.current.blur();
  }

  return (
    <div className="react-select-wrapper">
      {label ? <span className="input__label">{label}</span> : null}
      <Select ref={selectEl} classNamePrefix="react-select" {...rest} />
    </div>
  );
}

export default SelectField;

If you need to access the blur method outside of your SelectField component, you could use forwardRef to reference Select instead of the SelectField.

const SelectField = (props, ref) => {
  const {label = "",...rest} = props;
  const selectEl = React.useRef(null);      

  return (
    <div className="react-select-wrapper">
      {label ? <span className="input__label">{label}</span> : null}
      <Select ref={ref} classNamePrefix="react-select" {...rest} />
    </div>
  );
}

export default React.forwardRef(SelectField);

Then you can call the blur method with the reference of the SelectField component :

const ref = React.createRef();
<SelectField ref={ref} label=""/>;

ref.blur();
Olivier Boissé
  • 15,834
  • 6
  • 38
  • 56
  • I am implementing your suggestion, but the TypeScript part is something. I am trying to declare in my interface the blur method, but it is not working. Is this correct? `export interface SelectFieldProps extends SelectProps { label?: string; blur?: Function; }` – Aldo May 09 '19 at 12:54
  • don't really understant what you are trying to do. Do you need to access the `blur` method of `react-select` outside of your `SelectField` component ? – Olivier Boissé May 09 '19 at 13:00
  • 1
    That's it. I am using SelectField into another Component. There, I'd like to call blur(). The code is pretty complicated, but I am relying on refs there too: ` { selectRef.current.map(select => select.blur()); return onPriceChange(field, value) }}` – Aldo May 09 '19 at 13:04
  • I edited my answer, you will need to update the `SelectField` type – Olivier Boissé May 09 '19 at 13:14
  • your answer is real nice and I guess it works fine with React + JS. But I am still struggling with TypeScript. TS requires the blur method to be explicitly declared into SelectField and it is not happening. I can't just invoke the function. – Aldo May 09 '19 at 14:14
1

If I understand this correctly you want to trigger Select's blur() inside of SelectField.

There are many ways for example parent-child-binding it via props. Here is a discussion about this:

Call child method from parent

p0rter
  • 961
  • 2
  • 13
  • 28
  • 1
    This discussion don't address TypeScript. Refs do the work, but the function must be declared inside the Component. – Aldo May 09 '19 at 14:18
0

If you want to access it from outside the SelectField component, I would recommend using React.forwardRef to forward the ref prop to your SelectField component so you can assign it on your own:

/* ... */

type GenericSelectField<TValue> = React.StatelessComponent<
  SelectFieldProps<TValue>
>;

type SelectFieldWithRef<TValue> = React.StatelessComponent<
  SelectFieldProps<TValue>,
  React.Ref<*>
>;

const RefSelectField: SelectFieldWithRef<SelectValue> = (props, ref: any) => {

    /* ... */

    return (<Select ref={ref} {...} />);
};

const SelectField: GenericSelectField<SelectValue> = React.forwardRef(RefSelectField);

export default SelectField;

In the component you want to call blur you have to do the following:

class ParentComponent extends Component {
    constructor(props) {
        super(props);

        this.selectRef = React.createRef();
    }

    somehowTriggerBlur = () => this.selectRef.current.blur();

    render() {
        return (<SelectField ref={ref} {...} />);
    }
}
Rallen
  • 2,240
  • 20
  • 22