How do I define an onChange handler for React-Select in typescript
Asked Answered
U

10

21

I can't get the onchange function typing correct. I created a handler function but typescript keeps complaining about type mismatch.

My function:

private handleChange(options: Array<{label: string, value: number}>) {
}

Typescript error:

src/questionnaire-elements/AssessmentFactorSearch.tsx (43,9): Type '{ ref: "select"; name: string; backspaceToRemoveMessage: ""; value: { label: string; value: IAsse...' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Async<OptionValues>> & Readonly<{ children?: React...'. Type '{ ref: "select"; name: string; backspaceToRemoveMessage: ""; value: { label: string; value: IAsse...' is not assignable to type 'Readonly<ReactAsyncSelectProps<OptionValues>>'. Types of property 'onChange' are incompatible. Type '(options: { label: string; value: number; }[]) => void' is not assignable to type 'OnChangeHandler<OptionValues, Option<OptionValues> | Option<OptionValues>[]> | undefined'. Type '(options: { label: string; value: number; }[]) => void' is not assignable to type 'OnChangeHandler<OptionValues, Option<OptionValues> | Option<OptionValues>[]>'. (2322)

How do I type the onchange method?

Undershorts answered 11/12, 2017 at 20:43 Comment(2)
How did you solve this?Olvera
@Olvera see my proposal down under. I'm not a fan of the api by react-select, but that solution would offer proper type checks.Likable
M
13

Based on https://github.com/JedWatson/react-select/issues/2902, just write your handler like this:

// define your option type
type MyOption = {label: string, value: number}

// ...

  // define your onChange handler:
  private handleChange = (selected?: MyOption | MyOption[] | null) => {
    /** Code **/
  }

Take note of the part MyOption | MyOption[] | null, which matches the same structure as the definition of onChange in the react-select library. Just make your own MyOption type.

Another example in that issue thread also shows that you can pass the function inline, and in that case the type of the handler parameter is inferred automatically:

<Select
  onChange={selectedOption /* type is automatically inferred here */ => {
    if (Array.isArray(selectedOption)) {
      throw new Error("Unexpected type passed to ReactSelect onChange handler");
    }

    doSomethingWithSelectedValue(selectedOption.value);
  }}
  ...
/>
Morganite answered 25/1, 2019 at 17:23 Comment(3)
Thanks. I think github.com/JedWatson/react-select/issues/… works.Trocki
no longer works: Cannot find module 'react-select/lib/types' or its corresponding type declarations.Dorisdorisa
Then you should look at this answer most probably https://mcmap.net/q/600866/-how-do-i-define-an-onchange-handler-for-react-select-in-typescriptMorganite
T
13

as for react-select ˆ3.1.0 the code below should work:

import {ValueType, ActionMeta} from 'react-select';

type MyOptionType = { label: string, value: number }

type OnChange = (value: ValueType<MyOptionType>, actionMeta: ActionMeta<MyOptionType>) => void;
Turbojet answered 28/7, 2020 at 12:14 Comment(4)
ValueType is a generic and requires second type.Newsletter
value: ValueType<MyOptionType , false>. you need to add false or true as a second parameter depending if you allow the select multiple options or notProtomartyr
Typescript mess. Yet have to see the added value of this.Blotto
I'm getting Module '"react-select"' has no exported member 'ValueType'.Dail
B
5

2022, using "react-select": "^5.4.0" and "typescript": "4.7.4".

This worked for me, as per the react-select documentation:

type SelectOptionType = { label: string, value: string }

const [myState, setMyState] = useState({})

const handleSelectionChange = (option: SelectOptionType | null) => {
  if (option) {
    setMyState(option)
  }
};

...

<Select 
  options={options} 
  onChange={handleSelectionChange}
/>

In case you're using a multi-select, you must change the handleSelectionChange method signature to receive an option variable as a readonly array of SelectOptionType (as I named my option's type). Something like:

const handleSelectionChange = (option: readonly Option[]) => {
 // typecheck and do whatever you want e.g. setState
}

I'm not using the actionMeta which is optional, but you could receive it in your handleSelectionChange method as a second parameter as well.

Bugs answered 6/7, 2022 at 19:23 Comment(2)
how do you do this conditionally based on if the multi is passed in as a prop or not?Matthei
@RedBaron did you figure it out? I'm struggling to change the type based on the isMulti prop.Grata
M
3

Create a type for the option value:

type SelectValue = {
  label: string;
  value: string;
};

Use MultiValue<SelectValue> for multiple value select input. SingleValue<SelectValue> for single value select input. ActionMeta holds info about the action performed. E.g. in multi value select input it holds the info about what is added or removed from selection.

import Select, { ActionMeta, MultiValue } from "react-select";

const handleSelectionChange = (
   newSelections: MultiValue<SelectValue>,
   actionMeta: ActionMeta<SelectValue>
) => {
    // define your logic
};

React Select version: 5.2.2

Macur answered 5/3, 2022 at 20:15 Comment(1)
how do you do this conditionally based on if the multi is passed in as a prop or not?Matthei
B
2

2021 answer:

  1. Don't use the @types/react-select lib, it's been deprecated
  2. onChange handler

Handler:

const addTagHandler = (tags: OnChangeValue<TagOptionType, true>) => {
    if (tags) {
      setTags((tags as TagOptionType[]).map((tag: TagOptionType) => tag.value));
    }
  };

Where:

  1. boolean true refers to isMulti (are there multiple elements, or single)
  2. Where TagOptionType looks

As follows:

type TagOptionType = { label: string, value: string }
Blotto answered 8/12, 2021 at 8:10 Comment(0)
G
1

The second type parameter dictates if the Select should be typed as a multi-choice select or single.

Use Select<MyOption, true> for multi choice, Select<MyOption> for single choice.

Glyceride answered 27/10, 2021 at 14:43 Comment(0)
W
0

If people are still having a hard time, this is what worked for me (excluding the meta):

import { ValueType } from 'react-select/src/types';
interface MyOptionType = { value: string; label: string };

const handleChange = (value: ValueType<MyOptionType>) => {
  const val: MyOptionType = value as MyOptionType; // casting
  
  // insert your logic here using the casted variable
  ...
}
Welloiled answered 1/4, 2022 at 5:11 Comment(2)
Where does ValueType come from in this example?Yee
@Yee thanks for pointing that out. I forgot to add that it came from react-select library. I have updated the code showing how it got imported.Welloiled
S
0

The other answers sadly didn't work for me. Through reading the error message I came to this solution. Please note this works only for single-select

import { SingleValue } from "react-select";

type OptionType = {
  value: string;
  label: string;
};

// alternative: onChange(option: OptionType | null | "")
function onChange(option: SingleValue<OptionType> | "") {
  if (option === null || option === "") {
    return;
  }
  // logic
}

<Select
  ...
  onChange={onChange}
/>
Stav answered 29/4, 2023 at 14:25 Comment(0)
F
0

As per the latest version(5.8.0) this is how you type the onChange function.

const handleChange = (option: OnChangeValue<SelectOptions,false>,actionMeta: ActionMeta<SelectOptions>) => {
   //...
}

false --> Single Value

true --> Multi Value

where SelectOptions is the type of Options you pass the React Select

Flanders answered 6/12, 2023 at 8:23 Comment(0)
T
-1

None of the above worked for me. This did.

type SelectOption = {
  label: string
  value: string
}

const isSelectOption = (v: any): v is SelectOption => {
  if((v as SelectOption).value !== undefined) return v.value
  return false
}

<Select
  ...
  onChange={(v) => {
    if(isSelectOption(v)) {
      doSomethingWith(v.value)
    }
  }}
/>
Tse answered 31/8, 2021 at 18:6 Comment(1)
It worked for you because you used any as type, so of course it worked. But the point of TypeScript is to use types, not any.Santinasantini

© 2022 - 2024 — McMap. All rights reserved.