Anyone experienced with this case, I don't know how to declare the Select properties in Typescript. Please help!
import React from 'react'
import Select, {components, IndicatorProps} from 'react-select'
import {connect} from 'react-redux'
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
import { Users } from '../../../Redux/Users/UsersReducer';
import { RootState } from '../../../Redux/rootReducer';
interface SelectButtonProps {
onSelect: (event: React.FormEvent<HTMLSelectElement>) => void,
users: Users[],
stateId: string
}
const SelectUserButton:React.FunctionComponent<SelectButtonProps> = ({onSelect, users, stateId}): JSX.Element => {
const options = users.map( (user:Users) => ({label: user.name, value:`${user.id}`}))
const DropdownIndicator = (
props : IndicatorProps<any>
) => {
return (
<components.DropdownIndicator {...props}>
<ArrowDropDownIcon id="arrow_icon"/>
</components.DropdownIndicator>
);
};
const SingleValue = ({ children, ...props }:any) => (
<components.SingleValue {...props}>
{children}
</components.SingleValue>
);
return (
<div className='select__user'>
<label htmlFor='react-select-container'>Users</label>
<Select
SingleValue={SingleValue}
name="user"
components={{ DropdownIndicator }}
options={options}
onChange={onSelect}
maxMenuHeight={120}
placeholder={stateId}
/>
</div>
)
}
I got an error on the onChange event, because when using react-select, it will return an object.
And the browser also shows this error: "
The above error occurred in the <WithStyles(ForwardRef(SvgIcon))> component:
in WithStyles(ForwardRef(SvgIcon))
in ForwardRef
in ForwardRef (at SelectUserButton.tsx:23)
in div (created by Context.Consumer)
in EmotionCssPropInternal (created by DropdownIndicator)
in DropdownIndicator (at SelectUserButton.tsx:22)"
Let's start with the easy errors/issues which are to do with
SingleValue
.Your const
SingleValue
is passing through all of the props it receives so it's not doing anything. You can writeconst SingleValue = components.SingleValue
and it will be the same thing. You can also passcomponents.SingleValue
directly without assigning it to a local variable.I've had a look at the docs and source code for "react-select" and it doesn't seem like
SingleValue
is a prop ofSelect
, though oddly this does not trigger a typescript error. Instead it should be an additional property oncomponents
alongsideDropdownIndicator
. It would becomponents={{ DropdownIndicator, SingleValue: components.SingleValue }}
, but since you aren't changing anything from the default, it's not necessary to includeSingleValue
at all.Now to the hard part which is the event handling.
Your interface declares that the
onSelect
prop is a function which receives an event of typeReact.FormEvent<HTMLSelectElement>
. Unfortunately it is not possible to properly call this function because the "react-select" package doesn't pass the actual event to your callback functions.If you want to keep using this package, you will need to change your
SelectButtonProps
interface to something which is compatible with the data that you are getting back from the "react-select"onChange
function. For example, it can be a function which receives the selectedUsers
object:The typescript type of the first argument given to
onChange
isany
, but in practice it is the type of each option in your options array which is{label: string, value: (the type of your user.id which I don't know if it's string or number) }
. Frankly, the package author could have done a better job there as they really should be giving you the correct type based on the options array that you are providing. There are multiple places that they should be using generics (value type, option type) where they are using vague types likeany
instead.This is a function which you can pass to
onChange
:This is not necessary, but an alternative way of approaching this is that instead of mapping the users to a
{label, value}
and then mapping that{label, value}
option back to aUser
in youronChange
callback, what you can do is pass the users array directly to Select asoptions
. Then instead of using the default behaviors forgetOptionLabel
(option => option.label
) andgetOptionValue
(option => option.value
), you can pass your own mappings, which areuser => user.name
anduser => user.id
.Revised Code